Merge pull request #6851 from ethereum/makeOptimizerWorkForWasm

Make Yul optimizer not fail for wasm.
This commit is contained in:
chriseth 2019-06-18 11:37:51 +02:00 committed by GitHub
commit 5fd9264dcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 321 additions and 210 deletions

View File

@ -7,6 +7,7 @@ Language Features:
Compiler Features: Compiler Features:
* Optimizer: Add rule to simplify SUB(~0, X) to NOT(X). * Optimizer: Add rule to simplify SUB(~0, X) to NOT(X).
* Commandline Interface: Experimental parser error recovery via the ``--error-recovery`` commandline switch. * Commandline Interface: Experimental parser error recovery via the ``--error-recovery`` commandline switch.
* Yul Optimizer: Make the optimizer work for all dialects of Yul including eWasm.

View File

@ -32,8 +32,8 @@
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
#include <libyul/backends/evm/AsmCodeGen.h> #include <libyul/backends/evm/AsmCodeGen.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/optimiser/Suite.h> #include <libyul/optimiser/Suite.h>
#include <libyul/optimiser/Metrics.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>

View File

@ -31,6 +31,7 @@
#include <libyul/backends/evm/EVMCodeTransform.h> #include <libyul/backends/evm/EVMCodeTransform.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/EVMObjectCompiler.h> #include <libyul/backends/evm/EVMObjectCompiler.h>
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/backends/wasm/WasmDialect.h> #include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/backends/wasm/EWasmObjectCompiler.h> #include <libyul/backends/wasm/EWasmObjectCompiler.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
@ -146,15 +147,17 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
for (auto& subNode: _object.subObjects) for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<Object*>(subNode.get())) if (auto subObject = dynamic_cast<Object*>(subNode.get()))
optimize(*subObject, false); optimize(*subObject, false);
EVMDialect const& dialect = dynamic_cast<EVMDialect const&>(languageToDialect(m_language, m_evmVersion)); if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&languageToDialect(m_language, m_evmVersion)))
GasMeter meter(dialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment); {
OptimiserSuite::run( GasMeter meter(*dialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
dialect, OptimiserSuite::run(
meter, *dialect,
*_object.code, meter,
*_object.analysisInfo, *_object.code,
m_optimiserSettings.optimizeStackAllocation *_object.analysisInfo,
); m_optimiserSettings.optimizeStackAllocation
);
}
} }
MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const

View File

