mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Modify parser and optimizer.
This commit is contained in:
@@ -35,9 +35,9 @@ namespace
|
||||
|
||||
ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression)
|
||||
{
|
||||
return {_location, FunctionalInstruction{
|
||||
return {_location, FunctionCall{
|
||||
_location,
|
||||
dev::eth::Instruction::POP,
|
||||
Identifier{_location, "pop"_yulstring},
|
||||
{std::move(_expression)}
|
||||
}};
|
||||
}
|
||||
@@ -84,9 +84,9 @@ OptionalStatements reduceSingleCaseSwitch(Switch& _switchStmt)
|
||||
if (switchCase.value)
|
||||
return make_vector<Statement>(If{
|
||||
std::move(_switchStmt.location),
|
||||
make_unique<Expression>(FunctionalInstruction{
|
||||
std::move(loc),
|
||||
dev::eth::Instruction::EQ,
|
||||
make_unique<Expression>(FunctionCall{
|
||||
loc,
|
||||
Identifier{loc, "eq"_yulstring},
|
||||
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
|
||||
}),
|
||||
std::move(switchCase.body)
|
||||
@@ -122,7 +122,7 @@ void ControlFlowSimplifier::visit(Statement& _st)
|
||||
if (!forLoop.body.statements.empty())
|
||||
{
|
||||
bool isTerminating = false;
|
||||
TerminationFinder::ControlFlow controlFlow = TerminationFinder::controlFlowKind(forLoop.body.statements.back());
|
||||
TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back());
|
||||
if (controlFlow == TerminationFinder::ControlFlow::Break)
|
||||
{
|
||||
isTerminating = true;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
namespace yul
|
||||
{
|
||||
struct Dialect;
|
||||
|
||||
/**
|
||||
* Simplifies several control-flow structures:
|
||||
@@ -45,6 +46,8 @@ namespace yul
|
||||
class ControlFlowSimplifier: public ASTModifier
|
||||
{
|
||||
public:
|
||||
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
|
||||
using ASTModifier::operator();
|
||||
void operator()(Break&) override { ++m_numBreakStatements; }
|
||||
void operator()(Continue&) override { ++m_numContinueStatements; }
|
||||
@@ -55,6 +58,7 @@ public:
|
||||
private:
|
||||
void simplify(std::vector<Statement>& _statements);
|
||||
|
||||
Dialect const& m_dialect;
|
||||
size_t m_numBreakStatements = 0;
|
||||
size_t m_numContinueStatements = 0;
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ void DeadCodeEliminator::operator()(Block& _block)
|
||||
{
|
||||
TerminationFinder::ControlFlow controlFlowChange;
|
||||
size_t index;
|
||||
tie(controlFlowChange, index) = TerminationFinder::firstUnconditionalControlFlowChange(_block.statements);
|
||||
tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements);
|
||||
|
||||
// Erase everything after the terminating statement that is not a function definition.
|
||||
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1))
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
namespace yul
|
||||
{
|
||||
struct Dialect;
|
||||
|
||||
/**
|
||||
* Optimisation stage that removes unreachable code
|
||||
@@ -46,9 +47,14 @@ namespace yul
|
||||
class DeadCodeEliminator: public ASTModifier
|
||||
{
|
||||
public:
|
||||
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
|
||||
using ASTModifier::operator();
|
||||
void operator()(ForLoop& _for) override;
|
||||
void operator()(Block& _block) override;
|
||||
|
||||
private:
|
||||
Dialect const& m_dialect;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
@@ -92,9 +93,9 @@ void CodeSize::visit(Expression const& _expression)
|
||||
}
|
||||
|
||||
|
||||
size_t CodeCost::codeCost(Expression const& _expr)
|
||||
size_t CodeCost::codeCost(Dialect const& _dialect, Expression const& _expr)
|
||||
{
|
||||
CodeCost cc;
|
||||
CodeCost cc(_dialect);
|
||||
cc.visit(_expr);
|
||||
return cc.m_cost;
|
||||
}
|
||||
@@ -102,23 +103,26 @@ size_t CodeCost::codeCost(Expression const& _expr)
|
||||
|
||||
void CodeCost::operator()(FunctionCall const& _funCall)
|
||||
{
|
||||
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
|
||||
m_cost += 49;
|
||||
ASTWalker::operator()(_funCall);
|
||||
|
||||
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
|
||||
if (BuiltinFunctionForEVM const* f = dialect->builtin(_funCall.functionName.name))
|
||||
if (f->instruction)
|
||||
{
|
||||
addInstructionCost(*f->instruction);
|
||||
return;
|
||||
}
|
||||
|
||||
m_cost += 49;
|
||||
}
|
||||
|
||||
void CodeCost::operator()(FunctionalInstruction const& _instr)
|
||||
{
|
||||
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
|
||||
dev::eth::Tier gasPriceTier = dev::eth::instructionInfo(_instr.instruction).gasPriceTier;
|
||||
if (gasPriceTier < dev::eth::Tier::VeryLow)
|
||||
m_cost -= 1;
|
||||
else if (gasPriceTier < dev::eth::Tier::High)
|
||||
m_cost += 1;
|
||||
else
|
||||
m_cost += 49;
|
||||
addInstructionCost(_instr.instruction);
|
||||
ASTWalker::operator()(_instr);
|
||||
}
|
||||
|
||||
void CodeCost::operator()(Literal const& _literal)
|
||||
{
|
||||
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
|
||||
@@ -151,6 +155,17 @@ void CodeCost::visit(Expression const& _expression)
|
||||
ASTWalker::visit(_expression);
|
||||
}
|
||||
|
||||
void CodeCost::addInstructionCost(eth::Instruction _instruction)
|
||||
{
|
||||
dev::eth::Tier gasPriceTier = dev::eth::instructionInfo(_instruction).gasPriceTier;
|
||||
if (gasPriceTier < dev::eth::Tier::VeryLow)
|
||||
m_cost -= 1;
|
||||
else if (gasPriceTier < dev::eth::Tier::High)
|
||||
m_cost += 1;
|
||||
else
|
||||
m_cost += 49;
|
||||
}
|
||||
|
||||
void AssignmentCounter::operator()(Assignment const& _assignment)
|
||||
{
|
||||
for (auto const& variable: _assignment.variableNames)
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
|
||||
struct Dialect;
|
||||
|
||||
/**
|
||||
* Metric for the size of code.
|
||||
* More specifically, the number of AST nodes.
|
||||
@@ -71,9 +75,11 @@ private:
|
||||
class CodeCost: public ASTWalker
|
||||
{
|
||||
public:
|
||||
static size_t codeCost(Expression const& _expression);
|
||||
static size_t codeCost(Dialect const& _dialect, Expression const& _expression);
|
||||
|
||||
private:
|
||||
CodeCost(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
|
||||
void operator()(FunctionCall const& _funCall) override;
|
||||
void operator()(FunctionalInstruction const& _instr) override;
|
||||
void operator()(Literal const& _literal) override;
|
||||
@@ -81,6 +87,9 @@ private:
|
||||
void visit(Expression const& _expression) override;
|
||||
|
||||
private:
|
||||
void addInstructionCost(dev::eth::Instruction _instruction);
|
||||
|
||||
Dialect const& m_dialect;
|
||||
size_t m_cost = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ void Rematerialiser::visit(Expression& _e)
|
||||
assertThrow(m_value.at(name), OptimizerException, "");
|
||||
auto const& value = *m_value.at(name);
|
||||
size_t refs = m_referenceCounts[name];
|
||||
size_t cost = CodeCost::codeCost(value);
|
||||
size_t cost = CodeCost::codeCost(m_dialect, value);
|
||||
if (refs <= 1 || cost == 0 || (refs <= 5 && cost <= 1) || m_varsToAlwaysRematerialize.count(name))
|
||||
{
|
||||
assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
@@ -112,10 +113,14 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
|
||||
|
||||
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
|
||||
{
|
||||
if (_exprStmnt.expression.type() != typeid(FunctionalInstruction))
|
||||
return false;
|
||||
|
||||
return eth::SemanticInformation::terminatesControlFlow(
|
||||
boost::get<FunctionalInstruction>(_exprStmnt.expression).instruction
|
||||
);
|
||||
if (_exprStmnt.expression.type() == typeid(FunctionalInstruction))
|
||||
return eth::SemanticInformation::terminatesControlFlow(
|
||||
boost::get<FunctionalInstruction>(_exprStmnt.expression).instruction
|
||||
);
|
||||
else if (_exprStmnt.expression.type() == typeid(FunctionCall))
|
||||
if (auto const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
|
||||
if (auto const* builtin = dialect->builtin(boost::get<FunctionCall>(_exprStmnt.expression).functionName.name))
|
||||
if (builtin->instruction)
|
||||
return eth::SemanticInformation::terminatesControlFlow(*builtin->instruction);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ class TerminationFinder
|
||||
public:
|
||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate };
|
||||
|
||||
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
|
||||
/// @returns the index of the first statement in the provided sequence
|
||||
/// that is an unconditional ``break``, ``continue`` or a
|
||||
/// call to a terminating builtin function.
|
||||
@@ -77,18 +79,21 @@ public:
|
||||
/// returns `FlowOut` and ``size_t(-1)``.
|
||||
/// The function might return ``FlowOut`` even though control
|
||||
/// flow cannot actually continue.
|
||||
static std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange(
|
||||
std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange(
|
||||
std::vector<Statement> const& _statements
|
||||
);
|
||||
|
||||
/// @returns the control flow type of the given statement.
|
||||
/// This function could return FlowOut even if control flow never continues.
|
||||
static ControlFlow controlFlowKind(Statement const& _statement);
|
||||
ControlFlow controlFlowKind(Statement const& _statement);
|
||||
|
||||
/// @returns true if the expression statement is a direct
|
||||
/// call to a builtin terminating function like
|
||||
/// ``stop``, ``revert`` or ``return``.
|
||||
static bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
|
||||
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
|
||||
|
||||
private:
|
||||
Dialect const& m_dialect;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const
|
||||
vector<Expression> arguments;
|
||||
for (auto const& arg: m_arguments)
|
||||
arguments.emplace_back(arg.toExpression(_location));
|
||||
// TODO convert to FunctionCall
|
||||
return FunctionalInstruction{_location, m_instruction, std::move(arguments)};
|
||||
}
|
||||
assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group.");
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
{
|
||||
YulString varName = _varDecl.variables.front().name;
|
||||
if (m_value.count(varName))
|
||||
m_expressionCodeCost[varName] = CodeCost::codeCost(*m_value[varName]);
|
||||
m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *m_value[varName]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -73,14 +73,14 @@ void OptimiserSuite::run(
|
||||
FunctionHoister{}(ast);
|
||||
BlockFlattener{}(ast);
|
||||
ForLoopInitRewriter{}(ast);
|
||||
DeadCodeEliminator{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
FunctionGrouper{}(ast);
|
||||
EquivalentFunctionCombiner::run(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
BlockFlattener{}(ast);
|
||||
ControlFlowSimplifier{}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
StructuralSimplifier{_dialect}(ast);
|
||||
ControlFlowSimplifier{}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
BlockFlattener{}(ast);
|
||||
|
||||
// None of the above can make stack problems worse.
|
||||
@@ -110,11 +110,11 @@ void OptimiserSuite::run(
|
||||
|
||||
{
|
||||
// still in SSA, perform structural simplification
|
||||
ControlFlowSimplifier{}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
StructuralSimplifier{_dialect}(ast);
|
||||
ControlFlowSimplifier{}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
BlockFlattener{}(ast);
|
||||
DeadCodeEliminator{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
}
|
||||
{
|
||||
@@ -166,8 +166,8 @@ void OptimiserSuite::run(
|
||||
ExpressionSimplifier::run(_dialect, ast);
|
||||
StructuralSimplifier{_dialect}(ast);
|
||||
BlockFlattener{}(ast);
|
||||
DeadCodeEliminator{}(ast);
|
||||
ControlFlowSimplifier{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
SSATransform::run(ast, dispenser);
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
@@ -202,8 +202,8 @@ void OptimiserSuite::run(
|
||||
// message once we perform code generation.
|
||||
StackCompressor::run(_dialect, ast, _optimizeStackAllocation, stackCompressorMaxIterations);
|
||||
BlockFlattener{}(ast);
|
||||
DeadCodeEliminator{}(ast);
|
||||
ControlFlowSimplifier{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
|
||||
FunctionGrouper{}(ast);
|
||||
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast);
|
||||
|
||||
Reference in New Issue
Block a user