Merge pull request #7485 from ethereum/develop

Merge develop into develop_060
This commit is contained in:
Leonardo 2019-09-26 15:43:12 +02:00 committed by GitHub
commit ca714a2d3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 731 additions and 335 deletions

View File

@ -142,7 +142,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
ssl, ssl,
string("This variable is of storage pointer type and can be ") + string("This variable is of storage pointer type and can be ") +
(variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") + (variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
" without prior assignment." " without prior assignment, which would lead to undefined behaviour."
); );
} }
} }

View File

@ -621,8 +621,8 @@ void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier)
auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type); auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
if (fType.returnParameterTypes().size() == 1) if (fType.returnParameterTypes().size() == 1)
{ {
defineGlobalVariable(fType.richIdentifier(), _identifier); defineGlobalVariable(fType.identifier(), _identifier);
m_context.createExpression(_identifier, m_context.globalSymbol(fType.richIdentifier())); m_context.createExpression(_identifier, m_context.globalSymbol(fType.identifier()));
} }
} }

View File

@ -46,7 +46,7 @@ void SMTLib2Interface::reset()
m_accumulatedOutput.emplace_back(); m_accumulatedOutput.emplace_back();
m_variables.clear(); m_variables.clear();
write("(set-option :produce-models true)"); write("(set-option :produce-models true)");
write("(set-logic QF_UFLIA)"); write("(set-logic ALL)");
} }
void SMTLib2Interface::push() void SMTLib2Interface::push()
@ -126,9 +126,24 @@ string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
{ {
if (_expr.arguments.empty()) if (_expr.arguments.empty())
return _expr.name; return _expr.name;
std::string sexpr = "(" + _expr.name;
for (auto const& arg: _expr.arguments) std::string sexpr = "(";
sexpr += " " + toSExpr(arg); if (_expr.name == "const_array")
{
solAssert(_expr.arguments.size() == 2, "");
auto sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments.at(0).sort);
solAssert(sortSort, "");
auto arraySort = dynamic_pointer_cast<ArraySort>(sortSort->inner);
solAssert(arraySort, "");
sexpr += "(as const " + toSmtLibSort(*arraySort) + ") ";
sexpr += toSExpr(_expr.arguments.at(1));
}
else
{
sexpr += _expr.name;
for (auto const& arg: _expr.arguments)
sexpr += " " + toSExpr(arg);
}
sexpr += ")"; sexpr += ")";
return sexpr; return sexpr;
} }

View File

@ -114,6 +114,7 @@ add_library(yul
optimiser/NameDispenser.h optimiser/NameDispenser.h
optimiser/NameDisplacer.cpp optimiser/NameDisplacer.cpp
optimiser/NameDisplacer.h optimiser/NameDisplacer.h
optimiser/OptimiserStep.h
optimiser/OptimizerUtilities.cpp optimiser/OptimizerUtilities.cpp
optimiser/OptimizerUtilities.h optimiser/OptimizerUtilities.h
optimiser/RedundantAssignEliminator.cpp optimiser/RedundantAssignEliminator.cpp

View File

@ -28,6 +28,7 @@
#include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/Disambiguator.h> #include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/NameDisplacer.h> #include <libyul/optimiser/NameDisplacer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmParser.h> #include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
@ -637,11 +638,14 @@ Object EVMToEWasmTranslator::run(Object const& _object)
parsePolyfill(); parsePolyfill();
Block ast = boost::get<Block>(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code)); Block ast = boost::get<Block>(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code));
NameDispenser nameDispenser{m_dialect, ast}; set<YulString> reservedIdentifiers;
FunctionHoister{}(ast); NameDispenser nameDispenser{m_dialect, ast, reservedIdentifiers};
FunctionGrouper{}(ast); OptimiserStepContext context{m_dialect, nameDispenser, reservedIdentifiers};
FunctionHoister::run(context, ast);
FunctionGrouper::run(context, ast);
MainFunction{}(ast); MainFunction{}(ast);
ExpressionSplitter{m_dialect, nameDispenser}(ast); ExpressionSplitter::run(context, ast);
WordSizeTransform::run(m_dialect, ast, nameDispenser); WordSizeTransform::run(m_dialect, ast, nameDispenser);
NameDisplacer{nameDispenser, m_polyfillFunctions}(ast); NameDisplacer{nameDispenser, m_polyfillFunctions}(ast);

View File

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -24,8 +25,14 @@ namespace yul
class BlockFlattener: public ASTModifier class BlockFlattener: public ASTModifier
{ {
public: public:
static constexpr char const* name{"BlockFlattener"};
static void run(OptimiserStepContext&, Block& _ast) { BlockFlattener{}(_ast); }
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Block& _block) override; void operator()(Block& _block) override;
private:
BlockFlattener() = default;
}; };
} }

View File

