Adds support for the EVM version "Paris".

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

Co-authored-by: Alex Beregszaszi <alex@rtfs.hu>
Co-authored-by: Daniel <daniel@ekpyron.org>
Co-authored-by: matheusaaguiar <95899911+matheusaaguiar@users.noreply.github.com>
Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>
This commit is contained in:
Rodrigo Q. Saramago
2023-01-23 18:50:36 +00:00
co-authored by Alex Beregszaszi Daniel matheusaaguiar Nikola Matić
parent d70d79af4a
commit ef6ff2f055
114 changed files with 842 additions and 221 deletions
+1 -1
View File
@@ -237,7 +237,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
sourceIndex = static_cast<int>(iter->second);
}
auto [name, data] = item.nameAndData();
auto [name, data] = item.nameAndData(m_evmVersion);
Json::Value jsonItem;
jsonItem["name"] = name;
jsonItem["begin"] = item.location().start;
+4 -1
View File
@@ -49,7 +49,7 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly
{
public:
Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { }
Assembly(langutil::EVMVersion _evmVersion, bool _creation, std::string _name): m_evmVersion(_evmVersion), m_creation(_creation), m_name(std::move(_name)) { }
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
@@ -112,6 +112,7 @@ public:
/// Changes the source location used for each appended item.
void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; }
langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; }
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
/// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.
LinkerObject const& assemble() const;
@@ -208,6 +209,8 @@ protected:
mutable LinkerObject m_assembledObject;
mutable std::vector<size_t> m_tagPositionsInBytecode;
langutil::EVMVersion m_evmVersion;
int m_deposit = 0;
/// True, if the assembly contains contract creation code.
bool const m_creation = false;
+11 -6
View File
@@ -69,12 +69,12 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
return make_pair(subId, tag);
}
pair<string, string> AssemblyItem::nameAndData() const
pair<string, string> AssemblyItem::nameAndData(langutil::EVMVersion _evmVersion) const
{
switch (type())
{
case Operation:
return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
case Push:
return {"PUSH", toStringInHex(data())};
case PushTag:
@@ -168,7 +168,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
size_t AssemblyItem::arguments() const
{
if (type() == Operation)
return static_cast<size_t>(instructionInfo(instruction()).args);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).args);
else if (type() == VerbatimBytecode)
return get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable)
@@ -182,7 +184,9 @@ size_t AssemblyItem::returnValues() const
switch (m_type)
{
case Operation:
return static_cast<size_t>(instructionInfo(instruction()).ret);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).ret);
case Push:
case PushTag:
case PushData:
@@ -251,7 +255,7 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
case Operation:
{
assertThrow(isValidInstruction(instruction()), AssemblyException, "Invalid instruction.");
text = util::toLower(instructionInfo(instruction()).name);
text = util::toLower(instructionInfo(instruction(), _assembly.evmVersion()).name);
break;
}
case Push:
@@ -323,12 +327,13 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
return text;
}
// Note: This method is exclusively used for debugging.
ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
{
switch (_item.type())
{
case Operation:
_out << " " << instructionInfo(_item.instruction()).name;
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
_out << "\t" << _item.getJumpTypeAsString();
break;
+2 -1
View File
@@ -108,10 +108,11 @@ public:
/// This function is used in `Assembly::assemblyJSON`.
/// It returns the name & data of the current assembly item.
/// @param _evmVersion the EVM version.
/// @returns a pair, where the first element is the json-assembly
/// item name, where second element is the string representation
/// of it's data.
std::pair<std::string, std::string> nameAndData() const;
std::pair<std::string, std::string> nameAndData(langutil::EVMVersion _evmVersion) const;
bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); }
+2 -2
View File
@@ -406,7 +406,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
m_stack.erase(m_stackHeight - static_cast<int>(i));
}
appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction(), EVMVersion()).ret == 1)
{
m_stack[m_stackHeight] = _c;
m_classPositions[_c].insert(m_stackHeight);
@@ -414,7 +414,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
else
{
assertThrow(
instructionInfo(expr.item->instruction()).ret == 0,
instructionInfo(expr.item->instruction(), EVMVersion()).ret == 0,
OptimizerException,
"Invalid number of return values."
);
+6 -6
View File
@@ -79,18 +79,18 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
return optimisations;
}
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion)
{
bigint gas = 0;
for (AssemblyItem const& item: _items)
if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1);
gas += GasMeter::runGas(Instruction::PUSH1, _evmVersion);
else if (item.type() == Operation)
{
if (item.instruction() == Instruction::EXP)
gas += GasCosts::expGas;
else
gas += GasMeter::runGas(item.instruction());
gas += GasMeter::runGas(item.instruction(), _evmVersion);
}
return gas;
}
@@ -131,7 +131,7 @@ void ConstantOptimisationMethod::replaceConstants(
bigint LiteralMethod::gasNeeded() const
{
return combineGas(
simpleRunGas({Instruction::PUSH1}),
simpleRunGas({Instruction::PUSH1}, m_params.evmVersion),
// PUSHX plus data
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
0
@@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const
{
return combineGas(
// Run gas: we ignore memory increase costs
simpleRunGas(copyRoutine()) + GasCosts::copyGas,
simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
// Data gas for data itself
@@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
{
auto numExps = static_cast<size_t>(count(_routine.begin(), _routine.end(), Instruction::EXP));
return combineGas(
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
simpleRunGas(_routine, m_params.evmVersion) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
// Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
0
+1 -1
View File
@@ -76,7 +76,7 @@ protected:
protected:
/// @returns the run gas for the given items ignoring special gas costs
static bigint simpleRunGas(AssemblyItems const& _items);
static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion);
/// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const;
static size_t bytesRequired(AssemblyItems const& _items);
+5 -4
View File
@@ -30,6 +30,7 @@ using namespace solidity::evmasm;
void solidity::evmasm::eachInstruction(
bytes const& _mem,
langutil::EVMVersion _evmVersion,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
@@ -38,7 +39,7 @@ void solidity::evmasm::eachInstruction(
Instruction const instr{*it};
int additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
additional = instructionInfo(instr, _evmVersion).additional;
u256 data{};
@@ -57,15 +58,15 @@ void solidity::evmasm::eachInstruction(
}
}
string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter)
string solidity::evmasm::disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, string const& _delimiter)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
eachInstruction(_mem, _evmVersion, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << std::uppercase << std::hex << static_cast<int>(_instr) << _delimiter;
else
{
InstructionInfo info = instructionInfo(_instr);
InstructionInfo info = instructionInfo(_instr, _evmVersion);
ret << info.name;
if (info.additional)
ret << " 0x" << std::uppercase << std::hex << _data;
+2 -2
View File
@@ -30,9 +30,9 @@ namespace solidity::evmasm
{
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction, u256 const&)> const& _onInstruction);
void eachInstruction(bytes const& _mem, langutil::EVMVersion _evmVersion, std::function<void(Instruction, u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem, std::string const& _delimiter = " ");
std::string disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, std::string const& _delimiter = " ");
}
+12 -12
View File
@@ -52,10 +52,10 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case PushProgramSize:
case PushLibraryAddress:
case PushDeployTimeAddress:
gas = runGas(Instruction::PUSH1);
gas = runGas(Instruction::PUSH1, m_evmVersion);
break;
case Tag:
gas = runGas(Instruction::JUMPDEST);
gas = runGas(Instruction::JUMPDEST, m_evmVersion);
break;
case Operation:
{
@@ -80,19 +80,19 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
break;
case Instruction::RETURN:
case Instruction::REVERT:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
case Instruction::MSTORE:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
case Instruction::MSTORE8:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
@@ -106,7 +106,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
@@ -195,13 +195,13 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas = GasCosts::balanceGas(m_evmVersion);
break;
case Instruction::CHAINID:
gas = runGas(Instruction::CHAINID);
gas = runGas(Instruction::CHAINID, m_evmVersion);
break;
case Instruction::SELFBALANCE:
gas = runGas(Instruction::SELFBALANCE);
gas = runGas(Instruction::SELFBALANCE, m_evmVersion);
break;
default:
gas = runGas(_item.instruction());
gas = runGas(_item.instruction(), m_evmVersion);
break;
}
break;
@@ -252,12 +252,12 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS
}));
}
unsigned GasMeter::runGas(Instruction _instruction)
unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVersion)
{
if (_instruction == Instruction::JUMPDEST)
return 1;
switch (instructionInfo(_instruction).gasPriceTier)
switch (instructionInfo(_instruction, _evmVersion).gasPriceTier)
{
case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas;
@@ -268,7 +268,7 @@ unsigned GasMeter::runGas(Instruction _instruction)
case Tier::Ext: return GasCosts::tier6Gas;
default: break;
}
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name);
return 0;
}
+1 -2
View File
@@ -221,7 +221,7 @@ public:
/// @returns gas costs for simple instructions with constant gas costs (that do not
/// change with EVM versions)
static unsigned runGas(Instruction _instruction);
static unsigned runGas(Instruction _instruction, langutil::EVMVersion _evmVersion);
/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
@@ -257,5 +257,4 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con
return _str << std::dec << _consumption.value;
}
}
+7 -3
View File
@@ -76,7 +76,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
{ "DIFFICULTY", Instruction::DIFFICULTY },
{ "DIFFICULTY", Instruction::PREVRANDAO },
{ "PREVRANDAO", Instruction::PREVRANDAO },
{ "GASLIMIT", Instruction::GASLIMIT },
{ "CHAINID", Instruction::CHAINID },
{ "SELFBALANCE", Instruction::SELFBALANCE },
@@ -174,6 +175,7 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT }
};
/// @note InstructionInfo is assumed to be the same across all EVM versions except for the instruction name.
static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ // Add, Args, Ret, SideEffects, GasPriceTier
{ Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } },
@@ -223,7 +225,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } },
{ Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::Base } },
{ Instruction::PREVRANDAO, { "PREVRANDAO", 0, 0, 1, false, Tier::Base } },
{ Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::Base } },
{ Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } },
{ Instruction::SELFBALANCE, { "SELFBALANCE", 0, 0, 1, false, Tier::Low } },
@@ -321,10 +323,12 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
};
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion)
{
try
{
if (_inst == Instruction::PREVRANDAO && _evmVersion < langutil::EVMVersion::paris())
return InstructionInfo({ "DIFFICULTY", 0, 0, 1, false, Tier::Base });
return c_instructionInfo.at(_inst);
}
catch (...)
+3 -2
View File
@@ -25,6 +25,7 @@
#include <libevmasm/Exceptions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <liblangutil/EVMVersion.h>
namespace solidity::evmasm
{
@@ -83,7 +84,7 @@ enum class Instruction: uint8_t
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number
DIFFICULTY, ///< get the block's difficulty
PREVRANDAO, ///< get randomness provided by the beacon chain
GASLIMIT, ///< get the block's gas limit
CHAINID, ///< get the config's chainid param
SELFBALANCE, ///< get balance of the current account
@@ -306,7 +307,7 @@ struct InstructionInfo
};
/// Information on all the instructions.
InstructionInfo instructionInfo(Instruction _inst);
InstructionInfo instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion);
/// check whether instructions exists.
bool isValidInstruction(Instruction _inst);
+3 -1
View File
@@ -130,7 +130,9 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
else
{
Instruction instruction = _item.instruction();
InstructionInfo info = instructionInfo(instruction);
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
InstructionInfo info = instructionInfo(instruction, EVMVersion());
if (SemanticInformation::isDupInstruction(_item))
setStackElement(
m_stackHeight + 1,
+50 -23
View File
@@ -38,7 +38,7 @@ struct OptimiserState
{
AssemblyItems const& items;
size_t i;
std::back_insert_iterator<AssemblyItems> out;
back_insert_iterator<AssemblyItems> out;
};
template<typename FunctionType>
@@ -53,7 +53,11 @@ template <class Method>
struct SimplePeepholeOptimizerMethod
{
template <size_t... Indices>
static bool applyRule(AssemblyItems::const_iterator _in, back_insert_iterator<AssemblyItems> _out, index_sequence<Indices...>)
static bool applyRule(
AssemblyItems::const_iterator _in,
back_insert_iterator<AssemblyItems> _out,
index_sequence<Indices...>
)
{
return Method::applySimple(_in[Indices]..., _out);
}
@@ -75,7 +79,10 @@ struct SimplePeepholeOptimizerMethod
struct Identity: SimplePeepholeOptimizerMethod<Identity>
{
static bool applySimple(AssemblyItem const& _item, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _item,
back_insert_iterator<AssemblyItems> _out
)
{
*_out = _item;
return true;
@@ -84,7 +91,11 @@ struct Identity: SimplePeepholeOptimizerMethod<Identity>
struct PushPop: SimplePeepholeOptimizerMethod<PushPop>
{
static bool applySimple(AssemblyItem const& _push, AssemblyItem const& _pop, std::back_insert_iterator<AssemblyItems>)
static bool applySimple(
AssemblyItem const& _push,
AssemblyItem const& _pop,
back_insert_iterator<AssemblyItems>
)
{
auto t = _push.type();
return _pop == Instruction::POP && (
@@ -100,15 +111,15 @@ struct OpPop: SimplePeepholeOptimizerMethod<OpPop>
static bool applySimple(
AssemblyItem const& _op,
AssemblyItem const& _pop,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_pop == Instruction::POP && _op.type() == Operation)
{
Instruction instr = _op.instruction();
if (instructionInfo(instr).ret == 1 && !instructionInfo(instr).sideEffects)
if (instructionInfo(instr, langutil::EVMVersion()).ret == 1 && !instructionInfo(instr, langutil::EVMVersion()).sideEffects)
{
for (int j = 0; j < instructionInfo(instr).args; j++)
for (int j = 0; j < instructionInfo(instr, langutil::EVMVersion()).args; j++)
*_out = {Instruction::POP, _op.location()};
return true;
}
@@ -122,7 +133,7 @@ struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
static bool applySimple(
AssemblyItem const& _op,
AssemblyItem const& _stop,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_stop == Instruction::STOP)
@@ -130,7 +141,7 @@ struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
if (_op.type() == Operation)
{
Instruction instr = _op.instruction();
if (!instructionInfo(instr).sideEffects)
if (!instructionInfo(instr, langutil::EVMVersion()).sideEffects)
{
*_out = {Instruction::STOP, _op.location()};
return true;
@@ -153,7 +164,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
AssemblyItem const& _push,
AssemblyItem const& _pushOrDup,
AssemblyItem const& _returnRevert,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -162,7 +173,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
(_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1))
)
if (
(_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
(_op.type() == Operation && !instructionInfo(_op.instruction(), langutil::EVMVersion()).sideEffects) ||
_op.type() == Push
)
{
@@ -177,7 +188,11 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
{
static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator<AssemblyItems>)
static size_t applySimple(
AssemblyItem const& _s1,
AssemblyItem const& _s2,
back_insert_iterator<AssemblyItems>
)
{
return _s1 == _s2 && SemanticInformation::isSwapInstruction(_s1);
}
@@ -185,7 +200,11 @@ struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
{
static bool applySimple(AssemblyItem const& _push1, AssemblyItem const& _push2, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _push1,
AssemblyItem const& _push2,
back_insert_iterator<AssemblyItems> _out
)
{
if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
{
@@ -200,7 +219,11 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
{
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _swap,
AssemblyItem const& _op,
back_insert_iterator<AssemblyItems> _out
)
{
// Remove SWAP1 if following instruction is commutative
if (
@@ -218,7 +241,11 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison>
{
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
static bool applySimple(
AssemblyItem const& _swap,
AssemblyItem const& _op,
back_insert_iterator<AssemblyItems> _out
)
{
static map<Instruction, Instruction> const swappableOps{
{ Instruction::LT, Instruction::GT },
@@ -247,7 +274,7 @@ struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
static size_t applySimple(
AssemblyItem const& _dupN,
AssemblyItem const& _swapN,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -272,7 +299,7 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI>
AssemblyItem const& _iszero2,
AssemblyItem const& _pushTag,
AssemblyItem const& _jumpi,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -298,7 +325,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
AssemblyItem const& _iszero,
AssemblyItem const& _pushTag,
AssemblyItem const& _jumpi,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -327,7 +354,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod<DoubleJump>
AssemblyItem const& _pushTag2,
AssemblyItem const& _jump,
AssemblyItem const& _tag1,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -356,7 +383,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
AssemblyItem const& _pushTag,
AssemblyItem const& _jump,
AssemblyItem const& _tag,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (
@@ -382,7 +409,7 @@ struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions>
AssemblyItem const& _pushTag,
AssemblyItem const& _pushConstant,
AssemblyItem const& _and,
std::back_insert_iterator<AssemblyItems> _out
back_insert_iterator<AssemblyItems> _out
)
{
if (_and != Instruction::AND)
@@ -417,7 +444,7 @@ struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd>
AssemblyItem const& _push,
AssemblyItem const& _not,
AssemblyItem const& _and,
std::back_insert_iterator<AssemblyItems>
back_insert_iterator<AssemblyItems>
)
{
return (
@@ -484,7 +511,7 @@ bool PeepholeOptimiser::optimise()
{
// Avoid referencing immutables too early by using approx. counting in bytesRequired()
auto const approx = evmasm::Precision::Approximate;
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
OptimiserState state {m_items, 0, back_inserter(m_optimisedItems)};
while (state.i < m_items.size())
applyMethods(
state,
+5 -5
View File
@@ -110,7 +110,7 @@ vector<SemanticInformation::Operation> SemanticInformation::readWriteOperations(
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
{
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction).args);
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction, langutil::EVMVersion()).args);
vector<Operation> operations{
Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}},
Operation{Location::Storage, Effect::Read, {}, {}, {}}
@@ -178,7 +178,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
InstructionInfo info = instructionInfo(_item.instruction());
InstructionInfo info = instructionInfo(_item.instruction(), langutil::EVMVersion());
if (_item.instruction() == Instruction::SSTORE)
return false;
if (_item.instruction() == Instruction::MSTORE)
@@ -318,7 +318,7 @@ bool SemanticInformation::movable(Instruction _instruction)
// These are not really functional.
if (isDupInstruction(_instruction) || isSwapInstruction(_instruction))
return false;
InstructionInfo info = instructionInfo(_instruction);
InstructionInfo info = instructionInfo(_instruction, langutil::EVMVersion());
if (info.sideEffects)
return false;
switch (_instruction)
@@ -345,7 +345,7 @@ bool SemanticInformation::canBeRemoved(Instruction _instruction)
// These are not really functional.
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
return !instructionInfo(_instruction).sideEffects;
return !instructionInfo(_instruction, langutil::EVMVersion()).sideEffects;
}
bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction)
@@ -483,7 +483,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction)
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::DIFFICULTY:
case Instruction::PREVRANDAO:
case Instruction::GASLIMIT:
case Instruction::STATICCALL:
case Instruction::SLOAD:
+1 -1
View File
@@ -122,7 +122,7 @@ struct EVMBuiltins
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{};
static auto constexpr PREVRANDAO = PatternGenerator<Instruction::PREVRANDAO>{};
static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{};
static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{};
static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{};
+2 -1
View File
@@ -141,7 +141,8 @@ string Pattern::toString() const
switch (m_type)
{
case Operation:
s << instructionInfo(m_instruction).name;
// Note: This function is exclusively used for debugging.
s << instructionInfo(m_instruction, EVMVersion()).name;
break;
case Push:
if (m_data)