From b2e9a063deb76fff43a44fc64f4ce9f773199fdd Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 20 Feb 2026 13:31:37 -0800 Subject: [PATCH 1/3] [Wasm RyuJit] fix some issues with calls For calls with managed conventions, pass a faked-up PE pointer as the last argument to match the expected signature. If the last instruction in the main method or funclet is a call that does not return, make sure to emit an `end` after any `unreachable`. Pass the right number --- src/coreclr/jit/codegenlinear.cpp | 14 ++++++++++++++ src/coreclr/jit/gentree.cpp | 5 ++--- src/coreclr/jit/morph.cpp | 6 +++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index e5304cef1cb6d3..ce705fd032de28 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -790,6 +790,7 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) break; case BBJ_THROW: + { // If we have a throw at the end of a function or funclet, we need to emit another instruction // afterwards to help the OS unwinder determine the correct context during unwind. // We insert an unexecuted breakpoint instruction in several situations @@ -800,11 +801,13 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) // 2. If this is this is the last block of the hot section. // 3. If the subsequent block is a special throw block. // 4. On AMD64, if the next block is in a different EH region. + bool addedBreakpoint = false; if (block->IsLast() || !BasicBlock::sameEHRegion(block, block->Next()) || (!isFramePointerUsed() && m_compiler->fgIsThrowHlpBlk(block->Next())) || m_compiler->bbIsFuncletBeg(block->Next()) || block->IsLastHotBlock(m_compiler)) { instGen(INS_BREAKPOINT); // This should never get executed + addedBreakpoint = true; } // Do likewise for blocks that end in DOES_NOT_RETURN calls // that were not caught by the above rules. This ensures that @@ -818,11 +821,22 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) if (call->AsCall()->IsNoReturn()) { instGen(INS_BREAKPOINT); // This should never get executed + addedBreakpoint = true; } } } +#if defined(TARGET_WASM) + // For wasm the last instruction in a function or funclet must be end. + // + if (addedBreakpoint && (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))) + { + GetEmitter()->emitIns(INS_end); + } +#endif // defined(TARGET_WASM) + break; + } case BBJ_CALLFINALLY: result = genCallFinally(block); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index cf4f69d8ca2376..5634d4ac81dc68 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2346,9 +2346,8 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const { #if defined(TARGET_WASM) // TODO-WASM: may need adjustments for other hidden args - // For now: managed calls get extra SP + PortableEntryPoint args, but - // we're not adding the PE arg yet. So just note one extra arg. - return IsUnmanaged() ? 0 : 1; + // For now: managed calls get extra SP + PortableEntryPoint args. + return IsUnmanaged() ? 0 : 2; #endif // defined(TARGET_WASM) if (IsUnmanaged() && !compiler->opts.ShouldUsePInvokeHelpers()) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 5d8075ec7236d3..560c49102dfcda 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1836,8 +1836,12 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call { GenTree* const stackPointer = comp->gtNewLclVarNode(comp->lvaWasmSpArg, TYP_I_IMPL); PushFront(comp, NewCallArg::Primitive(stackPointer).WellKnown(WellKnownArg::WasmShadowStackPointer)); + + // TODO-WASM: pass proper portable entry point as the last argument for managed calls + GenTree* const pePointer = comp->gtNewZeroConNode(TYP_I_IMPL); + PushFront(comp, NewCallArg::Primitive(pePointer).WellKnown(WellKnownArg::WasmPortableEntryPoint)); } - // TODO-WASM: pass the portable entry point as the last argument for managed calls + #endif // defined(TARGET_WASM) ClassifierInfo info; From 43853bcdca3fbb6a4a6d6770b63e69b6ad39a569 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 20 Feb 2026 14:58:41 -0800 Subject: [PATCH 2/3] Update src/coreclr/jit/morph.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/morph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 560c49102dfcda..6b2cb9611a2cc0 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1839,7 +1839,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // TODO-WASM: pass proper portable entry point as the last argument for managed calls GenTree* const pePointer = comp->gtNewZeroConNode(TYP_I_IMPL); - PushFront(comp, NewCallArg::Primitive(pePointer).WellKnown(WellKnownArg::WasmPortableEntryPoint)); + PushBack(comp, NewCallArg::Primitive(pePointer).WellKnown(WellKnownArg::WasmPortableEntryPoint)); } #endif // defined(TARGET_WASM) From eead97f3a68f55bc25903ab4ac2f09afbb8ad937 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sat, 21 Feb 2026 07:40:48 -0800 Subject: [PATCH 3/3] unconditionally add end when the last block is BBJ_THROW --- src/coreclr/jit/codegenlinear.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index ce705fd032de28..dcaa66af607be4 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -801,13 +801,11 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) // 2. If this is this is the last block of the hot section. // 3. If the subsequent block is a special throw block. // 4. On AMD64, if the next block is in a different EH region. - bool addedBreakpoint = false; if (block->IsLast() || !BasicBlock::sameEHRegion(block, block->Next()) || (!isFramePointerUsed() && m_compiler->fgIsThrowHlpBlk(block->Next())) || m_compiler->bbIsFuncletBeg(block->Next()) || block->IsLastHotBlock(m_compiler)) { instGen(INS_BREAKPOINT); // This should never get executed - addedBreakpoint = true; } // Do likewise for blocks that end in DOES_NOT_RETURN calls // that were not caught by the above rules. This ensures that @@ -821,7 +819,6 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) if (call->AsCall()->IsNoReturn()) { instGen(INS_BREAKPOINT); // This should never get executed - addedBreakpoint = true; } } } @@ -829,7 +826,7 @@ BasicBlock* CodeGen::genEmitEndBlock(BasicBlock* block) #if defined(TARGET_WASM) // For wasm the last instruction in a function or funclet must be end. // - if (addedBreakpoint && (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))) + if (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next())) { GetEmitter()->emitIns(INS_end); }