[llvm] 480b2c9 - [InstCombine][AArch64] Combine ANDs with two constant CMPHS (#213995)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 7 03:13:20 PDT 2026


Author: Matthew Devereau
Date: 2026-08-07T11:13:15+01:00
New Revision: 480b2c9eb47f1c105432a99b1623abda97e88259

URL: https://github.com/llvm/llvm-project/commit/480b2c9eb47f1c105432a99b1623abda97e88259
DIFF: https://github.com/llvm/llvm-project/commit/480b2c9eb47f1c105432a99b1623abda97e88259.diff

LOG: [InstCombine][AArch64] Combine ANDs with two constant CMPHS (#213995)

InstCombine

and(cmphs(pg, ConstA, A), cmphs(pg, A, ConstB))
 ->
cmphs(pg, ConstA - ConstB, sub(pg, A, ConstB))

Also emit zero when ConstB > ConstA

Added: 
    llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-and.ll

Modified: 
    llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 643c5bf4441b1..b55e0856c792e 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3412,6 +3412,46 @@ static std::optional<Instruction *> instCombineSVEOrr(InstCombiner &IC,
   return IC.replaceInstUsesWith(II, NewUMin);
 }
 
+static std::optional<Instruction *> instCombineSVEAnd(InstCombiner &IC,
+                                                      IntrinsicInst &II) {
+  // and(cmphs(pg, ConstA, A), cmphs(pg, A, ConstB))
+  // ->
+  // cmphs(pg, ConstA - ConstB, sub(pg, A, ConstB))
+  constexpr Intrinsic::ID CmphsID = Intrinsic::aarch64_sve_cmphs;
+  Value *Pg = II.getOperand(0);
+  Value *LHS = II.getOperand(1);
+  Value *RHS = II.getOperand(2);
+
+  Value *A, *PgLHS, *PgRHS;
+  uint64_t ConstA, ConstB;
+  if (!match(LHS, m_Intrinsic<CmphsID>(m_Value(PgLHS), m_ConstantInt(ConstA),
+                                       m_Value(A))) ||
+      !match(RHS, m_Intrinsic<CmphsID>(m_Value(PgRHS), m_Specific(A),
+                                       m_ConstantInt(ConstB))) ||
+      !LHS->hasOneUser() || !RHS->hasOneUser())
+    return std::nullopt;
+
+  // Always false regardless of predication
+  if (ConstB > ConstA)
+    return IC.replaceInstUsesWith(II, Constant::getNullValue(II.getType()));
+
+  // The predicate for both CMPHSs must match.
+  // The predicate for the AND can either be equal to the CMPHS predicates, or
+  // either of the CMPHS values.
+  if (PgLHS != PgRHS || (Pg != LHS && Pg != RHS && Pg != PgLHS))
+    return std::nullopt;
+
+  Type *VecTy = A->getType();
+  Constant *Base = ConstantInt::get(VecTy, ConstB);
+  Value *Sub = IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_sub_u, VecTy,
+                                          {PgLHS, A, Base});
+  Constant *Limit = ConstantInt::get(VecTy, ConstA - ConstB);
+  Value *NewCmphs =
+      IC.Builder.CreateIntrinsic(CmphsID, VecTy, {PgLHS, Limit, Sub});
+
+  return IC.replaceInstUsesWith(II, NewCmphs);
+}
+
 std::optional<Instruction *>
 AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
                                      IntrinsicInst &II) const {
@@ -3535,6 +3575,8 @@ AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
     return instCombineSVEUMin(IC, II);
   case Intrinsic::aarch64_sve_orr_u:
     return instCombineSVEOrr(IC, II);
+  case Intrinsic::aarch64_sve_and_z:
+    return instCombineSVEAnd(IC, II);
   }
 
   return std::nullopt;

diff  --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-and.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-and.ll
new file mode 100644
index 0000000000000..4ef84dccdb5d9
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-and.ll
@@ -0,0 +1,109 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+define <vscale x 16 x i1> @and_cmphs_range_lhs_pred_op1(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_lhs_pred_op1(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.sub.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> splat (i8 25), <vscale x 16 x i8> [[TMP1]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %le.z, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_lhs_pred_op2(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_lhs_pred_op2(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.sub.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> splat (i8 25), <vscale x 16 x i8> [[TMP1]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %ge.a, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_i8_pred_pg(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_i8_pred_pg(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.sub.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> splat (i8 25), <vscale x 16 x i8> [[TMP1]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_underflow(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_underflow(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    ret <vscale x 16 x i1> zeroinitializer
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 90))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 65), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %le.z, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_
diff erent_cmp_preds(<vscale x 16 x i1> %pg0, <vscale x 16 x i1> %pg1, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_
diff erent_cmp_preds(
+; CHECK-SAME: <vscale x 16 x i1> [[PG0:%.*]], <vscale x 16 x i1> [[PG1:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[GE_A:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG0]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[LE_Z:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG1]], <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> [[A]])
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[LE_Z]], <vscale x 16 x i1> [[LE_Z]], <vscale x 16 x i1> [[GE_A]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg0, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg1, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %le.z, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_unrelated_and_pred(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %and.pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_unrelated_and_pred(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[AND_PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[GE_A:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[LE_Z:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> [[A]])
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[AND_PG]], <vscale x 16 x i1> [[LE_Z]], <vscale x 16 x i1> [[GE_A]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %and.pg, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  ret <vscale x 16 x i1> %in.range
+}
+
+define <vscale x 16 x i1> @and_cmphs_range_multi_uses(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+;
+; CHECK-LABEL: define <vscale x 16 x i1> @and_cmphs_range_multi_uses(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[GE_A:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 65))
+; CHECK-NEXT:    [[LE_Z:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> [[A]])
+; CHECK-NEXT:    [[IN_RANGE:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[LE_Z]], <vscale x 16 x i1> [[LE_Z]], <vscale x 16 x i1> [[GE_A]])
+; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i1> [[GE_A]])
+; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i1> [[LE_Z]])
+; CHECK-NEXT:    ret <vscale x 16 x i1> [[IN_RANGE]]
+;
+  %ge.a = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 65))
+  %le.z = call <vscale x 16 x i1> @llvm.aarch64.sve.cmphs.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> splat (i8 90), <vscale x 16 x i8> %a)
+  %in.range = call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %le.z, <vscale x 16 x i1> %le.z, <vscale x 16 x i1> %ge.a)
+  call void (...) @llvm.fake.use(<vscale x 16 x i1> %ge.a)
+  call void (...) @llvm.fake.use(<vscale x 16 x i1> %le.z)
+  ret <vscale x 16 x i1> %in.range
+}
+
+attributes #0 = { "target-features"="+sve" }


        


More information about the llvm-commits mailing list