Skip to content
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: 0 additions & 1 deletion libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include <libevmasm/JumpdestRemover.h>
#include <libevmasm/BlockDeduplicator.h>
#include <libevmasm/ConstantOptimiser.h>
#include <libevmasm/GasMeter.h>

#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
Expand Down
24 changes: 13 additions & 11 deletions libevmasm/GasMeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,18 +274,20 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer

switch (instructionInfo(_instruction, _evmVersion).gasPriceTier)
{
case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas;
case Tier::VeryLow: return GasCosts::tier2Gas;
case Tier::Low: return GasCosts::tier3Gas;
case Tier::Mid: return GasCosts::tier4Gas;
case Tier::High: return GasCosts::tier5Gas;
case Tier::Ext: return GasCosts::tier6Gas;
case Tier::WarmAccess: return GasCosts::warmStorageReadCost;
default: break;
case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas;
case Tier::VeryLow: return GasCosts::tier2Gas;
case Tier::Low: return GasCosts::tier3Gas;
case Tier::Mid: return GasCosts::tier4Gas;
case Tier::High: return GasCosts::tier5Gas;
case Tier::BlockHash: return GasCosts::tier6Gas;
case Tier::WarmAccess: return GasCosts::warmStorageReadCost;

case Tier::Special:
case Tier::Invalid:
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name);
}
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name);
return 0;
util::unreachable();
}

u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion)
Expand Down
56 changes: 29 additions & 27 deletions libevmasm/GasMeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,32 +43,34 @@ class KnownState;

