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