Skip to content

Commit

Permalink
Move simulateFunctionsWithJumps flag
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Nov 29, 2024
1 parent d133602 commit 182293d
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 38 deletions.
4 changes: 1 addition & 3 deletions libyul/backends/evm/ControlFlowGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ inline bool canBeFreelyGenerated(StackSlot const& _slot)
/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow.
struct CFG
{
explicit CFG(bool _simulateFunctionsWithJumps): simulateFunctionsWithJumps(_simulateFunctionsWithJumps) {}
explicit CFG() {}
CFG(CFG const&) = delete;
CFG(CFG&&) = delete;
CFG& operator=(CFG const&) = delete;
Expand Down Expand Up @@ -220,8 +220,6 @@ struct CFG
bool canContinue = true;
};

/// True if control flow graph simulates functions with jumps. False otherwise. True for legacy bytecode
bool simulateFunctionsWithJumps = true;
/// The main entry point, i.e. the start of the outermost Yul block.
BasicBlock* entry = nullptr;
/// Subgraphs for functions.
Expand Down
8 changes: 5 additions & 3 deletions libyul/backends/evm/ControlFlowGraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
eofVersion = evmDialect->eofVersion();

auto result = std::make_unique<CFG>(!eofVersion.has_value());
auto result = std::make_unique<CFG>();
result->entry = &result->makeBlock(debugDataOf(_block));

