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