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