diff --git a/libsolidity/codegen/AsmCodeGen.cpp b/libsolidity/codegen/AsmCodeGen.cpp index c04c1c34f..02ae6e661 100644 --- a/libsolidity/codegen/AsmCodeGen.cpp +++ b/libsolidity/codegen/AsmCodeGen.cpp @@ -184,14 +184,25 @@ void CodeGenerator::assemble( ) { EthAssemblyAdapter assemblyAdapter(_assembly); - CodeTransform( - assemblyAdapter, - _analysisInfo, - _parsedData, - *EVMDialect::strictAssemblyForEVM(), - _optimize, - false, - _identifierAccess, - _useNamedLabelsForFunctions - )(_parsedData); + try + { + CodeTransform( + assemblyAdapter, + _analysisInfo, + _parsedData, + *EVMDialect::strictAssemblyForEVM(), + _optimize, + false, + _identifierAccess, + _useNamedLabelsForFunctions + )(_parsedData); + } + catch (StackTooDeepError const& _e) + { + BOOST_THROW_EXCEPTION( + InternalCompilerError() << errinfo_comment( + "Stack too deep when compiling inline assembly" + + (_e.comment() ? ": " + *_e.comment() : ".") + )); + } } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 04dc50400..d36540d46 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -192,7 +192,8 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) bool atTopOfStack = true; for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex) { - auto& var = boost::get(m_scope->identifiers.at(_varDecl.variables[varIndex].name)); + YulString varName = _varDecl.variables[varIndex].name; + auto& var = boost::get(m_scope->identifiers.at(varName)); m_context->variableStackHeights[&var] = height + varIndex; if (!m_allowStackOpt) continue; @@ -217,7 +218,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) m_unusedStackSlots.erase(m_unusedStackSlots.begin()); m_context->variableStackHeights[&var] = slot; 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::Instruction::POP); --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 // 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)); else // 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) 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)) if (stackLayout.back() < 0) { @@ -711,7 +719,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) if (auto var = m_scope->lookup(_variableName.name)) { Scope::Variable const& _var = boost::get(*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::Instruction::POP); 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), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; - if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) - { - solUnimplemented( - "Variable inaccessible, too deep inside stack (" + to_string(heightDiff) + ")" - ); - return 0; - } - else - return heightDiff; + solAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable."); + int limit = _forSwap ? 17 : 16; + if (heightDiff > limit) + BOOST_THROW_EXCEPTION(StackTooDeepError() << errinfo_comment( + "Variable " + + _varName.str() + + " is " + + to_string(heightDiff - limit) + + " slot(s) too deep inside the stack." + )); + return heightDiff; } void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 7be6f892c..511fa73d7 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -40,6 +40,8 @@ namespace yul struct AsmAnalysisInfo; class EVMAssembly; +struct StackTooDeepError: virtual YulException {}; + struct CodeTransformContext { std::map labelIDs; @@ -85,6 +87,10 @@ class CodeTransform: public boost::static_visitor<> public: /// Create the code transformer. /// @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( AbstractAssembly& _assembly, AsmAnalysisInfo& _analysisInfo, @@ -172,7 +178,7 @@ private: /// 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 /// 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; diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp index 3f7634b23..dfeb7bb3a 100644 --- a/libyul/backends/evm/EVMObjectCompiler.cpp +++ b/libyul/backends/evm/EVMObjectCompiler.cpp @@ -60,5 +60,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize) yulAssert(_object.analysisInfo, "No analysis info."); 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); }