Skip to content

JIT: Improve parameter register field extraction and reconstruction - #131174

Open
lewing wants to merge 25 commits into
dotnet:mainfrom
lewing:wasm-r2r-simd-scalar-extract
Open

lewing wants to merge 25 commits into
dotnet:mainfrom
lewing:wasm-r2r-simd-scalar-extract

Conversation

@lewing

@lewing lewing commented Jul 21, 2026 •

Copy link
Copy Markdown
Member

Rationalization and lowering now handle struct fields in ABI registers directly instead of going through memory:

  • Rationalizer::RewriteParameterField extracts aligned scalar fields from SIMD or FP parameter registers with GetElement (x64 movshdup, arm64 dup). Misaligned fields keep the bitcast/shift/narrow path.
  • On xarch, an 8-byte FP register is rebuilt from two promoted floats with unpcklps.
  • Promotion::MapsToParameterRegister is kept in sync with the rationalizer.

For {float X; float Y;}, return is 22 B → 4 B (PerfScore 6.25 → 2.00) and construct-and-pass is 9.4 ns → 4.4 ns. SuperPMI is net −7.8 KB on linux-x64 and −132 B on linux-arm, and library collections only improve. The regressions are mostly x64 HFA arguments that are only forwarded, which are now split and rebuilt with unpcklps. Promoting those by ABI segment instead would avoid that; this PR accepts the trade-off until then. The wasm fix landed separately in #131237. SuperPMI diffs

Note

This PR was authored with the assistance of GitHub Copilot.

The parameter-register-to-local mapping in lowering maps a scalar field of an
unenregisterable parameter to the local created for the containing register
segment, retyping the local access to the field's scalar type. On x64/arm64 a
scalar overlaps the low bytes of the vector register, so reading the register
local at scalar width is a free, correct reinterpret.

On wasm this is invalid: a v128 and a scalar (f64/f32/...) are distinct
value-stack types with no implicit reinterpret, so 'local.get <v128>' typed as
a scalar produces invalid wasm (wasm-tools: 'expected f64, found v128'). This
surfaces when crossgen2 compiles, e.g., Vector128<T>.GetHashCode (whose
GetElementUnsafe reads a scalar lane of the vector) inlined into
GenericEqualityComparer<Vector128<double>>.GetHashCode.

Guard the mapping on wasm: skip mapping a scalar field to a SIMD register
segment so the access falls back to the (memory-homed) unenregisterable
parameter and a normal scalar load is emitted. This is the floating-point
ToScalar / SIMD->scalar var_type case noted as deferred in dotnet#130444.
Copilot AI lite review requested due to automatic review settings July 21, 2026 22:31
@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: @agocke
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 adjusts JIT lowering for the wasm target to avoid inducing an invalid scalar read from a SIMD (v128) register-local when the accessed parameter field is a non-SIMD scalar type. The change is localized to the parameter-register-local mapping logic and is guarded to affect only wasm.

Changes:

  • Add a TARGET_WASM-guarded check in Lowering::FindInducedParameterRegisterLocals to skip mapping when the ABI segment is SIMD but the field access is scalar.
  • Preserve existing behavior for non-wasm targets and for SIMD-typed field accesses.

Comment thread src/coreclr/jit/lower.cpp Resolved
@AndyAyersMS

Copy link
Copy Markdown
Member

Is this on top of #130866? Else how do we have a V128 parameter?

I agree with Tanner, would be nice to use f64x2.extract_lane 0 here or similar to avoid having to spill and reload.

@lewing

lewing commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

Yes — this is effectively on top of #130866 (it's in a wasm R2R prototype branch that has #130866 merged, which materializes `Vector128` as a wasm `v128` parameter — hence the v128 register parameter here). Agreed on `f64x2.extract_lane 0` (and the general extract/insert for the sibling paths) being the better, spill-free lowering. Let me look at doing it that way in this PR rather than the memory fallback.

Note

This comment was authored with the assistance of GitHub Copilot.

Per review feedback (thanks @tannergooding, @AndyAyersMS): rather than skipping the
parameter-register-local mapping for a scalar field of a SIMD register (which forces a spill/reload
through the memory-homed parameter), extract the lane directly from the SIMD register local via
gtNewSimdGetElementNode. This lowers to an explicit lane extract (e.g. f64x2.extract_lane 0) with no
spill. The GetElement node is lowered by the subsequent per-block lowering pass.

> [!NOTE]
> This change was authored with the assistance of GitHub Copilot.
Copilot AI review requested due to automatic review settings July 21, 2026 23:36

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

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

