xref: /wasmtime-44.0.1/winch/codegen/src/stack.rs (revision af4d94c8)
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     /// Get the length of the stack.
121     pub fn len(&self) -> usize {
122         self.inner.len()
123     }
124 
125     /// Push a value to the stack.
126     pub fn push(&mut self, val: Val) {
127         self.inner.push_back(val);
128     }
129 
130     /// Peek into the top in the stack.
131     pub fn peek(&self) -> Option<&Val> {
132         self.inner.back()
133     }
134 
135     /// Returns an iterator referencing the last n items of the stack,
136     /// in bottom-most to top-most order.
137     pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ {
138         let len = self.len();
139         assert!(n <= len);
140 
141         let partition = len - n;
142         self.inner.range(partition..)
143     }
144 
145     /// Pops the top element of the stack, if any.
146     pub fn pop(&mut self) -> Option<Val> {
147         self.inner.pop_back()
148     }
149 
150     /// Pops the element at the top of the stack if it is an i32 const;
151     /// returns `None` otherwise.
152     pub fn pop_i32_const(&mut self) -> Option<i32> {
153         match self.peek() {
154             Some(v) => v.is_i32_const().then(|| self.pop().unwrap().get_i32()),
155             _ => None,
156         }
157     }
158 
159     /// Pops the element at the top of the stack if it is an i64 const;
160     /// returns `None` otherwise.
161     pub fn pop_i64_const(&mut self) -> Option<i64> {
162         match self.peek() {
163             Some(v) => v.is_i64_const().then(|| self.pop().unwrap().get_i64()),
164             _ => None,
165         }
166     }
167 
168     /// Pops the element at the top of the stack if it is a register;
169     /// returns `None` otherwise.
170     pub fn pop_reg(&mut self) -> Option<Reg> {
171         match self.peek() {
172             Some(v) => v.is_reg().then(|| self.pop().unwrap().get_reg()),
173             _ => None,
174         }
175     }
176 
177     /// Pops the given register if it is at the top of the stack;
178     /// returns `None` otherwise.
179     pub fn pop_named_reg(&mut self, reg: Reg) -> Option<Reg> {
180         match self.peek() {
181             Some(v) => (v.is_reg() && v.get_reg() == reg).then(|| self.pop().unwrap().get_reg()),
182             _ => None,
183         }
184     }
185 
186     /// Get a mutable reference to the inner stack representation.
187     pub fn inner_mut(&mut self) -> &mut VecDeque<Val> {
188         &mut self.inner
189     }
190 }
191 
192 #[cfg(test)]
193 mod tests {
194     use super::{Stack, Val};
195     use crate::isa::reg::Reg;
196 
197     #[test]
198     fn test_pop_i32_const() {
199         let mut stack = Stack::new();
200         stack.push(Val::i32(33i32));
201         assert_eq!(33, stack.pop_i32_const().unwrap());
202 
203         stack.push(Val::local(10));
204         assert!(stack.pop_i32_const().is_none());
205     }
206 
207     #[test]
208     fn test_pop_reg() {
209         let mut stack = Stack::new();
210         let reg = Reg::int(2usize);
211         stack.push(Val::reg(reg));
212         stack.push(Val::i32(4));
213 
214         assert_eq!(None, stack.pop_reg());
215         let _ = stack.pop().unwrap();
216         assert_eq!(reg, stack.pop_reg().unwrap());
217     }
218 
219     #[test]
220     fn test_pop_named_reg() {
221         let mut stack = Stack::new();
222         let reg = Reg::int(2usize);
223         stack.push(Val::reg(reg));
224         stack.push(Val::reg(Reg::int(4)));
225 
226         assert_eq!(None, stack.pop_named_reg(reg));
227         let _ = stack.pop().unwrap();
228         assert_eq!(reg, stack.pop_named_reg(reg).unwrap());
229     }
230 }
231