Adds support for the EVM version "Paris".

Deprecates `block.difficulty` and disallow `difficulty()` in inline assembly for EVM versions >= paris.
The change is due to the renaming introduced by EIP-4399 (see: https://eips.ethereum.org/EIPS/eip-4399).
Introduces `block.prevrandao` in Solidity and `prevrandao()` in inline assembly for EVM versions >= paris.

Co-authored-by: Alex Beregszaszi <alex@rtfs.hu>
Co-authored-by: Daniel <daniel@ekpyron.org>
Co-authored-by: matheusaaguiar <95899911+matheusaaguiar@users.noreply.github.com>
Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>
This commit is contained in:
Rodrigo Q. Saramago 2022-11-23 11:51:34 +01:00
parent d70d79af4a
commit ef6ff2f055
No known key found for this signature in database
GPG Key ID: 9B36B2525704A359
114 changed files with 842 additions and 221 deletions

View File

@ -7,7 +7,9 @@ Language Features:
Compiler Features:
* Commandline Interface: Return exit code ``2`` on uncaught exceptions.
* Commandline Interface: Add `--no-cbor-metadata` that skips CBOR metadata from getting appended at the end of the bytecode.
* EVM: Basic support for the EVM version "Paris".
* EVM: Deprecate ``block.difficulty`` and disallow ``difficulty()`` in inline assembly for EVM versions >= paris. The change is due to the renaming introduced by [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399).
* EVM: Introduce ``block.prevrandao`` in Solidity and ``prevrandao()`` in inline assembly for EVM versions >= paris.
* EVM: Support for the EVM version "Paris".
* Natspec: Add event Natspec inheritance for devdoc.
* Standard JSON: Add a boolean field `settings.metadata.appendCBOR` that skips CBOR metadata from getting appended at the end of the bytecode.
* Yul EVM Code Transform: Generate more optimal code for user-defined functions that always terminate a transaction. No return labels will be pushed for calls to functions that always terminate.

View File

@ -8,7 +8,7 @@ Order of Precedence of Operators
================================
.. include:: types/operator-precedence-table.rst
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send
.. index:: assert, block, coinbase, difficulty, prevrandao, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send
Global Variables
================
@ -32,9 +32,10 @@ Global Variables
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
- ``block.chainid`` (``uint``): current chain id
- ``block.coinbase`` (``address payable``): current block miner's address
- ``block.difficulty`` (``uint``): current block difficulty
- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` that will be removed in the next breaking release
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``) (see `EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_ )
- ``block.timestamp`` (``uint``): current block timestamp in seconds since Unix epoch
- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata

View File

@ -291,8 +291,8 @@ YulEVMBuiltin:
| 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode'
| 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid'
| 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice'
| 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit'
| 'basefee';
| 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao'
| 'gaslimit' | 'basefee';
YulLBrace: '{' -> pushMode(YulMode);
YulRBrace: '}' -> popMode;

View File

@ -65,7 +65,7 @@ There are special variables and functions which always exist in the global
namespace and are mainly used to provide information about the blockchain
or are general-use utility functions.
.. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin
.. index:: abi, block, coinbase, difficulty, prevrandao, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin
Block and Transaction Properties
@ -75,9 +75,10 @@ Block and Transaction Properties
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
- ``block.chainid`` (``uint``): current chain id
- ``block.coinbase`` (``address payable``): current block miner's address
- ``block.difficulty`` (``uint``): current block difficulty
- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` (`EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_ )
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``)
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes calldata``): complete calldata
@ -394,4 +395,4 @@ These keywords are reserved in Solidity. They might become part of the syntax in
``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
``var``.
``var``.

View File

