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