From f73635a5797297019a8578703abd1196ebc433c8 Mon Sep 17 00:00:00 2001 From: Andreas Jonson 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 2e57597455c2c..4d2ce57293ba3 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> , %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, + %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> , %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 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 b4033fc2a418a..da58bedbce8b3 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(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 4d2ce57293ba3..e4586a231535a 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> , %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> , %c1 From 266fac9af5e6c1a1f861f09200e3054295b7c970 Mon Sep 17 00:00:00 2001 From: Andreas Jonson 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 e4586a231535a..85c0424a1e482 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 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 da58bedbce8b3..3688f6a31ceea 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(LHS)) + bool Swapped = false; + if (isa(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 85c0424a1e482..be8aa21867ddd 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