[llvm] [AArch64][SVE] Fold nested boolean UMIN trees (PR #209234)

Matthew Devereau via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 31 07:13:16 PDT 2026


https://github.com/MDevereau updated https://github.com/llvm/llvm-project/pull/209234

>From f0a6e03d5c9db0887f694c34feaf05ada44f39e0 Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Wed, 29 Jul 2026 15:31:58 +0000
Subject: [PATCH 1/6] [AArch64][SVE] Fold nested boolean UMIN trees

Fold redundant UMIN clamps in logical boolean reduction trees when all
operations use the same predicate.
---
 .../AArch64/AArch64TargetTransformInfo.cpp    |  35 ++++++
 .../AArch64/sve-intrinsic-opts-cmpne.ll       | 100 ++++++++++++++++++
 2 files changed, 135 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 8642cb9a3ca08..4f5b0a24a6f3f 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3366,6 +3366,39 @@ instCombineInStreamingMode(InstCombiner &IC, IntrinsicInst &II) {
   return std::nullopt;
 }
 
+static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
+                                                       IntrinsicInst &II) {
+  Value *Pg = II.getOperand(0);
+  auto getUMinOpFromBooleanUMin = [&Pg](Value *UMin) -> IntrinsicInst * {
+    Value *LHS;
+    if (!match(UMin, m_Intrinsic<Intrinsic::aarch64_sve_umin_u>(
+                         m_Specific(Pg), m_Value(LHS), m_One())))
+      return nullptr;
+    if (!match(LHS, m_Intrinsic<Intrinsic::aarch64_sve_umin_u>(
+                        m_Specific(Pg), m_Value(), m_Value())))
+      return nullptr;
+
+    return cast<IntrinsicInst>(LHS);
+  };
+
+  IntrinsicInst *OuterUMin = getUMinOpFromBooleanUMin(&II);
+  if (!OuterUMin || !OuterUMin->hasOneUse())
+    return std::nullopt;
+
+  IntrinsicInst *LHS = getUMinOpFromBooleanUMin(OuterUMin->getOperand(1));
+  IntrinsicInst *RHS = getUMinOpFromBooleanUMin(OuterUMin->getOperand(2));
+  if (!LHS || !RHS)
+    return std::nullopt;
+
+  Value *NewUMin = IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_umin_u,
+                                              II.getType(), {Pg, LHS, RHS});
+  Value *NewBooleanUMin =
+      IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_umin_u, II.getType(),
+                                 {Pg, NewUMin, II.getOperand(2)});
+
+  return IC.replaceInstUsesWith(II, NewBooleanUMin);
+}
+
 std::optional<Instruction *>
 AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
                                      IntrinsicInst &II) const {
@@ -3485,6 +3518,8 @@ AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
     return instCombineSVEUxt(IC, II, 32);
   case Intrinsic::aarch64_sme_in_streaming_mode:
     return instCombineInStreamingMode(IC, II);
+  case Intrinsic::aarch64_sve_umin_u:
+    return instCombineSVEUMin(IC, II);
   }
 
   return std::nullopt;
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
index d9a5434c35269..c490595eb3eab 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
@@ -572,6 +572,106 @@ define <vscale x 16 x i1> @dupq_b_idx(i64 %idx) #0 {
   ret <vscale x 16 x i1> %5
 }
 
+define <vscale x 16 x i8> @logical_bool_tree_i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP3]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
+;
+  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
+  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
+  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
+  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
+  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %abcd.bool
+}
+
+define <vscale x 16 x i8> @logical_bool_tree_i8_multiuse_outer_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_multiuse_outer_umin(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
+; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
+; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP2]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
+;
+  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
+  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
+  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
+  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
+  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
+  call void (...) @llvm.fake.use(<vscale x 16 x i8> %abcd)
+  ret <vscale x 16 x i8> %abcd.bool
+}
+
+define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_outer_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_outer_umin(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
+; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
+; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
+;
+  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
+  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
+  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
+  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
+  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %abcd.bool
+}
+
+define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_inner_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_inner_umin(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
+; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
+; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
+;
+  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
+  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
+  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
+  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
+  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %abcd.bool
+}
+
+define <vscale x 16 x i8> @logical_bool_tree_i8_nonboolean_clamp(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_nonboolean_clamp(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
+; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
+; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 2))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
+;
+  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
+  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
+  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
+  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
+  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 2))
+  ret <vscale x 16 x i8> %abcd.bool
+}
+
 declare <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32)
 
 declare <vscale x 16 x i8> @llvm.vector.insert.nxv16i8.v16i8(<vscale x 16 x i8>, <16 x i8>, i64)

