Skip to main content

ethrex_levm/opcode_handlers/
bitwise_comparison.rs

1//! # Bitwise and comparison operations
2//!
3//! Includes the following opcodes:
4//!   - `LT`
5//!   - `GT`
6//!   - `SLT`
7//!   - `SGT`
8//!   - `EQ`
9//!   - `ISZERO`
10//!   - `AND`
11//!   - `OR`
12//!   - `XOR`
13//!   - `NOT`
14//!   - `BYTE`
15//!   - `SHL`
16//!   - `SHR`
17//!   - `SAR`
18
19use crate::{
20    errors::{OpcodeResult, VMError},
21    gas_cost,
22    opcode_handlers::OpcodeHandler,
23    vm::VM,
24};
25use ethrex_common::U256;
26
27/// Implementation for the `LT` opcode.
28pub struct OpLtHandler;
29impl OpcodeHandler for OpLtHandler {
30    #[inline(always)]
31    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
32        vm.current_call_frame.increase_consumed_gas(gas_cost::LT)?;
33
34        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
35        #[expect(clippy::as_conversions, reason = "safe")]
36        let res = (lhs < *rhs) as u64;
37        *rhs = res.into();
38
39        Ok(OpcodeResult::Continue)
40    }
41}
42
43/// Implementation for the `GT` opcode.
44pub struct OpGtHandler;
45impl OpcodeHandler for OpGtHandler {
46    #[inline(always)]
47    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
48        vm.current_call_frame.increase_consumed_gas(gas_cost::GT)?;
49
50        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
51        #[expect(clippy::as_conversions, reason = "safe")]
52        let res = (lhs > *rhs) as u64;
53        *rhs = res.into();
54
55        Ok(OpcodeResult::Continue)
56    }
57}
58
59/// Implementation for the `SLT` opcode.
60pub struct OpSLtHandler;
61impl OpcodeHandler for OpSLtHandler {
62    #[inline(always)]
63    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
64        vm.current_call_frame.increase_consumed_gas(gas_cost::SLT)?;
65
66        let (lhs, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
67        let rhs = *slot;
68        let lhs_sign = lhs.bit(255);
69        let rhs_sign = rhs.bit(255);
70
71        *slot = match (lhs_sign, rhs_sign) {
72            (false, true) => U256::zero(),
73            (true, false) => U256::one(),
74            #[expect(clippy::as_conversions, reason = "safe")]
75            _ => ((lhs < rhs) as u64).into(),
76        };
77
78        Ok(OpcodeResult::Continue)
79    }
80}
81
82/// Implementation for the `SGT` opcode.
83pub struct OpSGtHandler;
84impl OpcodeHandler for OpSGtHandler {
85    #[inline(always)]
86    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
87        vm.current_call_frame.increase_consumed_gas(gas_cost::SGT)?;
88
89        let (lhs, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
90        let rhs = *slot;
91        let lhs_sign = lhs.bit(255);
92        let rhs_sign = rhs.bit(255);
93
94        *slot = match (lhs_sign, rhs_sign) {
95            (false, true) => U256::one(),
96            (true, false) => U256::zero(),
97            #[expect(clippy::as_conversions, reason = "safe")]
98            _ => ((lhs > rhs) as u64).into(),
99        };
100
101        Ok(OpcodeResult::Continue)
102    }
103}
104
105/// Implementation for the `EQ` opcode.
106pub struct OpEqHandler;
107impl OpcodeHandler for OpEqHandler {
108    #[inline(always)]
109    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
110        vm.current_call_frame.increase_consumed_gas(gas_cost::EQ)?;
111
112        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
113        #[expect(clippy::as_conversions, reason = "safe")]
114        let res = (lhs == *rhs) as u64;
115        *rhs = res.into();
116
117        Ok(OpcodeResult::Continue)
118    }
119}
120
121/// Implementation for the `ISZERO` opcode.
122pub struct OpIsZeroHandler;
123impl OpcodeHandler for OpIsZeroHandler {
124    #[inline(always)]
125    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
126        vm.current_call_frame
127            .increase_consumed_gas(gas_cost::ISZERO)?;
128
129        // In-place top mutation: no pop/push, no `offset` write.
130        let slot = vm.current_call_frame.stack.top_mut()?;
131        #[expect(clippy::as_conversions, reason = "safe")]
132        let z = slot.is_zero() as u64;
133        *slot = z.into();
134
135        Ok(OpcodeResult::Continue)
136    }
137}
138
139/// Implementation for the `AND` opcode.
140pub struct OpAndHandler;
141impl OpcodeHandler for OpAndHandler {
142    #[inline(always)]
143    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
144        vm.current_call_frame.increase_consumed_gas(gas_cost::AND)?;
145
146        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
147        *rhs = lhs & *rhs;
148
149        Ok(OpcodeResult::Continue)
150    }
151}
152
153/// Implementation for the `OR` opcode.
154pub struct OpOrHandler;
155impl OpcodeHandler for OpOrHandler {
156    #[inline(always)]
157    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
158        vm.current_call_frame.increase_consumed_gas(gas_cost::OR)?;
159
160        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
161        *rhs = lhs | *rhs;
162
163        Ok(OpcodeResult::Continue)
164    }
165}
166
167/// Implementation for the `XOR` opcode.
168pub struct OpXorHandler;
169impl OpcodeHandler for OpXorHandler {
170    #[inline(always)]
171    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
172        vm.current_call_frame.increase_consumed_gas(gas_cost::XOR)?;
173
174        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
175        *rhs = lhs ^ *rhs;
176
177        Ok(OpcodeResult::Continue)
178    }
179}
180
181/// Implementation for the `NOT` opcode.
182pub struct OpNotHandler;
183impl OpcodeHandler for OpNotHandler {
184    #[inline(always)]
185    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
186        vm.current_call_frame.increase_consumed_gas(gas_cost::NOT)?;
187
188        // In-place top mutation: no pop/push, no `offset` write.
189        let slot = vm.current_call_frame.stack.top_mut()?;
190        *slot = !*slot;
191
192        Ok(OpcodeResult::Continue)
193    }
194}
195
196/// Implementation for the `BYTE` opcode.
197pub struct OpByteHandler;
198impl OpcodeHandler for OpByteHandler {
199    #[inline(always)]
200    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
201        vm.current_call_frame
202            .increase_consumed_gas(gas_cost::BYTE)?;
203
204        let (index, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
205        let value = *slot;
206        *slot = match usize::try_from(index) {
207            #[expect(
208                clippy::arithmetic_side_effects,
209                reason = "x < 32 guard prevents overflow"
210            )]
211            Ok(x) if x < 32 => value.byte(31 - x).into(),
212            _ => U256::zero(),
213        };
214
215        Ok(OpcodeResult::Continue)
216    }
217}
218
219/// Implementation for the `SHL` opcode.
220pub struct OpShlHandler;
221impl OpcodeHandler for OpShlHandler {
222    #[inline(always)]
223    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
224        vm.current_call_frame.increase_consumed_gas(gas_cost::SHL)?;
225
226        let (shift_amount, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
227        let value = *slot;
228        *slot = match u8::try_from(shift_amount) {
229            #[expect(clippy::arithmetic_side_effects, reason = "U256 shift by u8 is safe")]
230            Ok(shift_amount) => value << shift_amount,
231            Err(_) => U256::zero(),
232        };
233
234        Ok(OpcodeResult::Continue)
235    }
236}
237
238/// Implementation for the `SHR` opcode.
239pub struct OpShrHandler;
240impl OpcodeHandler for OpShrHandler {
241    #[inline(always)]
242    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
243        vm.current_call_frame.increase_consumed_gas(gas_cost::SHR)?;
244
245        let (shift_amount, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
246        let value = *slot;
247        *slot = match u8::try_from(shift_amount) {
248            #[expect(clippy::arithmetic_side_effects, reason = "U256 shift by u8 is safe")]
249            Ok(shift_amount) => value >> shift_amount,
250            Err(_) => U256::zero(),
251        };
252
253        Ok(OpcodeResult::Continue)
254    }
255}
256
257/// Implementation for the `SAR` opcode.
258pub struct OpSarHandler;
259impl OpcodeHandler for OpSarHandler {
260    #[inline(always)]
261    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
262        vm.current_call_frame.increase_consumed_gas(gas_cost::SAR)?;
263
264        let (shift_amount, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
265        let value = *slot;
266        #[expect(clippy::arithmetic_side_effects, reason = "U256 shift by u8 is safe")]
267        {
268            *slot = match (u8::try_from(shift_amount), value.bit(255)) {
269                (Ok(shift_amount), false) => value >> shift_amount,
270                (Ok(shift_amount), true) => !(!value >> shift_amount),
271                (Err(_), false) => U256::zero(),
272                (Err(_), true) => U256::MAX,
273            };
274        }
275
276        Ok(OpcodeResult::Continue)
277    }
278}