@ -34,11 +34,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void CommonSubexpressionEliminator::run(Dialect const& _dialect, Block& _ast) void CommonSubexpressionEliminator::run(OptimiserStepContext& _context, Block& _ast)
{ {
CommonSubexpressionEliminator cse{ CommonSubexpressionEliminator cse{
_dialect, _context.dialect,
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast)) SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast))
}; };
cse(_ast); cse(_ast);
} }

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h> #include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -38,8 +39,8 @@ struct SideEffects;
class CommonSubexpressionEliminator: public DataFlowAnalyzer class CommonSubexpressionEliminator: public DataFlowAnalyzer
{ {
public: public:
/// Runs the CSE pass. @a _ast needs to be the complete AST of the program! static constexpr char const* name{"CommonSubexpressionEliminator"};
static void run(Dialect const& _dialect, Block& _ast); static void run(OptimiserStepContext&, Block& _ast);
private: private:
CommonSubexpressionEliminator( CommonSubexpressionEliminator(

View File

@ -16,6 +16,7 @@
*/ */
#include <libyul/optimiser/ControlFlowSimplifier.h> #include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libyul/Dialect.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) void ControlFlowSimplifier::operator()(Block& _block)
{ {
simplify(_block.statements); simplify(_block.statements);

View File

@ -17,10 +17,12 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
struct Dialect; struct Dialect;
struct OptimiserStepContext;
/** /**
* Simplifies several control-flow structures: * Simplifies several control-flow structures:
@ -46,7 +48,8 @@ struct Dialect;
class ControlFlowSimplifier: public ASTModifier class ControlFlowSimplifier: public ASTModifier
{ {
public: public:
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {} static constexpr char const* name{"ControlFlowSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Break&) override { ++m_numBreakStatements; } void operator()(Break&) override { ++m_numBreakStatements; }
@ -56,6 +59,8 @@ public:
void visit(Statement& _st) override; void visit(Statement& _st) override;
private: private:
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
void simplify(std::vector<Statement>& _statements); void simplify(std::vector<Statement>& _statements);
Dialect const& m_dialect; Dialect const& m_dialect;

View File

@ -20,6 +20,7 @@
#include <libyul/optimiser/DeadCodeEliminator.h> #include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
@ -32,6 +33,11 @@ using namespace dev;
using namespace yul; using namespace yul;
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
{
DeadCodeEliminator{_context.dialect}(_ast);
}
void DeadCodeEliminator::operator()(ForLoop& _for) void DeadCodeEliminator::operator()(ForLoop& _for)
{ {
yulAssert(_for.pre.statements.empty(), "DeadCodeEliminator needs ForLoopInitRewriter as a prerequisite."); yulAssert(_for.pre.statements.empty(), "DeadCodeEliminator needs ForLoopInitRewriter as a prerequisite.");

View File

@ -29,6 +29,7 @@
namespace yul namespace yul
{ {
struct Dialect; struct Dialect;
struct OptimiserStepContext;
/** /**
* Optimisation stage that removes unreachable code * Optimisation stage that removes unreachable code
@ -47,13 +48,16 @@ struct Dialect;
class DeadCodeEliminator: public ASTModifier class DeadCodeEliminator: public ASTModifier
{ {
public: public:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {} static constexpr char const* name{"DeadCodeEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(ForLoop& _for) override; void operator()(ForLoop& _for) override;
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: private:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
Dialect const& m_dialect; Dialect const& m_dialect;
}; };

View File

@ -26,7 +26,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void EquivalentFunctionCombiner::run(Block& _ast) void EquivalentFunctionCombiner::run(OptimiserStepContext&, Block& _ast)
{ {
EquivalentFunctionCombiner{EquivalentFunctionDetector::run(_ast)}(_ast); EquivalentFunctionCombiner{EquivalentFunctionDetector::run(_ast)}(_ast);
} }

View File

@ -21,11 +21,14 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/EquivalentFunctionDetector.h> #include <libyul/optimiser/EquivalentFunctionDetector.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
namespace yul namespace yul
{ {
struct OptimiserStepContext;
/** /**
* Optimiser component that detects syntactically equivalent functions and replaces all calls to any of them by calls * Optimiser component that detects syntactically equivalent functions and replaces all calls to any of them by calls
* to one particular of them. * to one particular of them.
@ -35,7 +38,8 @@ namespace yul
class EquivalentFunctionCombiner: public ASTModifier class EquivalentFunctionCombiner: public ASTModifier
{ {
public: public:
static void run(Block& _ast); static constexpr char const* name{"EquivalentFunctionCombiner"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(FunctionCall& _funCall) override; void operator()(FunctionCall& _funCall) override;

View File

@ -25,6 +25,7 @@
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Substitution.h> #include <libyul/optimiser/Substitution.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
@ -32,13 +33,12 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void ExpressionInliner::run() void ExpressionInliner::run(OptimiserStepContext& _context, Block& _ast)
{ {
InlinableExpressionFunctionFinder funFinder; InlinableExpressionFunctionFinder funFinder;
funFinder(m_block); funFinder(_ast);
m_inlinableFunctions = funFinder.inlinableFunctions(); ExpressionInliner inliner{_context.dialect, funFinder.inlinableFunctions()};
inliner(_ast);
(*this)(m_block);
} }
void ExpressionInliner::operator()(FunctionDefinition& _fun) void ExpressionInliner::operator()(FunctionDefinition& _fun)

View File

@ -30,6 +30,7 @@
namespace yul namespace yul
{ {
struct Dialect; struct Dialect;
struct OptimiserStepContext;
/** /**
* Optimiser component that modifies an AST in place, inlining functions that can be * Optimiser component that modifies an AST in place, inlining functions that can be
@ -48,11 +49,8 @@ struct Dialect;
class ExpressionInliner: public ASTModifier class ExpressionInliner: public ASTModifier
{ {
public: public:
ExpressionInliner(Dialect const& _dialect, Block& _block): static constexpr char const* name{"ExpressionInliner"};
m_block(_block), m_dialect(_dialect) static void run(OptimiserStepContext&, Block& _ast);
{}
void run();
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(FunctionDefinition& _fun) override; void operator()(FunctionDefinition& _fun) override;
@ -60,13 +58,18 @@ public:
void visit(Expression& _expression) override; void visit(Expression& _expression) override;
private: 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; std::map<YulString, YulString> m_varReplacements;
/// Set of functions we are currently visiting inside. /// Set of functions we are currently visiting inside.
std::set<YulString> m_currentFunctions; std::set<YulString> m_currentFunctions;
Block& m_block;
Dialect const& m_dialect;
}; };
} }

View File

@ -34,6 +34,12 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void ExpressionJoiner::run(OptimiserStepContext&, Block& _ast)
{
ExpressionJoiner{_ast}(_ast);
}
void ExpressionJoiner::operator()(FunctionalInstruction& _instruction) void ExpressionJoiner::operator()(FunctionalInstruction& _instruction)
{ {
handleArguments(_instruction.arguments); handleArguments(_instruction.arguments);
@ -78,11 +84,6 @@ void ExpressionJoiner::visit(Expression& _e)
ASTModifier::visit(_e); ASTModifier::visit(_e);
} }
void ExpressionJoiner::run(Block& _ast)
{
ExpressionJoiner{_ast}(_ast);
}
ExpressionJoiner::ExpressionJoiner(Block& _ast) ExpressionJoiner::ExpressionJoiner(Block& _ast)
{ {
m_references = ReferencesCounter::countReferences(_ast); m_references = ReferencesCounter::countReferences(_ast);

View File

@ -29,6 +29,7 @@ namespace yul
{ {
class NameCollector; class NameCollector;
struct OptimiserStepContext;
/** /**
@ -70,7 +71,8 @@ class NameCollector;
class ExpressionJoiner: public ASTModifier class ExpressionJoiner: public ASTModifier
{ {
public: public:
static void run(Block& _ast); static constexpr char const* name{"ExpressionJoiner"};
static void run(OptimiserStepContext&, Block& _ast);
private: private:
explicit ExpressionJoiner(Block& _ast); explicit ExpressionJoiner(Block& _ast);

View File

@ -22,7 +22,7 @@
#include <libyul/optimiser/SimplificationRules.h> #include <libyul/optimiser/SimplificationRules.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/SSAValueTracker.h> #include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
@ -31,6 +31,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ExpressionSimplifier{_context.dialect}(_ast);
}
void ExpressionSimplifier::visit(Expression& _expression) void ExpressionSimplifier::visit(Expression& _expression)
{ {
ASTModifier::visit(_expression); ASTModifier::visit(_expression);
@ -48,8 +53,3 @@ void ExpressionSimplifier::visit(Expression& _expression)
_expression = match->action().toExpression(locationOf(_expression)); _expression = match->action().toExpression(locationOf(_expression));
} }
} }
void ExpressionSimplifier::run(Dialect const& _dialect, Block& _ast)
{
ExpressionSimplifier{_dialect}(_ast);
}

View File

@ -27,6 +27,7 @@
namespace yul namespace yul
{ {
struct Dialect; struct Dialect;
struct OptimiserStepContext;
/** /**
* Applies simplification rules to all expressions. * Applies simplification rules to all expressions.
@ -41,10 +42,12 @@ struct Dialect;
class ExpressionSimplifier: public DataFlowAnalyzer class ExpressionSimplifier: public DataFlowAnalyzer
{ {
public: public:
static constexpr char const* name{"ExpressionSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
virtual void visit(Expression& _expression); virtual void visit(Expression& _expression);
static void run(Dialect const& _dialect, Block& _ast);
private: private:
explicit ExpressionSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} explicit ExpressionSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {}
}; };

View File

@ -22,6 +22,7 @@
#include <libyul/optimiser/ExpressionSplitter.h> #include <libyul/optimiser/ExpressionSplitter.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
@ -35,6 +36,11 @@ using namespace dev;
using namespace langutil; using namespace langutil;
using namespace yul; using namespace yul;
void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
{
ExpressionSplitter{_context.dialect, _context.dispenser}(_ast);
}
void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) void ExpressionSplitter::operator()(FunctionalInstruction& _instruction)
{ {
for (auto& arg: _instruction.arguments | boost::adaptors::reversed) for (auto& arg: _instruction.arguments | boost::adaptors::reversed)

View File

@ -32,7 +32,7 @@ namespace yul
class NameCollector; class NameCollector;
struct Dialect; struct Dialect;
struct OptimiserStepContext;
/** /**
* Optimiser component that modifies an AST in place, turning complex * Optimiser component that modifies an AST in place, turning complex
@ -58,9 +58,8 @@ struct Dialect;
class ExpressionSplitter: public ASTModifier class ExpressionSplitter: public ASTModifier
{ {
public: public:
explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser): static constexpr char const* name{"ExpressionSplitter"};
m_dialect(_dialect), m_nameDispenser(_nameDispenser) static void run(OptimiserStepContext&, Block& _ast);
{ }
void operator()(FunctionalInstruction&) override; void operator()(FunctionalInstruction&) override;
void operator()(FunctionCall&) override; void operator()(FunctionCall&) override;
@ -70,6 +69,10 @@ public:
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: 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 /// 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. /// instruction. The declaration of the variable is appended to m_statementsToPrefix.
/// Recurses via visit(). /// Recurses via visit().

View File

@ -16,6 +16,7 @@
*/ */
#include <libyul/optimiser/ForLoopConditionIntoBody.h> #include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
@ -23,6 +24,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void ForLoopConditionIntoBody::run(OptimiserStepContext& _context, Block& _ast)
{
ForLoopConditionIntoBody{_context.dialect}(_ast);
}
void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop)
{ {
if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal)) if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal))

View File

@ -22,6 +22,8 @@
namespace yul namespace yul
{ {
struct OptimiserStepContext;
/** /**
* Rewrites ForLoop by moving iteration condition into the ForLoop body. * Rewrites ForLoop by moving iteration condition into the ForLoop body.
* For example, `for {} lt(a, b) {} { mstore(1, 2) }` will become * For example, `for {} lt(a, b) {} { mstore(1, 2) }` will become
@ -40,11 +42,15 @@ namespace yul
class ForLoopConditionIntoBody: public ASTModifier class ForLoopConditionIntoBody: public ASTModifier
{ {
public: public:
ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {} static constexpr char const* name{"ForLoopConditionIntoBody"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override; void operator()(ForLoop& _forLoop) override;
private: private:
ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {}
Dialect const& m_dialect; Dialect const& m_dialect;
}; };

View File

@ -25,6 +25,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void ForLoopConditionOutOfBody::run(OptimiserStepContext& _context, Block& _ast)
{
ForLoopConditionOutOfBody{_context.dialect}(_ast);
}
void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop)
{ {
ASTModifier::operator()(_forLoop); ASTModifier::operator()(_forLoop);

View File

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
namespace yul namespace yul
@ -56,13 +57,17 @@ namespace yul
class ForLoopConditionOutOfBody: public ASTModifier class ForLoopConditionOutOfBody: public ASTModifier
{ {
public: public:
ForLoopConditionOutOfBody(Dialect const& _dialect): static constexpr char const* name{"ForLoopConditionOutOfBody"};
m_dialect(_dialect) static void run(OptimiserStepContext&, Block& _ast);
{}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override; void operator()(ForLoop& _forLoop) override;
private: private:
ForLoopConditionOutOfBody(Dialect const& _dialect):
m_dialect(_dialect)
{}
Dialect const& m_dialect; Dialect const& m_dialect;
}; };

View File

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -29,8 +30,17 @@ namespace yul
class ForLoopInitRewriter: public ASTModifier class ForLoopInitRewriter: public ASTModifier
{ {
public: public:
static constexpr char const* name{"ForLoopInitRewriter"};
static void run(OptimiserStepContext&, Block& _ast)
{
ForLoopInitRewriter{}(_ast);
}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Block& _block) override; void operator()(Block& _block) override;
private:
ForLoopInitRewriter() = default;
}; };
} }

View File

@ -38,6 +38,11 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
{
FullInliner{_ast, _context.dispenser}.run();
}
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
m_ast(_ast), m_nameDispenser(_dispenser) m_ast(_ast), m_nameDispenser(_dispenser)
{ {

View File

@ -24,6 +24,7 @@
#include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTCopier.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/NameDispenser.h> #include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
@ -69,9 +70,8 @@ class NameCollector;
class FullInliner: public ASTModifier class FullInliner: public ASTModifier
{ {
public: public:
explicit FullInliner(Block& _ast, NameDispenser& _dispenser); static constexpr char const* name{"FullInliner"};
static void run(OptimiserStepContext&, Block& _ast);
void run();
/// Inlining heuristic. /// Inlining heuristic.
/// @param _callSite the name of the function in which the function call is located. /// @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); void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
private: private:
FullInliner(Block& _ast, NameDispenser& _dispenser);
void run();
void updateCodeSize(FunctionDefinition const& _fun); void updateCodeSize(FunctionDefinition const& _fun);
void handleBlock(YulString _currentFunctionName, Block& _block); void handleBlock(YulString _currentFunctionName, Block& _block);
bool recursive(FunctionDefinition const& _fun) const; bool recursive(FunctionDefinition const& _fun) const;

View File

@ -26,6 +26,8 @@
namespace yul namespace yul
{ {
struct OptimiserStepContext;
/** /**
* Moves all instructions in a block into a new block at the start of the block, followed by * Moves all instructions in a block into a new block at the start of the block, followed by
* all function definitions. * all function definitions.
@ -37,9 +39,14 @@ namespace yul
class FunctionGrouper class FunctionGrouper
{ {
public: public:
static constexpr char const* name{"FunctionGrouper"};
static void run(OptimiserStepContext&, Block& _ast) { FunctionGrouper{}(_ast); }
void operator()(Block& _block); void operator()(Block& _block);
private: private:
FunctionGrouper() = default;
bool alreadyGrouped(Block const& _block); bool alreadyGrouped(Block const& _block);
}; };

View File

@ -26,6 +26,7 @@
namespace yul namespace yul
{ {
struct OptimiserStepContext;
/** /**
* Moves all functions to the top-level scope. * Moves all functions to the top-level scope.
@ -37,10 +38,15 @@ namespace yul
class FunctionHoister: public ASTModifier class FunctionHoister: public ASTModifier
{ {
public: public:
static constexpr char const* name{"FunctionHoister"};
static void run(OptimiserStepContext&, Block& _ast) { FunctionHoister{}(_ast); }
using ASTModifier::operator(); using ASTModifier::operator();
virtual void operator()(Block& _block); virtual void operator()(Block& _block);
private: private:
FunctionHoister() = default;
bool m_isTopLevel = true; bool m_isTopLevel = true;
std::vector<Statement> m_functions; std::vector<Statement> m_functions;
}; };

View File

@ -31,12 +31,12 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; 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{ LoadResolver{
_dialect, _context.dialect,
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast)), SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)),
!containsMSize !containsMSize
}(_ast); }(_ast);
} }

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h> #include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
namespace yul namespace yul
@ -41,8 +42,9 @@ struct BuiltinFunctionForEVM;
class LoadResolver: public DataFlowAnalyzer class LoadResolver: public DataFlowAnalyzer
{ {
public: public:
static constexpr char const* name{"LoadResolver"};
/// Run the load resolver on the given complete AST. /// Run the load resolver on the given complete AST.
static void run(Dialect const& _dialect, Block& _ast); static void run(OptimiserStepContext&, Block& _ast);
private: private:
LoadResolver( LoadResolver(

View File

@ -26,13 +26,21 @@
namespace yul namespace yul
{ {
struct OptimiserStepContext;
/** /**
* Prerequisites: Function Grouper * Prerequisites: Function Grouper
*/ */
class MainFunction class MainFunction
{ {
public: public:
static constexpr char const* name{"MainFunction"};
static void run(OptimiserStepContext&, Block& _ast) { MainFunction{}(_ast); }
void operator()(Block& _block); void operator()(Block& _block);
private:
MainFunction() = default;
}; };
} }

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);
}
};
}