>From 601d6c39df3a497786b9025e201f283a26253f3c Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Wed, 29 Jul 2026 16:35:36 +0000
Subject: [PATCH 2/6] add comment

---
 llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 4f5b0a24a6f3f..e356f1f6c49bf 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3368,6 +3368,15 @@ instCombineInStreamingMode(InstCombiner &IC, IntrinsicInst &II) {
 
 static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
                                                        IntrinsicInst &II) {
+  // Fold umin(umin(umin(umin(A, B), 1),
+  //                umin(umin(C, D), 1)),
+  //           1)
+  // into
+  //      umin(umin(umin(A, B),
+  //                umin(C, D)),
+  //           1),
+  // removing two redundant boolean umins when predication is equal across all
+  // umins.
   Value *Pg = II.getOperand(0);
   auto getUMinOpFromBooleanUMin = [&Pg](Value *UMin) -> IntrinsicInst * {
     Value *LHS;

>From c14b388cdf39028ec294856fa458d5f0c7c5829c Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Thu, 30 Jul 2026 16:50:25 +0000
Subject: [PATCH 3/6] Rework patch to two simpler combines

---
 .../AArch64/AArch64TargetTransformInfo.cpp    |  59 ++++----
 .../AArch64/sve-intrinsic-opts-cmpne.ll       | 100 --------------
 .../AArch64/sve-intrinsic-opts-umin.ll        | 126 ++++++++++++++++++
 3 files changed, 149 insertions(+), 136 deletions(-)
 create mode 100644 llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll

diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index e356f1f6c49bf..e23371c8873e5 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3368,44 +3368,31 @@ instCombineInStreamingMode(InstCombiner &IC, IntrinsicInst &II) {
 
 static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
                                                        IntrinsicInst &II) {
-  // Fold umin(umin(umin(umin(A, B), 1),
-  //                umin(umin(C, D), 1)),
-  //           1)
-  // into
-  //      umin(umin(umin(A, B),
-  //                umin(C, D)),
-  //           1),
-  // removing two redundant boolean umins when predication is equal across all
-  // umins.
+  // umin(umin(A, 1), umin(B, 1)) -> umin(umin(A,B), 1)
+  constexpr Intrinsic::ID UMinID = Intrinsic::aarch64_sve_umin_u;
+  Value *A, *B;
   Value *Pg = II.getOperand(0);
-  auto getUMinOpFromBooleanUMin = [&Pg](Value *UMin) -> IntrinsicInst * {
-    Value *LHS;
-    if (!match(UMin, m_Intrinsic<Intrinsic::aarch64_sve_umin_u>(
-                         m_Specific(Pg), m_Value(LHS), m_One())))
-      return nullptr;
-    if (!match(LHS, m_Intrinsic<Intrinsic::aarch64_sve_umin_u>(
-                        m_Specific(Pg), m_Value(), m_Value())))
-      return nullptr;
+  if (match(II.getOperand(1),
+            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(A), m_One())) &&
+      match(II.getOperand(2),
+            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(B), m_One())) &&
+      II.hasOneUse()) {
+    Value *NewUMin =
+        IC.Builder.CreateIntrinsic(UMinID, II.getType(), {Pg, A, B});
+    Value *NewLogicalUMin = IC.Builder.CreateIntrinsic(
+        UMinID, II.getType(), {Pg, NewUMin, ConstantInt::get(II.getType(), 1)});
+    return IC.replaceInstUsesWith(II, NewLogicalUMin);
+  }
+
+  // umin(umin(A, 1), 1) -> umin(A, 1)
+  Value *InnerUMin;
+  if (match(&II,
+            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(InnerUMin), m_One())) &&
+      match(InnerUMin,
+            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(A), m_One())))
+    return IC.replaceInstUsesWith(II, InnerUMin);
 
