Relax inliner heuristics.

This commit is contained in:
Daniel Kirchner 2022-03-02 15:52:22 +01:00 committed by chriseth
parent f08d349791
commit 7168c27f0d
3 changed files with 37 additions and 4 deletions

View File

@ -7,6 +7,7 @@ Language Features:
Compiler Features: Compiler Features:
* LSP: Add rudimentary support for semantic highlighting. * LSP: Add rudimentary support for semantic highlighting.
* Yul Optimizer: Improve inlining heuristics for via IR code generation and pure Yul compilation.
Bugfixes: Bugfixes:

View File

@ -22,11 +22,14 @@
#include <libyul/optimiser/FullInliner.h> #include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTCopier.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/SSAValueTracker.h> #include <libyul/optimiser/SSAValueTracker.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/CallGraphGenerator.h> #include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
@ -46,8 +49,12 @@ void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
} }
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect): 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 // Determine constants
SSAValueTracker tracker; SSAValueTracker tracker;
tracker(m_ast); tracker(m_ast);
@ -71,6 +78,15 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const&
m_singleUse.emplace(fun.name); m_singleUse.emplace(fun.name);
updateCodeSize(fun); updateCodeSize(fun);
} }
// Check for memory guard.
vector<FunctionCall*> 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) void FullInliner::run(Pass _pass)
@ -177,8 +193,20 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
if (m_pass == Pass::InlineTiny) if (m_pass == Pass::InlineTiny)
return false; return false;
// Do not inline into already big functions. bool aggressiveInlining = true;
if (m_functionSizes.at(_callSite) > 45)
if (
EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&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; return false;
if (m_singleUse.count(calledFunction->name)) if (m_singleUse.count(calledFunction->name))
@ -196,7 +224,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
break; break;
} }
return (size < 6 || (constantArg && size < 12)); return (size < (aggressiveInlining ? 8 : 6) || (constantArg && size < (aggressiveInlining ? 16 : 12)));
} }
void FullInliner::tentativelyUpdateCodeSize(YulString _function, YulString _callSite) void FullInliner::tentativelyUpdateCodeSize(YulString _function, YulString _callSite)

View File

@ -111,6 +111,10 @@ private:
std::map<YulString, FunctionDefinition*> m_functions; std::map<YulString, FunctionDefinition*> m_functions;
/// Functions not to be inlined (because they contain the ``leave`` statement). /// Functions not to be inlined (because they contain the ``leave`` statement).
std::set<YulString> m_noInlineFunctions; std::set<YulString> 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<YulString> m_recursiveFunctions;
/// Names of functions to always inline. /// Names of functions to always inline.
std::set<YulString> m_singleUse; std::set<YulString> m_singleUse;
/// Variables that are constants (used for inlining heuristic) /// Variables that are constants (used for inlining heuristic)