mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
parent
d70d79af4a
commit
ef6ff2f055
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
12
docs/yul.rst
12
docs/yul.rst
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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); }
|
||||||
|
|
||||||
|
@ -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."
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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 = " ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 (...)
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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:
|
||||||
|
@ -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>{};
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 (
|
||||||
|
@ -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()},
|
||||||
|
@ -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},
|
||||||
|
@ -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")
|
||||||
|
@ -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")
|
||||||
|
@ -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,
|
||||||
|
@ -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())
|
||||||
)
|
)
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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())};
|
||||||
}
|
}
|
||||||
|
@ -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&)
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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))
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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:
|
||||||
|
@ -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}},
|
||||||
|
@ -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;
|
||||||
|
@ -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"
|
||||||
|
@ -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' |
|
||||||
|
@ -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())
|
||||||
|
1
test/cmdlineTests/evmasm_difficulty_post_paris/args
Normal file
1
test/cmdlineTests/evmasm_difficulty_post_paris/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris
|
5
test/cmdlineTests/evmasm_difficulty_post_paris/err
Normal file
5
test/cmdlineTests/evmasm_difficulty_post_paris/err
Normal 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;
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
18
test/cmdlineTests/evmasm_difficulty_post_paris/input.sol
Normal file
18
test/cmdlineTests/evmasm_difficulty_post_paris/input.sol
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
test/cmdlineTests/evmasm_difficulty_post_paris/output
Normal file
13
test/cmdlineTests/evmasm_difficulty_post_paris/output
Normal 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
|
1
test/cmdlineTests/evmasm_difficulty_pre_paris/args
Normal file
1
test/cmdlineTests/evmasm_difficulty_pre_paris/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london
|
18
test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol
Normal file
18
test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
test/cmdlineTests/evmasm_difficulty_pre_paris/output
Normal file
13
test/cmdlineTests/evmasm_difficulty_pre_paris/output
Normal 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
|
1
test/cmdlineTests/evmasm_prevrandao_post_paris/args
Normal file
1
test/cmdlineTests/evmasm_prevrandao_post_paris/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris
|
18
test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol
Normal file
18
test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
test/cmdlineTests/evmasm_prevrandao_post_paris/output
Normal file
13
test/cmdlineTests/evmasm_prevrandao_post_paris/output
Normal 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
|
1
test/cmdlineTests/evmasm_prevrandao_pre_paris/args
Normal file
1
test/cmdlineTests/evmasm_prevrandao_pre_paris/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london
|
5
test/cmdlineTests/evmasm_prevrandao_pre_paris/err
Normal file
5
test/cmdlineTests/evmasm_prevrandao_pre_paris/err
Normal 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;
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
18
test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol
Normal file
18
test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
test/cmdlineTests/evmasm_prevrandao_pre_paris/output
Normal file
13
test/cmdlineTests/evmasm_prevrandao_pre_paris/output
Normal 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
|
@ -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);
|
||||||
|
@ -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{
|
||||||
|
@ -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++;
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
12
test/libsolidity/semanticTests/inlineAssembly/difficulty.sol
Normal file
12
test/libsolidity/semanticTests/inlineAssembly/difficulty.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view returns (uint ret) {
|
||||||
|
assembly {
|
||||||
|
ret := difficulty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// EVMVersion: <paris
|
||||||
|
// ----
|
||||||
|
// f() -> 200000000
|
12
test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol
Normal file
12
test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view returns (uint ret) {
|
||||||
|
assembly {
|
||||||
|
ret := prevrandao()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// EVMVersion: >=paris
|
||||||
|
// ----
|
||||||
|
// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777
|
10
test/libsolidity/semanticTests/state/block_prevrandao.sol
Normal file
10
test/libsolidity/semanticTests/state/block_prevrandao.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view returns (uint) {
|
||||||
|
return block.prevrandao;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// EVMVersion: >=paris
|
||||||
|
// ----
|
||||||
|
// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view returns (uint) {
|
||||||
|
return block.prevrandao;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// EVMVersion: <paris
|
||||||
|
// ----
|
||||||
|
// f() -> 200000000
|
@ -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
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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
|
||||||
|
// ----
|
@ -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
|
||||||
|
// ----
|
@ -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.
|
@ -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".
|
@ -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.
|
@ -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 { ... }`.
|
@ -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 { ... }`.
|
@ -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 { ... }`.
|
@ -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 { ... }`.
|
@ -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 { ... }`.
|
@ -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 { ... }`.
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
test/libsolidity/syntaxTests/types/magic_block_paris.sol
Normal file
13
test/libsolidity/syntaxTests/types/magic_block_paris.sol
Normal 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
|
||||||
|
// ----
|
@ -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.
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public view {
|
||||||
|
assembly {
|
||||||
|
pop(difficulty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: <=london
|
||||||
|
// ----
|
@ -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".
|
|
||||||
|
@ -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".
|
@ -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.
|
@ -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".
|
@ -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
Loading…
Reference in New Issue
Block a user