Skip to content

Commit

Permalink
Merge pull request ethereum#14322 from ethereum/stackLayoutsForUnassi…
Browse files Browse the repository at this point in the history
…gnedReturnVariables

Account for unassigned return variables in stack layout generation.
  • Loading branch information
ekpyron authored Jun 14, 2023
2 parents c2e9736 + aa01223 commit ca8db58
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 11 deletions.
33 changes: 23 additions & 10 deletions libyul/backends/evm/StackLayoutGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -83,13 +83,15 @@ vector<StackLayoutGenerator::StackTooDeep> 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)
{
}

Expand Down Expand Up @@ -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)
{
Expand All @@ -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<unsigned>(*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<unsigned>(*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<VariableSlot>(_slot));
yulAssert(util::contains(m_currentFunctionInfo->returnVariables, get<VariableSlot>(_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()); };
Expand Down
3 changes: 2 additions & 1 deletion libyul/backends/evm/StackLayoutGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class StackLayoutGenerator
static std::vector<StackTooDeep> 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.
Expand Down Expand Up @@ -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;
};

}
19 changes: 19 additions & 0 deletions test/libyul/evmCodeTransform/unassigned_return_variable.yul
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ca8db58

Please sign in to comment.