ControlFlowSideEffectsCollector sideEffects(_dialect, _block);
Expand Down Expand Up @@ -246,6 +246,8 @@ ControlFlowGraphBuilder::ControlFlowGraphBuilder(
m_functionSideEffects(_functionSideEffects),
m_dialect(_dialect)
{
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
m_simulateFunctionsWithJumps = !evmDialect->eofVersion().has_value();
}

StackSlot ControlFlowGraphBuilder::operator()(Literal const& _literal)
Expand Down Expand Up @@ -547,8 +549,8 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
Scope::Function const& function = lookupFunction(_call.functionName.name);
canContinue = m_graph.functionInfo.at(&function).canContinue;
Stack inputs;
// For EOF (simulateFunctionsWithJumps == false) we do not have to put return label on stack.
if (m_graph.simulateFunctionsWithJumps && canContinue)
// For EOF (m_simulateFunctionsWithJumps == false) we do not have to put return label on stack.
if (m_simulateFunctionsWithJumps && canContinue)
inputs.emplace_back(FunctionCallReturnLabelSlot{_call});
for (auto const& arg: _call.arguments | ranges::views::reverse)
inputs.emplace_back(std::visit(*this, arg));
Expand Down
2 changes: 2 additions & 0 deletions libyul/backends/evm/ControlFlowGraphBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class ControlFlowGraphBuilder
};
std::optional<ForLoopInfo> m_forLoopInfo;
std::optional<CFG::FunctionInfo*> m_currentFunction;
/// True if control flow graph simulates functions with jumps. False otherwise. True for legacy bytecode
bool m_simulateFunctionsWithJumps = true;
};

}
36 changes: 19 additions & 17 deletions libyul/backends/evm/OptimizedEVMCodeTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ std::vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
)
{
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
yulAssert(_dialect.eofVersion().has_value() != dfg->simulateFunctionsWithJumps);
StackLayout stackLayout = StackLayoutGenerator::run(*dfg);
StackLayout stackLayout = StackLayoutGenerator::run(*dfg, !_dialect.eofVersion().has_value());

if (!dfg->simulateFunctionsWithJumps)
if (_dialect.eofVersion().has_value())
{
for (Scope::Function const* function: dfg->functions)
{
Expand All @@ -73,7 +72,8 @@ std::vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
_builtinContext,
_useNamedLabelsForFunctions,
*dfg,
stackLayout
stackLayout,
!_dialect.eofVersion().has_value()
);
// Create initial entry layout.
optimizedCodeTransform.createStackLayout(debugDataOf(*dfg->entry), stackLayout.blockInfos.at(dfg->entry).entryLayout);
Expand All @@ -85,7 +85,7 @@ std::vector<StackTooDeepError> OptimizedEVMCodeTransform::run(

void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
{
bool useReturnLabel = m_dfg.simulateFunctionsWithJumps && _call.canContinue;
bool useReturnLabel = m_simulateFunctionsWithJumps && _call.canContinue;
// Validate stack.
{
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
Expand All @@ -109,7 +109,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
// Emit code.
{
m_assembly.setSourceLocation(originLocationOf(_call));
if (!m_dfg.simulateFunctionsWithJumps)
if (!m_simulateFunctionsWithJumps)
m_assembly.appendFunctionCall(m_builtinContext.functionIDs.at(&_call.function.get()));
else
m_assembly.appendJumpTo(
Expand All @@ -127,7 +127,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
for (size_t i = 0; i < _call.function.get().numArguments + (useReturnLabel ? 1 : 0); ++i)
m_stack.pop_back();
// Push return values to m_stack.
if (!m_dfg.simulateFunctionsWithJumps)
if (!m_simulateFunctionsWithJumps)
yulAssert(_call.function.get().numReturns < 0x80, "Num of function output >= 128");
for (size_t index: ranges::views::iota(0u, _call.function.get().numReturns))
m_stack.emplace_back(TemporarySlot{_call.functionCall, index});
Expand Down Expand Up @@ -201,13 +201,14 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg,
StackLayout const& _stackLayout
StackLayout const& _stackLayout,
bool _simulateFunctionsWithJumps
):
m_assembly(_assembly),
m_builtinContext(_builtinContext),
m_dfg(_dfg),
m_stackLayout(_stackLayout),
m_functionLabels(!_dfg.simulateFunctionsWithJumps ? decltype(m_functionLabels)() : [&](){
m_functionLabels(!_simulateFunctionsWithJumps ? decltype(m_functionLabels)() : [&](){
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
std::set<YulName> assignedFunctionNames;
for (Scope::Function const* function: m_dfg.functions)
Expand All @@ -227,7 +228,8 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
m_assembly.newLabelId();
}
return functionLabels;
}())
}()),
m_simulateFunctionsWithJumps(_simulateFunctionsWithJumps)
{
}

Expand All @@ -240,7 +242,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS

AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
{
yulAssert(m_dfg.simulateFunctionsWithJumps);
yulAssert(m_simulateFunctionsWithJumps);
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
}

Expand Down Expand Up @@ -518,12 +520,12 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
return StackSlot{_varSlot};
}) | ranges::to<Stack>;
if (m_dfg.simulateFunctionsWithJumps)
if (m_simulateFunctionsWithJumps)
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});

// Create the function return layout and jump.
createStackLayout(debugDataOf(_functionReturn), exitStack);
if (!m_dfg.simulateFunctionsWithJumps)
if (!m_simulateFunctionsWithJumps)
m_assembly.appendFunctionReturn();
else
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
Expand All @@ -547,7 +549,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)

void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
{
bool useReturnLabel = m_dfg.simulateFunctionsWithJumps && _functionInfo.canContinue;
bool useReturnLabel = m_simulateFunctionsWithJumps && _functionInfo.canContinue;
yulAssert(!m_currentFunctionInfo, "");
ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);

Expand All @@ -558,20 +560,20 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInf
m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
m_stack.emplace_back(param);
if (!m_dfg.simulateFunctionsWithJumps)
if (!m_simulateFunctionsWithJumps)
m_assembly.beginFunction(m_builtinContext.functionIDs[&_functionInfo.function]);
m_assembly.setStackHeight(static_cast<int>(m_stack.size()));

m_assembly.setSourceLocation(originLocationOf(_functionInfo));
if (m_dfg.simulateFunctionsWithJumps)
if (m_simulateFunctionsWithJumps)
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));

// Create the entry layout of the function body block and visit.
createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout);
(*this)(*_functionInfo.entry);

