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