Better error message for out of stack in assembly.

This commit is contained in:
chriseth 2019-01-28 17:19:41 +01:00
parent d6b8521ed5
commit 17a1e7aed5
4 changed files with 55 additions and 26 deletions

View File

@ -184,14 +184,25 @@ void CodeGenerator::assemble(
) )
{ {
EthAssemblyAdapter assemblyAdapter(_assembly); EthAssemblyAdapter assemblyAdapter(_assembly);
CodeTransform( try
assemblyAdapter, {
_analysisInfo, CodeTransform(
_parsedData, assemblyAdapter,
*EVMDialect::strictAssemblyForEVM(), _analysisInfo,
_optimize, _parsedData,
false, *EVMDialect::strictAssemblyForEVM(),
_identifierAccess, _optimize,
_useNamedLabelsForFunctions false,
)(_parsedData); _identifierAccess,
_useNamedLabelsForFunctions
)(_parsedData);
}
catch (StackTooDeepError const& _e)
{
BOOST_THROW_EXCEPTION(
InternalCompilerError() << errinfo_comment(
"Stack too deep when compiling inline assembly" +
(_e.comment() ? ": " + *_e.comment() : ".")
));
}
} }

View File

@ -192,7 +192,8 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
bool atTopOfStack = true; bool atTopOfStack = true;
for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex) for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
{ {
auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(_varDecl.variables[varIndex].name)); YulString varName = _varDecl.variables[varIndex].name;
auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(varName));
m_context->variableStackHeights[&var] = height + varIndex; m_context->variableStackHeights[&var] = height + varIndex;
if (!m_allowStackOpt) if (!m_allowStackOpt)
continue; continue;
@ -217,7 +218,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
m_unusedStackSlots.erase(m_unusedStackSlots.begin()); m_unusedStackSlots.erase(m_unusedStackSlots.begin());
m_context->variableStackHeights[&var] = slot; m_context->variableStackHeights[&var] = slot;
m_assembly.setSourceLocation(_varDecl.location); m_assembly.setSourceLocation(_varDecl.location);
if (int heightDiff = variableHeightDiff(var, true)) if (int heightDiff = variableHeightDiff(var, varName, true))
m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1));
m_assembly.appendInstruction(solidity::Instruction::POP); m_assembly.appendInstruction(solidity::Instruction::POP);
--m_stackAdjustment; --m_stackAdjustment;
@ -353,7 +354,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
{ {
// TODO: opportunity for optimization: Do not DUP if this is the last reference // TODO: opportunity for optimization: Do not DUP if this is the last reference
// to the top most element of the stack // to the top most element of the stack
if (int heightDiff = variableHeightDiff(_var, false)) if (int heightDiff = variableHeightDiff(_var, _identifier.name, false))
m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); m_assembly.appendInstruction(solidity::dupInstruction(heightDiff));
else else
// Store something to balance the stack // Store something to balance the stack
@ -542,7 +543,14 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
for (size_t i = 0; i < _function.returnVariables.size(); ++i) for (size_t i = 0; i < _function.returnVariables.size(); ++i)
stackLayout.push_back(i); // Move return values down, but keep order. stackLayout.push_back(i); // Move return values down, but keep order.
solAssert(stackLayout.size() <= 17, "Stack too deep"); if (stackLayout.size() > 17)
BOOST_THROW_EXCEPTION(StackTooDeepError() << errinfo_comment(
"The function " +
_function.name.str() +
" has " +
to_string(stackLayout.size() - 17) +
" parameters or return variables too many to fit the stack size."
));
while (!stackLayout.empty() && stackLayout.back() != int(stackLayout.size() - 1)) while (!stackLayout.empty() && stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
{ {
@ -711,7 +719,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName)
if (auto var = m_scope->lookup(_variableName.name)) if (auto var = m_scope->lookup(_variableName.name))
{ {
Scope::Variable const& _var = boost::get<Scope::Variable>(*var); Scope::Variable const& _var = boost::get<Scope::Variable>(*var);
if (int heightDiff = variableHeightDiff(_var, true)) if (int heightDiff = variableHeightDiff(_var, _variableName.name, true))
m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1));
m_assembly.appendInstruction(solidity::Instruction::POP); m_assembly.appendInstruction(solidity::Instruction::POP);
decreaseReference(_variableName.name, _var); decreaseReference(_variableName.name, _var);
@ -726,19 +734,21 @@ void CodeTransform::generateAssignment(Identifier const& _variableName)
} }
} }
int CodeTransform::variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const int CodeTransform::variableHeightDiff(Scope::Variable const& _var, YulString _varName, bool _forSwap) const
{ {
solAssert(m_context->variableStackHeights.count(&_var), ""); solAssert(m_context->variableStackHeights.count(&_var), "");
int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var];
if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) solAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable.");
{ int limit = _forSwap ? 17 : 16;
solUnimplemented( if (heightDiff > limit)
"Variable inaccessible, too deep inside stack (" + to_string(heightDiff) + ")" BOOST_THROW_EXCEPTION(StackTooDeepError() << errinfo_comment(
); "Variable " +
return 0; _varName.str() +
} " is " +
else to_string(heightDiff - limit) +
return heightDiff; " slot(s) too deep inside the stack."
));
return heightDiff;
} }
void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const

View File

@ -40,6 +40,8 @@ namespace yul
struct AsmAnalysisInfo; struct AsmAnalysisInfo;
class EVMAssembly; class EVMAssembly;
struct StackTooDeepError: virtual YulException {};
struct CodeTransformContext struct CodeTransformContext
{ {
std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs; std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs;
@ -85,6 +87,10 @@ class CodeTransform: public boost::static_visitor<>
public: public:
/// Create the code transformer. /// Create the code transformer.
/// @param _identifierAccess used to resolve identifiers external to the inline assembly /// @param _identifierAccess used to resolve identifiers external to the inline assembly
/// As a side-effect of its construction, translates the Yul code and appends it to the
/// given assembly.
/// Throws StackTooDeepError if a variable is not accessible or if a function has too
/// many parameters.
CodeTransform( CodeTransform(
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo, AsmAnalysisInfo& _analysisInfo,
@ -172,7 +178,7 @@ private:
/// Determines the stack height difference to the given variables. Throws /// Determines the stack height difference to the given variables. Throws
/// if it is not yet in scope or the height difference is too large. Returns /// if it is not yet in scope or the height difference is too large. Returns
/// the (positive) stack height difference otherwise. /// the (positive) stack height difference otherwise.
int variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const; int variableHeightDiff(Scope::Variable const& _var, YulString _name, bool _forSwap) const;
void expectDeposit(int _deposit, int _oldHeight) const; void expectDeposit(int _deposit, int _oldHeight) const;

View File

@ -60,5 +60,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
yulAssert(_object.analysisInfo, "No analysis info."); yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code."); yulAssert(_object.code, "No code.");
// We do not catch and re-throw the stack too deep exception here because it is a YulException,
// which should be native to this part of the code.
CodeTransform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, _optimize, m_evm15}(*_object.code); CodeTransform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, _optimize, m_evm15}(*_object.code);
} }