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: Compiler Features:
* Commandline Interface: Return exit code ``2`` on uncaught exceptions. * 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. * 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. * 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. * 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. * 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 .. 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 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.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.chainid`` (``uint``): current chain id
- ``block.coinbase`` (``address payable``): current block miner's address - ``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.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number - ``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 - ``block.timestamp`` (``uint``): current block timestamp in seconds since Unix epoch
- ``gasleft() returns (uint256)``: remaining gas - ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata - ``msg.data`` (``bytes``): complete calldata

View File

@ -291,8 +291,8 @@ YulEVMBuiltin:
| 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode'
| 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid'
| 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice'
| 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit' | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao'
| 'basefee'; | 'gaslimit' | 'basefee';
YulLBrace: '{' -> pushMode(YulMode); YulLBrace: '{' -> pushMode(YulMode);
YulRBrace: '}' -> popMode; 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 namespace and are mainly used to provide information about the blockchain
or are general-use utility functions. 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 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.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.chainid`` (``uint``): current chain id
- ``block.coinbase`` (``address payable``): current block miner's address - ``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.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number - ``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 - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
- ``gasleft() returns (uint256)``: remaining gas - ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes calldata``): complete calldata - ``msg.data`` (``bytes calldata``): complete calldata

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. 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 ``-`` 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, Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L`` and ``P`` are present since Frontier,
Byzantium, Constantinople, Istanbul or London respectively. 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 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``. 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) | | 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 | | 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. The remaining bytes will retain their values as of before the call.
.. note:: .. note::
With the Paris network upgrade the semantics of ``difficulty`` have been changed. The `difficulty()` instruction is disallowed in EVM version >= Paris.
It returns the value of ``prevrandao``, which is a 256-bit value, whereas the highest recorded 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. difficulty value within Ethash was ~54 bits.
This change is described in `EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_. 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 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); sourceIndex = static_cast<int>(iter->second);
} }
auto [name, data] = item.nameAndData(); auto [name, data] = item.nameAndData(m_evmVersion);
Json::Value jsonItem; Json::Value jsonItem;
jsonItem["name"] = name; jsonItem["name"] = name;
jsonItem["begin"] = item.location().start; jsonItem["begin"] = item.location().start;

View File

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

View File

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

View File

@ -108,10 +108,11 @@ public:
/// This function is used in `Assembly::assemblyJSON`. /// This function is used in `Assembly::assemblyJSON`.
/// It returns the name & data of the current assembly item. /// 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 /// @returns a pair, where the first element is the json-assembly
/// item name, where second element is the string representation /// item name, where second element is the string representation
/// of it's data. /// 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); } 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)); m_stack.erase(m_stackHeight - static_cast<int>(i));
} }
appendItem(*expr.item); 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_stack[m_stackHeight] = _c;
m_classPositions[_c].insert(m_stackHeight); m_classPositions[_c].insert(m_stackHeight);
@ -414,7 +414,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
else else
{ {
assertThrow( assertThrow(
instructionInfo(expr.item->instruction()).ret == 0, instructionInfo(expr.item->instruction(), EVMVersion()).ret == 0,
OptimizerException, OptimizerException,
"Invalid number of return values." "Invalid number of return values."
); );

View File

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

View File

@ -76,7 +76,7 @@ protected:
protected: protected:
/// @returns the run gas for the given items ignoring special gas costs /// @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 /// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const; bigint dataGas(bytes const& _data) const;
static size_t bytesRequired(AssemblyItems const& _items); static size_t bytesRequired(AssemblyItems const& _items);

View File

@ -30,6 +30,7 @@ using namespace solidity::evmasm;
void solidity::evmasm::eachInstruction( void solidity::evmasm::eachInstruction(
bytes const& _mem, bytes const& _mem,
langutil::EVMVersion _evmVersion,
function<void(Instruction,u256 const&)> const& _onInstruction function<void(Instruction,u256 const&)> const& _onInstruction
) )
{ {
@ -38,7 +39,7 @@ void solidity::evmasm::eachInstruction(
Instruction const instr{*it}; Instruction const instr{*it};
int additional = 0; int additional = 0;
if (isValidInstruction(instr)) if (isValidInstruction(instr))
additional = instructionInfo(instr).additional; additional = instructionInfo(instr, _evmVersion).additional;
u256 data{}; 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; stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) { eachInstruction(_mem, _evmVersion, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr)) if (!isValidInstruction(_instr))
ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter; ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter;
else else
{ {
InstructionInfo info = instructionInfo(_instr); InstructionInfo info = instructionInfo(_instr, _evmVersion);
ret << info.name; ret << info.name;
if (info.additional) if (info.additional)
ret << " 0x" << std::uppercase << std::hex << _data; 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. /// 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. /// 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 PushProgramSize:
case PushLibraryAddress: case PushLibraryAddress:
case PushDeployTimeAddress: case PushDeployTimeAddress:
gas = runGas(Instruction::PUSH1); gas = runGas(Instruction::PUSH1, m_evmVersion);
break; break;
case Tag: case Tag:
gas = runGas(Instruction::JUMPDEST); gas = runGas(Instruction::JUMPDEST, m_evmVersion);
break; break;
case Operation: case Operation:
{ {
@ -80,19 +80,19 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break; break;
case Instruction::RETURN: case Instruction::RETURN:
case Instruction::REVERT: case Instruction::REVERT:
gas = runGas(_item.instruction()); gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -1); gas += memoryGas(0, -1);
break; break;
case Instruction::MLOAD: case Instruction::MLOAD:
case Instruction::MSTORE: case Instruction::MSTORE:
gas = runGas(_item.instruction()); gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, { gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0), m_state->relativeStackElement(0),
classes.find(AssemblyItem(32)) classes.find(AssemblyItem(32))
})); }));
break; break;
case Instruction::MSTORE8: case Instruction::MSTORE8:
gas = runGas(_item.instruction()); gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, { gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0), m_state->relativeStackElement(0),
classes.find(AssemblyItem(1)) classes.find(AssemblyItem(1))
@ -106,7 +106,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALLDATACOPY: case Instruction::CALLDATACOPY:
case Instruction::CODECOPY: case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY: case Instruction::RETURNDATACOPY:
gas = runGas(_item.instruction()); gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -2); gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break; break;
@ -195,13 +195,13 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasCosts::balanceGas(m_evmVersion); gas = GasCosts::balanceGas(m_evmVersion);
break; break;
case Instruction::CHAINID: case Instruction::CHAINID:
gas = runGas(Instruction::CHAINID); gas = runGas(Instruction::CHAINID, m_evmVersion);
break; break;
case Instruction::SELFBALANCE: case Instruction::SELFBALANCE:
gas = runGas(Instruction::SELFBALANCE); gas = runGas(Instruction::SELFBALANCE, m_evmVersion);
break; break;
default: default:
gas = runGas(_item.instruction()); gas = runGas(_item.instruction(), m_evmVersion);
break; break;
} }
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) if (_instruction == Instruction::JUMPDEST)
return 1; return 1;
switch (instructionInfo(_instruction).gasPriceTier) switch (instructionInfo(_instruction, _evmVersion).gasPriceTier)
{ {
case Tier::Zero: return GasCosts::tier0Gas; case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas; case Tier::Base: return GasCosts::tier1Gas;
@ -268,7 +268,7 @@ unsigned GasMeter::runGas(Instruction _instruction)
case Tier::Ext: return GasCosts::tier6Gas; case Tier::Ext: return GasCosts::tier6Gas;
default: break; 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; return 0;
} }

View File

@ -221,7 +221,7 @@ public:
/// @returns gas costs for simple instructions with constant gas costs (that do not /// @returns gas costs for simple instructions with constant gas costs (that do not
/// change with EVM versions) /// 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. /// @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 /// 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; 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 }, { "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP }, { "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER }, { "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY }, { "DIFFICULTY", Instruction::PREVRANDAO },
{ "PREVRANDAO", Instruction::PREVRANDAO },
{ "GASLIMIT", Instruction::GASLIMIT }, { "GASLIMIT", Instruction::GASLIMIT },
{ "CHAINID", Instruction::CHAINID }, { "CHAINID", Instruction::CHAINID },
{ "SELFBALANCE", Instruction::SELFBALANCE }, { "SELFBALANCE", Instruction::SELFBALANCE },
@ -174,6 +175,7 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT } { "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 = static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ // Add, Args, Ret, SideEffects, GasPriceTier { // Add, Args, Ret, SideEffects, GasPriceTier
{ Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } }, { 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::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 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::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::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::Base } },
{ Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } }, { Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } },
{ Instruction::SELFBALANCE, { "SELFBALANCE", 0, 0, 1, false, Tier::Low } }, { 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 } } { 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 try
{ {
if (_inst == Instruction::PREVRANDAO && _evmVersion < langutil::EVMVersion::paris())
return InstructionInfo({ "DIFFICULTY", 0, 0, 1, false, Tier::Base });
return c_instructionInfo.at(_inst); return c_instructionInfo.at(_inst);
} }
catch (...) catch (...)

View File

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

View File

@ -130,7 +130,9 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
else else
{ {
Instruction instruction = _item.instruction(); 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)) if (SemanticInformation::isDupInstruction(_item))
setStackElement( setStackElement(
m_stackHeight + 1, m_stackHeight + 1,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3296,18 +3296,33 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
(memberName == "min" || memberName == "max") (memberName == "min" || memberName == "max")
) )
annotation.isPure = true; annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::Block && memberName == "chainid" && !m_evmVersion.hasChainID()) else if (magicType->kind() == MagicType::Kind::Block)
{
if (memberName == "chainid" && !m_evmVersion.hasChainID())
m_errorReporter.typeError( m_errorReporter.typeError(
3081_error, 3081_error,
_memberAccess.location(), _memberAccess.location(),
"\"chainid\" is not supported by the VM version." "\"chainid\" is not supported by the VM version."
); );
else if (magicType->kind() == MagicType::Kind::Block && memberName == "basefee" && !m_evmVersion.hasBaseFee()) else if (memberName == "basefee" && !m_evmVersion.hasBaseFee())
m_errorReporter.typeError( m_errorReporter.typeError(
5921_error, 5921_error,
_memberAccess.location(), _memberAccess.location(),
"\"basefee\" is not supported by the VM version." "\"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 ( if (

View File

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

View File

@ -65,7 +65,7 @@ public:
RevertStrings _revertStrings, RevertStrings _revertStrings,
CompilerContext* _runtimeContext = nullptr 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_evmVersion(_evmVersion),
m_revertStrings(_revertStrings), m_revertStrings(_revertStrings),
m_reservedMemory{0}, m_reservedMemory{0},

View File

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

View File

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

View File

@ -381,6 +381,7 @@ Json::Value formatImmutableReferences(map<u256, pair<string, vector<size_t>>> co
} }
Json::Value collectEVMObject( Json::Value collectEVMObject(
langutil::EVMVersion _evmVersion,
evmasm::LinkerObject const& _object, evmasm::LinkerObject const& _object,
string const* _sourceMap, string const* _sourceMap,
Json::Value _generatedSources, Json::Value _generatedSources,
@ -392,7 +393,7 @@ Json::Value collectEVMObject(
if (_artifactRequested("object")) if (_artifactRequested("object"))
output["object"] = _object.toHex(); output["object"] = _object.toHex();
if (_artifactRequested("opcodes")) if (_artifactRequested("opcodes"))
output["opcodes"] = evmasm::disassemble(_object.bytecode); output["opcodes"] = evmasm::disassemble(_object.bytecode, _evmVersion);
if (_artifactRequested("sourceMap")) if (_artifactRequested("sourceMap"))
output["sourceMap"] = _sourceMap ? *_sourceMap : ""; output["sourceMap"] = _sourceMap ? *_sourceMap : "";
if (_artifactRequested("functionDebugData")) if (_artifactRequested("functionDebugData"))
@ -1328,6 +1329,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
wildcardMatchesExperimental wildcardMatchesExperimental
)) ))
evmData["bytecode"] = collectEVMObject( evmData["bytecode"] = collectEVMObject(
_inputsAndSettings.evmVersion,
compilerStack.object(contractName), compilerStack.object(contractName),
compilerStack.sourceMapping(contractName), compilerStack.sourceMapping(contractName),
compilerStack.generatedSources(contractName), compilerStack.generatedSources(contractName),
@ -1349,6 +1351,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
wildcardMatchesExperimental wildcardMatchesExperimental
)) ))
evmData["deployedBytecode"] = collectEVMObject( evmData["deployedBytecode"] = collectEVMObject(
_inputsAndSettings.evmVersion,
compilerStack.runtimeObject(contractName), compilerStack.runtimeObject(contractName),
compilerStack.runtimeSourceMapping(contractName), compilerStack.runtimeSourceMapping(contractName),
compilerStack.generatedSources(contractName, true), compilerStack.generatedSources(contractName, true),
@ -1491,6 +1494,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
if (o.bytecode) if (o.bytecode)
output["contracts"][sourceName][contractName]["evm"][kind] = output["contracts"][sourceName][contractName]["evm"][kind] =
collectEVMObject( collectEVMObject(
_inputsAndSettings.evmVersion,
*o.bytecode, *o.bytecode,
o.sourceMappings.get(), o.sourceMappings.get(),
Json::arrayValue, 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() + "\"." "Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
); );
return {_literal.type}; return {_literal.type};
} }
@ -619,7 +618,6 @@ Scope& AsmAnalyzer::scope(Block const* _block)
void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation const& _location) void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation const& _location)
{ {
// NOTE: the leading dot case is handled by the parser not allowing it. // NOTE: the leading dot case is handled by the parser not allowing it.
if (boost::ends_with(_identifier.str(), ".")) if (boost::ends_with(_identifier.str(), "."))
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
3384_error, 3384_error,
@ -692,7 +690,7 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
_location, _location,
fmt::format( fmt::format(
"The \"{instruction}\" instruction is {kind} VMs (you are currently compiling for \"{version}\").", "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("kind", vmKindMessage),
fmt::arg("version", m_evmVersion.name()) fmt::arg("version", m_evmVersion.name())
) )

View File

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

View File

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

View File

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

View File

@ -47,11 +47,12 @@ namespace
{ {
pair<YulString, BuiltinFunctionForEVM> createEVMFunction( pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
langutil::EVMVersion _evmVersion,
string const& _name, string const& _name,
evmasm::Instruction _instruction evmasm::Instruction _instruction
) )
{ {
evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction); evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction, _evmVersion);
BuiltinFunctionForEVM f; BuiltinFunctionForEVM f;
f.name = YulString{_name}; f.name = YulString{_name};
f.parameters.resize(static_cast<size_t>(info.args)); 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(); 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; set<YulString> reserved;
for (auto const& instr: evmasm::c_instructions) for (auto const& instr: evmasm::c_instructions)
{ {
string name = toLower(instr.first); string name = toLower(instr.first);
if (!baseFeeException(instr.second)) if (!baseFeeException(instr.second) && !prevRandaoException(name))
reserved.emplace(name); reserved.emplace(name);
} }
reserved += vector<YulString>{ reserved += vector<YulString>{
@ -139,6 +148,13 @@ set<YulString> createReservedIdentifiers(langutil::EVMVersion _evmVersion)
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess) 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; map<YulString, BuiltinFunctionForEVM> builtins;
for (auto const& instr: evmasm::c_instructions) 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::JUMP &&
opcode != evmasm::Instruction::JUMPI && opcode != evmasm::Instruction::JUMPI &&
opcode != evmasm::Instruction::JUMPDEST && 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) if (_objectAccess)

View File

@ -88,7 +88,7 @@ void GasMeterVisitor::operator()(FunctionCall const& _funCall)
void GasMeterVisitor::operator()(Literal const& _lit) 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 += m_dataGas +=
singleByteDataGas() + singleByteDataGas() +
evmasm::GasMeter::dataGas( evmasm::GasMeter::dataGas(
@ -100,7 +100,7 @@ void GasMeterVisitor::operator()(Literal const& _lit)
void GasMeterVisitor::operator()(Identifier const&) 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(); 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). // Assumes that Keccak-256 is computed on a single word (rounded up).
m_runGas += evmasm::GasCosts::keccak256Gas + evmasm::GasCosts::keccak256WordGas; m_runGas += evmasm::GasCosts::keccak256Gas + evmasm::GasCosts::keccak256WordGas;
else else
m_runGas += evmasm::GasMeter::runGas(_instruction); m_runGas += evmasm::GasMeter::runGas(_instruction, m_dialect.evmVersion());
m_dataGas += singleByteDataGas(); m_dataGas += singleByteDataGas();
} }

View File

@ -124,7 +124,7 @@ void EthAssemblyAdapter::appendAssemblySize()
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name) 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); auto sub = m_assembly.newSub(assembly);
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())}; 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) 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&) void NoOutputAssembly::appendConstant(u256 const&)

View File

@ -45,7 +45,7 @@ namespace solidity::yul
class NoOutputAssembly: public AbstractAssembly class NoOutputAssembly: public AbstractAssembly
{ {
public: public:
explicit NoOutputAssembly() { } explicit NoOutputAssembly(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) { }
~NoOutputAssembly() override = default; ~NoOutputAssembly() override = default;
void setSourceLocation(langutil::SourceLocation const&) override {} void setSourceLocation(langutil::SourceLocation const&) override {}
@ -79,6 +79,7 @@ public:
private: private:
int m_stackHeight = 0; 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) if (_swapDepth > 16)
opGas += 1000; opGas += 1000;
else else
opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth)); opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth), langutil::EVMVersion());
}; };
auto dupOrPush = [&](StackSlot const& _slot) auto dupOrPush = [&](StackSlot const& _slot)
{ {
if (canBeFreelyGenerated(_slot)) if (canBeFreelyGenerated(_slot))
opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32)); opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32), langutil::EVMVersion());
else else
{ {
auto depth = util::findOffset(_source | ranges::views::reverse, _slot); auto depth = util::findOffset(_source | ranges::views::reverse, _slot);
yulAssert(depth); yulAssert(depth);
if (*depth < 16) 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 else
opGas += 1000; 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); createStackLayout(_source, _target, swap, dupOrPush, pop);
return opGas; return opGas;
}; };

View File

@ -47,7 +47,7 @@ void ExpressionSimplifier::visit(Expression& _expression)
m_dialect, m_dialect,
[this](YulString _var) { return variableValue(_var); } [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 (auto* functionCall = get_if<FunctionCall>(&_expression))
if (optional<evmasm::Instruction> instruction = toEVMInstruction(m_dialect, functionCall->functionName.name)) 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); arg = simplifyRecursively(arg);
if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues)) 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; return _expression;
} }

View File

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

View File

@ -58,6 +58,13 @@ optional<evmasm::Instruction> yul::toEVMInstruction(Dialect const& _dialect, Yul
return nullopt; 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) void StatementRemover::operator()(Block& _block)
{ {
util::iterateReplacing( util::iterateReplacing(

View File

@ -26,6 +26,7 @@
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <optional> #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 /// Helper function that returns the instruction, if the `_name` is a BuiltinFunction
std::optional<evmasm::Instruction> toEVMInstruction(Dialect const& _dialect, YulString const& _name); 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 class StatementRemover: public ASTModifier
{ {
public: public:

View File

@ -235,7 +235,7 @@ evmasm::Instruction Pattern::instruction() const
return m_instruction; 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()) if (matchGroup())
return ASTCopier().translate(matchGroupValue()); return ASTCopier().translate(matchGroupValue());
@ -248,9 +248,9 @@ Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData)
{ {
vector<Expression> arguments; vector<Expression> arguments;
for (auto const& arg: m_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, return FunctionCall{_debugData,
Identifier{_debugData, YulString{name}}, Identifier{_debugData, YulString{name}},

View File

@ -131,7 +131,7 @@ public:
/// Turns this pattern into an actual expression. Should only be called /// Turns this pattern into an actual expression. Should only be called
/// for patterns resulting from an action, i.e. with match groups assigned. /// 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: private:
Expression const& matchGroupValue() const; Expression const& matchGroupValue() const;

View File

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

View File

@ -121,6 +121,9 @@ done < <(
grep -v -E 'revertStatement/non_called.sol' | grep -v -E 'revertStatement/non_called.sol' |
# Skipping a test with "let basefee := ..." # Skipping a test with "let basefee := ..."
grep -v -E 'inlineAssembly/basefee_berlin_function.sol' | 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 # Skipping license error, unrelated to the grammar
grep -v -E 'license/license_double5.sol' | grep -v -E 'license/license_double5.sol' |
grep -v -E 'license/license_hidden_unicode.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); solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
if (!m_options.output.dir.empty()) 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 else
{ {
sout() << "Opcodes:" << endl; 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; sout() << endl;
} }
} }
@ -825,7 +825,7 @@ void CommandLineInterface::handleCombinedJSON()
if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful())
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful()) 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()) if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful())
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); contractData[g_strAsm] = m_compiler->assemblyJSON(contractName);
if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful()) 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 * @date 2018
* Tests for the assembler. * Tests for the assembler.
*/ */
#include <test/Common.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libsolutil/JSON.h> #include <libsolutil/JSON.h>
@ -59,15 +60,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{ "sub.asm", 1 }, { "sub.asm", 1 },
{ "verbatim.asm", 2 } { "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"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm{false, {}}; Assembly _subAsm{evmVersion, false, {}};
auto sub_asm = make_shared<string>("sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
Assembly _verbatimAsm(true, ""); Assembly _verbatimAsm(evmVersion, true, "");
auto verbatim_asm = make_shared<string>("verbatim.asm"); auto verbatim_asm = make_shared<string>("verbatim.asm");
_verbatimAsm.setSourceLocation({8, 18, 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) BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
{ {
EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion();
// Tests for 1, 2, 3 number of immutables. // Tests for 1, 2, 3 number of immutables.
for (int numImmutables = 1; numImmutables <= 3; ++numImmutables) for (int numImmutables = 1; numImmutables <= 3; ++numImmutables)
{ {
@ -243,7 +246,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
{ *subName, 1 } { *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 (char i = 0; i < numImmutables; ++i)
{ {
for (int r = 0; r < numActualRefs; ++r) 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) for (char i = 1; i <= numImmutables; ++i)
{ {
assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName}); 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(), ';'); auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';');
LinkerObject const& obj = assembly.assemble(); 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'); auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n');
#if 0 // {{{ debug prints #if 0 // {{{ debug prints
@ -302,11 +305,12 @@ BOOST_AUTO_TEST_CASE(immutable)
{ "root.asm", 0 }, { "root.asm", 0 },
{ "sub.asm", 1 } { "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"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm{false, {}}; Assembly _subAsm{evmVersion, false, {}};
auto sub_asm = make_shared<string>("sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
_subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someImmutable");
@ -395,10 +399,11 @@ BOOST_AUTO_TEST_CASE(immutable)
BOOST_AUTO_TEST_CASE(subobject_encode_decode) 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> subAsmPtr = make_shared<Assembly>(evmVersion, false, string{});
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(false, string{}); shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(evmVersion, false, string{});
assembly.appendSubroutine(subAsmPtr); assembly.appendSubroutine(subAsmPtr);
subAsmPtr->appendSubroutine(subSubAsmPtr); subAsmPtr->appendSubroutine(subSubAsmPtr);

View File

@ -1250,8 +1250,18 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
// tag unifications (due to block deduplication) is also // tag unifications (due to block deduplication) is also
// visible at the super-assembly. // visible at the super-assembly.
Assembly main{false, {}}; Assembly::OptimiserSettings settings;
AssemblyPointer sub = make_shared<Assembly>(true, string{}); 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)); sub->append(u256(1));
auto t1 = sub->newTag(); auto t1 = sub->newTag();
@ -1277,16 +1287,6 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
main.append(t1.toSubAssemblyTag(subId)); main.append(t1.toSubAssemblyTag(subId));
main.append(u256(8)); 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); main.optimise(settings);
AssemblyItems expectationMain{ AssemblyItems expectationMain{

View File

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

View File

@ -1860,6 +1860,7 @@ BOOST_AUTO_TEST_CASE(builtins)
block.chainid; block.chainid;
block.coinbase; block.coinbase;
block.difficulty; block.difficulty;
block.prevrandao;
block.gaslimit; block.gaslimit;
block.number; block.number;
block.timestamp; 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 { ... }`. // 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 { ... }`. // 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; return block.coinbase;
} }
function g() public view returns (uint) { function g() public view returns (uint) {
return block.difficulty;
}
function h() public view returns (uint) {
return block.gaslimit; return block.gaslimit;
} }
function i() public view returns (uint) { function h() public view returns (uint) {
return block.timestamp; return block.timestamp;
} }
function j() public view returns (uint) { function i() public view returns (uint) {
return block.chainid; 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(coinbase())
pop(timestamp()) pop(timestamp())
pop(number()) pop(number())
pop(difficulty()) pop(prevrandao())
pop(gaslimit()) pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled // 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: (89-1716): Unreachable code.
// Warning 5740: (1729-1741): 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(coinbase())
//pop(timestamp()) //pop(timestamp())
//pop(number()) //pop(number())
//pop(difficulty())
//pop(gaslimit()) //pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled // NOTE: msize() is allowed only with optimizer disabled

View File

@ -73,7 +73,7 @@ contract C {
pop(coinbase()) pop(coinbase())
pop(timestamp()) pop(timestamp())
pop(number()) pop(number())
pop(difficulty()) pop(prevrandao())
pop(gaslimit()) pop(gaslimit())
// NOTE: msize() is allowed only with optimizer disabled // 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: (94-1733): Unreachable code.
// Warning 5740: (1746-1758): 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(gas())
pop(address()) pop(address())
pop(balance(0)) pop(balance(0))
pop(selfbalance())
pop(caller()) pop(caller())
pop(callvalue()) pop(callvalue())
pop(extcodesize(0)) pop(extcodesize(0))
extcodecopy(0, 1, 2, 3) extcodecopy(0, 1, 2, 3)
pop(extcodehash(0))
pop(create(0, 1, 2)) pop(create(0, 1, 2))
pop(create2(0, 1, 2, 3))
pop(call(0, 1, 2, 3, 4, 5, 6)) pop(call(0, 1, 2, 3, 4, 5, 6))
pop(callcode(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(delegatecall(0, 1, 2, 3, 4, 5))
pop(staticcall(0, 1, 2, 3, 4, 5))
selfdestruct(0) selfdestruct(0)
log0(0, 1) log0(0, 1)
log1(0, 1, 2) log1(0, 1, 2)
log2(0, 1, 2, 3) log2(0, 1, 2, 3)
log3(0, 1, 2, 3, 4) log3(0, 1, 2, 3, 4)
log4(0, 1, 2, 3, 4, 5) log4(0, 1, 2, 3, 4, 5)
pop(chainid())
pop(basefee())
pop(origin()) pop(origin())
pop(gasprice()) pop(gasprice())
pop(blockhash(0)) pop(blockhash(0))
pop(coinbase()) pop(coinbase())
pop(timestamp()) pop(timestamp())
pop(number()) pop(number())
pop(difficulty())
pop(gaslimit()) pop(gaslimit())
// These two are disallowed too but the error suppresses other errors. // 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 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 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: (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: (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: (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: (208-216): 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: (234-245): 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: (263-277): 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: (291-314): 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 8961: (331-346): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 2527: (362-376): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 8961: (364-389): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (394-409): 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: (427-446): 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: (464-489): 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: (507-536): 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: (554-584): 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 2527: (602-630): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 8961: (575-591): Function cannot be declared as pure because this expression (potentially) modifies the state.
// TypeError 8961: (644-659): 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: (672-682): 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 8961: (695-708): 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 8961: (721-737): Function cannot be declared as pure because this expression (potentially) modifies the state. // TypeError 2527: (701-711): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (750-769): Function cannot be declared as pure because this expression (potentially) modifies the state. // TypeError 2527: (729-741): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 8961: (782-804): Function cannot be declared as pure because this expression (potentially) modifies the state. // TypeError 2527: (759-769): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (821-830): 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: (848-857): 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: (875-883): 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".
// 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".

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