1 use crate::{isa::reg::Reg, masm::StackSlot}; 2 use smallvec::SmallVec; 3 use wasmparser::{Ieee32, Ieee64}; 4 use wasmtime_environ::WasmValType; 5 6 /// A typed register value used to track register values in the value 7 /// stack. 8 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 9 pub struct TypedReg { 10 /// The physical register. 11 pub reg: Reg, 12 /// The type associated to the physical register. 13 pub ty: WasmValType, 14 } 15 16 impl TypedReg { 17 /// Create a new [`TypedReg`]. 18 pub fn new(ty: WasmValType, reg: Reg) -> Self { 19 Self { ty, reg } 20 } 21 22 /// Create an i64 [`TypedReg`]. 23 pub fn i64(reg: Reg) -> Self { 24 Self { 25 ty: WasmValType::I64, 26 reg, 27 } 28 } 29 30 /// Create an i32 [`TypedReg`]. 31 pub fn i32(reg: Reg) -> Self { 32 Self { 33 ty: WasmValType::I32, 34 reg, 35 } 36 } 37 38 /// Create an f64 [`TypedReg`]. 39 pub fn f64(reg: Reg) -> Self { 40 Self { 41 ty: WasmValType::F64, 42 reg, 43 } 44 } 45 46 /// Create an f32 [`TypedReg`]. 47 pub fn f32(reg: Reg) -> Self { 48 Self { 49 ty: WasmValType::F32, 50 reg, 51 } 52 } 53 } 54 55 impl From<TypedReg> for Reg { 56 fn from(tr: TypedReg) -> Self { 57 tr.reg 58 } 59 } 60 61 /// A local value. 62 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 63 pub struct Local { 64 /// The index of the local. 65 pub index: u32, 66 /// The type of the local. 67 pub ty: WasmValType, 68 } 69 70 /// A memory value. 71 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 72 pub struct Memory { 73 /// The type associated with the memory offset. 74 pub ty: WasmValType, 75 /// The stack slot corresponding to the memory value. 76 pub slot: StackSlot, 77 } 78 79 /// Value definition to be used within the shadow stack. 80 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 81 pub(crate) enum Val { 82 /// I32 Constant. 83 I32(i32), 84 /// I64 Constant. 85 I64(i64), 86 /// F32 Constant. 87 F32(Ieee32), 88 /// F64 Constant. 89 F64(Ieee64), 90 /// A register value. 91 Reg(TypedReg), 92 /// A local slot. 93 Local(Local), 94 /// Offset to a memory location. 95 Memory(Memory), 96 } 97 98 impl From<TypedReg> for Val { 99 fn from(tr: TypedReg) -> Self { 100 Val::Reg(tr) 101 } 102 } 103 104 impl From<Local> for Val { 105 fn from(local: Local) -> Self { 106 Val::Local(local) 107 } 108 } 109 110 impl From<Memory> for Val { 111 fn from(mem: Memory) -> Self { 112 Val::Memory(mem) 113 } 114 } 115 116 impl TryFrom<u32> for Val { 117 type Error = anyhow::Error; 118 fn try_from(value: u32) -> Result<Self, Self::Error> { 119 i32::try_from(value).map(Val::i32).map_err(Into::into) 120 } 121 } 122 123 impl Val { 124 /// Create a new I32 constant value. 125 pub fn i32(v: i32) -> Self { 126 Self::I32(v) 127 } 128 129 /// Create a new I64 constant value. 130 pub fn i64(v: i64) -> Self { 131 Self::I64(v) 132 } 133 134 /// Create a new F32 constant value. 135 pub fn f32(v: Ieee32) -> Self { 136 Self::F32(v) 137 } 138 139 pub fn f64(v: Ieee64) -> Self { 140 Self::F64(v) 141 } 142 143 /// Create a new Reg value. 144 pub fn reg(reg: Reg, ty: WasmValType) -> Self { 145 Self::Reg(TypedReg { reg, ty }) 146 } 147 148 /// Create a new Local value. 149 pub fn local(index: u32, ty: WasmValType) -> Self { 150 Self::Local(Local { index, ty }) 151 } 152 153 /// Create a Memory value. 154 pub fn mem(ty: WasmValType, slot: StackSlot) -> Self { 155 Self::Memory(Memory { ty, slot }) 156 } 157 158 /// Check whether the value is a register. 159 pub fn is_reg(&self) -> bool { 160 match *self { 161 Self::Reg(_) => true, 162 _ => false, 163 } 164 } 165 166 /// Check wheter the value is a memory offset. 167 pub fn is_mem(&self) -> bool { 168 match *self { 169 Self::Memory(_) => true, 170 _ => false, 171 } 172 } 173 174 /// Check whether the value is a constant. 175 pub fn is_const(&self) -> bool { 176 match *self { 177 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) => true, 178 _ => false, 179 } 180 } 181 182 /// Check whether the value is local with a particular index. 183 pub fn is_local_at_index(&self, index: u32) -> bool { 184 match *self { 185 Self::Local(Local { index: i, .. }) if i == index => true, 186 _ => false, 187 } 188 } 189 190 /// Get the register representation of the value. 191 /// 192 /// # Panics 193 /// This method will panic if the value is not a register. 194 pub fn unwrap_reg(&self) -> TypedReg { 195 match self { 196 Self::Reg(tr) => *tr, 197 v => panic!("expected value {:?} to be a register", v), 198 } 199 } 200 201 /// Get the integer representation of the value. 202 /// 203 /// # Panics 204 /// This method will panic if the value is not an i32. 205 pub fn unwrap_i32(&self) -> i32 { 206 match self { 207 Self::I32(v) => *v, 208 v => panic!("expected value {:?} to be i32", v), 209 } 210 } 211 212 /// Get the integer representation of the value. 213 /// 214 /// # Panics 215 /// This method will panic if the value is not an i64. 216 pub fn unwrap_i64(&self) -> i64 { 217 match self { 218 Self::I64(v) => *v, 219 v => panic!("expected value {:?} to be i64", v), 220 } 221 } 222 223 /// Returns the underlying memory value if it is one, panics otherwise. 224 pub fn unwrap_mem(&self) -> Memory { 225 match self { 226 Self::Memory(m) => *m, 227 v => panic!("expected value {:?} to be a Memory", v), 228 } 229 } 230 231 /// Check whether the value is an i32 constant. 232 pub fn is_i32_const(&self) -> bool { 233 match *self { 234 Self::I32(_) => true, 235 _ => false, 236 } 237 } 238 239 /// Check whether the value is an i64 constant. 240 pub fn is_i64_const(&self) -> bool { 241 match *self { 242 Self::I64(_) => true, 243 _ => false, 244 } 245 } 246 247 /// Get the type of the value. 248 pub fn ty(&self) -> WasmValType { 249 match self { 250 Val::I32(_) => WasmValType::I32, 251 Val::I64(_) => WasmValType::I64, 252 Val::F32(_) => WasmValType::F32, 253 Val::F64(_) => WasmValType::F64, 254 Val::Reg(r) => r.ty, 255 Val::Memory(m) => m.ty, 256 Val::Local(l) => l.ty, 257 } 258 } 259 } 260 261 /// The shadow stack used for compilation. 262 #[derive(Default, Debug)] 263 pub(crate) struct Stack { 264 // NB: The 64 is chosen arbitrarily. We can adjust as we see fit. 265 inner: SmallVec<[Val; 64]>, 266 } 267 268 impl Stack { 269 /// Allocate a new stack. 270 pub fn new() -> Self { 271 Self { 272 inner: Default::default(), 273 } 274 } 275 276 /// Returns true if the stack contains a local with the provided index 277 /// except if the only time the local appears is the top element. 278 pub fn contains_latent_local(&self, index: u32) -> bool { 279 self.inner 280 .iter() 281 // Iterate top-to-bottom so we can skip the top element and stop 282 // when we see a memory element. 283 .rev() 284 // The local is not latent if it's the top element because the top 285 // element will be popped next which materializes the local. 286 .skip(1) 287 // Stop when we see a memory element because that marks where we 288 // spilled up to so there will not be any locals past this point. 289 .take_while(|v| !v.is_mem()) 290 .any(|v| v.is_local_at_index(index)) 291 } 292 293 /// Extend the stack with the given elements. 294 pub fn extend(&mut self, values: impl IntoIterator<Item = Val>) { 295 self.inner.extend(values); 296 } 297 298 /// Inserts many values at the given index. 299 pub fn insert_many(&mut self, at: usize, values: &[Val]) { 300 debug_assert!(at <= self.len()); 301 302 if at == self.len() { 303 self.inner.extend_from_slice(values); 304 } else { 305 self.inner.insert_from_slice(at, values); 306 } 307 } 308 309 /// Get the length of the stack. 310 pub fn len(&self) -> usize { 311 self.inner.len() 312 } 313 314 /// Push a value to the stack. 315 pub fn push(&mut self, val: Val) { 316 self.inner.push(val); 317 } 318 319 /// Peek into the top in the stack. 320 pub fn peek(&self) -> Option<&Val> { 321 self.inner.last() 322 } 323 324 /// Returns an iterator referencing the last n items of the stack, 325 /// in bottom-most to top-most order. 326 pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ { 327 let len = self.len(); 328 assert!(n <= len); 329 330 let partition = len - n; 331 self.inner[partition..].into_iter() 332 } 333 334 /// Pops the top element of the stack, if any. 335 pub fn pop(&mut self) -> Option<Val> { 336 self.inner.pop() 337 } 338 339 /// Pops the element at the top of the stack if it is an i32 const; 340 /// returns `None` otherwise. 341 pub fn pop_i32_const(&mut self) -> Option<i32> { 342 match self.peek() { 343 Some(v) => v.is_i32_const().then(|| self.pop().unwrap().unwrap_i32()), 344 _ => None, 345 } 346 } 347 348 /// Pops the element at the top of the stack if it is an i64 const; 349 /// returns `None` otherwise. 350 pub fn pop_i64_const(&mut self) -> Option<i64> { 351 match self.peek() { 352 Some(v) => v.is_i64_const().then(|| self.pop().unwrap().unwrap_i64()), 353 _ => None, 354 } 355 } 356 357 /// Pops the element at the top of the stack if it is a register; 358 /// returns `None` otherwise. 359 pub fn pop_reg(&mut self) -> Option<TypedReg> { 360 match self.peek() { 361 Some(v) => v.is_reg().then(|| self.pop().unwrap().unwrap_reg()), 362 _ => None, 363 } 364 } 365 366 /// Pops the given register if it is at the top of the stack; 367 /// returns `None` otherwise. 368 pub fn pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg> { 369 match self.peek() { 370 Some(v) => { 371 (v.is_reg() && v.unwrap_reg().reg == reg).then(|| self.pop().unwrap().unwrap_reg()) 372 } 373 _ => None, 374 } 375 } 376 377 /// Get a mutable reference to the inner stack representation. 378 pub fn inner_mut(&mut self) -> &mut SmallVec<[Val; 64]> { 379 &mut self.inner 380 } 381 382 /// Get a reference to the inner stack representation. 383 pub fn inner(&self) -> &SmallVec<[Val; 64]> { 384 &self.inner 385 } 386 387 /// Calculates the size of, in bytes, of the top n [Memory] entries 388 /// in the value stack. 389 pub fn sizeof(&self, top: usize) -> u32 { 390 self.peekn(top).fold(0, |acc, v| { 391 if v.is_mem() { 392 acc + v.unwrap_mem().slot.size 393 } else { 394 acc 395 } 396 }) 397 } 398 } 399 400 #[cfg(test)] 401 mod tests { 402 use super::{Stack, Val}; 403 use crate::isa::reg::Reg; 404 use wasmtime_environ::WasmValType; 405 406 #[test] 407 fn test_pop_i32_const() { 408 let mut stack = Stack::new(); 409 stack.push(Val::i32(33i32)); 410 assert_eq!(33, stack.pop_i32_const().unwrap()); 411 412 stack.push(Val::local(10, WasmValType::I32)); 413 assert!(stack.pop_i32_const().is_none()); 414 } 415 416 #[test] 417 fn test_pop_reg() { 418 let mut stack = Stack::new(); 419 let reg = Reg::int(2usize); 420 stack.push(Val::reg(reg, WasmValType::I32)); 421 stack.push(Val::i32(4)); 422 423 assert_eq!(None, stack.pop_reg()); 424 let _ = stack.pop().unwrap(); 425 assert_eq!(reg, stack.pop_reg().unwrap().reg); 426 } 427 428 #[test] 429 fn test_pop_named_reg() { 430 let mut stack = Stack::new(); 431 let reg = Reg::int(2usize); 432 stack.push(Val::reg(reg, WasmValType::I32)); 433 stack.push(Val::reg(Reg::int(4), WasmValType::I32)); 434 435 assert_eq!(None, stack.pop_named_reg(reg)); 436 let _ = stack.pop().unwrap(); 437 assert_eq!(reg, stack.pop_named_reg(reg).unwrap().reg); 438 } 439 } 440