[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