xref: /wasmtime-44.0.1/winch/codegen/src/stack.rs (revision 9fcdc7a6)
1 use crate::isa::reg::Reg;
2 use std::collections::VecDeque;
3 
4 /// Value definition to be used within the shadow stack.
5 #[derive(Debug, Eq, PartialEq)]
6 pub(crate) enum Val {
7     /// I32 Constant.
8     I32(i32),
9     /// I64 Constant.
10     I64(i64),
11     /// A register.
12     Reg(Reg),
13     /// A local slot.
14     Local(u32),
15     /// Offset to a memory location.
16     Memory(u32),
17 }
18 
19 impl Val {
20     /// Create a new I32 constant value.
21     pub fn i32(v: i32) -> Self {
22         Self::I32(v)
23     }
24 
25     /// Create a new I64 constant value.
26     pub fn i64(v: i64) -> Self {
27         Self::I64(v)
28     }
29 
30     /// Create a new Reg value.
31     pub fn reg(r: Reg) -> Self {
32         Self::Reg(r)
33     }
34 
35     /// Create a new Local value.
36     pub fn local(index: u32) -> Self {
37         Self::Local(index)
38     }
39 
40     /// Check whether the value is a register.
41     pub fn is_reg(&self) -> bool {
42         match *self {
43             Self::Reg(_) => true,
44             _ => false,
45         }
46     }
47 
48     /// Check wheter the value is a memory offset.
49     pub fn is_mem(&self) -> bool {
50         match *self {
51             Self::Memory(_) => true,
52             _ => false,
53         }
54     }
55 
56     /// Get the register representation of the value.
57     ///
58     /// # Panics
59     /// This method will panic if the value is not a register.
60     pub fn get_reg(&self) -> Reg {
61         match self {
62             Self::Reg(r) => *r,
63             v => panic!("expected value {:?} to be a register", v),
64         }
65     }
66 
67     /// Get the integer representation of the value.
68     ///
69     /// # Panics
70     /// This method will panic if the value is not an i32.
71     pub fn get_i32(&self) -> i32 {
72         match self {
73             Self::I32(v) => *v,
74             v => panic!("expected value {:?} to be i32", v),
75         }
76     }
77 
78     /// Get the integer representation of the value.
79     ///
80     /// # Panics
81     /// This method will panic if the value is not an i64.
82     pub fn get_i64(&self) -> i64 {
83         match self {
84             Self::I64(v) => *v,
85             v => panic!("expected value {:?} to be i64", v),
86         }
87     }
88 
89     /// Check whether the value is an i32 constant.
90     pub fn is_i32_const(&self) -> bool {
91         match *self {
92             Self::I32(_) => true,
93             _ => false,
94         }
95     }
96 
97     /// Check whether the value is an i64 constant.
98     pub fn is_i64_const(&self) -> bool {
99         match *self {
100             Self::I64(_) => true,
101             _ => false,
102         }
103     }
104 }
105 
106 /// The shadow stack used for compilation.
107 #[derive(Default, Debug)]
108 pub(crate) struct Stack {
109     inner: VecDeque<Val>,
110 }
111 
112 impl Stack {
113     /// Allocate a new stack.
114     pub fn new() -> Self {
115         Self {
116             inner: Default::default(),
117         }
118     }
119 
120     /// Insert a new value at the specified index.
121     pub fn insert(&mut self, at: usize, val: Val) {
122         self.inner.insert(at, val);
123     }
124 
125     /// Get the length of the stack.
126     pub fn len(&self) -> usize {
127         self.inner.len()
128     }
129 
130     /// Push a value to the stack.
131     pub fn push(&mut self, val: Val) {
132         self.inner.push_back(val);
133     }
134 
135     /// Peek into the top in the stack.
136     pub fn peek(&self) -> Option<&Val> {
137         self.inner.back()
138     }
139 
140     /// Returns an iterator referencing the last n items of the stack,
141     /// in bottom-most to top-most order.
142     pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ {
143         let len = self.len();
144         assert!(n <= len);
145 
146         let partition = len - n;
147         self.inner.range(partition..)
148     }
149 
150     /// Pops the top element of the stack, if any.
151     pub fn pop(&mut self) -> Option<Val> {
152         self.inner.pop_back()
153     }
154 
155     /// Pops the element at the top of the stack if it is an i32 const;
156     /// returns `None` otherwise.
157     pub fn pop_i32_const(&mut self) -> Option<i32> {
158         match self.peek() {
159             Some(v) => v.is_i32_const().then(|| self.pop().unwrap().get_i32()),
160             _ => None,
161         }
162     }
163 
164     /// Pops the element at the top of the stack if it is an i64 const;
165     /// returns `None` otherwise.
166     pub fn pop_i64_const(&mut self) -> Option<i64> {
167         match self.peek() {
168             Some(v) => v.is_i64_const().then(|| self.pop().unwrap().get_i64()),
169             _ => None,
170         }
171     }
172 
173     /// Pops the element at the top of the stack if it is a register;
174     /// returns `None` otherwise.
175     pub fn pop_reg(&mut self) -> Option<Reg> {
176         match self.peek() {
177             Some(v) => v.is_reg().then(|| self.pop().unwrap().get_reg()),
178             _ => None,
179         }
180     }
181 
182     /// Pops the given register if it is at the top of the stack;
183     /// returns `None` otherwise.
184     pub fn pop_named_reg(&mut self, reg: Reg) -> Option<Reg> {
185         match self.peek() {
186             Some(v) => (v.is_reg() && v.get_reg() == reg).then(|| self.pop().unwrap().get_reg()),
187             _ => None,
188         }
189     }
190 
191     /// Get a mutable reference to the inner stack representation.
192     pub fn inner_mut(&mut self) -> &mut VecDeque<Val> {
193         &mut self.inner
194     }
195 }
196 
197 #[cfg(test)]
198 mod tests {
199     use super::{Stack, Val};
200     use crate::isa::reg::Reg;
201 
202     #[test]
203     fn test_pop_i32_const() {
204         let mut stack = Stack::new();
205         stack.push(Val::i32(33i32));
206         assert_eq!(33, stack.pop_i32_const().unwrap());
207 
208         stack.push(Val::local(10));
209         assert!(stack.pop_i32_const().is_none());
210     }
211 
212     #[test]
213     fn test_pop_reg() {
214         let mut stack = Stack::new();
215         let reg = Reg::int(2usize);
216         stack.push(Val::reg(reg));
217         stack.push(Val::i32(4));
218 
219         assert_eq!(None, stack.pop_reg());
220         let _ = stack.pop().unwrap();
221         assert_eq!(reg, stack.pop_reg().unwrap());
222     }
223 
224     #[test]
225     fn test_pop_named_reg() {
226         let mut stack = Stack::new();
227         let reg = Reg::int(2usize);
228         stack.push(Val::reg(reg));
229         stack.push(Val::reg(Reg::int(4)));
230 
231         assert_eq!(None, stack.pop_named_reg(reg));
232         let _ = stack.pop().unwrap();
233         assert_eq!(reg, stack.pop_named_reg(reg).unwrap());
234     }
235 }
236