[llvm] 74f3e7b - [RISCV] Add additional conditions for signed icmp cases when canonicalizing masks for VL-predicate (#218068)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 13:54:41 PDT 2026


Author: Min-Yih Hsu
Date: 2026-08-25T13:54:35-07:00
New Revision: 74f3e7bd8306c84ecde6b3d5a55a0d51e9cfd726

URL: https://github.com/llvm/llvm-project/commit/74f3e7bd8306c84ecde6b3d5a55a0d51e9cfd726
DIFF: https://github.com/llvm/llvm-project/commit/74f3e7bd8306c84ecde6b3d5a55a0d51e9cfd726.diff

LOG: [RISCV] Add additional conditions for signed icmp cases when canonicalizing masks for VL-predicate (#218068)

Fixes #217500 

Previously in #214877 we added a mask canonicalization to make it easier
for the mask-consuming instructions to use VL-predicated instead. But
the transformation will be incorrect if the icmp against boundary value
is signed. Specifically, the problem arises if `%base + %offset` is
negative. This patch fixes this issue by guarding `%base + %offset` to
ensure this expression (1) never overflow, and (2) is non-negative.

Alive2 Proof: https://alive2.llvm.org/ce/z/L2M_LL

Added: 
    

Modified: 
    llvm/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/test/CodeGen/RISCV/rvv/masked-load-vl-predicatable.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 194aad22775cf..e9685006db1df 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19165,10 +19165,17 @@ static bool narrowIndex(SDValue &N, ISD::MemIndexType IndexType, SelectionDAG &D
 /// The idea behind this canonicalization is that if %p is used as a mask
 /// in a masked.load/store, we can easily turn it to use VL-predicate later
 /// by assigning `vl = min(%m, <number of vector elements>)`.
-static SDValue canonicalizeMaskForVLPredicate(EVT MaskVT, SDValue LHS,
-                                              SDValue RHS, ISD::CondCode CC,
-                                              const SDLoc &DL,
-                                              SelectionDAG &DAG) {
+static SDValue canonicalizeMaskForVLPredicate(
+    EVT MaskVT, SDValue LHS, SDValue RHS, ISD::CondCode CC, const SDLoc &DL,
+    SelectionDAG &DAG, const TargetLowering::DAGCombinerInfo &DCI) {
+
+  // This transformation performs checks against the vector element type, which
+  // is also used to generate scalar value that will be splatted later. Because
+  // of this, the emitted scalar value might not be legal type and therefore we
+  // need to run this before type legalizer.
+  if (!DCI.isBeforeLegalize())
+    return SDValue();
+
   using namespace SDPatternMatch;
   if (!MaskVT.isFixedLengthVector() ||
       !(CC == ISD::SETUGT || CC == ISD::SETGT || CC == ISD::SETULT ||
@@ -19183,7 +19190,10 @@ static SDValue canonicalizeMaskForVLPredicate(EVT MaskVT, SDValue LHS,
   }
   bool IsSigned = ISD::isSignedIntSetCC(CC);
 
-  SDValue Boundary = DAG.getSplatValue(RHS, /*LegalizeType=*/true);
+  EVT ElementVT = LHS.getValueType().getScalarType();
+  uint64_t ElementSize = LHS.getScalarValueSizeInBits();
+
+  SDValue Boundary = DAG.getSplatValue(RHS);
   if (!Boundary)
     return SDValue();
 
@@ -19196,32 +19206,48 @@ static SDValue canonicalizeMaskForVLPredicate(EVT MaskVT, SDValue LHS,
       return SDValue();
   }
 
-  SDValue BaseIndex = DAG.getSplatValue(LHSOp0, /*LegalizeType=*/true);
+  SDValue BaseIndex = DAG.getSplatValue(LHSOp0);
   if (!BaseIndex) {
     std::swap(LHSOp0, LHSOp1);
-    BaseIndex = DAG.getSplatValue(LHSOp0, /*LegalizeType=*/true);
+    BaseIndex = DAG.getSplatValue(LHSOp0);
   }
-  if (!BaseIndex || !isa<BuildVectorSDNode>(LHSOp1) ||
-      BaseIndex.getValueType() != Boundary.getValueType())
+  if (!BaseIndex || !isa<BuildVectorSDNode>(LHSOp1))
     return SDValue();
 
   // Return {a,n} from a build_vector sequence of {a, a+n, a+2n, a+3n, ....}
   auto StepVector = cast<BuildVectorSDNode>(LHSOp1)->isArithmeticSequence();
   if (!StepVector || !StepVector->second.isOne())
     return SDValue();
-  EVT LenVT = BaseIndex.getValueType();
   const APInt &Start = StepVector->first;
-  unsigned LenWidth = LenVT.getScalarSizeInBits();
-  APInt Offset =
-      IsSigned ? Start.sextOrTrunc(LenWidth) : Start.zextOrTrunc(LenWidth);
+
+  Boundary = DAG.getExtOrTrunc(IsSigned, Boundary, DL, ElementVT);
+  BaseIndex = DAG.getExtOrTrunc(IsSigned, BaseIndex, DL, ElementVT);
+  SDValue Offset = DAG.getConstant(IsSigned ? Start.sextOrTrunc(ElementSize)
+                                            : Start.zextOrTrunc(ElementSize),
+                                   DL, ElementVT);
+
+  if (IsSigned) {
+    // Two additional conditions:
+    // 1. Offset + BaseIndex never overflow
+    if (!DAG.willNotOverflowAdd(/*IsSigned=*/true, BaseIndex, Offset))
+      return SDValue();
+
+    // 2. Offset + BaseIndex has to be non-negative
+    auto BaseIndexKB = DAG.computeKnownBits(BaseIndex);
+    auto OffsetKB = DAG.computeKnownBits(Offset);
+    if (!KnownBits::add(BaseIndexKB, OffsetKB, /*NSW=*/true,
+                        /*NUW=*/false)
+             .isNonNegative())
+      return SDValue();
+  }
 
   unsigned MinOpc = IsSigned ? ISD::SMIN : ISD::UMIN;
   SDValue NewStepVector = DAG.getStepVector(DL, LHS.getValueType());
-  BaseIndex = DAG.getNode(
-      ISD::ADD, DL, LenVT, BaseIndex, DAG.getConstant(Offset, DL, LenVT),
-      IsSigned ? SDNodeFlags::NoSignedWrap : SDNodeFlags::NoUnsignedWrap);
-  BaseIndex = DAG.getNode(MinOpc, DL, LenVT, Boundary, BaseIndex);
-  Boundary = DAG.getNode(ISD::SUB, DL, LenVT, Boundary, BaseIndex);
+  BaseIndex = DAG.getNode(ISD::ADD, DL, ElementVT, BaseIndex, Offset,
+                          IsSigned ? SDNodeFlags::NoSignedWrap
+                                   : SDNodeFlags::NoUnsignedWrap);
+  BaseIndex = DAG.getNode(MinOpc, DL, ElementVT, Boundary, BaseIndex);
+  Boundary = DAG.getNode(ISD::SUB, DL, ElementVT, Boundary, BaseIndex);
   Boundary = DAG.getSplat(RHS.getValueType(), DL, Boundary);
 
   return DAG.getSetCC(DL, MaskVT, NewStepVector, Boundary, CC);
@@ -19356,7 +19382,8 @@ static SDValue performSETCCCombine(SDNode *N,
   EVT OpVT = N0.getValueType();
 
   ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(2))->get();
-  if (SDValue V = canonicalizeMaskForVLPredicate(VT, N0, N1, Cond, dl, DAG))
+  if (SDValue V =
+          canonicalizeMaskForVLPredicate(VT, N0, N1, Cond, dl, DAG, DCI))
     return V;
 
   // Looking for an equality compare.

diff  --git a/llvm/test/CodeGen/RISCV/rvv/masked-load-vl-predicatable.ll b/llvm/test/CodeGen/RISCV/rvv/masked-load-vl-predicatable.ll
index 6eeb9839418df..9a4d28493aa93 100644
--- a/llvm/test/CodeGen/RISCV/rvv/masked-load-vl-predicatable.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/masked-load-vl-predicatable.ll
@@ -92,14 +92,16 @@ define <16 x float> @masked_load_signed_cmp(ptr %p, i32 signext %n, i32 signext
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    srliw a2, a2, 3
 ; CHECK-NEXT:    min a2, a1, a2
-; CHECK-NEXT:    sub a1, a1, a2
+; CHECK-NEXT:    subw a1, a1, a2
 ; CHECK-NEXT:    li a2, 16
 ; CHECK-NEXT:    min a1, a1, a2
 ; CHECK-NEXT:    vsetvli zero, a1, e32, m4, tu, ma
 ; CHECK-NEXT:    vle32.v v8, (a0)
 ; CHECK-NEXT:    ret
-  %11 = insertelement <16 x i32> poison, i32 %iv, i32 0
+  %base = lshr i32 %iv, 3
+  %11 = insertelement <16 x i32> poison, i32 %base, i32 0
   %12 = shufflevector <16 x i32> %11, <16 x i32> poison, <16 x i32> zeroinitializer
   %13 = or disjoint <16 x i32> %12, <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
   %14 = insertelement <16 x i32> poison, i32 %n, i32 0
@@ -114,14 +116,16 @@ define <16 x float> @masked_load_sgt(ptr %p, i32 signext %n, i32 signext %iv) no
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    srliw a2, a2, 3
 ; CHECK-NEXT:    min a2, a1, a2
-; CHECK-NEXT:    sub a1, a1, a2
+; CHECK-NEXT:    subw a1, a1, a2
 ; CHECK-NEXT:    li a2, 16
 ; CHECK-NEXT:    min a1, a1, a2
 ; CHECK-NEXT:    vsetvli zero, a1, e32, m4, tu, ma
 ; CHECK-NEXT:    vle32.v v8, (a0)
 ; CHECK-NEXT:    ret
-  %11 = insertelement <16 x i32> poison, i32 %iv, i32 0
+  %base = lshr i32 %iv, 3
+  %11 = insertelement <16 x i32> poison, i32 %base, i32 0
   %12 = shufflevector <16 x i32> %11, <16 x i32> poison, <16 x i32> zeroinitializer
   %13 = or disjoint <16 x i32> %12, <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
   %14 = insertelement <16 x i32> poison, i32 %n, i32 0
@@ -253,3 +257,24 @@ define <16 x float> @negative_masked_load_wrong_step_vector(ptr %p, i32 signext
   %19 = tail call <16 x float> @llvm.masked.load(ptr %p, <16 x i1> %16, <16 x float> zeroinitializer)
   ret <16 x float> %19
 }
+
+; We can't to do this transformation if we cannot gaurantee %base + %offset to always be non-negative.
+define <16 x float> @negative_masked_load_signed_cmp(ptr %p, i32 signext %n, i32 signext %base) nounwind {
+; CHECK-LABEL: negative_masked_load_signed_cmp:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 16, e32, m4, ta, mu
+; CHECK-NEXT:    vid.v v8
+; CHECK-NEXT:    vor.vx v8, v8, a2
+; CHECK-NEXT:    vmslt.vx v0, v8, a1
+; CHECK-NEXT:    vmv.v.i v8, 0
+; CHECK-NEXT:    vle32.v v8, (a0), v0.t
+; CHECK-NEXT:    ret
+  %11 = insertelement <16 x i32> poison, i32 %base, i32 0
+  %12 = shufflevector <16 x i32> %11, <16 x i32> poison, <16 x i32> zeroinitializer
+  %13 = or disjoint <16 x i32> %12, <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+  %14 = insertelement <16 x i32> poison, i32 %n, i32 0
+  %15 = shufflevector <16 x i32> %14, <16 x i32> poison, <16 x i32> zeroinitializer
+  %16 = icmp slt <16 x i32> %13, %15
+  %19 = tail call <16 x float> @llvm.masked.load(ptr %p, <16 x i1> %16, <16 x float> zeroinitializer)
+  ret <16 x float> %19
+}


        


More information about the llvm-commits mailing list