View File

@ -32,6 +32,15 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; 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) void RedundantAssignEliminator::operator()(Identifier const& _identifier)
{ {
changeUndecidedTo(_identifier.name, State::Used); changeUndecidedTo(_identifier.name, State::Used);
@ -204,14 +213,6 @@ void RedundantAssignEliminator::operator()(Block const& _block)
swap(m_declaredVariables, outerDeclaredVariables); 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> template <class K, class V, class F>
void joinMap(std::map<K, V>& _a, std::map<K, V>&& _b, F _conflictSolver) void joinMap(std::map<K, V>& _a, std::map<K, V>&& _b, F _conflictSolver)

View File

@ -23,6 +23,7 @@
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <map> #include <map>
#include <vector> #include <vector>
@ -105,6 +106,9 @@ struct Dialect;
class RedundantAssignEliminator: public ASTWalker class RedundantAssignEliminator: public ASTWalker
{ {
public: public:
static constexpr char const* name{"RedundantAssignEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
explicit RedundantAssignEliminator(Dialect const& _dialect): m_dialect(&_dialect) {} explicit RedundantAssignEliminator(Dialect const& _dialect): m_dialect(&_dialect) {}
RedundantAssignEliminator() = delete; RedundantAssignEliminator() = delete;
RedundantAssignEliminator(RedundantAssignEliminator const&) = delete; RedundantAssignEliminator(RedundantAssignEliminator const&) = delete;
@ -123,8 +127,6 @@ public:
void operator()(Continue const&) override; void operator()(Continue const&) override;
void operator()(Block const& _block) override; void operator()(Block const& _block) override;
static void run(Dialect const& _dialect, Block& _ast);
private: private:
class State class State
{ {

View File

@ -21,6 +21,7 @@
#pragma once #pragma once
#include <libyul/optimiser/DataFlowAnalyzer.h> #include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -38,6 +39,12 @@ namespace yul
class Rematerialiser: public DataFlowAnalyzer class Rematerialiser: public DataFlowAnalyzer
{ {
public: public:
static constexpr char const* name{"Rematerialiser"};
static void run(
OptimiserStepContext& _context,
Block& _ast
) { run(_context.dialect, _ast); }
static void run( static void run(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
@ -80,12 +87,19 @@ protected:
class LiteralRematerialiser: public DataFlowAnalyzer class LiteralRematerialiser: public DataFlowAnalyzer
{ {
public: public:
LiteralRematerialiser(Dialect const& _dialect): static constexpr char const* name{"LiteralRematerialiser"};
DataFlowAnalyzer(_dialect) static void run(
{} OptimiserStepContext& _context,
Block& _ast
) { LiteralRematerialiser{_context.dialect}(_ast); }
using ASTModifier::visit; using ASTModifier::visit;
void visit(Expression& _e) override; void visit(Expression& _e) override;
private:
LiteralRematerialiser(Dialect const& _dialect):
DataFlowAnalyzer(_dialect)
{}
}; };

View File

@ -23,7 +23,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
void SSAReverser::run(Block& _block) void SSAReverser::run(OptimiserStepContext&, Block& _block)
{ {
AssignmentCounter assignmentCounter; AssignmentCounter assignmentCounter;
assignmentCounter(_block); assignmentCounter(_block);

View File

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -69,12 +70,15 @@ class AssignmentCounter;
class SSAReverser: public ASTModifier class SSAReverser: public ASTModifier
{ {
public: public:
static constexpr char const* name{"SSAReverser"};
static void run(OptimiserStepContext& _context, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Block& _block) override; void operator()(Block& _block) override;
static void run(Block& _block);
private: private:
SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {} explicit SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {}
AssignmentCounter const& m_assignmentCounter; AssignmentCounter const& m_assignmentCounter;
}; };

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 assignments;
assignments(_ast); assignments(_ast);
IntroduceSSA{_nameDispenser, assignments.names()}(_ast); IntroduceSSA{_context.dispenser, assignments.names()}(_ast);
IntroduceControlFlowSSA{_nameDispenser, assignments.names()}(_ast); IntroduceControlFlowSSA{_context.dispenser, assignments.names()}(_ast);
PropagateValues{assignments.names()}(_ast); PropagateValues{assignments.names()}(_ast);
} }

View File

@ -22,6 +22,7 @@
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
@ -89,7 +90,8 @@ class NameDispenser;
class SSATransform: public ASTModifier class SSATransform: public ASTModifier
{ {
public: public:
static void run(Block& _ast, NameDispenser& _nameDispenser); static constexpr char const* name{"SSATransform"};
static void run(OptimiserStepContext& _context, Block& _ast);
}; };
} }

View File

@ -59,6 +59,11 @@ OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _const
} }
void StructuralSimplifier::run(OptimiserStepContext&, Block& _ast)
{
StructuralSimplifier{}(_ast);
}
void StructuralSimplifier::operator()(Block& _block) void StructuralSimplifier::operator()(Block& _block)
{ {
simplify(_block.statements); simplify(_block.statements);

View File

@ -18,6 +18,7 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/DataFlowAnalyzer.h> #include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
namespace yul namespace yul
@ -39,9 +40,14 @@ namespace yul
class StructuralSimplifier: public ASTModifier class StructuralSimplifier: public ASTModifier
{ {
public: public:
static constexpr char const* name{"StructuralSimplifier"};
static void run(OptimiserStepContext&, Block& _ast);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: private:
StructuralSimplifier() = default;
void simplify(std::vector<Statement>& _statements); void simplify(std::vector<Statement>& _statements);
bool expressionAlwaysTrue(Expression const& _expression); bool expressionAlwaysTrue(Expression const& _expression);
bool expressionAlwaysFalse(Expression const& _expression); bool expressionAlwaysFalse(Expression const& _expression);

View File

@ -83,26 +83,28 @@ void OptimiserSuite::run(
)(*_object.code)); )(*_object.code));
Block& ast = *_object.code; Block& ast = *_object.code;
VarDeclInitializer{}(ast); OptimiserSuite suite(_dialect, reservedIdentifiers, Debug::None, ast);
FunctionHoister{}(ast);
BlockFlattener{}(ast); suite.runSequence({
ForLoopInitRewriter{}(ast); VarDeclInitializer::name,
DeadCodeEliminator{_dialect}(ast); FunctionHoister::name,
FunctionGrouper{}(ast); BlockFlattener::name,
EquivalentFunctionCombiner::run(ast); ForLoopInitRewriter::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); DeadCodeEliminator::name,
BlockFlattener{}(ast); FunctionGrouper::name,
ControlFlowSimplifier{_dialect}(ast); EquivalentFunctionCombiner::name,
LiteralRematerialiser{_dialect}(ast); UnusedPruner::name,
StructuralSimplifier{}(ast); BlockFlattener::name,
ControlFlowSimplifier{_dialect}(ast); ControlFlowSimplifier::name,
ForLoopConditionIntoBody{_dialect}(ast); LiteralRematerialiser::name,
BlockFlattener{}(ast); StructuralSimplifier::name,
ControlFlowSimplifier::name,
ForLoopConditionIntoBody::name,
BlockFlattener::name
}, ast);
// None of the above can make stack problems worse. // None of the above can make stack problems worse.
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
size_t codeSize = 0; size_t codeSize = 0;
for (size_t rounds = 0; rounds < 12; ++rounds) for (size_t rounds = 0; rounds < 12; ++rounds)
{ {
@ -115,120 +117,138 @@ void OptimiserSuite::run(
{ {
// Turn into SSA and simplify // Turn into SSA and simplify
ExpressionSplitter{_dialect, dispenser}(ast); suite.runSequence({
SSATransform::run(ast, dispenser); ExpressionSplitter::name,
RedundantAssignEliminator::run(_dialect, ast); SSATransform::name,
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::name,
RedundantAssignEliminator::name,
ExpressionSimplifier::run(_dialect, ast); ExpressionSimplifier::name,
CommonSubexpressionEliminator::name,
CommonSubexpressionEliminator::run(_dialect, ast); LoadResolver::name
LoadResolver::run(_dialect, ast); }, ast);
} }
{ {
// still in SSA, perform structural simplification // still in SSA, perform structural simplification
LiteralRematerialiser{_dialect}(ast); suite.runSequence({
ForLoopConditionOutOfBody{_dialect}(ast); LiteralRematerialiser::name,
ControlFlowSimplifier{_dialect}(ast); ForLoopConditionOutOfBody::name,
StructuralSimplifier{}(ast); ControlFlowSimplifier::name,
ControlFlowSimplifier{_dialect}(ast); StructuralSimplifier::name,
BlockFlattener{}(ast); ControlFlowSimplifier::name,
DeadCodeEliminator{_dialect}(ast); BlockFlattener::name,
ForLoopConditionIntoBody{_dialect}(ast); DeadCodeEliminator::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); ForLoopConditionIntoBody::name,
UnusedPruner::name
}, ast);
} }
{ {
// simplify again // simplify again
LoadResolver::run(_dialect, ast); suite.runSequence({
CommonSubexpressionEliminator::run(_dialect, ast); LoadResolver::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); CommonSubexpressionEliminator::name,
UnusedPruner::name,
}, ast);
} }
{ {
// reverse SSA // reverse SSA
SSAReverser::run(ast); suite.runSequence({
CommonSubexpressionEliminator::run(_dialect, ast); SSAReverser::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); CommonSubexpressionEliminator::name,
UnusedPruner::name,
ExpressionJoiner::run(ast); ExpressionJoiner::name,
ExpressionJoiner::run(ast); ExpressionJoiner::name,
}, ast);
} }
// should have good "compilability" property here. // should have good "compilability" property here.
{ {
// run functional expression inliner // run functional expression inliner
ExpressionInliner(_dialect, ast).run(); suite.runSequence({
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); ExpressionInliner::name,
UnusedPruner::name,
}, ast);
} }
{ {
// Turn into SSA again and simplify // Turn into SSA again and simplify
ExpressionSplitter{_dialect, dispenser}(ast); suite.runSequence({
SSATransform::run(ast, dispenser); ExpressionSplitter::name,
RedundantAssignEliminator::run(_dialect, ast); SSATransform::name,
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::name,
CommonSubexpressionEliminator::run(_dialect, ast); RedundantAssignEliminator::name,
LoadResolver::run(_dialect, ast); CommonSubexpressionEliminator::name,
LoadResolver::name,
}, ast);
} }
{ {
// run full inliner // run full inliner
FunctionGrouper{}(ast); suite.runSequence({
EquivalentFunctionCombiner::run(ast); FunctionGrouper::name,
FullInliner{ast, dispenser}.run(); EquivalentFunctionCombiner::name,
BlockFlattener{}(ast); FullInliner::name,
BlockFlattener::name
}, ast);
} }
{ {
// SSA plus simplify // SSA plus simplify
SSATransform::run(ast, dispenser); suite.runSequence({
RedundantAssignEliminator::run(_dialect, ast); SSATransform::name,
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::name,
LoadResolver::run(_dialect, ast); RedundantAssignEliminator::name,
ExpressionSimplifier::run(_dialect, ast); LoadResolver::name,
LiteralRematerialiser{_dialect}(ast); ExpressionSimplifier::name,
ForLoopConditionOutOfBody{_dialect}(ast); LiteralRematerialiser::name,
StructuralSimplifier{}(ast); ForLoopConditionOutOfBody::name,
BlockFlattener{}(ast); StructuralSimplifier::name,
DeadCodeEliminator{_dialect}(ast); BlockFlattener::name,
ControlFlowSimplifier{_dialect}(ast); DeadCodeEliminator::name,
CommonSubexpressionEliminator::run(_dialect, ast); ControlFlowSimplifier::name,
SSATransform::run(ast, dispenser); CommonSubexpressionEliminator::name,
RedundantAssignEliminator::run(_dialect, ast); SSATransform::name,
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::name,
ForLoopConditionIntoBody{_dialect}(ast); RedundantAssignEliminator::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); ForLoopConditionIntoBody::name,
CommonSubexpressionEliminator::run(_dialect, ast); UnusedPruner::name,
CommonSubexpressionEliminator::name,
}, ast);
} }
} }
// Make source short and pretty. // Make source short and pretty.
ExpressionJoiner::run(ast); suite.runSequence({
Rematerialiser::run(_dialect, ast); ExpressionJoiner::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); Rematerialiser::name,
ExpressionJoiner::run(ast); UnusedPruner::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); ExpressionJoiner::name,
ExpressionJoiner::run(ast); UnusedPruner::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); ExpressionJoiner::name,
UnusedPruner::name,
SSAReverser::run(ast); SSAReverser::name,
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::name,
LiteralRematerialiser{_dialect}(ast); LiteralRematerialiser::name,
ForLoopConditionOutOfBody{_dialect}(ast); ForLoopConditionOutOfBody::name,
CommonSubexpressionEliminator::run(_dialect, ast); CommonSubexpressionEliminator::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); UnusedPruner::name,
ExpressionJoiner::run(ast); ExpressionJoiner::name,
Rematerialiser::run(_dialect, ast); Rematerialiser::name,
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); UnusedPruner::name,
}, ast);
// 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;
FunctionGrouper{}(ast); suite.runSequence({
FunctionGrouper::name
}, ast);
// We ignore the return value because we will get a much better error // We ignore the return value because we will get a much better error
// message once we perform code generation. // message once we perform code generation.
StackCompressor::run( StackCompressor::run(
@ -237,14 +257,16 @@ void OptimiserSuite::run(
_optimizeStackAllocation, _optimizeStackAllocation,
stackCompressorMaxIterations stackCompressorMaxIterations
); );
BlockFlattener{}(ast); suite.runSequence({
DeadCodeEliminator{_dialect}(ast); BlockFlattener::name,
ControlFlowSimplifier{_dialect}(ast); DeadCodeEliminator::name,
LiteralRematerialiser{_dialect}(ast); ControlFlowSimplifier::name,
ForLoopConditionOutOfBody{_dialect}(ast); LiteralRematerialiser::name,
CommonSubexpressionEliminator::run(_dialect, ast); ForLoopConditionOutOfBody::name,
CommonSubexpressionEliminator::name,
FunctionGrouper{}(ast); FunctionGrouper::name,
}, ast);
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect)) 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()) if (ast.statements.size() > 1 && boost::get<Block>(ast.statements.front()).statements.empty())
ast.statements.erase(ast.statements.begin()); ast.statements.erase(ast.statements.begin());
} }
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast); suite.runSequence({
VarNameCleaner::name
}, ast);
*_object.analysisInfo = AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); *_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);
}
}