namespace GasCosts
{
/// NOTE: The GAS_... constants referenced by comments are defined for each EVM version in the Execution Specs:
/// https://ethereum.github.io/execution-specs/autoapi/ethereum/<evm version>/vm/gas/index.html
Copy link
Collaborator Author

@cameel cameel Jan 9, 2024

Choose a reason for hiding this comment

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

Cancun spec is still in a branch: https://github.com/ethereum/execution-specs/blob/forks/cancun/src/ethereum/cancun/vm/gas.py.

Also, the links for earlier versions (e.g. gas docs for shanghai) are broken temporarily. There was a PR merged an hour ago and something must have gone wrong. Fortunately it can also be viewed in the repo. E.g. gas docs for shanghai on master.


static unsigned const stackLimit = 1024;
static unsigned const tier0Gas = 0;
static unsigned const tier1Gas = 2;
static unsigned const tier2Gas = 3;
static unsigned const tier3Gas = 5;
static unsigned const tier4Gas = 8;
static unsigned const tier5Gas = 10;
static unsigned const tier6Gas = 20;
static unsigned const tier7Gas = 0;
static unsigned const expGas = 10;
static unsigned const tier0Gas = 0; // GAS_ZERO (in Execution Specs)
static unsigned const tier1Gas = 2; // GAS_BASE
static unsigned const tier2Gas = 3; // GAS_VERY_LOW
static unsigned const tier3Gas = 5; // GAS_LOW / GAS_FAST_STEP
static unsigned const tier4Gas = 8; // GAS_MID
static unsigned const tier5Gas = 10; // GAS_HIGH
static unsigned const tier6Gas = 20; // GAS_BLOCK_HASH
static unsigned const expGas = 10; // GAS_EXPONENTIATION
inline unsigned expByteGas(langutil::EVMVersion _evmVersion)
{
return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10;
return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10; // GAS_EXPONENTIATION_PER_BYTE
}
static unsigned const keccak256Gas = 30;
static unsigned const keccak256WordGas = 6;
static unsigned const keccak256Gas = 30; // GAS_KECCAK256
static unsigned const keccak256WordGas = 6; // GAS_KECCAK256_WORD
/// Corresponds to ACCESS_LIST_ADDRESS_COST from EIP-2930
static unsigned const accessListAddressCost = 2400;
/// Corresponds to ACCESS_LIST_STORAGE_COST from EIP-2930
static unsigned const accessListStorageKeyCost = 1900;
/// Corresponds to COLD_SLOAD_COST from EIP-2929
static unsigned const coldSloadCost = 2100;
static unsigned const coldSloadCost = 2100; // GAS_COLD_SLOAD
/// Corresponds to COLD_ACCOUNT_ACCESS_COST from EIP-2929
static unsigned const coldAccountAccessCost = 2600;
static unsigned const coldAccountAccessCost = 2600; // GAS_COLD_ACCOUNT_ACCESS
/// Corresponds to WARM_STORAGE_READ_COST from EIP-2929
static unsigned const warmStorageReadCost = 100;
static unsigned const warmStorageReadCost = 100; // GAS_WARM_ACCESS
inline unsigned sloadGas(langutil::EVMVersion _evmVersion)
{
if (_evmVersion >= langutil::EVMVersion::berlin())
Expand All @@ -81,7 +83,7 @@ namespace GasCosts
return 50;
}
/// Corresponds to SSTORE_SET_GAS
static unsigned const sstoreSetGas = 20000;
static unsigned const sstoreSetGas = 20000; // GAS_STORAGE_SET
/// Corresponds to SSTORE_RESET_GAS from EIP-2929
static unsigned const sstoreResetGas = 5000 - coldSloadCost;
/// Corresponds to SSTORE_CLEARS_SCHEDULE from EIP-2200
Expand Down Expand Up @@ -130,11 +132,11 @@ namespace GasCosts
else
return 20;
}
static unsigned const jumpdestGas = 1;
static unsigned const logGas = 375;
static unsigned const logDataGas = 8;
static unsigned const logTopicGas = 375;
static unsigned const createGas = 32000;
static unsigned const jumpdestGas = 1; // GAS_JUMPDEST
static unsigned const logGas = 375; // GAS_LOG
static unsigned const logDataGas = 8; // GAS_LOG_DATA
static unsigned const logTopicGas = 375; // GAS_LOG_TOPIC
static unsigned const createGas = 32000; // GAS_CREATE
inline unsigned callGas(langutil::EVMVersion _evmVersion)
{
if (_evmVersion >= langutil::EVMVersion::berlin())
Expand All @@ -144,10 +146,10 @@ namespace GasCosts
else
return 40;
}
static unsigned const callStipend = 2300;
static unsigned const callValueTransferGas = 9000;
static unsigned const callNewAccountGas = 25000;
inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion)
static unsigned const callStipend = 2300; // GAS_CALL_STIPEND
static unsigned const callValueTransferGas = 9000; // GAS_CALL_VALUE
static unsigned const callNewAccountGas = 25000; // GAS_NEW_ACCOUNT / GAS_SELF_DESTRUCT_NEW_ACCOUNT
inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion) // GAS_SELF_DESTRUCT
{
if (_evmVersion >= langutil::EVMVersion::berlin())
return coldAccountAccessCost;
Expand All @@ -164,9 +166,9 @@ namespace GasCosts
else
return 24000;
}
static unsigned const memoryGas = 3;
static unsigned const memoryGas = 3; // GAS_MEMORY / GAS_COPY / GAS_RETURN_DATA_COPY
static unsigned const quadCoeffDiv = 512;
static unsigned const createDataGas = 200;
static unsigned const createDataGas = 200; // GAS_CODE_DEPOSIT
static unsigned const txGas = 21000;
static unsigned const txCreateGas = 53000;
static unsigned const txDataZeroGas = 4;
Expand Down
14 changes: 7 additions & 7 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } },
{ Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Special } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } },
{ Instruction::CALLER, { "CALLER", 0, 0, 1, false, Tier::Base } },
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, Tier::Base } },
Expand All @@ -221,13 +221,13 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, Tier::Base } },
{ Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } },
{ Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } },
{ Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::Special } },
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::Special } },
{ Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } },
{ Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::MCOPY, { "MCOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } },
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
{ Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Special } },
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::BlockHash } },
{ Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
Expand Down
27 changes: 15 additions & 12 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,20 +287,23 @@ inline Instruction logInstruction(unsigned _number)
return Instruction(unsigned(Instruction::LOG0) + _number);
}

