Skip to content

[Wasm RyuJIT] "Partial containment" for GT_LCL_ADDR#128680

Draft
kg wants to merge 11 commits into
dotnet:mainfrom
kg:wasm-lcladdr-partial-containment
Draft

[Wasm RyuJIT] "Partial containment" for GT_LCL_ADDR#128680
kg wants to merge 11 commits into
dotnet:mainfrom
kg:wasm-lcladdr-partial-containment

Conversation

@kg
Copy link
Copy Markdown
Member

@kg kg commented May 28, 2026

Experimenting with an approach for folding offsets into GT_IND and GT_STOREIND. Seems to work so far and doesn't complicate the code much, but the need to change codegenwasm.cpp:702 makes me wonder if this approach is fundamentally doomed...

cc @AndyAyersMS @SingleAccretion thoughts? I found a comment somewhere suggesting we need a new "nuw" frontend node but I'm not sure what a nuw is and whether that is the right solution for these scenarios.

LEAs are the next candidate but they seem trickier to get right. I'm not sure how common 'partially containable' LEAs and LCL_ADDRs are in a larger codebase, might do some instrumented runs over SPC to find out.

Copilot AI review requested due to automatic review settings May 28, 2026 05:34
@kg kg added arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels May 28, 2026
@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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 experiments with Wasm RyuJIT containment for GT_LCL_ADDR under indirect loads/stores, folding local frame offsets into Wasm memory operands while still emitting the frame pointer push.

Changes:

  • Marks GT_LCL_ADDR addresses of indirections as contained in Wasm lowering.
  • Adjusts Wasm codegen so contained GT_LCL_ADDR still emits its base address but skips the explicit offset add.
  • Emits folded offsets for GT_IND and GT_STOREIND memory operands.

Reviewed changes

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

File Description
src/coreclr/jit/lowerwasm.cpp Adds containment marking for GT_LCL_ADDR operands of indirections.
src/coreclr/jit/codegenwasm.cpp Implements partial-contained GT_LCL_ADDR codegen and folded load/store offsets.

Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp Outdated
@SingleAccretion
Copy link
Copy Markdown
Contributor

SingleAccretion commented May 28, 2026

thoughts?

I don't think we need to break the lowering-codegen contract to get this optimization. My thinking on it was transforming:

l = LCL_VAR_ADDR [+8]
    ...
    STOREIND(l, ...)

into:

l = LCL_VAR_ADDR [+0]
    ...
    LEA(l) [+8] ; contained
    STOREIND(l, ...)

there are some tweaks needed to make this work in liveness and things like VisitLocalDefs, since this store form would be 'new' for tracked (but on-stack) locals. But otherwise it's not controversial / tricky.

BTW, this is only a problem for STOREIND (and other stores), since for loads we can just contain directly.

"nuw" frontend node but I'm not sure what a nuw is

"No unsigned wrap" (LLVM terminology). The basic problem is avoid transforming this:

// p is byte*, consider "p == nuint.MaxValue - 2"
*(p + 3)

into a WASM address mode.

@kg
Copy link
Copy Markdown
Member Author

kg commented May 28, 2026

thoughts?

I don't think we need to break the lowering-codegen contract to get this optimization. My thinking on it was transforming:

l = LCL_VAR_ADDR [+8]
    ...
    STOREIND(l, ...)

into:

l = LCL_VAR_ADDR [+0]
    ...
    LEA(l) [+8] ; contained
    STOREIND(l, ...)

there are some tweaks needed to make this work in liveness and things like VisitLocalDefs, since this store form would be 'new' for tracked (but on-stack) locals. But otherwise it's not controversial / tricky.

The tweaks are because this would 'erase' the reference to the var at +8 right, by replacing it with +0 +8? So the reference to var2 disappears and becomes a false reference to var0.

I'm not sure I like that...

BTW, this is only a problem for STOREIND (and other stores), since for loads we can just contain directly.

"nuw" frontend node but I'm not sure what a nuw is

"No unsigned wrap" (LLVM terminology). The basic problem is avoid transforming this:

// p is byte*, consider "p == nuint.MaxValue - 2"
*(p + 3)

into a WASM address mode.

@SingleAccretion
Copy link
Copy Markdown
Contributor

SingleAccretion commented May 28, 2026

The tweaks are because this would 'erase' the reference to the var at +8 right, by replacing it with +0 +8? So the reference to var2 disappears and becomes a false reference to var0.

It needs a bit of thinking around how to represent the frame base while still referencing the original local (it's not negotiable the original local needs to stay referenced, of course). The saving grace I think is that we only need this new contract for the very small window from RA to codegen.

The easiest (but not the most efficient) way is to introduce a new LEA-like node that'd have a contained LCL_ADDR and another operand as just the frame base (e. g. PHYS_REG), like so:

STOREIND
  NEW_NODE ; contained
    LCL_ADDR ; contained, for liveness
    PHYS_REG <frame base> ; not contained

it looks wacky though, maybe there are better idea out there.

@kg kg force-pushed the wasm-lcladdr-partial-containment branch from e27c7b7 to 4a4b002 Compare May 29, 2026 13:26
Copilot AI review requested due to automatic review settings May 29, 2026 13:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 13:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread src/coreclr/jit/lowerwasm.cpp
Comment thread src/coreclr/jit/codegenwasm.cpp
@kg
Copy link
Copy Markdown
Member Author

kg commented May 30, 2026

I'm hitting this assertion now that I added the containment check copilot suggested:

Single method repro args:--singlemethodtypename "Microsoft.Win32.SafeHandles.SafeFileHandle" --singlemethodname "CreateAnonymousPipe" --singlemethodindex 1
Z:\runtime\src\coreclr\jit\lower.cpp:12013
Assertion failed 'm_compiler->lvaGetDesc(lclAddr)->IsAddressExposed()' in 'Microsoft.Win32.SafeHandles.SafeFileHandle:CreateAnonymousPipe(byref,byref,bool,bool)' during 'Linear scan register alloc' (IL size 160; hash 0x22c9c037; MinOpts)
image

Not sure what's going on, but will keep digging...

Copilot AI review requested due to automatic review settings May 30, 2026 07:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment on lines +2430 to +2437
case GT_PARTIALLY_CONTAINED_LCL_ADDR:
// WASM-TODO: Implement removal for dead partially contained lcl_addrs and their containers
if (TLiveness::EliminateDeadCode && node->IsUnusedValue())
{
JITDUMP("WASM-TODO: Not removing unused partially contained local address:");
DISPNODE(node);
}
break;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This isn't hit at all during a CG2 of S.P.CoreLib for me so it may be a theoretical problem

Comment thread src/coreclr/jit/codegenwasm.cpp
Comment on lines +450 to +454
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size() DEBUGARG(true /* disableAssertion */)))
{
// An indir through a lcl_addr should never have a multiply-used address since it doesn't need a null check
// and can't fault. If it does, this transform is incorrect.
assert((addr->gtLIRFlags & LIR::Flags::MultiplyUsed) == LIR::Flags::None);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture 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