diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 2d5cdde87fd0..71bf19458b86 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -51,10 +51,10 @@ using namespace std; StackLayout StackLayoutGenerator::run(CFG const& _cfg) { StackLayout stackLayout; - StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry); + StackLayoutGenerator{stackLayout, nullptr}.processEntryPoint(*_cfg.entry); for (auto& functionInfo: _cfg.functionInfo | ranges::views::values) - StackLayoutGenerator{stackLayout}.processEntryPoint(*functionInfo.entry, &functionInfo); + StackLayoutGenerator{stackLayout, &functionInfo}.processEntryPoint(*functionInfo.entry, &functionInfo); return stackLayout; } @@ -83,13 +83,15 @@ vector StackLayoutGenerator::reportStackTooD yulAssert(functionInfo, "Function not found."); } - StackLayoutGenerator generator{stackLayout}; + StackLayoutGenerator generator{stackLayout, functionInfo}; CFG::BasicBlock const* entry = functionInfo ? functionInfo->entry : _cfg.entry; generator.processEntryPoint(*entry); return generator.reportStackTooDeep(*entry); } -StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout): m_layout(_layout) +StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout, CFG::FunctionInfo const* _functionInfo): + m_layout(_layout), + m_currentFunctionInfo(_functionInfo) { } @@ -740,7 +742,7 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi }); }; /// @returns the number of operations required to transform @a _source to @a _target. - auto evaluateTransform = [](Stack _source, Stack const& _target) -> size_t { + auto evaluateTransform = [&](Stack _source, Stack const& _target) -> size_t { size_t opGas = 0; auto swap = [&](unsigned _swapDepth) { @@ -755,12 +757,23 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32), langutil::EVMVersion()); else { - auto depth = util::findOffset(_source | ranges::views::reverse, _slot); - yulAssert(depth); - if (*depth < 16) - opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast(*depth + 1)), langutil::EVMVersion()); + if (auto depth = util::findOffset(_source | ranges::views::reverse, _slot)) + { + if (*depth < 16) + opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast(*depth + 1)), langutil::EVMVersion()); + else + opGas += 1000; + } else - opGas += 1000; + { + // This has to be a previously unassigned return variable. + // We at least sanity-check that it is among the return variables at all. + yulAssert(m_currentFunctionInfo && holds_alternative(_slot)); + yulAssert(util::contains(m_currentFunctionInfo->returnVariables, get(_slot))); + // Strictly speaking the cost of the PUSH0 depends on the targeted EVM version, but the difference + // will not matter here. + opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(0), langutil::EVMVersion());; + } } }; auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP,langutil::EVMVersion()); }; diff --git a/libyul/backends/evm/StackLayoutGenerator.h b/libyul/backends/evm/StackLayoutGenerator.h index 40b554cd6038..d04b80bdc02a 100644 --- a/libyul/backends/evm/StackLayoutGenerator.h +++ b/libyul/backends/evm/StackLayoutGenerator.h @@ -66,7 +66,7 @@ class StackLayoutGenerator static std::vector reportStackTooDeep(CFG const& _cfg, YulString _functionName); private: - StackLayoutGenerator(StackLayout& _context); + StackLayoutGenerator(StackLayout& _context, CFG::FunctionInfo const* _functionInfo); /// @returns the optimal entry stack layout, s.t. @a _operation can be applied to it and /// the result can be transformed to @a _exitStack with minimal stack shuffling. @@ -115,6 +115,7 @@ class StackLayoutGenerator void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr); StackLayout& m_layout; + CFG::FunctionInfo const* m_currentFunctionInfo = nullptr; }; } diff --git a/test/libyul/evmCodeTransform/unassigned_return_variable.yul b/test/libyul/evmCodeTransform/unassigned_return_variable.yul new file mode 100644 index 000000000000..666fefe1aa64 --- /dev/null +++ b/test/libyul/evmCodeTransform/unassigned_return_variable.yul @@ -0,0 +1,19 @@ +{ + // This used to throw during stack layout generation. + function g(b,s) -> y { + y := g(b, g(y, s)) + } +} +// ==== +// stackOptimization: true +// ---- +// /* "":0:111 */ +// stop +// /* "":60:109 */ +// tag_1: +// pop +// /* "":99:100 */ +// 0x00 +// /* "":97:104 */ +// tag_1 +// jump // in