aboutsummaryrefslogtreecommitdiffstats
path: root/ExpressionCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ExpressionCompiler.cpp')
-rw-r--r--ExpressionCompiler.cpp177
1 files changed, 97 insertions, 80 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 6efb8b20..05bbb091 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -46,23 +46,16 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
bool ExpressionCompiler::visit(Assignment& _assignment)
{
- m_currentLValue = nullptr;
-
- Expression& rightHandSide = _assignment.getRightHandSide();
- rightHandSide.accept(*this);
- Type const& resultType = *_assignment.getType();
- appendTypeConversion(*rightHandSide.getType(), resultType);
+ _assignment.getRightHandSide().accept(*this);
+ appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
+ m_currentLValue.reset();
_assignment.getLeftHandSide().accept(*this);
Token::Value op = _assignment.getAssignmentOperator();
- if (op != Token::ASSIGN)
- {
- // compound assignment
- m_context << eth::Instruction::SWAP1;
- appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
- }
+ if (op != Token::ASSIGN) // compound assignment
+ appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
else
- m_context << eth::Instruction::POP; //@todo do not retrieve the value in the first place
+ m_context << eth::Instruction::POP;
storeInLValue(_assignment);
return false;
@@ -99,10 +92,7 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
m_context << eth::Instruction::ADD;
else
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
- if (_unaryOperation.isPrefixOperation())
- storeInLValue(_unaryOperation);
- else
- moveToLValue(_unaryOperation);
+ storeInLValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
break;
case Token::ADD: // +
// unary add, so basically no-op
@@ -123,11 +113,8 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
Type const& commonType = _binaryOperation.getCommonType();
Token::Value const op = _binaryOperation.getOperator();
- if (op == Token::AND || op == Token::OR)
- {
- // special case: short-circuiting
+ if (op == Token::AND || op == Token::OR) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
- }
else
{
bool cleanupNeeded = false;
@@ -135,10 +122,10 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
cleanupNeeded = true;
- leftExpression.accept(*this);
- appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
+ leftExpression.accept(*this);
+ appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
if (Token::isCompareOp(op))
appendCompareOperatorCode(op, commonType);
else
@@ -164,9 +151,13 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
- m_currentLValue = nullptr;
+ m_currentLValue.reset();
_functionCall.getExpression().accept(*this);
- FunctionDefinition const& function = dynamic_cast<FunctionDefinition&>(*m_currentLValue);
+ if (asserts(m_currentLValue.isInCode()))
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
+ eth::AssemblyItem functionTag(eth::PushTag, m_currentLValue.location);
+
+ FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
eth::AssemblyItem returnLabel = m_context.pushNewTag();
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
@@ -175,11 +166,10 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(),
- *function.getParameters()[i]->getType());
+ appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
}
- m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
+ m_context.appendJumpTo(functionTag);
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
@@ -205,24 +195,20 @@ void ExpressionCompiler::endVisit(IndexAccess&)
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
- m_currentLValue = _identifier.getReferencedDeclaration();
- switch (_identifier.getType()->getCategory())
- {
- case Type::Category::BOOL:
- case Type::Category::INTEGER:
- case Type::Category::REAL:
- {
- //@todo we also have to check where to retrieve them from once we add storage variables
- unsigned stackPos = stackPositionOfLValue();
- if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_identifier.getLocation())
- << errinfo_comment("Stack too deep."));
- m_context << eth::dupInstruction(stackPos + 1);
- break;
- }
- default:
- break;
- }
+ Declaration const* declaration = _identifier.getReferencedDeclaration();
+ if (m_context.isLocalVariable(declaration))
+ m_currentLValue = LValueLocation(LValueLocation::STACK,
+ m_context.getBaseStackOffsetOfVariable(*declaration));
+ else if (m_context.isStateVariable(declaration))
+ m_currentLValue = LValueLocation(LValueLocation::STORAGE,
+ m_context.getStorageLocationOfVariable(*declaration));
+ else if (m_context.isFunctionDefinition(declaration))
+ m_currentLValue = LValueLocation(LValueLocation::CODE,
+ m_context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(*declaration)).data());
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not supported or identifier not found."));
+
+ retrieveLValueValue(_identifier);
}
void ExpressionCompiler::endVisit(Literal& _literal)
@@ -267,23 +253,21 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const isSigned = type.isSigned();
- // note that EVM opcodes compare like "stack[0] < stack[1]",
- // but our left value is at stack[1], so everyhing is reversed.
switch (_operator)
{
case Token::GTE:
- m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
+ m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
<< eth::Instruction::ISZERO;
break;
case Token::LTE:
- m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
+ m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
<< eth::Instruction::ISZERO;
break;
case Token::GT:
- m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
+ m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
case Token::LT:
- m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
+ m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
@@ -314,16 +298,16 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
m_context << eth::Instruction::ADD;
break;
case Token::SUB:
- m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ m_context << eth::Instruction::SUB;
break;
case Token::MUL:
m_context << eth::Instruction::MUL;
break;
case Token::DIV:
- m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
+ m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
- m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
+ m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
@@ -364,10 +348,9 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
- // If the type of one of the operands is extended, we need to remove all
- // higher-order bits that we might have ignored in previous operations.
- // @todo: store in the AST whether the operand might have "dirty" higher
- // order bits
+ // For a type extension, we need to remove all higher-order bits that we might have ignored in
+ // previous operations.
+ // @todo: store in the AST whether the operand might have "dirty" higher order bits
if (_typeOnStack == _targetType && !_cleanupNeeded)
return;
@@ -388,31 +371,65 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
}
-void ExpressionCompiler::storeInLValue(Expression const& _expression)
-{
- moveToLValue(_expression);
- unsigned stackPos = stackPositionOfLValue();
- if (stackPos > 16)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
- << errinfo_comment("Stack too deep."));
- m_context << eth::dupInstruction(stackPos + 1);
-}
-
-void ExpressionCompiler::moveToLValue(Expression const& _expression)
+void ExpressionCompiler::retrieveLValueValue(Expression const& _expression)
{
- unsigned stackPos = stackPositionOfLValue();
- if (stackPos > 16)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
- << errinfo_comment("Stack too deep."));
- else if (stackPos > 0)
- m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
+ switch (m_currentLValue.locationType)
+ {
+ case LValueLocation::CODE:
+ // not stored on the stack
+ break;
+ case LValueLocation::STACK:
+ {
+ unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location));
+ if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ << errinfo_comment("Stack too deep."));
+ m_context << eth::dupInstruction(stackPos + 1);
+ break;
+ }
+ case LValueLocation::STORAGE:
+ m_context << m_currentLValue.location << eth::Instruction::SLOAD;
+ break;
+ case LValueLocation::MEMORY:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented."));
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type."));
+ break;
+ }
}
-unsigned ExpressionCompiler::stackPositionOfLValue() const
+void ExpressionCompiler::storeInLValue(Expression const& _expression, bool _move)
{
- if (asserts(m_currentLValue))
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not available on request."));
- return m_context.getStackPositionOfVariable(*m_currentLValue);
+ switch (m_currentLValue.locationType)
+ {
+ case LValueLocation::STACK:
+ {
+ unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location));
+ if (stackPos > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ << errinfo_comment("Stack too deep."));
+ else if (stackPos > 0)
+ m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
+ if (!_move)
+ retrieveLValueValue(_expression);
+ break;
+ }
+ case LValueLocation::STORAGE:
+ if (!_move)
+ m_context << eth::Instruction::DUP1;
+ m_context << m_currentLValue.location << eth::Instruction::SSTORE;
+ break;
+ case LValueLocation::CODE:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type does not support assignment."));
+ break;
+ case LValueLocation::MEMORY:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented."));
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type."));
+ break;
+ }
}
}