Description
Background
Right now, the WasmAbi
trait works using WASM primitives. Primitives are Rust types that map directly to WASM ABI types, namely i32
, u32
, i64
, u64
, f32
, f64
, and ()
. ()
is interesting here, because its ABI is nothing, as in: it doesn't appear. The WasmAbi
trait uses the quirk to allow for a variable number of primitives (up to 4) by filling the rest with ()
.
This design has the major limitation that ALL Rust types must be representable with at most 4 WASM primitives for them to be WASM compatible. This makes it very difficult to support tuples as value types, since they can easily go past the 4 primitives limit. Raising the limit to some number N also isn't a full solution, since we can always create nested tuples that require more primitives.
Question
My question is whether we can use repr(C)
structs as primitives in the WASM ABI? Or to be more precise: is it safe to implement WasmPrimitive
for a repr(C)
structs where all fields also implement WasmPrimitive
?
Right now, compiling the following the Rust code:
#[repr(C)]
pub struct Bar {
pub a: i32,
pub b: i64,
}
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), no_mangle)]
pub unsafe extern "C" fn my_test_fn(a: Bar, b: Bar) {}
yields with WASM ABI:
(module $reference_test.wasm
(type (;0;) (func (param i32 i64 i32 i64)))
(func $my_test_fn (;0;) (type 5) (param i32 i64 i32 i64))
(memory (;0;) 17)
(export "memory" (memory 0))
(export "my_test_fn" (func $my_test_fn))
(@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext")
)
Note that I specifically chose i32
and i64
fields to see how the ABI handles padding. It seems to handle it well, but I don't know whether this is guaranteed.
Motivation
If this was possible, then it would be trivial to support tuples as value types in the ABI with helper types like this:
/// A "tuple" with a stable ABI.
#[repr(C)]
struct WasmTuple2<T1, T2>{
item1: T1,
item2: T2,
}
unsafe impl<T1: WasmPrimitive, T2: WasmPrimitive> WasmPrimitive for WasmTuple2<T1, T2> {}
This would also allow us to simplify the WasmAbi
trait, since a single primitive would be enough while allowing for arbitrarily complex ABIs. E.g. Option<T>
could define its own helper type or reuse WasmTuple2<u32, T>
as its ABI. Similar for Result<T, E>
and WasmTuple3<u32, T, E>
.