Next Generation WASM Microkernel Operating System
at trap_handler 172 lines 6.2 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::arch::PAGE_SIZE; 9use crate::wasm::translate::{WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType}; 10use cranelift_codegen::ir; 11use cranelift_codegen::ir::{AbiParam, ArgumentPurpose, Signature}; 12use cranelift_codegen::isa::{CallConv, TargetIsa}; 13 14/// Helper macro to generate accessors for an enum. 15macro_rules! enum_accessors { 16 (@$bind:ident, $variant:ident, $ty:ty, $is:ident, $get:ident, $unwrap:ident, $cvt:expr) => { 17 /// Returns true when the enum is the correct variant. 18 pub fn $is(&self) -> bool { 19 matches!(self, Self::$variant(_)) 20 } 21 22 /// Returns the variant's value, returning None if it is not the correct type. 23 #[inline] 24 pub fn $get(&self) -> Option<$ty> { 25 if let Self::$variant($bind) = self { 26 Some($cvt) 27 } else { 28 None 29 } 30 } 31 32 /// Returns the variant's value, panicking if it is not the correct type. 33 /// 34 /// # Panics 35 /// 36 /// Panics if `self` is not of the right type. 37 #[inline] 38 pub fn $unwrap(&self) -> $ty { 39 self.$get().expect(concat!("expected ", stringify!($ty))) 40 } 41 }; 42 ($bind:ident $(($variant:ident($ty:ty) $is:ident $get:ident $unwrap:ident $cvt:expr))*) => ($(enum_accessors!{@$bind, $variant, $ty, $is, $get, $unwrap, $cvt})*) 43} 44 45/// Like `enum_accessors!`, but generated methods take ownership of `self`. 46macro_rules! owned_enum_accessors { 47 ($bind:ident $(($variant:ident($ty:ty) $get:ident $cvt:expr))*) => ($( 48 /// Attempt to access the underlying value of this `Val`, returning 49 /// `None` if it is not the correct type. 50 #[inline] 51 pub fn $get(self) -> Option<$ty> { 52 if let Self::$variant($bind) = self { 53 Some($cvt) 54 } else { 55 None 56 } 57 } 58 )*) 59} 60 61/// Like `offset_of!`, but returns a `u32`. 62/// 63/// # Panics 64/// 65/// Panics if the offset is too large to fit in a `u32`. 66macro_rules! u32_offset_of { 67 ($ty:ident, $field:ident) => { 68 u32::try_from(core::mem::offset_of!($ty, $field)).unwrap() 69 }; 70} 71 72pub(crate) use {enum_accessors, owned_enum_accessors, u32_offset_of}; 73 74pub fn value_type(ty: &WasmValType, pointer_type: ir::Type) -> ir::Type { 75 match ty { 76 WasmValType::I32 => ir::types::I32, 77 WasmValType::I64 => ir::types::I64, 78 WasmValType::F32 => ir::types::F32, 79 WasmValType::F64 => ir::types::F64, 80 WasmValType::V128 => ir::types::I8X16, 81 WasmValType::Ref(rf) => reference_type(&rf.heap_type, pointer_type), 82 } 83} 84 85/// Returns the reference type to use for the provided wasm type. 86pub fn reference_type(wasm_ht: &WasmHeapType, pointer_type: ir::Type) -> ir::Type { 87 match wasm_ht.top().0 { 88 WasmHeapTopType::Func => pointer_type, 89 WasmHeapTopType::Any | WasmHeapTopType::Extern => ir::types::I32, 90 WasmHeapTopType::Exn => todo!(), 91 WasmHeapTopType::Cont => todo!(), 92 } 93} 94 95fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> Signature { 96 let pointer_type = isa.pointer_type(); 97 let mut sig = Signature::new(call_conv); 98 99 // Add the caller/callee `vmctx` parameters. 100 sig.params 101 .push(AbiParam::special(pointer_type, ArgumentPurpose::VMContext)); 102 sig.params.push(AbiParam::new(pointer_type)); 103 104 sig 105} 106 107pub fn wasm_call_signature(isa: &dyn TargetIsa, func_ty: &WasmFuncType) -> Signature { 108 let mut sig = blank_sig(isa, CallConv::Fast); 109 110 let cvt = |ty: &WasmValType| AbiParam::new(value_type(ty, isa.pointer_type())); 111 sig.params.extend(func_ty.params.iter().map(&cvt)); 112 sig.returns.extend(func_ty.results.iter().map(&cvt)); 113 114 sig 115} 116 117/// Get the Cranelift signature for all array-call functions, that is: 118/// 119/// ```ignore 120/// unsafe extern "C" fn( 121/// callee_vmctx: *mut VMOpaqueContext, 122/// caller_vmctx: *mut VMOpaqueContext, 123/// values_ptr: *mut ValRaw, 124/// values_len: usize, 125/// ) 126/// ``` 127/// 128/// This signature uses the target's default calling convention. 129/// 130/// Note that regardless of the Wasm function type, the array-call calling 131/// convention always uses that same signature. 132pub fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature { 133 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple())); 134 // The array-call signature has an added parameter for the `values_vec` 135 // input/output buffer in addition to the size of the buffer, in units 136 // of `ValRaw`. 137 sig.params.push(AbiParam::new(isa.pointer_type())); 138 sig.params.push(AbiParam::new(isa.pointer_type())); 139 // boolean return value of whether this function trapped 140 sig.returns.push(ir::AbiParam::new(ir::types::I8)); 141 sig 142} 143 144/// Is `bytes` a multiple of the host page size? 145pub fn usize_is_multiple_of_host_page_size(bytes: usize) -> bool { 146 bytes % PAGE_SIZE == 0 147} 148 149pub fn round_u64_up_to_host_pages(bytes: u64) -> u64 { 150 let page_size = u64::try_from(PAGE_SIZE).unwrap(); 151 debug_assert!(page_size.is_power_of_two()); 152 let page_size_minus_one = page_size.checked_sub(1).unwrap(); 153 bytes 154 .checked_add(page_size_minus_one) 155 .map(|val| val & !page_size_minus_one) 156 .unwrap_or_else(|| panic!("{bytes} is too large to be rounded up to a multiple of the host page size of {page_size}")) 157} 158 159/// Same as `round_u64_up_to_host_pages` but for `usize`s. 160pub fn round_usize_up_to_host_pages(bytes: usize) -> usize { 161 let bytes = u64::try_from(bytes).unwrap(); 162 let rounded = round_u64_up_to_host_pages(bytes); 163 usize::try_from(rounded).unwrap() 164} 165 166/// Like `mem::size_of` but returns `u8` instead of `usize` 167/// # Panics 168/// 169/// Panics if the size of `T` is greater than `u8::MAX`. 170pub fn u8_size_of<T: Sized>() -> u8 { 171 u8::try_from(size_of::<T>()).expect("type size is too large to be represented as a u8") 172}