miden_processor/fast/
stack_ops.rs

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