Skip to content

[Wasm Ryujit]: implement runtime-async codegen for R2R Wasm - #131167

Merged
AndyAyersMS merged 13 commits into
dotnet:mainfrom
AndyAyersMS:wasm-async
Jul 28, 2026
Merged

[Wasm Ryujit]: implement runtime-async codegen for R2R Wasm#131167
AndyAyersMS merged 13 commits into
dotnet:mainfrom
AndyAyersMS:wasm-async

Conversation

@AndyAyersMS

Copy link
Copy Markdown
Member

Adds Wasm R2R support for runtime-async methods.

  • The continuation is returned by a Wasm global. The continuation global is referenced through the recently introduced CORINFO_WASM_WELLKNOWN_GLOBALS.

  • Per-method async resume info is emitted into read-only data. The info Resume field points to code, so is emitted as a relocatable function-table index, requiring a new fixup type (function index in data).

  • AsyncResumptionStub target calls are routed through MethodEntrypoint on Wasm (no direct code-pointer call exists). We may need to revisit this some day to ensure the right IL version is being invoked.

  • The async continuation is encoded via a new 'a' signature marker. The interp<->R2R thunks and the delay-load import thunk explicitly forward the continuation as needed. There is perhaps too much ceremony in the code to get correct argument ordering.

  • The Wasm shadow stack pointer is a caller-established local, not continuation state, so it is excluded from async save/restore in JIT codegen.

Adding a new Wasm global requires creating a new JIT Guid.

Fixes #130952.

Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452

Adds Wasm R2R support for runtime-async methods.

Key design decisions:

* The continuation is returned by a Wasm global. The continuation global is
  referenced through the newly introduced CORINFO_WASM_WELLKNOWN_GLOBALS.

* Per-method async resume info is emitted into read-only data. The info
  `Resume` field points to code, so is emitted as a relocatable function-table
  index, requiring a new fixup type (function index in data).

* AsyncResumptionStub target calls are routed through MethodEntrypoint on Wasm
  (no direct code-pointer call exists). We may need to revisit this some day to
  ensure the right IL version is being invoked.

* The async continuation is encoded via a new 'a' signature marker. The
  interp<->R2R thunks and the delay-load import thunk explicitly forward the
  continuation as needed. There is perhaps too much ceremony in the code to get
  correct argument ordering.

* The Wasm shadow stack pointer is a caller-established local, not continuation
  state, so it is excluded from async save/restore in JIT codegen.

Adding a new Wasm global requires creating a new JIT Guid.

Fixes dotnet#130952.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
@AndyAyersMS
AndyAyersMS requested a review from maraf as a code owner July 21, 2026 20:28
Copilot AI review requested due to automatic review settings July 21, 2026 20:28
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 21, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 7 pipeline(s).
9 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@AndyAyersMS

Copy link
Copy Markdown
Member Author

@davidwrighton PTAL
fyi @dotnet/wasm-contrib @jakobbotsch

Simple R2R'd async test case works on top of interpreted SPC. Haven't tested much else yet.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the Wasm ReadyToRun (R2R) pipeline to support runtime-async codegen by introducing an asyncContinuation well-known Wasm global, updating signature encoding to include an async marker ('a'), and adding the necessary relocations and thunk plumbing across the JIT/EE/ILCompiler/tooling/host to carry async resume metadata and continuation values.

