xref: /wasmtime-44.0.1/winch/codegen/src/stack.rs (revision 7c5c7e4b)
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     /// Get the register representation of the value.
49     ///
50     /// # Panics
51     /// This method will panic if the value is not a register.
52     pub fn get_reg(&self) -> Reg {
53         match self {
54             Self::Reg(r) => *r,
55             v => panic!("expected value {:?} to be a register", v),
56         }
57     }
58 
59     /// Get the integer representation of the value.
60     ///
61     /// # Panics
62     /// This method will panic if the value is not an i32.
63     pub fn get_i32(&self) -> i32 {
64         match self {
65             Self::I32(v) => *v,
66             v => panic!("expected value {:?} to be i32", v),
67         }
68     }
69 
70     /// Get the integer representation of the value.
71     ///
72     /// # Panics
73     /// This method will panic if the value is not an i64.
74     pub fn get_i64(&self) -> i64 {
75         match self {
76             Self::I64(v) => *v,
77             v => panic!("expected value {:?} to be i64", v),
78         }
79     }
80 
81     /// Check whether the value is an i32 constant.
82     pub fn is_i32_const(&self) -> bool {
83         match *self {
84             Self::I32(_) => true,
85             _ => false,
86         }
87     }
88 
89     /// Check whether the value is an i64 constant.
90     pub fn is_i64_const(&self) -> bool {
91         match *self {
92             Self::I64(_) => true,
93             _ => false,
94         }
95     }
96 }
97 
98 /// The shadow stack used for compilation.
99 #[derive(Default, Debug)]
100 pub(crate) struct Stack {
101     inner: VecDeque<Val>,
102 }
103 
104 impl Stack {
105     /// Allocate a new stack.
106     pub fn new() -> Self {
107         Self {
108             inner: Default::default(),
109         }
110     }
111 
112     /// Push a value to the stack.
113     pub fn push(&mut self, val: Val) {
114         self.inner.push_back(val);
115     }
116 
117     /// Peek into the top in the stack.
118     pub fn peek(&self) -> Option<&Val> {
119         self.inner.back()
120     }
121 
122     /// Pops the top element of the stack, if any.
123     pub fn pop(&mut self) -> Option<Val> {
124         self.inner.pop_back()
125     }
126 
127     /// Pops the element at the top of the stack if it is an i32 const;
128     /// returns `None` otherwise.
129     pub fn pop_i32_const(&mut self) -> Option<i32> {
130         match self.peek() {
131             Some(v) => v.is_i32_const().then(|| self.pop().unwrap().get_i32()),
132             _ => None,
133         }
134     }
135 
136     /// Pops the element at the top of the stack if it is an i64 const;
137     /// returns `None` otherwise.
138     pub fn pop_i64_const(&mut self) -> Option<i64> {
139         match self.peek() {
140             Some(v) => v.is_i64_const().then(|| self.pop().unwrap().get_i64()),
141             _ => None,
142         }
143     }
144 
145     /// Pops the element at the top of the stack if it is a register;
146     /// returns `None` otherwise.
147     pub fn pop_reg(&mut self) -> Option<Reg> {
148         match self.peek() {
149             Some(v) => v.is_reg().then(|| self.pop().unwrap().get_reg()),
150             _ => None,
151         }
152     }
153 
154     /// Pops the given register if it is at the top of the stack;
155     /// returns `None` otherwise.
156     pub fn pop_named_reg(&mut self, reg: Reg) -> Option<Reg> {
157         match self.peek() {
158             Some(v) => (v.is_reg() && v.get_reg() == reg).then(|| self.pop().unwrap().get_reg()),
159             _ => None,
160         }
161     }
162 
163     /// Get a mutable reference to the inner stack representation.
164     pub fn inner_mut(&mut self) -> &mut VecDeque<Val> {
165         &mut self.inner
166     }
167 }
168 
169 #[cfg(test)]
170 mod tests {
171     use super::{Stack, Val};
172     use crate::isa::reg::Reg;
173 
174     #[test]
175     fn test_pop_i32_const() {
176         let mut stack = Stack::new();
177         stack.push(Val::i32(33i32));
178         assert_eq!(33, stack.pop_i32_const().unwrap());
179 
180         stack.push(Val::local(10));
181         assert!(stack.pop_i32_const().is_none());
182     }
183 
184     #[test]
185     fn test_pop_reg() {
186         let mut stack = Stack::new();
187         let reg = Reg::int(2usize);
188         stack.push(Val::reg(reg));
189         stack.push(Val::i32(4));
190 
191         assert_eq!(None, stack.pop_reg());
192         let _ = stack.pop().unwrap();
193         assert_eq!(reg, stack.pop_reg().unwrap());
194     }
195 
196     #[test]
197     fn test_pop_named_reg() {
198         let mut stack = Stack::new();
199         let reg = Reg::int(2usize);
200         stack.push(Val::reg(reg));
201         stack.push(Val::reg(Reg::int(4)));
202 
203         assert_eq!(None, stack.pop_named_reg(reg));
204         let _ = stack.pop().unwrap();
205         assert_eq!(reg, stack.pop_named_reg(reg).unwrap());
206     }
207 }
208