@@ -11,7 +11,7 @@ use ethrex_common::{U256, types::Fork};
11
11
// Opcodes: PUSH0, PUSH1 ... PUSH32
12
12
13
13
impl < ' a > VM < ' a > {
14
- // PUSH operation
14
+ // Generic PUSH operation
15
15
pub fn op_push ( & mut self , n_bytes : usize ) -> Result < OpcodeResult , VMError > {
16
16
let current_call_frame = self . current_call_frame_mut ( ) ?;
17
17
current_call_frame. increase_consumed_gas ( gas_cost:: PUSHN ) ?;
@@ -30,6 +30,42 @@ impl<'a> VM<'a> {
30
30
} )
31
31
}
32
32
33
+ /// Specialized PUSH1 operation
34
+ ///
35
+ /// We use specialized push1 and push2 implementations because they are way more frequent than the others,
36
+ /// so their impact on performance is significant.
37
+ /// These implementations allow using U256::from, which is considerable more performant than U256::from_big_endian)
38
+ pub fn op_push1 ( & mut self ) -> Result < OpcodeResult , VMError > {
39
+ let current_call_frame = self . current_call_frame_mut ( ) ?;
40
+ current_call_frame. increase_consumed_gas ( gas_cost:: PUSHN ) ?;
41
+
42
+ let value = read_bytcode_slice_const :: < 1 > ( current_call_frame) ?[ 0 ] ;
43
+
44
+ current_call_frame. stack . push ( U256 :: from ( value) ) ?;
45
+
46
+ Ok ( OpcodeResult :: Continue {
47
+ // The 1 byte that you push to the stack + 1 for the next instruction
48
+ pc_increment : 2 ,
49
+ } )
50
+ }
51
+
52
+ // Specialized PUSH2 operation
53
+ pub fn op_push2 ( & mut self ) -> Result < OpcodeResult , VMError > {
54
+ let current_call_frame = self . current_call_frame_mut ( ) ?;
55
+ current_call_frame. increase_consumed_gas ( gas_cost:: PUSHN ) ?;
56
+
57
+ let read_n_bytes = read_bytcode_slice_const :: < 2 > ( current_call_frame) ?;
58
+
59
+ let value = u16:: from_be_bytes ( read_n_bytes) ;
60
+
61
+ current_call_frame. stack . push ( U256 :: from ( value) ) ?;
62
+
63
+ Ok ( OpcodeResult :: Continue {
64
+ // The 2 bytes that you push to the stack + 1 for the next instruction
65
+ pc_increment : 3 ,
66
+ } )
67
+ }
68
+
33
69
// PUSH0
34
70
pub fn op_push0 ( & mut self ) -> Result < OpcodeResult , VMError > {
35
71
// [EIP-3855] - PUSH0 is only available from SHANGHAI
@@ -61,3 +97,28 @@ fn read_bytcode_slice(current_call_frame: &CallFrame, n_bytes: usize) -> Result<
61
97
. get ( pc_offset..pc_offset. checked_add ( n_bytes) . ok_or ( OutOfBounds ) ?)
62
98
. unwrap_or_default ( ) )
63
99
}
100
+
101
+ // Like `read_bytcode_slice` but using a const generic and returning a fixed size array.
102
+ fn read_bytcode_slice_const < const N : usize > (
103
+ current_call_frame : & CallFrame ,
104
+ ) -> Result < [ u8 ; N ] , VMError > {
105
+ let current_pc = current_call_frame. pc ;
106
+ let pc_offset = current_pc
107
+ // Add 1 to the PC because we don't want to include the
108
+ // Bytecode of the current instruction in the data we're about
109
+ // to read. We only want to read the data _NEXT_ to that
110
+ // bytecode
111
+ . checked_add ( 1 )
112
+ . ok_or ( InternalError :: Overflow ) ?;
113
+
114
+ if let Some ( slice) = current_call_frame
115
+ . bytecode
116
+ . get ( pc_offset..pc_offset. checked_add ( N ) . ok_or ( OutOfBounds ) ?)
117
+ {
118
+ Ok ( slice
119
+ . try_into ( )
120
+ . map_err ( |_| VMError :: Internal ( InternalError :: TypeConversion ) ) ?)
121
+ } else {
122
+ Ok ( [ 0 ; N ] )
123
+ }
124
+ }
0 commit comments