From 3a4ffc37c69b65aa9258de88ae498f308949f756 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Fri, 29 Nov 2024 14:28:17 +0100 Subject: [PATCH] Move `simulateFunctionsWithJumps` flag --- libyul/backends/evm/ControlFlowGraph.h | 4 +-- .../backends/evm/ControlFlowGraphBuilder.cpp | 8 +++-- libyul/backends/evm/ControlFlowGraphBuilder.h | 2 ++ .../evm/OptimizedEVMCodeTransform.cpp | 36 ++++++++++--------- .../backends/evm/OptimizedEVMCodeTransform.h | 5 ++- libyul/backends/evm/StackLayoutGenerator.cpp | 16 ++++----- libyul/backends/evm/StackLayoutGenerator.h | 7 ++-- libyul/optimiser/StackCompressor.cpp | 6 +++- libyul/optimiser/StackLimitEvader.cpp | 2 +- test/libyul/StackLayoutGeneratorTest.cpp | 8 ++++- 10 files changed, 56 insertions(+), 38 deletions(-) diff --git a/libyul/backends/evm/ControlFlowGraph.h b/libyul/backends/evm/ControlFlowGraph.h index 0c1059e84f90..980787c52297 100644 --- a/libyul/backends/evm/ControlFlowGraph.h +++ b/libyul/backends/evm/ControlFlowGraph.h @@ -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; @@ -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. diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp index 568042d80b0f..e92515e988ad 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.cpp +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -216,7 +216,7 @@ std::unique_ptr ControlFlowGraphBuilder::build( if (EVMDialect const* evmDialect = dynamic_cast(&_dialect)) eofVersion = evmDialect->eofVersion(); - auto result = std::make_unique(!eofVersion.has_value()); + auto result = std::make_unique(); result->entry = &result->makeBlock(debugDataOf(_block)); ControlFlowSideEffectsCollector sideEffects(_dialect, _block); @@ -246,6 +246,8 @@ ControlFlowGraphBuilder::ControlFlowGraphBuilder( m_functionSideEffects(_functionSideEffects), m_dialect(_dialect) { + if (EVMDialect const* evmDialect = dynamic_cast(&m_dialect)) + m_simulateFunctionsWithJumps = !evmDialect->eofVersion().has_value(); } StackSlot ControlFlowGraphBuilder::operator()(Literal const& _literal) @@ -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)); diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h index 77feaea7b469..1d0227a56b71 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.h +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -90,6 +90,8 @@ class ControlFlowGraphBuilder }; std::optional m_forLoopInfo; std::optional m_currentFunction; + /// True if control flow graph simulates functions with jumps. False otherwise. True for legacy bytecode + bool m_simulateFunctionsWithJumps = true; }; } diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp index 22a7098247f8..89a75019ec31 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -49,10 +49,9 @@ std::vector OptimizedEVMCodeTransform::run( ) { std::unique_ptr 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) { @@ -73,7 +72,8 @@ std::vector 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); @@ -85,7 +85,7 @@ std::vector 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(m_stack.size()), ""); @@ -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( @@ -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}); @@ -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 functionLabels; std::set assignedFunctionNames; for (Scope::Function const* function: m_dfg.functions) @@ -227,7 +228,8 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform( m_assembly.newLabelId(); } return functionLabels; - }()) + }()), + m_simulateFunctionsWithJumps(_simulateFunctionsWithJumps) { } @@ -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)); } @@ -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; - 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); @@ -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); @@ -558,12 +560,12 @@ 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(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. @@ -571,7 +573,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInf (*this)(*_functionInfo.entry); m_stack.clear(); - if (!m_dfg.simulateFunctionsWithJumps) + if (!m_simulateFunctionsWithJumps) m_assembly.endFunction(); m_assembly.setStackHeight(0); } diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.h b/libyul/backends/evm/OptimizedEVMCodeTransform.h index cb6cfb0be4b9..2c072bcca2f6 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.h +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.h @@ -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. @@ -111,6 +112,8 @@ class OptimizedEVMCodeTransform std::set m_generated; CFG::FunctionInfo const* m_currentFunctionInfo = nullptr; std::vector m_stackErrors; + /// True if it simulates functions with jumps. False otherwise. True for legacy bytecode + bool m_simulateFunctionsWithJumps = true; }; } diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 56afa4cbf813..3d1d5f7b0d76 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -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> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg) +std::map> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, bool _simulateFunctionsWithJumps) { std::map> 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::reportStackTooDeep(CFG const& _cfg, YulName _functionName) +std::vector StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulName _functionName, bool _simulateFunctionsWithJumps) { StackLayout stackLayout{{}, {}}; CFG::FunctionInfo const* functionInfo = nullptr; @@ -82,7 +82,7 @@ std::vector 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); diff --git a/libyul/backends/evm/StackLayoutGenerator.h b/libyul/backends/evm/StackLayoutGenerator.h index ffdc9a2d845d..44cbbd43a4b3 100644 --- a/libyul/backends/evm/StackLayoutGenerator.h +++ b/libyul/backends/evm/StackLayoutGenerator.h @@ -55,15 +55,15 @@ class StackLayoutGenerator std::vector 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> reportStackTooDeep(CFG const& _cfg); + static std::map> 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 reportStackTooDeep(CFG const& _cfg, YulName _functionName); + static std::vector reportStackTooDeep(CFG const& _cfg, YulName _functionName, bool _simulateFunctionsWithJumps); private: StackLayoutGenerator(StackLayout& _context, CFG::FunctionInfo const* _functionInfo, bool _simulateFunctionsWithJumps); @@ -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; }; diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 6ad3899e96c5..71a0463c9b17 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -248,11 +248,15 @@ std::tuple StackCompressor::run( "Need to run the function grouper before the stack compressor." ); bool usesOptimizedCodeGenerator = false; + bool simulateFunctionsWithJumps = true; if (auto evmDialect = dynamic_cast(&_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(ASTCopier{}(_object.code()->root())); if (usesOptimizedCodeGenerator) @@ -266,7 +270,7 @@ std::tuple StackCompressor::run( eliminateVariablesOptimizedCodegen( _dialect, astRoot, - StackLayoutGenerator::reportStackTooDeep(*cfg), + StackLayoutGenerator::reportStackTooDeep(*cfg, simulateFunctionsWithJumps), allowMSizeOptimization ); } diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp index 6ef4b267b67e..02c6de1594e3 100644 --- a/libyul/optimiser/StackLimitEvader.cpp +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -138,7 +138,7 @@ Block StackLimitEvader::run( _object.summarizeStructure() ); std::unique_ptr cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, astRoot); - run(_context, astRoot, StackLayoutGenerator::reportStackTooDeep(*cfg)); + run(_context, astRoot, StackLayoutGenerator::reportStackTooDeep(*cfg, !evmDialect->eofVersion().has_value())); } else { diff --git a/test/libyul/StackLayoutGeneratorTest.cpp b/test/libyul/StackLayoutGeneratorTest.cpp index be4824492f88..49af089ef82f 100644 --- a/test/libyul/StackLayoutGeneratorTest.cpp +++ b/test/libyul/StackLayoutGeneratorTest.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -229,7 +230,12 @@ TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& _stream, std::s std::ostringstream output; std::unique_ptr cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, object->code()->root()); - StackLayout stackLayout = StackLayoutGenerator::run(*cfg); + + bool simulateFunctionsWithJumps = true; + if (auto const* evmDialect = dynamic_cast(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};