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