From 1c5845e3f256ee21e9bab893a6253139257da531 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Aug 2019 18:38:11 +0200 Subject: [PATCH] Side-effects of user-defined functions. --- .../CommonSubexpressionEliminator.cpp | 20 ++++++++++++++ .../optimiser/CommonSubexpressionEliminator.h | 10 ++++++- libyul/optimiser/DataFlowAnalyzer.cpp | 2 +- libyul/optimiser/DataFlowAnalyzer.h | 15 ++++++++++- libyul/optimiser/Semantics.cpp | 5 +++- libyul/optimiser/Semantics.h | 12 +++++++-- libyul/optimiser/Suite.cpp | 18 ++++++++----- test/libyul/YulOptimizerTest.cpp | 10 ++++--- .../movable_functions.yul | 26 +++++++++++++++++++ test/tools/yulopti.cpp | 2 +- 10 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 5f182ebaf..dded41ba8 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include +#include #include #include #include @@ -31,6 +34,23 @@ using namespace std; using namespace dev; using namespace yul; +void CommonSubexpressionEliminator::run(Dialect const& _dialect, Block& _ast) +{ + CommonSubexpressionEliminator cse{ + _dialect, + SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast)) + }; + cse(_ast); +} + +CommonSubexpressionEliminator::CommonSubexpressionEliminator( + Dialect const& _dialect, + map _functionSideEffects +): + DataFlowAnalyzer(_dialect, std::move(_functionSideEffects)) +{ +} + void CommonSubexpressionEliminator::visit(Expression& _e) { bool descend = true; diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index 6499e6db3..07bd47fcc 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -27,6 +27,7 @@ namespace yul { struct Dialect; +struct SideEffects; /** * Optimisation stage that replaces expressions known to be the current value of a variable @@ -37,7 +38,14 @@ struct Dialect; class CommonSubexpressionEliminator: public DataFlowAnalyzer { public: - CommonSubexpressionEliminator(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} + /// Runs the CSE pass. @a _ast needs to be the complete AST of the program! + static void run(Dialect const& _dialect, Block& _ast); + +private: + CommonSubexpressionEliminator( + Dialect const& _dialect, + std::map _functionSideEffects + ); protected: using ASTModifier::visit; diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 7c852fb47..3f3b31f2a 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -211,7 +211,7 @@ void DataFlowAnalyzer::handleAssignment(set const& _variables, Expres { clearValues(_variables); - MovableChecker movableChecker{m_dialect}; + MovableChecker movableChecker{m_dialect, &m_functionSideEffects}; if (_value) movableChecker.visit(*_value); else diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 5df45dcb8..b8e455ea1 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -26,6 +26,7 @@ #include #include #include +#include // TODO avoid #include @@ -38,6 +39,7 @@ namespace yul { struct Dialect; +struct SideEffects; /** * Base class to perform data flow analysis during AST walks. @@ -67,8 +69,16 @@ struct Dialect; class DataFlowAnalyzer: public ASTModifier { public: - explicit DataFlowAnalyzer(Dialect const& _dialect): + /// @param _functionSideEffects + /// Side-effects of user-defined functions. Worst-case side-effects are assumed + /// if this is not provided or the function is not found. + /// The parameter is mostly used to determine movability of expressions. + explicit DataFlowAnalyzer( + Dialect const& _dialect, + std::map _functionSideEffects = {} + ): m_dialect(_dialect), + m_functionSideEffects(std::move(_functionSideEffects)), m_knowledgeBase(_dialect, m_value) {} @@ -124,6 +134,9 @@ protected: ) const; Dialect const& m_dialect; + /// Side-effects of user-defined functions. Worst-case side-effects are assumed + /// if this is not provided or the function is not found. + std::map m_functionSideEffects; /// Current values of variables, always movable. std::map m_value; diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index b4f3fc10a..78d1a281a 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -64,8 +64,11 @@ void SideEffectsCollector::operator()(FunctionCall const& _functionCall) { ASTWalker::operator()(_functionCall); - if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name)) + YulString functionName = _functionCall.functionName.name; + if (BuiltinFunction const* f = m_dialect.builtin(functionName)) m_sideEffects += f->sideEffects; + else if (m_functionSideEffects && m_functionSideEffects->count(functionName)) + m_sideEffects += m_functionSideEffects->at(functionName); else m_sideEffects += SideEffects::worst(); } diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index e8cfc615e..55d44a663 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -36,7 +37,10 @@ struct Dialect; class SideEffectsCollector: public ASTWalker { public: - explicit SideEffectsCollector(Dialect const& _dialect): m_dialect(_dialect) {} + explicit SideEffectsCollector( + Dialect const& _dialect, + std::map const* _functionSideEffects = nullptr + ): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} SideEffectsCollector(Dialect const& _dialect, Expression const& _expression); SideEffectsCollector(Dialect const& _dialect, Statement const& _statement); SideEffectsCollector(Dialect const& _dialect, Block const& _ast); @@ -59,6 +63,7 @@ public: private: Dialect const& m_dialect; + std::map const* m_functionSideEffects = nullptr; SideEffects m_sideEffects; }; @@ -108,7 +113,10 @@ private: class MovableChecker: public SideEffectsCollector { public: - explicit MovableChecker(Dialect const& _dialect): SideEffectsCollector(_dialect) {} + explicit MovableChecker( + Dialect const& _dialect, + std::map const* _functionSideEffects = nullptr + ): SideEffectsCollector(_dialect, _functionSideEffects) {} MovableChecker(Dialect const& _dialect, Expression const& _expression); void operator()(Identifier const& _identifier) override; diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 6c0fb0fbc..6c5f160db 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +116,8 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(_dialect, ast); ExpressionSimplifier::run(_dialect, ast); - CommonSubexpressionEliminator{_dialect}(ast); + + CommonSubexpressionEliminator::run(_dialect, ast); } { @@ -126,16 +129,17 @@ void OptimiserSuite::run( DeadCodeEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); } + { // simplify again - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); } { // reverse SSA SSAReverser::run(ast); - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); ExpressionJoiner::run(ast); @@ -156,7 +160,7 @@ void OptimiserSuite::run( SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); } { @@ -177,12 +181,12 @@ void OptimiserSuite::run( BlockFlattener{}(ast); DeadCodeEliminator{_dialect}(ast); ControlFlowSimplifier{_dialect}(ast); - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); } } @@ -197,7 +201,7 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); SSAReverser::run(ast); - CommonSubexpressionEliminator{_dialect}(ast); + CommonSubexpressionEliminator::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); ExpressionJoiner::run(ast); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 019536b9d..dcdb4adc8 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -149,7 +151,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); - (CommonSubexpressionEliminator{*m_dialect})(*m_ast); + CommonSubexpressionEliminator::run(*m_dialect, *m_ast); } else if (m_optimizerStep == "expressionSplitter") { @@ -218,7 +220,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); ForLoopInitRewriter{}(*m_ast); - CommonSubexpressionEliminator{*m_dialect}(*m_ast); + CommonSubexpressionEliminator::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_dialect, *m_ast); UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); DeadCodeEliminator{*m_dialect}(*m_ast); @@ -260,7 +262,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line ForLoopInitRewriter{}(*m_ast); NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); - CommonSubexpressionEliminator{*m_dialect}(*m_ast); + CommonSubexpressionEliminator::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_dialect, *m_ast); LoadResolver::run(*m_dialect, *m_ast); @@ -298,7 +300,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line RedundantAssignEliminator::run(*m_dialect, *m_ast); // reverse SSA SSAReverser::run(*m_ast); - CommonSubexpressionEliminator{*m_dialect}(*m_ast); + CommonSubexpressionEliminator::run(*m_dialect, *m_ast); UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); } else if (m_optimizerStep == "stackCompressor") diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul new file mode 100644 index 000000000..3b3e65dbd --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul @@ -0,0 +1,26 @@ +{ + function double(x) -> y { y := add(x, x) } + function double_with_se(x) -> y { y := add(x, x) mstore(40, 4) } + let i := mload(3) + let a := double(i) + let b := double(i) + let c := double_with_se(i) + let d := double_with_se(i) +} +// ==== +// step: commonSubexpressionEliminator +// ---- +// { +// function double(x) -> y +// { y := add(x, x) } +// function double_with_se(x_1) -> y_2 +// { +// y_2 := add(x_1, x_1) +// mstore(40, 4) +// } +// let i := mload(3) +// let a := double(i) +// let b := a +// let c := double_with_se(i) +// let d := double_with_se(i) +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 2729c4577..e098ae4c8 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -151,7 +151,7 @@ public: ForLoopConditionIntoBody{}(*m_ast); break; case 'c': - (CommonSubexpressionEliminator{m_dialect})(*m_ast); + CommonSubexpressionEliminator::run(m_dialect, *m_ast); break; case 'd': (VarDeclInitializer{})(*m_ast);