1 //! Evaluating const expressions. 2 3 use crate::runtime::vm::{Instance, VMGcRef, ValRaw, I31}; 4 use crate::store::{AutoAssertNoGc, StoreOpaque}; 5 use crate::{ 6 prelude::*, ArrayRef, ArrayRefPre, ArrayType, StructRef, StructRefPre, StructType, Val, 7 }; 8 use smallvec::SmallVec; 9 use wasmtime_environ::{ 10 ConstExpr, ConstOp, FuncIndex, GlobalIndex, ModuleInternedTypeIndex, WasmCompositeInnerType, 11 WasmCompositeType, WasmSubType, 12 }; 13 14 /// An interpreter for const expressions. 15 /// 16 /// This can be reused across many const expression evaluations to reuse 17 /// allocated resources, if any. 18 #[derive(Default)] 19 pub struct ConstExprEvaluator { 20 stack: SmallVec<[ValRaw; 2]>, 21 } 22 23 /// The context within which a particular const expression is evaluated. 24 pub struct ConstEvalContext<'a> { 25 pub(crate) instance: &'a mut Instance, 26 } 27 28 impl<'a> ConstEvalContext<'a> { 29 /// Create a new context. 30 pub fn new(instance: &'a mut Instance) -> Self { 31 Self { instance } 32 } 33 34 fn global_get(&mut self, store: &mut AutoAssertNoGc<'_>, index: GlobalIndex) -> Result<ValRaw> { 35 unsafe { 36 let global = self 37 .instance 38 .defined_or_imported_global_ptr(index) 39 .as_ref() 40 .unwrap(); 41 global.to_val_raw(store, self.instance.env_module().globals[index].wasm_ty) 42 } 43 } 44 45 fn ref_func(&mut self, index: FuncIndex) -> Result<ValRaw> { 46 Ok(ValRaw::funcref( 47 self.instance.get_func_ref(index).unwrap().as_ptr().cast(), 48 )) 49 } 50 51 #[cfg(feature = "gc")] 52 fn struct_fields_len(&self, struct_type_index: ModuleInternedTypeIndex) -> usize { 53 let module = self 54 .instance 55 .runtime_module() 56 .expect("should never be allocating a struct type defined in a dummy module"); 57 58 let struct_ty = match &module.types()[struct_type_index].composite_type.inner { 59 WasmCompositeInnerType::Struct(s) => s, 60 _ => unreachable!(), 61 }; 62 63 struct_ty.fields.len() 64 } 65 66 /// Safety: field values must be of the correct types. 67 #[cfg(feature = "gc")] 68 unsafe fn struct_new( 69 &mut self, 70 store: &mut AutoAssertNoGc<'_>, 71 struct_type_index: ModuleInternedTypeIndex, 72 fields: &[ValRaw], 73 ) -> Result<ValRaw> { 74 let module = self 75 .instance 76 .runtime_module() 77 .expect("should never be allocating a struct type defined in a dummy module"); 78 let shared_ty = module 79 .signatures() 80 .shared_type(struct_type_index) 81 .expect("should have an engine type for module type"); 82 83 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty); 84 let fields = fields 85 .iter() 86 .zip(struct_ty.fields()) 87 .map(|(raw, ty)| { 88 let ty = ty.element_type().unpack(); 89 Val::_from_raw(store, *raw, ty) 90 }) 91 .collect::<Vec<_>>(); 92 93 let allocator = StructRefPre::_new(store, struct_ty); 94 let struct_ref = StructRef::_new(store, &allocator, &fields)?; 95 let raw = struct_ref.to_anyref()._to_raw(store)?; 96 Ok(ValRaw::anyref(raw)) 97 } 98 99 #[cfg(feature = "gc")] 100 fn struct_new_default( 101 &mut self, 102 store: &mut AutoAssertNoGc<'_>, 103 struct_type_index: ModuleInternedTypeIndex, 104 ) -> Result<ValRaw> { 105 let module = self 106 .instance 107 .runtime_module() 108 .expect("should never be allocating a struct type defined in a dummy module"); 109 110 let shared_ty = module 111 .signatures() 112 .shared_type(struct_type_index) 113 .expect("should have an engine type for module type"); 114 115 let borrowed = module 116 .engine() 117 .signatures() 118 .borrow(shared_ty) 119 .expect("should have a registered type for struct"); 120 let WasmSubType { 121 composite_type: 122 WasmCompositeType { 123 shared: false, 124 inner: WasmCompositeInnerType::Struct(struct_ty), 125 }, 126 .. 127 } = &*borrowed 128 else { 129 unreachable!("registered type should be a struct"); 130 }; 131 132 let fields = struct_ty 133 .fields 134 .iter() 135 .map(|ty| match &ty.element_type { 136 wasmtime_environ::WasmStorageType::I8 | wasmtime_environ::WasmStorageType::I16 => { 137 ValRaw::i32(0) 138 } 139 wasmtime_environ::WasmStorageType::Val(v) => match v { 140 wasmtime_environ::WasmValType::I32 => ValRaw::i32(0), 141 wasmtime_environ::WasmValType::I64 => ValRaw::i64(0), 142 wasmtime_environ::WasmValType::F32 => ValRaw::f32(0.0f32.to_bits()), 143 wasmtime_environ::WasmValType::F64 => ValRaw::f64(0.0f64.to_bits()), 144 wasmtime_environ::WasmValType::V128 => ValRaw::v128(0), 145 wasmtime_environ::WasmValType::Ref(r) => { 146 assert!(r.nullable); 147 ValRaw::null() 148 } 149 }, 150 }) 151 .collect::<SmallVec<[_; 8]>>(); 152 153 unsafe { self.struct_new(store, struct_type_index, &fields) } 154 } 155 } 156 157 impl ConstExprEvaluator { 158 /// Evaluate the given const expression in the given context. 159 /// 160 /// # Unsafety 161 /// 162 /// The given const expression must be valid within the given context, 163 /// e.g. the const expression must be well-typed and the context must return 164 /// global values of the expected types. This evaluator operates directly on 165 /// untyped `ValRaw`s and does not and cannot check that its operands are of 166 /// the correct type. 167 pub unsafe fn eval( 168 &mut self, 169 store: &mut StoreOpaque, 170 context: &mut ConstEvalContext<'_>, 171 expr: &ConstExpr, 172 ) -> Result<ValRaw> { 173 log::trace!("evaluating const expr: {:?}", expr); 174 175 self.stack.clear(); 176 177 // Ensure that we don't permanently root any GC references we allocate 178 // during const evaluation, keeping them alive for the duration of the 179 // store's lifetime. 180 #[cfg(feature = "gc")] 181 let mut store = crate::OpaqueRootScope::new(store); 182 #[cfg(not(feature = "gc"))] 183 let mut store = store; 184 185 // We cannot allow GC during const evaluation because the stack of 186 // `ValRaw`s are not rooted. If we had a GC reference on our stack, and 187 // then performed a collection, that on-stack reference's object could 188 // be reclaimed or relocated by the collector, and then when we use the 189 // reference again we would basically get a use-after-free bug. 190 let mut store = AutoAssertNoGc::new(&mut store); 191 192 for op in expr.ops() { 193 match op { 194 ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)), 195 ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)), 196 ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)), 197 ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)), 198 ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)), 199 ConstOp::GlobalGet(g) => self.stack.push(context.global_get(&mut store, *g)?), 200 ConstOp::RefNull => self.stack.push(ValRaw::null()), 201 ConstOp::RefFunc(f) => self.stack.push(context.ref_func(*f)?), 202 ConstOp::RefI31 => { 203 let i = self.pop()?.get_i32(); 204 let i31 = I31::wrapping_i32(i); 205 let raw = VMGcRef::from_i31(i31).as_raw_u32(); 206 self.stack.push(ValRaw::anyref(raw)); 207 } 208 ConstOp::I32Add => { 209 let b = self.pop()?.get_i32(); 210 let a = self.pop()?.get_i32(); 211 self.stack.push(ValRaw::i32(a.wrapping_add(b))); 212 } 213 ConstOp::I32Sub => { 214 let b = self.pop()?.get_i32(); 215 let a = self.pop()?.get_i32(); 216 self.stack.push(ValRaw::i32(a.wrapping_sub(b))); 217 } 218 ConstOp::I32Mul => { 219 let b = self.pop()?.get_i32(); 220 let a = self.pop()?.get_i32(); 221 self.stack.push(ValRaw::i32(a.wrapping_mul(b))); 222 } 223 ConstOp::I64Add => { 224 let b = self.pop()?.get_i64(); 225 let a = self.pop()?.get_i64(); 226 self.stack.push(ValRaw::i64(a.wrapping_add(b))); 227 } 228 ConstOp::I64Sub => { 229 let b = self.pop()?.get_i64(); 230 let a = self.pop()?.get_i64(); 231 self.stack.push(ValRaw::i64(a.wrapping_sub(b))); 232 } 233 ConstOp::I64Mul => { 234 let b = self.pop()?.get_i64(); 235 let a = self.pop()?.get_i64(); 236 self.stack.push(ValRaw::i64(a.wrapping_mul(b))); 237 } 238 239 #[cfg(not(feature = "gc"))] 240 ConstOp::StructNew { .. } 241 | ConstOp::StructNewDefault { .. } 242 | ConstOp::ArrayNew { .. } 243 | ConstOp::ArrayNewDefault { .. } 244 | ConstOp::ArrayNewFixed { .. } => { 245 bail!( 246 "const expr evaluation error: struct operations are not \ 247 supported without the `gc` feature" 248 ) 249 } 250 251 #[cfg(feature = "gc")] 252 ConstOp::StructNew { struct_type_index } => { 253 let interned_type_index = 254 context.instance.env_module().types[*struct_type_index]; 255 let len = context.struct_fields_len(interned_type_index); 256 257 if self.stack.len() < len { 258 bail!( 259 "const expr evaluation error: expected at least {len} values on the stack, found {}", 260 self.stack.len() 261 ) 262 } 263 264 let start = self.stack.len() - len; 265 let s = context.struct_new( 266 &mut store, 267 interned_type_index, 268 &self.stack[start..], 269 )?; 270 self.stack.truncate(start); 271 self.stack.push(s); 272 } 273 274 #[cfg(feature = "gc")] 275 ConstOp::StructNewDefault { struct_type_index } => { 276 let interned_type_index = 277 context.instance.env_module().types[*struct_type_index]; 278 self.stack 279 .push(context.struct_new_default(&mut store, interned_type_index)?); 280 } 281 282 #[cfg(feature = "gc")] 283 ConstOp::ArrayNew { array_type_index } => { 284 let interned_type_index = 285 context.instance.env_module().types[*array_type_index]; 286 let module = context.instance.runtime_module().expect( 287 "should never be allocating a struct type defined in a dummy module", 288 ); 289 let shared_ty = module 290 .signatures() 291 .shared_type(interned_type_index) 292 .expect("should have an engine type for module type"); 293 let ty = ArrayType::from_shared_type_index(store.engine(), shared_ty); 294 295 #[allow(clippy::cast_sign_loss)] 296 let len = self.pop()?.get_i32() as u32; 297 298 let elem = Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack()); 299 300 let pre = ArrayRefPre::_new(&mut store, ty); 301 let array = ArrayRef::_new(&mut store, &pre, &elem, len)?; 302 303 self.stack 304 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?)); 305 } 306 307 #[cfg(feature = "gc")] 308 ConstOp::ArrayNewDefault { array_type_index } => { 309 let interned_type_index = 310 context.instance.env_module().types[*array_type_index]; 311 let module = context.instance.runtime_module().expect( 312 "should never be allocating a struct type defined in a dummy module", 313 ); 314 let shared_ty = module 315 .signatures() 316 .shared_type(interned_type_index) 317 .expect("should have an engine type for module type"); 318 let ty = ArrayType::from_shared_type_index(store.engine(), shared_ty); 319 320 #[allow(clippy::cast_sign_loss)] 321 let len = self.pop()?.get_i32() as u32; 322 323 let elem = Val::default_for_ty(ty.element_type().unpack()) 324 .expect("type should have a default value"); 325 326 let pre = ArrayRefPre::_new(&mut store, ty); 327 let array = ArrayRef::_new(&mut store, &pre, &elem, len)?; 328 329 self.stack 330 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?)); 331 } 332 333 #[cfg(feature = "gc")] 334 ConstOp::ArrayNewFixed { 335 array_type_index, 336 array_size, 337 } => { 338 let interned_type_index = 339 context.instance.env_module().types[*array_type_index]; 340 let module = context.instance.runtime_module().expect( 341 "should never be allocating a struct type defined in a dummy module", 342 ); 343 let shared_ty = module 344 .signatures() 345 .shared_type(interned_type_index) 346 .expect("should have an engine type for module type"); 347 let ty = ArrayType::from_shared_type_index(store.engine(), shared_ty); 348 349 let array_size = usize::try_from(*array_size).unwrap(); 350 if self.stack.len() < array_size { 351 bail!( 352 "const expr evaluation error: expected at least {array_size} values on the stack, found {}", 353 self.stack.len() 354 ) 355 } 356 357 let start = self.stack.len() - array_size; 358 359 let elem_ty = ty.element_type(); 360 let elem_ty = elem_ty.unpack(); 361 362 let elems = self 363 .stack 364 .drain(start..) 365 .map(|raw| Val::_from_raw(&mut store, raw, elem_ty)) 366 .collect::<SmallVec<[_; 8]>>(); 367 368 let pre = ArrayRefPre::_new(&mut store, ty); 369 let array = ArrayRef::_new_fixed(&mut store, &pre, &elems)?; 370 371 self.stack 372 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?)); 373 } 374 } 375 } 376 377 if self.stack.len() == 1 { 378 log::trace!("const expr evaluated to {:?}", self.stack[0]); 379 Ok(self.stack[0]) 380 } else { 381 bail!( 382 "const expr evaluation error: expected 1 resulting value, found {}", 383 self.stack.len() 384 ) 385 } 386 } 387 388 fn pop(&mut self) -> Result<ValRaw> { 389 self.stack.pop().ok_or_else(|| { 390 anyhow!( 391 "const expr evaluation error: attempted to pop from an empty \ 392 evaluation stack" 393 ) 394 }) 395 } 396 } 397