-    return cast<IntrinsicInst>(LHS);
-  };
-
-  IntrinsicInst *OuterUMin = getUMinOpFromBooleanUMin(&II);
-  if (!OuterUMin || !OuterUMin->hasOneUse())
-    return std::nullopt;
-
-  IntrinsicInst *LHS = getUMinOpFromBooleanUMin(OuterUMin->getOperand(1));
-  IntrinsicInst *RHS = getUMinOpFromBooleanUMin(OuterUMin->getOperand(2));
-  if (!LHS || !RHS)
-    return std::nullopt;
-
-  Value *NewUMin = IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_umin_u,
-                                              II.getType(), {Pg, LHS, RHS});
-  Value *NewBooleanUMin =
-      IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_umin_u, II.getType(),
-                                 {Pg, NewUMin, II.getOperand(2)});
-
-  return IC.replaceInstUsesWith(II, NewBooleanUMin);
+  return std::nullopt;
 }
 
 std::optional<Instruction *>
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
index c490595eb3eab..d9a5434c35269 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-cmpne.ll
@@ -572,106 +572,6 @@ define <vscale x 16 x i1> @dupq_b_idx(i64 %idx) #0 {
   ret <vscale x 16 x i1> %5
 }
 
-define <vscale x 16 x i8> @logical_bool_tree_i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
-; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
-; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
-; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
-; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP3]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
-;
-  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
-  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
-  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
-  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
-  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
-  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
-  ret <vscale x 16 x i8> %abcd.bool
-}
-
-define <vscale x 16 x i8> @logical_bool_tree_i8_multiuse_outer_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
-; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_multiuse_outer_umin(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
-; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
-; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
-; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP2]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i8> [[TMP2]])
-; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
-;
-  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
-  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
-  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
-  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
-  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
-  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
-  call void (...) @llvm.fake.use(<vscale x 16 x i8> %abcd)
-  ret <vscale x 16 x i8> %abcd.bool
-}
-
-define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_outer_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
-; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_outer_umin(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
-; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
-; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
-; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
-;
-  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
-  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
-  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
-  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
-  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
-  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
-  ret <vscale x 16 x i8> %abcd.bool
-}
-
-define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_inner_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
-; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_inactive_inner_umin(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
-; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
-; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
-; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
-;
-  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
-  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
-  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
-  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
-  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
-  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 1))
-  ret <vscale x 16 x i8> %abcd.bool
-}
-
-define <vscale x 16 x i8> @logical_bool_tree_i8_nonboolean_clamp(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d) #0 {
-; CHECK-LABEL: define <vscale x 16 x i8> @logical_bool_tree_i8_nonboolean_clamp(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]], <vscale x 16 x i8> [[C:%.*]], <vscale x 16 x i8> [[D:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[AB:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
-; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[CD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[C]], <vscale x 16 x i8> [[D]])
-; CHECK-NEXT:    [[CD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[CD]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[ABCD:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[AB_BOOL]], <vscale x 16 x i8> [[CD_BOOL]])
-; CHECK-NEXT:    [[ABCD_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[ABCD]], <vscale x 16 x i8> splat (i8 2))
-; CHECK-NEXT:    ret <vscale x 16 x i8> [[ABCD_BOOL]]
-;
-  %ab = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b)
-  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab, <vscale x 16 x i8> splat (i8 1))
-  %cd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %c, <vscale x 16 x i8> %d)
-  %cd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %cd, <vscale x 16 x i8> splat (i8 1))
-  %abcd = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %ab.bool, <vscale x 16 x i8> %cd.bool)
-  %abcd.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %abcd, <vscale x 16 x i8> splat (i8 2))
-  ret <vscale x 16 x i8> %abcd.bool
-}
-
 declare <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32)
 
 declare <vscale x 16 x i8> @llvm.vector.insert.nxv16i8.v16i8(<vscale x 16 x i8>, <16 x i8>, i64)
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
new file mode 100644
index 0000000000000..4b71eef26f30d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
@@ -0,0 +1,126 @@
+; 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"
+
+; umin(umin(A, 1), umin(B, 1)) -> umin(umin(A,B), 1)
+
+define <vscale x 16 x i8> @umin_with_logical_umin_operands(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP2]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  ret <vscale x 16 x i8> %3
+}
+
+define <vscale x 16 x i8> @umin_with_operand_1_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_operand_1_umin(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 2))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 2))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  ret <vscale x 16 x i8> %3
+}
+
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_1(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_1(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  ret <vscale x 16 x i8> %3
+}
+
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_2(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_2(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  ret <vscale x 16 x i8> %3
+}
+
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  ret <vscale x 16 x i8> %3
+}
+
+; umin(umin(A, 1), 1) -> umin(A, 1)
+
+define <vscale x 16 x i8> @logical_umin_with_logical_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_umin_with_logical_umin(
+; 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.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP1]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %2
+}
+
+define <vscale x 16 x i8> @logical_umin_with_logical_umin_predicate_mismatch(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_umin_with_logical_umin_predicate_mismatch(
+; 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.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP2]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %2
+}
+
+define <vscale x 16 x i8> @logical_umin_with_umin_1(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_umin_with_umin_1(
+; 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.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 40))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP2]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 40))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> splat (i8 1))
+  ret <vscale x 16 x i8> %2
+}
+
+define <vscale x 16 x i8> @logical_umin_with_umin_2(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @logical_umin_with_umin_2(
+; 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.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> splat (i8 40))
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP2]]
+;
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> splat (i8 40))
+  ret <vscale x 16 x i8> %2
+}
+
+attributes #0 = { "target-features"="+sve" }

