1 //! Evaluating const expressions.
2 
3 use crate::prelude::*;
4 use crate::runtime::vm::{Instance, VMGcRef, ValRaw, I31};
5 use smallvec::SmallVec;
6 use wasmtime_environ::{ConstExpr, FuncIndex, GlobalIndex, Module};
7 
8 /// An interpreter for const expressions.
9 ///
10 /// This can be reused across many const expression evaluations to reuse
11 /// allocated resources, if any.
12 #[derive(Default)]
13 pub struct ConstExprEvaluator {
14     stack: SmallVec<[ValRaw; 2]>,
15 }
16 
17 /// The context within which a particular const expression is evaluated.
18 pub struct ConstEvalContext<'a, 'b> {
19     instance: &'a mut Instance,
20     module: &'b Module,
21 }
22 
23 impl<'a, 'b> ConstEvalContext<'a, 'b> {
24     /// Create a new context.
25     pub fn new(instance: &'a mut Instance, module: &'b Module) -> Self {
26         Self { instance, module }
27     }
28 
29     fn global_get(&mut self, index: GlobalIndex) -> Result<ValRaw> {
30         unsafe {
31             let gc_store = (*self.instance.store()).unwrap_gc_store_mut();
32             let global = self
33                 .instance
34                 .defined_or_imported_global_ptr(index)
35                 .as_ref()
36                 .unwrap();
37             Ok(global.to_val_raw(gc_store, self.module.globals[index].wasm_ty))
38         }
39     }
40 
41     fn ref_func(&mut self, index: FuncIndex) -> Result<ValRaw> {
42         Ok(ValRaw::funcref(
43             self.instance.get_func_ref(index).unwrap().cast(),
44         ))
45     }
46 }
47 
48 impl ConstExprEvaluator {
49     /// Evaluate the given const expression in the given context.
50     ///
51     /// # Unsafety
52     ///
53     /// The given const expression must be valid within the given context,
54     /// e.g. the const expression must be well-typed and the context must return
55     /// global values of the expected types. This evaluator operates directly on
56     /// untyped `ValRaw`s and does not and cannot check that its operands are of
57     /// the correct type.
58     pub unsafe fn eval(
59         &mut self,
60         context: &mut ConstEvalContext<'_, '_>,
61         expr: &ConstExpr,
62     ) -> Result<ValRaw> {
63         self.stack.clear();
64 
65         for op in expr.ops() {
66             match op {
67                 wasmtime_environ::ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)),
68                 wasmtime_environ::ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)),
69                 wasmtime_environ::ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)),
70                 wasmtime_environ::ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)),
71                 wasmtime_environ::ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)),
72                 wasmtime_environ::ConstOp::GlobalGet(g) => self.stack.push(context.global_get(*g)?),
73                 wasmtime_environ::ConstOp::RefNull => self.stack.push(ValRaw::null()),
74                 wasmtime_environ::ConstOp::RefFunc(f) => self.stack.push(context.ref_func(*f)?),
75                 wasmtime_environ::ConstOp::RefI31 => {
76                     let i = self.pop()?.get_i32();
77                     let i31 = I31::wrapping_i32(i);
78                     let raw = VMGcRef::from_i31(i31).as_raw_u32();
79                     self.stack.push(ValRaw::anyref(raw));
80                 }
81                 wasmtime_environ::ConstOp::I32Add => {
82                     let b = self.pop()?.get_i32();
83                     let a = self.pop()?.get_i32();
84                     self.stack.push(ValRaw::i32(a.wrapping_add(b)));
85                 }
86                 wasmtime_environ::ConstOp::I32Sub => {
87                     let b = self.pop()?.get_i32();
88                     let a = self.pop()?.get_i32();
89                     self.stack.push(ValRaw::i32(a.wrapping_sub(b)));
90                 }
91                 wasmtime_environ::ConstOp::I32Mul => {
92                     let b = self.pop()?.get_i32();
93                     let a = self.pop()?.get_i32();
94                     self.stack.push(ValRaw::i32(a.wrapping_mul(b)));
95                 }
96                 wasmtime_environ::ConstOp::I64Add => {
97                     let b = self.pop()?.get_i64();
98                     let a = self.pop()?.get_i64();
99                     self.stack.push(ValRaw::i64(a.wrapping_add(b)));
100                 }
101                 wasmtime_environ::ConstOp::I64Sub => {
102                     let b = self.pop()?.get_i64();
103                     let a = self.pop()?.get_i64();
104                     self.stack.push(ValRaw::i64(a.wrapping_sub(b)));
105                 }
106                 wasmtime_environ::ConstOp::I64Mul => {
107                     let b = self.pop()?.get_i64();
108                     let a = self.pop()?.get_i64();
109                     self.stack.push(ValRaw::i64(a.wrapping_mul(b)));
110                 }
111             }
112         }
113 
114         if self.stack.len() == 1 {
115             Ok(self.stack[0])
116         } else {
117             bail!(
118                 "const expr evaluation error: expected 1 resulting value, found {}",
119                 self.stack.len()
120             )
121         }
122     }
123 
124     fn pop(&mut self) -> Result<ValRaw> {
125         self.stack.pop().ok_or_else(|| {
126             anyhow!(
127                 "const expr evaluation error: attempted to pop from an empty \
128                  evaluation stack"
129             )
130         })
131     }
132 }
133