miden_processor/fast/
u32_ops.rs

1use vm_core::{Felt, ZERO};
2
3use super::FastProcessor;
4use crate::{ErrorContext, ExecutionError, utils::split_element};
5
6impl FastProcessor {
7    /// Analogous to `Process::op_u32split`.
8    pub fn op_u32split(&mut self) -> Result<(), ExecutionError> {
9        let top = self.stack_get(0);
10        let (hi, lo) = split_element(top);
11
12        self.increment_stack_size();
13        self.stack_write(0, hi);
14        self.stack_write(1, lo);
15
16        Ok(())
17    }
18
19    /// Analogous to `Process::op_u32add`.
20    pub fn op_u32add(&mut self) -> Result<(), ExecutionError> {
21        self.u32_pop2_applyfn_push_lowhigh(|a, b| a + b)
22    }
23
24    /// Analogous to `Process::op_u32add3`.
25    ///
26    /// Pops three elements off the stack, adds them, splits the result into low and high 32-bit
27    /// values, and pushes these values back onto the stack.
28    ///
29    /// The size of the stack is decremented by 1.
30    pub fn op_u32add3(&mut self) -> Result<(), ExecutionError> {
31        let (sum_hi, sum_lo) = {
32            let c = self.stack_get(0).as_int();
33            let b = self.stack_get(1).as_int();
34            let a = self.stack_get(2).as_int();
35
36            // Check that a, b, and c are u32 values.
37            if a > u32::MAX as u64 {
38                return Err(ExecutionError::not_u32_value(
39                    Felt::new(a),
40                    ZERO,
41                    &ErrorContext::default(),
42                ));
43            }
44            if b > u32::MAX as u64 {
45                return Err(ExecutionError::not_u32_value(
46                    Felt::new(b),
47                    ZERO,
48                    &ErrorContext::default(),
49                ));
50            }
51            if c > u32::MAX as u64 {
52                return Err(ExecutionError::not_u32_value(
53                    Felt::new(c),
54                    ZERO,
55                    &ErrorContext::default(),
56                ));
57            }
58            let result = Felt::new(a + b + c);
59            split_element(result)
60        };
61
62        // write the high 32 bits to the new top of the stack, and low 32 bits after
63        self.decrement_stack_size();
64        self.stack_write(0, sum_hi);
65        self.stack_write(1, sum_lo);
66        Ok(())
67    }
68
69    /// Analogous to `Process::op_u32sub`.
70    pub fn op_u32sub(&mut self, op_idx: usize) -> Result<(), ExecutionError> {
71        let op_idx = Felt::from(op_idx as u32);
72        self.u32_pop2_applyfn_push_results(op_idx, |first_old, second_old| {
73            let result = second_old.wrapping_sub(first_old);
74            let first_new = result >> 63;
75            let second_new = result & u32::MAX as u64;
76
77            Ok((first_new, second_new))
78        })
79    }
80
81    /// Analogous to `Process::op_u32mul`.
82    pub fn op_u32mul(&mut self) -> Result<(), ExecutionError> {
83        self.u32_pop2_applyfn_push_lowhigh(|a, b| a * b)
84    }
85
86    /// Analogous to `Process::op_u32madd`.
87    ///
88    /// Pops three elements off the stack, multiplies the first two and adds the third element to
89    /// the result, splits the result into low and high 32-bit values, and pushes these values
90    /// back onto the stack.
91    pub fn op_u32madd(&mut self) -> Result<(), ExecutionError> {
92        let (result_hi, result_lo) = {
93            let b = self.stack_get(0).as_int();
94            let a = self.stack_get(1).as_int();
95            let c = self.stack_get(2).as_int();
96
97            // Check that a, b, and c are u32 values.
98            if b > u32::MAX as u64 {
99                return Err(ExecutionError::not_u32_value(
100                    Felt::new(a),
101                    ZERO,
102                    &ErrorContext::default(),
103                ));
104            }
105            if a > u32::MAX as u64 {
106                return Err(ExecutionError::not_u32_value(
107                    Felt::new(b),
108                    ZERO,
109                    &ErrorContext::default(),
110                ));
111            }
112            if c > u32::MAX as u64 {
113                return Err(ExecutionError::not_u32_value(
114                    Felt::new(c),
115                    ZERO,
116                    &ErrorContext::default(),
117                ));
118            }
119            let result = Felt::new(a * b + c);
120            split_element(result)
121        };
122
123        // write the high 32 bits to the new top of the stack, and low 32 bits after
124        self.decrement_stack_size();
125        self.stack_write(0, result_hi);
126        self.stack_write(1, result_lo);
127        Ok(())
128    }
129
130    /// Analogous to `Process::op_u32div`.
131    pub fn op_u32div(&mut self, op_idx: usize) -> Result<(), ExecutionError> {
132        let clk = self.clk + op_idx;
133        self.u32_pop2_applyfn_push_results(ZERO, |first, second| {
134            if first == 0 {
135                return Err(ExecutionError::divide_by_zero(clk, &ErrorContext::default()));
136            }
137
138            // a/b = n*q + r for some n>=0 and 0<=r<b
139            let q = second / first;
140            let r = second - q * first;
141
142            // r is placed on top of the stack, followed by q
143            Ok((r, q))
144        })
145    }
146
147    /// Analogous to `Process::op_u32and`.
148    pub fn op_u32and(&mut self) -> Result<(), ExecutionError> {
149        self.u32_pop2_applyfn_push(|a, b| a & b)
150    }
151
152    /// Analogous to `Process::op_u32xor`.
153    pub fn op_u32xor(&mut self) -> Result<(), ExecutionError> {
154        self.u32_pop2_applyfn_push(|a, b| a ^ b)
155    }
156
157    /// Analogous to `Process::op_u32assert2`.
158    pub fn op_u32assert2(&mut self, err_code: Felt) -> Result<(), ExecutionError> {
159        self.u32_pop2_applyfn_push_results(err_code, |first, second| Ok((first, second)))
160    }
161
162    // HELPERS
163    // ----------------------------------------------------------------------------------------------
164
165    /// Equivalent to `pop2_applyfn_push`, but for u32 values.
166    fn u32_pop2_applyfn_push(
167        &mut self,
168        f: impl FnOnce(u64, u64) -> u64,
169    ) -> Result<(), ExecutionError> {
170        let b = self.stack_get(0).as_int();
171        let a = self.stack_get(1).as_int();
172
173        // Check that a and b are u32 values.
174        if b > u32::MAX as u64 {
175            return Err(ExecutionError::not_u32_value(
176                Felt::new(b),
177                ZERO,
178                &ErrorContext::default(),
179            ));
180        }
181        if a > u32::MAX as u64 {
182            return Err(ExecutionError::not_u32_value(
183                Felt::new(a),
184                ZERO,
185                &ErrorContext::default(),
186            ));
187        }
188
189        let result = f(a, b);
190        self.decrement_stack_size();
191        self.stack_write(0, Felt::new(result));
192
193        Ok(())
194    }
195
196    /// Pops 2 elements from the stack, applies the given function to them, and pushes the low/high
197    /// u32 values of the result back onto the stack.
198    ///
199    /// Specifically, this function
200    /// 1. pops the top two elements from the stack,
201    /// 2. applies the given function to them,
202    /// 3. splits the result into low/high u32 values, and
203    /// 4. pushes the low/high values back onto the stack.
204    ///
205    /// The size of the stack doesn't change.
206    #[inline(always)]
207    fn u32_pop2_applyfn_push_lowhigh(
208        &mut self,
209        f: impl FnOnce(u64, u64) -> u64,
210    ) -> Result<(), ExecutionError> {
211        let b = self.stack_get(0).as_int();
212        let a = self.stack_get(1).as_int();
213
214        // Check that a and b are u32 values.
215        if a > u32::MAX as u64 {
216            return Err(ExecutionError::not_u32_value(
217                Felt::new(a),
218                ZERO,
219                &ErrorContext::default(),
220            ));
221        }
222        if b > u32::MAX as u64 {
223            return Err(ExecutionError::not_u32_value(
224                Felt::new(b),
225                ZERO,
226                &ErrorContext::default(),
227            ));
228        }
229
230        let result = Felt::new(f(a, b));
231        let (hi, lo) = split_element(result);
232
233        self.stack_write(0, hi);
234        self.stack_write(1, lo);
235        Ok(())
236    }
237
238    /// Pops 2 elements from the stack, applies the given function to them, and pushes the resulting
239    /// 2 u32 values back onto the stack.
240    ///
241    /// The size of the stack doesn't change.
242    #[inline(always)]
243    fn u32_pop2_applyfn_push_results(
244        &mut self,
245        err_code: Felt,
246        f: impl FnOnce(u64, u64) -> Result<(u64, u64), ExecutionError>,
247    ) -> Result<(), ExecutionError> {
248        let first_old = self.stack_get(0).as_int();
249        let second_old = self.stack_get(1).as_int();
250
251        // Check that a and b are u32 values.
252        if first_old > u32::MAX as u64 {
253            return Err(ExecutionError::not_u32_value(
254                Felt::new(first_old),
255                err_code,
256                &ErrorContext::default(),
257            ));
258        }
259        if second_old > u32::MAX as u64 {
260            return Err(ExecutionError::not_u32_value(
261                Felt::new(second_old),
262                err_code,
263                &ErrorContext::default(),
264            ));
265        }
266
267        let (first_new, second_new) = f(first_old, second_old)?;
268
269        self.stack_write(0, Felt::new(first_new));
270        self.stack_write(1, Felt::new(second_new));
271        Ok(())
272    }
273}