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
-
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.
-
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.
-
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.
Tracking the work to run a hello-world on the CoreCLR wasm
corerunhost with anR2R-crossgen'd
System.Private.CoreLib.wasm. Assumes these in-flight PRs land:UnmanagedCallersOnly paths (@pavelsavara) -- open
loads) (@AndyAyersMS) -- open
fix-readytorun-log-empty-name, PR pending-- already merged
With those, R2R SPC loads and
coreclr_initializecompletes, but only after theworkarounds below; execution then fails in the static-init path.
Early-init workarounds
node version. The JIT emits
exnref(valtype0x69) for EH-bearing methods.The bundled emsdk node (18.17) predates
exnrefreusing0x69(the removed GCrttbyte) and rejects the module ("invalid value type 'rtt'"). Must run with amodern node (v26 works). The shipped emsdk node needs updating, or we need a
non-exnref EH lowering.
DOTNET_EnableDiagnostics=0.DiagnosticServerAdapter::Initializeabortsduring EEStartup on wasm (diagnostics IPC isn't supported). Should be a no-op /
disabled by default on wasm.
SystemJS_RandomBytes. It throws when called fromInitGSCookie(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
-1return.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, butthe import delay-load-resolves to the raw native
void InitClassHelper(MethodTable*)=
(i32) -> void. wasmcall_indirectstrict-type-checks, so 3 params vs 1 traps.src/coreclr/vm/wasm/helpers.cppconfirms the gap:PInvokeImportThunk,JIT_PInvokeBegin/End,JIT_InitPInvokeFrame,GenericPInvokeCalliHelper,VarargPInvokeStub*are allPORTABILITY_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 cctorvia
((delegate*<void*,void>)cctor)(instantiatingArg)"to match the ABI of anon-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_indirectrequires an exact signature, so this will trap the same way oncereached. Expect more cross-platform ABI assumptions like this to surface as execution
proceeds past the QCall blocker.