Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM version #3569

Merged
merged 19 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Features:
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
* Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
* Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
* Standard JSON: Reject badly formatted invalid JSON inputs.
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
* Support and recommend using ``emit EventName();`` to call events explicitly.
Expand Down
8 changes: 0 additions & 8 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ jobs:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
steps:
- checkout
- run:
name: Init submodules
command: |
git submodule update --init
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why were these removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't have any submodules anymore, do we? I can remove it from this PR.

- restore_cache:
name: Restore Boost build
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
Expand Down Expand Up @@ -94,10 +90,6 @@ jobs:
command: |
apt-get -qq update
apt-get -qy install ccache cmake libboost-all-dev libz3-dev
- run:
name: Init submodules
command: |
git submodule update --init
- run:
name: Store commit hash and prerelease
command: |
Expand Down
1 change: 1 addition & 0 deletions docs/using-the-compiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Input Description
enabled: true,
runs: 500
},
evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
// Metadata settings (optional)
metadata: {
// Use only literal content and not URLs (false by default)
Expand Down
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woohoo, I should be using this for the bitwise shifting optimiser rules in #3580.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was a really painful process. This setting is needed literally everywhere...

{
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
Loading