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