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