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 /// 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 /// Get the length of the stack. 121 pub fn len(&self) -> usize { 122 self.inner.len() 123 } 124 125 /// Push a value to the stack. 126 pub fn push(&mut self, val: Val) { 127 self.inner.push_back(val); 128 } 129 130 /// Peek into the top in the stack. 131 pub fn peek(&self) -> Option<&Val> { 132 self.inner.back() 133 } 134 135 /// Returns an iterator referencing the last n items of the stack, 136 /// in bottom-most to top-most order. 137 pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ { 138 let len = self.len(); 139 assert!(n <= len); 140 141 let partition = len - n; 142 self.inner.range(partition..) 143 } 144 145 /// Pops the top element of the stack, if any. 146 pub fn pop(&mut self) -> Option<Val> { 147 self.inner.pop_back() 148 } 149 150 /// Pops the element at the top of the stack if it is an i32 const; 151 /// returns `None` otherwise. 152 pub fn pop_i32_const(&mut self) -> Option<i32> { 153 match self.peek() { 154 Some(v) => v.is_i32_const().then(|| self.pop().unwrap().get_i32()), 155 _ => None, 156 } 157 } 158 159 /// Pops the element at the top of the stack if it is an i64 const; 160 /// returns `None` otherwise. 161 pub fn pop_i64_const(&mut self) -> Option<i64> { 162 match self.peek() { 163 Some(v) => v.is_i64_const().then(|| self.pop().unwrap().get_i64()), 164 _ => None, 165 } 166 } 167 168 /// Pops the element at the top of the stack if it is a register; 169 /// returns `None` otherwise. 170 pub fn pop_reg(&mut self) -> Option<Reg> { 171 match self.peek() { 172 Some(v) => v.is_reg().then(|| self.pop().unwrap().get_reg()), 173 _ => None, 174 } 175 } 176 177 /// Pops the given register if it is at the top of the stack; 178 /// returns `None` otherwise. 179 pub fn pop_named_reg(&mut self, reg: Reg) -> Option<Reg> { 180 match self.peek() { 181 Some(v) => (v.is_reg() && v.get_reg() == reg).then(|| self.pop().unwrap().get_reg()), 182 _ => None, 183 } 184 } 185 186 /// Get a mutable reference to the inner stack representation. 187 pub fn inner_mut(&mut self) -> &mut VecDeque<Val> { 188 &mut self.inner 189 } 190 } 191 192 #[cfg(test)] 193 mod tests { 194 use super::{Stack, Val}; 195 use crate::isa::reg::Reg; 196 197 #[test] 198 fn test_pop_i32_const() { 199 let mut stack = Stack::new(); 200 stack.push(Val::i32(33i32)); 201 assert_eq!(33, stack.pop_i32_const().unwrap()); 202 203 stack.push(Val::local(10)); 204 assert!(stack.pop_i32_const().is_none()); 205 } 206 207 #[test] 208 fn test_pop_reg() { 209 let mut stack = Stack::new(); 210 let reg = Reg::int(2usize); 211 stack.push(Val::reg(reg)); 212 stack.push(Val::i32(4)); 213 214 assert_eq!(None, stack.pop_reg()); 215 let _ = stack.pop().unwrap(); 216 assert_eq!(reg, stack.pop_reg().unwrap()); 217 } 218 219 #[test] 220 fn test_pop_named_reg() { 221 let mut stack = Stack::new(); 222 let reg = Reg::int(2usize); 223 stack.push(Val::reg(reg)); 224 stack.push(Val::reg(Reg::int(4))); 225 226 assert_eq!(None, stack.pop_named_reg(reg)); 227 let _ = stack.pop().unwrap(); 228 assert_eq!(reg, stack.pop_named_reg(reg).unwrap()); 229 } 230 } 231