Changes:

  • Add an asyncContinuation entry to CORINFO_WASM_WELLKNOWN_GLOBALS and plumb it through the JIT/EE interface, SuperPMI replay, r2rdump, object writer, and the Wasm host (libCorerun.js).
  • Implement Wasm JIT codegen for async nodes (e.g., GT_ASYNC_CONTINUATION, GT_RETURN_SUSPEND, GT_ASYNC_RESUME_INFO, GT_RECORD_ASYNC_RESUME) and add a Wasm-specific reloc to store function-table indices in data.
  • Update signature encoding/decoding and R2R thunk generation to recognize/forward the async marker ('a') and adjust interpreter ↔ R2R call boundaries accordingly.

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs Updates signature-token parsing docs to include the new async marker token.
src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs Strips the 'a' marker when generating interp→native call helper signatures / mappings.
src/coreclr/vm/wasmasynccontinuation.h Adds Wasm-only C-callable accessors for reading/writing the shared async continuation global.
src/coreclr/vm/wasm/helpers.cpp Updates interpreter→native invocation to optionally pass/handle an async continuation out parameter.
src/coreclr/vm/prestub.cpp Writes/reads the async continuation via the shared global on Wasm at interpreter boundaries.
src/coreclr/vm/jitinterface.cpp Adjusts R2R dynamic info resumption stub entry point resolution for Wasm function-table indices.
src/coreclr/vm/interpexec.cpp Ensures calli paths only pass the continuation slot to async callees under portable entrypoints.
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp Records/replays the new asyncContinuation well-known global for SuperPMI Wasm contexts.
src/coreclr/tools/superpmi/superpmi-shared/agnostic.h Extends agnostic Wasm well-known globals struct with asyncContinuation.
src/coreclr/tools/r2rdump/WasmDisassembler.cs Annotates global.get/set disassembly with well-known global names including asyncContinuation.
src/coreclr/tools/r2rdump/R2RDump.csproj Links WasmGlobalImports.cs into r2rdump for consistent global index naming.
src/coreclr/tools/Common/JitInterface/WasmLowering.cs Adds 'a' signature marker emission/parsing for async calls and adjusts roundtrip validation.
src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs Adds WASM_TABLE_INDEX_I32 reloc and the asyncContinuation well-known global field.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Plumbs asyncContinuation global and maps the new reloc kind into object-writer relocation types.
src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs Improves relocation error messages and centralizes global indices via WasmGlobalImports.
src/coreclr/tools/Common/Compiler/ObjectWriter/WasmGlobalImports.cs New shared constants for imported Wasm global indices (including async continuation).
src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs Adds symbol name for __async_continuation.
src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs Adds node factory caching for the new well-known global symbol.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs Avoids direct-call optimization for async resumption stubs on Wasm (routes through indirection).
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj Links WasmGlobalImports.cs into ILCompiler.ReadyToRun.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmR2RToInterpreterThunkNode.cs Forwards async continuation across R2R→interpreter thunks (including generic-context ordering special-case).
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs Adds async-aware interp→R2R thunk signature and forwards the continuation via the shared global.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs Updates import thunk emission to store/forward the async continuation value as needed.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ResumptionStubEntryPointSignature.cs Emits Wasm-specific relocation for resumption stub entry points (table index in data).
src/coreclr/jit/lowerwasm.cpp Forces managed calls to be treated as indirect on Wasm (no “in-range” direct-call assumption).
src/coreclr/jit/instrswasm.h Adds i32.const_dataoffs pseudo-instruction for data-section offset constants.
src/coreclr/jit/emitwasm.h Declares emitDataOffsetConstant helper.
src/coreclr/jit/emitwasm.cpp Implements emitDataOffsetConstant and adds IF_DATAOFFS emission/output/disasm support.
src/coreclr/jit/emitfmtswasm.h Adds IF_DATAOFFS instruction format.
src/coreclr/jit/emit.cpp Stores async resume Resume as a Wasm table index in data and records WASM_TABLE_INDEX_I32 relocations.
src/coreclr/jit/codegenwasm.cpp Implements Wasm codegen for async continuation/global handling and async resume info addressing.
src/coreclr/jit/codegenlinear.cpp Adjusts Wasm #ifdef factoring so async resume recording helpers are available where needed.
src/coreclr/jit/codegencommon.cpp Clears async continuation (global on Wasm) on normal returns from async methods.
src/coreclr/jit/codegen.h Adds Wasm-only helpers to store/clear the async continuation global.
src/coreclr/jit/async.cpp Excludes the Wasm shadow stack pointer local from async save/restore sets.
src/coreclr/inc/jiteeversionguid.h Bumps the JIT/EE interface GUID due to interface shape changes.
src/coreclr/inc/corinfo.h Adds new reloc kind and extends CORINFO_WASM_WELLKNOWN_GLOBALS with asyncContinuation.
src/coreclr/hosts/corerun/wasm/libCorerun.js Creates/imports a shared WebAssembly.Global for async continuation and exposes C-callable accessors.
docs/design/coreclr/botr/readytorun-format.md Updates docs to reflect 'a' async continuation marker in Wasm signature strings.

Comment thread src/coreclr/vm/wasm/helpers.cpp Outdated
Comment thread src/coreclr/tools/Common/JitInterface/WasmLowering.cs Outdated
@AndyAyersMS

Copy link
Copy Markdown
Member Author

Going to revise things so the async return global is "owned" by the runtime so hosts don't need to define / export accessors.

Make the runtime-async continuation global owned and exported by the runtime
module (an inline-asm wasm global in helpers.cpp) and imported by every webcil,
instead of a host-JS-created global with JS accessors. This defines
RuntimeAsync_Load/StoreAsyncContinuation in the runtime itself, so browserhost's
dotnet.native.wasm links (it was failing with undefined symbols, breaking the
browser/wasi CoreCLR CI legs). corerun now wires webcils to
wasmExports.__async_continuation.

Also addresses PR review:
* InvokeCalliStub keeps the 3-arg interpreter cookie and reads the continuation
  from the shared global after the call, instead of casting to a 4-arg cookie
  the generated CallFunc helpers never provide.
* WasmLowering.RaiseSignature derives the generic-context hidden-pointer char
  from PointerSize (i32/i64) instead of hard-coding 'i'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Copilot AI review requested due to automatic review settings July 21, 2026 23:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated 6 comments.