View File

@ -22,9 +22,13 @@
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/NameDispenser.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <set> #include <set>
#include <string>
#include <memory>
namespace yul namespace yul
{ {
@ -41,6 +45,11 @@ struct Object;
class OptimiserSuite class OptimiserSuite
{ {
public: public:
enum class Debug
{
None,
PrintStep
};
static void run( static void run(
Dialect const& _dialect, Dialect const& _dialect,
GasMeter const* _meter, GasMeter const* _meter,
@ -48,6 +57,26 @@ public:
bool _optimizeStackAllocation, bool _optimizeStackAllocation,
std::set<YulString> const& _externallyUsedIdentifiers = {} 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;
}; };
} }

View File

@ -21,6 +21,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <map> #include <map>
@ -47,19 +48,11 @@ struct SideEffects;
class UnusedPruner: public ASTModifier class UnusedPruner: public ASTModifier
{ {
public: public:
UnusedPruner( static constexpr char const* name{"UnusedPruner"};
Dialect const& _dialect, static void run(OptimiserStepContext& _context, Block& _ast) {
Block& _ast, UnusedPruner::runUntilStabilisedOnFullAST(_context.dialect, _ast, _context.reservedIdentifiers);
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 = {}
);
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Block& _block) override; void operator()(Block& _block) override;
@ -76,6 +69,15 @@ public:
std::set<YulString> const& _externallyUsedFunctions = {} 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. /// Run the pruner until the code does not change anymore.
/// The provided block has to be a full AST. /// The provided block has to be a full AST.
/// The pruner itself determines if msize is used and which user-defined functions /// The pruner itself determines if msize is used and which user-defined functions
@ -99,6 +101,20 @@ public:
); );
private: 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; bool used(YulString _name) const;
void subtractReferences(std::map<YulString, size_t> const& _subtrahend); void subtractReferences(std::map<YulString, size_t> const& _subtrahend);

View File

@ -19,6 +19,7 @@
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
namespace yul namespace yul
{ {
@ -32,6 +33,9 @@ namespace yul
class VarDeclInitializer: public ASTModifier class VarDeclInitializer: public ASTModifier
{ {
public: public:
static constexpr char const* name{"VarDeclInitializer"};
static void run(OptimiserStepContext&, Block& _ast) { VarDeclInitializer{}(_ast); }
void operator()(Block& _block) override; void operator()(Block& _block) override;
}; };

View File

@ -21,6 +21,7 @@
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <map> #include <map>
#include <set> #include <set>
@ -43,11 +44,11 @@ struct Dialect;
class VarNameCleaner: public ASTModifier class VarNameCleaner: public ASTModifier
{ {
public: public:
VarNameCleaner( static constexpr char const* name{"VarNameCleaner"};
Block const& _ast, static void run(OptimiserStepContext& _context, Block& _ast)
Dialect const& _dialect, {
std::set<YulString> _blacklist = {} VarNameCleaner{_ast, _context.dialect, _context.reservedIdentifiers}(_ast);
); }
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(VariableDeclaration& _varDecl) override; void operator()(VariableDeclaration& _varDecl) override;
@ -55,6 +56,12 @@ public:
void operator()(FunctionDefinition& _funDef) override; void operator()(FunctionDefinition& _funDef) override;
private: private:
VarNameCleaner(
Block const& _ast,
Dialect const& _dialect,
std::set<YulString> _blacklist = {}
);
/// Tries to rename a list of variables. /// Tries to rename a list of variables.
void renameVariables(std::vector<TypedName>& _variables); void renameVariables(std::vector<TypedName>& _variables);

View File

@ -3,8 +3,8 @@
{ {
"smtlib2responses": "smtlib2responses":
{ {
"0x047d0c67d7e03c5ac96ca227d1e19ba63257f4ab19cef30029413219ec8963af": "sat\n((|EVALEXPR_0| 0))\n", "0x82fb8ee094f0f56b7a63a74177b54a1710d6fc531d426f288c18f36b76cf6a8b": "sat\n((|EVALEXPR_0| 1))\n",
"0xada7569fb01a9b3e2823517ed40dcc99b11fb1e433e6e3ec8a8713f6f95753d3": "sat\n((|EVALEXPR_0| 1))\n" "0xb524e7c577188e2e36f0e67fead51269fa0f8b8fb41bff2d973dcf584d38cd1e": "sat\n((|EVALEXPR_0| 0))\n"
} }
} }
} }

View File

@ -3,7 +3,7 @@
{ {
"smtlib2responses": "smtlib2responses":
{ {
"0x2e32517a1410b1a16decd448bb9bac7789d7cf1c6f98703ed6bacfcad6abebfb": "sat\n((|EVALEXPR_0| 0))\n" "0x45c37a9829e623d7838d82b547d297cd446d6b5faff36c53a56862fcee50fb41": "sat\n((|EVALEXPR_0| 0))\n"
} }
} }
} }

View File

@ -2,4 +2,4 @@ contract C {
function f() internal pure returns (mapping(uint=>uint) storage r) { } function f() internal pure returns (mapping(uint=>uint) storage r) { }
} }
// ---- // ----
// TypeError: (53-82): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (53-82): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -2,4 +2,4 @@ contract C {
function f() internal pure returns (mapping(uint=>uint) storage) {} function f() internal pure returns (mapping(uint=>uint) storage) {}
} }
// ---- // ----
// TypeError: (53-80): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (53-80): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -7,4 +7,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (87-96): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (87-96): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -45,12 +45,12 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// Warning: (146-151): Unreachable code. // Warning: (146-151): Unreachable code.
// Warning: (169-174): Unreachable code. // Warning: (169-174): Unreachable code.
// TypeError: (223-234): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (223-234): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// Warning: (316-321): Unreachable code. // Warning: (316-321): Unreachable code.
// TypeError: (440-451): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (440-451): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (654-665): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (654-665): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (871-882): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (871-882): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// Warning: (933-938): Unreachable code. // Warning: (933-938): Unreachable code.

