Refactor Optimiser Steps Interface.

This commit is contained in:
chriseth
2019-09-24 14:52:01 +02:00
parent c4208a6ab8
commit 9ce1ca2340
49 changed files with 674 additions and 293 deletions
+1
View File
@@ -114,6 +114,7 @@ add_library(yul
optimiser/NameDispenser.h
optimiser/NameDisplacer.cpp
optimiser/NameDisplacer.h
optimiser/OptimiserStep.h
optimiser/OptimizerUtilities.cpp
optimiser/OptimizerUtilities.h
optimiser/RedundantAssignEliminator.cpp
@@ -28,6 +28,7 @@
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/NameDisplacer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
@@ -637,11 +638,14 @@ Object EVMToEWasmTranslator::run(Object const& _object)
parsePolyfill();
Block ast = boost::get<Block>(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code));
NameDispenser nameDispenser{m_dialect, ast};
FunctionHoister{}(ast);
FunctionGrouper{}(ast);
set<YulString> reservedIdentifiers;
NameDispenser nameDispenser{m_dialect, ast, reservedIdentifiers};
OptimiserStepContext context{m_dialect, nameDispenser, reservedIdentifiers};
FunctionHoister::run(context, ast);
FunctionGrouper::run(context, ast);
MainFunction{}(ast);
ExpressionSplitter{m_dialect, nameDispenser}(ast);
ExpressionSplitter::run(context, ast);
WordSizeTransform::run(m_dialect, ast, nameDispenser);
NameDisplacer{nameDispenser, m_polyfillFunctions}(ast);
+7
View File
@@ -17,6 +17,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -24,8 +25,14 @@ namespace yul
class BlockFlattener: public ASTModifier
{
public:
static constexpr char const* name{"BlockFlattener"};
static void run(OptimiserStepContext&, Block& _ast) { BlockFlattener{}(_ast); }
using ASTModifier::operator();
void operator()(Block& _block) override;
private:
BlockFlattener() = default;
};
}
@@ -34,11 +34,11 @@ using namespace std;
using namespace dev;
using namespace yul;
void CommonSubexpressionEliminator::run(Dialect const& _dialect, Block& _ast)
void CommonSubexpressionEliminator::run(OptimiserStepContext& _context, Block& _ast)
{
CommonSubexpressionEliminator cse{
_dialect,
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast))
_context.dialect,
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast))
};
cse(_ast);
}
@@ -22,6 +22,7 @@
#pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -38,8 +39,8 @@ struct SideEffects;
class CommonSubexpressionEliminator: public DataFlowAnalyzer
{
public:
/// Runs the CSE pass. @a _ast needs to be the complete AST of the program!
static void run(Dialect const& _dialect, Block& _ast);
static constexpr char const* name{"CommonSubexpressionEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
private:
CommonSubexpressionEliminator(
@@ -16,6 +16,7 @@
*/
#include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libyul/Utilities.h>
#include <libyul/Dialect.h>
@@ -125,6 +126,11 @@ OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _swit
}
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ControlFlowSimplifier{_context.dialect}(_ast);
}
void ControlFlowSimplifier::operator()(Block& _block)
{
simplify(_block.statements);
+6 -1
View File
@@ -17,10 +17,12 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
struct Dialect;
struct OptimiserStepContext;
/**
* Simplifies several control-flow structures:
@@ -46,7 +48,8 @@ struct Dialect;
class ControlFlowSimplifier: public ASTModifier
{
public:
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
static constexpr char const* name{"ControlFlowSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(Break&) override { ++m_numBreakStatements; }
@@ -56,6 +59,8 @@ public:
void visit(Statement& _st) override;
private:
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
void simplify(std::vector<Statement>& _statements);
Dialect const& m_dialect;
+6
View File
@@ -20,6 +20,7 @@
#include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libevmasm/SemanticInformation.h>
@@ -32,6 +33,11 @@ using namespace dev;
using namespace yul;
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
{
DeadCodeEliminator{_context.dialect}(_ast);
}
void DeadCodeEliminator::operator()(ForLoop& _for)
{
yulAssert(_for.pre.statements.empty(), "DeadCodeEliminator needs ForLoopInitRewriter as a prerequisite.");
+5 -1
View File
@@ -29,6 +29,7 @@
namespace yul
{
struct Dialect;
struct OptimiserStepContext;
/**
* Optimisation stage that removes unreachable code
@@ -47,13 +48,16 @@ struct Dialect;
class DeadCodeEliminator: public ASTModifier
{
public:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
static constexpr char const* name{"DeadCodeEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(ForLoop& _for) override;
void operator()(Block& _block) override;
private:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
Dialect const& m_dialect;
};
@@ -26,7 +26,7 @@ using namespace std;
using namespace dev;
using namespace yul;
void EquivalentFunctionCombiner::run(Block& _ast)
void EquivalentFunctionCombiner::run(OptimiserStepContext&, Block& _ast)
{
EquivalentFunctionCombiner{EquivalentFunctionDetector::run(_ast)}(_ast);
}
@@ -21,11 +21,14 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/EquivalentFunctionDetector.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmDataForward.h>
namespace yul
{
struct OptimiserStepContext;
/**
* Optimiser component that detects syntactically equivalent functions and replaces all calls to any of them by calls
* to one particular of them.
@@ -35,7 +38,8 @@ namespace yul
class EquivalentFunctionCombiner: public ASTModifier
{
public:
static void run(Block& _ast);
static constexpr char const* name{"EquivalentFunctionCombiner"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(FunctionCall& _funCall) override;
+5 -5
View File
@@ -25,6 +25,7 @@
#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Substitution.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
@@ -32,13 +33,12 @@ using namespace std;
using namespace dev;
using namespace yul;
void ExpressionInliner::run()
void ExpressionInliner::run(OptimiserStepContext& _context, Block& _ast)
{
InlinableExpressionFunctionFinder funFinder;
funFinder(m_block);
m_inlinableFunctions = funFinder.inlinableFunctions();
(*this)(m_block);
funFinder(_ast);
ExpressionInliner inliner{_context.dialect, funFinder.inlinableFunctions()};
inliner(_ast);
}
void ExpressionInliner::operator()(FunctionDefinition& _fun)
+12 -9
View File
@@ -30,6 +30,7 @@
namespace yul
{
struct Dialect;
struct OptimiserStepContext;
/**
* Optimiser component that modifies an AST in place, inlining functions that can be
@@ -48,11 +49,8 @@ struct Dialect;
class ExpressionInliner: public ASTModifier
{
public:
ExpressionInliner(Dialect const& _dialect, Block& _block):
m_block(_block), m_dialect(_dialect)
{}
void run();
static constexpr char const* name{"ExpressionInliner"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(FunctionDefinition& _fun) override;
@@ -60,13 +58,18 @@ public:
void visit(Expression& _expression) override;
private:
std::map<YulString, FunctionDefinition const*> m_inlinableFunctions;
ExpressionInliner(
Dialect const& _dialect,
std::map<YulString, FunctionDefinition const*> const& _inlinableFunctions
): m_dialect(_dialect), m_inlinableFunctions(_inlinableFunctions)
{}
Dialect const& m_dialect;
std::map<YulString, FunctionDefinition const*> const& m_inlinableFunctions;
std::map<YulString, YulString> m_varReplacements;
/// Set of functions we are currently visiting inside.
std::set<YulString> m_currentFunctions;
Block& m_block;
Dialect const& m_dialect;
};
}
+6 -5
View File
@@ -34,6 +34,12 @@ using namespace std;
using namespace dev;
using namespace yul;
void ExpressionJoiner::run(OptimiserStepContext&, Block& _ast)
{
ExpressionJoiner{_ast}(_ast);
}
void ExpressionJoiner::operator()(FunctionalInstruction& _instruction)
{
handleArguments(_instruction.arguments);
@@ -78,11 +84,6 @@ void ExpressionJoiner::visit(Expression& _e)
ASTModifier::visit(_e);
}
void ExpressionJoiner::run(Block& _ast)
{
ExpressionJoiner{_ast}(_ast);
}
ExpressionJoiner::ExpressionJoiner(Block& _ast)
{
m_references = ReferencesCounter::countReferences(_ast);
+3 -1
View File
@@ -29,6 +29,7 @@ namespace yul
{
class NameCollector;
struct OptimiserStepContext;
/**
@@ -70,7 +71,8 @@ class NameCollector;
class ExpressionJoiner: public ASTModifier
{
public:
static void run(Block& _ast);
static constexpr char const* name{"ExpressionJoiner"};
static void run(OptimiserStepContext&, Block& _ast);
private:
explicit ExpressionJoiner(Block& _ast);
+6 -6
View File
@@ -22,7 +22,7 @@
#include <libyul/optimiser/SimplificationRules.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/SSAValueTracker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libdevcore/CommonData.h>
@@ -31,6 +31,11 @@ using namespace std;
using namespace dev;
using namespace yul;
void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ExpressionSimplifier{_context.dialect}(_ast);
}
void ExpressionSimplifier::visit(Expression& _expression)
{
ASTModifier::visit(_expression);
@@ -48,8 +53,3 @@ void ExpressionSimplifier::visit(Expression& _expression)
_expression = match->action().toExpression(locationOf(_expression));
}
}
void ExpressionSimplifier::run(Dialect const& _dialect, Block& _ast)
{
ExpressionSimplifier{_dialect}(_ast);
}
+4 -1
View File
@@ -27,6 +27,7 @@
namespace yul
{
struct Dialect;
struct OptimiserStepContext;
/**
* Applies simplification rules to all expressions.
@@ -41,10 +42,12 @@ struct Dialect;
class ExpressionSimplifier: public DataFlowAnalyzer
{
public:
static constexpr char const* name{"ExpressionSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
virtual void visit(Expression& _expression);
static void run(Dialect const& _dialect, Block& _ast);
private:
explicit ExpressionSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {}
};
+6
View File
@@ -22,6 +22,7 @@
#include <libyul/optimiser/ExpressionSplitter.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libyul/Dialect.h>
@@ -35,6 +36,11 @@ using namespace dev;
using namespace langutil;
using namespace yul;
void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
{
ExpressionSplitter{_context.dialect, _context.dispenser}(_ast);
}
void ExpressionSplitter::operator()(FunctionalInstruction& _instruction)
{
for (auto& arg: _instruction.arguments | boost::adaptors::reversed)
+7 -4
View File
@@ -32,7 +32,7 @@ namespace yul
class NameCollector;
struct Dialect;
struct OptimiserStepContext;
/**
* Optimiser component that modifies an AST in place, turning complex
@@ -58,9 +58,8 @@ struct Dialect;
class ExpressionSplitter: public ASTModifier
{
public:
explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser):
m_dialect(_dialect), m_nameDispenser(_nameDispenser)
{ }
static constexpr char const* name{"ExpressionSplitter"};
static void run(OptimiserStepContext&, Block& _ast);
void operator()(FunctionalInstruction&) override;
void operator()(FunctionCall&) override;
@@ -70,6 +69,10 @@ public:
void operator()(Block& _block) override;
private:
explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser):
m_dialect(_dialect), m_nameDispenser(_nameDispenser)
{ }
/// Replaces the expression by a variable if it is a function call or functional
/// instruction. The declaration of the variable is appended to m_statementsToPrefix.
/// Recurses via visit().
@@ -16,6 +16,7 @@
*/
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libdevcore/CommonData.h>
@@ -23,6 +24,11 @@ using namespace std;
using namespace dev;
using namespace yul;
void ForLoopConditionIntoBody::run(OptimiserStepContext& _context, Block& _ast)
{
ForLoopConditionIntoBody{_context.dialect}(_ast);
}
void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop)
{
if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal))
+7 -1
View File
@@ -22,6 +22,8 @@
namespace yul
{
struct OptimiserStepContext;
/**
* Rewrites ForLoop by moving iteration condition into the ForLoop body.
* For example, `for {} lt(a, b) {} { mstore(1, 2) }` will become
@@ -40,11 +42,15 @@ namespace yul
class ForLoopConditionIntoBody: public ASTModifier
{
public:
ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {}
static constexpr char const* name{"ForLoopConditionIntoBody"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override;
private:
ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {}
Dialect const& m_dialect;
};
@@ -25,6 +25,11 @@ using namespace std;
using namespace dev;
using namespace yul;
void ForLoopConditionOutOfBody::run(OptimiserStepContext& _context, Block& _ast)
{
ForLoopConditionOutOfBody{_context.dialect}(_ast);
}
void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop)
{
ASTModifier::operator()(_forLoop);
+8 -3
View File
@@ -17,6 +17,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/Dialect.h>
namespace yul
@@ -56,13 +57,17 @@ namespace yul
class ForLoopConditionOutOfBody: public ASTModifier
{
public:
ForLoopConditionOutOfBody(Dialect const& _dialect):
m_dialect(_dialect)
{}
static constexpr char const* name{"ForLoopConditionOutOfBody"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override;
private:
ForLoopConditionOutOfBody(Dialect const& _dialect):
m_dialect(_dialect)
{}
Dialect const& m_dialect;
};
+10
View File
@@ -17,6 +17,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -29,8 +30,17 @@ namespace yul
class ForLoopInitRewriter: public ASTModifier
{
public:
static constexpr char const* name{"ForLoopInitRewriter"};
static void run(OptimiserStepContext&, Block& _ast)
{
ForLoopInitRewriter{}(_ast);
}
using ASTModifier::operator();
void operator()(Block& _block) override;
private:
ForLoopInitRewriter() = default;
};
}
+5
View File
@@ -38,6 +38,11 @@ using namespace std;
using namespace dev;
using namespace yul;
void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
{
FullInliner{_ast, _context.dispenser}.run();
}
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
m_ast(_ast), m_nameDispenser(_dispenser)
{
+6 -3
View File
@@ -24,6 +24,7 @@
#include <libyul/optimiser/ASTCopier.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/Exceptions.h>
#include <liblangutil/SourceLocation.h>
@@ -69,9 +70,8 @@ class NameCollector;
class FullInliner: public ASTModifier
{
public:
explicit FullInliner(Block& _ast, NameDispenser& _dispenser);
void run();
static constexpr char const* name{"FullInliner"};
static void run(OptimiserStepContext&, Block& _ast);
/// Inlining heuristic.
/// @param _callSite the name of the function in which the function call is located.
@@ -91,6 +91,9 @@ public:
void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
private:
FullInliner(Block& _ast, NameDispenser& _dispenser);
void run();
void updateCodeSize(FunctionDefinition const& _fun);
void handleBlock(YulString _currentFunctionName, Block& _block);
bool recursive(FunctionDefinition const& _fun) const;
+7
View File
@@ -26,6 +26,8 @@
namespace yul
{
struct OptimiserStepContext;
/**
* Moves all instructions in a block into a new block at the start of the block, followed by
* all function definitions.
@@ -37,9 +39,14 @@ namespace yul
class FunctionGrouper
{
public:
static constexpr char const* name{"FunctionGrouper"};
static void run(OptimiserStepContext&, Block& _ast) { FunctionGrouper{}(_ast); }
void operator()(Block& _block);
private:
FunctionGrouper() = default;
bool alreadyGrouped(Block const& _block);
};
+6
View File
@@ -26,6 +26,7 @@
namespace yul
{
struct OptimiserStepContext;
/**
* Moves all functions to the top-level scope.
@@ -37,10 +38,15 @@ namespace yul
class FunctionHoister: public ASTModifier
{
public:
static constexpr char const* name{"FunctionHoister"};
static void run(OptimiserStepContext&, Block& _ast) { FunctionHoister{}(_ast); }
using ASTModifier::operator();
virtual void operator()(Block& _block);
private:
FunctionHoister() = default;
bool m_isTopLevel = true;
std::vector<Statement> m_functions;
};
+4 -4
View File
@@ -31,12 +31,12 @@ using namespace std;
using namespace dev;
using namespace yul;
void LoadResolver::run(Dialect const& _dialect, Block& _ast)
void LoadResolver::run(OptimiserStepContext& _context, Block& _ast)
{
bool containsMSize = MSizeFinder::containsMSize(_dialect, _ast);
bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast);
LoadResolver{
_dialect,
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast)),
_context.dialect,
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)),
!containsMSize
}(_ast);
}
+3 -1
View File
@@ -22,6 +22,7 @@
#pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libevmasm/Instruction.h>
namespace yul
@@ -41,8 +42,9 @@ struct BuiltinFunctionForEVM;
class LoadResolver: public DataFlowAnalyzer
{
public:
static constexpr char const* name{"LoadResolver"};
/// Run the load resolver on the given complete AST.
static void run(Dialect const& _dialect, Block& _ast);
static void run(OptimiserStepContext&, Block& _ast);
private:
LoadResolver(
+8
View File
@@ -26,13 +26,21 @@
namespace yul
{
struct OptimiserStepContext;
/**
* Prerequisites: Function Grouper
*/
class MainFunction
{
public:
static constexpr char const* name{"MainFunction"};
static void run(OptimiserStepContext&, Block& _ast) { MainFunction{}(_ast); }
void operator()(Block& _block);
private:
MainFunction() = default;
};
}
+65
View File
@@ -0,0 +1,65 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <libyul/Exceptions.h>
#include <string>
#include <set>
namespace yul
{
struct Dialect;
struct Block;
class YulString;
class NameDispenser;
struct OptimiserStepContext
{
Dialect const& dialect;
NameDispenser& dispenser;
std::set<YulString> const& reservedIdentifiers;
};
/**
* Construction to create dynamically callable objects out of the
* statically callable optimiser steps.
*/
struct OptimiserStep
{
explicit OptimiserStep(std::string _name): name(std::move(_name)) {}
virtual ~OptimiserStep() = default;
virtual void run(OptimiserStepContext&, Block&) const = 0;
std::string name;
};
template <class Step>
struct OptimiserStepInstance: public OptimiserStep
{
OptimiserStepInstance(): OptimiserStep{Step::name} {}
void run(OptimiserStepContext& _context, Block& _ast) const override
{
Step::run(_context, _ast);
}
};
}
@@ -32,6 +32,15 @@ using namespace std;
using namespace dev;
using namespace yul;
void RedundantAssignEliminator::run(OptimiserStepContext& _context, Block& _ast)
{
RedundantAssignEliminator rae{_context.dialect};
rae(_ast);
AssignmentRemover remover{rae.m_pendingRemovals};
remover(_ast);
}
void RedundantAssignEliminator::operator()(Identifier const& _identifier)
{
changeUndecidedTo(_identifier.name, State::Used);
@@ -204,14 +213,6 @@ void RedundantAssignEliminator::operator()(Block const& _block)
swap(m_declaredVariables, outerDeclaredVariables);
}
void RedundantAssignEliminator::run(Dialect const& _dialect, Block& _ast)
{
RedundantAssignEliminator rae{_dialect};
rae(_ast);
AssignmentRemover remover{rae.m_pendingRemovals};
remover(_ast);
}
template <class K, class V, class F>
void joinMap(std::map<K, V>& _a, std::map<K, V>&& _b, F _conflictSolver)
+4 -2
View File
@@ -23,6 +23,7 @@
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <map>
#include <vector>
@@ -105,6 +106,9 @@ struct Dialect;
class RedundantAssignEliminator: public ASTWalker
{
public:
static constexpr char const* name{"RedundantAssignEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
explicit RedundantAssignEliminator(Dialect const& _dialect): m_dialect(&_dialect) {}
RedundantAssignEliminator() = delete;
RedundantAssignEliminator(RedundantAssignEliminator const&) = delete;
@@ -123,8 +127,6 @@ public:
void operator()(Continue const&) override;
void operator()(Block const& _block) override;
static void run(Dialect const& _dialect, Block& _ast);
private:
class State
{
+17 -3
View File
@@ -21,6 +21,7 @@
#pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -38,6 +39,12 @@ namespace yul
class Rematerialiser: public DataFlowAnalyzer
{
public:
static constexpr char const* name{"Rematerialiser"};
static void run(
OptimiserStepContext& _context,
Block& _ast
) { run(_context.dialect, _ast); }
static void run(
Dialect const& _dialect,
Block& _ast,
@@ -80,12 +87,19 @@ protected:
class LiteralRematerialiser: public DataFlowAnalyzer
{
public:
LiteralRematerialiser(Dialect const& _dialect):
DataFlowAnalyzer(_dialect)
{}
static constexpr char const* name{"LiteralRematerialiser"};
static void run(
OptimiserStepContext& _context,
Block& _ast
) { LiteralRematerialiser{_context.dialect}(_ast); }
using ASTModifier::visit;
void visit(Expression& _e) override;
private:
LiteralRematerialiser(Dialect const& _dialect):
DataFlowAnalyzer(_dialect)
{}
};
+1 -1
View File
@@ -23,7 +23,7 @@ using namespace std;
using namespace dev;
using namespace yul;
void SSAReverser::run(Block& _block)
void SSAReverser::run(OptimiserStepContext&, Block& _block)
{
AssignmentCounter assignmentCounter;
assignmentCounter(_block);
+6 -2
View File
@@ -17,6 +17,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -69,12 +70,15 @@ class AssignmentCounter;
class SSAReverser: public ASTModifier
{
public:
static constexpr char const* name{"SSAReverser"};
static void run(OptimiserStepContext& _context, Block& _ast);
using ASTModifier::operator();
void operator()(Block& _block) override;
static void run(Block& _block);
private:
SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {}
explicit SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {}
AssignmentCounter const& m_assignmentCounter;
};
+3 -3
View File
@@ -373,12 +373,12 @@ void PropagateValues::operator()(Block& _block)
}
void SSATransform::run(Block& _ast, NameDispenser& _nameDispenser)
void SSATransform::run(OptimiserStepContext& _context, Block& _ast)
{
Assignments assignments;
assignments(_ast);
IntroduceSSA{_nameDispenser, assignments.names()}(_ast);
IntroduceControlFlowSSA{_nameDispenser, assignments.names()}(_ast);
IntroduceSSA{_context.dispenser, assignments.names()}(_ast);
IntroduceControlFlowSSA{_context.dispenser, assignments.names()}(_ast);
PropagateValues{assignments.names()}(_ast);
}
+3 -1
View File
@@ -22,6 +22,7 @@
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <liblangutil/SourceLocation.h>
@@ -89,7 +90,8 @@ class NameDispenser;
class SSATransform: public ASTModifier
{
public:
static void run(Block& _ast, NameDispenser& _nameDispenser);
static constexpr char const* name{"SSATransform"};
static void run(OptimiserStepContext& _context, Block& _ast);
};
}
@@ -59,6 +59,11 @@ OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _const
}
void StructuralSimplifier::run(OptimiserStepContext&, Block& _ast)
{
StructuralSimplifier{}(_ast);
}
void StructuralSimplifier::operator()(Block& _block)
{
simplify(_block.statements);
+6
View File
@@ -18,6 +18,7 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libdevcore/Common.h>
namespace yul
@@ -39,9 +40,14 @@ namespace yul
class StructuralSimplifier: public ASTModifier
{
public:
static constexpr char const* name{"StructuralSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator();
void operator()(Block& _block) override;
private:
StructuralSimplifier() = default;
void simplify(std::vector<Statement>& _statements);
bool expressionAlwaysTrue(Expression const& _expression);
bool expressionAlwaysFalse(Expression const& _expression);
+186 -98
View File
@@ -83,26 +83,28 @@ void OptimiserSuite::run(
)(*_object.code));
Block& ast = *_object.code;
VarDeclInitializer{}(ast);
FunctionHoister{}(ast);
BlockFlattener{}(ast);
ForLoopInitRewriter{}(ast);
DeadCodeEliminator{_dialect}(ast);
FunctionGrouper{}(ast);
EquivalentFunctionCombiner::run(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
BlockFlattener{}(ast);
ControlFlowSimplifier{_dialect}(ast);
LiteralRematerialiser{_dialect}(ast);
StructuralSimplifier{}(ast);
ControlFlowSimplifier{_dialect}(ast);
ForLoopConditionIntoBody{_dialect}(ast);
BlockFlattener{}(ast);
OptimiserSuite suite(_dialect, reservedIdentifiers, Debug::None, ast);
suite.runSequence({
VarDeclInitializer::name,
FunctionHoister::name,
BlockFlattener::name,
ForLoopInitRewriter::name,
DeadCodeEliminator::name,
FunctionGrouper::name,
EquivalentFunctionCombiner::name,
UnusedPruner::name,
BlockFlattener::name,
ControlFlowSimplifier::name,
LiteralRematerialiser::name,
StructuralSimplifier::name,
ControlFlowSimplifier::name,
ForLoopConditionIntoBody::name,
BlockFlattener::name
}, ast);
// None of the above can make stack problems worse.
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
size_t codeSize = 0;
for (size_t rounds = 0; rounds < 12; ++rounds)
{
@@ -115,120 +117,138 @@ void OptimiserSuite::run(
{
// Turn into SSA and simplify
ExpressionSplitter{_dialect, dispenser}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast);
ExpressionSimplifier::run(_dialect, ast);
CommonSubexpressionEliminator::run(_dialect, ast);
LoadResolver::run(_dialect, ast);
suite.runSequence({
ExpressionSplitter::name,
SSATransform::name,
RedundantAssignEliminator::name,
RedundantAssignEliminator::name,
ExpressionSimplifier::name,
CommonSubexpressionEliminator::name,
LoadResolver::name
}, ast);
}
{
// still in SSA, perform structural simplification
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{}(ast);
ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
ForLoopConditionIntoBody{_dialect}(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
suite.runSequence({
LiteralRematerialiser::name,
ForLoopConditionOutOfBody::name,
ControlFlowSimplifier::name,
StructuralSimplifier::name,
ControlFlowSimplifier::name,
BlockFlattener::name,
DeadCodeEliminator::name,
ForLoopConditionIntoBody::name,
UnusedPruner::name
}, ast);
}
{
// simplify again
LoadResolver::run(_dialect, ast);
CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
suite.runSequence({
LoadResolver::name,
CommonSubexpressionEliminator::name,
UnusedPruner::name,
}, ast);
}
{
// reverse SSA
SSAReverser::run(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
suite.runSequence({
SSAReverser::name,
CommonSubexpressionEliminator::name,
UnusedPruner::name,
ExpressionJoiner::run(ast);
ExpressionJoiner::run(ast);
ExpressionJoiner::name,
ExpressionJoiner::name,
}, ast);
}
// should have good "compilability" property here.
{
// run functional expression inliner
ExpressionInliner(_dialect, ast).run();
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
suite.runSequence({
ExpressionInliner::name,
UnusedPruner::name,
}, ast);
}
{
// Turn into SSA again and simplify
ExpressionSplitter{_dialect, dispenser}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast);
CommonSubexpressionEliminator::run(_dialect, ast);
LoadResolver::run(_dialect, ast);
suite.runSequence({
ExpressionSplitter::name,
SSATransform::name,
RedundantAssignEliminator::name,
RedundantAssignEliminator::name,
CommonSubexpressionEliminator::name,
LoadResolver::name,
}, ast);
}
{
// run full inliner
FunctionGrouper{}(ast);
EquivalentFunctionCombiner::run(ast);
FullInliner{ast, dispenser}.run();
BlockFlattener{}(ast);
suite.runSequence({
FunctionGrouper::name,
EquivalentFunctionCombiner::name,
FullInliner::name,
BlockFlattener::name
}, ast);
}
{
// SSA plus simplify
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast);
LoadResolver::run(_dialect, ast);
ExpressionSimplifier::run(_dialect, ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
StructuralSimplifier{}(ast);
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
ControlFlowSimplifier{_dialect}(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast);
ForLoopConditionIntoBody{_dialect}(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
CommonSubexpressionEliminator::run(_dialect, ast);
suite.runSequence({
SSATransform::name,
RedundantAssignEliminator::name,
RedundantAssignEliminator::name,
LoadResolver::name,
ExpressionSimplifier::name,
LiteralRematerialiser::name,
ForLoopConditionOutOfBody::name,
StructuralSimplifier::name,
BlockFlattener::name,
DeadCodeEliminator::name,
ControlFlowSimplifier::name,
CommonSubexpressionEliminator::name,
SSATransform::name,
RedundantAssignEliminator::name,
RedundantAssignEliminator::name,
ForLoopConditionIntoBody::name,
UnusedPruner::name,
CommonSubexpressionEliminator::name,
}, ast);
}
}
// Make source short and pretty.
ExpressionJoiner::run(ast);
Rematerialiser::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
suite.runSequence({
ExpressionJoiner::name,
Rematerialiser::name,
UnusedPruner::name,
ExpressionJoiner::name,
UnusedPruner::name,
ExpressionJoiner::name,
UnusedPruner::name,
SSAReverser::run(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
SSAReverser::name,
CommonSubexpressionEliminator::name,
LiteralRematerialiser::name,
ForLoopConditionOutOfBody::name,
CommonSubexpressionEliminator::name,
UnusedPruner::name,
ExpressionJoiner::run(ast);
Rematerialiser::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::name,
Rematerialiser::name,
UnusedPruner::name,
}, ast);
// This is a tuning parameter, but actually just prevents infinite loops.
size_t stackCompressorMaxIterations = 16;
FunctionGrouper{}(ast);
suite.runSequence({
FunctionGrouper::name
}, ast);
// We ignore the return value because we will get a much better error
// message once we perform code generation.
StackCompressor::run(
@@ -237,14 +257,16 @@ void OptimiserSuite::run(
_optimizeStackAllocation,
stackCompressorMaxIterations
);
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
ControlFlowSimplifier{_dialect}(ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
suite.runSequence({
BlockFlattener::name,
DeadCodeEliminator::name,
ControlFlowSimplifier::name,
LiteralRematerialiser::name,
ForLoopConditionOutOfBody::name,
CommonSubexpressionEliminator::name,
FunctionGrouper{}(ast);
FunctionGrouper::name,
}, ast);
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
{
@@ -258,7 +280,73 @@ void OptimiserSuite::run(
if (ast.statements.size() > 1 && boost::get<Block>(ast.statements.front()).statements.empty())
ast.statements.erase(ast.statements.begin());
}
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast);
suite.runSequence({
VarNameCleaner::name
}, ast);
*_object.analysisInfo = AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
}
namespace
{
template <class... Step>
map<string, unique_ptr<OptimiserStep>> optimiserStepCollection()
{
map<string, unique_ptr<OptimiserStep>> ret;
for (unique_ptr<OptimiserStep>& s: make_vector<unique_ptr<OptimiserStep>>(
(make_unique<OptimiserStepInstance<Step>>())...
))
{
yulAssert(!ret.count(s->name), "");
ret[s->name] = std::move(s);
}
return ret;
}
}
map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
{
static map<string, unique_ptr<OptimiserStep>> instance;
if (instance.empty())
instance = optimiserStepCollection<
BlockFlattener,
CommonSubexpressionEliminator,
ControlFlowSimplifier,
DeadCodeEliminator,
EquivalentFunctionCombiner,
ExpressionInliner,
ExpressionJoiner,
ExpressionSimplifier,
ExpressionSplitter,
ForLoopConditionIntoBody,
ForLoopConditionOutOfBody,
ForLoopInitRewriter,
FullInliner,
FunctionGrouper,
FunctionHoister,
LiteralRematerialiser,
LoadResolver,
RedundantAssignEliminator,
Rematerialiser,
SSAReverser,
SSATransform,
StructuralSimplifier,
UnusedPruner,
VarDeclInitializer,
VarNameCleaner
>();
return instance;
}
void OptimiserSuite::runSequence(std::vector<string> const& _steps, Block& _ast)
{
for (string const& step: _steps)
{
if (m_debug == Debug::PrintStep)
cout << "Running " << step << endl;
allSteps().at(step)->run(m_context, _ast);
}
}
+29
View File
@@ -22,9 +22,13 @@
#include <libyul/AsmDataForward.h>
#include <libyul/YulString.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/NameDispenser.h>
#include <liblangutil/EVMVersion.h>
#include <set>
#include <string>
#include <memory>
namespace yul
{
@@ -41,6 +45,11 @@ struct Object;
class OptimiserSuite
{
public:
enum class Debug
{
None,
PrintStep
};
static void run(
Dialect const& _dialect,
GasMeter const* _meter,
@@ -48,6 +57,26 @@ public:
bool _optimizeStackAllocation,
std::set<YulString> const& _externallyUsedIdentifiers = {}
);
void runSequence(std::vector<std::string> const& _steps, Block& _ast);
static std::map<std::string, std::unique_ptr<OptimiserStep>> const& allSteps();
private:
OptimiserSuite(
Dialect const& _dialect,
std::set<YulString> const& _externallyUsedIdentifiers,
Debug _debug,
Block& _ast
):
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
m_context{_dialect, m_dispenser, _externallyUsedIdentifiers},
m_debug(_debug)
{}
NameDispenser m_dispenser;
OptimiserStepContext m_context;
Debug m_debug;
};
}
+29 -13
View File
@@ -21,6 +21,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/YulString.h>
#include <map>
@@ -47,19 +48,11 @@ struct SideEffects;
class UnusedPruner: public ASTModifier
{
public:
UnusedPruner(
Dialect const& _dialect,
Block& _ast,
bool _allowMSizeOptimization,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
std::set<YulString> const& _externallyUsedFunctions = {}
);
UnusedPruner(
Dialect const& _dialect,
FunctionDefinition& _function,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
static constexpr char const* name{"UnusedPruner"};
static void run(OptimiserStepContext& _context, Block& _ast) {
UnusedPruner::runUntilStabilisedOnFullAST(_context.dialect, _ast, _context.reservedIdentifiers);
}
using ASTModifier::operator();
void operator()(Block& _block) override;
@@ -76,6 +69,15 @@ public:
std::set<YulString> const& _externallyUsedFunctions = {}
);
static void run(
Dialect const& _dialect,
Block& _ast,
std::set<YulString> const& _externallyUsedFunctions = {}
)
{
runUntilStabilisedOnFullAST(_dialect, _ast, _externallyUsedFunctions);
}
/// 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
@@ -99,6 +101,20 @@ public:
);
private:
UnusedPruner(
Dialect const& _dialect,
Block& _ast,
bool _allowMSizeOptimization,
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
std::set<YulString> const& _externallyUsedFunctions = {}
);
UnusedPruner(
Dialect const& _dialect,
FunctionDefinition& _function,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
bool used(YulString _name) const;
void subtractReferences(std::map<YulString, size_t> const& _subtrahend);
+4
View File
@@ -19,6 +19,7 @@
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul
{
@@ -32,6 +33,9 @@ namespace yul
class VarDeclInitializer: public ASTModifier
{
public:
static constexpr char const* name{"VarDeclInitializer"};
static void run(OptimiserStepContext&, Block& _ast) { VarDeclInitializer{}(_ast); }
void operator()(Block& _block) override;
};
+12 -5
View File
@@ -21,6 +21,7 @@
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <map>
#include <set>
@@ -43,11 +44,11 @@ struct Dialect;
class VarNameCleaner: public ASTModifier
{
public:
VarNameCleaner(
Block const& _ast,
Dialect const& _dialect,
std::set<YulString> _blacklist = {}
);
static constexpr char const* name{"VarNameCleaner"};
static void run(OptimiserStepContext& _context, Block& _ast)
{
VarNameCleaner{_ast, _context.dialect, _context.reservedIdentifiers}(_ast);
}
using ASTModifier::operator();
void operator()(VariableDeclaration& _varDecl) override;
@@ -55,6 +56,12 @@ public:
void operator()(FunctionDefinition& _funDef) override;
private:
VarNameCleaner(
Block const& _ast,
Dialect const& _dialect,
std::set<YulString> _blacklist = {}
);
/// Tries to rename a list of variables.
void renameVariables(std::vector<TypedName>& _variables);