Comment thread src/coreclr/jit/lower.cpp Resolved
Comment thread src/coreclr/jit/lower.cpp Resolved
lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…tnet#131174)

Extract the scalar lane directly from the SIMD register local instead of a memory spill/reload.
lewing added a commit to lewing/runtime that referenced this pull request Jul 22, 2026
…tnet#131174)

Extract the scalar lane directly from the SIMD register local instead of a memory spill/reload.
lewing added 2 commits July 21, 2026 20:10
Use direct SIMD lane extraction and scalar bit manipulation for fields carried in floating-point parameter registers. Keep physical promotion eligibility synchronized and retain the ARM32 fallback.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 22214765-2cd2-4951-b778-8793730f2bc1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 22214765-2cd2-4951-b778-8793730f2bc1
Copilot AI review requested due to automatic review settings July 22, 2026 01:24
@lewing lewing changed the title JIT: Fix wasm scalar-field extraction from a SIMD-register parameter JIT: Improve parameter register field extraction Jul 22, 2026

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

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

Copilot AI review requested due to automatic review settings July 22, 2026 01:33

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

JIT/Directed uses hand-maintained MergedWrapperProjectReference lists
rather than the glob that JIT/opt uses, and the new project was never
added to one. No runner referenced it, so the test never ran in CI in
any form - it appears in neither the passed nor the skipped results for
the Pri0 legs. Building it directly with -Test or -Dir hid this, since
those bypass runner discovery.

Add it to Directed_ro.csproj alongside the other StructABI projects;
DebugType None with Optimize true matches that runner. The generated
runner now registers it as an out-of-process test, the same shape as
EmptyStructs.cmd.

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Accept both the legacy two-operand and VEX three-operand forms of unpcklps in the FieldListFloatInsertion checks.

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

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 review overview

🔵 Needs a closer look

ARM32 promotion needs a conservative exclusion or a corresponding reconstruction path.

Review tier: Lite
Findings: None

Previously missed findings (1)

In code that hasn't changed since last review

Medium severity Avoid re-enabling costly float promotion on ARM32

src/​coreclr/​jit/​promotion.cpp:3077

On ARM32 this condition now allows MapsToParameterRegister to promote a float field from a floating-point parameter-register segment at offset 0. The induced extraction is supported, but ARM32 has no matching field-list reconstruction path, so calls that forward or P/Invoke these promoted values spill and reload; the PR reports a net +314-byte ARM32 regression, including +270 bytes in RayTracer:GetNaturalColor. Please retain the previous conservative exclusion for float accesses on ARM32 (while still allowing the existing integer-at-offset-zero case), or provide an ARM32 reconstruction before enabling this promotion.

Copilot AI review requested due to automatic review settings September 17, 2026 23:26

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 review overview

🟡 Changes recommended

Critical SIMD extraction safety and ARM32 regression issues remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Medium severity

Open (2)

Comment thread src/coreclr/jit/lower.cpp Resolved
Comment thread src/coreclr/jit/promotion.cpp Resolved
lewing and others added 2 commits October 19, 2026 18:07
Both callers guarantee a non-null use.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b44b5a92-06b0-45c9-a11f-c3ac6cab9b81
Lowering::FindInducedParameterRegisterLocals moved to
Rationalizer::RewriteParameterField on main; carry the scalar
extraction changes over to the new location.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b44b5a92-06b0-45c9-a11f-c3ac6cab9b81
lewing and others added 2 commits October 28, 2026 15:03
A GC-typed LCL_FLD over a SIMD parameter register reached
gtNewSimdGetElementNode, which requires an arithmetic base type.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b44b5a92-06b0-45c9-a11f-c3ac6cab9b81
- Gate the conservative FP-register check on LOWER_DECOMPOSE_LONGS, the
  actual reason it exists, instead of TARGET_ARM.
- Clarify comments for the wasm exclusion and xarch-only float-pair rebuild.
- Move FieldListFloatInsertion to JIT/opt/Structs, which is picked up by
  the JIT/opt merged runner glob.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b44b5a92-06b0-45c9-a11f-c3ac6cab9b81
@lewing lewing added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed area-VM-coreclr labels Oct 29, 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.

Comment thread src/coreclr/jit/rationalize.cpp Resolved
Comment thread src/coreclr/jit/lower.cpp Resolved
…ility

IsFloatPairFieldListRegister already validates the whole register, so skip
the generic per-field loop for it, and restore the original float insertion
check. Document the fallthrough cases of the SIMD8 extraction.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b44b5a92-06b0-45c9-a11f-c3ac6cab9b81

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants