1use crate::prelude::*;
4use crate::runtime::vm::{Instance, VMGcRef, ValRaw, I31};
5use crate::store::{AutoAssertNoGc, StoreOpaque};
6#[cfg(feature = "gc")]
7use crate::{ArrayRef, ArrayRefPre, ArrayType, StructRef, StructRefPre, StructType, Val};
8use smallvec::SmallVec;
9use wasmtime_environ::{ConstExpr, ConstOp, FuncIndex, GlobalIndex};
10#[cfg(feature = "gc")]
11use wasmtime_environ::{VMSharedTypeIndex, WasmCompositeInnerType, WasmCompositeType, WasmSubType};
12
13#[derive(Default)]
18pub struct ConstExprEvaluator {
19 stack: SmallVec<[ValRaw; 2]>,
20}
21
22pub struct ConstEvalContext<'a> {
24 pub(crate) instance: &'a mut Instance,
25}
26
27impl<'a> ConstEvalContext<'a> {
28 pub fn new(instance: &'a mut Instance) -> Self {
30 Self { instance }
31 }
32
33 fn global_get(&mut self, store: &mut AutoAssertNoGc<'_>, index: GlobalIndex) -> Result<ValRaw> {
34 unsafe {
35 let global = self.instance.defined_or_imported_global_ptr(index).as_ref();
36 global.to_val_raw(store, self.instance.env_module().globals[index].wasm_ty)
37 }
38 }
39
40 fn ref_func(&mut self, index: FuncIndex) -> Result<ValRaw> {
41 Ok(ValRaw::funcref(
42 self.instance.get_func_ref(index).unwrap().as_ptr().cast(),
43 ))
44 }
45
46 #[cfg(feature = "gc")]
47 fn struct_fields_len(
48 &self,
49 store: &mut AutoAssertNoGc<'_>,
50 shared_ty: VMSharedTypeIndex,
51 ) -> usize {
52 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
53 let fields = struct_ty.fields();
54 fields.len()
55 }
56
57 #[cfg(feature = "gc")]
59 unsafe fn struct_new(
60 &mut self,
61 store: &mut AutoAssertNoGc<'_>,
62 shared_ty: VMSharedTypeIndex,
63 fields: &[ValRaw],
64 ) -> Result<ValRaw> {
65 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
66 let fields = fields
67 .iter()
68 .zip(struct_ty.fields())
69 .map(|(raw, ty)| {
70 let ty = ty.element_type().unpack();
71 Val::_from_raw(store, *raw, ty)
72 })
73 .collect::<Vec<_>>();
74
75 let allocator = StructRefPre::_new(store, struct_ty);
76 let struct_ref = StructRef::_new(store, &allocator, &fields)?;
77 let raw = struct_ref.to_anyref()._to_raw(store)?;
78 Ok(ValRaw::anyref(raw))
79 }
80
81 #[cfg(feature = "gc")]
82 fn struct_new_default(
83 &mut self,
84 store: &mut AutoAssertNoGc<'_>,
85 shared_ty: VMSharedTypeIndex,
86 ) -> Result<ValRaw> {
87 let module = self
88 .instance
89 .runtime_module()
90 .expect("should never be allocating a struct type defined in a dummy module");
91
92 let borrowed = module
93 .engine()
94 .signatures()
95 .borrow(shared_ty)
96 .expect("should have a registered type for struct");
97 let WasmSubType {
98 composite_type:
99 WasmCompositeType {
100 shared: false,
101 inner: WasmCompositeInnerType::Struct(struct_ty),
102 },
103 ..
104 } = &*borrowed
105 else {
106 unreachable!("registered type should be a struct");
107 };
108
109 let fields = struct_ty
110 .fields
111 .iter()
112 .map(|ty| match &ty.element_type {
113 wasmtime_environ::WasmStorageType::I8 | wasmtime_environ::WasmStorageType::I16 => {
114 ValRaw::i32(0)
115 }
116 wasmtime_environ::WasmStorageType::Val(v) => match v {
117 wasmtime_environ::WasmValType::I32 => ValRaw::i32(0),
118 wasmtime_environ::WasmValType::I64 => ValRaw::i64(0),
119 wasmtime_environ::WasmValType::F32 => ValRaw::f32(0.0f32.to_bits()),
120 wasmtime_environ::WasmValType::F64 => ValRaw::f64(0.0f64.to_bits()),
121 wasmtime_environ::WasmValType::V128 => ValRaw::v128(0),
122 wasmtime_environ::WasmValType::Ref(r) => {
123 assert!(r.nullable);
124 ValRaw::null()
125 }
126 },
127 })
128 .collect::<SmallVec<[_; 8]>>();
129
130 unsafe { self.struct_new(store, shared_ty, &fields) }
131 }
132}
133
134impl ConstExprEvaluator {
135 pub unsafe fn eval(
145 &mut self,
146 store: &mut StoreOpaque,
147 context: &mut ConstEvalContext<'_>,
148 expr: &ConstExpr,
149 ) -> Result<ValRaw> {
150 log::trace!("evaluating const expr: {:?}", expr);
151
152 self.stack.clear();
153
154 #[cfg(feature = "gc")]
158 let mut store = crate::OpaqueRootScope::new(store);
159 #[cfg(not(feature = "gc"))]
160 let mut store = store;
161
162 let mut store = AutoAssertNoGc::new(&mut store);
168
169 for op in expr.ops() {
170 log::trace!("const-evaluating op: {op:?}");
171 match op {
172 ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)),
173 ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)),
174 ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)),
175 ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)),
176 ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)),
177 ConstOp::GlobalGet(g) => self.stack.push(context.global_get(&mut store, *g)?),
178 ConstOp::RefNull => self.stack.push(ValRaw::null()),
179 ConstOp::RefFunc(f) => self.stack.push(context.ref_func(*f)?),
180 ConstOp::RefI31 => {
181 let i = self.pop()?.get_i32();
182 let i31 = I31::wrapping_i32(i);
183 let raw = VMGcRef::from_i31(i31).as_raw_u32();
184 self.stack.push(ValRaw::anyref(raw));
185 }
186 ConstOp::I32Add => {
187 let b = self.pop()?.get_i32();
188 let a = self.pop()?.get_i32();
189 self.stack.push(ValRaw::i32(a.wrapping_add(b)));
190 }
191 ConstOp::I32Sub => {
192 let b = self.pop()?.get_i32();
193 let a = self.pop()?.get_i32();
194 self.stack.push(ValRaw::i32(a.wrapping_sub(b)));
195 }
196 ConstOp::I32Mul => {
197 let b = self.pop()?.get_i32();
198 let a = self.pop()?.get_i32();
199 self.stack.push(ValRaw::i32(a.wrapping_mul(b)));
200 }
201 ConstOp::I64Add => {
202 let b = self.pop()?.get_i64();
203 let a = self.pop()?.get_i64();
204 self.stack.push(ValRaw::i64(a.wrapping_add(b)));
205 }
206 ConstOp::I64Sub => {
207 let b = self.pop()?.get_i64();
208 let a = self.pop()?.get_i64();
209 self.stack.push(ValRaw::i64(a.wrapping_sub(b)));
210 }
211 ConstOp::I64Mul => {
212 let b = self.pop()?.get_i64();
213 let a = self.pop()?.get_i64();
214 self.stack.push(ValRaw::i64(a.wrapping_mul(b)));
215 }
216
217 #[cfg(not(feature = "gc"))]
218 ConstOp::StructNew { .. }
219 | ConstOp::StructNewDefault { .. }
220 | ConstOp::ArrayNew { .. }
221 | ConstOp::ArrayNewDefault { .. }
222 | ConstOp::ArrayNewFixed { .. } => {
223 bail!(
224 "const expr evaluation error: struct operations are not \
225 supported without the `gc` feature"
226 )
227 }
228
229 #[cfg(feature = "gc")]
230 ConstOp::StructNew { struct_type_index } => {
231 let interned_type_index = context.instance.env_module().types
232 [*struct_type_index]
233 .unwrap_engine_type_index();
234 let len = context.struct_fields_len(&mut store, interned_type_index);
235
236 if self.stack.len() < len {
237 bail!(
238 "const expr evaluation error: expected at least {len} values on the stack, found {}",
239 self.stack.len()
240 )
241 }
242
243 let start = self.stack.len() - len;
244 let s = context.struct_new(
245 &mut store,
246 interned_type_index,
247 &self.stack[start..],
248 )?;
249 self.stack.truncate(start);
250 self.stack.push(s);
251 }
252
253 #[cfg(feature = "gc")]
254 ConstOp::StructNewDefault { struct_type_index } => {
255 let ty = context.instance.env_module().types[*struct_type_index]
256 .unwrap_engine_type_index();
257 self.stack.push(context.struct_new_default(&mut store, ty)?);
258 }
259
260 #[cfg(feature = "gc")]
261 ConstOp::ArrayNew { array_type_index } => {
262 let ty = context.instance.env_module().types[*array_type_index]
263 .unwrap_engine_type_index();
264 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
265
266 #[allow(clippy::cast_sign_loss)]
267 let len = self.pop()?.get_i32() as u32;
268
269 let elem = Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack());
270
271 let pre = ArrayRefPre::_new(&mut store, ty);
272 let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
273
274 self.stack
275 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
276 }
277
278 #[cfg(feature = "gc")]
279 ConstOp::ArrayNewDefault { array_type_index } => {
280 let ty = context.instance.env_module().types[*array_type_index]
281 .unwrap_engine_type_index();
282 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
283
284 #[allow(clippy::cast_sign_loss)]
285 let len = self.pop()?.get_i32() as u32;
286
287 let elem = Val::default_for_ty(ty.element_type().unpack())
288 .expect("type should have a default value");
289
290 let pre = ArrayRefPre::_new(&mut store, ty);
291 let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
292
293 self.stack
294 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
295 }
296
297 #[cfg(feature = "gc")]
298 ConstOp::ArrayNewFixed {
299 array_type_index,
300 array_size,
301 } => {
302 let ty = context.instance.env_module().types[*array_type_index]
303 .unwrap_engine_type_index();
304 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
305
306 let array_size = usize::try_from(*array_size).unwrap();
307 if self.stack.len() < array_size {
308 bail!(
309 "const expr evaluation error: expected at least {array_size} values on the stack, found {}",
310 self.stack.len()
311 )
312 }
313
314 let start = self.stack.len() - array_size;
315
316 let elem_ty = ty.element_type();
317 let elem_ty = elem_ty.unpack();
318
319 let elems = self
320 .stack
321 .drain(start..)
322 .map(|raw| Val::_from_raw(&mut store, raw, elem_ty))
323 .collect::<SmallVec<[_; 8]>>();
324
325 let pre = ArrayRefPre::_new(&mut store, ty);
326 let array = ArrayRef::_new_fixed(&mut store, &pre, &elems)?;
327
328 self.stack
329 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
330 }
331 }
332 }
333
334 if self.stack.len() == 1 {
335 log::trace!("const expr evaluated to {:?}", self.stack[0]);
336 Ok(self.stack[0])
337 } else {
338 bail!(
339 "const expr evaluation error: expected 1 resulting value, found {}",
340 self.stack.len()
341 )
342 }
343 }
344
345 fn pop(&mut self) -> Result<ValRaw> {
346 self.stack.pop().ok_or_else(|| {
347 anyhow!(
348 "const expr evaluation error: attempted to pop from an empty \
349 evaluation stack"
350 )
351 })
352 }
353}