Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Wasm RyuJit] fix throw helper layout for side try entries in enclosed regions - #130945

#130945
Merged
AndyAyersMS merged 1 commit into
dotnet:maindotnet/runtime:mainfrom
AndyAyersMS:wasm-throwhelper-enclosing-tryAndyAyersMS/runtime:wasm-throwhelper-enclosing-tryCopy head branch name to clipboard
Jul 17, 2026
Merged

[Wasm RyuJit] fix throw helper layout for side try entries in enclosed regions#130945
AndyAyersMS merged 1 commit into
dotnet:maindotnet/runtime:mainfrom
AndyAyersMS:wasm-throwhelper-enclosing-tryAndyAyersMS/runtime:wasm-throwhelper-enclosing-tryCopy head branch name to clipboard

Conversation

@AndyAyersMS

Copy link
Copy Markdown
Member

If an enclosed try region has a side entry (say from an async resume), add edges to (eagerly visit) the helper blocks in this region and in all enclosing try regions, so that the throw helpers end up being placed after any blocks in their respective regions.

This generalizes the fix from #130153.

…d regions

If an enclosed try region has a side entry (say from an async resume), add edges to
(eagerly visit) the helper blocks in this region and in all enclosing try regions,
so that the throw helpers end up being placed after any blocks in the respective
regions.

This generalizes the fix from dotnet#130153.
Copilot AI review requested due to automatic review settings July 17, 2026 02:13
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 17, 2026
@AndyAyersMS

AndyAyersMS commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

@adamperlin PTAL
fyi @dotnet/wasm-contrib

Fixes Wasm control flow generation for ~10 async methods in SPC