@ -751,8 +751,8 @@ This document does not want to be a full description of the Ethereum virtual mac
Please refer to a different document if you are interested in the precise semantics.
Opcodes marked with ``-`` do not return a result and all others return exactly one value.
Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I`` and ``L`` are present since Frontier, Homestead,
Byzantium, Constantinople, Istanbul or London respectively.
Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L`` and ``P`` are present since Frontier,
Homestead, Byzantium, Constantinople, Istanbul, London or Paris respectively.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``.
@ -931,6 +931,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
+-------------------------+-----+---+-----------------------------------------------------------------+
| difficulty() | | F | difficulty of the current block (see note below) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| prevrandao() | | P | randomness provided by the beacon chain (see note below) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gaslimit() | | F | block gas limit of the current block |
+-------------------------+-----+---+-----------------------------------------------------------------+
@ -945,8 +947,10 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
The remaining bytes will retain their values as of before the call.
.. note::
With the Paris network upgrade the semantics of ``difficulty`` have been changed.
It returns the value of ``prevrandao``, which is a 256-bit value, whereas the highest recorded
The `difficulty()` instruction is disallowed in EVM version >= Paris.
With the Paris network upgrade the semantics of the instruction that was previously called
``difficulty`` have been changed and the instruction was renamed to ``prevrandao``.
It can now return arbitrary values in the full 256-bit range, whereas the highest recorded
difficulty value within Ethash was ~54 bits.
This change is described in `EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_.
Please note that irrelevant to which EVM version is selected in the compiler, the semantics of

View File

@ -237,7 +237,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
sourceIndex = static_cast<int>(iter->second);
}
auto [name, data] = item.nameAndData();
auto [name, data] = item.nameAndData(m_evmVersion);
Json::Value jsonItem;
jsonItem["name"] = name;
jsonItem["begin"] = item.location().start;

View File

@ -49,7 +49,7 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly
{
public:
Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { }
Assembly(langutil::EVMVersion _evmVersion, bool _creation, std::string _name): m_evmVersion(_evmVersion), m_creation(_creation), m_name(std::move(_name)) { }
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
@ -112,6 +112,7 @@ public:
/// Changes the source location used for each appended item.
void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; }
langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; }
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
/// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.
LinkerObject const& assemble() const;
@ -208,6 +209,8 @@ protected:
mutable LinkerObject m_assembledObject;
mutable std::vector<size_t> m_tagPositionsInBytecode;
langutil::EVMVersion m_evmVersion;
int m_deposit = 0;
/// True, if the assembly contains contract creation code.
bool const m_creation = false;

View File

@ -69,12 +69,12 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
return make_pair(subId, tag);
}
pair<string, string> AssemblyItem::nameAndData() const
pair<string, string> AssemblyItem::nameAndData(langutil::EVMVersion _evmVersion) const
{
switch (type())
{
case Operation:
return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
case Push:
return {"PUSH", toStringInHex(data())};
case PushTag:
@ -168,7 +168,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
size_t AssemblyItem::arguments() const
{
if (type() == Operation)
return static_cast<size_t>(instructionInfo(instruction()).args);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).args);
else if (type() == VerbatimBytecode)
return get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable)
@ -182,7 +184,9 @@ size_t AssemblyItem::returnValues() const
switch (m_type)
{
case Operation:
return static_cast<size_t>(instructionInfo(instruction()).ret);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).ret);
case Push:
case PushTag:
case PushData:
@ -251,7 +255,7 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
case Operation:
{
assertThrow(isValidInstruction(instruction()), AssemblyException, "Invalid instruction.");
text = util::toLower(instructionInfo(instruction()).name);
text = util::toLower(instructionInfo(instruction(), _assembly.evmVersion()).name);
break;
}
case Push:
@ -323,12 +327,13 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
return text;
}
// Note: This method is exclusively used for debugging.
ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
{
switch (_item.type())
{
case Operation:
_out << " " << instructionInfo(_item.instruction()).name;
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
_out << "\t" << _item.getJumpTypeAsString();
break;

View File

@ -108,10 +108,11 @@ public:
/// This function is used in `Assembly::assemblyJSON`.
/// It returns the name & data of the current assembly item.
/// @param _evmVersion the EVM version.
/// @returns a pair, where the first element is the json-assembly
/// item name, where second element is the string representation
/// of it's data.
std::pair<std::string, std::string> nameAndData() const;
std::pair<std::string, std::string> nameAndData(langutil::EVMVersion _evmVersion) const;
bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); }

View File

@ -406,7 +406,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
m_stack.erase(m_stackHeight - static_cast<int>(i));
}
appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction(), EVMVersion()).ret == 1)
{
m_stack[m_stackHeight] = _c;
m_classPositions[_c].insert(m_stackHeight);
@ -414,7 +414,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
else
{
assertThrow(
instructionInfo(expr.item->instruction()).ret == 0,
instructionInfo(expr.item->instruction(), EVMVersion()).ret == 0,
OptimizerException,
"Invalid number of return values."
);

View File

@ -79,18 +79,18 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
return optimisations;
}
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion)
{
bigint gas = 0;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
gas += GasMeter::runGas(Instruction::PUSH1, _evmVersion);
else if (item.type() == Operation)
{
if (item.instruction() == Instruction::EXP)
gas += GasCosts::expGas;
else
gas += GasMeter::runGas(item.instruction());
gas += GasMeter::runGas(item.instruction(), _evmVersion);
}
return gas;
}
@ -131,7 +131,7 @@ void ConstantOptimisationMethod::replaceConstants(
bigint LiteralMethod::gasNeeded() const
{
return combineGas(
simpleRunGas({Instruction::PUSH1}),
simpleRunGas({Instruction::PUSH1}, m_params.evmVersion),
// PUSHX plus data
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
0
@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const
{
return combineGas(
// Run gas: we ignore memory increase costs
simpleRunGas(copyRoutine()) + GasCosts::copyGas,
simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
// Data gas for data itself
@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
{
auto numExps = static_cast<size_t>(count(_routine.begin(), _routine.end(), Instruction::EXP));
return combineGas(
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
simpleRunGas(_routine, m_params.evmVersion) + 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(m_params.evmVersion) : GasCosts::createDataGas),
0

View File

@ -76,7 +76,7 @@ protected:
protected:
/// @returns the run gas for the given items ignoring special gas costs
static bigint simpleRunGas(AssemblyItems const& _items);
static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion);
/// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const;
static size_t bytesRequired(AssemblyItems const& _items);

View File

@ -30,6 +30,7 @@ using namespace solidity::evmasm;
void solidity::evmasm::eachInstruction(
bytes const& _mem,
langutil::EVMVersion _evmVersion,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
@ -38,7 +39,7 @@ void solidity::evmasm::eachInstruction(
Instruction const instr{*it};
int additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
additional = instructionInfo(instr, _evmVersion).additional;
u256 data{};
@ -57,15 +58,15 @@ void solidity::evmasm::eachInstruction(
}
}
string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter)
string solidity::evmasm::disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, string const& _delimiter)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
eachInstruction(_mem, _evmVersion, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter;
else
{
InstructionInfo info = instructionInfo(_instr);
InstructionInfo info = instructionInfo(_instr, _evmVersion);
ret << info.name;
if (info.additional)
ret << " 0x" << std::uppercase << std::hex << _data;

View File

@ -30,9 +30,9 @@ namespace solidity::evmasm
{
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction, u256 const&)> const& _onInstruction);
void eachInstruction(bytes const& _mem, langutil::EVMVersion _evmVersion, std::function<void(Instruction, u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem, std::string const& _delimiter = " ");
std::string disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, std::string const& _delimiter = " ");
}

View File

@ -52,10 +52,10 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case PushProgramSize:
case PushLibraryAddress:
case PushDeployTimeAddress:
gas = runGas(Instruction::PUSH1);
gas = runGas(Instruction::PUSH1, m_evmVersion);
break;
case Tag:
gas = runGas(Instruction::JUMPDEST);
gas = runGas(Instruction::JUMPDEST, m_evmVersion);
break;
case Operation:
{
@ -80,19 +80,19 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
case Instruction::RETURN:
case Instruction::REVERT:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
@ -106,7 +106,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
@ -195,13 +195,13 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasCosts::balanceGas(m_evmVersion);
break;
case Instruction::CHAINID:
gas = runGas(Instruction::CHAINID);
gas = runGas(Instruction::CHAINID, m_evmVersion);
break;
case Instruction::SELFBALANCE:
gas = runGas(Instruction::SELFBALANCE);
gas = runGas(Instruction::SELFBALANCE, m_evmVersion);
break;
default:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
break;
}
break;
@ -252,12 +252,12 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS
}));
}
unsigned GasMeter::runGas(Instruction _instruction)
unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVersion)
{
if (_instruction == Instruction::JUMPDEST)
return 1;
switch (instructionInfo(_instruction).gasPriceTier)
switch (instructionInfo(_instruction, _evmVersion).gasPriceTier)
{
case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas;
@ -268,7 +268,7 @@ unsigned GasMeter::runGas(Instruction _instruction)
case Tier::Ext: return GasCosts::tier6Gas;
default: break;
}
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name);
return 0;
}

View File

@ -221,7 +221,7 @@ public:
/// @returns gas costs for simple instructions with constant gas costs (that do not
/// change with EVM versions)
static unsigned runGas(Instruction _instruction);
static unsigned runGas(Instruction _instruction, langutil::EVMVersion _evmVersion);
/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
@ -257,5 +257,4 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con
return _str << std::dec << _consumption.value;
}
}

View File

@ -76,7 +76,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY },
{ "DIFFICULTY", Instruction::PREVRANDAO },
{ "PREVRANDAO", Instruction::PREVRANDAO },
{ "GASLIMIT", Instruction::GASLIMIT },
{ "CHAINID", Instruction::CHAINID },
{ "SELFBALANCE", Instruction::SELFBALANCE },
@ -174,6 +175,7 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT }
};
/// @note InstructionInfo is assumed to be the same across all EVM versions except for the instruction name.
static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ // Add, Args, Ret, SideEffects, GasPriceTier
{ Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } },
@ -223,7 +225,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } },
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::Base } },
{ Instruction::PREVRANDAO, { "PREVRANDAO", 0, 0, 1, false, Tier::Base } },
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::Base } },
{ Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } },
{ Instruction::SELFBALANCE, { "SELFBALANCE", 0, 0, 1, false, Tier::Low } },
@ -321,10 +323,12 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
};
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion)
{
try
{
if (_inst == Instruction::PREVRANDAO && _evmVersion < langutil::EVMVersion::paris())
return InstructionInfo({ "DIFFICULTY", 0, 0, 1, false, Tier::Base });
return c_instructionInfo.at(_inst);
}
catch (...)

View File

@ -25,6 +25,7 @@
#include <libevmasm/Exceptions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <liblangutil/EVMVersion.h>
namespace solidity::evmasm
{
@ -83,7 +84,7 @@ enum class Instruction: uint8_t
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number
DIFFICULTY, ///< get the block's difficulty
PREVRANDAO, ///< get randomness provided by the beacon chain
GASLIMIT, ///< get the block's gas limit
CHAINID, ///< get the config's chainid param
SELFBALANCE, ///< get balance of the current account
@ -306,7 +307,7 @@ struct InstructionInfo
};
/// Information on all the instructions.
InstructionInfo instructionInfo(Instruction _inst);
InstructionInfo instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion);
/// check whether instructions exists.
bool isValidInstruction(Instruction _inst);

View File

@ -130,7 +130,9 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
else
{
Instruction instruction = _item.instruction();
InstructionInfo info = instructionInfo(instruction);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
InstructionInfo info = instructionInfo(instruction, EVMVersion());
if (SemanticInformation::isDupInstruction(_item))
setStackElement(
m_stackHeight + 1,

View File

@ -38,7 +38,7 @@ struct OptimiserState
{
AssemblyItems const& items;
size_t i;
std::back_insert_iterator<AssemblyItems> out;
back_insert_iterator<AssemblyItems> out;
};
template<typename FunctionType>
@ -53,7 +53,11 @@ template <class Method>
struct SimplePeepholeOptimizerMethod
{
template <size_t... Indices>
static bool applyRule(AssemblyItems::const_iterator _in, back_insert_iterator<AssemblyItems> _out, index_sequence<Indices...>)
static bool applyRule(
AssemblyItems::const_iterator _in,
back_insert_iterator<AssemblyItems> _out,
index_sequence<Indices...>
)
{
return Method::applySimple(_in[Indices]..., _out);
}
@ -75,7 +79,10 @@ struct SimplePeepholeOptimizerMethod
struct Identity: SimplePeepholeOptimizerMethod<Identity>
{
static bool applySimple(AssemblyItem const& _item, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _item,
back_insert_iterator<AssemblyItems> _out
)
{
*_out = _item;
return true;
@ -84,7 +91,11 @@ struct Identity: SimplePeepholeOptimizerMethod<Identity>
struct PushPop: SimplePeepholeOptimizerMethod<PushPop>
{
static bool applySimple(AssemblyItem const& _push, AssemblyItem const& _pop, std::back_insert_iterator<AssemblyItems>)
static bool applySimple(
AssemblyItem const& _push,
AssemblyItem const& _pop,
back_insert_iterator<AssemblyItems>
)
{
auto t = _push.type();
return _pop == Instruction::POP && (
@ -100,15 +111,15 @@ struct OpPop: SimplePeepholeOptimizerMethod<OpPop>
static bool applySimple(
AssemblyItem const& _op,
AssemblyItem const& _pop,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_pop == Instruction::POP && _op.type() == Operation)
{
Instruction instr = _op.instruction();
if (instructionInfo(instr).ret == 1 && !instructionInfo(instr).sideEffects)
if (instructionInfo(instr, langutil::EVMVersion()).ret == 1 && !instructionInfo(instr, langutil::EVMVersion()).sideEffects)
{
for (int j = 0; j < instructionInfo(instr).args; j++)
for (int j = 0; j < instructionInfo(instr, langutil::EVMVersion()).args; j++)
*_out = {Instruction::POP, _op.location()};
return true;
}
@ -122,7 +133,7 @@ struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
static bool applySimple(
AssemblyItem const& _op,
AssemblyItem const& _stop,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_stop == Instruction::STOP)
@ -130,7 +141,7 @@ struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
if (_op.type() == Operation)
{
Instruction instr = _op.instruction();
if (!instructionInfo(instr).sideEffects)
if (!instructionInfo(instr, langutil::EVMVersion()).sideEffects)
{
*_out = {Instruction::STOP, _op.location()};
return true;
@ -153,7 +164,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
AssemblyItem const& _push,
AssemblyItem const& _pushOrDup,
AssemblyItem const& _returnRevert,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -162,7 +173,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
(_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1))
)
if (
(_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
(_op.type() == Operation && !instructionInfo(_op.instruction(), langutil::EVMVersion()).sideEffects) ||
_op.type() == Push
)
{
@ -177,7 +188,11 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
{
static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator<AssemblyItems>)
static size_t applySimple(
AssemblyItem const& _s1,
AssemblyItem const& _s2,
back_insert_iterator<AssemblyItems>
)
{
return _s1 == _s2 && SemanticInformation::isSwapInstruction(_s1);
}
@ -185,7 +200,11 @@ struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
{
static bool applySimple(AssemblyItem const& _push1, AssemblyItem const& _push2, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _push1,
AssemblyItem const& _push2,
back_insert_iterator<AssemblyItems> _out
)
{
if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
{
@ -200,7 +219,11 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
{
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _swap,
AssemblyItem const& _op,
back_insert_iterator<AssemblyItems> _out
)
{
// Remove SWAP1 if following instruction is commutative
if (
@ -218,7 +241,11 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison>
{
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _swap,
AssemblyItem const& _op,
back_insert_iterator<AssemblyItems> _out
)
{
static map<Instruction, Instruction> const swappableOps{
{ Instruction::LT, Instruction::GT },
@ -247,7 +274,7 @@ struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
static size_t applySimple(
AssemblyItem const& _dupN,
AssemblyItem const& _swapN,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -272,7 +299,7 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI>
AssemblyItem const& _iszero2,
AssemblyItem const& _pushTag,
AssemblyItem const& _jumpi,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -298,7 +325,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
AssemblyItem const& _iszero,
AssemblyItem const& _pushTag,
AssemblyItem const& _jumpi,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -327,7 +354,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod<DoubleJump>
AssemblyItem const& _pushTag2,
AssemblyItem const& _jump,
AssemblyItem const& _tag1,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -356,7 +383,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
AssemblyItem const& _pushTag,
AssemblyItem const& _jump,
AssemblyItem const& _tag,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@ -382,7 +409,7 @@ struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions>
AssemblyItem const& _pushTag,
AssemblyItem const& _pushConstant,
AssemblyItem const& _and,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_and != Instruction::AND)
@ -417,7 +444,7 @@ struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd>
AssemblyItem const& _push,
AssemblyItem const& _not,
AssemblyItem const& _and,
std::back_insert_iterator<AssemblyItems>
back_insert_iterator<AssemblyItems>
)
{
return (
@ -484,7 +511,7 @@ bool PeepholeOptimiser::optimise()
{
// Avoid referencing immutables too early by using approx. counting in bytesRequired()
auto const approx = evmasm::Precision::Approximate;
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
OptimiserState state {m_items, 0, back_inserter(m_optimisedItems)};
while (state.i < m_items.size())
applyMethods(
state,

View File

@ -110,7 +110,7 @@ vector<SemanticInformation::Operation> SemanticInformation::readWriteOperations(
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
{
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction).args);
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction, langutil::EVMVersion()).args);
vector<Operation> operations{
Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}},
Operation{Location::Storage, Effect::Read, {}, {}, {}}
@ -178,7 +178,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
InstructionInfo info = instructionInfo(_item.instruction());
InstructionInfo info = instructionInfo(_item.instruction(), langutil::EVMVersion());
if (_item.instruction() == Instruction::SSTORE)
return false;
if (_item.instruction() == Instruction::MSTORE)
@ -318,7 +318,7 @@ bool SemanticInformation::movable(Instruction _instruction)
// These are not really functional.
if (isDupInstruction(_instruction) || isSwapInstruction(_instruction))
return false;
InstructionInfo info = instructionInfo(_instruction);
InstructionInfo info = instructionInfo(_instruction, langutil::EVMVersion());
if (info.sideEffects)
return false;
switch (_instruction)
@ -345,7 +345,7 @@ bool SemanticInformation::canBeRemoved(Instruction _instruction)
// These are not really functional.
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
return !instructionInfo(_instruction).sideEffects;
return !instructionInfo(_instruction, langutil::EVMVersion()).sideEffects;
}
bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction)
@ -483,7 +483,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction)
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::DIFFICULTY:
case Instruction::PREVRANDAO:
case Instruction::GASLIMIT:
case Instruction::STATICCALL:
case Instruction::SLOAD:

View File

@ -122,7 +122,7 @@ struct EVMBuiltins
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{};
static auto constexpr PREVRANDAO = PatternGenerator<Instruction::PREVRANDAO>{};
static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{};
static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{};
static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{};

View File

@ -141,7 +141,8 @@ string Pattern::toString() const
switch (m_type)
{
case Operation:
s << instructionInfo(m_instruction).name;
// Note: This function is exclusively used for debugging.
s << instructionInfo(m_instruction, EVMVersion()).name;
break;
case Push:
if (m_data)

View File

@ -96,6 +96,7 @@ public:
bool hasChainID() const { return *this >= istanbul(); }
bool hasSelfBalance() const { return *this >= istanbul(); }
bool hasBaseFee() const { return *this >= london(); }
bool hasPrevRandao() const { return *this >= paris(); }
bool hasOpcode(evmasm::Instruction _opcode) const;

View File

@ -3296,18 +3296,33 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
(memberName == "min" || memberName == "max")
)
annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::Block && memberName == "chainid" && !m_evmVersion.hasChainID())
m_errorReporter.typeError(
3081_error,
_memberAccess.location(),
"\"chainid\" is not supported by the VM version."
);
else if (magicType->kind() == MagicType::Kind::Block && memberName == "basefee" && !m_evmVersion.hasBaseFee())
m_errorReporter.typeError(
5921_error,
_memberAccess.location(),
"\"basefee\" is not supported by the VM version."
);
else if (magicType->kind() == MagicType::Kind::Block)
{
if (memberName == "chainid" && !m_evmVersion.hasChainID())
m_errorReporter.typeError(
3081_error,
_memberAccess.location(),
"\"chainid\" is not supported by the VM version."
);
else if (memberName == "basefee" && !m_evmVersion.hasBaseFee())
m_errorReporter.typeError(
5921_error,
_memberAccess.location(),
"\"basefee\" is not supported by the VM version."
);
else if (memberName == "prevrandao" && !m_evmVersion.hasPrevRandao())
m_errorReporter.warning(
9432_error,
_memberAccess.location(),
"\"prevrandao\" is not supported by the VM version and will be treated as \"difficulty\"."
);
else if (memberName == "difficulty" && m_evmVersion.hasPrevRandao())
m_errorReporter.warning(
8417_error,
_memberAccess.location(),
"Since the VM version paris, \"difficulty\" was replaced by \"prevrandao\", which now returns a random number based on the beacon chain."
);
}
}
if (

View File

@ -4067,6 +4067,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
{"timestamp", TypeProvider::uint256()},
{"blockhash", TypeProvider::function(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)},
{"difficulty", TypeProvider::uint256()},
{"prevrandao", TypeProvider::uint256()},
{"number", TypeProvider::uint256()},
{"gaslimit", TypeProvider::uint256()},
{"chainid", TypeProvider::uint256()},

View File

@ -65,7 +65,7 @@ public:
RevertStrings _revertStrings,
CompilerContext* _runtimeContext = nullptr
):
m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
m_asm(std::make_shared<evmasm::Assembly>(_evmVersion, _runtimeContext != nullptr, std::string{})),
m_evmVersion(_evmVersion),
m_revertStrings(_revertStrings),
m_reservedMemory{0},

View File

@ -1808,8 +1808,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
m_context << Instruction::COINBASE;
else if (member == "timestamp")
m_context << Instruction::TIMESTAMP;
else if (member == "difficulty")
m_context << Instruction::DIFFICULTY;
else if (member == "difficulty" || member == "prevrandao")
m_context << Instruction::PREVRANDAO;
else if (member == "number")
m_context << Instruction::NUMBER;
else if (member == "gaslimit")

View File

@ -1822,8 +1822,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
define(_memberAccess) << "coinbase()\n";
else if (member == "timestamp")
define(_memberAccess) << "timestamp()\n";
else if (member == "difficulty")
define(_memberAccess) << "difficulty()\n";
else if (member == "difficulty" || member == "prevrandao")
{
if (m_context.evmVersion().hasPrevRandao())
define(_memberAccess) << "prevrandao()\n";
else
define(_memberAccess) << "difficulty()\n";
}
else if (member == "number")
define(_memberAccess) << "number()\n";
else if (member == "gaslimit")

View File

@ -381,6 +381,7 @@ Json::Value formatImmutableReferences(map<u256, pair<string, vector<size_t>>> co
}
Json::Value collectEVMObject(
langutil::EVMVersion _evmVersion,
evmasm::LinkerObject const& _object,
string const* _sourceMap,
Json::Value _generatedSources,
@ -392,7 +393,7 @@ Json::Value collectEVMObject(
if (_artifactRequested("object"))
output["object"] = _object.toHex();
if (_artifactRequested("opcodes"))
output["opcodes"] = evmasm::disassemble(_object.bytecode);
output["opcodes"] = evmasm::disassemble(_object.bytecode, _evmVersion);
if (_artifactRequested("sourceMap"))
output["sourceMap"] = _sourceMap ? *_sourceMap : "";
if (_artifactRequested("functionDebugData"))
@ -1328,6 +1329,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
wildcardMatchesExperimental
))
evmData["bytecode"] = collectEVMObject(
_inputsAndSettings.evmVersion,
compilerStack.object(contractName),
compilerStack.sourceMapping(contractName),
compilerStack.generatedSources(contractName),
@ -1349,6 +1351,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
wildcardMatchesExperimental
))
evmData["deployedBytecode"] = collectEVMObject(
_inputsAndSettings.evmVersion,
compilerStack.runtimeObject(contractName),
compilerStack.runtimeSourceMapping(contractName),
compilerStack.generatedSources(contractName, true),
@ -1491,6 +1494,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
if (o.bytecode)
output["contracts"][sourceName][contractName]["evm"][kind] =
collectEVMObject(
_inputsAndSettings.evmVersion,
*o.bytecode,
o.sourceMappings.get(),
Json::arrayValue,

View File

@ -119,7 +119,6 @@ vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
);
return {_literal.type};
}
@ -619,7 +618,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation const& _location)
{
// NOTE: the leading dot case is handled by the parser not allowing it.
if (boost::ends_with(_identifier.str(), "."))
m_errorReporter.syntaxError(
3384_error,
@ -692,7 +690,7 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
_location,
fmt::format(
"The \"{instruction}\" instruction is {kind} VMs (you are currently compiling for \"{version}\").",
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr).name)),
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)),
fmt::arg("kind", vmKindMessage),
fmt::arg("version", m_evmVersion.name())
)

View File

@ -50,7 +50,7 @@ CompilabilityChecker::CompilabilityChecker(
builtinContext.subIDs[_object.name] = 1;
for (auto const& subNode: _object.subObjects)
builtinContext.subIDs[subNode->name] = 1;
NoOutputAssembly assembly;
NoOutputAssembly assembly{evmDialect->evmVersion()};
CodeTransform transform(
assembly,
analysisInfo,

View File

@ -264,7 +264,7 @@ YulStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
yulAssert(m_parserResult->code, "");
yulAssert(m_parserResult->analysisInfo, "");
evmasm::Assembly assembly(true, {});
evmasm::Assembly assembly(m_evmVersion, true, {});
EthAssemblyAdapter adapter(assembly);
compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation);

View File

@ -28,6 +28,7 @@
#include <libsolutil/Common.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Numeric.h>
#include <liblangutil/EVMVersion.h>
#include <functional>
#include <memory>

View File

@ -47,11 +47,12 @@ namespace
{
pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
langutil::EVMVersion _evmVersion,
string const& _name,
evmasm::Instruction _instruction
)
{
evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction);
evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction, _evmVersion);
BuiltinFunctionForEVM f;
f.name = YulString{_name};
f.parameters.resize(static_cast<size_t>(info.args));
@ -119,11 +120,19 @@ set<YulString> createReservedIdentifiers(langutil::EVMVersion _evmVersion)
return _instr == evmasm::Instruction::BASEFEE && _evmVersion < langutil::EVMVersion::london();
};
// TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name
// prevrandao for VMs before paris.
auto prevRandaoException = [&](string const& _instrName) -> bool
{
// Using string comparison as the opcode is the same as for "difficulty"
return _instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris();
};
set<YulString> reserved;
for (auto const& instr: evmasm::c_instructions)
{
string name = toLower(instr.first);
if (!baseFeeException(instr.second))
if (!baseFeeException(instr.second) && !prevRandaoException(name))
reserved.emplace(name);
}
reserved += vector<YulString>{
@ -139,6 +148,13 @@ set<YulString> createReservedIdentifiers(langutil::EVMVersion _evmVersion)
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess)
{
// Exclude prevrandao as builtin for VMs before paris and difficulty for VMs after paris.
auto prevRandaoException = [&](string const& _instrName) -> bool
{
return (_instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris()) || (_instrName == "difficulty" && _evmVersion >= langutil::EVMVersion::paris());
};
map<YulString, BuiltinFunctionForEVM> builtins;
for (auto const& instr: evmasm::c_instructions)
{
@ -152,9 +168,10 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
opcode != evmasm::Instruction::JUMP &&
opcode != evmasm::Instruction::JUMPI &&
opcode != evmasm::Instruction::JUMPDEST &&
_evmVersion.hasOpcode(opcode)
_evmVersion.hasOpcode(opcode) &&
!prevRandaoException(name)
)
builtins.emplace(createEVMFunction(name, opcode));
builtins.emplace(createEVMFunction(_evmVersion, name, opcode));
}
if (_objectAccess)

View File

@ -88,7 +88,7 @@ void GasMeterVisitor::operator()(FunctionCall const& _funCall)
void GasMeterVisitor::operator()(Literal const& _lit)
{
m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::PUSH1);
m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::PUSH1, m_dialect.evmVersion());
m_dataGas +=
singleByteDataGas() +
evmasm::GasMeter::dataGas(
@ -100,7 +100,7 @@ void GasMeterVisitor::operator()(Literal const& _lit)
void GasMeterVisitor::operator()(Identifier const&)
{
m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1);
m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1, m_dialect.evmVersion());
m_dataGas += singleByteDataGas();
}
@ -120,6 +120,6 @@ void GasMeterVisitor::instructionCostsInternal(evmasm::Instruction _instruction)
// Assumes that Keccak-256 is computed on a single word (rounded up).
m_runGas += evmasm::GasCosts::keccak256Gas + evmasm::GasCosts::keccak256WordGas;
else
m_runGas += evmasm::GasMeter::runGas(_instruction);
m_runGas += evmasm::GasMeter::runGas(_instruction, m_dialect.evmVersion());
m_dataGas += singleByteDataGas();
}

View File

@ -124,7 +124,7 @@ void EthAssemblyAdapter::appendAssemblySize()
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name)
{
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(_creation, std::move(_name))};
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(m_assembly.evmVersion(), _creation, std::move(_name))};
auto sub = m_assembly.newSub(assembly);
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
}

View File

@ -37,7 +37,7 @@ using namespace solidity::langutil;
void NoOutputAssembly::appendInstruction(evmasm::Instruction _instr)
{
m_stackHeight += instructionInfo(_instr).ret - instructionInfo(_instr).args;
m_stackHeight += instructionInfo(_instr, m_evmVersion).ret - instructionInfo(_instr, m_evmVersion).args;
}
void NoOutputAssembly::appendConstant(u256 const&)

View File

@ -45,7 +45,7 @@ namespace solidity::yul
class NoOutputAssembly: public AbstractAssembly
{
public:
explicit NoOutputAssembly() { }
explicit NoOutputAssembly(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) { }
~NoOutputAssembly() override = default;
void setSourceLocation(langutil::SourceLocation const&) override {}
@ -79,6 +79,7 @@ public:
private:
int m_stackHeight = 0;
langutil::EVMVersion m_evmVersion;
};

View File

@ -747,23 +747,23 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi
if (_swapDepth > 16)
opGas += 1000;
else
opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth));
opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth), langutil::EVMVersion());
};
auto dupOrPush = [&](StackSlot const& _slot)
{
if (canBeFreelyGenerated(_slot))
opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32));
opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32), langutil::EVMVersion());
else
{
auto depth = util::findOffset(_source | ranges::views::reverse, _slot);
yulAssert(depth);
if (*depth < 16)
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)));
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)), langutil::EVMVersion());
else
opGas += 1000;
}
};
auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP); };
auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP,langutil::EVMVersion()); };
createStackLayout(_source, _target, swap, dupOrPush, pop);
return opGas;
};

View File

@ -47,7 +47,7 @@ void ExpressionSimplifier::visit(Expression& _expression)
m_dialect,
[this](YulString _var) { return variableValue(_var); }
))
_expression = match->action().toExpression(debugDataOf(_expression));
_expression = match->action().toExpression(debugDataOf(_expression), evmVersionFromDialect(m_dialect));
if (auto* functionCall = get_if<FunctionCall>(&_expression))
if (optional<evmasm::Instruction> instruction = toEVMInstruction(m_dialect, functionCall->functionName.name))

View File

@ -102,7 +102,7 @@ Expression KnowledgeBase::simplifyRecursively(Expression _expression)
arg = simplifyRecursively(arg);
if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues))
return simplifyRecursively(match->action().toExpression(debugDataOf(_expression)));
return simplifyRecursively(match->action().toExpression(debugDataOf(_expression), langutil::EVMVersion()));
return _expression;
}

View File

@ -20,6 +20,7 @@
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/OptimizerUtilities.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AST.h>
#include <libyul/Exceptions.h>
@ -180,7 +181,7 @@ void CodeCost::visit(Expression const& _expression)
void CodeCost::addInstructionCost(evmasm::Instruction _instruction)
{
evmasm::Tier gasPriceTier = evmasm::instructionInfo(_instruction).gasPriceTier;
evmasm::Tier gasPriceTier = evmasm::instructionInfo(_instruction, evmVersionFromDialect(m_dialect)).gasPriceTier;
if (gasPriceTier < evmasm::Tier::VeryLow)
m_cost -= 1;
else if (gasPriceTier < evmasm::Tier::High)

View File

@ -58,6 +58,13 @@ optional<evmasm::Instruction> yul::toEVMInstruction(Dialect const& _dialect, Yul
return nullopt;
}
langutil::EVMVersion const yul::evmVersionFromDialect(Dialect const& _dialect)
{
if (auto const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
return dialect->evmVersion();
return langutil::EVMVersion();
}
void StatementRemover::operator()(Block& _block)
{
util::iterateReplacing(

View File

@ -26,6 +26,7 @@
#include <libyul/Dialect.h>
#include <libyul/YulString.h>
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <optional>
@ -49,6 +50,10 @@ bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifie
/// Helper function that returns the instruction, if the `_name` is a BuiltinFunction
std::optional<evmasm::Instruction> toEVMInstruction(Dialect const& _dialect, YulString const& _name);
/// Helper function that returns the EVM version from a dialect.
/// It returns the default EVM version if dialect is not an EVMDialect.
langutil::EVMVersion const evmVersionFromDialect(Dialect const& _dialect);
class StatementRemover: public ASTModifier
{
public:

View File

@ -235,7 +235,7 @@ evmasm::Instruction Pattern::instruction() const
return m_instruction;
}
Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData) const
Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData, langutil::EVMVersion _evmVersion) const
{
if (matchGroup())
return ASTCopier().translate(matchGroupValue());
@ -248,9 +248,9 @@ Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData)
{
vector<Expression> arguments;
for (auto const& arg: m_arguments)
arguments.emplace_back(arg.toExpression(_debugData));
arguments.emplace_back(arg.toExpression(_debugData, _evmVersion));
string name = util::toLower(instructionInfo(m_instruction).name);
string name = util::toLower(instructionInfo(m_instruction, _evmVersion).name);
return FunctionCall{_debugData,
Identifier{_debugData, YulString{name}},

View File

@ -131,7 +131,7 @@ public:
/// Turns this pattern into an actual expression. Should only be called
/// for patterns resulting from an action, i.e. with match groups assigned.
Expression toExpression(std::shared_ptr<DebugData const> const& _debugData) const;
Expression toExpression(std::shared_ptr<DebugData const> const& _debugData, langutil::EVMVersion _evmVersion) const;
private:
Expression const& matchGroupValue() const;

View File

@ -5,9 +5,10 @@ set -eu
ERROR_LOG="$(mktemp -t check_style_XXXXXX.log)"
EXCLUDE_FILES=(
# The line below is left unquoted to allow the shell globbing path expansion
test/cmdlineTests/*/{err,output}
"libsolutil/picosha2.h"
"test/cmdlineTests/strict_asm_only_cr/input.yul"
"test/cmdlineTests/strict_asm_only_cr/err"
"test/libsolutil/UTF8.cpp"
"test/libsolidity/syntaxTests/license/license_cr_endings.sol"
"test/libsolidity/syntaxTests/license/license_crlf_endings.sol"

View File

@ -121,6 +121,9 @@ done < <(
grep -v -E 'revertStatement/non_called.sol' |
# Skipping a test with "let basefee := ..."
grep -v -E 'inlineAssembly/basefee_berlin_function.sol' |
# Skipping tests with "let prevrandao := ..."
grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' |
grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' |
# Skipping license error, unrelated to the grammar
grep -v -E 'license/license_double5.sol' |
grep -v -E 'license/license_hidden_unicode.sol' |

View File

@ -198,11 +198,11 @@ void CommandLineInterface::handleOpcode(string const& _contract)
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode));
createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion));
else
{
sout() << "Opcodes:" << endl;
sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode);
sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion);
sout() << endl;
}
}
@ -825,7 +825,7 @@ void CommandLineInterface::handleCombinedJSON()
if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful())
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful())
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode);
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion);
if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful())
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName);
if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful())

View File

@ -0,0 +1 @@
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris

View File

@ -0,0 +1,5 @@
Warning: Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain.
--> evmasm_difficulty_post_paris/input.sol:6:21:
|
6 | uint256 x = block.difficulty;
| ^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0.0;
contract C {
constructor() payable {
uint256 x = block.difficulty;
assembly {
sstore(0, x)
stop()
}
}
fallback() external payable {
assembly {
stop()
}
}
}

View File

@ -0,0 +1,13 @@
======= evmasm_difficulty_post_paris/input.sol:C =======
EVM assembly:
sstore(0x00, prevrandao)
stop
stop
sub_0: assembly {
stop
}
Opcodes:
PREVRANDAO PUSH1 0x0 SSTORE STOP INVALID

View File

@ -0,0 +1 @@
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0.0;
contract C {
constructor() payable {
uint256 x = block.difficulty;
assembly {
sstore(0, x)
stop()
}
}
fallback() external payable {
assembly {
stop()
}
}
}

View File

@ -0,0 +1,13 @@
======= evmasm_difficulty_pre_paris/input.sol:C =======
EVM assembly:
sstore(0x00, difficulty)
stop
stop
sub_0: assembly {
stop
}
Opcodes:
DIFFICULTY PUSH1 0x0 SSTORE STOP INVALID

View File

@ -0,0 +1 @@
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0.0;
contract C {
constructor() payable {
uint256 x = block.prevrandao;
assembly {
sstore(0, x)
stop()
}
}
fallback() external payable {
assembly {
stop()
}
}
}

View File

@ -0,0 +1,13 @@
======= evmasm_prevrandao_post_paris/input.sol:C =======
EVM assembly:
sstore(0x00, prevrandao)
stop
stop
sub_0: assembly {
stop
}
Opcodes:
PREVRANDAO PUSH1 0x0 SSTORE STOP INVALID

View File

@ -0,0 +1 @@
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london

View File

@ -0,0 +1,5 @@
Warning: "prevrandao" is not supported by the VM version and will be treated as "difficulty".
--> evmasm_prevrandao_pre_paris/input.sol:6:21:
|
6 | uint256 x = block.prevrandao;
| ^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0.0;
contract C {
constructor() payable {
uint256 x = block.prevrandao;
assembly {
sstore(0, x)
stop()
}
}
fallback() external payable {
assembly {
stop()
}
}
}

View File

@ -0,0 +1,13 @@
======= evmasm_prevrandao_pre_paris/input.sol:C =======
EVM assembly:
sstore(0x00, difficulty)
stop
stop
sub_0: assembly {
stop
}
Opcodes:
DIFFICULTY PUSH1 0x0 SSTORE STOP INVALID

View File

@ -20,6 +20,7 @@
* @date 2018
* Tests for the assembler.
*/
#include <test/Common.h>
#include <libevmasm/Assembly.h>
#include <libsolutil/JSON.h>
@ -59,15 +60,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{ "sub.asm", 1 },
{ "verbatim.asm", 2 }
};
Assembly _assembly{false, {}};
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
Assembly _assembly{evmVersion, false, {}};
auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm{false, {}};
Assembly _subAsm{evmVersion, false, {}};
auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm});
Assembly _verbatimAsm(true, "");
Assembly _verbatimAsm(evmVersion, true, "");
auto verbatim_asm = make_shared<string>("verbatim.asm");
_verbatimAsm.setSourceLocation({8, 18, verbatim_asm});
@ -215,6 +217,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
{
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
// Tests for 1, 2, 3 number of immutables.
for (int numImmutables = 1; numImmutables <= 3; ++numImmutables)
{
@ -243,7 +246,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
{ *subName, 1 }
};
auto subAsm = make_shared<Assembly>(false, string{});
auto subAsm = make_shared<Assembly>(evmVersion, false, string{});
for (char i = 0; i < numImmutables; ++i)
{
for (int r = 0; r < numActualRefs; ++r)
@ -253,7 +256,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
}
}
Assembly assembly{true, {}};
Assembly assembly{evmVersion, true, {}};
for (char i = 1; i <= numImmutables; ++i)
{
assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName});
@ -270,7 +273,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';');
LinkerObject const& obj = assembly.assemble();
string const disassembly = disassemble(obj.bytecode, "\n");
string const disassembly = disassemble(obj.bytecode, evmVersion, "\n");
auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n');
#if 0 // {{{ debug prints
@ -302,11 +305,12 @@ BOOST_AUTO_TEST_CASE(immutable)
{ "root.asm", 0 },
{ "sub.asm", 1 }
};
Assembly _assembly{true, {}};
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
Assembly _assembly{evmVersion, true, {}};
auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm{false, {}};
Assembly _subAsm{evmVersion, false, {}};
auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm});
_subAsm.appendImmutable("someImmutable");
@ -395,10 +399,11 @@ BOOST_AUTO_TEST_CASE(immutable)
BOOST_AUTO_TEST_CASE(subobject_encode_decode)
{
Assembly assembly{true, {}};
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
Assembly assembly{evmVersion, true, {}};
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>(false, string{});
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(false, string{});
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>(evmVersion, false, string{});
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(evmVersion, false, string{});
assembly.appendSubroutine(subAsmPtr);
subAsmPtr->appendSubroutine(subSubAsmPtr);

View File

@ -1250,8 +1250,18 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
// tag unifications (due to block deduplication) is also
// visible at the super-assembly.
Assembly main{false, {}};
AssemblyPointer sub = make_shared<Assembly>(true, string{});
Assembly::OptimiserSettings settings;
settings.runInliner = false;
settings.runJumpdestRemover = true;
settings.runPeephole = true;
settings.runDeduplicate = true;
settings.runCSE = true;
settings.runConstantOptimiser = true;
settings.evmVersion = solidity::test::CommonOptions::get().evmVersion();
settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment;
Assembly main{settings.evmVersion, false, {}};
AssemblyPointer sub = make_shared<Assembly>(settings.evmVersion, true, string{});
sub->append(u256(1));
auto t1 = sub->newTag();
@ -1277,16 +1287,6 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
main.append(t1.toSubAssemblyTag(subId));
main.append(u256(8));
Assembly::OptimiserSettings settings;
settings.runInliner = false;
settings.runJumpdestRemover = true;
settings.runPeephole = true;
settings.runDeduplicate = true;
settings.runCSE = true;
settings.runConstantOptimiser = true;
settings.evmVersion = solidity::test::CommonOptions::get().evmVersion();
settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment;
main.optimise(settings);
AssemblyItems expectationMain{

View File

@ -109,7 +109,7 @@ public:
bytes realCode = bytecodeSansMetadata(_bytecode);
BOOST_REQUIRE_MESSAGE(!realCode.empty(), "Invalid or missing metadata in bytecode.");
size_t instructions = 0;
evmasm::eachInstruction(realCode, [&](Instruction _instr, u256 const&) {
evmasm::eachInstruction(realCode, m_evmVersion, [&](Instruction _instr, u256 const&) {
if (!_which || *_which == _instr)
instructions++;
});
@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches)
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true);
size_t numSHA3s = 0;
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
eachInstruction(optimizedBytecode, m_evmVersion, [&](Instruction _instr, u256 const&) {
if (_instr == Instruction::KECCAK256)
numSHA3s++;
});
@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions)
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "test", true);
size_t numSHA3s = 0;
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
eachInstruction(optimizedBytecode, m_evmVersion, [&](Instruction _instr, u256 const&) {
if (_instr == Instruction::KECCAK256)
numSHA3s++;
});

View File

@ -1860,6 +1860,7 @@ BOOST_AUTO_TEST_CASE(builtins)
block.chainid;
block.coinbase;
block.difficulty;
block.prevrandao;
block.gaslimit;
block.number;
block.timestamp;

View File

@ -0,0 +1,12 @@
contract C {
function f() public view returns (uint ret) {
assembly {
ret := difficulty()
}
}
}
// ====
// compileToEwasm: also
// EVMVersion: <paris
// ----
// f() -> 200000000

View File

@ -0,0 +1,12 @@
contract C {
function f() public view returns (uint ret) {
assembly {
ret := prevrandao()
}
}
}
// ====
// compileToEwasm: also
// EVMVersion: >=paris
// ----
// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777

View File

@ -0,0 +1,10 @@
contract C {
function f() public view returns (uint) {
return block.prevrandao;
}
}
// ====
// compileToEwasm: also
// EVMVersion: >=paris
// ----
// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777

View File

@ -0,0 +1,10 @@
contract C {
function f() public view returns (uint) {
return block.prevrandao;
}
}
// ====
// compileToEwasm: also
// EVMVersion: <paris
// ----
// f() -> 200000000

View File

@ -0,0 +1,14 @@
contract C {
function f() public view returns (uint256) {
return block.difficulty;
}
function g() public view returns (uint256 ret) {
assembly {
ret := difficulty()
}
}
}
// ====
// EVMVersion: <paris
// ----

View File

@ -0,0 +1,21 @@
contract C {
function f() public view returns (uint256 ret) {
assembly {
let difficulty := sload(0)
ret := difficulty
}
}
function g() public pure returns (uint256 ret) {
assembly {
function difficulty() -> r {
r := 1000
}
ret := difficulty()
}
}
}
// ====
// EVMVersion: <paris
// ----
// ParserError 5568: (101-111): Cannot use builtin function name "difficulty" as identifier name.

View File

@ -0,0 +1,9 @@
contract C {
function f() public view returns (uint256) {
return block.difficulty;
}
}
// ====
// EVMVersion: >=paris
// ----
// Warning 8417: (77-93): Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain.

View File

@ -0,0 +1,12 @@
contract C {
function f() public {
assembly {
pop(difficulty())
}
}
}
// ====
// EVMVersion: >=paris
// ----
// DeclarationError 4619: (74-84): Function "difficulty" not found.
// TypeError 3950: (74-86): Expected expression to evaluate to one value, but got 0 values instead.

View File

@ -0,0 +1,22 @@
contract C {
function f() public view returns (uint256 ret) {
assembly {
let difficulty := sload(0)
ret := difficulty
}
}
function g() public pure returns (uint256 ret) {
assembly {
function difficulty() -> r {
r := 1000
}
ret := difficulty()
}
}
}
// ====
// EVMVersion: >=paris
// ----
// DeclarationError 5017: (101-111): The identifier "difficulty" is reserved and can not be used.
// DeclarationError 5017: (255-323): The identifier "difficulty" is reserved and can not be used.

View File

@ -0,0 +1,20 @@
contract C {
function f() public view returns (uint256 ret) {
assembly {
let prevrandao := sload(0)
ret := prevrandao
}
}
function g() public pure returns (uint256 ret) {
assembly {
function prevrandao() -> r {
r := 1000
}
ret := prevrandao()
}
}
}
// ====
// EVMVersion: <paris
// ----

View File

@ -0,0 +1,14 @@
contract C {
function f() public view returns (uint256) {
return block.prevrandao;
}
function g() public view returns (uint256 ret) {
assembly {
ret := prevrandao()
}
}
}
// ====
// EVMVersion: >=paris
// ----

View File

@ -0,0 +1,21 @@
contract C {
function f() public view returns (uint256 ret) {
assembly {
let prevrandao := sload(0)
ret := prevrandao
}
}
function g() public pure returns (uint256 ret) {
assembly {
function prevrandao() -> r {
r := 1000
}
ret := prevrandao()
}
}
}
// ====
// EVMVersion: >=paris
// ----
// ParserError 5568: (101-111): Cannot use builtin function name "prevrandao" as identifier name.

View File

@ -0,0 +1,9 @@
contract C {
function f() public view returns (uint256) {
return block.prevrandao;
}
}
// ====
// EVMVersion: <paris
// ----
// Warning 9432: (77-93): "prevrandao" is not supported by the VM version and will be treated as "difficulty".

View File

@ -0,0 +1,12 @@
contract C {
function f() public {
assembly {
pop(prevrandao())
}
}
}
// ====
// EVMVersion: <paris
// ----
// DeclarationError 4619: (74-84): Function "prevrandao" not found.
// TypeError 3950: (74-86): Expected expression to evaluate to one value, but got 0 values instead.

View File

@ -8,6 +8,6 @@ contract C {
}
}
// ====
// EVMVersion: <byzantium
// EVMVersion: =homestead
// ----
// TypeError 9908: (73-106): This catch clause type cannot be used on the selected EVM version (homestead). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -0,0 +1,13 @@
contract C {
function f() public {
try this.f() {
} catch (bytes memory) {
}
}
}
// ====
// EVMVersion: =spuriousDragon
// ----
// TypeError 9908: (73-106): This catch clause type cannot be used on the selected EVM version (spuriousDragon). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -0,0 +1,13 @@
contract C {
function f() public {
try this.f() {
} catch (bytes memory) {
}
}
}
// ====
// EVMVersion: =tangerineWhistle
// ----
// TypeError 9908: (73-106): This catch clause type cannot be used on the selected EVM version (tangerineWhistle). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -8,6 +8,6 @@ contract C {
}
}
// ====
// EVMVersion: <byzantium
// EVMVersion: =homestead
// ----
// TypeError 1812: (73-112): This catch clause type cannot be used on the selected EVM version (homestead). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -0,0 +1,13 @@
contract C {
function f() public {
try this.f() {
} catch Error(string memory) {
}
}
}
// ====
// EVMVersion: =spuriousDragon
// ----
// TypeError 1812: (73-112): This catch clause type cannot be used on the selected EVM version (spuriousDragon). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -0,0 +1,13 @@
contract C {
function f() public {
try this.f() {
} catch Error(string memory) {
}
}
}
// ====
// EVMVersion: =tangerineWhistle
// ----
// TypeError 1812: (73-112): This catch clause type cannot be used on the selected EVM version (tangerineWhistle). You need at least a Byzantium-compatible EVM or use `catch { ... }`.

View File

@ -3,15 +3,12 @@ contract C {
return block.coinbase;
}
function g() public view returns (uint) {
return block.difficulty;
}
function h() public view returns (uint) {
return block.gaslimit;
}
function i() public view returns (uint) {
function h() public view returns (uint) {
return block.timestamp;
}
function j() public view returns (uint) {
function i() public view returns (uint) {
return block.chainid;
}
}

View File

@ -0,0 +1,13 @@
contract C {
function f() public view returns (uint) {
return block.prevrandao;
}
function g() public view returns (uint ret) {
assembly {
ret := prevrandao()
}
}
}
// ====
// EVMVersion: >=paris
// ----

View File

@ -73,7 +73,7 @@ contract C {
pop(coinbase())
pop(timestamp())
pop(number())
pop(difficulty())
pop(prevrandao())
pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled
@ -83,7 +83,7 @@ contract C {
}
}
// ====
// EVMVersion: >=london
// EVMVersion: >=paris
// ----
// Warning 5740: (89-1716): Unreachable code.
// Warning 5740: (1729-1741): Unreachable code.

View File

@ -0,0 +1,11 @@
contract C {
function f() public {
assembly {
pop(difficulty())
}
}
}
// ====
// EVMVersion: <=london
// ----
// Warning 2018: (17-103): Function state mutability can be restricted to view

View File

@ -73,7 +73,6 @@ contract C {
//pop(coinbase())
//pop(timestamp())
//pop(number())
//pop(difficulty())
//pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled

View File

@ -73,7 +73,7 @@ contract C {
pop(coinbase())
pop(timestamp())
pop(number())
pop(difficulty())
pop(prevrandao())
pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled
@ -83,7 +83,7 @@ contract C {
}
}
// ====
// EVMVersion: >=london
// EVMVersion: >=paris
// ----
// Warning 5740: (94-1733): Unreachable code.
// Warning 5740: (1746-1758): Unreachable code.

View File

@ -0,0 +1,10 @@
contract C {
function f() public view {
assembly {
pop(difficulty())
}
}
}
// ====
// EVMVersion: <=london
// ----

View File

@ -6,33 +6,26 @@ contract C {
pop(gas())
pop(address())
pop(balance(0))
pop(selfbalance())
pop(caller())
pop(callvalue())
pop(extcodesize(0))
extcodecopy(0, 1, 2, 3)
pop(extcodehash(0))
pop(create(0, 1, 2))
pop(create2(0, 1, 2, 3))
pop(call(0, 1, 2, 3, 4, 5, 6))
pop(callcode(0, 1, 2, 3, 4, 5, 6))
pop(delegatecall(0, 1, 2, 3, 4, 5))
pop(staticcall(0, 1, 2, 3, 4, 5))
selfdestruct(0)
log0(0, 1)
log1(0, 1, 2)
log2(0, 1, 2, 3)
log3(0, 1, 2, 3, 4)
log4(0, 1, 2, 3, 4, 5)
pop(chainid())
pop(basefee())
pop(origin())
pop(gasprice())
pop(blockhash(0))
pop(coinbase())
pop(timestamp())
pop(number())
pop(difficulty())
pop(gaslimit())
// These two are disallowed too but the error suppresses other errors.
@ -41,40 +34,31 @@ contract C {
}
}
}
// ====
// EVMVersion: >=london
// ----
// Warning 5740: (672-1083): Unreachable code.
// Warning 5740: (526-853): Unreachable code.
// TypeError 2527: (79-87): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (101-113): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 2527: (130-135): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (153-162): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (180-190): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (208-221): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (239-247): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (265-276): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (294-308): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (322-345): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (362-376): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (394-409): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (427-446): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (464-489): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (507-536): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (554-584): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 2527: (602-630): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (644-659): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (672-682): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (695-708): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (721-737): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (750-769): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (782-804): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 2527: (821-830): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (848-857): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (875-883): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (901-911): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (929-941): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (959-969): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (987-998): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (1016-1024): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (1042-1054): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (1072-1082): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (208-216): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (234-245): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (263-277): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (291-314): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (331-346): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (364-389): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (407-436): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (454-484): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (498-513): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (526-536): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (549-562): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (575-591): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (604-623): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (636-658): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 2527: (675-683): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (701-711): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (729-741): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (759-769): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (787-798): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (816-824): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (842-852): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

View File

@ -0,0 +1,11 @@
contract C {
function f() public pure {
assembly {
pop(staticcall(0, 1, 2, 3, 4, 5))
}
}
}
// ====
// EVMVersion: >=byzantium
// ----
// TypeError 2527: (79-107): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

View File

@ -0,0 +1,13 @@
contract C {
function f() public pure {
assembly {
pop(extcodehash(0))
pop(create2(0, 1, 2, 3))
}
}
}
// ====
// EVMVersion: >=constantinople
// ----
// TypeError 2527: (79-93): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (111-130): Function cannot be declared as pure because this expression (potentially) modifies the state.

View File

@ -0,0 +1,13 @@
contract C {
function f() public pure {
assembly {
pop(selfbalance())
pop(chainid())
}
}
}
// ====
// EVMVersion: >=istanbul
// ----
// TypeError 2527: (79-92): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (110-119): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

View File

@ -0,0 +1,11 @@
contract C {
function f() public pure {
assembly {
pop(basefee())
}
}
}
// ====
// EVMVersion: >=london
// ----
// TypeError 2527: (79-88): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

Some files were not shown because too many files have changed in this diff Show More