Skip to content

Arm64 SVE: Support scalable constant vectors and masks#127520

Open
a74nh wants to merge 89 commits into
dotnet:mainfrom
a74nh:truemasknode_github
Open

Arm64 SVE: Support scalable constant vectors and masks#127520
a74nh wants to merge 89 commits into
dotnet:mainfrom
a74nh:truemasknode_github

Conversation

@a74nh

@a74nh a74nh commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Adds support to GenTreeVecCon and GenTreeMskCon for constants with unknown sizes. Instead of having a blob of data, the constant is represented as being one of either: a repeated value, an sequence with start and step values, or a value in the first lane and the rest zeroed. To handle this the base type is also required.

As this new structure is slightly bigger than a simd16, the simd_t typedef is pushed up to simd32 sized.

For vector constants, a vector is scalable because if it is of TYP_SIMD.

For mask constants, the type is always TYP_MASK. However on Arm64, masks are only used by SVE. Therefore to tell if a mask is scalable then JitUseScalableVectorT is checked.

The IsAllBitsSet() on mask constants is updated to include a base type. A mask that is all set for TYP_LONG will not be all set for TYP_BYTE, and instead will be 100010001000...

Given two scalable constants it may not be possible to add them together to produce a third scalable constant. Instead they will remain as two vectors in the IR.

To show this implementation is workable, scalable support is added for:

  • Sve.CreateTrueMask*()
  • Sve.CreateFalseMask*()
  • Vector.Create()
  • Vector.CreateScalar()
  • Vector.CreateScalarUnsafe()
  • Vector.CreateSequence()

Fixes #125057

Copilot AI review requested due to automatic review settings April 28, 2026 17:32
@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 Apr 28, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Apr 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.

Adds support to GenTreeVecCon and GenTreeMskCon for constants with unknown sizes. Instead of having a blob of data, the constant is represented as being one of either: a repeated value, an sequence with start and step values, or a value in the first lane and the rest zeroed. To handle this the base type is also required.

As this new structure is slightly bigger than a simd16, the simd_t typedef is pushed up to simd32 sized.

For vector constants, a vector is scalable because if it is of TYP_SIMD.

For mask constants, the type is always TYP_MASK. However on Arm64, masks are only used by SVE. Therefore to tell if a mask is scalable then JitUseScalableVectorT is checked.

The IsAllBitsSet() on mask constants is updated to include a base type. A mask that is all set for TYP_LONG will not be all set for TYP_BYTE, and instead will be 100010001000...

Given two scalable constants it may not be possible to add them together to produce a third scalable constant. Instead they will remain as two vectors in the IR.

To show this implementation is workable, scalable support is added for:
Sve.CreateTrueMask*()
Sve.CreateFalseMask*()
Vector.Create()
Vector.CreateScalar()
Vector.CreateScalarUnsafe()
Vector.CreateSequence()

Fixes dotnet#125057
@a74nh
a74nh force-pushed the truemasknode_github branch from 4754486 to 7fac1f9 Compare April 29, 2026 14:36
@a74nh

a74nh commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

Taking this out of draft now.

Because of the very limited support for scalable SVE, this is currently very hard to test. I've been working off the top of @snickolls-arm's WIP branch with all his code in, which allows me to to call handwritten tests. In current HEAD, there are too many errors before getting to my code.

There's still a lot of work to do on top of this. Eg, I need to get generic ops working, plus all the other Vector APIs which create constants. But, I didn't want this PR to grow too big. The important part is this serves as a base for further constant work.

@dotnet/arm64-contrib @jakobbotsch @tannergooding

@a74nh
a74nh requested review from Copilot and removed request for Copilot April 30, 2026 11:04

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

Note

Copilot was unable to run its full agentic suite in this review.

This PR adds Arm64 SVE “scalable VectorT” support across the JIT, including new encodings for scalable vector/mask constants and updates to value numbering, folding, lowering, LSRA, and codegen to recognize and emit SVE-friendly patterns.

Changes:

  • Introduce new scalable constant representations (simdscalable_t, simdmaskscalable_t) and plumb them through GenTree constant nodes and hashing.
  • Extend value numbering and folding to create/consume scalable SIMD constants on Arm64.
  • Implement Arm64 SVE VectorT intrinsics import and codegen pathways (create/broadcast/sequence), plus mask handling updates.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/coreclr/jit/valuenum.h Adds VN support for scalable SIMD constants on Arm64
