Skip to content

Commit

Permalink
Use EVM version in gas meter and optimizer.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Mar 5, 2018
1 parent 5a54cd5 commit 6ec4517
Show file tree
Hide file tree
Showing 28 changed files with 328 additions and 201 deletions.
4 changes: 3 additions & 1 deletion libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ void Assembly::injectStart(AssemblyItem const& _i)
m_items.insert(m_items.begin(), _i);
}

Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
{
OptimiserSettings settings;
settings.isCreation = _isCreation;
Expand All @@ -365,6 +365,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
settings.runCSE = true;
settings.runConstantOptimiser = true;
}
settings.evmVersion = _evmVersion;
settings.expectedExecutionsPerDeployment = _runs;
optimise(settings);
return *this;
Expand Down Expand Up @@ -482,6 +483,7 @@ map<u256, u256> Assembly::optimiseInternal(
ConstantOptimisationMethod::optimiseConstants(
_settings.isCreation,
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment,
_settings.evmVersion,
*this,
m_items
);
Expand Down
5 changes: 4 additions & 1 deletion libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <libevmasm/LinkerObject.h>
#include <libevmasm/Exceptions.h>

#include <libsolidity/interface/EVMVersion.h>

#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/SHA3.h>
Expand Down Expand Up @@ -107,6 +109,7 @@ class Assembly
bool runDeduplicate = false;
bool runCSE = false;
bool runConstantOptimiser = false;
solidity::EVMVersion evmVersion;
/// This specifies an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
size_t expectedExecutionsPerDeployment = 200;
Expand All @@ -120,7 +123,7 @@ class Assembly
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
/// If @a _enable is not set, will perform some simple peephole optimizations.
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200);

/// Create a text representation of the assembly.
std::string assemblyString(
Expand Down
11 changes: 9 additions & 2 deletions libevmasm/ConstantOptimiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ using namespace dev::eth;
unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation,
size_t _runs,
solidity::EVMVersion _evmVersion,
Assembly& _assembly,
AssemblyItems& _items
)
Expand All @@ -48,6 +49,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
params.multiplicity = it.second;
params.isCreation = _isCreation;
params.runs = _runs;
params.evmVersion = _evmVersion;
LiteralMethod lit(params, item.data());
bigint literalGas = lit.gasNeeded();
CodeCopyMethod copy(params, item.data());
Expand Down Expand Up @@ -80,7 +82,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
else if (item.type() == Operation)
gas += GasMeter::runGas(item.instruction());
{
if (item.instruction() == Instruction::EXP)
gas += GasCosts::expGas;
else
gas += GasMeter::runGas(item.instruction());
}
return gas;
}

Expand Down Expand Up @@ -286,7 +293,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
{
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
return combineGas(
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas),
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
// Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
0
Expand Down
4 changes: 4 additions & 0 deletions libevmasm/ConstantOptimiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#include <libevmasm/Exceptions.h>

#include <libsolidity/interface/EVMVersion.h>

#include <libdevcore/Assertions.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
Expand Down Expand Up @@ -50,6 +52,7 @@ class ConstantOptimisationMethod
static unsigned optimiseConstants(
bool _isCreation,
size_t _runs,
solidity::EVMVersion _evmVersion,
Assembly& _assembly,
AssemblyItems& _items
);
Expand All @@ -59,6 +62,7 @@ class ConstantOptimisationMethod
bool isCreation; ///< Whether this is called during contract creation or runtime.
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
size_t multiplicity; ///< Number of times the constant appears in the code.
solidity::EVMVersion evmVersion; ///< Version of the EVM
};

explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
Expand Down
32 changes: 20 additions & 12 deletions libevmasm/GasMeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Operation:
{
ExpressionClasses& classes = m_state->expressionClasses();
gas = runGas(_item.instruction());
switch (_item.instruction())
{
case Instruction::SSTORE:
Expand All @@ -72,26 +71,29 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
m_state->storageContent().count(slot) &&
classes.knownNonZero(m_state->storageContent().at(slot))
))
gas += GasCosts::sstoreResetGas; //@todo take refunds into account
gas = GasCosts::sstoreResetGas; //@todo take refunds into account
else
gas += GasCosts::sstoreSetGas;
gas = GasCosts::sstoreSetGas;
break;
}
case Instruction::SLOAD:
gas += GasCosts::sloadGas;
gas = GasCosts::sloadGas(m_evmVersion);
break;
case Instruction::RETURN:
case Instruction::REVERT:
gas = runGas(_item.instruction());
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE:
gas = runGas(_item.instruction());
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8:
gas = runGas(_item.instruction());
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
Expand All @@ -105,10 +107,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
gas = runGas(_item.instruction());
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
case Instruction::EXTCODESIZE:
gas = GasCosts::extCodeGas(m_evmVersion);
break;
case Instruction::EXTCODECOPY:
gas = GasCosts::extCodeGas(m_evmVersion);
gas += memoryGas(-1, -3);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
break;
Expand Down Expand Up @@ -137,7 +144,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasConsumption::infinite();
else
{
gas = GasCosts::callGas;
gas = GasCosts::callGas(m_evmVersion);
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
gas += (*value);
else
Expand All @@ -155,7 +162,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
}
case Instruction::SELFDESTRUCT:
gas = GasCosts::selfdestructGas;
gas = GasCosts::selfdestructGas(m_evmVersion);
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
break;
case Instruction::CREATE:
Expand All @@ -172,11 +179,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::EXP:
gas = GasCosts::expGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += GasCosts::expByteGas * (32 - (h256(*value).firstBitSet() / 8));
gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8));
else
gas += GasCosts::expByteGas * 32;
gas += GasCosts::expByteGas(m_evmVersion) * 32;
break;
case Instruction::BALANCE:
gas = GasCosts::balanceGas(m_evmVersion);
break;
default:
gas = runGas(_item.instruction());
break;
}
break;
Expand Down Expand Up @@ -241,12 +252,9 @@ unsigned GasMeter::runGas(Instruction _instruction)
case Tier::Mid: return GasCosts::tier4Gas;
case Tier::High: return GasCosts::tier5Gas;
case Tier::Ext: return GasCosts::tier6Gas;
case Tier::Special: return GasCosts::tier7Gas;
case Tier::ExtCode: return GasCosts::extCodeGas;
case Tier::Balance: return GasCosts::balanceGas;
default: break;
}
assertThrow(false, OptimizerException, "Invalid gas tier.");
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
return 0;
}

