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}