diff --git a/crates/revm-jit-builtins/src/lib.rs b/crates/revm-jit-builtins/src/lib.rs index 235760aa..3a1c9d62 100644 --- a/crates/revm-jit-builtins/src/lib.rs +++ b/crates/revm-jit-builtins/src/lib.rs @@ -91,7 +91,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_exp( spec_id: SpecId, ) -> InstructionResult { let exponent = exponent_ptr.to_u256(); - gas_opt!(ecx, gas::exp_cost(spec_id, exponent)); + gas_opt!(ecx, gas::exp_cost(spec_id, exponent).map(|g| g - gas::EXP)); *exponent_ptr = base.to_u256().pow(exponent).into(); InstructionResult::Continue } @@ -101,20 +101,16 @@ pub unsafe extern "C" fn __revm_jit_builtin_keccak256( ecx: &mut EvmContext<'_>, rev![offset, len_ptr]: &mut [EvmWord; 2], ) -> InstructionResult { - if *len_ptr == EvmWord::ZERO { - *len_ptr = EvmWord::from_be_bytes(KECCAK_EMPTY.0); - gas!(ecx, gas::KECCAK256); - return InstructionResult::Continue; - } - let len = try_into_usize!(len_ptr.as_u256()); - gas_opt!(ecx, gas::keccak256_cost(len as u64)); - let offset = try_into_usize!(offset.as_u256()); - - resize_memory!(ecx, offset, len); - - let data = ecx.memory.slice(offset, len); - *len_ptr = EvmWord::from_be_bytes(revm_primitives::keccak256(data).0); - + let len = try_into_usize!(len_ptr); + *len_ptr = EvmWord::from_be_bytes(if len == 0 { + KECCAK_EMPTY.0 + } else { + gas_opt!(ecx, gas::keccak256_cost(len as u64).map(|g| g - gas::KECCAK256)); + let offset = try_into_usize!(offset); + resize_memory!(ecx, offset, len); + let data = ecx.memory.slice(offset, len); + revm_primitives::keccak256(data).0 + }); InstructionResult::Continue } @@ -195,10 +191,10 @@ pub unsafe extern "C" fn __revm_jit_builtin_extcodecopy( spec_id: SpecId, ) -> InstructionResult { let (code, is_cold) = try_host!(ecx.host.code(address.to_address())); - let len = tri!(usize::try_from(len)); + let len = try_into_usize!(len); gas_opt!(ecx, gas::extcodecopy_cost(spec_id, len as u64, is_cold)); if len != 0 { - let memory_offset = try_into_usize!(memory_offset.as_u256()); + let memory_offset = try_into_usize!(memory_offset); let code_offset = code_offset.to_u256(); let code_offset = as_usize_saturated!(code_offset).min(code.len()); resize_memory!(ecx, memory_offset, len); @@ -212,8 +208,8 @@ pub unsafe extern "C" fn __revm_jit_builtin_returndatacopy( ecx: &mut EvmContext<'_>, rev![memory_offset, offset, len]: &mut [EvmWord; 3], ) -> InstructionResult { - let len = tri!(usize::try_from(len)); - gas_opt!(ecx, gas::verylowcopy_cost(len as u64)); + let len = try_into_usize!(len); + gas_opt!(ecx, gas::verylowcopy_cost(len as u64).map(|g| g - gas::VERYLOW)); let data_offset = offset.to_u256(); let data_offset = as_usize_saturated!(data_offset); let (data_end, overflow) = data_offset.overflowing_add(len); @@ -221,7 +217,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_returndatacopy( return InstructionResult::OutOfOffset; } if len != 0 { - let memory_offset = try_into_usize!(memory_offset.as_u256()); + let memory_offset = try_into_usize!(memory_offset); resize_memory!(ecx, memory_offset, len); ecx.memory.set(memory_offset, &ecx.return_data[data_offset..data_end]); } @@ -312,8 +308,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_mload( ecx: &mut EvmContext<'_>, offset_ptr: &mut EvmWord, ) -> InstructionResult { - gas!(ecx, gas::VERYLOW); - let offset = try_into_usize!(offset_ptr.as_u256()); + let offset = try_into_usize!(offset_ptr); resize_memory!(ecx, offset, 32); *offset_ptr = ecx.memory.get_u256(offset).into(); InstructionResult::Continue @@ -324,8 +319,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_mstore( ecx: &mut EvmContext<'_>, rev![offset, value]: &mut [EvmWord; 2], ) -> InstructionResult { - gas!(ecx, gas::VERYLOW); - let offset = try_into_usize!(offset.as_u256()); + let offset = try_into_usize!(offset); resize_memory!(ecx, offset, 32); ecx.memory.set(offset, &value.to_be_bytes()); InstructionResult::Continue @@ -336,8 +330,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_mstore8( ecx: &mut EvmContext<'_>, rev![offset, value]: &mut [EvmWord; 2], ) -> InstructionResult { - gas!(ecx, gas::VERYLOW); - let offset = try_into_usize!(offset.as_u256()); + let offset = try_into_usize!(offset); resize_memory!(ecx, offset, 1); ecx.memory.set_byte(offset, value.to_u256().byte(0)); InstructionResult::Continue @@ -394,11 +387,11 @@ pub unsafe extern "C" fn __revm_jit_builtin_mcopy( ecx: &mut EvmContext<'_>, rev![dst, src, len]: &mut [EvmWord; 3], ) -> InstructionResult { - let len = try_into_usize!(len.to_u256()); - gas_opt!(ecx, gas::verylowcopy_cost(len as u64)); + let len = try_into_usize!(len); + gas_opt!(ecx, gas::verylowcopy_cost(len as u64).map(|g| g - gas::VERYLOW)); if len != 0 { - let dst = try_into_usize!(dst.to_u256()); - let src = try_into_usize!(src.to_u256()); + let dst = try_into_usize!(dst); + let src = try_into_usize!(src); resize_memory!(ecx, dst.max(src), len); ecx.memory.copy(dst, src, len); } @@ -411,13 +404,13 @@ pub unsafe extern "C" fn __revm_jit_builtin_log( sp: *mut EvmWord, n: u8, ) -> InstructionResult { - debug_assert!(n <= 4, "invalid log topic count: {n}"); + assume!(n <= 4, "invalid log topic count: {n}"); let sp = sp.add(n as usize); read_words!(sp, offset, len); - let len = tri!(usize::try_from(len)); - gas_opt!(ecx, gas::log_cost(n, len as u64)); + let len = try_into_usize!(len); + gas_opt!(ecx, gas::LOGDATA.checked_mul(len as u64)); let data = if len != 0 { - let offset = try_into_usize!(offset.as_u256()); + let offset = try_into_usize!(offset); resize_memory!(ecx, offset, len); Bytes::copy_from_slice(ecx.memory.slice(offset, len)) } else { @@ -452,7 +445,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_create( let mut sp = sp.add(len); pop!(sp; value, code_offset, len); - let len = tri!(usize::try_from(len)); + let len = try_into_usize!(len); let code = if len != 0 { if spec_id.is_enabled_in(SpecId::SHANGHAI) { // Limit is set as double of max contract bytecode size @@ -469,7 +462,7 @@ pub unsafe extern "C" fn __revm_jit_builtin_create( gas!(ecx, gas::initcode_cost(len as u64)); } - let code_offset = try_into_usize!(code_offset.as_u256()); + let code_offset = try_into_usize!(code_offset); resize_memory!(ecx, code_offset, len); Bytes::copy_from_slice(ecx.memory.slice(code_offset, len)) } else { @@ -541,18 +534,18 @@ pub unsafe extern "C" fn __revm_jit_builtin_call( pop!(sp; in_offset, in_len, out_offset, out_len); - let in_len = try_into_usize!(in_len.to_u256()); + let in_len = try_into_usize!(in_len); let input = if in_len != 0 { - let in_offset = try_into_usize!(in_offset.to_u256()); + let in_offset = try_into_usize!(in_offset); resize_memory!(ecx, in_offset, in_len); Bytes::copy_from_slice(ecx.memory.slice(in_offset, in_len)) } else { Bytes::new() }; - let out_len = try_into_usize!(out_len.to_u256()); + let out_len = try_into_usize!(out_len); let out_offset = if out_len != 0 { - let out_offset = try_into_usize!(out_offset.to_u256()); + let out_offset = try_into_usize!(out_offset); resize_memory!(ecx, out_offset, out_len); out_offset } else { @@ -620,9 +613,9 @@ pub unsafe extern "C" fn __revm_jit_builtin_do_return( rev![offset, len]: &mut [EvmWord; 2], result: InstructionResult, ) -> InstructionResult { - let len = try_into_usize!(len.as_u256()); + let len = try_into_usize!(len); let output = if len != 0 { - let offset = try_into_usize!(offset.as_u256()); + let offset = try_into_usize!(offset); resize_memory!(ecx, offset, len); ecx.memory.slice(offset, len).to_vec().into() } else { diff --git a/crates/revm-jit-builtins/src/macros.rs b/crates/revm-jit-builtins/src/macros.rs index 041250fe..81de7b75 100644 --- a/crates/revm-jit-builtins/src/macros.rs +++ b/crates/revm-jit-builtins/src/macros.rs @@ -1,3 +1,4 @@ +#[allow(unused_macros)] macro_rules! tri { ($e:expr) => { match $e { @@ -69,16 +70,12 @@ macro_rules! pop { macro_rules! try_into_usize { ($x:expr) => { - match $x { + match $x.to_u256().as_limbs() { x => { - let x = x.as_limbs(); - if x[1] != 0 || x[2] != 0 || x[3] != 0 { + if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) { return InstructionResult::InvalidOperandOOG; } - let Ok(val) = usize::try_from(x[0]) else { - return InstructionResult::InvalidOperandOOG; - }; - val + x[0] as usize } } }; diff --git a/crates/revm-jit-builtins/src/utils.rs b/crates/revm-jit-builtins/src/utils.rs index 5f79620a..2b77135f 100644 --- a/crates/revm-jit-builtins/src/utils.rs +++ b/crates/revm-jit-builtins/src/utils.rs @@ -1,4 +1,5 @@ -use revm_interpreter::{as_usize_saturated, gas as rgas, InstructionResult}; +use crate::gas; +use revm_interpreter::{as_usize_saturated, InstructionResult}; use revm_jit_context::{EvmContext, EvmWord}; /// Splits the stack pointer into `N` elements by casting it to an array. @@ -37,12 +38,12 @@ pub(crate) unsafe fn copy_operation( rev![memory_offset, data_offset, len]: &mut [EvmWord; 3], data: &[u8], ) -> InstructionResult { - let len = tri!(usize::try_from(len)); - gas_opt!(ecx, rgas::verylowcopy_cost(len as u64)); + let len = try_into_usize!(len); + gas_opt!(ecx, gas::verylowcopy_cost(len as u64).map(|g| g - gas::VERYLOW)); if len == 0 { return InstructionResult::Continue; } - let memory_offset = try_into_usize!(memory_offset.as_u256()); + let memory_offset = try_into_usize!(memory_offset); resize_memory!(ecx, memory_offset, len); let data_offset = data_offset.to_u256(); let data_offset = as_usize_saturated!(data_offset); diff --git a/crates/revm-jit/src/bytecode/info.rs b/crates/revm-jit/src/bytecode/info.rs index 9bb4db40..19ab46c4 100644 --- a/crates/revm-jit/src/bytecode/info.rs +++ b/crates/revm-jit/src/bytecode/info.rs @@ -1,4 +1,4 @@ -use revm_interpreter::opcode as op; +use revm_interpreter::{gas, opcode as op}; use revm_primitives::{spec_to_generic, SpecId}; /// Opcode information. @@ -39,14 +39,12 @@ impl OpcodeInfo { self.0 & Self::DISABLED != 0 } - /// Returns the gas cost of the opcode. + /// Returns the base gas cost of the opcode. + /// + /// This may not be the final/full gas cost of the opcode as it may also have a dynamic cost. #[inline] - pub const fn static_gas(self) -> Option { - if self.is_dynamic() { - None - } else { - Some(self.0 & Self::MASK) - } + pub const fn base_gas(self) -> u16 { + self.0 & Self::MASK } /// Sets the unknown flag. @@ -106,7 +104,9 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { )* }; } - // [1]: Not dynamic in all `SpecId`s, but is calculated dynamically in a builtin. + // [1]: Not dynamic in all `SpecId`s, but gas is calculated dynamically in a builtin. + // TODO: Could be converted into [2] with a base cost. + // [2]: Dynamic with a base cost. Only the dynamic part is paid in builtins. set! { STOP = 0; @@ -119,7 +119,7 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { SMOD = 5; ADDMOD = 8; MULMOD = 8; - EXP = DYNAMIC; + EXP = 10 | DYNAMIC; // [2] SIGNEXTEND = 5; // 0x0C // 0x0D @@ -141,7 +141,7 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { SAR = 3, if CONSTANTINOPLE; // 0x1E // 0x1F - KECCAK256 = DYNAMIC; + KECCAK256 = 30 | DYNAMIC; // [2] // 0x21 // 0x22 // 0x23 @@ -164,15 +164,15 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { CALLVALUE = 2; CALLDATALOAD = 3; CALLDATASIZE = 2; - CALLDATACOPY = DYNAMIC; + CALLDATACOPY = 3 | DYNAMIC; // [2] CODESIZE = 2; - CODECOPY = DYNAMIC; + CODECOPY = 3 | DYNAMIC; // [2] GASPRICE = 2; EXTCODESIZE = DYNAMIC; // [1] EXTCODECOPY = DYNAMIC; RETURNDATASIZE = 2, if BYZANTIUM; - RETURNDATACOPY = DYNAMIC, if BYZANTIUM; + RETURNDATACOPY = 3 | DYNAMIC, if BYZANTIUM; // [2] EXTCODEHASH = DYNAMIC, if CONSTANTINOPLE; // [1] BLOCKHASH = 20; COINBASE = 2; @@ -191,9 +191,9 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { // 0x4E // 0x4F POP = 2; - MLOAD = DYNAMIC; - MSTORE = DYNAMIC; - MSTORE8 = DYNAMIC; + MLOAD = 3 | DYNAMIC; // [2] + MSTORE = 3 | DYNAMIC; // [2] + MSTORE8 = 3 | DYNAMIC; // [2] SLOAD = DYNAMIC; // [1] SSTORE = DYNAMIC; JUMP = 8; @@ -204,7 +204,7 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { JUMPDEST = 1; TLOAD = 100, if CANCUN; TSTORE = 100, if CANCUN; - MCOPY = DYNAMIC, if CANCUN; + MCOPY = 3 | DYNAMIC, if CANCUN; // [2] PUSH0 = 2, if SHANGHAI; PUSH1 = 3; @@ -274,11 +274,11 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { SWAP15 = 3; SWAP16 = 3; - LOG0 = DYNAMIC; - LOG1 = DYNAMIC; - LOG2 = DYNAMIC; - LOG3 = DYNAMIC; - LOG4 = DYNAMIC; + LOG0 = log_cost(0) | DYNAMIC; // [2] + LOG1 = log_cost(1) | DYNAMIC; // [2] + LOG2 = log_cost(2) | DYNAMIC; // [2] + LOG3 = log_cost(3) | DYNAMIC; // [2] + LOG4 = log_cost(4) | DYNAMIC; // [2] // 0xA5 // 0xA6 // 0xA7 @@ -374,58 +374,12 @@ const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { map } -/// [`gas::account_access_gas`] -#[cfg(any())] -const fn balance_cost(spec_id: SpecId) -> u16 { - let gas = gas::account_access_gas(spec_id, true); - if gas == gas::COLD_ACCOUNT_ACCESS_COST { - OpcodeInfo::DYNAMIC - } else { - gas as u16 - } -} - -/// [`gas::sload_cost`] -#[cfg(any())] -const fn sload_cost(spec_id: SpecId) -> u16 { - let gas = gas::sload_cost(spec_id, true); - if gas == gas::COLD_SLOAD_COST { - OpcodeInfo::DYNAMIC - } else { - gas as u16 - } -} - -/// [`revm_interpreter::instructions::host::extcodesize`] -#[cfg(any())] -const fn extcodesize_cost(spec_id: SpecId) -> u16 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - OpcodeInfo::DYNAMIC - // if is_cold { - // COLD_ACCOUNT_ACCESS_COST - // } else { - // WARM_STORAGE_READ_COST - // } - } else if spec_id.is_enabled_in(SpecId::TANGERINE) { - 700 - } else { - 20 - } -} - -/// [`revm_interpreter::instructions::host::extcodehash`] -#[cfg(any())] -const fn extcodehash_cost(spec_id: SpecId) -> u16 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - OpcodeInfo::DYNAMIC - // if is_cold { - // COLD_ACCOUNT_ACCESS_COST - // } else { - // WARM_STORAGE_READ_COST - // } - } else if spec_id.is_enabled_in(SpecId::ISTANBUL) { - 700 - } else { - 400 +const fn log_cost(n: u8) -> u16 { + match gas::log_cost(n, 0) { + Some(gas) => { + assert!(gas <= u16::MAX as u64); + gas as u16 + } + None => unreachable!(), } } diff --git a/crates/revm-jit/src/bytecode/mod.rs b/crates/revm-jit/src/bytecode/mod.rs index 685477b2..14201669 100644 --- a/crates/revm-jit/src/bytecode/mod.rs +++ b/crates/revm-jit/src/bytecode/mod.rs @@ -114,11 +114,11 @@ impl<'a> Bytecode<'a> { if info.is_disabled() { flags |= InstFlags::DISABLED; } - let static_gas = info.static_gas().unwrap_or(u16::MAX); + let base_gas = info.base_gas(); let section = Section::default(); - insts.push(InstData { opcode, flags, static_gas, data, pc: pc as u32, section }); + insts.push(InstData { opcode, flags, base_gas, data, pc: pc as u32, section }); } let mut bytecode = Self { @@ -368,8 +368,10 @@ pub(crate) struct InstData { pub(crate) opcode: u8, /// Flags. pub(crate) flags: InstFlags, - /// Static gas. Stored as `u16::MAX` if the gas is dynamic. - static_gas: u16, + /// The base gas cost of the opcode. + /// + /// This may not be the final/full gas cost of the opcode as it may also have a dynamic cost. + base_gas: u16, /// Instruction-specific data: /// - if the instruction has immediate data, this is a packed offset+length into the bytecode; /// - `JUMP{,I} && STATIC_JUMP in kind`: the jump target, `Instr`; @@ -447,12 +449,6 @@ impl InstData { Opcode { opcode: self.opcode, immediate: bytecode.get_imm_of(self) } } - /// Returns the static gas for this instruction, if any. - #[inline] - pub(crate) fn static_gas(&self) -> Option { - (self.static_gas != u16::MAX).then_some(self.static_gas) - } - /// Returns `true` if this instruction is a push instruction. pub(crate) fn is_push(&self) -> bool { matches!(self.opcode, op::PUSH0..=op::PUSH32) diff --git a/crates/revm-jit/src/bytecode/sections.rs b/crates/revm-jit/src/bytecode/sections.rs index 4cc6a661..b47f5fb8 100644 --- a/crates/revm-jit/src/bytecode/sections.rs +++ b/crates/revm-jit/src/bytecode/sections.rs @@ -66,7 +66,7 @@ impl SectionAnalysis { self.diff += stack_diff; self.max_growth = self.max_growth.max(self.diff); - self.gas_cost += data.static_gas().unwrap_or(0) as u64; + self.gas_cost += data.base_gas as u64; // Branching instructions end a section, starting a new one on the next instruction, if any. if data.opcode == op::GAS || data.is_branching(bytecode.is_eof()) || data.will_suspend() { diff --git a/crates/revm-jit/src/tests/macros.rs b/crates/revm-jit/src/tests/macros.rs index 0783407e..c18256cf 100644 --- a/crates/revm-jit/src/tests/macros.rs +++ b/crates/revm-jit/src/tests/macros.rs @@ -85,7 +85,7 @@ macro_rules! tests { (@gas $op:expr; $($args:expr),+) => { tests!(@gas $op, - DEF_OPINFOS[$op as usize].static_gas().expect(stringify!($op)) as u64; + DEF_OPINFOS[$op as usize].base_gas() as u64; $($args),+ ) };