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:
co-authored by
Alex Beregszaszi
Daniel
matheusaaguiar
Nikola Matić
parent
d70d79af4a
commit
ef6ff2f055
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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 (...)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>{};
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user