Files
revive/crates/solidity/src/evmla/assembly/instruction/mod.rs
T
xermicus c0dd845b39 s/field/word
Signed-off-by: xermicus <cyrill@parity.io>
2024-05-07 13:37:17 +02:00

383 lines
10 KiB
Rust

//! The EVM instruction.
pub mod codecopy;
pub mod jump;
pub mod name;
pub mod stack;
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
use self::name::Name;
/// The EVM instruction.
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Instruction {
/// The opcode or tag identifier.
pub name: Name,
/// The optional value argument.
pub value: Option<String>,
/// The source code identifier.
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<isize>,
/// The source code location begin.
pub begin: isize,
/// The source code location end.
pub end: isize,
}
impl Instruction {
/// Returns the number of input stack arguments.
pub const fn input_size(&self, version: &semver::Version) -> usize {
match self.name {
Name::POP => 1,
Name::JUMP => 1,
Name::JUMPI => 2,
Name::ADD => 2,
Name::SUB => 2,
Name::MUL => 2,
Name::DIV => 2,
Name::MOD => 2,
Name::SDIV => 2,
Name::SMOD => 2,
Name::LT => 2,
Name::GT => 2,
Name::EQ => 2,
Name::ISZERO => 1,
Name::SLT => 2,
Name::SGT => 2,
Name::OR => 2,
Name::XOR => 2,
Name::NOT => 1,
Name::AND => 2,
Name::SHL => 2,
Name::SHR => 2,
Name::SAR => 2,
Name::BYTE => 2,
Name::ADDMOD => 3,
Name::MULMOD => 3,
Name::EXP => 2,
Name::SIGNEXTEND => 2,
Name::SHA3 => 2,
Name::KECCAK256 => 2,
Name::MLOAD => 1,
Name::MSTORE => 2,
Name::MSTORE8 => 2,
Name::MCOPY => 3,
Name::SLOAD => 1,
Name::SSTORE => 2,
Name::TLOAD => 1,
Name::TSTORE => 2,
Name::PUSHIMMUTABLE => 0,
Name::ASSIGNIMMUTABLE => {
if version.minor >= 8 {
2
} else {
1
}
}
Name::CALLDATALOAD => 1,
Name::CALLDATACOPY => 3,
Name::CODECOPY => 3,
Name::RETURNDATACOPY => 3,
Name::EXTCODESIZE => 1,
Name::EXTCODEHASH => 1,
Name::CALL => 7,
Name::CALLCODE => 7,
Name::STATICCALL => 6,
Name::DELEGATECALL => 6,
Name::RETURN => 2,
Name::REVERT => 2,
Name::SELFDESTRUCT => 1,
Name::LOG0 => 2,
Name::LOG1 => 3,
Name::LOG2 => 4,
Name::LOG3 => 5,
Name::LOG4 => 6,
Name::CREATE => 3,
Name::CREATE2 => 4,
Name::ZK_CREATE => 3,
Name::ZK_CREATE2 => 4,
Name::BALANCE => 1,
Name::BLOCKHASH => 1,
Name::BLOBHASH => 1,
Name::EXTCODECOPY => 4,
Name::RecursiveCall { input_size, .. } => input_size,
Name::RecursiveReturn { input_size } => input_size,
_ => 0,
}
}
/// Returns the number of output stack arguments.
pub const fn output_size(&self) -> usize {
match self.name {
Name::PUSH => 1,
Name::PUSH_Data => 1,
Name::PUSH_Tag => 1,
Name::PUSH_ContractHash => 1,
Name::PUSH_ContractHashSize => 1,
Name::PUSHLIB => 1,
Name::PUSHDEPLOYADDRESS => 1,
Name::PUSH1 => 1,
Name::PUSH2 => 1,
Name::PUSH3 => 1,
Name::PUSH4 => 1,
Name::PUSH5 => 1,
Name::PUSH6 => 1,
Name::PUSH7 => 1,
Name::PUSH8 => 1,
Name::PUSH9 => 1,
Name::PUSH10 => 1,
Name::PUSH11 => 1,
Name::PUSH12 => 1,
Name::PUSH13 => 1,
Name::PUSH14 => 1,
Name::PUSH15 => 1,
Name::PUSH16 => 1,
Name::PUSH17 => 1,
Name::PUSH18 => 1,
Name::PUSH19 => 1,
Name::PUSH20 => 1,
Name::PUSH21 => 1,
Name::PUSH22 => 1,
Name::PUSH23 => 1,
Name::PUSH24 => 1,
Name::PUSH25 => 1,
Name::PUSH26 => 1,
Name::PUSH27 => 1,
Name::PUSH28 => 1,
Name::PUSH29 => 1,
Name::PUSH30 => 1,
Name::PUSH31 => 1,
Name::PUSH32 => 1,
Name::DUP1 => 1,
Name::DUP2 => 1,
Name::DUP3 => 1,
Name::DUP4 => 1,
Name::DUP5 => 1,
Name::DUP6 => 1,
Name::DUP7 => 1,
Name::DUP8 => 1,
Name::DUP9 => 1,
Name::DUP10 => 1,
Name::DUP11 => 1,
Name::DUP12 => 1,
Name::DUP13 => 1,
Name::DUP14 => 1,
Name::DUP15 => 1,
Name::DUP16 => 1,
Name::ADD => 1,
Name::SUB => 1,
Name::MUL => 1,
Name::DIV => 1,
Name::MOD => 1,
Name::SDIV => 1,
Name::SMOD => 1,
Name::LT => 1,
Name::GT => 1,
Name::EQ => 1,
Name::ISZERO => 1,
Name::SLT => 1,
Name::SGT => 1,
Name::OR => 1,
Name::XOR => 1,
Name::NOT => 1,
Name::AND => 1,
Name::SHL => 1,
Name::SHR => 1,
Name::SAR => 1,
Name::BYTE => 1,
Name::ADDMOD => 1,
Name::MULMOD => 1,
Name::EXP => 1,
Name::SIGNEXTEND => 1,
Name::SHA3 => 1,
Name::KECCAK256 => 1,
Name::MLOAD => 1,
Name::SLOAD => 1,
Name::TLOAD => 1,
Name::PUSHIMMUTABLE => 1,
Name::CALLDATALOAD => 1,
Name::CALLDATASIZE => 1,
Name::CODESIZE => 1,
Name::PUSHSIZE => 1,
Name::RETURNDATASIZE => 1,
Name::EXTCODESIZE => 1,
Name::EXTCODEHASH => 1,
Name::CALL => 1,
Name::CALLCODE => 1,
Name::STATICCALL => 1,
Name::DELEGATECALL => 1,
Name::CREATE => 1,
Name::CREATE2 => 1,
Name::ZK_CREATE => 1,
Name::ZK_CREATE2 => 1,
Name::ADDRESS => 1,
Name::CALLER => 1,
Name::TIMESTAMP => 1,
Name::NUMBER => 1,
Name::CALLVALUE => 1,
Name::GAS => 1,
Name::BALANCE => 1,
Name::SELFBALANCE => 1,
Name::GASLIMIT => 1,
Name::GASPRICE => 1,
Name::ORIGIN => 1,
Name::CHAINID => 1,
Name::BLOCKHASH => 1,
Name::BLOBHASH => 1,
Name::DIFFICULTY => 1,
Name::PREVRANDAO => 1,
Name::COINBASE => 1,
Name::MSIZE => 1,
Name::BASEFEE => 1,
Name::BLOBBASEFEE => 1,
Name::PC => 1,
Name::RecursiveCall { output_size, .. } => output_size,
_ => 0,
}
}
/// Replaces the instruction data aliases with the actual data.
pub fn replace_data_aliases(
instructions: &mut [Self],
mapping: &BTreeMap<String, String>,
) -> anyhow::Result<()> {
for instruction in instructions.iter_mut() {
match instruction {
Instruction {
name: Name::PUSH_ContractHash | Name::PUSH_ContractHashSize,
value: Some(value),
..
} => {
*value = mapping.get(value.as_str()).cloned().ok_or_else(|| {
anyhow::anyhow!("Contract alias `{}` data not found", value)
})?;
}
Instruction {
name: Name::PUSH_Data,
value: Some(value),
..
} => {
let mut key_extended =
"0".repeat(revive_common::BYTE_LENGTH_WORD * 2 - value.len());
key_extended.push_str(value.as_str());
*value = mapping.get(key_extended.as_str()).cloned().ok_or_else(|| {
anyhow::anyhow!("Data chunk alias `{}` data not found", key_extended)
})?;
}
_ => {}
}
}
Ok(())
}
/// Initializes an `INVALID` instruction to terminate an invalid unreachable block part.
pub fn invalid(previous: &Self) -> Self {
Self {
name: Name::INVALID,
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
/// Initializes a recursive function `Call` instruction.
pub fn recursive_call(
name: String,
entry_key: revive_llvm_context::PolkaVMFunctionBlockKey,
stack_hash: md5::Digest,
input_size: usize,
output_size: usize,
return_address: revive_llvm_context::PolkaVMFunctionBlockKey,
previous: &Self,
) -> Self {
Self {
name: Name::RecursiveCall {
name,
entry_key,
stack_hash,
input_size,
output_size,
return_address,
},
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
/// Initializes a recursive function `Return` instruction.
pub fn recursive_return(input_size: usize, previous: &Self) -> Self {
Self {
name: Name::RecursiveReturn { input_size },
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
}
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.name.to_string();
match self.name {
Name::Tag => write!(f, "{:4}", name),
_ => write!(f, "{:15}", name),
}?;
match self.value {
Some(ref value) if value.len() <= 64 => write!(f, "{}", value)?,
Some(ref value) => write!(f, "... {}", &value[value.len() - 60..])?,
None => {}
}
Ok(())
}
}