[llvm] [AArch64][SVE] Optimize logical ops with convert.to.svbool. (PR #160408)
Vladimir Miloserdov via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 15 10:58:54 PDT 2026
https://github.com/miloserdow updated https://github.com/llvm/llvm-project/pull/160408
>From 353c565b2f138235c364fcd8cd46e7c2a1d75e29 Mon Sep 17 00:00:00 2001
From: Vladimir Miloserdov <milosvova at gmail.com>
Date: Tue, 23 Sep 2025 23:07:05 +0100
Subject: [PATCH 1/3] [AArch64][SVE] Optimize logical ops with
convert.to.svbool
When both operands of a logical operation (and/or/xor) are convert.to.svbool
from the same narrower type, unwrap to that type, simplify using simplifyBinOp,
and rewrap the result. This eliminates redundant instructions in cases like:
svand_z(svptrue_b8(), svpnext_b16(prev, pg), svptrue_b16());
Fixes #160279.
---
.../AArch64/AArch64TargetTransformInfo.cpp | 24 ++++
.../sve-intrinsic-and-or-with-all-true.ll | 123 ++++++++++++++++++
2 files changed, 147 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 2e28901cc5127..dedf77a030975 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -2004,6 +2004,30 @@ simplifySVEIntrinsicBinOp(InstCombiner &IC, IntrinsicInst &II,
return &II;
}
+ // If both operands are convert.to.svbool from the same narrower type, try to
+ // simplify the operation at that narrower type first.
+ if (isAllActivePredicate(Pg)) {
+ auto *ConvIntr1 = dyn_cast<IntrinsicInst>(Op1);
+ auto *ConvIntr2 = dyn_cast<IntrinsicInst>(Op2);
+ if (ConvIntr1 && ConvIntr2 &&
+ ConvIntr1->getIntrinsicID() ==
+ Intrinsic::aarch64_sve_convert_to_svbool &&
+ ConvIntr2->getIntrinsicID() ==
+ Intrinsic::aarch64_sve_convert_to_svbool) {
+ Value *NarrowOp1 = ConvIntr1->getArgOperand(0);
+ Value *NarrowOp2 = ConvIntr2->getArgOperand(0);
+ if (NarrowOp1->getType() == NarrowOp2->getType()) {
+ if (Value *SimplifiedNarrow =
+ simplifyBinOp(Opc, NarrowOp1, NarrowOp2, DL)) {
+ Value *NewConv = IC.Builder.CreateIntrinsic(
+ Intrinsic::aarch64_sve_convert_to_svbool,
+ {SimplifiedNarrow->getType()}, {SimplifiedNarrow});
+ return IC.replaceInstUsesWith(II, NewConv);
+ }
+ }
+ }
+ }
+
// Only active lanes matter when simplifying the operation.
Op1 = stripInactiveLanes(Op1, Pg);
Op2 = stripInactiveLanes(Op2, Pg);
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
new file mode 100644
index 0000000000000..f214fa5872b9e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --tool ../../llvm-build/bin/opt
+; RUN: opt -passes=instcombine -mtriple aarch64 -mattr=+sve -S < %s | FileCheck %s
+;
+; Test AArch64-specific InstCombine optimizations for SVE logical operations
+; with all-true predicates.
+; - a AND true = a
+; - a OR true = true
+
+declare <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
+declare <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
+declare <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
+declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1>)
+declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1>)
+declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1>)
+declare <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1>)
+declare <vscale x 8 x i1> @llvm.aarch64.sve.pnext.nxv8i1(<vscale x 8 x i1>, <vscale x 8 x i1>)
+
+define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: @test_and_convert_all_true_right_b16(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: @test_and_convert_all_true_left_b16(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_true, <vscale x 16 x i1> %conv_x)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: @test_or_convert_all_true_right_b16(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: @test_or_convert_all_true_left_b16(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_true, <vscale x 16 x i1> %conv_x)
+ ret <vscale x 16 x i1> %result
+}
+define <vscale x 16 x i1> @test_and_convert_all_true_b32(<vscale x 4 x i1> %x) {
+; CHECK-LABEL: @test_and_convert_all_true_b32(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[X:%.*]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_and_convert_all_true_b64(<vscale x 2 x i1> %x) {
+; CHECK-LABEL: @test_and_convert_all_true_b64(
+; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[X:%.*]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+; Negative test
+define <vscale x 16 x i1> @test_and_convert_different_granularities(<vscale x 8 x i1> %x, <vscale x 4 x i1> %y) {
+; CHECK-LABEL: @test_and_convert_different_granularities(
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[Y:%.*]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_Y]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_y = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %y)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_y)
+ ret <vscale x 16 x i1> %result
+}
+
+; Negative test
+define <vscale x 16 x i1> @test_and_convert_non_all_true_predicate(<vscale x 16 x i1> %pred, <vscale x 8 x i1> %x) {
+; CHECK-LABEL: @test_and_convert_non_all_true_predicate(
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-NEXT: [[CONV_TRUE:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[PRED:%.*]], <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_TRUE]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %pred, <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+; Negative test
+define <vscale x 16 x i1> @test_and_convert_no_all_true(<vscale x 8 x i1> %x, <vscale x 8 x i1> %y) {
+; CHECK-LABEL: @test_and_convert_no_all_true(
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[Y:%.*]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_Y]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_y = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %y)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_y)
+ ret <vscale x 16 x i1> %result
+}
>From 566a67d60686d56f03b47fa39bea5a2df20bc223 Mon Sep 17 00:00:00 2001
From: Vladimir Miloserdov <milosvova at gmail.com>
Date: Sat, 15 Aug 2026 18:34:02 +0100
Subject: [PATCH 2/3] Address review comments: use PatternMatch, clean up tests
- Use m_Intrinsic pattern matching instead of manual dyn_cast checks.
- Guard against undef results from simplifyBinOp, matching the wide path.
- Remove unneeded intrinsic declarations from the test file.
- Regenerate CHECK lines with --opt-binary so no local tool path is embedded.
- Re-add the reproducer from the original issue as a regression test.
---
.../AArch64/AArch64TargetTransformInfo.cpp | 36 +++----
.../sve-intrinsic-and-or-with-all-true.ll | 102 +++++++++++-------
2 files changed, 77 insertions(+), 61 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index dedf77a030975..a424e7f947de9 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -2005,26 +2005,22 @@ simplifySVEIntrinsicBinOp(InstCombiner &IC, IntrinsicInst &II,
}
// If both operands are convert.to.svbool from the same narrower type, try to
- // simplify the operation at that narrower type first.
- if (isAllActivePredicate(Pg)) {
- auto *ConvIntr1 = dyn_cast<IntrinsicInst>(Op1);
- auto *ConvIntr2 = dyn_cast<IntrinsicInst>(Op2);
- if (ConvIntr1 && ConvIntr2 &&
- ConvIntr1->getIntrinsicID() ==
- Intrinsic::aarch64_sve_convert_to_svbool &&
- ConvIntr2->getIntrinsicID() ==
- Intrinsic::aarch64_sve_convert_to_svbool) {
- Value *NarrowOp1 = ConvIntr1->getArgOperand(0);
- Value *NarrowOp2 = ConvIntr2->getArgOperand(0);
- if (NarrowOp1->getType() == NarrowOp2->getType()) {
- if (Value *SimplifiedNarrow =
- simplifyBinOp(Opc, NarrowOp1, NarrowOp2, DL)) {
- Value *NewConv = IC.Builder.CreateIntrinsic(
- Intrinsic::aarch64_sve_convert_to_svbool,
- {SimplifiedNarrow->getType()}, {SimplifiedNarrow});
- return IC.replaceInstUsesWith(II, NewConv);
- }
- }
+ // simplify the operation at that narrower type first. This is valid because
+ // convert.to.svbool zeros the lanes not represented by the narrower type,
+ // and so both operands agree on those lanes, whose result is also zero.
+ Value *NarrowOp1, *NarrowOp2;
+ if (isAllActivePredicate(Pg) &&
+ match(Op1, m_Intrinsic<Intrinsic::aarch64_sve_convert_to_svbool>(
+ m_Value(NarrowOp1))) &&
+ match(Op2, m_Intrinsic<Intrinsic::aarch64_sve_convert_to_svbool>(
+ m_Value(NarrowOp2))) &&
+ NarrowOp1->getType() == NarrowOp2->getType()) {
+ Value *SimpleNarrow = simplifyBinOp(Opc, NarrowOp1, NarrowOp2, DL);
+ if (SimpleNarrow && !isa<UndefValue>(SimpleNarrow)) {
+ Value *NewConv =
+ IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_convert_to_svbool,
+ {SimpleNarrow->getType()}, {SimpleNarrow});
+ return IC.replaceInstUsesWith(II, NewConv);
}
}
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
index f214fa5872b9e..3c144fa2d204c 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
@@ -1,24 +1,15 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --tool ../../llvm-build/bin/opt
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=instcombine -mtriple aarch64 -mattr=+sve -S < %s | FileCheck %s
;
-; Test AArch64-specific InstCombine optimizations for SVE logical operations
-; with all-true predicates.
-; - a AND true = a
-; - a OR true = true
-
-declare <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
-declare <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
-declare <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1>, <vscale x 16 x i1>, <vscale x 16 x i1>)
-declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1>)
-declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1>)
-declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1>)
-declare <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1>)
-declare <vscale x 8 x i1> @llvm.aarch64.sve.pnext.nxv8i1(<vscale x 8 x i1>, <vscale x 8 x i1>)
+; Check that logical operations with an all-active predicate, whose operands
+; are both convert.to.svbool from the same narrower type, are simplified at
+; the narrower type where constant operands are visible.
define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
-; CHECK-LABEL: @test_and_convert_all_true_right_b16(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
@@ -27,9 +18,10 @@ define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(<vscale x 8 x i1>
}
define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
-; CHECK-LABEL: @test_and_convert_all_true_left_b16(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
@@ -38,9 +30,10 @@ define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(<vscale x 8 x i1>
}
define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
-; CHECK-LABEL: @test_or_convert_all_true_right_b16(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
@@ -49,19 +42,22 @@ define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(<vscale x 8 x i1>
}
define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
-; CHECK-LABEL: @test_or_convert_all_true_left_b16(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
%result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_true, <vscale x 16 x i1> %conv_x)
ret <vscale x 16 x i1> %result
}
+
define <vscale x 16 x i1> @test_and_convert_all_true_b32(<vscale x 4 x i1> %x) {
-; CHECK-LABEL: @test_and_convert_all_true_b32(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[X:%.*]])
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_b32(
+; CHECK-SAME: <vscale x 4 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> splat (i1 true))
@@ -70,9 +66,10 @@ define <vscale x 16 x i1> @test_and_convert_all_true_b32(<vscale x 4 x i1> %x) {
}
define <vscale x 16 x i1> @test_and_convert_all_true_b64(<vscale x 2 x i1> %x) {
-; CHECK-LABEL: @test_and_convert_all_true_b64(
-; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[X:%.*]])
-; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP1]]
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_b64(
+; CHECK-SAME: <vscale x 2 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %x)
%conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> splat (i1 true))
@@ -80,11 +77,32 @@ define <vscale x 16 x i1> @test_and_convert_all_true_b64(<vscale x 2 x i1> %x) {
ret <vscale x 16 x i1> %result
}
+; Original reproducer from https://github.com/llvm/llvm-project/issues/160279:
+; svand_z(svptrue_b8(), svpnext_b16(prev, pg), svptrue_b16())
+define <vscale x 16 x i1> @test_and_pnext_ptrue_b16(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %prev) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_pnext_ptrue_b16(
+; CHECK-SAME: <vscale x 16 x i1> [[PG:%.*]], <vscale x 16 x i1> [[PREV:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[TMP1:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> [[PREV]])
+; CHECK-NEXT: [[TMP2:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> [[PG]])
+; CHECK-NEXT: [[TMP3:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.pnext.nxv8i1(<vscale x 8 x i1> [[TMP1]], <vscale x 8 x i1> [[TMP2]])
+; CHECK-NEXT: [[TMP4:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[TMP3]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP4]]
+;
+ %1 = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> %prev)
+ %2 = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> %pg)
+ %3 = tail call <vscale x 8 x i1> @llvm.aarch64.sve.pnext.nxv8i1(<vscale x 8 x i1> %1, <vscale x 8 x i1> %2)
+ %4 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %3)
+ %5 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %6 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %4, <vscale x 16 x i1> %5)
+ ret <vscale x 16 x i1> %6
+}
+
; Negative test
define <vscale x 16 x i1> @test_and_convert_different_granularities(<vscale x 8 x i1> %x, <vscale x 4 x i1> %y) {
-; CHECK-LABEL: @test_and_convert_different_granularities(
-; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
-; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[Y:%.*]])
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_different_granularities(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]], <vscale x 4 x i1> [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[Y]])
; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_Y]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
@@ -96,10 +114,11 @@ define <vscale x 16 x i1> @test_and_convert_different_granularities(<vscale x 8
; Negative test
define <vscale x 16 x i1> @test_and_convert_non_all_true_predicate(<vscale x 16 x i1> %pred, <vscale x 8 x i1> %x) {
-; CHECK-LABEL: @test_and_convert_non_all_true_predicate(
-; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_non_all_true_predicate(
+; CHECK-SAME: <vscale x 16 x i1> [[PRED:%.*]], <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
; CHECK-NEXT: [[CONV_TRUE:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
-; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[PRED:%.*]], <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_TRUE]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[PRED]], <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_TRUE]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
@@ -110,9 +129,10 @@ define <vscale x 16 x i1> @test_and_convert_non_all_true_predicate(<vscale x 16
; Negative test
define <vscale x 16 x i1> @test_and_convert_no_all_true(<vscale x 8 x i1> %x, <vscale x 8 x i1> %y) {
-; CHECK-LABEL: @test_and_convert_no_all_true(
-; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X:%.*]])
-; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[Y:%.*]])
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_no_all_true(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]], <vscale x 8 x i1> [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[CONV_Y:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[Y]])
; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_Y]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
>From b71517a5417596c815217e4d1a51e930e3234e65 Mon Sep 17 00:00:00 2001
From: Vladimir Miloserdov <milosvova at gmail.com>
Date: Sat, 15 Aug 2026 18:56:26 +0100
Subject: [PATCH 3/3] Run the narrow simplification only when the wide one
fails
Self-review follow-ups:
- Try simplifyBinOp at the original width first, so folds that produce wide
constants (e.g. eor_z(pg, a, a) -> zeroinitializer) are not shadowed by a
rewrapped narrow result.
- Fold a narrow zero result directly to the wide zero constant.
- Reuse the existing convert.to.svbool when the simplified value is one of
the narrow operands, rather than materializing a duplicate call.
- Expand test coverage: eor_z cases, zero operands, multi-use operands,
the undef/poison guard, and a widened-narrow-ptrue governing predicate.
---
.../AArch64/AArch64TargetTransformInfo.cpp | 42 +++---
.../sve-intrinsic-and-or-with-all-true.ll | 127 +++++++++++++++++-
2 files changed, 147 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index a424e7f947de9..e93f5b071687b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -2004,19 +2004,41 @@ simplifySVEIntrinsicBinOp(InstCombiner &IC, IntrinsicInst &II,
return &II;
}
- // If both operands are convert.to.svbool from the same narrower type, try to
- // simplify the operation at that narrower type first. This is valid because
- // convert.to.svbool zeros the lanes not represented by the narrower type,
- // and so both operands agree on those lanes, whose result is also zero.
+ // Only active lanes matter when simplifying the operation.
+ Op1 = stripInactiveLanes(Op1, Pg);
+ Op2 = stripInactiveLanes(Op2, Pg);
+
+ Value *SimpleII;
+ if (auto FII = dyn_cast<FPMathOperator>(&II))
+ SimpleII = simplifyBinOp(Opc, Op1, Op2, FII->getFastMathFlags(), DL);
+ else
+ SimpleII = simplifyBinOp(Opc, Op1, Op2, DL);
+
+ // If the operation cannot be simplified at its current width, but both
+ // operands are convert.to.svbool from the same narrower type, try to
+ // simplify it at that narrower type, where constant operands may be
+ // visible. This is valid because convert.to.svbool zeros the lanes not
+ // represented by the narrower type, and "zero op zero == zero" for the
+ // logical operations that can reach here, so those lanes of the result
+ // are zero either way.
Value *NarrowOp1, *NarrowOp2;
- if (isAllActivePredicate(Pg) &&
+ if (!SimpleII && isAllActivePredicate(Pg) &&
match(Op1, m_Intrinsic<Intrinsic::aarch64_sve_convert_to_svbool>(
m_Value(NarrowOp1))) &&
match(Op2, m_Intrinsic<Intrinsic::aarch64_sve_convert_to_svbool>(
m_Value(NarrowOp2))) &&
NarrowOp1->getType() == NarrowOp2->getType()) {
Value *SimpleNarrow = simplifyBinOp(Opc, NarrowOp1, NarrowOp2, DL);
+ // As below, simplifications to an undefined result must be ignored.
if (SimpleNarrow && !isa<UndefValue>(SimpleNarrow)) {
+ // A zero result remains zero after widening.
+ if (match(SimpleNarrow, m_ZeroInt()))
+ return IC.replaceInstUsesWith(II, Constant::getNullValue(II.getType()));
+ // Reuse an existing conversion of the simplified value if possible.
+ if (SimpleNarrow == NarrowOp1)
+ return IC.replaceInstUsesWith(II, Op1);
+ if (SimpleNarrow == NarrowOp2)
+ return IC.replaceInstUsesWith(II, Op2);
Value *NewConv =
IC.Builder.CreateIntrinsic(Intrinsic::aarch64_sve_convert_to_svbool,
{SimpleNarrow->getType()}, {SimpleNarrow});
@@ -2024,16 +2046,6 @@ simplifySVEIntrinsicBinOp(InstCombiner &IC, IntrinsicInst &II,
}
}
- // Only active lanes matter when simplifying the operation.
- Op1 = stripInactiveLanes(Op1, Pg);
- Op2 = stripInactiveLanes(Op2, Pg);
-
- Value *SimpleII;
- if (auto FII = dyn_cast<FPMathOperator>(&II))
- SimpleII = simplifyBinOp(Opc, Op1, Op2, FII->getFastMathFlags(), DL);
- else
- SimpleII = simplifyBinOp(Opc, Op1, Op2, DL);
-
// An SVE intrinsic's result is always defined. However, this is not the case
// for its equivalent IR instruction (e.g. when shifting by an amount more
// than the data's bitwidth). Simplifications to an undefined result must be
diff --git a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
index 3c144fa2d204c..b4de96733e9b7 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/sve-intrinsic-and-or-with-all-true.ll
@@ -8,7 +8,7 @@
define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(
; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
@@ -20,7 +20,7 @@ define <vscale x 16 x i1> @test_and_convert_all_true_right_b16(<vscale x 8 x i1>
define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(
; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
@@ -32,7 +32,7 @@ define <vscale x 16 x i1> @test_and_convert_all_true_left_b16(<vscale x 8 x i1>
define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(<vscale x 8 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(
; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
@@ -44,7 +44,7 @@ define <vscale x 16 x i1> @test_or_convert_all_true_right_b16(<vscale x 8 x i1>
define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(<vscale x 8 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(
; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
@@ -56,7 +56,7 @@ define <vscale x 16 x i1> @test_or_convert_all_true_left_b16(<vscale x 8 x i1> %
define <vscale x 16 x i1> @test_and_convert_all_true_b32(<vscale x 4 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_b32(
; CHECK-SAME: <vscale x 4 x i1> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[X]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[X]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %x)
@@ -68,7 +68,7 @@ define <vscale x 16 x i1> @test_and_convert_all_true_b32(<vscale x 4 x i1> %x) {
define <vscale x 16 x i1> @test_and_convert_all_true_b64(<vscale x 2 x i1> %x) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_all_true_b64(
; CHECK-SAME: <vscale x 2 x i1> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RESULT:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[X]])
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[X]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
;
%conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %x)
@@ -85,7 +85,7 @@ define <vscale x 16 x i1> @test_and_pnext_ptrue_b16(<vscale x 16 x i1> %pg, <vsc
; CHECK-NEXT: [[TMP1:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> [[PREV]])
; CHECK-NEXT: [[TMP2:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> [[PG]])
; CHECK-NEXT: [[TMP3:%.*]] = tail call <vscale x 8 x i1> @llvm.aarch64.sve.pnext.nxv8i1(<vscale x 8 x i1> [[TMP1]], <vscale x 8 x i1> [[TMP2]])
-; CHECK-NEXT: [[TMP4:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[TMP3]])
+; CHECK-NEXT: [[TMP4:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[TMP3]])
; CHECK-NEXT: ret <vscale x 16 x i1> [[TMP4]]
;
%1 = tail call <vscale x 8 x i1> @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> %prev)
@@ -97,6 +97,119 @@ define <vscale x 16 x i1> @test_and_pnext_ptrue_b16(<vscale x 16 x i1> %pg, <vsc
ret <vscale x 16 x i1> %6
}
+; The wide operation already simplifies (x ^ x -> 0); make sure the result is
+; the wide zero constant rather than a rewrapped narrow zero.
+define <vscale x 16 x i1> @test_eor_convert_same(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_eor_convert_same(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: ret <vscale x 16 x i1> zeroinitializer
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_x)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_eor_convert_zero(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_eor_convert_zero(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[CONV_X]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_zero = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> zeroinitializer)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_zero)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_and_convert_zero(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_zero(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: ret <vscale x 16 x i1> zeroinitializer
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_zero = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> zeroinitializer)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_zero)
+ ret <vscale x 16 x i1> %result
+}
+
+define <vscale x 16 x i1> @test_orr_convert_zero(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_orr_convert_zero(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[CONV_X]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_zero = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> zeroinitializer)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.orr.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_zero)
+ ret <vscale x 16 x i1> %result
+}
+
+; The simplification still applies when the reused operand has other users.
+define { <vscale x 16 x i1>, <vscale x 16 x i1> } @test_and_convert_multiuse(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define { <vscale x 16 x i1>, <vscale x 16 x i1> } @test_and_convert_multiuse(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[RET0:%.*]] = insertvalue { <vscale x 16 x i1>, <vscale x 16 x i1> } poison, <vscale x 16 x i1> [[CONV_X]], 0
+; CHECK-NEXT: [[RET1:%.*]] = insertvalue { <vscale x 16 x i1>, <vscale x 16 x i1> } [[RET0]], <vscale x 16 x i1> [[CONV_X]], 1
+; CHECK-NEXT: ret { <vscale x 16 x i1>, <vscale x 16 x i1> } [[RET1]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ %ret0 = insertvalue { <vscale x 16 x i1>, <vscale x 16 x i1> } poison, <vscale x 16 x i1> %result, 0
+ %ret1 = insertvalue { <vscale x 16 x i1>, <vscale x 16 x i1> } %ret0, <vscale x 16 x i1> %conv_x, 1
+ ret { <vscale x 16 x i1>, <vscale x 16 x i1> } %ret1
+}
+
+; Negative test: eor with an all-true operand is a NOT of the other operand,
+; which is not a simplification.
+define <vscale x 16 x i1> @test_eor_convert_all_true(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_eor_convert_all_true(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[CONV_TRUE:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_TRUE]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.eor.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
+; Negative test: simplifications to an undefined result must be ignored.
+define <vscale x 16 x i1> @test_and_convert_poison(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_poison(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[CONV_POISON:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> poison)
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_POISON]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_poison = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> poison)
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> splat (i1 true), <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_poison)
+ ret <vscale x 16 x i1> %result
+}
+
+; Negative test: a widened narrow all-true predicate is not an all-active
+; predicate (its unrepresented lanes are zero).
+define <vscale x 16 x i1> @test_and_pg_widened_true(<vscale x 8 x i1> %x) {
+; CHECK-LABEL: define <vscale x 16 x i1> @test_and_pg_widened_true(
+; CHECK-SAME: <vscale x 8 x i1> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[PG:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[CONV_X:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[X]])
+; CHECK-NEXT: [[CONV_TRUE:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+; CHECK-NEXT: [[RESULT:%.*]] = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> [[PG]], <vscale x 16 x i1> [[CONV_X]], <vscale x 16 x i1> [[CONV_TRUE]])
+; CHECK-NEXT: ret <vscale x 16 x i1> [[RESULT]]
+;
+ %pg = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %conv_x = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %x)
+ %conv_true = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> splat (i1 true))
+ %result = tail call <vscale x 16 x i1> @llvm.aarch64.sve.and.z.nxv16i1(<vscale x 16 x i1> %pg, <vscale x 16 x i1> %conv_x, <vscale x 16 x i1> %conv_true)
+ ret <vscale x 16 x i1> %result
+}
+
; Negative test
define <vscale x 16 x i1> @test_and_convert_different_granularities(<vscale x 8 x i1> %x, <vscale x 4 x i1> %y) {
; CHECK-LABEL: define <vscale x 16 x i1> @test_and_convert_different_granularities(
More information about the llvm-commits
mailing list