Next Generation WASM Microkernel Operating System
at trap_handler 335 lines 13 kB view raw
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}