Gas meter for Yul expressions.

This commit is contained in:
chriseth 2019-05-14 16:09:52 +02:00
parent 6e9bb2c073
commit 4407af53b5
2 changed files with 162 additions and 0 deletions

View File

@ -22,11 +22,14 @@
#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;
@ -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<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)

View File

@ -21,6 +21,8 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Instruction.h>
@ -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<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.