[llvm] [InstCombine] Fold (trunc X to i1) & !iszero(X & Pow2)) -> (X & (1 | Pow2)) == (1 | Pow2) (PR #119196)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 14 02:04:11 PST 2024


https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/119196

>From f73635a5797297019a8578703abd1196ebc433c8 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 8 Dec 2024 20:12:40 +0100
Subject: [PATCH 1/4] [NFC] Pre-commit test Fold not(trunc X to i1) | iszero(X
 & Pow2) -> (X & (1 | Pow2)) != (1 | Pow2)

---
 .../Transforms/InstCombine/onehot_merge.ll    | 312 ++++++++++++++++++
 1 file changed, 312 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/onehot_merge.ll b/llvm/test/Transforms/InstCombine/onehot_merge.ll
index 2e57597455c2cd..4d2ce57293ba3a 100644
--- a/llvm/test/Transforms/InstCombine/onehot_merge.ll
+++ b/llvm/test/Transforms/InstCombine/onehot_merge.ll
@@ -1143,3 +1143,315 @@ define i1 @foo1_and_signbit_lshr_without_shifting_signbit_not_pwr2_logical(i32 %
   %or = select i1 %t2, i1 true, i1 %t4
   ret i1 %or
 }
+
+define i1 @trunc_or_icmp_consts(i8 %k) {
+; CHECK-LABEL: @trunc_or_icmp_consts(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[AND]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %and = and i8 %k, 8
+  %icmp = icmp eq i8 %and, 0
+  %ret = or i1 %not, %icmp
+  ret i1 %ret
+}
+
+define i1 @icmp_or_trunc_consts(i8 %k) {
+; CHECK-LABEL: @icmp_or_trunc_consts(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[AND]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %and = and i8 %k, 8
+  %icmp = icmp eq i8 %and, 0
+  %ret = or i1 %icmp, %not
+  ret i1 %ret
+}
+
+define i1 @trunc_or_icmp(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_or_icmp(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = or i1 %icmp, %not
+  ret i1 %ret
+}
+
+define i1 @trunc_logical_or_icmp(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_logical_or_icmp(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = select i1 %icmp, i1 true, i1 %not
+  ret i1 %ret
+}
+
+define i1 @icmp_logical_or_trunc(i8 %k, i8 %c1) {
+; CHECK-LABEL: @icmp_logical_or_trunc(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[NOT]], i1 true, i1 [[ICMP]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = select i1 %not, i1 true, i1 %icmp
+  ret i1 %ret
+}
+
+define <2 x i1> @trunc_or_icmp_vec(<2 x i8> %k, <2 x i8> %c1) {
+; CHECK-LABEL: @trunc_or_icmp_vec(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw <2 x i8> splat (i8 1), [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and <2 x i8> [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq <2 x i8> [[T1]], zeroinitializer
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc <2 x i8> [[K]] to <2 x i1>
+; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[TRUNC]], splat (i1 true)
+; CHECK-NEXT:    [[RET:%.*]] = or <2 x i1> [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret <2 x i1> [[RET]]
+;
+  %t = shl <2 x i8> <i8 1, i8 1>, %c1
+  %t1 = and <2 x i8> %t, %k
+  %icmp = icmp eq <2 x i8> %t1, zeroinitializer
+  %trunc = trunc <2 x i8> %k to <2 x i1>
+  %not = xor <2 x i1> %trunc, <i1 true, i1 true>
+  %ret = or <2 x i1> %icmp, %not
+  ret <2 x i1> %ret
+}
+
+define i1 @neg_trunc_or_icmp_not_pow2(i8 %k, i8 %c1) {
+; CHECK-LABEL: @neg_trunc_or_icmp_not_pow2(
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[C1:%.*]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t1 = and i8 %c1, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = or i1 %icmp, %not
+  ret i1 %ret
+}
+
+define i1 @neg_trunc_or_icmp_not_trunc(i8 %k, i8 %c1) {
+; CHECK-LABEL: @neg_trunc_or_icmp_not_trunc(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = or i1 %icmp, %trunc
+  ret i1 %ret
+}
+
+define i1 @neg_trunc_or_icmp_ne(i8 %k, i8 %c1) {
+; CHECK-LABEL: @neg_trunc_or_icmp_ne(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = or i1 %icmp, %not
+  ret i1 %ret
+}
+
+define i1 @trunc_and_icmp_consts(i8 %k) {
+; CHECK-LABEL: @trunc_and_icmp_consts(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %trunc = trunc i8 %k to i1
+  %and = and i8 %k, 8
+  %icmp = icmp ne i8 %and, 0
+  %ret = and i1 %trunc, %icmp
+  ret i1 %ret
+}
+
+define i1 @icmp_and_trunc_consts(i8 %k) {
+; CHECK-LABEL: @icmp_and_trunc_consts(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %trunc = trunc i8 %k to i1
+  %and = and i8 %k, 8
+  %icmp = icmp ne i8 %and, 0
+  %ret = and i1 %icmp, %trunc
+  ret i1 %ret
+}
+
+define i1 @trunc_and_icmp(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_and_icmp(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = and i1 %icmp, %trunc
+  ret i1 %ret
+}
+
+define i1 @trunc_logical_and_icmp(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_logical_and_icmp(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = select i1 %icmp, i1 %trunc, i1 false
+  ret i1 %ret
+}
+
+define i1 @icmp_logical_and_trunc(i8 %k, i8 %c1) {
+; CHECK-LABEL: @icmp_logical_and_trunc(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[TRUNC]], i1 [[ICMP]], i1 false
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = select i1 %trunc, i1 %icmp, i1 false
+  ret i1 %ret
+}
+
+define <2 x i1> @trunc_and_icmp_vec(<2 x i8> %k, <2 x i8> %c1) {
+; CHECK-LABEL: @trunc_and_icmp_vec(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw <2 x i8> splat (i8 1), [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and <2 x i8> [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne <2 x i8> [[T1]], zeroinitializer
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc <2 x i8> [[K]] to <2 x i1>
+; CHECK-NEXT:    [[RET:%.*]] = and <2 x i1> [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret <2 x i1> [[RET]]
+;
+  %t = shl <2 x i8> <i8 1, i8 1>, %c1
+  %t1 = and <2 x i8> %t, %k
+  %icmp = icmp ne <2 x i8> %t1, zeroinitializer
+  %trunc = trunc <2 x i8> %k to <2 x i1>
+  %ret = and <2 x i1> %icmp, %trunc
+  ret <2 x i1> %ret
+}
+
+define i1 @trunc_and_icmp_not_pow2(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_and_icmp_not_pow2(
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[C1:%.*]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t1 = and i8 %c1, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = and i1 %icmp, %trunc
+  ret i1 %ret
+}
+
+define i1 @trunc_and_icmp_not_trunc(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_and_icmp_not_trunc(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp ne i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %not = xor i1 %trunc, true
+  %ret = and i1 %icmp, %not
+  ret i1 %ret
+}
+
+define i1 @trunc_and_icmp_eq(i8 %k, i8 %c1) {
+; CHECK-LABEL: @trunc_and_icmp_eq(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
+; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
+; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
+; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %t, %k
+  %icmp = icmp eq i8 %t1, 0
+  %trunc = trunc i8 %k to i1
+  %ret = and i1 %icmp, %trunc
+  ret i1 %ret
+}

>From b0c724b9b2ad47f91eed125ed81c1ff6563b1c1a Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 8 Dec 2024 20:19:17 +0100
Subject: [PATCH 2/4] [InstCombine] Fold not(trunc X to i1) | iszero(X & Pow2)
 -> (X & (1 | Pow2)) != (1 | Pow2)

also folds (trunc X to i1) & !iszero(X & Pow2)) -> (X & (1 | Pow2)) == (1 | Pow2)
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 38 ++++++++
 .../Transforms/InstCombine/onehot_merge.ll    | 88 +++++++------------
 2 files changed, 72 insertions(+), 54 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b4033fc2a418a1..da58bedbce8b34 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -805,6 +805,40 @@ Value *InstCombinerImpl::foldAndOrOfICmpsOfAndWithPow2(ICmpInst *LHS,
   return nullptr;
 }
 
+// Fold not(trunc X to i1) | iszero(X & Pow2)
+//  -> (X & (1 | Pow2)) != (1 | Pow2)
+// Fold (trunc X to i1) & !iszero(X & Pow2))
+//  -> (X & (1 | Pow2)) == (1 | Pow2)
+static Value *foldTruncAndOrICmpOfAndWithPow2(InstCombiner::BuilderTy &Builder,
+                                              Value *LHS, Value *RHS,
+                                              bool IsAnd, bool IsLogical,
+                                              const SimplifyQuery &Q) {
+  CmpInst::Predicate Pred = IsAnd ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ;
+
+  if (isa<ICmpInst>(LHS))
+    std::swap(LHS, RHS);
+
+  Value *X, *Pow2;
+
+  if ((IsAnd ? match(LHS, m_Trunc(m_Value(X)))
+             : match(LHS, m_Not(m_Trunc(m_Value(X))))) &&
+      match(RHS, m_SpecificICmp(Pred, m_c_And(m_Specific(X), m_Value(Pow2)),
+                                m_ZeroInt())) &&
+      isKnownToBeAPowerOfTwo(Pow2, Q.DL, /*OrZero=*/false, /*Depth=*/0, Q.AC,
+                             Q.CxtI, Q.DT)) {
+    // If this is a logical and/or, then we must prevent propagation of a
+    // poison value from the RHS by inserting freeze.
+    if (IsLogical)
+      Pow2 = Builder.CreateFreeze(Pow2);
+    Value *Mask = Builder.CreateOr(ConstantInt::get(Pow2->getType(), 1), Pow2);
+    Value *Masked = Builder.CreateAnd(X, Mask);
+    auto NewPred = IsAnd ? CmpInst::ICMP_EQ : CmpInst::ICMP_NE;
+    return Builder.CreateICmp(NewPred, Masked, Mask);
+  }
+
+  return nullptr;
+}
+
 /// General pattern:
 ///   X & Y
 ///
@@ -3541,6 +3575,10 @@ Value *InstCombinerImpl::foldBooleanAndOr(Value *LHS, Value *RHS,
 
   if (Value *Res = foldEqOfParts(LHS, RHS, IsAnd))
     return Res;
+  const SimplifyQuery Q = SQ.getWithInstruction(&I);
+  if (Value *Res = foldTruncAndOrICmpOfAndWithPow2(Builder, LHS, RHS, IsAnd,
+                                                   IsLogical, Q))
+    return Res;
 
   return nullptr;
 }
diff --git a/llvm/test/Transforms/InstCombine/onehot_merge.ll b/llvm/test/Transforms/InstCombine/onehot_merge.ll
index 4d2ce57293ba3a..e4586a231535a3 100644
--- a/llvm/test/Transforms/InstCombine/onehot_merge.ll
+++ b/llvm/test/Transforms/InstCombine/onehot_merge.ll
@@ -1146,11 +1146,8 @@ define i1 @foo1_and_signbit_lshr_without_shifting_signbit_not_pwr2_logical(i32 %
 
 define i1 @trunc_or_icmp_consts(i8 %k) {
 ; CHECK-LABEL: @trunc_or_icmp_consts(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[AND]], 0
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[K:%.*]], 9
+; CHECK-NEXT:    [[OR:%.*]] = icmp ne i8 [[TMP1]], 9
 ; CHECK-NEXT:    ret i1 [[OR]]
 ;
   %trunc = trunc i8 %k to i1
@@ -1163,11 +1160,8 @@ define i1 @trunc_or_icmp_consts(i8 %k) {
 
 define i1 @icmp_or_trunc_consts(i8 %k) {
 ; CHECK-LABEL: @icmp_or_trunc_consts(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[AND]], 0
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[K:%.*]], 9
+; CHECK-NEXT:    [[OR:%.*]] = icmp ne i8 [[TMP1]], 9
 ; CHECK-NEXT:    ret i1 [[OR]]
 ;
   %trunc = trunc i8 %k to i1
@@ -1181,11 +1175,9 @@ define i1 @icmp_or_trunc_consts(i8 %k) {
 define i1 @trunc_or_icmp(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @trunc_or_icmp(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[T]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1200,11 +1192,9 @@ define i1 @trunc_or_icmp(i8 %k, i8 %c1) {
 define i1 @trunc_logical_or_icmp(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @trunc_logical_or_icmp(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[RET:%.*]] = or i1 [[ICMP]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[T]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1219,11 +1209,10 @@ define i1 @trunc_logical_or_icmp(i8 %k, i8 %c1) {
 define i1 @icmp_logical_or_trunc(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @icmp_logical_or_trunc(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[RET:%.*]] = select i1 [[NOT]], i1 true, i1 [[ICMP]]
+; CHECK-NEXT:    [[TMP1:%.*]] = freeze i8 [[T]]
+; CHECK-NEXT:    [[TMP2:%.*]] = or i8 [[TMP1]], 1
+; CHECK-NEXT:    [[TMP3:%.*]] = and i8 [[K:%.*]], [[TMP2]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp ne i8 [[TMP3]], [[TMP2]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1238,11 +1227,9 @@ define i1 @icmp_logical_or_trunc(i8 %k, i8 %c1) {
 define <2 x i1> @trunc_or_icmp_vec(<2 x i8> %k, <2 x i8> %c1) {
 ; CHECK-LABEL: @trunc_or_icmp_vec(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw <2 x i8> splat (i8 1), [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and <2 x i8> [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp eq <2 x i8> [[T1]], zeroinitializer
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc <2 x i8> [[K]] to <2 x i1>
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[TRUNC]], splat (i1 true)
-; CHECK-NEXT:    [[RET:%.*]] = or <2 x i1> [[ICMP]], [[NOT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or <2 x i8> [[T]], splat (i8 1)
+; CHECK-NEXT:    [[TMP2:%.*]] = and <2 x i8> [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp ne <2 x i8> [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret <2 x i1> [[RET]]
 ;
   %t = shl <2 x i8> <i8 1, i8 1>, %c1
@@ -1309,10 +1296,8 @@ define i1 @neg_trunc_or_icmp_ne(i8 %k, i8 %c1) {
 
 define i1 @trunc_and_icmp_consts(i8 %k) {
 ; CHECK-LABEL: @trunc_and_icmp_consts(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[AND]], 0
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[K:%.*]], 9
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[TMP1]], 9
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %trunc = trunc i8 %k to i1
@@ -1324,10 +1309,8 @@ define i1 @trunc_and_icmp_consts(i8 %k) {
 
 define i1 @icmp_and_trunc_consts(i8 %k) {
 ; CHECK-LABEL: @icmp_and_trunc_consts(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K:%.*]] to i1
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[K]], 8
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[AND]], 0
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[K:%.*]], 9
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[TMP1]], 9
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %trunc = trunc i8 %k to i1
@@ -1340,10 +1323,9 @@ define i1 @icmp_and_trunc_consts(i8 %k) {
 define i1 @trunc_and_icmp(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @trunc_and_icmp(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[T]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1357,10 +1339,9 @@ define i1 @trunc_and_icmp(i8 %k, i8 %c1) {
 define i1 @trunc_logical_and_icmp(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @trunc_logical_and_icmp(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[RET:%.*]] = and i1 [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[T]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1374,10 +1355,10 @@ define i1 @trunc_logical_and_icmp(i8 %k, i8 %c1) {
 define i1 @icmp_logical_and_trunc(i8 %k, i8 %c1) {
 ; CHECK-LABEL: @icmp_logical_and_trunc(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 1, [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and i8 [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne i8 [[T1]], 0
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[K]] to i1
-; CHECK-NEXT:    [[RET:%.*]] = select i1 [[TRUNC]], i1 [[ICMP]], i1 false
+; CHECK-NEXT:    [[TMP1:%.*]] = freeze i8 [[T]]
+; CHECK-NEXT:    [[TMP2:%.*]] = or i8 [[TMP1]], 1
+; CHECK-NEXT:    [[TMP3:%.*]] = and i8 [[K:%.*]], [[TMP2]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[TMP3]], [[TMP2]]
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %t = shl i8 1, %c1
@@ -1391,10 +1372,9 @@ define i1 @icmp_logical_and_trunc(i8 %k, i8 %c1) {
 define <2 x i1> @trunc_and_icmp_vec(<2 x i8> %k, <2 x i8> %c1) {
 ; CHECK-LABEL: @trunc_and_icmp_vec(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw <2 x i8> splat (i8 1), [[C1:%.*]]
-; CHECK-NEXT:    [[T1:%.*]] = and <2 x i8> [[T]], [[K:%.*]]
-; CHECK-NEXT:    [[ICMP:%.*]] = icmp ne <2 x i8> [[T1]], zeroinitializer
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc <2 x i8> [[K]] to <2 x i1>
-; CHECK-NEXT:    [[RET:%.*]] = and <2 x i1> [[ICMP]], [[TRUNC]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or <2 x i8> [[T]], splat (i8 1)
+; CHECK-NEXT:    [[TMP2:%.*]] = and <2 x i8> [[K:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq <2 x i8> [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    ret <2 x i1> [[RET]]
 ;
   %t = shl <2 x i8> <i8 1, i8 1>, %c1

>From 266fac9af5e6c1a1f861f09200e3054295b7c970 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 14 Dec 2024 11:01:33 +0100
Subject: [PATCH 3/4] [NFC] Add test for redundant freeze

---
 .../Transforms/InstCombine/onehot_merge.ll    | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/onehot_merge.ll b/llvm/test/Transforms/InstCombine/onehot_merge.ll
index e4586a231535a3..85c0424a1e482f 100644
--- a/llvm/test/Transforms/InstCombine/onehot_merge.ll
+++ b/llvm/test/Transforms/InstCombine/onehot_merge.ll
@@ -1369,6 +1369,27 @@ define i1 @icmp_logical_and_trunc(i8 %k, i8 %c1) {
   ret i1 %ret
 }
 
+define i1 @trunc_logical_and_icmp_and_icmps(i8 %x, i8 %y, i8 %c1) {
+; CHECK-LABEL: @trunc_logical_and_icmp_and_icmps(
+; CHECK-NEXT:    [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
+; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
+; CHECK-NEXT:    [[TMP4:%.*]] = freeze i8 [[Z_SHIFT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[TMP4]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[TMP3]], i1 [[C1]], i1 false
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+  %t = shl i8 1, %c1
+  %t1 = and i8 %x, %t
+  %trunc = trunc i8 %x to i1
+  %icmp1 = icmp eq i8 %y, 42
+  %and1 = select i1 %trunc, i1 %icmp1, i1 false
+  %icmp2 = icmp ne i8 %t1, 0
+  %and2 = and i1 %icmp2, %and1
+  ret i1 %and2
+}
+
 define <2 x i1> @trunc_and_icmp_vec(<2 x i8> %k, <2 x i8> %c1) {
 ; CHECK-LABEL: @trunc_and_icmp_vec(
 ; CHECK-NEXT:    [[T:%.*]] = shl nuw <2 x i8> splat (i8 1), [[C1:%.*]]

>From b781ca8457b03b59c72e86d7eff87c5efe65df72 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 14 Dec 2024 11:03:55 +0100
Subject: [PATCH 4/4] [InstCombine] remove redundant freeze in
 foldTruncAndOrICmpOfAndWithPow2

---
 llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp | 7 +++++--
 llvm/test/Transforms/InstCombine/onehot_merge.ll        | 3 +--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index da58bedbce8b34..3688f6a31ceea4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -815,8 +815,11 @@ static Value *foldTruncAndOrICmpOfAndWithPow2(InstCombiner::BuilderTy &Builder,
                                               const SimplifyQuery &Q) {
   CmpInst::Predicate Pred = IsAnd ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ;
 
-  if (isa<ICmpInst>(LHS))
+  bool Swapped = false;
+  if (isa<ICmpInst>(LHS)) {
     std::swap(LHS, RHS);
+    Swapped = true;
+  }
 
   Value *X, *Pow2;
 
@@ -828,7 +831,7 @@ static Value *foldTruncAndOrICmpOfAndWithPow2(InstCombiner::BuilderTy &Builder,
                              Q.CxtI, Q.DT)) {
     // If this is a logical and/or, then we must prevent propagation of a
     // poison value from the RHS by inserting freeze.
-    if (IsLogical)
+    if (!Swapped && IsLogical)
       Pow2 = Builder.CreateFreeze(Pow2);
     Value *Mask = Builder.CreateOr(ConstantInt::get(Pow2->getType(), 1), Pow2);
     Value *Masked = Builder.CreateAnd(X, Mask);
diff --git a/llvm/test/Transforms/InstCombine/onehot_merge.ll b/llvm/test/Transforms/InstCombine/onehot_merge.ll
index 85c0424a1e482f..be8aa21867ddd8 100644
--- a/llvm/test/Transforms/InstCombine/onehot_merge.ll
+++ b/llvm/test/Transforms/InstCombine/onehot_merge.ll
@@ -1373,8 +1373,7 @@ define i1 @trunc_logical_and_icmp_and_icmps(i8 %x, i8 %y, i8 %c1) {
 ; CHECK-LABEL: @trunc_logical_and_icmp_and_icmps(
 ; CHECK-NEXT:    [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
 ; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
-; CHECK-NEXT:    [[TMP4:%.*]] = freeze i8 [[Z_SHIFT]]
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[TMP4]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
 ; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X:%.*]], [[TMP1]]
 ; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
 ; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[TMP3]], i1 [[C1]], i1 false



More information about the llvm-commits mailing list