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