Skip to content

Running wasm with R2R'd System.Private.CoreLib #129850

Description

@AndyAyersMS

Tracking the work to run a hello-world on the CoreCLR wasm corerun host with an
R2R-crossgen'd System.Private.CoreLib.wasm. Assumes these in-flight PRs land:

With those, R2R SPC loads and coreclr_initialize completes, but only after the
workarounds below; execution then fails in the static-init path.

Early-init workarounds

  1. node version. The JIT emits exnref (valtype 0x69) for EH-bearing methods.
    The bundled emsdk node (18.17) predates exnref reusing 0x69 (the removed GC
    rtt byte) and rejects the module ("invalid value type 'rtt'"). Must run with a
    modern node (v26 works). The shipped emsdk node needs updating, or we need a
    non-exnref EH lowering.

  2. DOTNET_EnableDiagnostics=0. DiagnosticServerAdapter::Initialize aborts
    during EEStartup on wasm (diagnostics IPC isn't supported). Should be a no-op /
    disabled by default on wasm.

  3. SystemJS_RandomBytes. It throws when called from InitGSCookie (very early,
    before the dotnet JS API is ready). Needs to be callable that early, or the GS
    cookie path needs a fallback that tolerates a -1 return.

Current blocking problem

QCall/P-Invoke from R2R-compiled wasm code is unimplemented, so the first such call
traps with RuntimeError: function signature mismatch.

Path: AppContext.Setup -> StaticsHelpers.GetNonGCStaticBase -> ...Slow -> InitHelpers.InitClassSlow -> InitClassHelper (a [LibraryImport(QCall)]).

The R2R call site uses the managed PE convention vip = (SP, mt, PEP) -> void, but
the import delay-load-resolves to the raw native void InitClassHelper(MethodTable*)
= (i32) -> void. wasm call_indirect strict-type-checks, so 3 params vs 1 traps.

src/coreclr/vm/wasm/helpers.cpp confirms the gap: PInvokeImportThunk,
JIT_PInvokeBegin/End, JIT_InitPInvokeFrame, GenericPInvokeCalliHelper,
VarargPInvokeStub* are all PORTABILITY_ASSERT("...not implemented on wasm").
There is no managed<->unmanaged bridge thunk for QCall/P-Invoke targets.

Problem foreseen

InitHelpers.CallClassConstructor (InitHelpers.cs:67) calls a non-instantiating cctor
via ((delegate*<void*,void>)cctor)(instantiatingArg) "to match the ABI of a
non-instantiating stub" -- i.e. it deliberately passes an extra arg expecting the
callee to ignore it. That register/stack-ABI tolerance does not hold on wasm, where
call_indirect requires an exact signature, so this will trap the same way once
reached. Expect more cross-platform ABI assumptions like this to surface as execution
proceeds past the QCall blocker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    arch-wasmWebAssembly architecturearea-Interop-monoos-browserBrowser variant of arch-wasmuntriagedNew issue has not been triaged by the area owner

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions