Skip to content

Commit

Permalink
feat(wasm): Pack multivalue return into u64 for C ABI compatibility
Browse files Browse the repository at this point in the history
Event though clang can compile C struct into multi-value return,
by nature C doesn't have notion of multi-value return.
  • Loading branch information
DrSensor committed Sep 11, 2023
1 parent f6e683a commit 4636bf0
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
10 changes: 9 additions & 1 deletion core/wasm/accessor/number.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
mod index;
mod types;

use core::ffi::c_void;
use index::current as index;
use types::{number::Type, JSNumber, Null, Number};
use types::{ffi::CTuple, number::Type, JSNumber, Null, Number};

type Setter = unsafe fn(Number, JSNumber);
type Getter = unsafe fn(Number) -> JSNumber;
Expand All @@ -27,6 +28,13 @@ fn accessor(ty: Type) -> (Getter, Setter) {
}
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
#[export_name = "cABIaccessor"]
fn c_accessor(ty: Type) -> CTuple<*const c_void, *const c_void> {
let (getter, setter) = accessor(ty);
CTuple::from((getter as *const c_void, setter as *const c_void))
}

#[no_mangle]
fn set(set: fn(Number, JSNumber), this: Number, val: JSNumber) {
set(this, val)
Expand Down
8 changes: 7 additions & 1 deletion core/wasm/instance/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod offset;
mod types;

use core::arch::wasm32::{memory_grow, memory_size};
use types::{number::Type, Null, Number};
use types::{ffi::CTuple, number::Type, Null, Number};

static PAGE: usize = u16::MAX as usize + 1; // 1 page = 64KiB = 65536

Expand Down Expand Up @@ -37,3 +37,9 @@ unsafe fn array_of(ty: Type, len: u16, nullable: bool) -> (Number, Null) {

(Number { addr }, Null { addr: null_addr })
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
#[export_name = "cABIallocate"]
unsafe fn c_array_of(ty: Type, len: u16, nullable: bool) -> CTuple<Number, Null> {
CTuple::from(array_of(ty, len, nullable))
}
83 changes: 83 additions & 0 deletions core/wasm/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,86 @@ pub use number::{JSNumber, Number};
pub struct Null {
pub addr: usize,
}

macro_rules! convert_between {
($into:ty |$($G:ident)?| $from:ty) => {
impl$(<$G>)? From<$from> for $into {
fn from(ptr: $from) -> Self {
let addr = ptr as usize;
Self { addr }
}
}
impl$(<$G>)? From<$into> for $from {
fn from(value: $into) -> Self {
value.addr as Self
}
}
};
}

pub(crate) use convert_between;
convert_between!(Null |T| *const T);
convert_between!(Number |T| *const T);

#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
pub mod ffi {
use core::ffi::c_void;
use core::marker::PhantomData;

#[repr(transparent)]
pub struct CTuple<L, R> {
lhs: PhantomData<L>,
rhs: PhantomData<R>,

#[cfg(target_pointer_width = "32")]
pub addr: u64,

#[cfg(target_pointer_width = "16")]
pub addr: u32,
}

impl<L, R> From<(L, R)> for CTuple<L, R>
where
L: Into<*const c_void>,
R: Into<*const c_void>,
{
fn from(tuple: (L, R)) -> Self {
let (lhs, rhs) = tuple;
let lead = (lhs.into() as u64) << usize::BITS;
let trail = rhs.into() as u64;
Self::from(lead & trail)
}
}

impl<L, R> From<CTuple<L, R>> for (L, R)
where
L: From<*const c_void>,
R: From<*const c_void>,
{
fn from(ctuple: CTuple<L, R>) -> Self {
let lead = (ctuple.addr >> usize::BITS) as *const c_void;
let trail = ctuple.addr as *const c_void;
(L::from(lead), R::from(trail))
}
}

macro_rules! impl_From {
($ty:ident, $Ty:ident<$($G:ident),+>) => {
impl<$($G),+> From<$ty> for $Ty<$($G),+> {
fn from(addr: $ty) -> Self {
Self {
addr,
lhs: PhantomData,
rhs: PhantomData,
}
}
}
}
}

#[cfg(target_pointer_width = "32")]
impl_From!(u64, CTuple<L, R>);

#[cfg(target_pointer_width = "16")]
impl_From!(u32, CTuple<L, R>);
}

0 comments on commit 4636bf0

Please sign in to comment.