/// Gas price tiers representing static cost of an instruction.
/// Opcodes whose cost is dynamic or depends on EVM version should use the `Special` tier and need
/// dedicated logic in GasMeter (especially in estimateMax()).
/// The tiers loosely follow opcode groups originally defined in the Yellow Paper.
enum class Tier
{
Zero = 0, // 0, Zero
Base, // 2, Quick
VeryLow, // 3, Fastest
Low, // 5, Fast
Mid, // 8, Mid
High, // 10, Slow
Ext, // 20, Ext
Copy link
Collaborator Author

@cameel cameel Jan 9, 2024

Choose a reason for hiding this comment

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

I'm not sure where Ext even comes from. It's for BLOCKHASH, which in Execution Specs is simply GAS_BLOCK_HASH, and there's no GAS_EXT there. From the name I at first assumed it could be for EXTCODEHASH and EXTCODESIZE, which in Yellow Paper is in a group called W_extaccount (along with BALANCE), but that's not it. And even in the Yellow Paper BLOCKHASH is in a group of its own.

Since all of this is unnecessarily confusing, I decided to rename this tier to BlockHash to match the Execution Specs.

WarmAccess, // 100, Warm Access
ExtCode, // 700, Extcode
Balance, // 400, Balance
Comment on lines -300 to -301
Copy link
Collaborator Author

@cameel cameel Jan 9, 2024

Choose a reason for hiding this comment

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

I'm removing the ExtCode and Balance tiers because they seem obsolete. The instructions using them no longer have a static cost independent of the EVM version so I think they should be using the Special tier.

Special, // multiparam or otherwise special
Invalid // Invalid.
// NOTE: Tiers should be ordered by cost, since we sometimes perform comparisons between them.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We do that in https://github.com/ethereum/solidity/blob/c78f9652f5fa7c342166db527e1a8d1b0f041580/libyul/optimiser/Metrics.cpp#L185-L194

It's also the only place other than GasMeter::runGas() where we refer to these tiers directly. All other uses are through GasMeter so the removal of the obsolete tiers will have no real effect on compiler behavior.

Copy link
Collaborator Author

@cameel cameel Jan 9, 2024

Choose a reason for hiding this comment

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

BTW, I didn't have time to dig into this, but I wonder if that logic in Metrics.cpp is even correct. Is Tier::BlockHash (formerly called Tier::Ext) really supposed add 49 to to cost even though BLOCKHASH costs 20 gas? Should TLOAD/TSTORE also get 49? And isn't this also overpricing some instructions that are in Special not due to dynamic cost but rather becuse their cost is EVM-version dependent?

Copy link
Contributor

@matheusaaguiar matheusaaguiar Jan 9, 2024

Choose a reason for hiding this comment

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

Maybe, when Tier::Ext was introduced, the logic was not updated accordingly and it went unnoticed...
I have no idea where the value 49 comes from, but it would make sense that Tier::BlockHash should get a value of 20 and then the upcoming Tier::WarmAccess for transient storage opcodes should get 100.
However, I also noticed that SSTORE is in Tier::Special, which would make it underpriced ?

Zero = 0, // 0, Zero
Base, // 2, Quick
VeryLow, // 3, Fastest
Low, // 5, Fast
Mid, // 8, Mid
High, // 10, Slow
BlockHash, // 20
WarmAccess, // 100, Warm Access
Special, // multiparam or otherwise special
Invalid, // Invalid.
};

/// Information structure for a particular instruction.
Expand Down
11 changes: 2 additions & 9 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1480,15 +1480,8 @@ void CompilerStack::compileContract(
solAssert(!m_viaIR, "");
bytes cborEncodedMetadata = createCBORMetadata(compiledContract, /* _forIR */ false);

try
{
// Run optimiser and compile the contract.
compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
}
catch(evmasm::OptimizerException const&)
{
solAssert(false, "Optimizer exception during compilation");
}
// Run optimiser and compile the contract.
compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
Comment on lines -1483 to +1484
Copy link
Collaborator Author

@cameel cameel Jan 9, 2024

Choose a reason for hiding this comment

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

I ran into this when I forgot to update GasMeter::runGas() and got an ICE without the original message and pointing here rather than at the original assert in GasMeter::runGas() that failed. Since OptimizerException inherits from util::Exception and we have top-level handlers for that, I think it's better to just let it through.


_otherCompilers[compiledContract.contract] = compiler;

Expand Down
2 changes: 0 additions & 2 deletions solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@

#include <libyul/YulStack.h>

#include <libevmasm/Instruction.h>
#include <libevmasm/Disassemble.h>
#include <libevmasm/GasMeter.h>

#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceReferenceFormatter.h>
Expand Down