aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/ast/Types.cpp32
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp10
-rw-r--r--libsolidity/codegen/CompilerUtils.h18
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp122
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h6
5 files changed, 144 insertions, 44 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index d9660bc0..03ff8471 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -251,6 +251,19 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members;
}
+bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType)
+{
+ // Disable >>> here.
+ if (_operator == Token::SHR)
+ return false;
+ else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType))
+ return !otherInt->isAddress();
+ else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
+ return otherRat->integerType() && !otherRat->integerType()->isSigned();
+ else
+ return false;
+}
+
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
@@ -340,6 +353,17 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
_other->category() != category()
)
return TypePointer();
+ if (Token::isShiftOp(_operator))
+ {
+ // Shifts are not symmetric with respect to the type
+ if (isAddress())
+ return TypePointer();
+ if (isValidShiftAndAmountType(_operator, *_other))
+ return shared_from_this();
+ else
+ return TypePointer();
+ }
+
auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point
if (!commonType)
return TypePointer();
@@ -954,6 +978,14 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
+ if (Token::isShiftOp(_operator))
+ {
+ if (isValidShiftAndAmountType(_operator, *_other))
+ return shared_from_this();
+ else
+ return TypePointer();
+ }
+
auto commonType = dynamic_pointer_cast<FixedBytesType const>(Type::commonType(shared_from_this(), _other));
if (!commonType)
return TypePointer();
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index da2e78e8..7d382aba 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -952,9 +952,9 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
}
}
-unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
+unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
{
- unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
+ unsigned numBytes = _type.calldataEncodedSize(_padToWords);
bool isExternalFunctionType = false;
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
if (funType->location() == FunctionType::Location::External)
@@ -993,9 +993,9 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
}
-unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries)
+unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
{
- unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
+ unsigned numBytes = _type.calldataEncodedSize(_padToWords);
bool leftAligned = _type.category() == Type::Category::FixedBytes;
if (numBytes == 0)
m_context << Instruction::POP;
@@ -1003,7 +1003,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
convertType(_type, _type, true);
- if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
+ if (numBytes != 32 && !leftAligned && !_padToWords)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL;
}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index ad53efea..b9ed6757 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -52,13 +52,13 @@ public:
/// @param _offset offset in memory (or calldata)
/// @param _type data type to load
/// @param _fromCalldata if true, load from calldata, not from memory
- /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries
+ /// @param _padToWords if true, assume the data is padded to full words (32 bytes)
/// @returns the number of bytes consumed in memory.
unsigned loadFromMemory(
unsigned _offset,
Type const& _type = IntegerType(256),
bool _fromCalldata = false,
- bool _padToWordBoundaries = false
+ bool _padToWords = false
);
/// Dynamic version of @see loadFromMemory, expects the memory offset on the stack.
/// Stack pre: memory_offset
@@ -66,7 +66,7 @@ public:
void loadFromMemoryDynamic(
Type const& _type,
bool _fromCalldata = false,
- bool _padToWordBoundaries = true,
+ bool _padToWords = true,
bool _keepUpdatedMemoryOffset = true
);
/// Stores a 256 bit integer from stack in memory.
@@ -76,11 +76,11 @@ public:
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that. For reference types, only copies the data pointer. Fails for
/// non-memory-references.
- /// @param _padToWordBoundaries if true, adds zeros to pad to multiple of 32 bytes. Array elements
+ /// @param _padToWords if true, adds zeros to pad to multiple of 32 bytes. Array elements
/// are always padded (except for byte arrays), regardless of this parameter.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
- void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
+ void storeInMemoryDynamic(Type const& _type, bool _padToWords = true);
/// Copies values (of types @a _givenTypes) given on the stack to a location in memory given
/// at the stack top, encoding them according to the ABI as the given types @a _targetTypes.
@@ -88,7 +88,7 @@ public:
/// Stack pre: <v1> <v2> ... <vn> <memptr>
/// Stack post: <memptr_updated>
/// Does not touch the memory-free pointer.
- /// @param _padToWordBoundaries if false, all values are concatenated without padding.
+ /// @param _padToWords if false, all values are concatenated without padding.
/// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
/// together with fixed-length data.
/// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not
@@ -98,7 +98,7 @@ public:
void encodeToMemory(
TypePointers const& _givenTypes = {},
TypePointers const& _targetTypes = {},
- bool _padToWordBoundaries = true,
+ bool _padToWords = true,
bool _copyDynamicDataInPlace = false,
bool _encodeAsLibraryTypes = false
);
@@ -193,9 +193,9 @@ private:
void cleanHigherOrderBits(IntegerType const& _typeOnStack);
/// Prepares the given type for storing in memory by shifting it if necessary.
- unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries);
+ unsigned prepareMemoryStore(Type const& _type, bool _padToWords);
/// Loads type from memory assuming memory offset is on stack top.
- unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries);
+ unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords);
CompilerContext& m_context;
};
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 5748d818..a7fb8408 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -197,21 +197,39 @@ bool ExpressionCompiler::visit(Conditional const& _condition)
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
+ Token::Value op = _assignment.assignmentOperator();
+ Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op);
+ Type const& leftType = *_assignment.leftHandSide().annotation().type;
+ if (leftType.category() == Type::Category::Tuple)
+ {
+ solAssert(*_assignment.annotation().type == TupleType(), "");
+ solAssert(op == Token::Assign, "");
+ }
+ else
+ solAssert(*_assignment.annotation().type == leftType, "");
+ bool cleanupNeeded = false;
+ if (op != Token::Assign)
+ cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp);
_assignment.rightHandSide().accept(*this);
// Perform some conversion already. This will convert storage types to memory and literals
// to their actual type, but will not convert e.g. memory to storage.
- TypePointer type = _assignment.rightHandSide().annotation().type->closestTemporaryType(
- _assignment.leftHandSide().annotation().type
- );
- utils().convertType(*_assignment.rightHandSide().annotation().type, *type);
+ TypePointer rightIntermediateType;
+ if (op != Token::Assign && Token::isShiftOp(binOp))
+ rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType();
+ else
+ rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
+ _assignment.leftHandSide().annotation().type
+ );
+ utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded);
_assignment.leftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved.");
- Token::Value op = _assignment.assignmentOperator();
- if (op != Token::Assign) // compound assignment
+ if (op == Token::Assign)
+ m_currentLValue->storeValue(*rightIntermediateType, _assignment.location());
+ else // compound assignment
{
- solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
+ solAssert(leftType.isValueType(), "Compound operators only available for value types.");
unsigned lvalueSize = m_currentLValue->sizeOnStack();
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
if (lvalueSize > 0)
@@ -221,7 +239,15 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
// value lvalue_ref value lvalue_ref
}
m_currentLValue->retrieveValue(_assignment.location(), true);
- appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type);
+ utils().convertType(leftType, leftType, cleanupNeeded);
+
+ if (Token::isShiftOp(binOp))
+ appendShiftOperatorCode(binOp, leftType, *rightIntermediateType);
+ else
+ {
+ solAssert(leftType == *rightIntermediateType, "");
+ appendOrdinaryBinaryOperatorCode(binOp, leftType);
+ }
if (lvalueSize > 0)
{
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
@@ -229,8 +255,8 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
for (unsigned i = 0; i < itemSize; ++i)
m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP;
}
+ m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location());
}
- m_currentLValue->storeValue(*type, _assignment.location());
m_currentLValue.reset();
return false;
}
@@ -351,20 +377,19 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
Expression const& leftExpression = _binaryOperation.leftExpression();
Expression const& rightExpression = _binaryOperation.rightExpression();
solAssert(!!_binaryOperation.annotation().commonType, "");
- Type const& commonType = *_binaryOperation.annotation().commonType;
+ TypePointer const& commonType = _binaryOperation.annotation().commonType;
Token::Value const c_op = _binaryOperation.getOperator();
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
- else if (commonType.category() == Type::Category::RationalNumber)
- m_context << commonType.literalValue(nullptr);
+ else if (commonType->category() == Type::Category::RationalNumber)
+ m_context << commonType->literalValue(nullptr);
else
{
- bool cleanupNeeded = false;
- if (Token::isCompareOp(c_op))
- cleanupNeeded = true;
- if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod))
- cleanupNeeded = true;
+ bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
+
+ TypePointer leftTargetType = commonType;
+ TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType;
// for commutative operators, push the literal as late as possible to allow improved optimization
auto isLiteral = [](Expression const& _e)
@@ -375,21 +400,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
if (swap)
{
leftExpression.accept(*this);
- utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded);
rightExpression.accept(*this);
- utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded);
}
else
{
rightExpression.accept(*this);
- utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded);
leftExpression.accept(*this);
- utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded);
}
- if (Token::isCompareOp(c_op))
- appendCompareOperatorCode(c_op, commonType);
+ if (Token::isShiftOp(c_op))
+ // shift only cares about the signedness of both sides
+ appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType);
+ else if (Token::isCompareOp(c_op))
+ appendCompareOperatorCode(c_op, *commonType);
else
- appendOrdinaryBinaryOperatorCode(c_op, commonType);
+ appendOrdinaryBinaryOperatorCode(c_op, *commonType);
}
// do not visit the child nodes, we already did that explicitly
@@ -1326,8 +1354,6 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator
appendArithmeticOperatorCode(_operator, _type);
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
- else if (Token::isShiftOp(_operator))
- appendShiftOperatorCode(_operator);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator."));
}
@@ -1390,17 +1416,45 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
}
}
-void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
+void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType)
{
- solUnimplemented("Shift operators not yet implemented.");
+ // stack: shift_amount value_to_shift
+
+ bool c_valueSigned = false;
+ if (auto valueType = dynamic_cast<IntegerType const*>(&_valueType))
+ c_valueSigned = valueType->isSigned();
+ else
+ solAssert(dynamic_cast<FixedBytesType const*>(&_valueType), "Only integer and fixed bytes type supported for shifts.");
+
+ // The amount can be a RationalNumberType too.
+ bool c_amountSigned = false;
+ if (auto amountType = dynamic_cast<RationalNumberType const*>(&_shiftAmountType))
+ {
+ // This should be handled by the type checker.
+ solAssert(amountType->integerType(), "");
+ solAssert(!amountType->integerType()->isSigned(), "");
+ }
+ else if (auto amountType = dynamic_cast<IntegerType const*>(&_shiftAmountType))
+ c_amountSigned = amountType->isSigned();
+ else
+ solAssert(false, "Invalid shift amount type.");
+
+ // shift by negative amount throws exception
+ if (c_amountSigned)
+ {
+ m_context << u256(0) << Instruction::DUP3 << Instruction::SLT;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
switch (_operator)
{
case Token::SHL:
+ m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::MUL;
break;
case Token::SAR:
+ m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::SHR:
- break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
}
@@ -1688,6 +1742,16 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
setLValue<StorageItem>(_expression, *_expression.annotation().type);
}
+bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op)
+{
+ if (Token::isCompareOp(_op) || Token::isShiftOp(_op))
+ return true;
+ else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod))
+ return true;
+ else
+ return false;
+}
+
CompilerUtils ExpressionCompiler::utils()
{
return CompilerUtils(m_context);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index f08bded9..d0a8ac15 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -91,7 +91,7 @@ private:
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
void appendBitOperatorCode(Token::Value _operator);
- void appendShiftOperatorCode(Token::Value _operator);
+ void appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType);
/// @}
/// Appends code to call a function of the given type with the given arguments.
@@ -117,6 +117,10 @@ private:
template <class _LValueType, class... _Arguments>
void setLValue(Expression const& _expression, _Arguments const&... _arguments);
+ /// @returns true if the operator applied to the given type requires a cleanup prior to the
+ /// operation.
+ bool cleanupNeededForOp(Type::Category _type, Token::Value _op);
+
/// @returns the CompilerUtils object containing the current context.
CompilerUtils utils();