diff --git a/Changelog.md b/Changelog.md index a8db61f6e..10c65182c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * LSP: Add rudimentary support for semantic highlighting. + * Yul Optimizer: Improve inlining heuristics for via IR code generation and pure Yul compilation. Bugfixes: diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 61e2800b8..f4a8aee85 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -22,11 +22,14 @@ #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -46,8 +49,12 @@ void FullInliner::run(OptimiserStepContext& _context, Block& _ast) } FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect): - m_ast(_ast), m_nameDispenser(_dispenser), m_dialect(_dialect) + m_ast(_ast), + m_recursiveFunctions(CallGraphGenerator::callGraph(_ast).recursiveFunctions()), + m_nameDispenser(_dispenser), + m_dialect(_dialect) { + // Determine constants SSAValueTracker tracker; tracker(m_ast); @@ -71,6 +78,15 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& m_singleUse.emplace(fun.name); updateCodeSize(fun); } + + // Check for memory guard. + vector memoryGuardCalls = FunctionCallFinder::run( + _ast, + "memoryguard"_yulstring + ); + // We will perform less aggressive inlining, if no ``memoryguard`` call is found. + if (!memoryGuardCalls.empty()) + m_hasMemoryGuard = true; } void FullInliner::run(Pass _pass) @@ -177,8 +193,20 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) if (m_pass == Pass::InlineTiny) return false; - // Do not inline into already big functions. - if (m_functionSizes.at(_callSite) > 45) + bool aggressiveInlining = true; + + if ( + EVMDialect const* evmDialect = dynamic_cast(&m_dialect); + !evmDialect || !evmDialect->providesObjectAccess() || evmDialect->evmVersion() <= langutil::EVMVersion::homestead() + ) + // No aggressive inlining with the old code transform. + aggressiveInlining = false; + + // No aggressive inlining, if we cannot perform stack-to-memory. + if (!m_hasMemoryGuard || m_recursiveFunctions.count(_callSite)) + aggressiveInlining = false; + + if (!aggressiveInlining && m_functionSizes.at(_callSite) > 45) return false; if (m_singleUse.count(calledFunction->name)) @@ -196,7 +224,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) break; } - return (size < 6 || (constantArg && size < 12)); + return (size < (aggressiveInlining ? 8 : 6) || (constantArg && size < (aggressiveInlining ? 16 : 12))); } void FullInliner::tentativelyUpdateCodeSize(YulString _function, YulString _callSite) diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index a743bf323..5029abf39 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -111,6 +111,10 @@ private: std::map m_functions; /// Functions not to be inlined (because they contain the ``leave`` statement). std::set m_noInlineFunctions; + /// True, if the code contains a ``memoryguard`` and we can expect to be able to move variables to memory later. + bool m_hasMemoryGuard = false; + /// Set of recursive functions. + std::set m_recursiveFunctions; /// Names of functions to always inline. std::set m_singleUse; /// Variables that are constants (used for inlining heuristic)