miden_processor/fast/
u32_ops.rs

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