The following example, found by Fuzzlyn, causes a JIT assertion failure on arm64 (SVE):
// Generated by Fuzzlyn v3.3 on 2026-07-05 17:04:37
// Run on Arm64 Linux
// Seed: 4638962307177550988-vectort,vector64,vector128,armadvsimd,armadvsimdarm64,armaes,armarmbase,armarmbasearm64,armcrc32,armcrc32arm64,armdp,armrdm,armrdmarm64,armsha1,armsha256,armsve,armsve2
// Reduced from 44.1 KiB to 1.2 KiB in 00:00:31
// Hits JIT assert for Release:
// Assertion failed 'canRepairTargetOverlap || (targetReg != intrin.op2->GetRegNum()) || genIsSameLocalVar(rmwOp, intrin.op2)' in 'Program:M0()' during 'Generate code' (IL size 138; hash 0xaf50ff37; FullOpts)
//
// File: /__w/1/s/src/coreclr/jit/hwintrinsiccodegenarm64.cpp Line: 874
//
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
public class Program
{
public static void Main()
{
M0();
}
public static void M0()
{
var vr1 = Vector.Create<long>(0);
var vr3 = Vector128.CreateScalar(2714202814947371637L).AsVector();
var vr7 = (Vector<short>)Vector128.CreateScalar(0).AsVector();
var vr8 = Vector.Create<short>(0);
var vr6 = Sve.CreateBreakPropagateMask(vr7, vr8);
var vr11 = (short)0;
var vr10 = Vector128.CreateScalar(vr11).AsVector();
var vr13 = (short)0;
var vr12 = Vector128.CreateScalar(vr13).AsVector();
var vr15 = (short)0;
var vr14 = Vector128.CreateScalar(vr15).AsVector();
var vr9 = Sve.CreateBreakBeforePropagateMask(vr10, vr12, vr14);
var vr5 = Sve.And(vr6, vr9);
var vr4 = Sve.AddAcross(vr5);
var vr2 = Sve.AddSaturate(vr3, vr4);
if (Sve.TestLastTrue(vr1, vr2))
{
var vr0 = new int[, ]
{
{
0
}
};
}
}
}
The assertion:
Assertion failed 'canRepairTargetOverlap || (targetReg != intrin.op2->GetRegNum()) || genIsSameLocalVar(rmwOp, intrin.op2)' in 'Program:M0()' during 'Generate code' (IL size 138; hash 0xaf50ff37; FullOpts)
File: /__w/1/s/src/coreclr/jit/hwintrinsiccodegenarm64.cpp Line: 874
Analysis of jitdump (AI-generated)
[!NOTE]
The following analysis was generated by AI (GitHub Copilot) and may contain inaccuracies.
Analysis (AI-generated)
Assertion: canRepairTargetOverlap || (targetReg != intrin.op2->GetRegNum()) || genIsSameLocalVar(rmwOp, intrin.op2)
Location: src/coreclr/jit/hwintrinsiccodegenarm64.cpp:874 (CodeGen::checkRMWRegisters)
Phase: Generate code (FullOpts), target arm64/SVE.
What the JIT is compiling
The reduced method computes a chain of SVE predicate operations. After
morph/lowering the relevant tree in BB01 is:
[000041] HWINTRINSIC mask And_Predicates (RMW flag set)
op1 = [000015] CreateBreakPropagateMask
op2 = [000031] CreateBreakBeforePropagateMask
And_Predicates is wrapped in a ConditionalSelect(ptrue, And_Predicates(a, b), zero)
pattern, so it is generated through the embedded-masked path
(genEmbeddedMaskedHWIntrinsic).
Register state at the failure
From the JitDump codegen trace right before the assert:
- Target register of the operation: p0 (
t86 ConditionalSelect REG p0)
op1 (CreateBreakPropagateMask) is materialized in p2
op2 (CreateBreakBeforePropagateMask, [000031]) is materialized in p0
So targetReg == op2Reg == p0, while rmwReg == p1/p2 != targetReg.
Why the assert fires
genEmbeddedMaskedHWIntrinsic unconditionally calls checkRMWRegisters(intrinEmbMask, targetReg).
For And_Predicates (2 operands, not an FMA and not an explicit-masked
operation) checkRMWRegisters falls into the default branch and selects
rmwOp = op1. Then, because targetReg != rmwReg, it validates each other
operand with:
case 2:
if (rmwReg != intrin.op2->GetRegNum())
assert(canRepairTargetOverlap || (targetReg != intrin.op2->GetRegNum())
|| genIsSameLocalVar(rmwOp, intrin.op2));
Here targetReg (p0) == op2Reg (p0), genIsSameLocalVar is false, and
canRepairTargetOverlap is only true for NI_Sve_MultiplyAddRotateComplex, so
the assertion fails.
Root cause
The mismatch is between LSRA and codegen over And_Predicates:
- On arm64,
GenTree::isRMWHWIntrinsic returns true for any
IsEmbeddedMaskedOperation/IsOptionalEmbeddedMaskedOperation
(gentree.cpp:22443). And_Predicates carries
HW_Flag_EmbeddedMaskedOperation (hwintrinsiclistarm64sve.h:568), so it is
treated as a destructive RMW operation whose destructive operand
checkRMWRegisters assumes to be op1.
- However, the emitted instruction is the SVE all-predicate AND
AND Pd.B, Pg/Z, Pn.B, Pm.B, which is not destructive — the destination
predicate may legally alias either source predicate. LSRA correctly allowed
the target to reuse op2's register (p0).
Codegen's checkRMWRegisters does not model this: it insists the target may
only overlap the single fixed rmwOp (op1). When the allocator legitimately
places op2 in the target register, the debug validation trips even though the
final encoding would be correct.
In short: And_Predicates (and likely other non-destructive predicate
embedded-masked operations) is over-classified as an op1-destructive RMW
intrinsic, so checkRMWRegisters rejects a valid target/op2 register overlap
that LSRA produced. Either checkRMWRegisters must recognize that predicate
operations allow the target to alias any source operand, or the RMW/destructive
operand modeling for these predicate intrinsics must be made consistent between
LSRA and codegen.
Attached details.zip file that includes the repro.mc and jitdump.txt files for this example.
cc @dotnet/jit-contrib
details.zip
The following example, found by Fuzzlyn, causes a JIT assertion failure on arm64 (SVE):
The assertion:
Analysis of jitdump (AI-generated)
Analysis (AI-generated)
Assertion:
canRepairTargetOverlap || (targetReg != intrin.op2->GetRegNum()) || genIsSameLocalVar(rmwOp, intrin.op2)Location:
src/coreclr/jit/hwintrinsiccodegenarm64.cpp:874(CodeGen::checkRMWRegisters)Phase: Generate code (FullOpts), target arm64/SVE.
What the JIT is compiling
The reduced method computes a chain of SVE predicate operations. After
morph/lowering the relevant tree in
BB01is:And_Predicatesis wrapped in aConditionalSelect(ptrue, And_Predicates(a, b), zero)pattern, so it is generated through the embedded-masked path
(
genEmbeddedMaskedHWIntrinsic).Register state at the failure
From the JitDump codegen trace right before the assert:
t86 ConditionalSelect REG p0)op1(CreateBreakPropagateMask) is materialized in p2op2(CreateBreakBeforePropagateMask,[000031]) is materialized in p0So
targetReg == op2Reg == p0, whilermwReg == p1/p2 != targetReg.Why the assert fires
genEmbeddedMaskedHWIntrinsicunconditionally callscheckRMWRegisters(intrinEmbMask, targetReg).For
And_Predicates(2 operands, not an FMA and not an explicit-maskedoperation)
checkRMWRegistersfalls into thedefaultbranch and selectsrmwOp = op1. Then, becausetargetReg != rmwReg, it validates each otheroperand with:
Here
targetReg (p0) == op2Reg (p0),genIsSameLocalVaris false, andcanRepairTargetOverlapis only true forNI_Sve_MultiplyAddRotateComplex, sothe assertion fails.
Root cause
The mismatch is between LSRA and codegen over
And_Predicates:GenTree::isRMWHWIntrinsicreturns true for anyIsEmbeddedMaskedOperation/IsOptionalEmbeddedMaskedOperation(
gentree.cpp:22443).And_PredicatescarriesHW_Flag_EmbeddedMaskedOperation(hwintrinsiclistarm64sve.h:568), so it istreated as a destructive RMW operation whose destructive operand
checkRMWRegistersassumes to beop1.AND Pd.B, Pg/Z, Pn.B, Pm.B, which is not destructive — the destinationpredicate may legally alias either source predicate. LSRA correctly allowed
the target to reuse
op2's register (p0).Codegen's
checkRMWRegistersdoes not model this: it insists the target mayonly overlap the single fixed
rmwOp(op1). When the allocator legitimatelyplaces
op2in the target register, the debug validation trips even though thefinal encoding would be correct.
In short:
And_Predicates(and likely other non-destructive predicateembedded-masked operations) is over-classified as an op1-destructive RMW
intrinsic, so
checkRMWRegistersrejects a valid target/op2register overlapthat LSRA produced. Either
checkRMWRegistersmust recognize that predicateoperations allow the target to alias any source operand, or the RMW/destructive
operand modeling for these predicate intrinsics must be made consistent between
LSRA and codegen.
Attached details.zip file that includes the repro.mc and jitdump.txt files for this example.
cc @dotnet/jit-contrib
details.zip