@ -28,6 +28,8 @@ add_library(yul
backends/evm/AbstractAssembly.h backends/evm/AbstractAssembly.h
backends/evm/AsmCodeGen.h backends/evm/AsmCodeGen.h
backends/evm/AsmCodeGen.cpp backends/evm/AsmCodeGen.cpp
backends/evm/ConstantOptimiser.cpp
backends/evm/ConstantOptimiser.h
backends/evm/EVMAssembly.cpp backends/evm/EVMAssembly.cpp
backends/evm/EVMAssembly.h backends/evm/EVMAssembly.h
backends/evm/EVMCodeTransform.cpp backends/evm/EVMCodeTransform.cpp
@ -36,6 +38,8 @@ add_library(yul
backends/evm/EVMDialect.h backends/evm/EVMDialect.h
backends/evm/EVMObjectCompiler.cpp backends/evm/EVMObjectCompiler.cpp
backends/evm/EVMObjectCompiler.h backends/evm/EVMObjectCompiler.h
backends/evm/EVMMetrics.cpp
backends/evm/EVMMetrics.h
backends/evm/NoOutputAssembly.h backends/evm/NoOutputAssembly.h
backends/evm/NoOutputAssembly.cpp backends/evm/NoOutputAssembly.cpp
backends/wasm/EWasmCodeTransform.cpp backends/wasm/EWasmCodeTransform.cpp
@ -58,8 +62,6 @@ add_library(yul
optimiser/BlockHasher.h optimiser/BlockHasher.h
optimiser/CommonSubexpressionEliminator.cpp optimiser/CommonSubexpressionEliminator.cpp
optimiser/CommonSubexpressionEliminator.h optimiser/CommonSubexpressionEliminator.h
optimiser/ConstantOptimiser.cpp
optimiser/ConstantOptimiser.h
optimiser/ControlFlowSimplifier.cpp optimiser/ControlFlowSimplifier.cpp
optimiser/ControlFlowSimplifier.h optimiser/ControlFlowSimplifier.h
optimiser/DataFlowAnalyzer.cpp optimiser/DataFlowAnalyzer.cpp

View File

@ -43,27 +43,31 @@ map<YulString, int> CompilabilityChecker::run(
solAssert(_dialect.flavour == AsmFlavour::Strict, ""); solAssert(_dialect.flavour == AsmFlavour::Strict, "");
solAssert(dynamic_cast<EVMDialect const*>(&_dialect), ""); if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
NoOutputEVMDialect noOutputDialect(dynamic_cast<EVMDialect const&>(_dialect));
BuiltinContext builtinContext;
yul::AsmAnalysisInfo analysisInfo =
yul::AsmAnalyzer::analyzeStrictAssertCorrect(noOutputDialect, _ast);
NoOutputAssembly assembly;
CodeTransform transform(assembly, analysisInfo, _ast, noOutputDialect, builtinContext, _optimizeStackAllocation);
try
{ {
transform(_ast); NoOutputEVMDialect noOutputDialect(*evmDialect);
} BuiltinContext builtinContext;
catch (StackTooDeepError const&)
{
solAssert(!transform.stackErrors().empty(), "Got stack too deep exception that was not stored.");
}
std::map<YulString, int> functions; yul::AsmAnalysisInfo analysisInfo =
for (StackTooDeepError const& error: transform.stackErrors()) yul::AsmAnalyzer::analyzeStrictAssertCorrect(noOutputDialect, _ast);
functions[error.functionName] = max(error.depth, functions[error.functionName]);
return functions; NoOutputAssembly assembly;
CodeTransform transform(assembly, analysisInfo, _ast, noOutputDialect, builtinContext, _optimizeStackAllocation);
try
{
transform(_ast);
}
catch (StackTooDeepError const&)
{
solAssert(!transform.stackErrors().empty(), "Got stack too deep exception that was not stored.");
}
std::map<YulString, int> functions;
for (StackTooDeepError const& error: transform.stackErrors())
functions[error.functionName] = max(error.depth, functions[error.functionName]);
return functions;
}
else
return {};
} }

View File

@ -66,6 +66,9 @@ struct Dialect: boost::noncopyable
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function. /// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; } virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
virtual BuiltinFunction const* discardFunction() const { return nullptr; }
virtual BuiltinFunction const* equalityFunction() const { return nullptr; }
Dialect(AsmFlavour _flavour): flavour(_flavour) {} Dialect(AsmFlavour _flavour): flavour(_flavour) {}
virtual ~Dialect() = default; virtual ~Dialect() = default;

View File

@ -18,14 +18,12 @@
* Optimisation stage that replaces constants by expressions that compute them. * Optimisation stage that replaces constants by expressions that compute them.
*/ */
#include <libyul/optimiser/ConstantOptimiser.h> #include <libyul/backends/evm/ConstantOptimiser.h>
#include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTCopier.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libyul/AsmParser.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>

View File

@ -68,6 +68,9 @@ struct EVMDialect: public Dialect
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function. /// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
BuiltinFunctionForEVM const* builtin(YulString _name) const override; BuiltinFunctionForEVM const* builtin(YulString _name) const override;
BuiltinFunctionForEVM const* discardFunction() const override { return builtin("pop"_yulstring); }
BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); }
static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version); static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version);
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version); static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version);

View File