m_stack.clear();
if (!m_dfg.simulateFunctionsWithJumps)
if (!m_simulateFunctionsWithJumps)
m_assembly.endFunction();
m_assembly.setStackHeight(0);
}
5 changes: 4 additions & 1 deletion libyul/backends/evm/OptimizedEVMCodeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ class OptimizedEVMCodeTransform
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg,
StackLayout const& _stackLayout
StackLayout const& _stackLayout,
bool _simulateFunctionsWithJumps
);

/// Assert that it is valid to transition from @a _currentStack to @a _desiredStack.
Expand Down Expand Up @@ -111,6 +112,8 @@ class OptimizedEVMCodeTransform
std::set<CFG::BasicBlock const*> m_generated;
CFG::FunctionInfo const* m_currentFunctionInfo = nullptr;
std::vector<StackTooDeepError> m_stackErrors;
/// True if it simulates functions with jumps. False otherwise. True for legacy bytecode
bool m_simulateFunctionsWithJumps = true;
};

}
16 changes: 8 additions & 8 deletions libyul/backends/evm/StackLayoutGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,28 @@
using namespace solidity;
using namespace solidity::yul;

StackLayout StackLayoutGenerator::run(CFG const& _cfg)
StackLayout StackLayoutGenerator::run(CFG const& _cfg, bool _simulateFunctionsWithJumps)
{
StackLayout stackLayout{{}, {}};
StackLayoutGenerator{stackLayout, nullptr, _cfg.simulateFunctionsWithJumps}.processEntryPoint(*_cfg.entry);
StackLayoutGenerator{stackLayout, nullptr, _simulateFunctionsWithJumps}.processEntryPoint(*_cfg.entry);

for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
StackLayoutGenerator{stackLayout, &functionInfo, _cfg.simulateFunctionsWithJumps}.processEntryPoint(*functionInfo.entry, &functionInfo);
StackLayoutGenerator{stackLayout, &functionInfo, _simulateFunctionsWithJumps}.processEntryPoint(*functionInfo.entry, &functionInfo);

return stackLayout;
}

std::map<YulName, std::vector<StackLayoutGenerator::StackTooDeep>> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg)
std::map<YulName, std::vector<StackLayoutGenerator::StackTooDeep>> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, bool _simulateFunctionsWithJumps)
{
std::map<YulName, std::vector<StackLayoutGenerator::StackTooDeep>> stackTooDeepErrors;
stackTooDeepErrors[YulName{}] = reportStackTooDeep(_cfg, YulName{});
stackTooDeepErrors[YulName{}] = reportStackTooDeep(_cfg, YulName{}, _simulateFunctionsWithJumps);
for (auto const& function: _cfg.functions)
if (auto errors = reportStackTooDeep(_cfg, function->name); !errors.empty())
if (auto errors = reportStackTooDeep(_cfg, function->name, _simulateFunctionsWithJumps); !errors.empty())
stackTooDeepErrors[function->name] = std::move(errors);
return stackTooDeepErrors;
}

std::vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulName _functionName)
std::vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulName _functionName, bool _simulateFunctionsWithJumps)
{
StackLayout stackLayout{{}, {}};
CFG::FunctionInfo const* functionInfo = nullptr;
Expand All @@ -82,7 +82,7 @@ std::vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStac
yulAssert(functionInfo, "Function not found.");
}

StackLayoutGenerator generator{stackLayout, functionInfo, _cfg.simulateFunctionsWithJumps};
StackLayoutGenerator generator{stackLayout, functionInfo, _simulateFunctionsWithJumps};
CFG::BasicBlock const* entry = functionInfo ? functionInfo->entry : _cfg.entry;
generator.processEntryPoint(*entry);
return generator.reportStackTooDeep(*entry);
Expand Down
7 changes: 4 additions & 3 deletions libyul/backends/evm/StackLayoutGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ class StackLayoutGenerator
std::vector<YulName> variableChoices;
};

