Skip to content

JIT: Rematerialize spilled FP/SIMD/mask constants instead of stack spill/reload#130313

Open
tannergooding wants to merge 8 commits into
dotnet:mainfrom
tannergooding:tannergooding-psychic-eureka
Open

JIT: Rematerialize spilled FP/SIMD/mask constants instead of stack spill/reload#130313
tannergooding wants to merge 8 commits into
dotnet:mainfrom
tannergooding:tannergooding-psychic-eureka

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Floating-point, SIMD, and mask constants (CNS_DBL, CNS_VEC, CNS_MSK) have no GC liveness and, for a well-defined subset of values, can be rematerialized cheaply without needing a scratch register. Rather than spilling such a value to the stack and reloading it, this PR skips creating the spill temp/store in genProduceReg and instead rematerializes the constant at the reload point in genUnspillRegIfNeeded.

Details

A shared predicate isRematerializableConstant gates both the skip-store site and the rematerialization site. It only returns true for values that genSetRegToConst can encode directly, i.e. that need no scratch register:

  • xarch: zero (xorps), all-bits-set (pcmpeqd/vpternlogd), or a RIP-relative load from the constant''s memory home.
  • Arm64: 0.0/fmov-immediate doubles, zero/all-bits-set/movi-broadcast vectors, and all masks (pfalse/ptrue). Values that Arm64 would otherwise load from the constant pool via adrp/ldr need a scratch address register that is not reserved at the reload point, so those remain spilled.

Rematerialization goes through the GenTree* overload of genSetRegToConst, which honors the explicit target register. Values consumed directly from the spill temp as a contained memory operand (GTF_NOREG_AT_USE) continue to be spilled normally.

Diffs

SPMI asmdiffs (coreclr_tests, windows-arm64): 554 methods improved, 0 regressions, -5432 bytes, with spill store/reload pairs replaced by movi/fmov/mvni/pfalse/ptrue.

Note

This PR description was generated with the assistance of GitHub Copilot.

tannergooding and others added 2 commits July 7, 2026 10:16
…ill/reload

Floating-point, SIMD, and mask constants (CNS_DBL, CNS_VEC, CNS_MSK) have no GC
liveness and can be rematerialized cheaply on xarch without a scratch register
(zero via xorps, all-bits-set via pcmpeqd/vpternlogd, or a RIP-relative load from
the constant's memory home). Rather than spilling such a value to the stack and
reloading it, skip creating the spill temp and store in genProduceReg, and
rematerialize the constant at the reload point in genUnspillRegIfNeeded.

A shared predicate isRematerializableConstant gates both sites and is currently
scoped to TARGET_XARCH; other targets can be enabled later for the subset of
values that likewise need no scratch register. Values consumed directly from the
spill temp as a contained memory operand (GTF_NOREG_AT_USE) continue to be
spilled normally.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Extend the constant rematerialization added for xarch to Arm64, for the subset of
CNS_DBL/CNS_VEC/CNS_MSK values that genSetRegToConst encodes directly into an
instruction without a scratch register: 0.0/fmov-immediate doubles, zero/all-bits-
set/movi-broadcast vectors, and all masks (pfalse/ptrue). Values that Arm64 would
otherwise load from the constant pool via adrp/ldr need a scratch address register
that is not reserved at the reload point, so those remain spilled.

isRematerializableConstant gains an Arm64 branch mirroring the no-temp fast paths in
genSetRegToConst, and genUnspillRegIfNeeded rematerializes into the reload target via
the GenTree* overload of genSetRegToConst (which honors the explicit target register
on Arm64). The genProduceReg skip-store site is already gated by the predicate.

SPMI asmdiffs (coreclr_tests, windows-arm64): 554 methods improved, 0 regressions,
-5432 bytes, with spill store/reload pairs replaced by movi/fmov/mvni/pfalse/ptrue.

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

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 changes CoreCLR JIT codegen to avoid stack spill/reload for certain spilled FP/SIMD/mask constants by rematerializing them at the reload point instead. It introduces a shared predicate to ensure the optimization is only applied when the constant can be materialized without requiring scratch registers (and without GC bookkeeping).

Changes:

  • Update CodeGen::genUnspillRegIfNeeded to rematerialize eligible spilled GT_CNS_DBL / GT_CNS_VEC / GT_CNS_MSK into the reload target register (xarch/arm64) instead of loading from a spill temp.
  • Add CodeGen::isRematerializableConstant to centralize the eligibility logic and keep the spill-skip and reload-remat sites consistent.
  • Update CodeGen::genProduceReg to skip creating a spill temp / emitting the spill store for eligible constants (while preserving the existing behavior for GTF_NOREG_AT_USE cases that must read from the spill temp as a contained memory operand).
Show a summary per file
File Description
src/coreclr/jit/codegenlinear.cpp Implements the rematerialization path on unspill, adds the shared predicate, and skips spill-temp/store creation for eligible constants.
src/coreclr/jit/codegen.h Declares the new isRematerializableConstant(GenTree*) helper on CodeGen.

Copilot's findings

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

tannergooding and others added 3 commits July 7, 2026 10:41
When a rematerializable FP/SIMD/mask constant is spilled immediately after its
definition (GTF_SPILL with spill-after semantics), the value is rematerialized at
each reload point and its stack store is skipped. The original definition therefore
materializes a value into a register that is never read.

Add a shared isRematerializedConstantSpill predicate and use it to also skip emitting
the definition (genSetRegToConst) at the genCodeForTreeNode sites on xarch and arm64,
in addition to the existing store-skip in genProduceReg.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The data section packed entries sequentially with no per-entry alignment padding, so emitDataGenFind's ((curOffs % alignment) == 0) guard could reject a valid match when the existing constant landed at a misaligned offset, emitting a duplicate.

Give each dataSection a dsOffset computed via AlignUp in the begin-functions and use it as the single source of truth for lookup, output, and display. This honors the requested alignment (a bug if it wasn't) while SMALL_CODE still gets tight packing via its smaller requested alignment.

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

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: 7/7 changed files
  • Comments generated: 3

Comment thread src/coreclr/jit/emit.cpp
Comment thread src/coreclr/jit/emit.cpp Outdated
Comment thread src/coreclr/jit/emit.cpp Outdated
Copilot AI review requested due to automatic review settings July 7, 2026 23:53
@tannergooding

Copy link
Copy Markdown
Member Author

tannergooding requested review from AaronRobinsonMSFT, dotnet-diag and janvorli code owners

Sorry for the noise. The src/native/external/libunwind/README.md got automatically changed from a symlink to a regular file and then auto-committed by copilot.

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: 7/7 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.

2 participants