Expand Down
44 changes: 34 additions & 10 deletions libevmasm/GasMeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@

#pragma once

#include <ostream>
#include <tuple>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h>

#include <libsolidity/interface/EVMVersion.h>

#include <ostream>
#include <tuple>

namespace dev
{
namespace eth
Expand All @@ -44,13 +47,25 @@ namespace GasCosts
static unsigned const tier5Gas = 10;
static unsigned const tier6Gas = 20;
static unsigned const tier7Gas = 0;
static unsigned const extCodeGas = 700;
static unsigned const balanceGas = 400;
inline unsigned extCodeGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20;
}
inline unsigned balanceGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20;
}
static unsigned const expGas = 10;
static unsigned const expByteGas = 50;
inline unsigned expByteGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10;
}
static unsigned const keccak256Gas = 30;
static unsigned const keccak256WordGas = 6;
static unsigned const sloadGas = 200;
inline unsigned sloadGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50;
}
static unsigned const sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000;
static unsigned const sstoreRefundGas = 15000;
Expand All @@ -59,11 +74,17 @@ namespace GasCosts
static unsigned const logDataGas = 8;
static unsigned const logTopicGas = 375;
static unsigned const createGas = 32000;
static unsigned const callGas = 700;
inline unsigned callGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 40;
}
static unsigned const callStipend = 2300;
static unsigned const callValueTransferGas = 9000;
static unsigned const callNewAccountGas = 25000;
static unsigned const selfdestructGas = 5000;
inline unsigned selfdestructGas(EVMVersion _evmVersion)
{
return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0;
}
static unsigned const selfdestructRefundGas = 24000;
static unsigned const memoryGas = 3;
static unsigned const quadCoeffDiv = 512;
Expand Down Expand Up @@ -100,8 +121,8 @@ class GasMeter
};

/// Constructs a new gas meter given the current state.
explicit GasMeter(std::shared_ptr<KnownState> const& _state, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {}
GasMeter(std::shared_ptr<KnownState> const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {}

/// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state.
Expand All @@ -110,6 +131,8 @@ class GasMeter

u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }

/// @returns gas costs for simple instructions with constant gas costs (that do not
/// change with EVM versions)
static unsigned runGas(Instruction _instruction);

private:
Expand All @@ -123,6 +146,7 @@ class GasMeter
GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize);

std::shared_ptr<KnownState> m_state;
EVMVersion m_evmVersion;
/// Largest point where memory was accessed since the creation of this object.
u256 m_largestMemoryAccess;
};
Expand Down
6 changes: 3 additions & 3 deletions libevmasm/PathGasMeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ using namespace std;
using namespace dev;
using namespace dev::eth;

PathGasMeter::PathGasMeter(AssemblyItems const& _items):
m_items(_items)
PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion):
m_items(_items), m_evmVersion(_evmVersion)
{
for (size_t i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
Expand Down Expand Up @@ -59,7 +59,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
m_queue.pop_back();

shared_ptr<KnownState> state = path->state;
GasMeter meter(state, path->largestMemoryAccess);
GasMeter meter(state, m_evmVersion, path->largestMemoryAccess);
ExpressionClasses& classes = state->expressionClasses();
GasMeter::GasConsumption gas = path->gas;
size_t index = path->index;
Expand Down
8 changes: 6 additions & 2 deletions libevmasm/PathGasMeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@

#pragma once

#include <libevmasm/GasMeter.h>

#include <libsolidity/interface/EVMVersion.h>

#include <set>
#include <vector>
#include <memory>
#include <libevmasm/GasMeter.h>

namespace dev
{
Expand All @@ -50,7 +53,7 @@ struct GasPath
class PathGasMeter
{
public:
explicit PathGasMeter(AssemblyItems const& _items);
explicit PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion);

GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);

Expand All @@ -60,6 +63,7 @@ class PathGasMeter
std::vector<std::unique_ptr<GasPath>> m_queue;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
solidity::EVMVersion m_evmVersion;
};

}
Expand Down
Loading

0 comments on commit 6ec4517

Please sign in to comment.