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