[llvm] [VPlan] Introduce m_c_Logical(And|Or) (PR #180048)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 12 04:45:10 PST 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/180048

>From 1b9b03eae8b875f85413e947a42a45da9e7b271b Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 11 Feb 2026 21:23:29 +0000
Subject: [PATCH 1/4] [LV] Pre-commit constant-fold-commutative-and test

---
 .../constant-fold-commutative-and.ll          | 84 +++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll

diff --git a/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll b/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll
new file mode 100644
index 0000000000000..1a5600b21ea18
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6
+; RUN: opt -passes=loop-vectorize -force-vector-width=2 \
+; RUN:   -prefer-predicate-over-epilogue=predicate-else-scalar-epilogue -S %s \
+; RUN:   | FileCheck %s
+
+; Test that we constant fold logical and with swapped operand order.
+
+define void @constant_fold_commutative_and(ptr %ptr.n, ptr noalias %p, i1 %cond) {
+; CHECK-LABEL: define void @constant_fold_commutative_and(
+; CHECK-SAME: ptr [[PTR_N:%.*]], ptr noalias [[P:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br label %[[VECTOR_PH:.*]]
+; CHECK:       [[VECTOR_PH]]:
+; CHECK-NEXT:    [[BROADCAST_SPLATINSERT:%.*]] = insertelement <2 x i1> poison, i1 [[COND]], i64 0
+; CHECK-NEXT:    [[BROADCAST_SPLAT:%.*]] = shufflevector <2 x i1> [[BROADCAST_SPLATINSERT]], <2 x i1> poison, <2 x i32> zeroinitializer
+; CHECK-NEXT:    [[TMP0:%.*]] = xor <2 x i1> [[BROADCAST_SPLAT]], splat (i1 true)
+; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
+; CHECK:       [[VECTOR_BODY]]:
+; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[PRED_STORE_CONTINUE5:.*]] ]
+; CHECK-NEXT:    [[VEC_IND:%.*]] = phi <2 x i8> [ <i8 0, i8 1>, %[[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], %[[PRED_STORE_CONTINUE5]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ule <2 x i8> [[VEC_IND]], splat (i8 16)
+; CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[PTR_N]], align 4
+; CHECK-NEXT:    [[BROADCAST_SPLATINSERT1:%.*]] = insertelement <2 x i64> poison, i64 [[TMP2]], i64 0
+; CHECK-NEXT:    [[BROADCAST_SPLAT2:%.*]] = shufflevector <2 x i64> [[BROADCAST_SPLATINSERT1]], <2 x i64> poison, <2 x i32> zeroinitializer
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ult <2 x i64> [[BROADCAST_SPLAT2]], splat (i64 4)
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp ult <2 x i64> [[BROADCAST_SPLAT2]], splat (i64 7)
+; CHECK-NEXT:    [[TMP5:%.*]] = select <2 x i1> [[TMP0]], <2 x i1> [[TMP3]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[TMP12:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> splat (i1 true), <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[PREDPHI:%.*]] = select <2 x i1> [[TMP5]], <2 x i1> splat (i1 true), <2 x i1> [[TMP4]]
+; CHECK-NEXT:    [[PREDPHI3:%.*]] = select i1 [[COND]], <2 x i1> zeroinitializer, <2 x i1> [[PREDPHI]]
+; CHECK-NEXT:    [[TMP6:%.*]] = extractelement <2 x i1> [[TMP12]], i32 0
+; CHECK-NEXT:    br i1 [[TMP6]], label %[[PRED_STORE_IF:.*]], label %[[PRED_STORE_CONTINUE:.*]]
+; CHECK:       [[PRED_STORE_IF]]:
+; CHECK-NEXT:    [[TMP7:%.*]] = add i64 [[INDEX]], 0
+; CHECK-NEXT:    [[TMP8:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP7]]
+; CHECK-NEXT:    [[TMP9:%.*]] = extractelement <2 x i1> [[PREDPHI3]], i32 0
+; CHECK-NEXT:    store i1 [[TMP9]], ptr [[TMP8]], align 1
+; CHECK-NEXT:    br label %[[PRED_STORE_CONTINUE]]
+; CHECK:       [[PRED_STORE_CONTINUE]]:
+; CHECK-NEXT:    [[TMP10:%.*]] = extractelement <2 x i1> [[TMP12]], i32 1
+; CHECK-NEXT:    br i1 [[TMP10]], label %[[PRED_STORE_IF4:.*]], label %[[PRED_STORE_CONTINUE5]]
+; CHECK:       [[PRED_STORE_IF4]]:
+; CHECK-NEXT:    [[TMP11:%.*]] = add i64 [[INDEX]], 1
+; CHECK-NEXT:    [[TMP20:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP11]]
+; CHECK-NEXT:    [[TMP21:%.*]] = extractelement <2 x i1> [[PREDPHI3]], i32 1
+; CHECK-NEXT:    store i1 [[TMP21]], ptr [[TMP20]], align 1
+; CHECK-NEXT:    br label %[[PRED_STORE_CONTINUE5]]
+; CHECK:       [[PRED_STORE_CONTINUE5]]:
+; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
+; CHECK-NEXT:    [[VEC_IND_NEXT]] = add <2 x i8> [[VEC_IND]], splat (i8 2)
+; CHECK-NEXT:    [[TMP15:%.*]] = icmp eq i64 [[INDEX_NEXT]], 18
+; CHECK-NEXT:    br i1 [[TMP15]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK:       [[MIDDLE_BLOCK]]:
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %ph
+
+ph:
+  %iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
+  %n = load i64, ptr %ptr.n
+  br i1 %cond, label %latch, label %pred.1
+
+pred.1:
+  %cmp = icmp ult i64 %n, 4
+  br i1 %cmp, label %latch, label %pred.2
+
+pred.2:
+  %cmp.2 = icmp ult i64 %n, 7
+  br label %latch
+
+latch:
+  %merge.val = phi i1 [ 0, %ph ], [ 1, %pred.1 ], [ %cmp.2, %pred.2 ]
+  %gep = getelementptr i8, ptr %p, i64 %iv
+  store i1 %merge.val, ptr %gep
+  %iv.next = add i64 %iv, 1
+  %ec = icmp eq i64 %iv, 16
+  br i1 %ec, label %exit, label %ph
+
+exit:
+  ret void
+}

>From b1465b7966b3597030fa6c1f211c87d32bfd28a2 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Thu, 5 Feb 2026 21:41:41 +0000
Subject: [PATCH 2/4] [VPlan] Introduce m_c_Logical(And|Or)

---
 .../Transforms/Vectorize/VPlanPatternMatch.h  | 48 +++++++++++++++++--
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  5 +-
 .../Transforms/Vectorize/VPlanTransforms.cpp  |  8 ++--
 3 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
index c0b736de1bc51..9dc14b635f3eb 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
@@ -364,12 +364,23 @@ template <unsigned Opcode, typename... OpTys>
 using VPInstruction_match = Recipe_match<std::tuple<OpTys...>, Opcode,
                                          /*Commutative*/ false, VPInstruction>;
 
+template <unsigned Opcode, typename... OpTys>
+using VPInstruction_commutative_match =
+    Recipe_match<std::tuple<OpTys...>, Opcode,
+                 /*Commutative*/ true, VPInstruction>;
+
 template <unsigned Opcode, typename... OpTys>
 inline VPInstruction_match<Opcode, OpTys...>
 m_VPInstruction(const OpTys &...Ops) {
   return VPInstruction_match<Opcode, OpTys...>(Ops...);
 }
 
+template <unsigned Opcode, typename Op0_t, typename Op1_t>
+inline VPInstruction_commutative_match<Opcode, Op0_t, Op1_t>
+m_c_VPInstruction(const Op0_t &Op0, const Op1_t &Op1) {
+  return VPInstruction_commutative_match<Opcode, Op0_t, Op1_t>(Op0, Op1);
+}
+
 /// BuildVector is matches only its opcode, w/o matching its operands as the
 /// number of operands is not fixed.
 inline VPInstruction_match<VPInstruction::BuildVector> m_BuildVector() {
@@ -791,14 +802,27 @@ m_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) {
 }
 
 template <typename Op0_t>
-inline match_combine_or<VPInstruction_match<VPInstruction::Not, Op0_t>,
-                        AllRecipe_commutative_match<
-                            Instruction::Xor, int_pred_ty<is_all_ones>, Op0_t>>
-m_Not(const Op0_t &Op0) {
+using Not_match =
+    match_combine_or<VPInstruction_match<VPInstruction::Not, Op0_t>,
+                     AllRecipe_commutative_match<
+                         Instruction::Xor, int_pred_ty<is_all_ones>, Op0_t>>;
+
+template <typename Op0_t> inline Not_match<Op0_t> m_Not(const Op0_t &Op0) {
   return m_CombineOr(m_VPInstruction<VPInstruction::Not>(Op0),
                      m_c_Binary<Instruction::Xor>(m_AllOnes(), Op0));
 }
 
+template <typename Op0_t, typename Op1_t, typename Op2_t>
+using Select_commutative_match = match_combine_or<
+    AllRecipe_match<Instruction::Select, Op0_t, Op1_t, Op2_t>,
+    AllRecipe_match<Instruction::Select, Not_match<Op0_t>, Op2_t, Op1_t>>;
+
+template <typename Op0_t, typename Op1_t, typename Op2_t>
+inline Select_commutative_match<Op0_t, Op1_t, Op2_t>
+m_c_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) {
+  return m_CombineOr(m_Select(Op0, Op1, Op2), m_Select(m_Not(Op0), Op2, Op1));
+}
+
 template <typename Op0_t, typename Op1_t>
 inline match_combine_or<
     VPInstruction_match<VPInstruction::LogicalAnd, Op0_t, Op1_t>,
@@ -809,12 +833,28 @@ m_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) {
       m_Select(Op0, Op1, m_False()));
 }
 
+template <typename Op0_t, typename Op1_t>
+inline match_combine_or<
+    VPInstruction_commutative_match<VPInstruction::LogicalAnd, Op0_t, Op1_t>,
+    Select_commutative_match<Op0_t, Op1_t, specific_intval<1>>>
+m_c_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) {
+  return m_CombineOr(
+      m_c_VPInstruction<VPInstruction::LogicalAnd, Op0_t, Op1_t>(Op0, Op1),
+      m_c_Select(Op0, Op1, m_False()));
+}
+
 template <typename Op0_t, typename Op1_t>
 inline AllRecipe_match<Instruction::Select, Op0_t, specific_intval<1>, Op1_t>
 m_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) {
   return m_Select(Op0, m_True(), Op1);
 }
 
+template <typename Op0_t, typename Op1_t>
+inline Select_commutative_match<Op0_t, specific_intval<1>, Op1_t>
+m_c_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) {
+  return m_c_Select(Op0, m_True(), Op1);
+}
+
 template <typename Op0_t, typename Op1_t, typename Op2_t>
 using VPScalarIVSteps_match = Recipe_match<std::tuple<Op0_t, Op1_t, Op2_t>, 0,
                                            false, VPScalarIVStepsRecipe>;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 5708abfaf6f5b..89a270d1219e7 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1064,8 +1064,9 @@ InstructionCost VPRecipeWithIRFlags::getCostForRecipeWithOpcode(
 
     VPValue *Op0, *Op1;
     bool IsLogicalAnd =
-        match(this, m_LogicalAnd(m_VPValue(Op0), m_VPValue(Op1)));
-    bool IsLogicalOr = match(this, m_LogicalOr(m_VPValue(Op0), m_VPValue(Op1)));
+        match(this, m_c_LogicalAnd(m_VPValue(Op0), m_VPValue(Op1)));
+    bool IsLogicalOr =
+        match(this, m_c_LogicalOr(m_VPValue(Op0), m_VPValue(Op1)));
     // Also match the inverted forms:
     // select x, false, y --> !x & y (still AND)
     // select x, y, true --> !x | y (still OR)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 2245a7e0f004f..d49cbfadcf079 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -1295,11 +1295,11 @@ static void simplifyRecipe(VPSingleDefRecipe *Def, VPTypeAnalysis &TypeInfo) {
     return Def->replaceAllUsesWith(X);
 
   // x && false -> false
-  if (match(Def, m_LogicalAnd(m_VPValue(X), m_False())))
-    return Def->replaceAllUsesWith(Def->getOperand(1));
+  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_False())))
+    return Def->replaceAllUsesWith(Plan->getFalse());
 
