Add experimental wasm externref lang type - #159749
Conversation
Adds core::arch::wasm32::externref behind feature(wasm_externref): an opaque host reference lowering to LLVM ptr addrspace(10), giving real wasm reference types in extern "C" signatures. externref is a bare-position-only type, following clang's __externref_t semantics: legal only as the top-level type of function parameters, return values and locals (function pointer signature slots included), enforced at type-check time via wf checks and typeck writeback. A monomorphization-time check backstops the remaining codegen-only channels (e.g. coroutine captures). Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
|
cc @Amanieu, @folkertdev, @sayantn |
|
r? @folkertdev rustbot has assigned @folkertdev. Use Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
Define the type in core's own arch module (shadowing the core_arch glob re-export) since the lang item is tightly coupled to compiler support and stdarch syncs to a separate repository. Also adds a Debug impl, required by core lints. Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
93e5ec0 to
e6299fb
Compare
At opt-level 0 every local gets an alloca, which the wasm backend must promote to wasm locals since reference types cannot enter linear memory. Verifies eight simultaneously-live externrefs lower to .local externref slots with no memory traffic. Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
With -Cdebuginfo=2, user-visible SSA locals are spilled to allocas so dbg.declare can reference them. wasm reference types cannot be stored to linear memory, so the debug spill of an externref local hit a fatal 'Cannot select' in the wasm backend (any cargo dev-profile build using externref locals). Skip the debuginfo spill for externref operands, alongside the existing SVE predicate skip; like clang's __externref_t, such variables get no memory-based debug location. Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
This comment has been minimized.
This comment has been minimized.
The gcc codegen backend cannot target wasm32, so even the minicore auxiliary build panics at backend init in the gcc CI job. Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Not anymore: |
|
@hoodmane I think that layers with this work well - it can loosen the compile-time checks as mentioned in the description, while still enabling the primary use case now. |
It can? It seems orthogonal to that to me. The representation of an externref changed from a pointer to a funny address space to a target ext type. Ideally we wait until that change makes it to rust/llvm to avoid churn I think. But maybe there's something I'm misunderstanding. |
|
@hoodmane the representation affects the compiled output, not the Rust code usage, and it is under an experimental feature. Not sure what the concern is? |
|
Well sure maybe it's fine. If we're lucky the people who roll llvm might fix it for us. |
|
Yeah appreciated for the heads up on that, I'd be happy to help rebase to LLVM 23 when the time comes. It should be a fairly simple codegen diff, not a fundamental design change. |
|
The upgrade to LLVM 23 is already in the queue (i.e. will merge in the coming days), so that should be the foundation for any new features. When you can no longer just use on the core pointer type, but need something custom to get your type into the backend, I suspect the implementation gets more complicated. Beyond that: extensions like this need at least good vibes from target maintainers. Was this change discussed with anyone in the rust project at all? At first glance this looks like an RFC (or similar) might be appropriate. |
|
This is a fundamentally new kind of type - this seems to need more than just a PR, maybe a lang experiment or even a RFC. |
|
Yeah, let's discuss this in #t-lang > wasm `externref`. |
Oh wow, that is some great timing. I will rework the PR to that then, per the discussion. Per discussion, let me close this for now, to return to it with process ducks lined up rather. |
This adds an experimental
core::arch::wasm32::externreftype behindfeature(wasm_externref)to support the sameexternref_ttype that is possible for Clang on Wasm targets.The use case is being able to receive and pass
externreftypes between foreign functions, marshalling them between calls as opposed to storing them. This is currently a limitation for any Wasm projects which need to bind to foreign functions that marshall externrefs, requiring a lot of internal custom boilerplate wrapping to handle a type that is really a core part of the JS FFI boundary.The approach taken is to implement a new
ExternReflang item as a scalar pointer inAddressSpace(10), LLVM's Wasm externref address space, with no further codegen changes necessary.externrefisCopy(duplicating a reference islocal.get),!Send/!Sync, andimproper_ctypestreats it as FFI-safe.externrefusage is heavily restricted here to being treated as only a bare-position-only type. wf-check enforcement is added so that it is legal only as the top-level type of function parameters, return values and locals (function pointer signature slots included), and nowhere else - no references, aggregates, statics or generic arguments. All enforcement is at type-check time, and could be relaxed to become more flexible in future. For example, to support addressibility via llvm/llvm-project#201466 if that were to land.Example usage:
compiles to:
Test coverage includes a codegen test for
addrspace(10)signatures, assembly test for.functype describe (externref) -> (externref), and a ui test for the position rules (12 error cases plus the allowed forms). Verified end-to-end in a JS host with identity preservation in both directions at-Copt-level=0and2.Credit: Since the initial prototype of this work was done by @hoodmane I've added him as a co-author.
Made with AI assistance under my review.