winch_codegen/
stack.rs

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