Comment thread src/coreclr/vm/wasmasynccontinuation.h Outdated
Comment thread src/coreclr/vm/wasm/helpers.cpp Outdated
Comment thread src/coreclr/vm/wasm/helpers.cpp Outdated
Comment thread src/coreclr/vm/prestub.cpp Outdated
Comment thread src/coreclr/vm/prestub.cpp
Comment thread src/coreclr/vm/wasm/helpers.cpp
The async continuation accessors carry an Object* bit-pattern through an i32
wasm global. Use uint32_t instead of int32_t so the pointer->integer round-trip
stays bit-preserving for references with the high bit set (wasm32 memory can
exceed 2 GB), avoiding implementation-defined uintptr_t->int32_t conversions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Copilot AI review requested due to automatic review settings July 22, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.

lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…expand, drop Wasi optimistic-ISA disable

Rebased for the v2 cleanup. Keeps runtime-async=on (now unconditional in main + dotnet#131167 provides wasm
async R2R codegen), so no async-off gate. corerun.cpp R2R host does NOT define the RuntimeAsync
accessors (helpers.cpp from dotnet#131167 now owns __async_continuation + the accessors).
lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…lobal wiring

dotnet#131167 makes the runtime own + export __async_continuation (browser wires it via libCorerun.js). The
standalone wasi corerun needs an explicit --export so --gc-sections keeps it and the offline merge can
wire each composite's webcil.asyncContinuation import to this same runtime global.
lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…expand, drop Wasi optimistic-ISA disable

Rebased for the v2 cleanup. Keeps runtime-async=on (now unconditional in main + dotnet#131167 provides wasm
async R2R codegen), so no async-off gate. corerun.cpp R2R host does NOT define the RuntimeAsync
accessors (helpers.cpp from dotnet#131167 now owns __async_continuation + the accessors).
lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…lobal wiring

dotnet#131167 makes the runtime own + export __async_continuation (browser wires it via libCorerun.js). The
standalone wasi corerun needs an explicit --export so --gc-sections keeps it and the offline merge can
wire each composite's webcil.asyncContinuation import to this same runtime global.
The R2R-to-interpreter thunk re-published the async continuation to the shared
global from the transition-block arg slot after the call. On wasm that slot is
never updated with the callee's result (the register area is unused; the runtime
writes the continuation straight to the global in ExecuteInterpretedMethod), so
the thunk overwrote the correct value with the stale inbound arg - breaking
suspensions that cross an R2R-to-interpreter async call. Drop the post-call
store; the runtime is the authoritative publisher.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Copilot AI review requested due to automatic review settings July 22, 2026 01:50
davidwrighton and others added 2 commits July 24, 2026 15:37
- Instead of embedding a table index in the resume info, use a new R2R fixup to place the right PortableEntryPoint into the location
- Tweak InterpreterToR2RThunk generation
  - Always use the standard MehtodSignature for InterpreterToR2RThunks
  - Build the ArgIterator based on having an async continuation
  - Always pass 0 as the async continuation parameter. The only case where a non-zero value is passed is on resumptions, and those always pass through the R2R'd resumption stub, not the interpter to R2R thunk
Copilot AI review requested due to automatic review settings July 24, 2026 22:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/lowerwasm.cpp
Comment thread src/coreclr/tools/Common/JitInterface/WasmLowering.cs Outdated
AndyAyersMS and others added 2 commits July 25, 2026 09:00
# Conflicts:
#	src/coreclr/inc/jiteeversionguid.h
#	src/coreclr/inc/readytorun.h
#	src/coreclr/jit/emitwasm.h
#	src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
#	src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Copilot AI review requested due to automatic review settings July 25, 2026 19:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/tools/Common/Compiler/ObjectWriter/WasmGlobalImports.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Copilot AI review requested due to automatic review settings July 25, 2026 20:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 3 comments.

Comment thread docs/design/coreclr/botr/clr-abi.md Outdated

@davidwrighton davidwrighton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concerns have been addressed, although I think that @jakobbotsch's comments about making the clr-abi change simpler have merit.

AndyAyersMS and others added 2 commits July 27, 2026 13:13
- clr-abi.md: compact wasm continuation-return table row (jakobbotsch)
- derive generic-context char from PointerSize for wasm64 (3 thunk nodes)
- fix misleading roundtrip assert message and WasmGlobalImports doc reference

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 760f2e57-f8d3-478b-a9cb-de67d71c9452
@AndyAyersMS

Copy link
Copy Markdown
Member Author

I have to respin CI for a new Guid, so will fix the doc/comment/diagnostic issues above too.

Copilot AI review requested due to automatic review settings July 27, 2026 23:13
@AndyAyersMS

Copy link
Copy Markdown
Member Author

@davidwrighton need a re-approval

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/vm/readytoruninfo.cpp
Comment thread docs/design/coreclr/botr/clr-abi.md
@AndyAyersMS
AndyAyersMS merged commit 35ccab4 into dotnet:main Jul 28, 2026
187 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-rc1 milestone Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WASM JIT: GT_ASYNC_CONTINUATION has no codegen (REG_ASYNC_CONTINUATION_RET is REG_NA on wasm)

4 participants