mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Variable-size stack elements for expression compiler.
This commit is contained in:
parent
b7d856ed5f
commit
35d5b28fae
12
Compiler.cpp
12
Compiler.cpp
@ -213,15 +213,9 @@ bool Compiler::visit(FunctionDefinition& _function)
|
||||
// Note that the fact that the return arguments are of increasing index is vital for this
|
||||
// algorithm to work.
|
||||
|
||||
unsigned argumentsSize = 0;
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
|
||||
argumentsSize += variable->getType()->getSizeOnStack();
|
||||
unsigned returnValuesSize = 0;
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
|
||||
returnValuesSize += variable->getType()->getSizeOnStack();
|
||||
unsigned localVariablesSize = 0;
|
||||
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
|
||||
localVariablesSize += localVariable->getType()->getSizeOnStack();
|
||||
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
|
||||
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
|
||||
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
|
||||
|
||||
vector<int> stackLayout;
|
||||
stackLayout.push_back(returnValuesSize); // target of return address
|
||||
|
@ -35,6 +35,9 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||
{
|
||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
||||
unsigned const size = _variable.getType()->getSizeOnStack();
|
||||
if (stackPosition - size + 1 > 16)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
|
||||
<< errinfo_comment("Stack too deep."));
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
|
||||
}
|
||||
@ -46,5 +49,13 @@ void CompilerUtils::popStackElement(Type const& _type)
|
||||
m_context << eth::Instruction::POP;
|
||||
}
|
||||
|
||||
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (shared_ptr<Type const> const& type: _variableTypes)
|
||||
size += type->getSizeOnStack();
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,23 @@ public:
|
||||
void moveToStackVariable(VariableDeclaration const& _variable);
|
||||
/// Removes the current value from the top of the stack.
|
||||
void popStackElement(Type const& _type);
|
||||
|
||||
template <class T>
|
||||
static unsigned getSizeOnStack(std::vector<T> const& _variables);
|
||||
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
|
||||
|
||||
private:
|
||||
CompilerContext& m_context;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (T const& variable: _variables)
|
||||
size += variable->getType()->getSizeOnStack();
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/ExpressionCompiler.h>
|
||||
#include <libsolidity/CompilerContext.h>
|
||||
#include <libsolidity/CompilerUtils.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -174,9 +175,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
||||
// explicit type conversion contract -> address, nothing to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -203,13 +202,14 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
||||
m_context.appendJump();
|
||||
m_context << returnLabel;
|
||||
|
||||
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
|
||||
// callee adds return parameters, but removes arguments and return label
|
||||
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1);
|
||||
m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
|
||||
|
||||
// @todo for now, the return value of a function is its first return value, so remove
|
||||
// all others
|
||||
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
|
||||
m_context << eth::Instruction::POP;
|
||||
CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
|
||||
break;
|
||||
}
|
||||
case Location::EXTERNAL:
|
||||
@ -356,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
|
||||
{
|
||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
||||
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
|
||||
m_currentLValue = LValue(m_context, LValue::STORAGE);
|
||||
m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
|
||||
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
|
||||
break;
|
||||
}
|
||||
@ -376,7 +376,7 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
|
||||
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
|
||||
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
||||
|
||||
m_currentLValue = LValue(m_context, LValue::STORAGE);
|
||||
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
|
||||
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
|
||||
|
||||
return false;
|
||||
@ -565,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
|
||||
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
|
||||
}
|
||||
|
||||
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, const Type& _dataType,
|
||||
unsigned _baseStackOffset):
|
||||
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
|
||||
m_stackSize(_dataType.getSizeOnStack())
|
||||
{
|
||||
}
|
||||
|
||||
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
|
||||
{
|
||||
switch (m_type)
|
||||
@ -575,7 +582,8 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
|
||||
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);
|
||||
for (unsigned i = 0; i < m_stackSize; ++i)
|
||||
*m_context << eth::dupInstruction(stackPos + 1);
|
||||
break;
|
||||
}
|
||||
case STORAGE:
|
||||
@ -583,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
|
||||
break; // no distinction between value and reference for non-value types
|
||||
if (!_remove)
|
||||
*m_context << eth::Instruction::DUP1;
|
||||
*m_context << eth::Instruction::SLOAD;
|
||||
if (m_stackSize == 1)
|
||||
*m_context << eth::Instruction::SLOAD;
|
||||
else
|
||||
for (unsigned i = 0; i < m_stackSize; ++i)
|
||||
{
|
||||
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
|
||||
if (i + 1 < m_stackSize)
|
||||
*m_context << u256(1) << eth::Instruction::ADD;
|
||||
else
|
||||
*m_context << eth::Instruction::POP;
|
||||
}
|
||||
break;
|
||||
case MEMORY:
|
||||
if (!_expression.getType()->isValueType())
|
||||
@ -604,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
|
||||
{
|
||||
case STACK:
|
||||
{
|
||||
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
|
||||
if (stackPos > 16)
|
||||
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
|
||||
if (stackDiff > 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;
|
||||
else if (stackDiff > 0)
|
||||
for (unsigned i = 0; i < m_stackSize; ++i)
|
||||
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
|
||||
if (!_move)
|
||||
retrieveValue(_expression);
|
||||
break;
|
||||
@ -617,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
|
||||
case LValue::STORAGE:
|
||||
if (!_expression.getType()->isValueType())
|
||||
break; // no distinction between value and reference for non-value types
|
||||
if (!_move)
|
||||
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
||||
*m_context << eth::Instruction::SSTORE;
|
||||
// stack layout: value value ... value ref
|
||||
if (!_move) // copy values
|
||||
{
|
||||
if (m_stackSize + 1 > 16)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
||||
<< errinfo_comment("Stack too deep."));
|
||||
for (unsigned i = 0; i < m_stackSize; ++i)
|
||||
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
|
||||
}
|
||||
if (m_stackSize > 0) // store high index value first
|
||||
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
|
||||
for (unsigned i = 0; i < m_stackSize; ++i)
|
||||
{
|
||||
if (i + 1 >= m_stackSize)
|
||||
*m_context << eth::Instruction::SSTORE;
|
||||
else
|
||||
// v v ... v v r+x
|
||||
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
|
||||
<< eth::Instruction::SSTORE
|
||||
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
||||
}
|
||||
break;
|
||||
case LValue::MEMORY:
|
||||
if (!_expression.getType()->isValueType())
|
||||
@ -645,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
|
||||
|
||||
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
|
||||
{
|
||||
m_stackSize = _identifier.getType()->getSizeOnStack();
|
||||
if (m_context->isLocalVariable(&_declaration))
|
||||
{
|
||||
m_type = STACK;
|
||||
|
@ -93,8 +93,7 @@ private:
|
||||
enum LValueType { NONE, STACK, MEMORY, STORAGE };
|
||||
|
||||
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
|
||||
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
|
||||
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
|
||||
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
|
||||
|
||||
/// Set type according to the declaration and retrieve the reference.
|
||||
/// @a _expression is the current expression
|
||||
@ -129,6 +128,8 @@ private:
|
||||
/// If m_type is STACK, this is base stack offset (@see
|
||||
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
|
||||
unsigned m_baseStackOffset;
|
||||
/// Size of the value of this lvalue on the stack.
|
||||
unsigned m_stackSize;
|
||||
};
|
||||
|
||||
CompilerContext& m_context;
|
||||
|
Loading…
Reference in New Issue
Block a user