Account for unassigned return variables in stack layout generation.

This commit is contained in:
Daniel Kirchner 2023-06-13 15:21:26 +02:00
parent e44b8b9097
commit aa01223398
3 changed files with 44 additions and 11 deletions

View File

@ -51,10 +51,10 @@ using namespace std;
StackLayout StackLayoutGenerator::run(CFG const& _cfg) StackLayout StackLayoutGenerator::run(CFG const& _cfg)
{ {
StackLayout stackLayout; StackLayout stackLayout;
StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry); StackLayoutGenerator{stackLayout, nullptr}.processEntryPoint(*_cfg.entry);
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values) for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
StackLayoutGenerator{stackLayout}.processEntryPoint(*functionInfo.entry, &functionInfo); StackLayoutGenerator{stackLayout, &functionInfo}.processEntryPoint(*functionInfo.entry, &functionInfo);
return stackLayout; return stackLayout;
} }
@ -83,13 +83,15 @@ vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooD
yulAssert(functionInfo, "Function not found."); yulAssert(functionInfo, "Function not found.");
} }
StackLayoutGenerator generator{stackLayout}; StackLayoutGenerator generator{stackLayout, functionInfo};
CFG::BasicBlock const* entry = functionInfo ? functionInfo->entry : _cfg.entry; CFG::BasicBlock const* entry = functionInfo ? functionInfo->entry : _cfg.entry;
generator.processEntryPoint(*entry); generator.processEntryPoint(*entry);
return generator.reportStackTooDeep(*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. /// @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; size_t opGas = 0;
auto swap = [&](unsigned _swapDepth) 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()); opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32), langutil::EVMVersion());
else else
{ {
auto depth = util::findOffset(_source | ranges::views::reverse, _slot); if (auto depth = util::findOffset(_source | ranges::views::reverse, _slot))
yulAssert(depth); {
if (*depth < 16) if (*depth < 16)
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)), langutil::EVMVersion()); opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)), langutil::EVMVersion());
else
opGas += 1000;
}
else 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()); }; auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP,langutil::EVMVersion()); };

View File

@ -66,7 +66,7 @@ public:
static std::vector<StackTooDeep> reportStackTooDeep(CFG const& _cfg, YulString _functionName); static std::vector<StackTooDeep> reportStackTooDeep(CFG const& _cfg, YulString _functionName);
private: 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 /// @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. /// the result can be transformed to @a _exitStack with minimal stack shuffling.
@ -115,6 +115,7 @@ private:
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr); void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
StackLayout& m_layout; StackLayout& m_layout;
CFG::FunctionInfo const* m_currentFunctionInfo = nullptr;
}; };
} }

View File

@ -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