Skip to content

How viable is it to use repr(C) structs as WASM primitives? #4232

Open
@RunDevelopment

Description

@RunDevelopment

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions