diff --git a/src/coreclr/jit/fgwasm.cpp b/src/coreclr/jit/fgwasm.cpp index f2a029f2816f1b..39ebe5aa9cfd64 100644 --- a/src/coreclr/jit/fgwasm.cpp +++ b/src/coreclr/jit/fgwasm.cpp @@ -1458,11 +1458,36 @@ PhaseStatus Compiler::fgWasmControlFlow() continue; } - // Non-contiguous, non-subsumed forward branch. Start the Block at the try - // header when crossing a try-catch exit so it encloses the wrapper. + // Non-contiguous, non-subsumed forward branch. If it exits enclosing + // try/catch regions, start the Block at the outermost escaped catch-try + // so the Block encloses those trys' ends. Each Try emits a trailing + // validation `unreachable` after its `end`; starting only at the innermost + // try would nest the Block inside an outer try sharing this target as its + // end cursor, so the branch would land on that `unreachable` instead of the + // continuation. Single-level try/catch is unchanged (outermost == innermost). // - BasicBlock* const blockStart = isCrossingTryCatchExit ? blockTryDsc->ebdTryBeg : block; - WasmInterval* const branch = WasmInterval::NewBlock(this, blockStart, initialLayout[succNum]); + BasicBlock* blockStart = block; + for (EHblkDsc* tryDsc = ehGetBlockTryDsc(block); tryDsc != nullptr;) + { + // Once succ is contained in an enclosing try, all outer trys contain it + // too, so the branch does not exit them. + // + if (bbInTryRegions(ehGetIndex(tryDsc), succ)) + { + break; + } + + if (tryDsc->HasCatchHandler()) + { + blockStart = tryDsc->ebdTryBeg; + } + + tryDsc = (tryDsc->ebdEnclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) + ? nullptr + : ehGetDsc(tryDsc->ebdEnclosingTryIndex); + } + + WasmInterval* const branch = WasmInterval::NewBlock(this, blockStart, initialLayout[succNum]); fgWasmIntervals->push_back(branch); // Remember an interval end here diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.cs b/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.cs new file mode 100644 index 00000000000000..d88b3a329814af --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Regression test for a WebAssembly R2R (crossgen) codegen bug in async +// exception-handling lowering. When an async method's try block completes +// normally by branching out early (a `return` after an `await`), the +// normal-completion branch was bound to a Block that ended at the try's own +// end cursor -- nested inside the exception-ref wrapper funclet -- so the +// branch landed on the wrapper's trailing validation `unreachable` and +// trapped (RuntimeError: unreachable) instead of resuming. +// +// The defect is in codegen, independent of whether the await actually +// suspends: `await Task.CompletedTask` still generates the state-machine +// normal-completion branch that traps. A completed await is used deliberately +// so the synchronous .GetAwaiter().GetResult() does not block the single wasm +// thread (blocking on incomplete work traps elsewhere in corelib on wasm). +// +// Reproduces only under crossgen wasm R2R (TargetOS=browser), where the +// unfixed JIT aborts the module (exit != 100 => test fails). Passes trivially +// on all other targets. + +using System; +using System.Threading.Tasks; +using Xunit; + +public class Runtime_131285 +{ + [Fact] + public static void TestEntryPoint() + { + // Sync [Fact]: the merged runner invokes this via a bare call, so drive + // the async method to completion synchronously here. RunAsync's MoveNext + // takes the normal-completion `return` path -- the edge that trapped + // under the unfixed wasm R2R JIT. + RunAsync().GetAwaiter().GetResult(); + } + + private static async Task RunAsync() + { + try + { + await Task.CompletedTask; + return; + } + catch (Exception ex) + { + GC.KeepAlive(ex); + } + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.csproj new file mode 100644 index 00000000000000..971c955364bc7e --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_131285/Runtime_131285.csproj @@ -0,0 +1,16 @@ + + + True + + true + + + + +