@ -0,0 +1,123 @@
/*
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/>.
*/
/**
* Module providing metrics for the EVM optimizer.
*/
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/AsmData.h>
#include <libyul/Exceptions.h>
#include <libyul/Utilities.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/GasMeter.h>
#include <libdevcore/Visitor.h>
#include <libdevcore/CommonData.h>
using namespace std;
using namespace dev;
using namespace yul;
size_t GasMeter::costs(Expression const& _expression) const
{
return combineCosts(GasMeterVisitor::costs(_expression, m_dialect, m_isCreation));
}
size_t GasMeter::instructionCosts(eth::Instruction _instruction) const
{
return combineCosts(GasMeterVisitor::instructionCosts(_instruction, m_dialect, m_isCreation));
}
size_t GasMeter::combineCosts(std::pair<size_t, size_t> _costs) const
{
return _costs.first * m_runs + _costs.second;
}
pair<size_t, size_t> GasMeterVisitor::costs(
Expression const& _expression,
EVMDialect const& _dialect,
bool _isCreation
)
{
GasMeterVisitor gmv(_dialect, _isCreation);
gmv.visit(_expression);
return {gmv.m_runGas, gmv.m_dataGas};
}
pair<size_t, size_t> GasMeterVisitor::instructionCosts(
dev::eth::Instruction _instruction,
EVMDialect const& _dialect,
bool _isCreation
)
{
GasMeterVisitor gmv(_dialect, _isCreation);
gmv.instructionCostsInternal(_instruction);
return {gmv.m_runGas, gmv.m_dataGas};
}
void GasMeterVisitor::operator()(FunctionCall const& _funCall)
{
ASTWalker::operator()(_funCall);
if (BuiltinFunctionForEVM const* f = m_dialect.builtin(_funCall.functionName.name))
if (f->instruction)
{
instructionCostsInternal(*f->instruction);
return;
}
yulAssert(false, "Functions not implemented.");
}
void GasMeterVisitor::operator()(FunctionalInstruction const& _fun)
{
ASTWalker::operator()(_fun);
instructionCostsInternal(_fun.instruction);
}
void GasMeterVisitor::operator()(Literal const& _lit)
{
m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::PUSH1);
m_dataGas +=
singleByteDataGas() +
size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation));
}
void GasMeterVisitor::operator()(Identifier const&)
{
m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::DUP1);
m_dataGas += singleByteDataGas();
}
size_t GasMeterVisitor::singleByteDataGas() const
{
if (m_isCreation)
return dev::eth::GasCosts::txDataNonZeroGas;
else
return dev::eth::GasCosts::createDataGas;
}
void GasMeterVisitor::instructionCostsInternal(dev::eth::Instruction _instruction)
{
if (_instruction == eth::Instruction::EXP)
m_runGas += dev::eth::GasCosts::expGas + dev::eth::GasCosts::expByteGas(m_dialect.evmVersion());
else
m_runGas += dev::eth::GasMeter::runGas(_instruction);
m_dataGas += singleByteDataGas();
}

View File

@ -0,0 +1,101 @@
/*
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/>.
*/
/**
* Module providing metrics for the optimizer.
*/
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <libevmasm/Instruction.h>
namespace yul
{
struct EVMDialect;
/**
* Gas meter for expressions only involving literals, identifiers and
* EVM instructions.
*
* Assumes that EXP is not used with exponents larger than a single byte.
* Is not particularly exact for anything apart from arithmetic.
*/
class GasMeter
{
public:
GasMeter(EVMDialect const& _dialect, bool _isCreation, size_t _runs):
m_dialect(_dialect),
m_isCreation{_isCreation},
m_runs(_runs)
{}
/// @returns the full combined costs of deploying and evaluating the expression.
size_t costs(Expression const& _expression) const;
/// @returns the combined costs of deploying and running the instruction, not including
/// the costs for its arguments.
size_t instructionCosts(dev::eth::Instruction _instruction) const;
private:
size_t combineCosts(std::pair<size_t, size_t> _costs) const;
EVMDialect const& m_dialect;
bool m_isCreation = false;
size_t m_runs;
};
class GasMeterVisitor: public ASTWalker
{
public:
static std::pair<size_t, size_t> costs(
Expression const& _expression,
EVMDialect const& _dialect,
bool _isCreation
);
static std::pair<size_t, size_t> instructionCosts(
dev::eth::Instruction _instruction,
EVMDialect const& _dialect,
bool _isCreation = false
);
public:
GasMeterVisitor(EVMDialect const& _dialect, bool _isCreation):
m_dialect(_dialect),
m_isCreation{_isCreation}
{}
void operator()(FunctionCall const& _funCall) override;
void operator()(FunctionalInstruction const& _instr) override;
void operator()(Literal const& _literal) override;
void operator()(Identifier const& _identifier) override;
private:
size_t singleByteDataGas() const;
/// Computes the cost of storing and executing the single instruction (excluding its arguments).
/// For EXP, it assumes that the exponent is at most 255.
/// Does not work particularly exact for anything apart from arithmetic.
void instructionCostsInternal(dev::eth::Instruction _instruction);
EVMDialect const& m_dialect;
bool m_isCreation = false;
size_t m_runGas = 0;
size_t m_dataGas = 0;
};
}

