Take user function side-effects into account for unused pruner.

This commit is contained in:
chriseth 2019-08-29 15:26:36 +02:00
parent 1c5845e3f2
commit 127bcfc69d
8 changed files with 72 additions and 26 deletions

View File

@ -35,8 +35,12 @@ using namespace dev;
using namespace yul; using namespace yul;
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Expression const& _expression): SideEffectsCollector::SideEffectsCollector(
SideEffectsCollector(_dialect) Dialect const& _dialect,
Expression const& _expression,
map<YulString, SideEffects> const* _functionSideEffects
):
SideEffectsCollector(_dialect, _functionSideEffects)
{ {
visit(_expression); visit(_expression);
} }

View File

@ -41,7 +41,11 @@ public:
Dialect const& _dialect, Dialect const& _dialect,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr std::map<YulString, SideEffects> const* _functionSideEffects = nullptr
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} ): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
SideEffectsCollector(Dialect const& _dialect, Expression const& _expression); SideEffectsCollector(
Dialect const& _dialect,
Expression const& _expression,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr
);
SideEffectsCollector(Dialect const& _dialect, Statement const& _statement); SideEffectsCollector(Dialect const& _dialect, Statement const& _statement);
SideEffectsCollector(Dialect const& _dialect, Block const& _ast); SideEffectsCollector(Dialect const& _dialect, Block const& _ast);

View File

