miden_processor/fast/
stack_ops.rs

1use miden_core::{WORD_SIZE, ZERO};
2
3use super::FastProcessor;
4use crate::{ErrorContext, ExecutionError};
5
6impl FastProcessor {
7    /// Analogous to `Process::op_pad`.
8    pub fn op_pad(&mut self) -> Result<(), ExecutionError> {
9        self.increment_stack_size()?;
10        self.stack_write(0, ZERO);
11        Ok(())
12    }
13
14    /// Analogous to `Process::op_swap`.
15    pub fn op_swap(&mut self) {
16        self.stack_swap(0, 1);
17    }
18
19    /// Analogous to `Process::op_swapdw`.
20    pub fn op_swap_double_word(&mut self) {
21        self.stack_swap(0, 8);
22        self.stack_swap(1, 9);
23        self.stack_swap(2, 10);
24        self.stack_swap(3, 11);
25        self.stack_swap(4, 12);
26        self.stack_swap(5, 13);
27        self.stack_swap(6, 14);
28        self.stack_swap(7, 15);
29    }
30
31    /// Rotates the top `n` elements of the stack to the left by 1.
32    ///
33    /// For example, if the stack is [a, b, c, d], with `d` at the top, then `rotate_left(3)` will
34    /// result in the top 3 elements being rotated left: [a, c, d, b].
35    ///
36    /// This operation is useful for implementing the `movup` instructions.
37    ///
38    /// The stack size doesn't change.
39    ///
40    /// Note: This method doesn't use the `stack_get()` and `stack_write()` methods because it is
41    /// more efficient to directly manipulate the stack array (~10% performance difference).
42    #[inline(always)]
43    pub fn rotate_left(&mut self, n: usize) {
44        let rotation_bot_index = self.stack_top_idx - n;
45        let new_stack_top_element = self.stack[rotation_bot_index];
46
47        // shift the top n elements down by 1, starting from the bottom of the rotation.
48        for i in 0..n - 1 {
49            self.stack[rotation_bot_index + i] = self.stack[rotation_bot_index + i + 1];
50        }
51
52        // Set the top element (which comes from the bottom of the rotation).
53        self.stack_write(0, new_stack_top_element);
54    }
55
56    /// Rotates the top `n` elements of the stack to the right by 1.
57    ///
58    /// Analogous to `rotate_left`, but in the opposite direction.
59    ///
60    /// Note: This method doesn't use the `stack_get()` and `stack_write()` methods because it is
61    /// more efficient to directly manipulate the stack array (~10% performance difference).
62    #[inline(always)]
63    pub fn rotate_right(&mut self, n: usize) {
64        let rotation_bot_index = self.stack_top_idx - n;
65        let new_stack_bot_element = self.stack[self.stack_top_idx - 1];
66
67        // shift the top n elements up by 1, starting from the top of the rotation.
68        for i in 1..n {
69            self.stack[self.stack_top_idx - i] = self.stack[self.stack_top_idx - i - 1];
70        }
71
72        // Set the bot element (which comes from the top of the rotation).
73        self.stack[rotation_bot_index] = new_stack_bot_element;
74    }
75
76    /// Duplicates the n'th element from the top of the stack to the top of the stack.
77    ///
78    /// The size of the stack is incremented by 1.
79    #[inline(always)]
80    pub fn dup_nth(&mut self, n: usize) -> Result<(), ExecutionError> {
81        let to_dup = self.stack_get(n);
82        self.increment_stack_size()?;
83        self.stack_write(0, to_dup);
84
85        Ok(())
86    }
87
88    /// Swaps the nth word from the top of the stack with the top word of the stack.
89    ///
90    /// Valid values of `n` are 1, 2, and 3.
91    pub fn swapw_nth(&mut self, n: usize) {
92        // For example, for n=3, the stack words and variables look like:
93        //    3     2     1     0
94        // | ... | ... | ... | ... |
95        // ^                 ^
96        // nth_word       top_word
97        let (rest_of_stack, top_word) = self.stack.split_at_mut(self.stack_top_idx - WORD_SIZE);
98        let (_, nth_word) = rest_of_stack.split_at_mut(rest_of_stack.len() - n * WORD_SIZE);
99
100        nth_word[0..WORD_SIZE].swap_with_slice(&mut top_word[0..WORD_SIZE]);
101    }
102
103    /// Analogous to `Process::op_cswap`.
104    pub fn op_cswap(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
105        let condition = self.stack_get(0);
106        self.decrement_stack_size();
107
108        match condition.as_int() {
109            0 => {
110                // do nothing, a and b are already in the right place
111            },
112            1 => {
113                self.stack_swap(0, 1);
114            },
115            _ => {
116                return Err(ExecutionError::not_binary_value_op(condition, err_ctx));
117            },
118        }
119
120        Ok(())
121    }
122
123    /// Analogous to `Process::op_cswapw`.
124    pub fn op_cswapw(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
125        let condition = self.stack_get(0);
126        self.decrement_stack_size();
127
128        match condition.as_int() {
129            0 => {
130                // do nothing, the words are already in the right place
131            },
132            1 => {
133                self.stack_swap(0, 4);
134                self.stack_swap(1, 5);
135                self.stack_swap(2, 6);
136                self.stack_swap(3, 7);
137            },
138            _ => {
139                return Err(ExecutionError::not_binary_value_op(condition, err_ctx));
140            },
141        }
142
143        Ok(())
144    }
145}