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