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