Skip to content

JIT: omit redundant return-buffer initialization - #133881

Open
benaadams wants to merge 2 commits into
dotnet:mainfrom
benaadams:jit/split-retbuf-initialization
Open

benaadams wants to merge 2 commits into
dotnet:mainfrom
benaadams:jit/split-retbuf-initialization

Conversation

@benaadams

@benaadams benaadams commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

A non-GC struct return buffer can be cleared in the prolog even though its first defining call overwrites it completely. Recognize that call as explicit initialization using the existing return-buffer definition helper and reference counts. Retain initialization for GC-containing buffers, partial definitions, promoted fields, async methods, and potentially observable exception paths.

Split from #133833 at @jakobbotsch's request so this change can be reviewed independently.

Base: main at a652cdfed564.

Focused generated code

Windows x64 method sizes. Tier1 measurements use a repeated harness with tiering and dynamic PGO enabled; the harness waits for tier-up and captures Tier1 output. FullOpts measurements disable tiering for deterministic codegen checks.

Method FullOpts bytes Tier1 bytes
FullyDefined 112 -> 95 112 -> 95

Assembly example: FullyDefined

FullOpts, Windows x64. Instruction bytes are omitted; ... denotes unchanged assembly.

 ...
        push     rsi
        push     rbx
        sub      rsp, 56
-       vxorps   xmm4, xmm4, xmm4
-       vmovdqa  xmmword ptr [rsp+0x20], xmm4
-       xor      eax, eax
-       mov      qword ptr [rsp+0x30], rax
        mov      rbx, rcx
 G_M24874_IG02:
        lea      rcx, [rsp+0x20]
 ...
-; Total code size: 112 bytes
+; Total code size: 95 bytes

SuperPMI results

Windows x64 Checked JIT, eleven SuperPMI collections; identical settings on both sides and loop alignment disabled.

Comparable contexts Before bytes After bytes Saved
983,234 238,457,491 238,455,292 2,199

131 contexts shrink; 7 grow by 36 bytes total; 5 equal-size assembly differences. Zero compilation failures. The existing recordings contain 983,424 contexts. Updated main lacks recorded answers in 190 contexts on both sides; those contexts are excluded from the table.

Seven contexts grow by 36 bytes in total through zeroing/layout changes. The two largest increases are 14 bytes each in C# LocalRewriter.MakeDecimalLiteral, with higher static PerfScore. These tradeoffs are included in the net figure.

Validation

  • Windows x64 Checked and Release JIT builds; Linux x64 Release JIT build.
  • Six existing physical-promotion regression files, run with the candidate JIT in normal, forced-promotion and JitStress=2 modes: 18 successful runs.
  • Focused behavioral tests pass on base and candidate in optimized, forced-promotion, JitStress=2 and tiering/PGO modes.
  • Exception, prior-read and partial-buffer tests retain .locals init while omitting explicit initialization stores. JIT dumps confirm the exception, reference-count and partial-definition guards are exercised. A GC-containing return-buffer test collects before returning; its caller retains GC-safe initialization.
  • Focused tests also pass with Windows x64 GCStress=0xC; the Release test project builds successfully.
  • Windows x64 codegen checks pass with the change and fail on its base.
  • Linux x64 behavioral tests pass on both sides. The codegen assertions for this ABI-specific example are Windows-only.
  • Changed-line clang-format 17.0.6 and git diff checks.