-  // true && x -> x
-  if (match(Def, m_LogicalAnd(m_True(), m_VPValue(X))))
+  // x && true -> x
+  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_True())))
     return Def->replaceAllUsesWith(X);
 
   // (x && y) | (x && z) -> x && (y | z)

>From acb5a6c82a813ff42706359a49e78f8a859969f1 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 11 Feb 2026 15:13:07 +0000
Subject: [PATCH 3/4] [VPlanPatternMatch] Use auto

---
 .../Transforms/Vectorize/VPlanPatternMatch.h  | 26 +++++--------------
 1 file changed, 7 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
index 9dc14b635f3eb..34cb5a8b48f78 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
@@ -802,24 +802,16 @@ m_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) {
 }
 
 template <typename Op0_t>
-using Not_match =
-    match_combine_or<VPInstruction_match<VPInstruction::Not, Op0_t>,
-                     AllRecipe_commutative_match<
-                         Instruction::Xor, int_pred_ty<is_all_ones>, Op0_t>>;
-
-template <typename Op0_t> inline Not_match<Op0_t> m_Not(const Op0_t &Op0) {
+inline match_combine_or<VPInstruction_match<VPInstruction::Not, Op0_t>,
+                        AllRecipe_commutative_match<
+                            Instruction::Xor, int_pred_ty<is_all_ones>, Op0_t>>
+m_Not(const Op0_t &Op0) {
   return m_CombineOr(m_VPInstruction<VPInstruction::Not>(Op0),
                      m_c_Binary<Instruction::Xor>(m_AllOnes(), Op0));
 }
 
 template <typename Op0_t, typename Op1_t, typename Op2_t>
-using Select_commutative_match = match_combine_or<
-    AllRecipe_match<Instruction::Select, Op0_t, Op1_t, Op2_t>,
-    AllRecipe_match<Instruction::Select, Not_match<Op0_t>, Op2_t, Op1_t>>;
-
-template <typename Op0_t, typename Op1_t, typename Op2_t>
-inline Select_commutative_match<Op0_t, Op1_t, Op2_t>
-m_c_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) {
+inline auto m_c_Select(const Op0_t &Op0, const Op1_t &Op1, const Op2_t &Op2) {
   return m_CombineOr(m_Select(Op0, Op1, Op2), m_Select(m_Not(Op0), Op2, Op1));
 }
 
@@ -834,10 +826,7 @@ m_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) {
 }
 
 template <typename Op0_t, typename Op1_t>
