[llvm] [InstCombine][AArch64] Combine ORRs with logical umin ops (PR #213651)
Matthew Devereau via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 3 04:27:19 PDT 2026
https://github.com/MDevereau created https://github.com/llvm/llvm-project/pull/213651
Combine:
orr(umin(A, 1), umin(B, 1)) -> umin(orr(A, B), 1)
To remove a redundant UMin. This pattern has been observed with reduction chains of multiple ORRs of UMin(x, 1), where only one final UMin(x, 1) is necessary for truncation.
>From 10c246c3aeda948936bff5b674a4ac72ade99bb2 Mon Sep 17 00:00:00 2001
From: Matt Devereau <matthew.devereau at arm.com>
Date: Mon, 3 Aug 2026 11:15:24 +0000
Subject: [PATCH] [InstCombine][AArch64] Combine ORRs with logical umin ops
Combine:
orr(umin(A, 1), umin(B, 1)) -> umin(orr(A, B), 1)
To remove a redundant UMin. This pattern has been observed with
reduction chains of multiple ORRs of UMin(x, 1), where only one final
UMin(x, 1) is necessary for truncation.
---
.../AArch64/AArch64TargetTransformInfo.cpp | 22 ++++
.../AArch64/sve-intrinsic-opts-orr.ll | 107 ++++++++++++++++++
2 files changed, 129 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-orr.ll
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 7b1c603d4134c..e57a648f67fe7 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -3392,6 +3392,26 @@ static std::optional<Instruction *> instCombineSVEUMin(InstCombiner &IC,
return std::nullopt;
}
+static std::optional<Instruction *> instCombineSVEOrr(InstCombiner &IC,
+ IntrinsicInst &II) {
+ // orr(umin(A, 1), umin(B, 1)) -> umin(orr(A, B), 1)
+ constexpr Intrinsic::ID UMinID = Intrinsic::aarch64_sve_umin_u;
+ Value *Pg = II.getOperand(0);
+
+ Value *A, *B;
+ 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()))))
+ return std::nullopt;
+
+ Value *NewOrr = IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_orr_u,
+ II.getType(), {Pg, A, B});
+ Value *NewUMin = IC.Builder.CreateIntrinsic(
+ UMinID, II.getType(), {Pg, NewOrr, ConstantInt::get(II.getType(), 1)});
+ return IC.replaceInstUsesWith(II, NewUMin);
+}
+
std::optional<Instruction *>
AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
IntrinsicInst &II) const {
@@ -3513,6 +3533,8 @@ AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
return instCombineInStreamingMode(IC, II);
case Intrinsic::aarch64_sve_umin_u:
return instCombineSVEUMin(IC, II);
+ case Intrinsic::aarch64_sve_orr_u:
+ return instCombineSVEOrr(IC, II);
}
return std::nullopt;
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-orr.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-orr.ll
new file mode 100644
index 0000000000000..53a62518e21ec
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-opts-orr.ll
@@ -0,0 +1,107 @@
+; 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 i8> @logical_or_reduction(<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> @logical_or_reduction(
+; 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.orr.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A]], <vscale x 16 x i8> [[B]])
+; CHECK-NEXT: [[TMP14:%.*]] = 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> [[TMP14]]
+;
+ %a.bool = tail 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 = tail 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 = tail call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+ ret <vscale x 16 x i8> %ab.bool
+}
+
+define <vscale x 16 x i8> @no_combine_lhs_not_one(<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> @no_combine_lhs_not_one(
+; 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 2))
+; 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: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A_BOOL]], <vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[RES]]
+;
+ %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 2))
+ %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))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+ ret <vscale x 16 x i8> %res
+}
+
+define <vscale x 16 x i8> @no_combine_rhs_not_one(<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> @no_combine_rhs_not_one(
+; 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 2))
+; CHECK-NEXT: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A_BOOL]], <vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[RES]]
+;
+ %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 2))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+ ret <vscale x 16 x i8> %res
+}
+
+define <vscale x 16 x i8> @no_combine_lhs_predicate_mismatch(<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> @no_combine_lhs_predicate_mismatch(
+; 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: [[A_BOOL:%.*]] = 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: [[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: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A_BOOL]], <vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[RES]]
+;
+ %a.bool = 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))
+ %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))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+ ret <vscale x 16 x i8> %res
+}
+
+define <vscale x 16 x i8> @no_combine_rhs_predicate_mismatch(<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> @no_combine_rhs_predicate_mismatch(
+; 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: [[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> [[OTHER_PG]], <vscale x 16 x i8> [[B]], <vscale x 16 x i8> splat (i8 1))
+; CHECK-NEXT: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> [[PG]], <vscale x 16 x i8> [[A_BOOL]], <vscale x 16 x i8> [[B_BOOL]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[RES]]
+;
+ %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> %other.pg, <vscale x 16 x i8> %b, <vscale x 16 x i8> splat (i8 1))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.u.nxv16i8(<vscale x 16 x i1> %pg, <vscale x 16 x i8> %a.bool, <vscale x 16 x i8> %b.bool)
+ ret <vscale x 16 x i8> %res
+}
+
+define <vscale x 16 x i8> @combine_lhs_umin_multiple_uses(<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> @combine_lhs_umin_multiple_uses(
+; 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: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.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: ret <vscale x 16 x i8> [[RES]]
+;
+ %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))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.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)
+ ret <vscale x 16 x i8> %res
+}
+
+define <vscale x 16 x i8> @combine_rhs_umin_multiple_uses(<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> @combine_rhs_umin_multiple_uses(
+; 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: [[RES:%.*]] = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.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> [[B_BOOL]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[RES]]
+;
+ %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))
+ %res = call <vscale x 16 x i8> @llvm.aarch64.sve.orr.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> %b.bool)
+ ret <vscale x 16 x i8> %res
+}
+
+attributes #0 = { "target-features"="+sve" }
More information about the llvm-commits
mailing list