Release JIT throughput was measured with PIN instruction counts (candidate versus this PR's base):

Collection Comparable contexts Before JIT instructions After JIT instructions Change
benchmarks.run.windows.x64.checked.mch 51,617 87,397,498,731 87,366,103,140 -0.03592%
hashset-pgo-recollected.mch 1,307 558,875,294 558,775,175 -0.01791%

These small JIT instruction-count differences are not application-throughput measurements. Code size and static PerfScore also do not establish execution speed. No ARM64 or x86 execution is claimed.

A non-GC struct return buffer can be cleared in the prolog even though its
first defining call overwrites it completely. Recognize that call as
explicit initialization using the existing return-buffer definition helper
and reference counts. Retain initialization for GC-containing buffers,
partial definitions, promoted fields, async methods, and potentially
observable exception paths.

Validation against this change's base on updated main a652cdf:
- Windows x64 Checked and Release and Linux x64 Release JIT builds.
- 18 existing physical-promotion regression runs pass with normal,
  forced promotion and JitStress=2 settings.
- Focused optimized, promotion-stress, JitStress=2 and warmed-up
  tiering/PGO executions pass on base and candidate; Tier1 is captured.
- Windows codegen checks fail on base and pass on candidate. Linux
  behavior passes, with codegen checks where applicable.
- Eleven SuperPMI collections: 983,234 comparable contexts;
  238,457,491 -> 238,455,292 bytes (2,199 saved).
  131 shrink; 7 grow by 36 bytes total.
  Zero compilation failures; 190 missing-recording contexts excluded.
  The 190 recording gaps affect both sides.
- Release PIN JIT instructions, benchmarks: -0.03592%.
- Release PIN JIT instructions, HashSet PGO: -0.01791%.

Zeroing/layout tradeoffs include two 14-byte MakeDecimalLiteral increases
with higher static PerfScore. The net figure includes all regressions.

Code size, static PerfScore and JIT instruction counts do not establish
application execution speed. No ARM64 or x86 execution is claimed.

Related to dotnet#133833.
Copilot AI lite review requested due to automatic review settings September 14, 2026 14:07
@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 Sep 14, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Sep 14, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 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.

🟡 Changes recommended

Regression coverage does not yet exercise the new safeguards, and the project reference ordering nit remains.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR removes redundant JIT prolog initialization for fully defined, non-GC struct return buffers while preserving safety cases.

Changes:

  • Recognizes eligible return-buffer calls as explicit initialization.
  • Preserves initialization for partial, GC-containing, promoted, asynchronous, and observable exception cases.
  • Adds regression/codegen tests and registers the test project.
File summaries
File Summary / review note
src/tests/JIT/Directed/physicalpromotion/ReturnBufferInitialization.csproj Configures the return-buffer initialization tests.
src/tests/JIT/Directed/physicalpromotion/ReturnBufferInitialization.cs Adds behavioral and codegen coverage; additional first-definition and GC-containing cases are needed to exercise the new safeguards.
src/tests/JIT/Directed/Directed_do.csproj Registers the test project; its reference should be moved after readbackbeforeqmark to preserve ordering.
src/coreclr/jit/optimizer.cpp Adds guarded recognition of eligible return-buffer initialization.
Review details

Suppressed comments (1)

src/tests/JIT/Directed/Directed_do.csproj:12

  • The MergedWrapperProjectReference entries in this group are kept in path order (addressinterference through readbackbeforeqmark), but the new ReturnBufferInitialization entry is inserted before them. Please move it after readbackbeforeqmark so the list remains ordered.
    <MergedWrapperProjectReference Include="physicalpromotion\ReturnBufferInitialization.csproj" />
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/tests/JIT/Directed/physicalpromotion/ReturnBufferInitialization.cs Outdated
Use SkipInit while retaining CLR local initialization so the exception,
prior-read and partial-buffer cases reach their intended guards without
a preceding explicit initialization store. Add a GC-containing return
buffer whose callee collects before returning. Alphabetize the test
project reference.

Validation: JIT phase dumps confirm the guarded shapes and retained
initialization. Windows baseline/candidate tests pass in normal, forced
promotion, JitStress=2 and tiering/PGO modes. Linux baseline/candidate
tests and Windows GCStress=0xC pass. Candidate codegen checks pass and
baseline checks fail as expected. Release test-project build passes.
Compiler logic is unchanged.
Copilot AI review requested due to automatic review settings September 14, 2026 14:53

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.

🔵 Needs a closer look

The change affects safety-sensitive JIT code generation and requires final human review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

This branch has not been deployed

No deployments
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 community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants