[llvm] [InstCombine][AArch64] Combine ANDs with two constant CMPHS (PR #213995)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 07:56:37 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Matthew Devereau (MDevereau)

<details>
<summary>Changes</summary>

InstCombine

and(cmphs(pg, C0, A), cmphs(pg, A, C1))
 ->
cmphs(pg, C0 - C1, sub(pg, A, C1))

Also emit zero when C1 > C0

---
Full diff: https://github.com/llvm/llvm-project/pull/213995.diff


2 Files Affected:

- (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp (+42) 
- (added) llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-and.ll (+109) 


``````````diff
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 7b1c603d4134c..c5dffa9e76df9 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3392,6 +3392,46 @@ static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
   return std::nullopt;
 }
 
+static std::optional<Instruction *> instCombineSVEAnd(InstCombiner &IC,
+                                                      IntrinsicInst &II) {
+  // and(cmphs(pg, C0, A), cmphs(pg, A, C1))
+  // ->
+  // cmphs(pg, C0 - C1, sub(pg, A, C1))
+  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, 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 {
@@ -3513,6 +3553,8 @@ AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
     return instCombineInStreamingMode(IC, II);
   case Intrinsic::aarch64_sve_umin_u:
     return instCombineSVEUMin(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..810cb95716197
--- /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.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.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.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_different_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_different_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" }

``````````

</details>


https://github.com/llvm/llvm-project/pull/213995


More information about the llvm-commits mailing list