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