xref: /wasmtime-44.0.1/winch/codegen/src/stack.rs (revision 55f9a4bd)
1 use crate::{isa::reg::Reg, masm::StackSlot};
2 use std::collections::VecDeque;
3 use wasmparser::{Ieee32, Ieee64};
4 use wasmtime_environ::WasmType;
5 
6 /// A typed register value used to track register values in the value
7 /// stack.
8 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
9 pub struct TypedReg {
10     /// The physical register.
11     pub reg: Reg,
12     /// The type associated to the physical register.
13     pub ty: WasmType,
14 }
15 
16 impl TypedReg {
17     /// Create a new [`TypedReg`].
18     pub fn new(ty: WasmType, reg: Reg) -> Self {
19         Self { ty, reg }
20     }
21 
22     /// Create an i64 [`TypedReg`].
23     pub fn i64(reg: Reg) -> Self {
24         Self {
25             ty: WasmType::I64,
26             reg,
27         }
28     }
29 
30     /// Create an i32 [`TypedReg`].
31     pub fn i32(reg: Reg) -> Self {
32         Self {
33             ty: WasmType::I32,
34             reg,
35         }
36     }
37 }
38 
39 impl From<TypedReg> for Reg {
40     fn from(tr: TypedReg) -> Self {
41         tr.reg
42     }
43 }
44 
45 /// A local value.
46 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
47 pub struct Local {
48     /// The index of the local.
49     pub index: u32,
50     /// The type of the local.
51     pub ty: WasmType,
52 }
53 
54 /// A memory value.
55 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
56 pub struct Memory {
57     /// The type associated with the memory offset.
58     pub ty: WasmType,
59     /// The stack slot corresponding to the memory value.
60     pub slot: StackSlot,
61 }
62 
63 /// Value definition to be used within the shadow stack.
64 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
65 pub(crate) enum Val {
66     /// I32 Constant.
67     I32(i32),
68     /// I64 Constant.
69     I64(i64),
70     /// F32 Constant.
71     F32(Ieee32),
72     /// F64 Constant.
73     F64(Ieee64),
74     /// A register value.
75     Reg(TypedReg),
76     /// A local slot.
77     Local(Local),
78     /// Offset to a memory location.
79     Memory(Memory),
80 }
81 
82 impl From<TypedReg> for Val {
83     fn from(tr: TypedReg) -> Self {
84         Val::Reg(tr)
85     }
86 }
87 
88 impl From<Local> for Val {
89     fn from(local: Local) -> Self {
90         Val::Local(local)
91     }
92 }
93 
94 impl From<Memory> for Val {
95     fn from(mem: Memory) -> Self {
96         Val::Memory(mem)
97     }
98 }
99 
100 impl TryFrom<u32> for Val {
101     type Error = anyhow::Error;
102     fn try_from(value: u32) -> Result<Self, Self::Error> {
103         i32::try_from(value).map(Val::i32).map_err(Into::into)
104     }
105 }
106 
107 impl Val {
108     /// Create a new I32 constant value.
109     pub fn i32(v: i32) -> Self {
110         Self::I32(v)
111     }
112 
113     /// Create a new I64 constant value.
114     pub fn i64(v: i64) -> Self {
115         Self::I64(v)
116     }
117 
118     /// Create a new F32 constant value.
119     pub fn f32(v: Ieee32) -> Self {
120         Self::F32(v)
121     }
122 
123     pub fn f64(v: Ieee64) -> Self {
124         Self::F64(v)
125     }
126 
127     /// Create a new Reg value.
128     pub fn reg(reg: Reg, ty: WasmType) -> Self {
129         Self::Reg(TypedReg { reg, ty })
130     }
131 
132     /// Create a new Local value.
133     pub fn local(index: u32, ty: WasmType) -> Self {
134         Self::Local(Local { index, ty })
135     }
136 
137     /// Create a Memory value.
138     pub fn mem(ty: WasmType, slot: StackSlot) -> Self {
139         Self::Memory(Memory { ty, slot })
140     }
141 
142     /// Check whether the value is a register.
143     pub fn is_reg(&self) -> bool {
144         match *self {
145             Self::Reg(_) => true,
146             _ => false,
147         }
148     }
149 
150     /// Check wheter the value is a memory offset.
151     pub fn is_mem(&self) -> bool {
152         match *self {
153             Self::Memory(_) => true,
154             _ => false,
155         }
156     }
157 
158     /// Check whether the value is a constant.
159     pub fn is_const(&self) -> bool {
160         match *self {
161             Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) => true,
162             _ => false,
163         }
164     }
165 
166     /// Check whether the value is local with a particular index.
167     pub fn is_local_at_index(&self, index: u32) -> bool {
168         match *self {
169             Self::Local(Local { index: i, .. }) if i == index => true,
170             _ => false,
171         }
172     }
173 
174     /// Get the register representation of the value.
175     ///
176     /// # Panics
177     /// This method will panic if the value is not a register.
178     pub fn unwrap_reg(&self) -> TypedReg {
179         match self {
180             Self::Reg(tr) => *tr,
181             v => panic!("expected value {:?} to be a register", v),
182         }
183     }
184 
185     /// Get the integer representation of the value.
186     ///
187     /// # Panics
188     /// This method will panic if the value is not an i32.
189     pub fn unwrap_i32(&self) -> i32 {
190         match self {
191             Self::I32(v) => *v,
192             v => panic!("expected value {:?} to be i32", v),
193         }
194     }
195 
196     /// Get the integer representation of the value.
197     ///
198     /// # Panics
199     /// This method will panic if the value is not an i64.
200     pub fn unwrap_i64(&self) -> i64 {
201         match self {
202             Self::I64(v) => *v,
203             v => panic!("expected value {:?} to be i64", v),
204         }
205     }
206 
207     /// Returns the underlying memory value if it is one, panics otherwise.
208     pub fn unwrap_mem(&self) -> Memory {
209         match self {
210             Self::Memory(m) => *m,
211             v => panic!("expected value {:?} to be a Memory", v),
212         }
213     }
214 
215     /// Check whether the value is an i32 constant.
216     pub fn is_i32_const(&self) -> bool {
217         match *self {
218             Self::I32(_) => true,
219             _ => false,
220         }
221     }
222 
223     /// Check whether the value is an i64 constant.
224     pub fn is_i64_const(&self) -> bool {
225         match *self {
226             Self::I64(_) => true,
227             _ => false,
228         }
229     }
230 
231     /// Get the type of the value.
232     pub fn ty(&self) -> WasmType {
233         match self {
234             Val::I32(_) => WasmType::I32,
235             Val::I64(_) => WasmType::I64,
236             Val::F32(_) => WasmType::F32,
237             Val::F64(_) => WasmType::F64,
238             Val::Reg(r) => r.ty,
239             Val::Memory(m) => m.ty,
240             Val::Local(l) => l.ty,
241         }
242     }
243 }
244 
245 /// The shadow stack used for compilation.
246 #[derive(Default, Debug)]
247 pub(crate) struct Stack {
248     inner: VecDeque<Val>,
249 }
250 
251 impl Stack {
252     /// Allocate a new stack.
253     pub fn new() -> Self {
254         Self {
255             inner: Default::default(),
256         }
257     }
258 
259     /// Returns true if the stack contains a local with the provided index
260     /// except if the only time the local appears is the top element.
261     pub fn contains_latent_local(&self, index: u32) -> bool {
262         self.inner
263             .iter()
264             // Iterate top-to-bottom so we can skip the top element and stop
265             // when we see a memory element.
266             .rev()
267             // The local is not latent if it's the top element because the top
268             // element will be popped next which materializes the local.
269             .skip(1)
270             // Stop when we see a memory element because that marks where we
271             // spilled up to so there will not be any locals past this point.
272             .take_while(|v| !v.is_mem())
273             .any(|v| v.is_local_at_index(index))
274     }
275 
276     /// Extend the stack with the given elements.
277     pub fn extend(&mut self, values: impl IntoIterator<Item = Val>) {
278         self.inner.extend(values);
279     }
280 
281     /// Inserts many values at the given index.
282     pub fn insert_many(&mut self, at: usize, values: impl IntoIterator<Item = Val>) {
283         debug_assert!(at <= self.len());
284         // If last, simply extend.
285         if at == self.inner.len() {
286             self.inner.extend(values);
287         } else {
288             let mut tail = self.inner.split_off(at);
289             self.inner.extend(values);
290             self.inner.append(&mut tail);
291         }
292     }
293 
294     /// Get the length of the stack.
295     pub fn len(&self) -> usize {
296         self.inner.len()
297     }
298 
299     /// Push a value to the stack.
300     pub fn push(&mut self, val: Val) {
301         self.inner.push_back(val);
302     }
303 
304     /// Peek into the top in the stack.
305     pub fn peek(&self) -> Option<&Val> {
306         self.inner.back()
307     }
308 
309     /// Returns an iterator referencing the last n items of the stack,
310     /// in bottom-most to top-most order.
311     pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ {
312         let len = self.len();
313         assert!(n <= len);
314 
315         let partition = len - n;
316         self.inner.range(partition..)
317     }
318 
319     /// Duplicates the top `n` elements of the stack.
320     // Will be needed for control flow, it's just not integrated yet.
321     #[allow(dead_code)]
322     pub fn dup(&mut self, n: usize) {
323         let len = self.len();
324         assert!(n <= len);
325         let partition = len - n;
326 
327         if n > 0 {
328             for e in partition..len {
329                 if let Some(v) = self.inner.get(e) {
330                     self.push(*v)
331                 }
332             }
333         }
334     }
335 
336     /// Pops the top element of the stack, if any.
337     pub fn pop(&mut self) -> Option<Val> {
338         self.inner.pop_back()
339     }
340 
341     /// Pops the element at the top of the stack if it is an i32 const;
342     /// returns `None` otherwise.
343     pub fn pop_i32_const(&mut self) -> Option<i32> {
344         match self.peek() {
345             Some(v) => v.is_i32_const().then(|| self.pop().unwrap().unwrap_i32()),
346             _ => None,
347         }
348     }
349 
350     /// Pops the element at the top of the stack if it is an i64 const;
351     /// returns `None` otherwise.
352     pub fn pop_i64_const(&mut self) -> Option<i64> {
353         match self.peek() {
354             Some(v) => v.is_i64_const().then(|| self.pop().unwrap().unwrap_i64()),
355             _ => None,
356         }
357     }
358 
359     /// Pops the element at the top of the stack if it is a register;
360     /// returns `None` otherwise.
361     pub fn pop_reg(&mut self) -> Option<TypedReg> {
362         match self.peek() {
363             Some(v) => v.is_reg().then(|| self.pop().unwrap().unwrap_reg()),
364             _ => None,
365         }
366     }
367 
368     /// Pops the given register if it is at the top of the stack;
369     /// returns `None` otherwise.
370     pub fn pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg> {
371         match self.peek() {
372             Some(v) => {
373                 (v.is_reg() && v.unwrap_reg().reg == reg).then(|| self.pop().unwrap().unwrap_reg())
374             }
375             _ => None,
376         }
377     }
378 
379     /// Get a mutable reference to the inner stack representation.
380     pub fn inner_mut(&mut self) -> &mut VecDeque<Val> {
381         &mut self.inner
382     }
383 
384     /// Calculates the size of, in bytes, of the top n [Memory] entries
385     /// in the value stack.
386     pub fn sizeof(&self, top: usize) -> u32 {
387         self.peekn(top).fold(0, |acc, v| {
388             if v.is_mem() {
389                 acc + v.unwrap_mem().slot.size
390             } else {
391                 acc
392             }
393         })
394     }
395 }
396 
397 #[cfg(test)]
398 mod tests {
399     use super::{Stack, Val};
400     use crate::isa::reg::Reg;
401     use wasmtime_environ::WasmType;
402 
403     #[test]
404     fn test_pop_i32_const() {
405         let mut stack = Stack::new();
406         stack.push(Val::i32(33i32));
407         assert_eq!(33, stack.pop_i32_const().unwrap());
408 
409         stack.push(Val::local(10, WasmType::I32));
410         assert!(stack.pop_i32_const().is_none());
411     }
412 
413     #[test]
414     fn test_pop_reg() {
415         let mut stack = Stack::new();
416         let reg = Reg::int(2usize);
417         stack.push(Val::reg(reg, WasmType::I32));
418         stack.push(Val::i32(4));
419 
420         assert_eq!(None, stack.pop_reg());
421         let _ = stack.pop().unwrap();
422         assert_eq!(reg, stack.pop_reg().unwrap().reg);
423     }
424 
425     #[test]
426     fn test_pop_named_reg() {
427         let mut stack = Stack::new();
428         let reg = Reg::int(2usize);
429         stack.push(Val::reg(reg, WasmType::I32));
430         stack.push(Val::reg(Reg::int(4), WasmType::I32));
431 
432         assert_eq!(None, stack.pop_named_reg(reg));
433         let _ = stack.pop().unwrap();
434         assert_eq!(reg, stack.pop_named_reg(reg).unwrap().reg);
435     }
436 }
437