From 6899a2e3f936e8c1fc3d6909541e5be4cba0c252 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 12 Jun 2026 15:06:30 -0700 Subject: [PATCH] Wasm R2R: push this before retbuf in InterpToR2R thunk args The thunk pushed retbuf before this, but the JIT (lvaInitArgs) and WasmR2RToInterpreterThunk both expect (, [this], [retbuf], ...). Swap the two if-blocks so interp -> R2R calls match. Fixes #129347 (NRE / pMT->Validate assert for Synchronized + struct return). --- .../ReadyToRun/WasmInterpreterToR2RThunkNode.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs index c391b4aec8b7c2..d3d6468a7ea213 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs @@ -139,7 +139,8 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr expressions.Add(I32.Store(0)); // Build the arguments for the R2R call_indirect. - // Target R2R wasm params: ($sp, [retbuf], [this], explicit_params..., portableEntrypoint) + // Target R2R wasm params: ($sp, [this], [retbuf], explicit_params..., portableEntrypoint) + // (matches Compiler::lvaInitArgs / WasmR2RToInterpreterThunkNode local order.) // We track targetParamIndex to look up the correct wasm type for each arg. int targetParamIndex = 0; @@ -154,13 +155,6 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr expressions.Add(Global.Get(WasmObjectWriter.StackPointerGlobalIndex)); targetParamIndex++; - // If the R2R function takes a return buffer, pass pRet directly as the retbuf arg - if (hasRetBuffArg) - { - expressions.Add(Local.Get(LocalPRet)); - targetParamIndex++; - } - // If the method has a 'this' pointer, load it from pArgs at offset 0 // (ArgIterator offset for this = OffsetOfArgumentRegisters = SizeOfTransitionBlock) if (hasThis) @@ -171,6 +165,13 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr targetParamIndex++; } + // If the R2R function takes a return buffer, pass pRet directly as the retbuf arg + if (hasRetBuffArg) + { + expressions.Add(Local.Get(LocalPRet)); + targetParamIndex++; + } + // Explicit parameters — load each from pArgs at the ArgIterator-derived offset for (int i = 0; i < methodSignature.Length; i++) {