1 use crate::{Result, codegen::CodeGenError, format_err, 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`]. new(ty: WasmValType, reg: Reg) -> Self18 pub fn new(ty: WasmValType, reg: Reg) -> Self { 19 Self { ty, reg } 20 } 21 22 /// Create an i64 [`TypedReg`]. i64(reg: Reg) -> Self23 pub fn i64(reg: Reg) -> Self { 24 Self { 25 ty: WasmValType::I64, 26 reg, 27 } 28 } 29 30 /// Create an i32 [`TypedReg`]. i32(reg: Reg) -> Self31 pub fn i32(reg: Reg) -> Self { 32 Self { 33 ty: WasmValType::I32, 34 reg, 35 } 36 } 37 38 /// Create an f64 [`TypedReg`]. f64(reg: Reg) -> Self39 pub fn f64(reg: Reg) -> Self { 40 Self { 41 ty: WasmValType::F64, 42 reg, 43 } 44 } 45 46 /// Create an f32 [`TypedReg`]. f32(reg: Reg) -> Self47 pub fn f32(reg: Reg) -> Self { 48 Self { 49 ty: WasmValType::F32, 50 reg, 51 } 52 } 53 54 /// Create a v128 [`TypedReg`]. v128(reg: Reg) -> Self55 pub fn v128(reg: Reg) -> Self { 56 Self { 57 ty: WasmValType::V128, 58 reg, 59 } 60 } 61 } 62 63 impl From<TypedReg> for Reg { from(tr: TypedReg) -> Self64 fn from(tr: TypedReg) -> Self { 65 tr.reg 66 } 67 } 68 69 /// A local value. 70 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 71 pub struct Local { 72 /// The index of the local. 73 pub index: u32, 74 /// The type of the local. 75 pub ty: WasmValType, 76 } 77 78 /// A memory value. 79 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 80 pub struct Memory { 81 /// The type associated with the memory offset. 82 pub ty: WasmValType, 83 /// The stack slot corresponding to the memory value. 84 pub slot: StackSlot, 85 } 86 87 /// Value definition to be used within the shadow stack. 88 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 89 pub(crate) enum Val { 90 /// I32 Constant. 91 I32(i32), 92 /// I64 Constant. 93 I64(i64), 94 /// F32 Constant. 95 F32(Ieee32), 96 /// F64 Constant. 97 F64(Ieee64), 98 /// V128 Constant. 99 V128(i128), 100 /// A register value. 101 Reg(TypedReg), 102 /// A local slot. 103 Local(Local), 104 /// Offset to a memory location. 105 Memory(Memory), 106 } 107 108 impl From<TypedReg> for Val { from(tr: TypedReg) -> Self109 fn from(tr: TypedReg) -> Self { 110 Val::Reg(tr) 111 } 112 } 113 114 impl From<Local> for Val { from(local: Local) -> Self115 fn from(local: Local) -> Self { 116 Val::Local(local) 117 } 118 } 119 120 impl From<Memory> for Val { from(mem: Memory) -> Self121 fn from(mem: Memory) -> Self { 122 Val::Memory(mem) 123 } 124 } 125 126 impl TryFrom<u32> for Val { 127 type Error = crate::Error; try_from(value: u32) -> Result<Self, Self::Error>128 fn try_from(value: u32) -> Result<Self, Self::Error> { 129 i32::try_from(value).map(Val::i32).map_err(Into::into) 130 } 131 } 132 133 impl Val { 134 /// Create a new I32 constant value. i32(v: i32) -> Self135 pub fn i32(v: i32) -> Self { 136 Self::I32(v) 137 } 138 139 /// Create a new I64 constant value. i64(v: i64) -> Self140 pub fn i64(v: i64) -> Self { 141 Self::I64(v) 142 } 143 144 /// Create a new F32 constant value. f32(v: Ieee32) -> Self145 pub fn f32(v: Ieee32) -> Self { 146 Self::F32(v) 147 } 148 f64(v: Ieee64) -> Self149 pub fn f64(v: Ieee64) -> Self { 150 Self::F64(v) 151 } 152 153 /// Create a new V128 constant value. v128(v: i128) -> Self154 pub fn v128(v: i128) -> Self { 155 Self::V128(v) 156 } 157 158 /// Create a new Reg value. reg(reg: Reg, ty: WasmValType) -> Self159 pub fn reg(reg: Reg, ty: WasmValType) -> Self { 160 Self::Reg(TypedReg { reg, ty }) 161 } 162 163 /// Create a new Local value. local(index: u32, ty: WasmValType) -> Self164 pub fn local(index: u32, ty: WasmValType) -> Self { 165 Self::Local(Local { index, ty }) 166 } 167 168 /// Create a Memory value. mem(ty: WasmValType, slot: StackSlot) -> Self169 pub fn mem(ty: WasmValType, slot: StackSlot) -> Self { 170 Self::Memory(Memory { ty, slot }) 171 } 172 173 /// Check whether the value is a register. is_reg(&self) -> bool174 pub fn is_reg(&self) -> bool { 175 match *self { 176 Self::Reg(_) => true, 177 _ => false, 178 } 179 } 180 181 /// Check whether the value is a memory offset. is_mem(&self) -> bool182 pub fn is_mem(&self) -> bool { 183 match *self { 184 Self::Memory(_) => true, 185 _ => false, 186 } 187 } 188 189 /// Check whether the value is a constant. is_const(&self) -> bool190 pub fn is_const(&self) -> bool { 191 match *self { 192 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true, 193 _ => false, 194 } 195 } 196 197 /// Check whether the value is local with a particular index. is_local_at_index(&self, index: u32) -> bool198 pub fn is_local_at_index(&self, index: u32) -> bool { 199 match *self { 200 Self::Local(Local { index: i, .. }) if i == index => true, 201 _ => false, 202 } 203 } 204 205 /// Get the register representation of the value. 206 /// 207 /// # Panics 208 /// This method will panic if the value is not a register. unwrap_reg(&self) -> TypedReg209 pub fn unwrap_reg(&self) -> TypedReg { 210 match self { 211 Self::Reg(tr) => *tr, 212 v => panic!("expected value {v:?} to be a register"), 213 } 214 } 215 216 /// Get the integer representation of the value. 217 /// 218 /// # Panics 219 /// This method will panic if the value is not an i32. unwrap_i32(&self) -> i32220 pub fn unwrap_i32(&self) -> i32 { 221 match self { 222 Self::I32(v) => *v, 223 v => panic!("expected value {v:?} to be i32"), 224 } 225 } 226 227 /// Get the integer representation of the value. 228 /// 229 /// # Panics 230 /// This method will panic if the value is not an i64. unwrap_i64(&self) -> i64231 pub fn unwrap_i64(&self) -> i64 { 232 match self { 233 Self::I64(v) => *v, 234 v => panic!("expected value {v:?} to be i64"), 235 } 236 } 237 238 /// Get the float representation of the value. 239 /// 240 /// # Panics 241 /// This method will panic if the value is not an f32. unwrap_f32(&self) -> Ieee32242 pub fn unwrap_f32(&self) -> Ieee32 { 243 match self { 244 Self::F32(v) => *v, 245 v => panic!("expected value {v:?} to be f32"), 246 } 247 } 248 249 /// Get the float representation of the value. 250 /// 251 /// # Panics 252 /// This method will panic if the value is not an f64. unwrap_f64(&self) -> Ieee64253 pub fn unwrap_f64(&self) -> Ieee64 { 254 match self { 255 Self::F64(v) => *v, 256 v => panic!("expected value {v:?} to be f64"), 257 } 258 } 259 260 /// Returns the underlying memory value if it is one, panics otherwise. unwrap_mem(&self) -> Memory261 pub fn unwrap_mem(&self) -> Memory { 262 match self { 263 Self::Memory(m) => *m, 264 v => panic!("expected value {v:?} to be a Memory"), 265 } 266 } 267 268 /// Check whether the value is an i32 constant. is_i32_const(&self) -> bool269 pub fn is_i32_const(&self) -> bool { 270 match *self { 271 Self::I32(_) => true, 272 _ => false, 273 } 274 } 275 276 /// Check whether the value is an i64 constant. is_i64_const(&self) -> bool277 pub fn is_i64_const(&self) -> bool { 278 match *self { 279 Self::I64(_) => true, 280 _ => false, 281 } 282 } 283 284 /// Check whether the value is an f32 constant. is_f32_const(&self) -> bool285 pub fn is_f32_const(&self) -> bool { 286 match *self { 287 Self::F32(_) => true, 288 _ => false, 289 } 290 } 291 292 /// Check whether the value is an f64 constant. is_f64_const(&self) -> bool293 pub fn is_f64_const(&self) -> bool { 294 match *self { 295 Self::F64(_) => true, 296 _ => false, 297 } 298 } 299 300 /// Get the type of the value. ty(&self) -> WasmValType301 pub fn ty(&self) -> WasmValType { 302 match self { 303 Val::I32(_) => WasmValType::I32, 304 Val::I64(_) => WasmValType::I64, 305 Val::F32(_) => WasmValType::F32, 306 Val::F64(_) => WasmValType::F64, 307 Val::V128(_) => WasmValType::V128, 308 Val::Reg(r) => r.ty, 309 Val::Memory(m) => m.ty, 310 Val::Local(l) => l.ty, 311 } 312 } 313 } 314 315 /// The shadow stack used for compilation. 316 #[derive(Default, Debug)] 317 pub(crate) struct Stack { 318 // NB: The 64 is chosen arbitrarily. We can adjust as we see fit. 319 inner: SmallVec<[Val; 64]>, 320 } 321 322 impl Stack { 323 /// Allocate a new stack. new() -> Self324 pub fn new() -> Self { 325 Self { 326 inner: Default::default(), 327 } 328 } 329 330 /// Ensures that there are at least `n` elements in the value stack, 331 /// and returns the index calculated by: stack length minus `n`. ensure_index_at(&self, n: usize) -> Result<usize>332 pub fn ensure_index_at(&self, n: usize) -> Result<usize> { 333 if self.len() >= n { 334 Ok(self.len() - n) 335 } else { 336 Err(format_err!(CodeGenError::missing_values_in_stack())) 337 } 338 } 339 340 /// Returns true if the stack contains a local with the provided index 341 /// except if the only time the local appears is the top element. contains_latent_local(&self, index: u32) -> bool342 pub fn contains_latent_local(&self, index: u32) -> bool { 343 self.inner 344 .iter() 345 // Iterate top-to-bottom so we can skip the top element and stop 346 // when we see a memory element. 347 .rev() 348 // The local is not latent if it's the top element because the top 349 // element will be popped next which materializes the local. 350 .skip(1) 351 // Stop when we see a memory element because that marks where we 352 // spilled up to so there will not be any locals past this point. 353 .take_while(|v| !v.is_mem()) 354 .any(|v| v.is_local_at_index(index)) 355 } 356 357 /// Extend the stack with the given elements. extend(&mut self, values: impl IntoIterator<Item = Val>)358 pub fn extend(&mut self, values: impl IntoIterator<Item = Val>) { 359 self.inner.extend(values); 360 } 361 362 /// Inserts many values at the given index. insert_many(&mut self, at: usize, values: &[Val])363 pub fn insert_many(&mut self, at: usize, values: &[Val]) { 364 debug_assert!(at <= self.len()); 365 366 if at == self.len() { 367 self.inner.extend_from_slice(values); 368 } else { 369 self.inner.insert_from_slice(at, values); 370 } 371 } 372 373 /// Get the length of the stack. len(&self) -> usize374 pub fn len(&self) -> usize { 375 self.inner.len() 376 } 377 378 /// Push a value to the stack. push(&mut self, val: Val)379 pub fn push(&mut self, val: Val) { 380 self.inner.push(val); 381 } 382 383 /// Peek into the top in the stack. peek(&self) -> Option<&Val>384 pub fn peek(&self) -> Option<&Val> { 385 self.inner.last() 386 } 387 388 /// Returns an iterator referencing the last n items of the stack, 389 /// in bottom-most to top-most order. peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_390 pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ { 391 let len = self.len(); 392 assert!(n <= len); 393 394 let partition = len - n; 395 self.inner[partition..].into_iter() 396 } 397 398 /// Pops the top element of the stack, if any. pop(&mut self) -> Option<Val>399 pub fn pop(&mut self) -> Option<Val> { 400 self.inner.pop() 401 } 402 403 /// Pops the element at the top of the stack if it is an i32 const; 404 /// returns `None` otherwise. pop_i32_const(&mut self) -> Option<i32>405 pub fn pop_i32_const(&mut self) -> Option<i32> { 406 match self.peek() { 407 Some(v) => v.is_i32_const().then(|| self.pop().unwrap().unwrap_i32()), 408 _ => None, 409 } 410 } 411 412 /// Pops the element at the top of the stack if it is an i64 const; 413 /// returns `None` otherwise. pop_i64_const(&mut self) -> Option<i64>414 pub fn pop_i64_const(&mut self) -> Option<i64> { 415 match self.peek() { 416 Some(v) => v.is_i64_const().then(|| self.pop().unwrap().unwrap_i64()), 417 _ => None, 418 } 419 } 420 421 /// Pops the element at the top of the stack if it is an f32 const; 422 /// returns `None` otherwise. pop_f32_const(&mut self) -> Option<Ieee32>423 pub fn pop_f32_const(&mut self) -> Option<Ieee32> { 424 match self.peek() { 425 Some(v) => v.is_f32_const().then(|| self.pop().unwrap().unwrap_f32()), 426 _ => None, 427 } 428 } 429 430 /// Pops the element at the top of the stack if it is an f64 const; 431 /// returns `None` otherwise. pop_f64_const(&mut self) -> Option<Ieee64>432 pub fn pop_f64_const(&mut self) -> Option<Ieee64> { 433 match self.peek() { 434 Some(v) => v.is_f64_const().then(|| self.pop().unwrap().unwrap_f64()), 435 _ => None, 436 } 437 } 438 439 /// Pops the element at the top of the stack if it is a register; 440 /// returns `None` otherwise. pop_reg(&mut self) -> Option<TypedReg>441 pub fn pop_reg(&mut self) -> Option<TypedReg> { 442 match self.peek() { 443 Some(v) => v.is_reg().then(|| self.pop().unwrap().unwrap_reg()), 444 _ => None, 445 } 446 } 447 448 /// Pops the given register if it is at the top of the stack; 449 /// returns `None` otherwise. pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg>450 pub fn pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg> { 451 match self.peek() { 452 Some(v) => { 453 (v.is_reg() && v.unwrap_reg().reg == reg).then(|| self.pop().unwrap().unwrap_reg()) 454 } 455 _ => None, 456 } 457 } 458 459 /// Get a mutable reference to the inner stack representation. inner_mut(&mut self) -> &mut SmallVec<[Val; 64]>460 pub fn inner_mut(&mut self) -> &mut SmallVec<[Val; 64]> { 461 &mut self.inner 462 } 463 464 /// Get a reference to the inner stack representation. inner(&self) -> &SmallVec<[Val; 64]>465 pub fn inner(&self) -> &SmallVec<[Val; 64]> { 466 &self.inner 467 } 468 469 /// Calculates the size of, in bytes, of the top n [Memory] entries 470 /// in the value stack. sizeof(&self, top: usize) -> u32471 pub fn sizeof(&self, top: usize) -> u32 { 472 self.peekn(top).fold(0, |acc, v| { 473 if v.is_mem() { 474 acc + v.unwrap_mem().slot.size 475 } else { 476 acc 477 } 478 }) 479 } 480 } 481 482 #[cfg(test)] 483 mod tests { 484 use super::{Stack, Val}; 485 use crate::isa::reg::Reg; 486 use wasmtime_environ::WasmValType; 487 488 #[test] test_pop_i32_const()489 fn test_pop_i32_const() { 490 let mut stack = Stack::new(); 491 stack.push(Val::i32(33i32)); 492 assert_eq!(33, stack.pop_i32_const().unwrap()); 493 494 stack.push(Val::local(10, WasmValType::I32)); 495 assert!(stack.pop_i32_const().is_none()); 496 } 497 498 #[test] test_pop_reg()499 fn test_pop_reg() { 500 let mut stack = Stack::new(); 501 let reg = Reg::int(2usize); 502 stack.push(Val::reg(reg, WasmValType::I32)); 503 stack.push(Val::i32(4)); 504 505 assert_eq!(None, stack.pop_reg()); 506 let _ = stack.pop().unwrap(); 507 assert_eq!(reg, stack.pop_reg().unwrap().reg); 508 } 509 510 #[test] test_pop_named_reg()511 fn test_pop_named_reg() { 512 let mut stack = Stack::new(); 513 let reg = Reg::int(2usize); 514 stack.push(Val::reg(reg, WasmValType::I32)); 515 stack.push(Val::reg(Reg::int(4), WasmValType::I32)); 516 517 assert_eq!(None, stack.pop_named_reg(reg)); 518 let _ = stack.pop().unwrap(); 519 assert_eq!(reg, stack.pop_named_reg(reg).unwrap().reg); 520 } 521 } 522