src/coreclr/jit/valuenum.cpp Creates/broadcasts scalable SIMD VN constants and dumps them
src/coreclr/jit/simd.h Defines new scalable vector/mask constant encodings and helper APIs
src/coreclr/jit/simd.cpp Implements scalable vector/mask helpers and conversions
src/coreclr/jit/lsraarm64.cpp Reserves temps for scalable vector constants that can’t be directly encoded
src/coreclr/jit/lowerarmarch.cpp Updates mask lowering + VectorT intrinsic handling
src/coreclr/jit/hwintrinsiclistarm64sve.h Enables VectorT intrinsics for SVE
src/coreclr/jit/hwintrinsiccodegenarm64.cpp Emits SVE instructions for VectorT intrinsics
src/coreclr/jit/hwintrinsicarm64.cpp Imports VectorT intrinsics and updates true/false mask creation
src/coreclr/jit/hwintrinsic.h Marks VectorT_* as special cases for scalar/broadcast creation
src/coreclr/jit/gentree.h Extends vector/mask constants to support scalable encodings
src/coreclr/jit/gentree.cpp Adds scalable constant construction, hashing, folding, and printing
src/coreclr/jit/emitarm64.h Repositions signed-immediate helpers used by new SVE paths
src/coreclr/jit/compiler.hpp Extends bitmask helpers for >64-register targets
src/coreclr/jit/compiler.h Adds new compiler helpers for scalable vector/mask constants
src/coreclr/jit/codegenarm64.cpp Adds emission for scalable vector/mask constants

Comment thread src/coreclr/jit/simd.h
Comment thread src/coreclr/jit/simd.cpp Outdated
Comment thread src/coreclr/jit/simd.h Outdated
Comment thread src/coreclr/jit/simd.cpp
Comment thread src/coreclr/jit/codegenarm64.cpp Outdated
Comment thread src/coreclr/jit/codegenarm64.cpp Outdated
Comment thread src/coreclr/jit/gentree.h Outdated
Comment thread src/coreclr/jit/gentree.h Outdated
Copilot AI review requested due to automatic review settings June 11, 2026 10:41

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 18 out of 18 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/coreclr/jit/gentree.cpp:1

  • The printElement lambda ignores its element parameter due to shadowing (e.g., uint8_t element = ...), and the sequence/scalar element computation is incorrect (index + index*step and scalar zeroing based on the value rather than the element index). This will print wrong values for scalable constants (including printing scalar vectors as all zeros when the scalar is non-zero). Rename the parameter (e.g., elementIndex), avoid shadowing, and compute value = index + step * elementIndex (and scalar should return index only for elementIndex == 0).
    src/coreclr/jit/gentree.cpp:1
  • simdscalable_t::operator== treats all “zero” encodings as equal (via IsZero() canonicalization), but this hash includes kind/baseType/step bits unconditionally. That breaks the hash/equality contract for cases like {kind=Sequence, index=0, step=0} vs {kind=Repeated, index=0, step=0} which compare equal but will hash differently, potentially degrading or breaking CSE/lookup logic based on gtHashValue. Canonicalize “zero” before hashing (e.g., if simdVal.IsZero() then hash only a canonical representation, similar to the VN-map hash code path).

Copilot AI review requested due to automatic review settings June 18, 2026 11:01

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 18 out of 18 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/coreclr/jit/gentree.cpp:20504

  • TryEvaluateUnaryInPlace has a control-flow path that doesn't return a value: the default case calls unreached() but then falls through without returning. Even if unreached() is treated as noreturn in some builds, making the return explicit avoids UB/warnings and keeps the function contract clear.

Comment thread src/coreclr/jit/codegenarm64.cpp
Comment thread src/coreclr/jit/gentree.cpp Outdated
Copilot AI review requested due to automatic review settings June 26, 2026 13:51

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 18 out of 18 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/gentree.cpp
Comment thread src/coreclr/jit/gentree.cpp
Copilot AI review requested due to automatic review settings June 26, 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.

Pull request overview

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

Comment on lines +24609 to 24613
if (varTypeIsIntegral(simdBaseType))
{
scalableVecCon->gtSimdScalableVal.gtSimdScalableIndex =
static_cast<uint64_t>(op1->AsIntConCommon()->IntegralValue());
}
Comment on lines +24767 to 24771
if (varTypeIsIntegral(simdBaseType))
{
uint16_t cnsVal = static_cast<uint16_t>(op1->AsIntConCommon()->IntegralValue());
vecCon->gtSimdVal.u16[0] = cnsVal;
break;
scalableVecCon->gtSimdScalableVal.gtSimdScalableIndex =
static_cast<uint64_t>(op1->AsIntConCommon()->IntegralValue());
}
Comment on lines +24915 to 24919
if (varTypeIsIntegral(simdBaseType))
{
scalableVecCon->gtSimdScalableVal.gtSimdScalableIndex =
static_cast<uint64_t>(op1->AsIntConCommon()->IntegralValue());
}
Comment on lines +25087 to +25090
scalableVecCon->gtSimdScalableVal.gtSimdScalableIndex =
static_cast<uint64_t>(op1->AsIntConCommon()->IntegralValue());
scalableVecCon->gtSimdScalableVal.gtSimdScalableStep =
static_cast<uint64_t>(op2->AsIntConCommon()->IntegralValue());
Comment thread src/coreclr/jit/hwintrinsiclistarm64sve.h Outdated
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "a7f17a70a3191b5f04900e39a3248e7148afebb7",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "c473d80e67a0e090fa4a3e67fbc5bc9d122b172a",
  "last_reviewed_commit": "a7f17a70a3191b5f04900e39a3248e7148afebb7",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "c473d80e67a0e090fa4a3e67fbc5bc9d122b172a",
  "last_recorded_worker_run_id": "29679155775",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "a7f17a70a3191b5f04900e39a3248e7148afebb7",
      "review_id": 4730529285
    }
  ]
}