-inline match_combine_or<
-    VPInstruction_commutative_match<VPInstruction::LogicalAnd, Op0_t, Op1_t>,
-    Select_commutative_match<Op0_t, Op1_t, specific_intval<1>>>
-m_c_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) {
+inline auto m_c_LogicalAnd(const Op0_t &Op0, const Op1_t &Op1) {
   return m_CombineOr(
       m_c_VPInstruction<VPInstruction::LogicalAnd, Op0_t, Op1_t>(Op0, Op1),
       m_c_Select(Op0, Op1, m_False()));
@@ -850,8 +839,7 @@ m_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) {
 }
 
 template <typename Op0_t, typename Op1_t>
-inline Select_commutative_match<Op0_t, specific_intval<1>, Op1_t>
-m_c_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) {
+inline auto m_c_LogicalOr(const Op0_t &Op0, const Op1_t &Op1) {
   return m_c_Select(Op0, m_True(), Op1);
 }
 

>From 93be0327c50fbe71b04459c0322b2ff577fd51be Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Thu, 12 Feb 2026 12:38:43 +0000
Subject: [PATCH 4/4] [LV] Test update

---
 .../LoopVectorize/constant-fold-commutative-and.ll           | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll b/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll
index 1a5600b21ea18..97c54fa5a06c2 100644
--- a/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll
+++ b/llvm/test/Transforms/LoopVectorize/constant-fold-commutative-and.ll
@@ -25,10 +25,9 @@ define void @constant_fold_commutative_and(ptr %ptr.n, ptr noalias %p, i1 %cond)
 ; CHECK-NEXT:    [[TMP3:%.*]] = icmp ult <2 x i64> [[BROADCAST_SPLAT2]], splat (i64 4)
 ; CHECK-NEXT:    [[TMP4:%.*]] = icmp ult <2 x i64> [[BROADCAST_SPLAT2]], splat (i64 7)
 ; CHECK-NEXT:    [[TMP5:%.*]] = select <2 x i1> [[TMP0]], <2 x i1> [[TMP3]], <2 x i1> zeroinitializer
-; CHECK-NEXT:    [[TMP12:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> splat (i1 true), <2 x i1> zeroinitializer
 ; CHECK-NEXT:    [[PREDPHI:%.*]] = select <2 x i1> [[TMP5]], <2 x i1> splat (i1 true), <2 x i1> [[TMP4]]
 ; CHECK-NEXT:    [[PREDPHI3:%.*]] = select i1 [[COND]], <2 x i1> zeroinitializer, <2 x i1> [[PREDPHI]]
-; CHECK-NEXT:    [[TMP6:%.*]] = extractelement <2 x i1> [[TMP12]], i32 0
+; CHECK-NEXT:    [[TMP6:%.*]] = extractelement <2 x i1> [[TMP1]], i32 0
 ; CHECK-NEXT:    br i1 [[TMP6]], label %[[PRED_STORE_IF:.*]], label %[[PRED_STORE_CONTINUE:.*]]
 ; CHECK:       [[PRED_STORE_IF]]:
 ; CHECK-NEXT:    [[TMP7:%.*]] = add i64 [[INDEX]], 0
@@ -37,7 +36,7 @@ define void @constant_fold_commutative_and(ptr %ptr.n, ptr noalias %p, i1 %cond)
 ; CHECK-NEXT:    store i1 [[TMP9]], ptr [[TMP8]], align 1
 ; CHECK-NEXT:    br label %[[PRED_STORE_CONTINUE]]
 ; CHECK:       [[PRED_STORE_CONTINUE]]:
-; CHECK-NEXT:    [[TMP10:%.*]] = extractelement <2 x i1> [[TMP12]], i32 1
+; CHECK-NEXT:    [[TMP10:%.*]] = extractelement <2 x i1> [[TMP1]], i32 1
 ; CHECK-NEXT:    br i1 [[TMP10]], label %[[PRED_STORE_IF4:.*]], label %[[PRED_STORE_CONTINUE5]]
 ; CHECK:       [[PRED_STORE_IF4]]:
 ; CHECK-NEXT:    [[TMP11:%.*]] = add i64 [[INDEX]], 1



More information about the llvm-commits mailing list