@ -87,7 +87,7 @@ void OptimiserSuite::run(
DeadCodeEliminator{_dialect}(ast); DeadCodeEliminator{_dialect}(ast);
FunctionGrouper{}(ast); FunctionGrouper{}(ast);
EquivalentFunctionCombiner::run(ast); EquivalentFunctionCombiner::run(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
BlockFlattener{}(ast); BlockFlattener{}(ast);
ControlFlowSimplifier{_dialect}(ast); ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{_dialect}(ast); StructuralSimplifier{_dialect}(ast);
@ -127,20 +127,20 @@ void OptimiserSuite::run(
ControlFlowSimplifier{_dialect}(ast); ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast); BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast); DeadCodeEliminator{_dialect}(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
} }
{ {
// simplify again // simplify again
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
} }
{ {
// reverse SSA // reverse SSA
SSAReverser::run(ast); SSAReverser::run(ast);
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
@ -151,7 +151,7 @@ void OptimiserSuite::run(
{ {
// run functional expression inliner // run functional expression inliner
ExpressionInliner(_dialect, ast).run(); ExpressionInliner(_dialect, ast).run();
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
} }
{ {
@ -185,7 +185,7 @@ void OptimiserSuite::run(
SSATransform::run(ast, dispenser); SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::run(_dialect, ast);
} }
} }
@ -194,19 +194,19 @@ void OptimiserSuite::run(
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
Rematerialiser::run(_dialect, ast); Rematerialiser::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
SSAReverser::run(ast); SSAReverser::run(ast);
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast); ExpressionJoiner::run(ast);
Rematerialiser::run(_dialect, ast); Rematerialiser::run(_dialect, ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
// This is a tuning parameter, but actually just prevents infinite loops. // This is a tuning parameter, but actually just prevents infinite loops.
size_t stackCompressorMaxIterations = 16; size_t stackCompressorMaxIterations = 16;

View File

@ -20,12 +20,14 @@
#include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/UnusedPruner.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimizerUtilities.h> #include <libyul/optimiser/OptimizerUtilities.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/SideEffects.h>
#include <boost/algorithm/cxx11/none_of.hpp> #include <boost/algorithm/cxx11/none_of.hpp>
@ -37,10 +39,12 @@ UnusedPruner::UnusedPruner(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptimization, bool _allowMSizeOptimization,
map<YulString, SideEffects> const* _functionSideEffects,
set<YulString> const& _externallyUsedFunctions set<YulString> const& _externallyUsedFunctions
): ):
m_dialect(_dialect), m_dialect(_dialect),
m_allowMSizeOptimization(_allowMSizeOptimization) m_allowMSizeOptimization(_allowMSizeOptimization),
m_functionSideEffects(_functionSideEffects)
{ {
m_references = ReferencesCounter::countReferences(_ast); m_references = ReferencesCounter::countReferences(_ast);
for (auto const& f: _externallyUsedFunctions) for (auto const& f: _externallyUsedFunctions)
@ -88,7 +92,10 @@ void UnusedPruner::operator()(Block& _block)
{ {
if (!varDecl.value) if (!varDecl.value)
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
else if (SideEffectsCollector(m_dialect, *varDecl.value).sideEffectFree(m_allowMSizeOptimization)) else if (
SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects).
sideEffectFree(m_allowMSizeOptimization)
)
{ {
subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
@ -104,7 +111,10 @@ void UnusedPruner::operator()(Block& _block)
else if (statement.type() == typeid(ExpressionStatement)) else if (statement.type() == typeid(ExpressionStatement))
{ {
ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement); ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement);
if (SideEffectsCollector(m_dialect, exprStmt.expression).sideEffectFree(m_allowMSizeOptimization)) if (
SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects).
sideEffectFree(m_allowMSizeOptimization)
)
{ {
subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); subtractReferences(ReferencesCounter::countReferences(exprStmt.expression));
statement = Block{std::move(exprStmt.location), {}}; statement = Block{std::move(exprStmt.location), {}};
@ -120,26 +130,31 @@ void UnusedPruner::runUntilStabilised(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptimization, bool _allowMSizeOptimization,
map<YulString, SideEffects> const* _functionSideEffects,
set<YulString> const& _externallyUsedFunctions set<YulString> const& _externallyUsedFunctions
) )
{ {
while (true) while (true)
{ {
UnusedPruner pruner(_dialect, _ast, _allowMSizeOptimization, _externallyUsedFunctions); UnusedPruner pruner(
_dialect, _ast, _allowMSizeOptimization, _functionSideEffects,
_externallyUsedFunctions);
pruner(_ast); pruner(_ast);
if (!pruner.shouldRunAgain()) if (!pruner.shouldRunAgain())
return; return;
} }
} }
void UnusedPruner::runUntilStabilised( void UnusedPruner::runUntilStabilisedOnFullAST(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
set<YulString> const& _externallyUsedFunctions set<YulString> const& _externallyUsedFunctions
) )
{ {
map<YulString, SideEffects> functionSideEffects =
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast));
bool allowMSizeOptimization = !MSizeFinder::containsMSize(_dialect, _ast); bool allowMSizeOptimization = !MSizeFinder::containsMSize(_dialect, _ast);
runUntilStabilised(_dialect, _ast, allowMSizeOptimization, _externallyUsedFunctions); runUntilStabilised(_dialect, _ast, allowMSizeOptimization, &functionSideEffects, _externallyUsedFunctions);
} }
void UnusedPruner::runUntilStabilised( void UnusedPruner::runUntilStabilised(

View File

@ -29,6 +29,7 @@
namespace yul namespace yul
{ {
struct Dialect; struct Dialect;
struct SideEffects;
/** /**
* Optimisation stage that removes unused variables and functions and also * Optimisation stage that removes unused variables and functions and also
@ -50,6 +51,7 @@ public:
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptimization, bool _allowMSizeOptimization,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
); );
UnusedPruner( UnusedPruner(
@ -70,10 +72,15 @@ public:
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptimization, bool _allowMSizeOptimization,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
); );
static void runUntilStabilised( /// Run the pruner until the code does not change anymore.
/// The provided block has to be a full AST.
/// The pruner itself determines if msize is used and which user-defined functions
/// are side-effect free.
static void runUntilStabilisedOnFullAST(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
@ -97,6 +104,7 @@ private:
Dialect const& m_dialect; Dialect const& m_dialect;
bool m_allowMSizeOptimization = false; bool m_allowMSizeOptimization = false;
std::map<YulString, SideEffects> const* m_functionSideEffects = nullptr;
bool m_shouldRunAgain = false; bool m_shouldRunAgain = false;
std::map<YulString, size_t> m_references; std::map<YulString, size_t> m_references;
}; };

View File

@ -222,7 +222,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter{}(*m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
DeadCodeEliminator{*m_dialect}(*m_ast); DeadCodeEliminator{*m_dialect}(*m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
@ -230,7 +230,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "unusedPruner") else if (m_optimizerStep == "unusedPruner")
{ {
disambiguate(); disambiguate();
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
} }
else if (m_optimizerStep == "deadCodeEliminator") else if (m_optimizerStep == "deadCodeEliminator")
{ {
@ -267,7 +267,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
LoadResolver::run(*m_dialect, *m_ast); LoadResolver::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
} }
@ -301,7 +301,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
// reverse SSA // reverse SSA
SSAReverser::run(*m_ast); SSAReverser::run(*m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
} }
else if (m_optimizerStep == "stackCompressor") else if (m_optimizerStep == "stackCompressor")
{ {

View File

@ -0,0 +1,13 @@
{
function f(x) -> t {
let b := 7
}
function g(x) -> t {
t := x
}
let x := f(g(2))
}
// ====
// step: unusedPruner
// ----
// { }

View File

@ -32,6 +32,7 @@
#include <libyul/optimiser/BlockFlattener.h> #include <libyul/optimiser/BlockFlattener.h>
#include <libyul/optimiser/Disambiguator.h> #include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h> #include <libyul/optimiser/CommonSubexpressionEliminator.h>
#include <libyul/optimiser/ControlFlowSimplifier.h> #include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
@ -54,6 +55,7 @@
#include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/SSATransform.h>
#include <libyul/optimiser/StackCompressor.h> #include <libyul/optimiser/StackCompressor.h>
#include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/VarDeclInitializer.h> #include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/VarNameCleaner.h> #include <libyul/optimiser/VarNameCleaner.h>
@ -187,7 +189,7 @@ public:
(ControlFlowSimplifier{m_dialect})(*m_ast); (ControlFlowSimplifier{m_dialect})(*m_ast);
break; break;
case 'u': case 'u':
UnusedPruner::runUntilStabilised(m_dialect, *m_ast); UnusedPruner::runUntilStabilisedOnFullAST(m_dialect, *m_ast);
break; break;
case 'D': case 'D':
DeadCodeEliminator{m_dialect}(*m_ast); DeadCodeEliminator{m_dialect}(*m_ast);