From 4407af53b5c9d206f1362968619bb09629388525 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 14 May 2019 16:09:52 +0200 Subject: [PATCH] Gas meter for Yul expressions. --- libyul/optimiser/Metrics.cpp | 90 ++++++++++++++++++++++++++++++++++++ libyul/optimiser/Metrics.h | 72 +++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 11e0b31c3..a8f9807df 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -22,11 +22,14 @@ #include #include +#include #include #include +#include #include +#include using namespace std; using namespace dev; @@ -166,6 +169,93 @@ 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 _costs) const +{ + return _costs.first * m_runs + _costs.second; +} + + +pair 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 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) diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index 98041a606..51c6df9a7 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -21,6 +21,8 @@ #pragma once #include +#include +#include #include @@ -28,6 +30,7 @@ namespace yul { struct Dialect; +struct EVMDialect; /** * Metric for the size of code. @@ -93,6 +96,75 @@ 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 _costs) const; + + EVMDialect const& m_dialect; + bool m_isCreation = false; + size_t m_runs; +}; + +class GasMeterVisitor: public ASTWalker +{ +public: + static std::pair costs( + Expression const& _expression, + EVMDialect const& _dialect, + bool _isCreation + ); + + static std::pair 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.