mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Make Yul optimizer not fail for wasm.
This commit is contained in:
parent
8260ae1397
commit
6cb6fe35ef
@ -7,6 +7,7 @@ Language Features:
|
||||
Compiler Features:
|
||||
* Optimizer: Add rule to simplify SUB(~0, X) to NOT(X).
|
||||
* 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.
|
||||
|
||||
|
||||
|
||||
|
@ -32,8 +32,8 @@
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/backends/evm/AsmCodeGen.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
#include <libyul/optimiser/Suite.h>
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/backends/evm/EVMObjectCompiler.h>
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
#include <libyul/backends/wasm/WasmDialect.h>
|
||||
#include <libyul/backends/wasm/EWasmObjectCompiler.h>
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
@ -146,15 +147,17 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
|
||||
for (auto& subNode: _object.subObjects)
|
||||
if (auto subObject = dynamic_cast<Object*>(subNode.get()))
|
||||
optimize(*subObject, false);
|
||||
EVMDialect const& dialect = dynamic_cast<EVMDialect const&>(languageToDialect(m_language, m_evmVersion));
|
||||
GasMeter meter(dialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||
OptimiserSuite::run(
|
||||
dialect,
|
||||
meter,
|
||||
*_object.code,
|
||||
*_object.analysisInfo,
|
||||
m_optimiserSettings.optimizeStackAllocation
|
||||
);
|
||||
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&languageToDialect(m_language, m_evmVersion)))
|
||||
{
|
||||
GasMeter meter(*dialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||
OptimiserSuite::run(
|
||||
*dialect,
|
||||
meter,
|
||||
*_object.code,
|
||||
*_object.analysisInfo,
|
||||
m_optimiserSettings.optimizeStackAllocation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
|
@ -28,6 +28,8 @@ add_library(yul
|
||||
backends/evm/AbstractAssembly.h
|
||||
backends/evm/AsmCodeGen.h
|
||||
backends/evm/AsmCodeGen.cpp
|
||||
backends/evm/ConstantOptimiser.cpp
|
||||
backends/evm/ConstantOptimiser.h
|
||||
backends/evm/EVMAssembly.cpp
|
||||
backends/evm/EVMAssembly.h
|
||||
backends/evm/EVMCodeTransform.cpp
|
||||
@ -36,6 +38,8 @@ add_library(yul
|
||||
backends/evm/EVMDialect.h
|
||||
backends/evm/EVMObjectCompiler.cpp
|
||||
backends/evm/EVMObjectCompiler.h
|
||||
backends/evm/EVMMetrics.cpp
|
||||
backends/evm/EVMMetrics.h
|
||||
backends/evm/NoOutputAssembly.h
|
||||
backends/evm/NoOutputAssembly.cpp
|
||||
backends/wasm/EWasmCodeTransform.cpp
|
||||
@ -58,8 +62,6 @@ add_library(yul
|
||||
optimiser/BlockHasher.h
|
||||
optimiser/CommonSubexpressionEliminator.cpp
|
||||
optimiser/CommonSubexpressionEliminator.h
|
||||
optimiser/ConstantOptimiser.cpp
|
||||
optimiser/ConstantOptimiser.h
|
||||
optimiser/ControlFlowSimplifier.cpp
|
||||
optimiser/ControlFlowSimplifier.h
|
||||
optimiser/DataFlowAnalyzer.cpp
|
||||
|
@ -43,27 +43,31 @@ map<YulString, int> CompilabilityChecker::run(
|
||||
|
||||
solAssert(_dialect.flavour == AsmFlavour::Strict, "");
|
||||
|
||||
solAssert(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
|
||||
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||
{
|
||||
transform(_ast);
|
||||
}
|
||||
catch (StackTooDeepError const&)
|
||||
{
|
||||
solAssert(!transform.stackErrors().empty(), "Got stack too deep exception that was not stored.");
|
||||
}
|
||||
NoOutputEVMDialect noOutputDialect(*evmDialect);
|
||||
BuiltinContext builtinContext;
|
||||
|
||||
std::map<YulString, int> functions;
|
||||
for (StackTooDeepError const& error: transform.stackErrors())
|
||||
functions[error.functionName] = max(error.depth, functions[error.functionName]);
|
||||
yul::AsmAnalysisInfo analysisInfo =
|
||||
yul::AsmAnalyzer::analyzeStrictAssertCorrect(noOutputDialect, _ast);
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
@ -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.
|
||||
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) {}
|
||||
virtual ~Dialect() = default;
|
||||
|
||||
|
@ -18,14 +18,12 @@
|
||||
* 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/Metrics.h>
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/Utilities.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
@ -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.
|
||||
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& strictAssemblyForEVM(langutil::EVMVersion _version);
|
||||
static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version);
|
||||
|
123
libyul/backends/evm/EVMMetrics.cpp
Normal file
123
libyul/backends/evm/EVMMetrics.cpp
Normal 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();
|
||||
}
|
101
libyul/backends/evm/EVMMetrics.h
Normal file
101
libyul/backends/evm/EVMMetrics.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -45,6 +45,8 @@ struct WasmDialect: public Dialect
|
||||
WasmDialect();
|
||||
|
||||
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();
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Utilities.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libdevcore/CommonData.h>
|
||||
#include <libdevcore/Visitor.h>
|
||||
|
||||
@ -33,11 +34,16 @@ using OptionalStatements = boost::optional<vector<Statement>>;
|
||||
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{
|
||||
_location,
|
||||
Identifier{_location, "pop"_yulstring},
|
||||
Identifier{_location, _dialect.discardFunction()->name},
|
||||
{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!");
|
||||
if (!_dialect.discardFunction())
|
||||
return {};
|
||||
|
||||
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!");
|
||||
|
||||
auto& switchCase = _switchStmt.cases.front();
|
||||
auto loc = locationOf(*_switchStmt.expression);
|
||||
if (switchCase.value)
|
||||
{
|
||||
if (!_dialect.equalityFunction())
|
||||
return {};
|
||||
return make_vector<Statement>(If{
|
||||
std::move(_switchStmt.location),
|
||||
make_unique<Expression>(FunctionCall{
|
||||
loc,
|
||||
Identifier{loc, "eq"_yulstring},
|
||||
Identifier{loc, _dialect.equalityFunction()->name},
|
||||
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
|
||||
}),
|
||||
std::move(switchCase.body)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_dialect.discardFunction())
|
||||
return {};
|
||||
|
||||
return make_vector<Statement>(
|
||||
makePopExpressionStatement(loc, std::move(*_switchStmt.expression)),
|
||||
makeDiscardCall(
|
||||
loc,
|
||||
_dialect,
|
||||
std::move(*_switchStmt.expression)
|
||||
),
|
||||
std::move(switchCase.body)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -151,10 +176,14 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
||||
{
|
||||
GenericFallbackReturnsVisitor<OptionalStatements, If, Switch> const visitor(
|
||||
[&](If& _ifStmt) -> OptionalStatements {
|
||||
if (_ifStmt.body.statements.empty())
|
||||
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction())
|
||||
{
|
||||
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 {};
|
||||
@ -164,9 +193,9 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
||||
removeEmptyCasesFromSwitch(_switchStmt);
|
||||
|
||||
if (_switchStmt.cases.empty())
|
||||
return reduceNoCaseSwitch(_switchStmt);
|
||||
return reduceNoCaseSwitch(m_dialect, _switchStmt);
|
||||
else if (_switchStmt.cases.size() == 1)
|
||||
return reduceSingleCaseSwitch(_switchStmt);
|
||||
return reduceSingleCaseSwitch(m_dialect, _switchStmt);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -169,93 +169,6 @@ void CodeCost::addInstructionCost(eth::Instruction _instruction)
|
||||
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)
|
||||
{
|
||||
for (auto const& variable: _assignment.variableNames)
|
||||
|
@ -22,9 +22,6 @@
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
@ -96,75 +93,6 @@ private:
|
||||
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.
|
||||
* Only works after running the Disambiguator.
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <libyul/optimiser/VarDeclInitializer.h>
|
||||
#include <libyul/optimiser/BlockFlattener.h>
|
||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||
#include <libyul/optimiser/ConstantOptimiser.h>
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/FunctionGrouper.h>
|
||||
#include <libyul/optimiser/FunctionHoister.h>
|
||||
@ -45,6 +44,7 @@
|
||||
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
||||
#include <libyul/optimiser/VarNameCleaner.h>
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
#include <libyul/backends/evm/ConstantOptimiser.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
@ -209,7 +209,8 @@ void OptimiserSuite::run(
|
||||
|
||||
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);
|
||||
yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast);
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <libyul/optimiser/VarDeclInitializer.h>
|
||||
#include <libyul/optimiser/VarNameCleaner.h>
|
||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||
#include <libyul/optimiser/ConstantOptimiser.h>
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/Disambiguator.h>
|
||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||
@ -47,8 +46,9 @@
|
||||
#include <libyul/optimiser/StructuralSimplifier.h>
|
||||
#include <libyul/optimiser/StackCompressor.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/EVMMetrics.h>
|
||||
#include <libyul/backends/wasm/WordSizeTransform.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
|
Loading…
Reference in New Issue
Block a user