Skip to content

Reject folding local addresses the emitter cannot encode#130990

Open
tannergooding wants to merge 4 commits into
dotnet:mainfrom
tannergooding:tannergooding-fix-minopts-avx512-invalidprogram
Open

Reject folding local addresses the emitter cannot encode#130990
tannergooding wants to merge 4 commits into
dotnet:mainfrom
tannergooding:tannergooding-fix-minopts-avx512-invalidprogram

Conversation

@tannergooding

@tannergooding tannergooding commented Jul 17, 2026

Copy link
Copy Markdown
Member

Fixes #126584.

The emitter only supports byte-sized offsets (< 256) for local variable numbers >= 32768 -- see emitLclVarAddr::initLclVarAddr, which otherwise hits IMPL_LIMITATION("JIT doesn't support offsets larger than 255 into valuetypes for local vars > 32767"). Compiler::IsValidLclAddr already encodes the other emitter limits (16-bit offset, in-bounds) but didn't account for this one.

As a result, a struct block-init of a local numbered >= 32768 whose access reaches offset >= 256 could be contained/folded into the stack (S_R) addressing form by ContainBlockStoreAddress, and then fail at emit time. Since the fold only happens under MinOpts here, it surfaced as an InvalidProgramException in Debug but not Release. The IL is valid (ILVerify agrees); the JIT was simply generating an address form the emitter can't encode.

The fix rejects that case in IsValidLclAddr. Returning false there declines only the fold -- lclmorph then keeps the access as an explicit address computation and codegen materializes it into a register (the ARX form, which uses a real base register + 32-bit displacement and has no varNum-encoding limit). This also covers direct LCL_FLD accesses at offset >= 256 for such locals, not just the block-init path.

The constants 32768 and 256 mirror emitLclVarAddr::initLclVarAddr exactly.

fgOptimizeAddition folds ADD(LCL_ADDR, const) into a single LCL_ADDR. That is a FullOpts-only sibling of the same problem: it previously validated only the addend, so repeated folds could accumulate an offset the emitter can't encode. It now validates the resulting offset through IsValidLclAddr, with the addend bounded to [0, UINT16_MAX] first so the sum is computed without signed overflow.


Added a regression test (Runtime_126584). It uses Reflection.Emit to build a MinOpts method with >32768 IL locals plus a >= 256-byte struct block-init, which is the minimal shape that reproduces the emitter limit without carrying a ~1.5MB source file. It is gated on IsReflectionEmitSupported, so it skips on NativeAOT and other no-dynamic-code scenarios. Harness-validated both directions: it fails against the baseline JIT (InvalidProgramException in M0) and passes with the fix.

The repro is CPU-independent -- despite the issue title, the bug isn't AVX-512-specific; those ISAs just perturbed the local numbering/codegen enough to expose it.

Note

This PR was authored with the assistance of GitHub Copilot.

The emitter only supports byte-sized offsets for locals numbered 32768 or
greater (see emitLclVarAddr::initLclVarAddr). IsValidLclAddr did not account
for this, so a struct block-init at such a local with an offset >= 256 could
be folded into the stack addressing form and hit an IMPL_LIMITATION, surfacing
as an InvalidProgramException under MinOpts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 17, 2026 18:48
@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
@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 the CoreCLR JIT’s Compiler::IsValidLclAddr to account for an additional emitter encoding constraint: when a local number is >= 32768, the emitter can only encode byte-sized offsets (< 256). The intent is to prevent IR forms like LCL_ADDR / LCL_FLD from being produced in cases the emitter can’t encode, avoiding emit-time failures.

Changes:

  • Extend IsValidLclAddr to reject (lclNum >= 32768 && offset >= 256).
  • Update the function’s header comment to document the emitter’s large-varnum offset encoding restriction.
Show a summary per file
File Description
src/coreclr/jit/compiler.hpp Adds an IsValidLclAddr guard for the emitter’s “large varnum => byte offset only” encoding limitation and documents it.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment thread src/coreclr/jit/compiler.hpp
Comment thread src/coreclr/jit/compiler.hpp
tannergooding and others added 2 commits July 17, 2026 12:04
fgOptimizeAddition folds ADD(LCL_ADDR(off), const) into LCL_ADDR(off+const)
but validated only the addend against IsValidLclAddr, not the resulting
offset. Repeated folds (or a fold onto an already-offset LCL_ADDR) could
accumulate an offset the emitter cannot encode. Validate the computed offset
instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Builds a method with more than 32768 locals via Reflection.Emit so the struct
local's number crosses the emitter's large-varnum boundary, then block-inits it
in-body reaching an offset >= 256 under MinOpts. Skips where dynamic code isn't
supported.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 17, 2026 19:35

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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 2

Comment thread src/coreclr/jit/morph.cpp Outdated
Bound the addend to [0, UINT16_MAX] before adding it to the existing
offset so the sum is computed in an unsigned domain that cannot overflow.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 17, 2026 20:28

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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

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.

InvalidProgramException in Debug build only (SIMD/AVX-512 intrinsics, CPU-dependent)

2 participants