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 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::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)
{
}
@ -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<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()); };

View File

@ -66,7 +66,7 @@ public:
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.
@ -115,6 +115,7 @@ private:
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
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