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 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}