>From d691e4e8959b861ae37ff804c4c172fc04531698 Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Fri, 31 Jul 2026 12:29:37 +0000
Subject: [PATCH 4/6] Fix oneuse location

---
 .../AArch64/AArch64TargetTransformInfo.cpp     |  9 ++++-----
 .../AArch64/sve-intrinsic-opts-umin.ll         | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index e23371c8873e5..b6509ced5a74e 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3372,11 +3372,10 @@ static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
   constexpr Intrinsic::ID UMinID = Intrinsic::aarch64_sve_umin_u;
   Value *A, *B;
   Value *Pg = II.getOperand(0);
-  if (match(II.getOperand(1),
-            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(A), m_One())) &&
-      match(II.getOperand(2),
-            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(B), m_One())) &&
-      II.hasOneUse()) {
+  if (match(II.getOperand(1), m_OneUse(m_Intrinsic<UMinID>(
+                                  m_Specific(Pg), m_Value(A), m_One()))) &&
+      match(II.getOperand(2), m_OneUse(m_Intrinsic<UMinID>(
+                                  m_Specific(Pg), m_Value(B), m_One())))) {
     Value *NewUMin =
         IC.Builder.CreateIntrinsic(UMinID, II.getType(), {Pg, A, B});
     Value *NewLogicalUMin = IC.Builder.CreateIntrinsic(
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
index 4b71eef26f30d..cf75e94ba2d36 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
@@ -74,6 +74,24 @@ define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(
   ret <vscale x 16 x i8> %3
 }
 
+define <vscale x 16 x i8> @umin_with_multiuse_logical_umin_operands(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_multiuse_logical_umin_operands(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[A_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[B_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[AB_BOOL:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A_BOOL]], <vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i8> [[A_BOOL]])
+; CHECK-NEXT:    call void (...) @llvm.fake.use(<vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT:    ret <vscale x 16 x i8> [[AB_BOOL]]
+;
+  %a.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %b.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %ab.bool = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+  call void (...) @llvm.fake.use(<vscale x 16 x i8> %a.bool)
+  call void (...) @llvm.fake.use(<vscale x 16 x i8> %b.bool)
+  ret <vscale x 16 x i8> %ab.bool
+}
+
 ; umin(umin(A, 1), 1) -> umin(A, 1)
 
 define <vscale x 16 x i8> @logical_umin_with_logical_umin(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {

>From 2527f9aff46b5f84e1c8fdbfa647ad771ecc1e4c Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Fri, 31 Jul 2026 12:43:50 +0000
Subject: [PATCH 5/6] Include predicate changes

---
 .../AArch64/sve-intrinsic-opts-umin.ll        | 32 +++++++++----------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
index cf75e94ba2d36..9b12b9a763778 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-umin.ll
@@ -32,44 +32,44 @@ define <vscale x 16 x i8> @umin_with_operand_1_umin(<vscale x 16 x i1> %pg, <vsc
   ret <vscale x 16 x i8> %3
 }
 
-define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_1(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_1(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
 ; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_1(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[OTHER_PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[OTHER_PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
 ; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
 ;
   %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
   %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
-  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
+  %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
   ret <vscale x 16 x i8> %3
 }
 
-define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_2(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_2(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
 ; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_2(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[OTHER_PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[OTHER_PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
 ; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
 ;
-  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
   %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
   %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
   ret <vscale x 16 x i8> %3
 }
 
-define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
+define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> %b) #0 {
 ; CHECK-LABEL: define <vscale x 16 x i8> @umin_with_logical_umin_operands_predicate_mismatch_3(
-; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[OTHER_PG:%.*]], <vscale x 16 x i8> [[A:%.*]], <vscale x 16 x i8> [[B:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
-; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[OTHER_PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    [[TMP3:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> [[TMP2]])
 ; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP3]]
 ;
   %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
-  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+  %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
   %3 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> %2)
   ret <vscale x 16 x i8> %3
 }
@@ -105,14 +105,14 @@ define <vscale x 16 x i8> @logical_umin_with_logical_umin(<vscale x 16 x i1> %pg
   ret <vscale x 16 x i8> %2
 }
 
-define <vscale x 16 x i8> @logical_umin_with_logical_umin_predicate_mismatch(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a) #0 {
+define <vscale x 16 x i8> @logical_umin_with_logical_umin_predicate_mismatch(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a) #0 {
 ; CHECK-LABEL: define <vscale x 16 x i8> @logical_umin_with_logical_umin_predicate_mismatch(
-; 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.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[OTHER_PG:%.*]], <vscale x 16 x i8> [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[OTHER_PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    [[TMP2:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[TMP1]], <vscale x 16 x i8> splat (i8 1))
 ; CHECK-NEXT:    ret <vscale x 16 x i8> [[TMP2]]
 ;
-  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
+  %1 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %other_pg, <vscale x 16 x i8> %a, <vscale x 16 x i8> splat (i8 1))
   %2 = call <vscale x 16 x i8> @llvm.aarch64.sve.umin.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %1, <vscale x 16 x i8> splat (i8 1))
   ret <vscale x 16 x i8> %2
 }

>From db66d1a345857903deeeb38ea3a2152b7411a81e Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Fri, 31 Jul 2026 14:06:03 +0000
Subject: [PATCH 6/6] Remove unused variables A and InnerUMin

---
 llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index b6509ced5a74e..7b1c603d4134c 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3384,12 +3384,10 @@ static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
   }
 
   // umin(umin(A, 1), 1) -> umin(A, 1)
-  Value *InnerUMin;
-  if (match(&II,
-            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(InnerUMin), m_One())) &&
-      match(InnerUMin,
-            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(A), m_One())))
-    return IC.replaceInstUsesWith(II, InnerUMin);
+  if (match(II.getOperand(1),
+            m_Intrinsic<UMinID>(m_Specific(Pg), m_Value(), m_One())) &&
+      match(II.getOperand(2), m_One()))
+    return IC.replaceInstUsesWith(II, II.getOperand(1));
 
   return std::nullopt;
 }



More information about the llvm-commits mailing list