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(
119        &mut self,
120        op_idx: usize,
121        err_ctx: &impl ErrorContext,
122    ) -> Result<(), ExecutionError> {
123        let clk = self.clk + op_idx;
124        self.u32_pop2_applyfn_push_results(
125            ZERO,
126            |first, second| {
127                if first == 0 {
128                    return Err(ExecutionError::divide_by_zero(clk, err_ctx));
129                }
130
131                // a/b = n*q + r for some n>=0 and 0<=r<b
132                let q = second / first;
133                let r = second - q * first;
134
135                // r is placed on top of the stack, followed by q
136                Ok((r, q))
137            },
138            err_ctx,
139        )
140    }
141
142    /// Analogous to `Process::op_u32and`.
143    #[inline(always)]
144    pub fn op_u32and(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
145        self.u32_pop2_applyfn_push(|a, b| a & b, err_ctx)
146    }
147
148    /// Analogous to `Process::op_u32xor`.
149    #[inline(always)]
150    pub fn op_u32xor(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
151        self.u32_pop2_applyfn_push(|a, b| a ^ b, err_ctx)
152    }
153
154    /// Analogous to `Process::op_u32assert2`.
155    #[inline(always)]
156    pub fn op_u32assert2(
157        &mut self,
158        err_code: Felt,
159        err_ctx: &impl ErrorContext,
160    ) -> Result<(), ExecutionError> {
161        self.u32_pop2_applyfn_push_results(err_code, |first, second| Ok((first, second)), err_ctx)
162    }
163
164    // HELPERS
165    // ----------------------------------------------------------------------------------------------
166
167    /// Equivalent to `pop2_applyfn_push`, but for u32 values.
168    #[inline(always)]
169    fn u32_pop2_applyfn_push(
170        &mut self,
171        f: impl FnOnce(u64, u64) -> u64,
172        err_ctx: &impl ErrorContext,
173    ) -> Result<(), ExecutionError> {
174        let b = self.stack_get(0).as_int();
175        let a = self.stack_get(1).as_int();
176
177        // Check that a and b are u32 values.
178        if b > u32::MAX as u64 {
179            return Err(ExecutionError::not_u32_value(Felt::new(b), ZERO, err_ctx));
180        }
181        if a > u32::MAX as u64 {
182            return Err(ExecutionError::not_u32_value(Felt::new(a), ZERO, err_ctx));
183        }
184
185        let result = f(a, b);
186        self.decrement_stack_size();
187        self.stack_write(0, Felt::new(result));
188
189        Ok(())
190    }
191
192    /// Pops 2 elements from the stack, applies the given function to them, and pushes the low/high
193    /// u32 values of the result back onto the stack.
194    ///
195    /// Specifically, this function
196    /// 1. pops the top two elements from the stack,
197    /// 2. applies the given function to them,
198    /// 3. splits the result into low/high u32 values, and
199    /// 4. pushes the low/high values back onto the stack.
200    ///
201    /// The size of the stack doesn't change.
202    #[inline(always)]
203    fn u32_pop2_applyfn_push_lowhigh(
204        &mut self,
205        f: impl FnOnce(u64, u64) -> u64,
206        err_ctx: &impl ErrorContext,
207    ) -> Result<(), ExecutionError> {
208        let b = self.stack_get(0).as_int();
209        let a = self.stack_get(1).as_int();
210
211        // Check that a and b are u32 values.
212        if a > u32::MAX as u64 {
213            return Err(ExecutionError::not_u32_value(Felt::new(a), ZERO, err_ctx));
214        }
215        if b > u32::MAX as u64 {
216            return Err(ExecutionError::not_u32_value(Felt::new(b), ZERO, err_ctx));
217        }
218
219        let result = Felt::new(f(a, b));
220        let (hi, lo) = split_element(result);
221
222        self.stack_write(0, hi);
223        self.stack_write(1, lo);
224        Ok(())
225    }
226
227    /// Pops 2 elements from the stack, applies the given function to them, and pushes the resulting
228    /// 2 u32 values back onto the stack.
229    ///
230    /// The size of the stack doesn't change.
231    #[inline(always)]
232    fn u32_pop2_applyfn_push_results(
233        &mut self,
234        err_code: Felt,
235        f: impl FnOnce(u64, u64) -> Result<(u64, u64), ExecutionError>,
236        err_ctx: &impl ErrorContext,
237    ) -> Result<(), ExecutionError> {
238        let first_old = self.stack_get(0).as_int();
239        let second_old = self.stack_get(1).as_int();
240
241        // Check that a and b are u32 values.
242        if first_old > u32::MAX as u64 {
243            return Err(ExecutionError::not_u32_value(Felt::new(first_old), err_code, err_ctx));
244        }
245        if second_old > u32::MAX as u64 {
246            return Err(ExecutionError::not_u32_value(Felt::new(second_old), err_code, err_ctx));
247        }
248
249        let (first_new, second_new) = f(first_old, second_old)?;
250
251        self.stack_write(0, Felt::new(first_new));
252        self.stack_write(1, Felt::new(second_new));
253        Ok(())
254    }
255}