(not reachable yet without other changes I'm still working on)

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Wasm-specific flow-graph successor enumeration to ensure throw helper (“ACD”) blocks are visited in a way that preserves correct layout ordering for EH regions when try regions have side entries (e.g., async/catch resumption). It also replaces hard-coded ACD region-kind bit flags with named constants and adds helpers to decode ACD key data.

Changes:

  • Replace 0x40000000 / 0x80000000 ACD region-kind bits with named flags in AddCodeDscKey and use them in bbThrowIndex / AddCodeDscKey(AddCodeDsc*).
  • Add AddCodeDscKey::Designator() and AddCodeDscKey::RegionIndex() helpers to interpret packed ACD key data.
  • Extend Wasm successor visiting to optionally treat throw helpers from enclosing try regions as successors for generalized try-entry blocks.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/coreclr/jit/flowgraph.cpp Uses named ACD flag constants instead of magic bit values when packing region kind into ACD keys.
src/coreclr/jit/fgwasm.h Updates Wasm successor enumeration to add throw-helper successor edges based on enclosing try regions for generalized try entries / side entries.
src/coreclr/jit/compiler.h Introduces named ACD flag constants and helper accessors to decode designator and EH region index from packed ACD key data.

Comment thread src/coreclr/jit/fgwasm.h
Comment thread src/coreclr/jit/fgwasm.h
Comment thread src/coreclr/jit/fgwasm.h

@adamperlin adamperlin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the copilot comments about wording are worth addressing, but otherwise this looks good to me.

@AndyAyersMS

Copy link
Copy Markdown
Member Author

I think the copilot comments about wording are worth addressing, but otherwise this looks good to me.

I'll fix these in a follow-on change.

@AndyAyersMS
AndyAyersMS merged commit 05311dc into dotnet:main Jul 17, 2026
140 of 142 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jul 18, 2026
lewing added a commit that referenced this pull request Jul 23, 2026
…#131279)

[wasm] Restrict enclosing-try throw-helper attach to the same funclet

## Problem

crossgen2 emits an invalid WASM (wasm32) R2R module for methods with
certain try/catch shapes: the terminal `end` opcode (0x0b) is dropped
for an EH funclet, so `wasm-tools validate` (and V8) reject the module
with *"function body must end with end opcode"*. A related symptom is a
miscomputed branch target depth in the same funclet (tracked as
#131252).

## Root cause

The defect is in wasm block layout, in `FgWasm::VisitWasmSuccs`
(`src/coreclr/jit/fgwasm.h`).

#130945 added an "enclosing try" relaxation: when a block is a try
side-entry and the throw-helper ACD key is `KD_TRY`, the helper is also
attached as a successor if

```cpp
comp->bbInTryRegions(key.RegionIndex(), block)
```

is true. `bbInTryRegions` is a purely **lexical** try-nesting test — it
does not check that the throw helper and the side-entry live in the same
**function region (funclet)**.

So a throw helper for an outer try region that belongs to the **main
method** can be attached to a catch-resumption side-entry
(`BBF_CATCH_RESUMPTION`) that merely nests inside that try but
physically lives in a **handler funclet**. RPO layout then lays the
main-method helper *inside* the funclet, interleaving it with funclet
blocks. Because a funclet is a distinct wasm function body, that
interleaving drops the funclet's terminal `end` and corrupts its branch
target depths.

Concrete trace from `System.Data.DataColumn:set_Expression`
(instrumented `VisitWasmSuccs`, deduped):

```
sideEntry BB50 (tryIdx=4 hndIdx=3) preds[async=0 catch=1 other=2] pulls dst BB76 (hndIdx=0) crossFunclet=1
sideEntry BB73 (tryIdx=4 hndIdx=3) preds[async=0 catch=1 other=0] pulls dst BB76 (hndIdx=0) crossFunclet=1
```

BB76 is the throw helper for root try region #4 (`KD_TRY`, no handler
index → main method); BB50/BB73 are catch-resumption side-entries in
handler funclet #3 (which lexically nests in try #4), so the
enclosing-try rule pulls the main-method helper into funclet #3.

This is an ordinary catch-resumption EH shape (`async=0`); it is
independent of runtime-async, and `fgwasm.h` is byte-identical to
`main`. It is latent on `main` only because R2R-wasm codegen isn't
enabled there yet.

## Fix

Gate the enclosing-try relaxation on same-function-region ownership. A
new `funcRegionOf` lambda returns the funclet index that physically
contains an arbitrary block (0 == main method); it mirrors
`funGetFuncIdx` but works for non-entry blocks and distinguishes a
filter funclet from its filter-handler. The helper is attached only when
it shares the side-entry's function region:

```cpp
if (!viaEnclosingTry || (funcRegionOf(block) == funcRegionOf(acd->acdDstBlk)))
{
    RETURN_ON_ABORT(func(acd->acdDstBlk));
}
```

The exact-match path (a helper keyed to the block's own region) is
unchanged, and #130945's intended case (an inner-try side-entry pulling
its enclosing try's helper *within the same funclet*) still matches — so
this only removes the cross-funclet edge.

## Validation

Standalone `System.Data.Common` R2R wasm crossgen, release JIT,
`wasm-tools validate` (only `fgwasm.h` differs between runs):

| | `wasm-tools validate` |
|---|---|
| baseline (`origin/main` layout) | `func 597 failed to validate` ❌ |
| with this fix | passes ✅ |

## Notes

- Supersedes #131251, which hardened the terminal-`end` emission (the
*effect*); this fixes the *cause* in layout. Closing #131251 in favor of
this.
- Related: #129335 (incomplete predecessor for this defect class, do not
reopen); #130945 (introduced the lexical enclosing-try match); #131252
(miscomputed branch target depth — same corrupted layout, very likely
subsumed by this fix).
- Follow-up idea (not in this PR): a JIT-time assert that a funclet's
blocks form a contiguous RPO range, so any future mis-layout traps at
compile time instead of surfacing as an invalid module.

> [!NOTE]
> This change was authored with the assistance of GitHub Copilot.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2e627b94-2658-41ce-a880-d9c27b7febd9
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…dotnet#131279)

[wasm] Restrict enclosing-try throw-helper attach to the same funclet

## Problem