View File

@ -45,6 +45,8 @@ struct WasmDialect: public Dialect
WasmDialect(); WasmDialect();
BuiltinFunction const* builtin(YulString _name) const override; BuiltinFunction const* builtin(YulString _name) const override;
BuiltinFunction const* discardFunction() const override { return builtin("drop"_yulstring); }
BuiltinFunction const* equalityFunction() const override { return builtin("i64.eq"_yulstring); }
static WasmDialect const& instance(); static WasmDialect const& instance();

View File

@ -18,6 +18,7 @@
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libyul/Dialect.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/Visitor.h> #include <libdevcore/Visitor.h>
@ -33,11 +34,16 @@ using OptionalStatements = boost::optional<vector<Statement>>;
namespace namespace
{ {
ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression) ExpressionStatement makeDiscardCall(
langutil::SourceLocation const& _location,
Dialect const& _dialect,
Expression&& _expression
)
{ {
yulAssert(_dialect.discardFunction(), "No discard function available.");
return {_location, FunctionCall{ return {_location, FunctionCall{
_location, _location,
Identifier{_location, "pop"_yulstring}, Identifier{_location, _dialect.discardFunction()->name},
{std::move(_expression)} {std::move(_expression)}
}}; }};
} }
@ -66,36 +72,55 @@ void removeEmptyCasesFromSwitch(Switch& _switchStmt)
); );
} }
OptionalStatements reduceNoCaseSwitch(Switch& _switchStmt) OptionalStatements reduceNoCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
{ {
yulAssert(_switchStmt.cases.empty(), "Expected no case!"); yulAssert(_switchStmt.cases.empty(), "Expected no case!");
if (!_dialect.discardFunction())
return {};
auto loc = locationOf(*_switchStmt.expression); auto loc = locationOf(*_switchStmt.expression);
return make_vector<Statement>(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); return make_vector<Statement>(makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
));
} }
OptionalStatements reduceSingleCaseSwitch(Switch& _switchStmt) OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
{ {
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
auto& switchCase = _switchStmt.cases.front(); auto& switchCase = _switchStmt.cases.front();
auto loc = locationOf(*_switchStmt.expression); auto loc = locationOf(*_switchStmt.expression);
if (switchCase.value) if (switchCase.value)
{
if (!_dialect.equalityFunction())
return {};
return make_vector<Statement>(If{ return make_vector<Statement>(If{
std::move(_switchStmt.location), std::move(_switchStmt.location),
make_unique<Expression>(FunctionCall{ make_unique<Expression>(FunctionCall{
loc, loc,
Identifier{loc, "eq"_yulstring}, Identifier{loc, _dialect.equalityFunction()->name},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)} {std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}), }),
std::move(switchCase.body) std::move(switchCase.body)
}); });
}
else else
{
if (!_dialect.discardFunction())
return {};
return make_vector<Statement>( return make_vector<Statement>(
makePopExpressionStatement(loc, std::move(*_switchStmt.expression)), makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
),
std::move(switchCase.body) std::move(switchCase.body)
); );
}
} }
} }
@ -151,10 +176,14 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
{ {
GenericFallbackReturnsVisitor<OptionalStatements, If, Switch> const visitor( GenericFallbackReturnsVisitor<OptionalStatements, If, Switch> const visitor(
[&](If& _ifStmt) -> OptionalStatements { [&](If& _ifStmt) -> OptionalStatements {
if (_ifStmt.body.statements.empty()) if (_ifStmt.body.statements.empty() && m_dialect.discardFunction())
{ {
OptionalStatements s = vector<Statement>{}; OptionalStatements s = vector<Statement>{};
s->emplace_back(makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))); s->emplace_back(makeDiscardCall(
_ifStmt.location,
m_dialect,
std::move(*_ifStmt.condition)
));
return s; return s;
} }
return {}; return {};
@ -164,9 +193,9 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
removeEmptyCasesFromSwitch(_switchStmt); removeEmptyCasesFromSwitch(_switchStmt);
if (_switchStmt.cases.empty()) if (_switchStmt.cases.empty())
return reduceNoCaseSwitch(_switchStmt); return reduceNoCaseSwitch(m_dialect, _switchStmt);
else if (_switchStmt.cases.size() == 1) else if (_switchStmt.cases.size() == 1)
return reduceSingleCaseSwitch(_switchStmt); return reduceSingleCaseSwitch(m_dialect, _switchStmt);
return {}; return {};
} }

View File

@ -169,93 +169,6 @@ void CodeCost::addInstructionCost(eth::Instruction _instruction)
m_cost += 49; m_cost += 49;
} }
size_t GasMeter::costs(Expression const& _expression) const
{
return combineCosts(GasMeterVisitor::costs(_expression, m_dialect, m_isCreation));
}
size_t GasMeter::instructionCosts(eth::Instruction _instruction) const
{
return combineCosts(GasMeterVisitor::instructionCosts(_instruction, m_dialect, m_isCreation));
}
size_t GasMeter::combineCosts(std::pair<size_t, size_t> _costs) const
{
return _costs.first * m_runs + _costs.second;
}
pair<size_t, size_t> GasMeterVisitor::costs(
Expression const& _expression,
EVMDialect const& _dialect,
bool _isCreation
)
{
GasMeterVisitor gmv(_dialect, _isCreation);
gmv.visit(_expression);
return {gmv.m_runGas, gmv.m_dataGas};
}
pair<size_t, size_t> GasMeterVisitor::instructionCosts(
dev::eth::Instruction _instruction,
EVMDialect const& _dialect,
bool _isCreation
)
{
GasMeterVisitor gmv(_dialect, _isCreation);
gmv.instructionCostsInternal(_instruction);
return {gmv.m_runGas, gmv.m_dataGas};
}
void GasMeterVisitor::operator()(FunctionCall const& _funCall)
{
ASTWalker::operator()(_funCall);
if (BuiltinFunctionForEVM const* f = m_dialect.builtin(_funCall.functionName.name))
if (f->instruction)
{
instructionCostsInternal(*f->instruction);
return;
}
yulAssert(false, "Functions not implemented.");
}
void GasMeterVisitor::operator()(FunctionalInstruction const& _fun)
{
ASTWalker::operator()(_fun);
instructionCostsInternal(_fun.instruction);
}
void GasMeterVisitor::operator()(Literal const& _lit)
{
m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::PUSH1);
m_dataGas +=
singleByteDataGas() +
size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation));
}
void GasMeterVisitor::operator()(Identifier const&)
{
m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::DUP1);
m_dataGas += singleByteDataGas();
}
size_t GasMeterVisitor::singleByteDataGas() const
{
if (m_isCreation)
return dev::eth::GasCosts::txDataNonZeroGas;
else
return dev::eth::GasCosts::createDataGas;
}
void GasMeterVisitor::instructionCostsInternal(dev::eth::Instruction _instruction)
{
if (_instruction == eth::Instruction::EXP)
m_runGas += dev::eth::GasCosts::expGas + dev::eth::GasCosts::expByteGas(m_dialect.evmVersion());
else
m_runGas += dev::eth::GasMeter::runGas(_instruction);
m_dataGas += singleByteDataGas();
}
void AssignmentCounter::operator()(Assignment const& _assignment) void AssignmentCounter::operator()(Assignment const& _assignment)
{ {
for (auto const& variable: _assignment.variableNames) for (auto const& variable: _assignment.variableNames)

View File

@ -22,9 +22,6 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Instruction.h>
namespace yul namespace yul
{ {
@ -96,75 +93,6 @@ private:
size_t m_cost = 0; size_t m_cost = 0;
}; };
/**
* Gas meter for expressions only involving literals, identifiers and
* EVM instructions.
*
* Assumes that EXP is not used with exponents larger than a single byte.
* Is not particularly exact for anything apart from arithmetic.
*/
class GasMeter
{
public:
GasMeter(EVMDialect const& _dialect, bool _isCreation, size_t _runs):
m_dialect(_dialect),
m_isCreation{_isCreation},
m_runs(_runs)
{}
/// @returns the full combined costs of deploying and evaluating the expression.
size_t costs(Expression const& _expression) const;
/// @returns the combined costs of deploying and running the instruction, not including
/// the costs for its arguments.
size_t instructionCosts(dev::eth::Instruction _instruction) const;
private:
size_t combineCosts(std::pair<size_t, size_t> _costs) const;
EVMDialect const& m_dialect;
bool m_isCreation = false;
size_t m_runs;
};
class GasMeterVisitor: public ASTWalker
{
public:
static std::pair<size_t, size_t> costs(
Expression const& _expression,
EVMDialect const& _dialect,
bool _isCreation
);
static std::pair<size_t, size_t> instructionCosts(
dev::eth::Instruction _instruction,
EVMDialect const& _dialect,
bool _isCreation = false
);
public:
GasMeterVisitor(EVMDialect const& _dialect, bool _isCreation):
m_dialect(_dialect),
m_isCreation{_isCreation}
{}
void operator()(FunctionCall const& _funCall) override;
void operator()(FunctionalInstruction const& _instr) override;
void operator()(Literal const& _literal) override;
void operator()(Identifier const& _identifier) override;
private:
size_t singleByteDataGas() const;
/// Computes the cost of storing and executing the single instruction (excluding its arguments).
/// For EXP, it assumes that the exponent is at most 255.
/// Does not work particularly exact for anything apart from arithmetic.
void instructionCostsInternal(dev::eth::Instruction _instruction);
EVMDialect const& m_dialect;
bool m_isCreation = false;
size_t m_runGas = 0;
size_t m_dataGas = 0;
};
/** /**
* Counts the number of assignments to every variable. * Counts the number of assignments to every variable.
* Only works after running the Disambiguator. * Only works after running the Disambiguator.

View File

@ -24,7 +24,6 @@
#include <libyul/optimiser/VarDeclInitializer.h> #include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/BlockFlattener.h> #include <libyul/optimiser/BlockFlattener.h>
#include <libyul/optimiser/ControlFlowSimplifier.h> #include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/ConstantOptimiser.h>
#include <libyul/optimiser/DeadCodeEliminator.h> #include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/FunctionGrouper.h> #include <libyul/optimiser/FunctionGrouper.h>
#include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/FunctionHoister.h>
@ -45,6 +44,7 @@
#include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/RedundantAssignEliminator.h>
#include <libyul/optimiser/VarNameCleaner.h> #include <libyul/optimiser/VarNameCleaner.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
#include <libyul/backends/evm/ConstantOptimiser.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
@ -209,7 +209,8 @@ void OptimiserSuite::run(
FunctionGrouper{}(ast); FunctionGrouper{}(ast);
ConstantOptimiser{dynamic_cast<EVMDialect const&>(_dialect), _meter}(ast); if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
ConstantOptimiser{*dialect, _meter}(ast);
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast); VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast);
yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast); yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast);

View File

@ -23,7 +23,6 @@
#include <libyul/optimiser/VarDeclInitializer.h> #include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/VarNameCleaner.h> #include <libyul/optimiser/VarNameCleaner.h>
#include <libyul/optimiser/ControlFlowSimplifier.h> #include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/ConstantOptimiser.h>
#include <libyul/optimiser/DeadCodeEliminator.h> #include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Disambiguator.h> #include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h> #include <libyul/optimiser/CommonSubexpressionEliminator.h>
@ -47,8 +46,9 @@
#include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/StackCompressor.h> #include <libyul/optimiser/StackCompressor.h>
#include <libyul/optimiser/Suite.h> #include <libyul/optimiser/Suite.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/backends/evm/ConstantOptimiser.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/backends/wasm/WordSizeTransform.h> #include <libyul/backends/wasm/WordSizeTransform.h>
#include <libyul/AsmPrinter.h> #include <libyul/AsmPrinter.h>
#include <libyul/AsmParser.h> #include <libyul/AsmParser.h>