@github-actions github-actions Bot 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.

Holistic Review

Motivation: The PR adds vector-length-agnostic constant support for Arm64 SVE (Fixes #125057). Previously scalable SIMD constants and predicate masks could not be represented, because a fixed-size data blob presumes a known vector width. This blocks constant folding and efficient codegen for Sve.CreateTrue/FalseMask* and Vector.Create/CreateScalar/CreateScalarUnsafe/CreateSequence on scalable types. The motivation is real and well-scoped.

Approach: Instead of a data blob, scalable constants are modeled abstractly via a new simdscalable_t (baseType + kind Repeated/Sequence/Scalar + 64-bit index/step unions) and simdmaskscalable_t (baseType + 0/1 index). The simd_t typedef is widened to simd32_t on Arm64 (guarded by static_assert(sizeof(simd_t) >= sizeof(simdscalable_t))) so existing storage can hold the new struct. Value numbering gains a simdmaskvalue_t wrapper plus new VN maps and specializations, codegen (genSetRegToConst TYP_SIMD) emits SVE dup/index/fmov/ins with a data-section fallback, and LSRA reserves internal registers when the constant isn't immediate-encodable. Crucially, scalable mask behavior is gated behind the DEBUG-only JitConfig.JitUseScalableVectorT(), which meaningfully limits release-codegen risk; scalable vector constants (TYP_SIMD) are wired in more broadly. The EvaluateUnaryInPlaceTryEvaluateUnaryInPlace (bool) refactor correctly threads fold-failure early-outs through all callers, since scalable constants cannot always be folded. The abstraction is clean and the layering is sensible.

Summary: ⚠️ Needs Human Review. This is a large (+2328/-311 across 16 JIT files), intricate SVE codegen change that a JIT/SVE maintainer should validate on real hardware and against the differential/antigen suites. I reviewed by reading only — this worker cannot build or run the JIT — so codegen correctness (SVE instruction selection/encoding, immediate encodability decisions, LSRA internal-register reservation) and VN round-tripping cannot be empirically confirmed here. No blocking defect was found on read-through, and the DEBUG-gating of scalable masks substantially reduces shipping risk. The change is well-structured and the correctness-sensitive refactor is threaded correctly.

Detailed Findings

Try* fold-failure threading is correct. EvaluateUnaryInPlaceTryEvaluateUnaryInPlace and gtFoldExprConvertVecCnsToMask returning GenTree* (original tree on failure) are handled at all call sites (morph.cpp x4, gtFoldExprHWIntrinsic) with proper early-outs; the caller only consumes the result when conversion succeeds.

genFindLowestBit/genMaxOneBit predicate-register handling (compiler.hpp). These are changed from asserting the high mask is RBM_NONE to actually handling high registers under HAS_MORE_THAN_64_REGISTERS. This is a genuine behavioral change enabling predicate-register masks and appears correct and necessary; flagging it as a focus area for the human reviewer since it affects register-mask math beyond the scalable-constant paths.

emitarm64.h isValidSimm hardening. The relocation/shift now guards against undefined-behavior shift overflow when bits == ssize_t bit-width — a real improvement.

💡 Zero-canonicalization equivalence (simd.h/simd.cpp). simdscalable_t::operator== and the VN hash canonicalize any IsZero() value equal regardless of baseType/kind, and a Sequence with index 0 / step 0 is treated as zero. This equivalence (zero-step sequence == repeated-zero) is semantically sound, but it is load-bearing for VN correctness and deduplication; worth a maintainer's confirmation that no consumer distinguishes those representations by baseType.

💡 FP sequence path. gtNewSimdCreateSequenceNode folds integral sequences to a scalable Sequence constant but keeps the FP path as an explicit indices*step+broadcast expression (codegen asserts integral for the sequence form). This appears intentional; noting it so the reviewer confirms the FP path is covered by existing tests.

💡 Test coverage. Per the repo's JIT guidance, new regression tests are not expected for mechanical/infra refactors, and differential testing covers codegen — so this is not a blocker. Given the breadth of new scalable codegen, confirming green SVE CI (and Antigen/Fuzzlyn differential runs) before merge is the primary validation.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 270.4 AIC · ⌖ 11.1 AIC · ⊞ 10K

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.

Design for Vector Constants with agnostic SVE

4 participants