crossgen2 emits an invalid WASM (wasm32) R2R module for methods with
certain try/catch shapes: the terminal `end` opcode (0x0b) is dropped
for an EH funclet, so `wasm-tools validate` (and V8) reject the module
with *"function body must end with end opcode"*. A related symptom is a
miscomputed branch target depth in the same funclet (tracked as
dotnet#131252).

## Root cause

The defect is in wasm block layout, in `FgWasm::VisitWasmSuccs`
(`src/coreclr/jit/fgwasm.h`).

dotnet#130945 added an "enclosing try" relaxation: when a block is a try
side-entry and the throw-helper ACD key is `KD_TRY`, the helper is also
attached as a successor if

```cpp
comp->bbInTryRegions(key.RegionIndex(), block)
```

is true. `bbInTryRegions` is a purely **lexical** try-nesting test — it
does not check that the throw helper and the side-entry live in the same
**function region (funclet)**.

So a throw helper for an outer try region that belongs to the **main
method** can be attached to a catch-resumption side-entry
(`BBF_CATCH_RESUMPTION`) that merely nests inside that try but
physically lives in a **handler funclet**. RPO layout then lays the
main-method helper *inside* the funclet, interleaving it with funclet
blocks. Because a funclet is a distinct wasm function body, that
interleaving drops the funclet's terminal `end` and corrupts its branch
target depths.

Concrete trace from `System.Data.DataColumn:set_Expression`
(instrumented `VisitWasmSuccs`, deduped):

```
sideEntry BB50 (tryIdx=4 hndIdx=3) preds[async=0 catch=1 other=2] pulls dst BB76 (hndIdx=0) crossFunclet=1
sideEntry BB73 (tryIdx=4 hndIdx=3) preds[async=0 catch=1 other=0] pulls dst BB76 (hndIdx=0) crossFunclet=1
```

BB76 is the throw helper for root try region #4 (`KD_TRY`, no handler
index → main method); BB50/BB73 are catch-resumption side-entries in
handler funclet #3 (which lexically nests in try #4), so the
enclosing-try rule pulls the main-method helper into funclet #3.

This is an ordinary catch-resumption EH shape (`async=0`); it is
independent of runtime-async, and `fgwasm.h` is byte-identical to
`main`. It is latent on `main` only because R2R-wasm codegen isn't
enabled there yet.

## Fix

Gate the enclosing-try relaxation on same-function-region ownership. A
new `funcRegionOf` lambda returns the funclet index that physically
contains an arbitrary block (0 == main method); it mirrors
`funGetFuncIdx` but works for non-entry blocks and distinguishes a
filter funclet from its filter-handler. The helper is attached only when
it shares the side-entry's function region:

```cpp
if (!viaEnclosingTry || (funcRegionOf(block) == funcRegionOf(acd->acdDstBlk)))
{
    RETURN_ON_ABORT(func(acd->acdDstBlk));
}
```

The exact-match path (a helper keyed to the block's own region) is
unchanged, and dotnet#130945's intended case (an inner-try side-entry pulling
its enclosing try's helper *within the same funclet*) still matches — so
this only removes the cross-funclet edge.

## Validation

Standalone `System.Data.Common` R2R wasm crossgen, release JIT,
`wasm-tools validate` (only `fgwasm.h` differs between runs):

| | `wasm-tools validate` |
|---|---|
| baseline (`origin/main` layout) | `func 597 failed to validate` ❌ |
| with this fix | passes ✅ |

## Notes

- Supersedes dotnet#131251, which hardened the terminal-`end` emission (the
*effect*); this fixes the *cause* in layout. Closing dotnet#131251 in favor of
this.
- Related: dotnet#129335 (incomplete predecessor for this defect class, do not
reopen); dotnet#130945 (introduced the lexical enclosing-try match); dotnet#131252
(miscomputed branch target depth — same corrupted layout, very likely
subsumed by this fix).
- Follow-up idea (not in this PR): a JIT-time assert that a funclet's
blocks form a contiguous RPO range, so any future mis-layout traps at
compile time instead of surfacing as an invalid module.

> [!NOTE]
> This change was authored with the assistance of GitHub Copilot.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2e627b94-2658-41ce-a880-d9c27b7febd9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.