Arm64: use FEAT_CSSC cnt/ctz for PopCount and TrailingZeroCount#130332
Conversation
Add support for the Arm64 FEAT_CSSC instruction set extension and use its scalar `cnt` (population count) and `ctz` (count trailing zeros) instructions to optimize BitOperations.PopCount and BitOperations.TrailingZeroCount. When InstructionSet_Cssc is available: * PopCount emits a single `cnt Rd, Rn` instead of the movi/ins/cnt.8b/addv/umov SIMD sequence (and no longer needs an internal SIMD register). * TrailingZeroCount emits a single `ctz Rd, Rn` instead of `rbit` + `clz`. The change follows the existing Rcpc2 instruction-set pattern: Cssc is added to InstructionSetDesc.txt (R2R bit 93) and the generated ISA files, detected in the minipal (Linux HWCAP2_CSSC, macOS sysctl hw.optional.arm.FEAT_CSSC), enabled in the VM behind a new EnableArm64Cssc config, and wired through the AOT tools. Windows is intentionally skipped as there is no official IsProcessorFeaturePresent flag for CSSC yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR adds Arm64 FEAT_CSSC (ARMv8.8+) detection and wiring so the JIT can opportunistically use scalar cnt/ctz instructions to optimize BitOperations.PopCount and BitOperations.TrailingZeroCount on supported CPUs.
Changes:
- Adds a new Arm64 ISA flag (
Cssc) end-to-end (minipal detection → VM CPU flags → JIT ISA plumbing → R2R/ILCompiler metadata). - Teaches the Arm64 backend to emit scalar
cnt/ctzwhenInstructionSet_Csscis available; updates LSRA and emitter/unit tests accordingly. - Bumps the JIT/EE interface GUID to reflect the new ISA definition.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/native/minipal/cpufeatures.h | Adds ARM64IntrinsicConstants_Cssc bit to the minipal feature mask. |
| src/native/minipal/cpufeatures.c | Detects FEAT_CSSC via Linux HWCAP2 and Apple sysctlbyname, and sets the Cssc feature bit. |
| src/coreclr/vm/codeman.cpp | Enables InstructionSet_Cssc based on detected CPU features + config gate. |
| src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt | Adds Cssc to the instruction set definitions and assigns an R2R bit. |
| src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs | Regenerates managed ISA enums/helpers to include Cssc. |
| src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs | Maps InstructionSet.ARM64_Cssc to the new R2R instruction set value. |
| src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs | Adds ReadyToRunInstructionSet.Cssc = 93. |
| src/coreclr/tools/Common/InstructionSetHelpers.cs | Includes cssc in the optimistic ARM64 instruction set support list. |
| src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs | Keeps tool-side Arm64 intrinsic-constant flags in sync with minipal and adds cssc mapping. |
| src/coreclr/jit/lsraarm64.cpp | Avoids requiring a SIMD temp register for PopCount when CSSC scalar cnt is available. |
| src/coreclr/jit/instrsarm64.h | Adds scalar cnt (GPR form) and new scalar ctz instruction encodings. |
| src/coreclr/jit/hwintrinsic.cpp | Extends ISA range table with a slot for Cssc. |
| src/coreclr/jit/emitarm64.cpp | Allows emitting GPR cnt by falling through to the DR_2G encoding; adds support for ctz formatting. |
| src/coreclr/jit/codegenarmarch.cpp | Uses scalar cnt/ctz when opportunistically depending on InstructionSet_Cssc. |
| src/coreclr/jit/codegenarm64test.cpp | Adds emitter unit tests covering scalar cnt/ctz emission. |
| src/coreclr/inc/readytoruninstructionset.h | Adds READYTORUN_INSTRUCTION_Cssc=93. |
| src/coreclr/inc/jiteeversionguid.h | Updates the JIT/EE interface version GUID. |
| src/coreclr/inc/corinfoinstructionset.h | Regenerates CORINFO instruction set enum/string mapping and adds R2R→CORINFO mapping for Cssc. |
| src/coreclr/inc/clrconfigvalues.h | Adds EXTERNAL_EnableArm64Cssc config switch. |
Copilot Code ReviewHolistic AssessmentMotivation: Justified — FEAT_CSSC provides scalar Approach: The approach is correct — add CSSC as an internal-only instruction set (no public API surface), detect it at runtime via hwcap/sysctl, and use Summary: ✅ LGTM. The change is well-structured, follows existing conventions for internal instruction sets, instruction encodings are correct, and the emitter/LSRA/codegen changes are consistent with each other. No correctness, safety, or compatibility concerns found. Detailed FindingsDetailed Findings✅ Instruction Encoding CorrectnessVerified the ARM64 instruction encodings against the FEAT_CSSC specification:
The promotion of ✅ Emitter Dispatch LogicThe
The removed guard ( ✅ LSRA ConsistencyThe LSRA change correctly avoids allocating a SIMD temp register for PopCount when CSSC is available, since the scalar ✅ Opportunistic ISA Usage
✅ InstructionSetDesc.txt & Generated FilesThe CSSC entry follows the internal-only pattern (empty public name column) matching Rcpc/Rcpc2/Dczva. R2R bit 93 is correctly assigned as the next available. The JIT-EE version GUID update is expected since the instruction set enum changed. ✅ CPU Feature Detection
✅ ReadyToRun & NativeAOTCSSC is correctly added to the optimistic instruction set builder in 💡 Emitter Unit Test CoverageThe emitter unit tests in Note This review was generated by GitHub Copilot.
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@jkotas @dotnet/jit-contrib any opinion on this? we don't yet have a HW to test it on CI (the closest is Apple M4 in helix queue, but we need Apple M5 or later, or Neoverse V4/N4 (my speculation) or later), but from the other hand it's just two simple instructions, perhaps, for marketing purposes? 🙂 I have ran the full test suite locally. |
|
By default, we should not be taking advantage of instruction extensions that we do not have sufficient automated test coverage for. Not worth the risk. I do not have a problem with the code being added, but it should be off by default until we have sufficient automated test coverage. |
Ok, I've disabled it by default (DOTNET_EnableArm64Cssc=0) |
Add compIsaSupportedDebugOnly(Cssc) asserts in emitIns_R_R for the general-register cnt/ctz encodings, plus a JitConfig.EnableArm64Cssc toggle so the altjit ISA block can enable CSSC for codegen inspection and emitter unit tests without CSSC hardware. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/ba-g wasm is heavily broken on ci |
A quick PR to optimize PopCount and TrailingZeroCount with CSSC (armv8.9/armv9.4) that I found on my macbook m5.
Codegen diff:
I ran the entire runtime & libs tests locally on apple m5 + my own smoke tests.
Helix has Apple M4, but that doesn't have CSSC yet, so we'll get the full coverage only when we add Apple M5+ (or, presumably, Neoverse N4+/V4+)
PS: We can use more from CSSC: abs, smax
Benchmarks
Each method sums the operation over a 1024-element array of random values (base = current codegen, PR = FEAT_CSSC
cnt/ctz):Apple M5 Pro, macOS,
--corerun base pr:The big
PopCount(ulong)win comes from avoiding the GPR↔SIMD round-trip, which is expensive on Apple cores. Numbers may look different on server arm64 hardware.Note
The benchmark harness and results table in this section were generated by GitHub Copilot.