Fixed point type conversions.

This commit is contained in:
chriseth 2021-07-27 12:00:22 +02:00
parent 7a1db634cc
commit a8a897cb49
3 changed files with 189 additions and 66 deletions

View File

@ -776,15 +776,11 @@ void CompilerUtils::convertType(
solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\""); solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\"");
bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum); bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum);
bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; bool chopSignBitsPending = false;
if (chopSignBitsPending) if (_chopSignBits && targetTypeCategory == Type::Category::Integer)
{ chopSignBitsPending = dynamic_cast<IntegerType const&>(_targetType).isSigned();
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType); else if (_chopSignBits && targetTypeCategory == Type::Category::FixedPoint)
chopSignBitsPending = targetIntegerType.isSigned(); chopSignBitsPending = dynamic_cast<FixedPointType const&>(_targetType).isSigned();
}
if (targetTypeCategory == Type::Category::FixedPoint)
solUnimplemented("Not yet implemented - FixedPointType.");
switch (stackTypeCategory) switch (stackTypeCategory)
{ {
@ -800,6 +796,14 @@ void CompilerUtils::convertType(
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8) if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded); convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
} }
else if (targetTypeCategory == Type::Category::FixedPoint)
{
// Conversion from bytes to fixed point type. No need to clean the high bit
// only to shift right because of opposite alignment.
FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType);
solAssert(targetFixedPointType.numBits() == typeOnStack.numBytes() * 8, "");
rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8);
}
else if (targetTypeCategory == Type::Category::Address) else if (targetTypeCategory == Type::Category::Address)
{ {
solAssert(typeOnStack.numBytes() * 8 == 160, ""); solAssert(typeOnStack.numBytes() * 8 == 160, "");
@ -835,10 +839,9 @@ void CompilerUtils::convertType(
enumOverflowCheckPending = false; enumOverflowCheckPending = false;
} }
break; break;
case Type::Category::FixedPoint:
solUnimplemented("Not yet implemented - FixedPointType.");
case Type::Category::Address: case Type::Category::Address:
case Type::Category::Integer: case Type::Category::Integer:
case Type::Category::FixedPoint:
case Type::Category::Contract: case Type::Category::Contract:
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
if (targetTypeCategory == Type::Category::FixedBytes) if (targetTypeCategory == Type::Category::FixedBytes)
@ -846,6 +849,7 @@ void CompilerUtils::convertType(
solAssert( solAssert(
stackTypeCategory == Type::Category::Address || stackTypeCategory == Type::Category::Address ||
stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Integer ||
stackTypeCategory == Type::Category::FixedPoint ||
stackTypeCategory == Type::Category::RationalNumber, stackTypeCategory == Type::Category::RationalNumber,
"Invalid conversion to FixedBytesType requested." "Invalid conversion to FixedBytesType requested."
); );
@ -859,11 +863,14 @@ void CompilerUtils::convertType(
} }
else if (stackTypeCategory == Type::Category::Address) else if (stackTypeCategory == Type::Category::Address)
solAssert(targetBytesType.numBytes() * 8 == 160, ""); solAssert(targetBytesType.numBytes() * 8 == 160, "");
else if (stackTypeCategory == Type::Category::FixedPoint)
solAssert(targetBytesType.numBytes() * 8 == dynamic_cast<FixedPointType const&>(_typeOnStack).numBits(), "");
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
} }
else if (targetTypeCategory == Type::Category::Enum) else if (targetTypeCategory == Type::Category::Enum)
{ {
solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested."); solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
solAssert(stackTypeCategory != Type::Category::FixedPoint, "Invalid conversion to EnumType requested.");
solAssert(_typeOnStack.mobileType(), ""); solAssert(_typeOnStack.mobileType(), "");
// just clean // just clean
convertType(_typeOnStack, *_typeOnStack.mobileType(), true); convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
@ -881,12 +888,50 @@ void CompilerUtils::convertType(
stackTypeCategory == Type::Category::FixedPoint, stackTypeCategory == Type::Category::FixedPoint,
"Invalid conversion to FixedMxNType requested." "Invalid conversion to FixedMxNType requested."
); );
//shift all integer bits onto the left side of the fixed type
FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType); FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) int digitDifference = static_cast<int>(targetFixedPointType.fractionalDigits());
if (targetFixedPointType.numBits() > typeOnStack->numBits()) bool isSigned = false;
if (auto const* typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
{
cleanHigherOrderBits(*typeOnStack); cleanHigherOrderBits(*typeOnStack);
solUnimplemented("Not yet implemented - FixedPointType."); isSigned = typeOnStack->isSigned();
}
else if (auto const* typeOnStack = dynamic_cast<FixedPointType const*>(&_typeOnStack))
{
isSigned = typeOnStack->isSigned();
digitDifference -= static_cast<int>(typeOnStack->fractionalDigits());
if (typeOnStack->isSigned() != targetFixedPointType.isSigned())
solAssert(digitDifference >= 0, "");
if (digitDifference < 0 || typeOnStack->numBits() < targetFixedPointType.numBits())
cleanHigherOrderBits(*typeOnStack->asIntegerType());
}
else if (auto const* typeOnStack = dynamic_cast<RationalNumberType const*>(&_typeOnStack))
{
if (typeOnStack->isFractional())
digitDifference -= static_cast<int>(typeOnStack->fixedPointType()->fractionalDigits());
isSigned = typeOnStack->isNegative();
}
else
solAssert(false, "");
if (digitDifference > 0)
m_context << pow(u256(10), static_cast<unsigned>(digitDifference)) << Instruction::MUL;
else if (digitDifference < 0)
m_context <<
pow(u256(10), static_cast<unsigned>(-digitDifference)) <<
(isSigned ? Instruction::SDIV : Instruction::DIV);
if (_cleanupNeeded)
cleanHigherOrderBits(*targetFixedPointType.asIntegerType());
if (chopSignBitsPending)
{
if (targetFixedPointType.numBits() < 256)
m_context
<< ((u256(1) << targetFixedPointType.numBits()) - 1)
<< Instruction::AND;
chopSignBitsPending = false;
}
} }
else else
{ {
@ -897,27 +942,43 @@ void CompilerUtils::convertType(
"" ""
); );
IntegerType addressType(160); IntegerType addressType(160);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer IntegerType const& targetType =
? dynamic_cast<IntegerType const&>(_targetType) : addressType; targetTypeCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_targetType) :
addressType;
if (stackTypeCategory == Type::Category::RationalNumber) if (stackTypeCategory == Type::Category::RationalNumber)
{ {
RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack); RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack);
// We know that the stack is clean, we only have to clean for a narrowing conversion // We know that the stack is clean, we only have to clean for a narrowing conversion
// where cleanup is forced. // where cleanup is forced.
solUnimplementedAssert(!constType.isFractional(), "Not yet implemented - FixedPointType."); solUnimplementedAssert(!constType.isFractional(), "Not yet implemented - rational to integer conversion.");
if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded) if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded)
cleanHigherOrderBits(targetType); cleanHigherOrderBits(targetType);
} }
else if (stackTypeCategory == Type::Category::FixedPoint)
{
FixedPointType const& typeOnStack = dynamic_cast<FixedPointType const&>(_typeOnStack);
solAssert(typeOnStack.isSigned() == targetType.isSigned(), "");
cleanHigherOrderBits(*typeOnStack.asIntegerType());
m_context <<
pow(u256(10), typeOnStack.fractionalDigits()) <<
(typeOnStack.isSigned() ? Instruction::SDIV : Instruction::DIV);
if (_cleanupNeeded)
cleanHigherOrderBits(targetType);
}
else else
{ {
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer IntegerType const& typeOnStack =
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType; stackTypeCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_typeOnStack) :
addressType;
// Widening: clean up according to source type width // Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits // Non-widening and force: clean up according to target type bits
if (targetType.numBits() > typeOnStack.numBits()) if (targetType.numBits() > typeOnStack.numBits())
cleanHigherOrderBits(typeOnStack); cleanHigherOrderBits(typeOnStack);
else if (_cleanupNeeded) else if (_cleanupNeeded)
cleanHigherOrderBits(targetType); cleanHigherOrderBits(targetType);
}
if (chopSignBitsPending) if (chopSignBitsPending)
{ {
if (targetType.numBits() < 256) if (targetType.numBits() < 256)
@ -927,7 +988,6 @@ void CompilerUtils::convertType(
chopSignBitsPending = false; chopSignBitsPending = false;
} }
} }
}
break; break;
case Type::Category::StringLiteral: case Type::Category::StringLiteral:
{ {

View File

@ -222,18 +222,18 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
m_context m_context
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1 << Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
if (m_dataType->category() == Type::Category::FixedPoint)
// implementation should be very similar to the integer case.
solUnimplemented("Not yet implemented - FixedPointType.");
if (m_dataType->category() == Type::Category::FixedBytes) if (m_dataType->category() == Type::Category::FixedBytes)
{ {
CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * m_dataType->storageBytes()); CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * m_dataType->storageBytes());
cleaned = true; cleaned = true;
} }
else if ( else if ((
m_dataType->category() == Type::Category::Integer && m_dataType->category() == Type::Category::Integer &&
dynamic_cast<IntegerType const&>(*m_dataType).isSigned() dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
) ) || (
m_dataType->category() == Type::Category::FixedPoint &&
dynamic_cast<FixedPointType const&>(*m_dataType).isSigned()
))
{ {
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND; m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
cleaned = true; cleaned = true;

View File

@ -282,7 +282,7 @@ string YulUtilFunctions::leftAlignFunction(Type const& _type)
templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)"); templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
break; break;
case Type::Category::FixedPoint: case Type::Category::FixedPoint:
solUnimplemented("Fixed point types not implemented."); templ("body", "aligned := " + leftAlignFunction(*dynamic_cast<FixedPointType const&>(_type).asIntegerType()) + "(value)");
break; break;
case Type::Category::Array: case Type::Category::Array:
case Type::Category::Struct: case Type::Category::Struct:
@ -435,7 +435,7 @@ string YulUtilFunctions::shiftRightSignedFunctionDynamic()
string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType) string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
{ {
solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType."); solAssert(_type.category() != Type::Category::FixedPoint, "No operators for fixed point types.");
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
solAssert(_amountType.category() == Type::Category::Integer, ""); solAssert(_amountType.category() == Type::Category::Integer, "");
solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), ""); solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
@ -458,7 +458,7 @@ string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _
string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType) string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
{ {
solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType."); solAssert(_type.category() != Type::Category::FixedPoint, "No operators for fixed point types.");
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
solAssert(_amountType.category() == Type::Category::Integer, ""); solAssert(_amountType.category() == Type::Category::Integer, "");
solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), ""); solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
@ -3266,13 +3266,17 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
case Type::Category::Integer: case Type::Category::Integer:
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
case Type::Category::Contract: case Type::Category::Contract:
case Type::Category::FixedPoint:
{ {
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from)) if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
solUnimplementedAssert(!rational->isFractional(), "Not yet implemented - FixedPointType."); if (rational->isFractional())
solAssert(toCategory == Type::Category::FixedPoint, "");
if (toCategory == Type::Category::FixedBytes) if (toCategory == Type::Category::FixedBytes)
{ {
solAssert( solAssert(
fromCategory == Type::Category::Integer || fromCategory == Type::Category::RationalNumber, fromCategory == Type::Category::Integer ||
fromCategory == Type::Category::FixedPoint ||
fromCategory == Type::Category::RationalNumber,
"Invalid conversion to FixedBytesType requested." "Invalid conversion to FixedBytesType requested."
); );
FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to); FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to);
@ -3284,6 +3288,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
} }
else if (toCategory == Type::Category::Enum) else if (toCategory == Type::Category::Enum)
{ {
solAssert(fromCategory != Type::Category::FixedPoint, "");
solAssert(_from.mobileType(), ""); solAssert(_from.mobileType(), "");
body = body =
Whiskers("converted := <cleanEnum>(<cleanInt>(value))") Whiskers("converted := <cleanEnum>(<cleanInt>(value))")
@ -3293,7 +3298,42 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
.render(); .render();
} }
else if (toCategory == Type::Category::FixedPoint) else if (toCategory == Type::Category::FixedPoint)
solUnimplemented("Not yet implemented - FixedPointType."); {
FixedPointType const& toFixedType = dynamic_cast<FixedPointType const&>(_to);
int digitDifference = static_cast<int>(toFixedType.fractionalDigits());
bool isSigned = false;
if (auto const* fromIntegerType = dynamic_cast<IntegerType const*>(&_from))
{
solAssert(
fromIntegerType->maxValue() <= toFixedType.maxIntegerValue() &&
fromIntegerType->minValue() >= toFixedType.minIntegerValue(),
""
);
isSigned = fromIntegerType->isSigned();
}
else if (auto const* fromFixedType = dynamic_cast<FixedPointType const*>(&_from))
{
isSigned = fromFixedType->isSigned();
digitDifference -= static_cast<int>(fromFixedType->fractionalDigits());
if (fromFixedType->isSigned() != toFixedType.isSigned())
solAssert(digitDifference >= 0, "");
}
else if (auto const* fromRationalType = dynamic_cast<RationalNumberType const*>(&_from))
{
if (fromRationalType->isFractional())
digitDifference -= static_cast<int>(fromRationalType->fixedPointType()->fractionalDigits());
isSigned = fromRationalType->isNegative();
}
else
solAssert(false, "");
// TODO maybe target cleanup can be avoided in some cases.
body = Whiskers{"converted := <cleanupTarget>(<operator>(<cleanupSource>(value), <factor>))"}
("cleanupSource", cleanupFunction(_from))
("operator", digitDifference >= 0 ? "mul" : isSigned ? "sdiv" : "div")
("factor", "1" + string(static_cast<unsigned>(abs(digitDifference)), '0'))
("cleanupTarget", cleanupFunction(toFixedType))
.render();
}
else if (toCategory == Type::Category::Address) else if (toCategory == Type::Category::Address)
body = body =
Whiskers("converted := <convert>(value)") Whiskers("converted := <convert>(value)")
@ -3310,7 +3350,20 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
toCategory == Type::Category::Integer ? toCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_to) : dynamic_cast<IntegerType const&>(_to) :
addressType; addressType;
if (fromCategory == Type::Category::FixedPoint)
{
auto const& fixedFrom = dynamic_cast<FixedPointType const&>(_from);
solAssert(fixedFrom.isSigned() == to.isSigned(), "" );
body =
Whiskers("converted := <cleanInt>(<div>(<cleanFixed>(value), <digits>))")
("cleanFixed", cleanupFunction(_from))
("div", to.isSigned() ? "sdiv" : "div")
("digits", "1"s + string(fixedFrom.fractionalDigits(), '0'))
("cleanInt", cleanupFunction(to))
.render();
}
else
{
// Clean according to the "to" type, except if this is // Clean according to the "to" type, except if this is
// a widening conversion. // a widening conversion.
IntegerType const* cleanupType = &to; IntegerType const* cleanupType = &to;
@ -3328,6 +3381,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
("cleanInt", cleanupFunction(*cleanupType)) ("cleanInt", cleanupFunction(*cleanupType))
.render(); .render();
} }
}
break; break;
} }
case Type::Category::Bool: case Type::Category::Bool:
@ -3339,9 +3393,6 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
.render(); .render();
break; break;
} }
case Type::Category::FixedPoint:
solUnimplemented("Fixed point types not implemented.");
break;
case Type::Category::Struct: case Type::Category::Struct:
{ {
solAssert(toCategory == Type::Category::Struct, ""); solAssert(toCategory == Type::Category::Struct, "");
@ -3390,6 +3441,18 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
("shift", shiftRightFunction(256 - from.numBytes() * 8)) ("shift", shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render(); .render();
else if (toCategory == Type::Category::FixedPoint)
{
auto const& toFixedPoint = dynamic_cast<FixedPointType const&>(_to);
IntegerType integerType(
toFixedPoint.numBits(),
toFixedPoint.isSigned() ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
);
body =
Whiskers("converted := <convert>(value)")
("convert", conversionFunction(_from, integerType))
.render();
}
else if (toCategory == Type::Category::Address) else if (toCategory == Type::Category::Address)
body = body =
Whiskers("converted := <convert>(value)") Whiskers("converted := <convert>(value)")
@ -3734,15 +3797,15 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")"); templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
break; break;
} }
case Type::Category::FixedPoint:
templ("body", "cleaned := " + cleanupFunction(*dynamic_cast<FixedPointType const&>(_type).asIntegerType()) + "(value)");
break;
case Type::Category::RationalNumber: case Type::Category::RationalNumber:
templ("body", "cleaned := value"); templ("body", "cleaned := value");
break; break;
case Type::Category::Bool: case Type::Category::Bool:
templ("body", "cleaned := iszero(iszero(value))"); templ("body", "cleaned := iszero(iszero(value))");
break; break;
case Type::Category::FixedPoint:
solUnimplemented("Fixed point types not implemented.");
break;
case Type::Category::Function: case Type::Category::Function:
switch (dynamic_cast<FunctionType const&>(_type).kind()) switch (dynamic_cast<FunctionType const&>(_type).kind())
{ {