static StackLayout run(CFG const& _cfg);
static StackLayout run(CFG const& _cfg, bool _simulateFunctionsWithJumps);
/// @returns a map from function names to the stack too deep errors occurring in that function.
/// Requires @a _cfg to be a control flow graph generated from disambiguated Yul.
/// The empty string is mapped to the stack too deep errors of the main entry point.
static std::map<YulName, std::vector<StackTooDeep>> reportStackTooDeep(CFG const& _cfg);
static std::map<YulName, std::vector<StackTooDeep>> reportStackTooDeep(CFG const& _cfg, bool _simulateFunctionsWithJumps);
/// @returns all stack too deep errors in the function named @a _functionName.
/// Requires @a _cfg to be a control flow graph generated from disambiguated Yul.
/// If @a _functionName is empty, the stack too deep errors of the main entry point are reported instead.
static std::vector<StackTooDeep> reportStackTooDeep(CFG const& _cfg, YulName _functionName);
static std::vector<StackTooDeep> reportStackTooDeep(CFG const& _cfg, YulName _functionName, bool _simulateFunctionsWithJumps);

private:
StackLayoutGenerator(StackLayout& _context, CFG::FunctionInfo const* _functionInfo, bool _simulateFunctionsWithJumps);
Expand Down Expand Up @@ -116,6 +116,7 @@ class StackLayoutGenerator

StackLayout& m_layout;
CFG::FunctionInfo const* m_currentFunctionInfo = nullptr;
/// True if it simulates functions with jumps. False otherwise. True for legacy bytecode
bool m_simulateFunctionsWithJumps = true;
};

Expand Down
6 changes: 5 additions & 1 deletion libyul/optimiser/StackCompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,15 @@ std::tuple<bool, Block> StackCompressor::run(
"Need to run the function grouper before the stack compressor."
);
bool usesOptimizedCodeGenerator = false;
bool simulateFunctionsWithJumps = true;
if (auto evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
{
usesOptimizedCodeGenerator =
_optimizeStackAllocation &&
evmDialect->evmVersion().canOverchargeGasForCall() &&
evmDialect->providesObjectAccess();
simulateFunctionsWithJumps = !evmDialect->eofVersion().has_value();
}
bool allowMSizeOptimization = !MSizeFinder::containsMSize(_dialect, _object.code()->root());
Block astRoot = std::get<Block>(ASTCopier{}(_object.code()->root()));
if (usesOptimizedCodeGenerator)
Expand All @@ -266,7 +270,7 @@ std::tuple<bool, Block> StackCompressor::run(
eliminateVariablesOptimizedCodegen(
_dialect,
astRoot,
StackLayoutGenerator::reportStackTooDeep(*cfg),
StackLayoutGenerator::reportStackTooDeep(*cfg, simulateFunctionsWithJumps),
allowMSizeOptimization
);
}
Expand Down
2 changes: 1 addition & 1 deletion libyul/optimiser/StackLimitEvader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Block StackLimitEvader::run(
_object.summarizeStructure()
);
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, astRoot);
run(_context, astRoot, StackLayoutGenerator::reportStackTooDeep(*cfg));
run(_context, astRoot, StackLayoutGenerator::reportStackTooDeep(*cfg, !evmDialect->eofVersion().has_value()));
}
else
{
Expand Down
8 changes: 7 additions & 1 deletion test/libyul/StackLayoutGeneratorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
#include <libyul/backends/evm/StackHelpers.h>
#include <libyul/backends/evm/StackLayoutGenerator.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/Object.h>
#include <liblangutil/SourceReferenceFormatter.h>

Expand Down Expand Up @@ -229,7 +230,12 @@ TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& _stream, std::s
std::ostringstream output;

std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, object->code()->root());
StackLayout stackLayout = StackLayoutGenerator::run(*cfg);

bool simulateFunctionsWithJumps = true;
if (auto const* evmDialect = dynamic_cast<EVMDialect const*>(m_dialect))
simulateFunctionsWithJumps = !evmDialect->eofVersion().has_value();

StackLayout stackLayout = StackLayoutGenerator::run(*cfg, simulateFunctionsWithJumps);

output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
StackLayoutPrinter printer{output, stackLayout};
Expand Down

0 comments on commit 182293d

Please sign in to comment.