View File

@ -12,5 +12,5 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (182-193): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (182-193): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -14,5 +14,5 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (186-197): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (186-197): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -18,5 +18,5 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (249-258): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (249-258): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (367-376): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (367-376): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -13,6 +13,6 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (176-187): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (176-187): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (264-275): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (264-275): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -9,5 +9,5 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.
// TypeError: (200-211): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (200-211): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -8,4 +8,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment. // TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour.

View File

@ -6,4 +6,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (92-116): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (92-116): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -5,4 +5,4 @@ contract C {
function f() m1(b) m2(b = s) internal view returns (uint[] storage b) {} function f() m1(b) m2(b = s) internal view returns (uint[] storage b) {}
} }
// ---- // ----
// TypeError: (129-130): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (129-130): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -10,4 +10,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -10,4 +10,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -7,4 +7,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (94-95): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (94-95): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -8,4 +8,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (109-110): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (109-110): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -8,9 +8,9 @@ library L {
function i(uint[] calldata, uint[] storage) external pure returns (S storage x) {return x; } function i(uint[] calldata, uint[] storage) external pure returns (S storage x) {return x; }
} }
// ---- // ----
// TypeError: (197-198): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (197-198): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (203-204): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (203-204): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (359-360): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (359-360): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (365-366): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (365-366): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (460-461): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (460-461): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (557-558): This variable is of storage pointer type and can be accessed without prior assignment. // TypeError: (557-558): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -45,6 +45,7 @@
#include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/ExpressionSimplifier.h>
#include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/UnusedPruner.h>
#include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSAReverser.h>
#include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/SSATransform.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
@ -114,21 +115,23 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
return TestResult::FatalError; return TestResult::FatalError;
soltestAssert(m_dialect, "Dialect not set."); soltestAssert(m_dialect, "Dialect not set.");
updateContext();
if (m_optimizerStep == "disambiguator") if (m_optimizerStep == "disambiguator")
disambiguate(); disambiguate();
else if (m_optimizerStep == "nameDisplacer") else if (m_optimizerStep == "nameDisplacer")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast};
NameDisplacer{ NameDisplacer{
nameDispenser, *m_nameDispenser,
{"illegal1"_yulstring, "illegal2"_yulstring, "illegal3"_yulstring, "illegal4"_yulstring, "illegal5"_yulstring} {"illegal1"_yulstring, "illegal2"_yulstring, "illegal3"_yulstring, "illegal4"_yulstring, "illegal5"_yulstring}
}(*m_ast); }(*m_ast);
} }
else if (m_optimizerStep == "blockFlattener") else if (m_optimizerStep == "blockFlattener")
{ {
disambiguate(); disambiguate();
BlockFlattener{}(*m_ast); BlockFlattener::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "constantOptimiser") else if (m_optimizerStep == "constantOptimiser")
{ {
@ -136,193 +139,182 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
ConstantOptimiser{dynamic_cast<EVMDialect const&>(*m_dialect), meter}(*m_ast); ConstantOptimiser{dynamic_cast<EVMDialect const&>(*m_dialect), meter}(*m_ast);
} }
else if (m_optimizerStep == "varDeclInitializer") else if (m_optimizerStep == "varDeclInitializer")
VarDeclInitializer{}(*m_ast); VarDeclInitializer::run(*m_context, *m_ast);
else if (m_optimizerStep == "varNameCleaner") else if (m_optimizerStep == "varNameCleaner")
VarNameCleaner{*m_ast, *m_dialect}(*m_ast); VarNameCleaner::run(*m_context, *m_ast);
else if (m_optimizerStep == "forLoopConditionIntoBody") else if (m_optimizerStep == "forLoopConditionIntoBody")
{ {
disambiguate(); disambiguate();
ForLoopConditionIntoBody{*m_dialect}(*m_ast); ForLoopConditionIntoBody::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "forLoopInitRewriter") else if (m_optimizerStep == "forLoopInitRewriter")
{ {
disambiguate(); disambiguate();
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "commonSubexpressionEliminator") else if (m_optimizerStep == "commonSubexpressionEliminator")
{ {
disambiguate(); disambiguate();
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); CommonSubexpressionEliminator::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "expressionSplitter") else if (m_optimizerStep == "expressionSplitter")
{ ExpressionSplitter::run(*m_context, *m_ast);
NameDispenser nameDispenser{*m_dialect, *m_ast};
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast);
}
else if (m_optimizerStep == "expressionJoiner") else if (m_optimizerStep == "expressionJoiner")
{ {
disambiguate(); disambiguate();
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "splitJoin") else if (m_optimizerStep == "splitJoin")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter::run(*m_context, *m_ast);
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast);
} }
else if (m_optimizerStep == "functionGrouper") else if (m_optimizerStep == "functionGrouper")
{ {
disambiguate(); disambiguate();
(FunctionGrouper{})(*m_ast); FunctionGrouper::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "functionHoister") else if (m_optimizerStep == "functionHoister")
{ {
disambiguate(); disambiguate();
(FunctionHoister{})(*m_ast); FunctionHoister::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "expressionInliner") else if (m_optimizerStep == "expressionInliner")
{ {
disambiguate(); disambiguate();
ExpressionInliner(*m_dialect, *m_ast).run(); ExpressionInliner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "fullInliner") else if (m_optimizerStep == "fullInliner")
{ {
disambiguate(); disambiguate();
(FunctionHoister{})(*m_ast); FunctionHoister::run(*m_context, *m_ast);
(FunctionGrouper{})(*m_ast); FunctionGrouper::run(*m_context, *m_ast);
NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter::run(*m_context, *m_ast);
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); FullInliner::run(*m_context, *m_ast);
FullInliner(*m_ast, nameDispenser).run(); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast);
} }
else if (m_optimizerStep == "mainFunction") else if (m_optimizerStep == "mainFunction")
{ {
disambiguate(); disambiguate();
(FunctionGrouper{})(*m_ast); FunctionGrouper::run(*m_context, *m_ast);
(MainFunction{})(*m_ast); MainFunction::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "rematerialiser") else if (m_optimizerStep == "rematerialiser")
{ {
disambiguate(); disambiguate();
Rematerialiser::run(*m_dialect, *m_ast); Rematerialiser::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "expressionSimplifier") else if (m_optimizerStep == "expressionSimplifier")
{ {
disambiguate(); disambiguate();
ExpressionSimplifier::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "fullSimplify") else if (m_optimizerStep == "fullSimplify")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter::run(*m_context, *m_ast);
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); ForLoopInitRewriter::run(*m_context, *m_ast);
ForLoopInitRewriter{}(*m_ast); CommonSubexpressionEliminator::run(*m_context, *m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast); UnusedPruner::run(*m_context, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast); DeadCodeEliminator::run(*m_context, *m_ast);
DeadCodeEliminator{*m_dialect}(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast);
} }
else if (m_optimizerStep == "unusedPruner") else if (m_optimizerStep == "unusedPruner")
{ {
disambiguate(); disambiguate();
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast); UnusedPruner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "deadCodeEliminator") else if (m_optimizerStep == "deadCodeEliminator")
{ {
disambiguate(); disambiguate();
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter::run(*m_context, *m_ast);
DeadCodeEliminator{*m_dialect}(*m_ast); DeadCodeEliminator::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "ssaTransform") else if (m_optimizerStep == "ssaTransform")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast}; SSATransform::run(*m_context, *m_ast);
SSATransform::run(*m_ast, nameDispenser);
} }
else if (m_optimizerStep == "redundantAssignEliminator") else if (m_optimizerStep == "redundantAssignEliminator")
{ {
disambiguate(); disambiguate();
RedundantAssignEliminator::run(*m_dialect, *m_ast); RedundantAssignEliminator::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "ssaPlusCleanup") else if (m_optimizerStep == "ssaPlusCleanup")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast}; SSATransform::run(*m_context, *m_ast);
SSATransform::run(*m_ast, nameDispenser); RedundantAssignEliminator::run(*m_context, *m_ast);
RedundantAssignEliminator::run(*m_dialect, *m_ast);
} }
else if (m_optimizerStep == "loadResolver") else if (m_optimizerStep == "loadResolver")
{ {
disambiguate(); disambiguate();
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter::run(*m_context, *m_ast);
NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter::run(*m_context, *m_ast);
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); CommonSubexpressionEliminator::run(*m_context, *m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_context, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast);
LoadResolver::run(*m_dialect, *m_ast); LoadResolver::run(*m_context, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast); UnusedPruner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "controlFlowSimplifier") else if (m_optimizerStep == "controlFlowSimplifier")
{ {
disambiguate(); disambiguate();
ControlFlowSimplifier{*m_dialect}(*m_ast); ControlFlowSimplifier::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "structuralSimplifier") else if (m_optimizerStep == "structuralSimplifier")
{ {
disambiguate(); disambiguate();
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter::run(*m_context, *m_ast);
LiteralRematerialiser{*m_dialect}(*m_ast); LiteralRematerialiser::run(*m_context, *m_ast);
StructuralSimplifier{}(*m_ast); StructuralSimplifier::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "equivalentFunctionCombiner") else if (m_optimizerStep == "equivalentFunctionCombiner")
{ {
disambiguate(); disambiguate();
EquivalentFunctionCombiner::run(*m_ast); EquivalentFunctionCombiner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "ssaReverser") else if (m_optimizerStep == "ssaReverser")
{ {
disambiguate(); disambiguate();
SSAReverser::run(*m_ast); SSAReverser::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "ssaAndBack") else if (m_optimizerStep == "ssaAndBack")
{ {
disambiguate(); disambiguate();
// apply SSA // apply SSA
NameDispenser nameDispenser{*m_dialect, *m_ast}; SSATransform::run(*m_context, *m_ast);
SSATransform::run(*m_ast, nameDispenser); RedundantAssignEliminator::run(*m_context, *m_ast);
RedundantAssignEliminator::run(*m_dialect, *m_ast);
// reverse SSA // reverse SSA
SSAReverser::run(*m_ast); SSAReverser::run(*m_context, *m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast); CommonSubexpressionEliminator::run(*m_context, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast); UnusedPruner::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "stackCompressor") else if (m_optimizerStep == "stackCompressor")
{ {
disambiguate(); disambiguate();
(FunctionGrouper{})(*m_ast); FunctionGrouper::run(*m_context, *m_ast);
size_t maxIterations = 16; size_t maxIterations = 16;
Object obj; Object obj;
obj.code = m_ast; obj.code = m_ast;
StackCompressor::run(*m_dialect, obj, true, maxIterations); StackCompressor::run(*m_dialect, obj, true, maxIterations);
m_ast = obj.code; m_ast = obj.code;
(BlockFlattener{})(*m_ast); BlockFlattener::run(*m_context, *m_ast);
} }
else if (m_optimizerStep == "wordSizeTransform") else if (m_optimizerStep == "wordSizeTransform")
{ {
disambiguate(); disambiguate();
NameDispenser nameDispenser{*m_dialect, *m_ast}; ExpressionSplitter::run(*m_context, *m_ast);
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast); WordSizeTransform::run(*m_dialect, *m_ast, *m_nameDispenser);
WordSizeTransform::run(*m_dialect, *m_ast, nameDispenser);
} }
else if (m_optimizerStep == "fullSuite") else if (m_optimizerStep == "fullSuite")
{ {
@ -413,6 +405,13 @@ void YulOptimizerTest::disambiguate()
{ {
*m_ast = boost::get<Block>(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); *m_ast = boost::get<Block>(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast));
m_analysisInfo.reset(); m_analysisInfo.reset();
updateContext();
}
void YulOptimizerTest::updateContext()
{
m_nameDispenser = make_unique<NameDispenser>(*m_dialect, *m_ast, m_reservedIdentifiers);
m_context = unique_ptr<OptimiserStepContext>(new OptimiserStepContext{*m_dialect, *m_nameDispenser, m_reservedIdentifiers});
} }
void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors) void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors)

View File

@ -19,6 +19,14 @@
#include <test/TestCase.h> #include <test/TestCase.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/YulString.h>
#include <set>
#include <memory>
namespace langutil namespace langutil
{ {
class Scanner; class Scanner;
@ -58,6 +66,7 @@ private:
void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const; void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const;
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
void disambiguate(); void disambiguate();
void updateContext();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
@ -67,6 +76,10 @@ private:
std::string m_expectation; std::string m_expectation;
Dialect const* m_dialect = nullptr; Dialect const* m_dialect = nullptr;
std::set<YulString> m_reservedIdentifiers;
std::unique_ptr<NameDispenser> m_nameDispenser;
std::unique_ptr<OptimiserStepContext> m_context;
std::shared_ptr<Block> m_ast; std::shared_ptr<Block> m_ast;
std::shared_ptr<AsmAnalysisInfo> m_analysisInfo; std::shared_ptr<AsmAnalysisInfo> m_analysisInfo;
std::string m_obtainedResult; std::string m_obtainedResult;

View File

@ -51,6 +51,7 @@
#include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/UnusedPruner.h>
#include <libyul/optimiser/DeadCodeEliminator.h> #include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/RedundantAssignEliminator.h>
#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSAReverser.h>
#include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/SSATransform.h>
@ -126,11 +127,12 @@ public:
cout << source << endl; cout << source << endl;
if (!parse(source)) if (!parse(source))
return; return;
set<YulString> reservedIdentifiers;
if (!disambiguated) if (!disambiguated)
{ {
*m_ast = boost::get<yul::Block>(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast)); *m_ast = boost::get<yul::Block>(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast));
m_analysisInfo.reset(); m_analysisInfo.reset();
m_nameDispenser = make_shared<NameDispenser>(m_dialect, *m_ast); m_nameDispenser = make_shared<NameDispenser>(m_dialect, *m_ast, reservedIdentifiers);
disambiguated = true; disambiguated = true;
} }
cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl;
@ -141,78 +143,83 @@ public:
cout.flush(); cout.flush();
int option = readStandardInputChar(); int option = readStandardInputChar();
cout << ' ' << char(option) << endl; cout << ' ' << char(option) << endl;
OptimiserStepContext context{m_dialect, *m_nameDispenser, reservedIdentifiers};
switch (option) switch (option)
{ {
case 'q': case 'q':
return; return;
case 'f': case 'f':
BlockFlattener{}(*m_ast); BlockFlattener::run(context, *m_ast);
break; break;
case 'o': case 'o':
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter::run(context, *m_ast);
break; break;
case 'O': case 'O':
ForLoopConditionOutOfBody{m_dialect}(*m_ast); ForLoopConditionOutOfBody::run(context, *m_ast);
break; break;
case 'I': case 'I':
ForLoopConditionIntoBody{m_dialect}(*m_ast); ForLoopConditionIntoBody::run(context, *m_ast);
break; break;
case 'c': case 'c':
CommonSubexpressionEliminator::run(m_dialect, *m_ast); CommonSubexpressionEliminator::run(context, *m_ast);
break; break;
case 'd': case 'd':
(VarDeclInitializer{})(*m_ast); VarDeclInitializer::run(context, *m_ast);
break; break;
case 'l': case 'l':
VarNameCleaner{*m_ast, m_dialect}(*m_ast); VarNameCleaner::run(context, *m_ast);
break; break;
case 'x': case 'x':
ExpressionSplitter{m_dialect, *m_nameDispenser}(*m_ast); ExpressionSplitter::run(context, *m_ast);
break; break;
case 'j': case 'j':
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(context, *m_ast);
break; break;
case 'g': case 'g':
(FunctionGrouper{})(*m_ast); FunctionGrouper::run(context, *m_ast);
break; break;
case 'h': case 'h':
(FunctionHoister{})(*m_ast); FunctionHoister::run(context, *m_ast);
break; break;
case 'e': case 'e':
ExpressionInliner{m_dialect, *m_ast}.run(); ExpressionInliner::run(context, *m_ast);
break; break;
case 'i': case 'i':
FullInliner(*m_ast, *m_nameDispenser).run(); FullInliner::run(context, *m_ast);
break; break;
case 's': case 's':
ExpressionSimplifier::run(m_dialect, *m_ast); ExpressionSimplifier::run(context, *m_ast);
break; break;
case 't': case 't':
StructuralSimplifier{}(*m_ast); StructuralSimplifier::run(context, *m_ast);
break;
case 'T':
LiteralRematerialiser::run(context, *m_ast);
break; break;
case 'n': case 'n':
(ControlFlowSimplifier{m_dialect})(*m_ast); ControlFlowSimplifier::run(context, *m_ast);
break; break;
case 'u': case 'u':
UnusedPruner::runUntilStabilisedOnFullAST(m_dialect, *m_ast); UnusedPruner::run(context, *m_ast);
break; break;
case 'D': case 'D':
DeadCodeEliminator{m_dialect}(*m_ast); DeadCodeEliminator::run(context, *m_ast);
break; break;
case 'a': case 'a':
SSATransform::run(*m_ast, *m_nameDispenser); SSATransform::run(context, *m_ast);
break; break;
case 'r': case 'r':
RedundantAssignEliminator::run(m_dialect, *m_ast); RedundantAssignEliminator::run(context, *m_ast);
break; break;
case 'm': case 'm':
Rematerialiser::run(m_dialect, *m_ast); Rematerialiser::run(context, *m_ast);
break; break;
case 'v': case 'v':
EquivalentFunctionCombiner::run(*m_ast); EquivalentFunctionCombiner::run(context, *m_ast);
break; break;
case 'V': case 'V':
SSAReverser::run(*m_ast); SSAReverser::run(context, *m_ast);
break; break;
case 'p': case 'p':
{ {
@ -222,7 +229,7 @@ public:
break; break;
} }
case 'L': case 'L':
LoadResolver::run(m_dialect, *m_ast); LoadResolver::run(context, *m_ast);
break; break;
default: default:
cout << "Unknown option." << endl; cout << "Unknown option." << endl;