Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::wasm::indices::{FuncIndex, GlobalIndex, VMSharedTypeIndex};
9use crate::wasm::store::StoreOpaque;
10use crate::wasm::translate::{
11 ConstExpr, ConstOp, WasmCompositeType, WasmCompositeTypeInner, WasmStorageType, WasmSubType,
12 WasmValType,
13};
14use crate::wasm::vm::instance::Instance;
15use crate::wasm::vm::vmcontext::VMVal;
16use anyhow::bail;
17use smallvec::SmallVec;
18
19/// Simple interpreter for constant expressions.
20#[derive(Debug, Default)]
21pub struct ConstExprEvaluator {
22 stack: SmallVec<[VMVal; 2]>,
23}
24
25/// The context within which a particular const expression is evaluated.
26pub struct ConstEvalContext<'a> {
27 pub(crate) instance: &'a mut Instance,
28}
29
30impl<'a> ConstEvalContext<'a> {
31 /// Create a new context.
32 ///
33 /// # Safety
34 ///
35 /// The caller must ensure `Instance` has its vmctx correctly initialized
36 pub unsafe fn new(instance: &'a mut Instance) -> Self {
37 Self { instance }
38 }
39
40 fn global_get(&mut self, store: &mut StoreOpaque, index: GlobalIndex) -> crate::Result<VMVal> {
41 // Safety: the caller promised that the vmctx is correctly initialized
42 unsafe {
43 let global = self.instance.defined_or_imported_global(index).as_ref();
44 global.to_vmval(
45 store,
46 self.instance.translated_module().globals[index].content_type,
47 )
48 }
49 }
50
51 fn ref_func(&mut self, index: FuncIndex) -> VMVal {
52 VMVal::funcref(self.instance.get_func_ref(index).unwrap().as_ptr().cast())
53 }
54
55 fn struct_fields_len(&self, _store: &mut StoreOpaque, _shared_ty: VMSharedTypeIndex) -> usize {
56 // let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
57 // let fields = struct_ty.fields();
58 // fields.len()
59 todo!()
60 }
61
62 /// Safety: field values must be of the correct types.
63 unsafe fn struct_new(
64 &mut self,
65 _store: &mut StoreOpaque,
66 _shared_ty: VMSharedTypeIndex,
67 _fields: &[VMVal],
68 ) -> crate::Result<VMVal> {
69 // let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
70 // let fields = fields
71 // .iter()
72 // .zip(struct_ty.fields())
73 // .map(|(raw, ty)| {
74 // let ty = ty.element_type().unpack();
75 // Val::from_vmval(store, *raw, ty)
76 // })
77 // .collect::<Vec<_>>();
78
79 // let allocator = StructRefPre::_new(store, struct_ty);
80 // let struct_ref = StructRef::_new(store, &allocator, &fields)?;
81 // let raw = struct_ref.to_anyref()._to_raw(store)?;
82 // Ok(VMVal::anyref(raw))
83
84 todo!()
85 }
86
87 fn struct_new_default(
88 &mut self,
89 store: &mut StoreOpaque,
90 shared_ty: VMSharedTypeIndex,
91 ) -> crate::Result<VMVal> {
92 let module = self.instance.module();
93
94 let borrowed = module
95 .engine()
96 .type_registry()
97 .borrow(shared_ty)
98 .expect("should have a registered type for struct");
99 let WasmSubType {
100 composite_type:
101 WasmCompositeType {
102 shared: false,
103 inner: WasmCompositeTypeInner::Struct(struct_ty),
104 },
105 ..
106 } = &*borrowed
107 else {
108 unreachable!("registered type should be a struct");
109 };
110
111 let fields = struct_ty
112 .fields
113 .iter()
114 .map(|ty| match &ty.element_type {
115 WasmStorageType::I8 | WasmStorageType::I16 => VMVal::i32(0),
116 WasmStorageType::Val(v) => match v {
117 WasmValType::I32 => VMVal::i32(0),
118 WasmValType::I64 => VMVal::i64(0),
119 WasmValType::F32 => VMVal::f32(0.0f32.to_bits()),
120 WasmValType::F64 => VMVal::f64(0.0f64.to_bits()),
121 WasmValType::V128 => VMVal::v128(0),
122 WasmValType::Ref(r) => {
123 assert!(r.nullable);
124 VMVal::null()
125 }
126 },
127 })
128 .collect::<SmallVec<[_; 8]>>();
129
130 // Safety: TODO
131 unsafe { self.struct_new(store, shared_ty, &fields) }
132 }
133}
134
135impl ConstExprEvaluator {
136 /// Evaluate a `ConstExpr` returning the result value.
137 ///
138 /// The only use of const expressions at the moment is to produce init values for globals,
139 /// or tables or to calculate offsets. As such all uses *require* a const expression to return
140 /// exactly one result.
141 ///
142 /// # Errors
143 ///
144 /// TODO
145 ///
146 /// # Panics
147 ///
148 /// The only uses of const expressions require them to evaluate to exactly one result.
149 /// This method will panic if there is not exactly one result.
150 pub fn eval(
151 &mut self,
152 store: &mut StoreOpaque,
153 ctx: &mut ConstEvalContext,
154 expr: &ConstExpr,
155 ) -> crate::Result<VMVal> {
156 for op in expr.ops() {
157 match op {
158 ConstOp::I32Const(value) => self.push(VMVal::i32(value)),
159 ConstOp::I64Const(value) => self.push(VMVal::i64(value)),
160 ConstOp::F32Const(value) => self.push(VMVal::f32(value)),
161 ConstOp::F64Const(value) => self.push(VMVal::f64(value)),
162 ConstOp::V128Const(value) => self.push(VMVal::v128(value)),
163 ConstOp::GlobalGet(g) => self.stack.push(ctx.global_get(store, g)?),
164 ConstOp::RefNull => self.stack.push(VMVal::null()),
165 ConstOp::RefFunc(f) => self.stack.push(ctx.ref_func(f)),
166 ConstOp::RefI31 => {
167 // let i = self.pop()?.get_i32();
168 // let i31 = I31::wrapping_i32(i);
169 // let raw = VMGcRef::from_i31(i31).as_raw_u32();
170 // self.stack.push(VMVal::anyref(raw));
171
172 todo!()
173 }
174 ConstOp::I32Add => {
175 let (arg1, arg2) = self.pop2();
176
177 self.push(VMVal::i32(arg1.get_i32().wrapping_add(arg2.get_i32())));
178 }
179 ConstOp::I32Sub => {
180 let (arg1, arg2) = self.pop2();
181
182 self.push(VMVal::i32(arg1.get_i32().wrapping_sub(arg2.get_i32())));
183 }
184 ConstOp::I32Mul => {
185 let (arg1, arg2) = self.pop2();
186
187 self.push(VMVal::i32(arg1.get_i32().wrapping_mul(arg2.get_i32())));
188 }
189 ConstOp::I64Add => {
190 let (arg1, arg2) = self.pop2();
191
192 self.push(VMVal::i64(arg1.get_i64().wrapping_add(arg2.get_i64())));
193 }
194 ConstOp::I64Sub => {
195 let (arg1, arg2) = self.pop2();
196
197 self.push(VMVal::i64(arg1.get_i64().wrapping_sub(arg2.get_i64())));
198 }
199 ConstOp::I64Mul => {
200 let (arg1, arg2) = self.pop2();
201
202 self.push(VMVal::i64(arg1.get_i64().wrapping_mul(arg2.get_i64())));
203 }
204 ConstOp::StructNew {
205 struct_type_index: _,
206 } => {
207 // let interned_type_index = ctx.instance.env_module().types[*struct_type_index]
208 // .unwrap_engine_type_index();
209 // let len = ctx.struct_fields_len(&mut store, interned_type_index);
210 //
211 // if self.stack.len() < len {
212 // bail!(
213 // "const expr evaluation error: expected at least {len} values on the stack, found {}",
214 // self.stack.len()
215 // )
216 // }
217 //
218 // let start = self.stack.len() - len;
219 // let s = unsafe {
220 // ctx.struct_new(&mut store, interned_type_index, &self.stack[start..])?
221 // };
222 // self.stack.truncate(start);
223 // self.stack.push(s);
224
225 todo!()
226 }
227 ConstOp::StructNewDefault {
228 struct_type_index: _,
229 } => {
230 // let ty = ctx.instance.env_module().types[*struct_type_index]
231 // .unwrap_engine_type_index();
232 // self.stack.push(ctx.struct_new_default(&mut store, ty)?);
233
234 todo!()
235 }
236 ConstOp::ArrayNew {
237 array_type_index: _,
238 } => {
239 // let ty = ctx.instance.env_module().types[*array_type_index]
240 // .unwrap_engine_type_index();
241 // let ty = ArrayType::from_shared_type_index(store.engine(), ty);
242 //
243 // #[allow(clippy::cast_sign_loss)]
244 // let len = self.pop()?.get_i32() as u32;
245 //
246 // let elem = Val::from_vmval(&mut store, self.pop()?, ty.element_type().unpack());
247 //
248 // let pre = ArrayRefPre::_new(&mut store, ty);
249 // let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
250 //
251 // self.stack
252 // .push(VMVal::anyref(array.to_anyref()._to_raw(&mut store)?));
253
254 todo!()
255 }
256 ConstOp::ArrayNewDefault {
257 array_type_index: _,
258 } => {
259 // let ty = ctx.instance.env_module().types[*array_type_index]
260 // .unwrap_engine_type_index();
261 // let ty = ArrayType::from_shared_type_index(store.engine(), ty);
262 //
263 // #[allow(clippy::cast_sign_loss)]
264 // let len = self.pop()?.get_i32() as u32;
265 //
266 // let elem = Val::default_for_ty(ty.element_type().unpack())
267 // .expect("type should have a default value");
268 //
269 // let pre = ArrayRefPre::_new(&mut store, ty);
270 // let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
271 //
272 // self.stack
273 // .push(VMVal::anyref(array.to_anyref()._to_raw(&mut store)?));
274
275 todo!()
276 }
277 ConstOp::ArrayNewFixed {
278 array_type_index: _,
279 array_size: _,
280 } => {
281 // let ty = ctx.instance.env_module().types[*array_type_index]
282 // .unwrap_engine_type_index();
283 // let ty = ArrayType::from_shared_type_index(store.engine(), ty);
284 //
285 // let array_size = usize::try_from(*array_size).unwrap();
286 // if self.stack.len() < array_size {
287 // bail!(
288 // "const expr evaluation error: expected at least {array_size} values on the stack, found {}",
289 // self.stack.len()
290 // )
291 // }
292 //
293 // let start = self.stack.len() - array_size;
294 //
295 // let elem_ty = ty.element_type();
296 // let elem_ty = elem_ty.unpack();
297 //
298 // let elems = self
299 // .stack
300 // .drain(start..)
301 // .map(|raw| Val::_from_raw(&mut store, raw, elem_ty))
302 // .collect::<SmallVec<[_; 8]>>();
303 //
304 // let pre = ArrayRefPre::_new(&mut store, ty);
305 // let array = ArrayRef::_new_fixed(&mut store, &pre, &elems)?;
306 //
307 // self.stack
308 // .push(VMVal::anyref(array.to_anyref()._to_raw(&mut store)?));
309
310 todo!()
311 }
312 }
313 }
314
315 if self.stack.len() == 1 {
316 tracing::trace!("const expr evaluated to {:?}", self.stack[0]);
317 Ok(self.stack.pop().unwrap())
318 } else {
319 let len = self.stack.len();
320 // we need to correctly clear the stack here for the next time we try to use the const eval
321 self.stack.clear();
322 bail!("const expr evaluation error: expected 1 resulting value, found {len}",)
323 }
324 }
325
326 fn push(&mut self, val: VMVal) {
327 self.stack.push(val);
328 }
329
330 fn pop2(&mut self) -> (VMVal, VMVal) {
331 let v2 = self.stack.pop().unwrap();
332 let v1 = self.stack.pop().unwrap();
333 (v1, v2)
334 }
335}