diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index b4033fc2a418a..3688f6a31ceea 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -805,6 +805,43 @@ 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; + + bool Swapped = false; + if (isa(LHS)) { + std::swap(LHS, RHS); + Swapped = true; + } + + 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 (!Swapped && 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 +3578,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 2e57597455c2c..be8aa21867ddd 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: [[TMP1:%.*]] = and i8 [[K:%.*]], 9 +; CHECK-NEXT: [[OR:%.*]] = icmp ne i8 [[TMP1]], 9 +; 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: [[TMP1:%.*]] = and i8 [[K:%.*]], 9 +; CHECK-NEXT: [[OR:%.*]] = icmp ne i8 [[TMP1]], 9 +; 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: [[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 + %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: [[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 + %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: [[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 + %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: [[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 + %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: [[TMP1:%.*]] = and i8 [[K:%.*]], 9 +; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[TMP1]], 9 +; 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: [[TMP1:%.*]] = and i8 [[K:%.*]], 9 +; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[TMP1]], 9 +; 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: [[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 + %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: [[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 + %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: [[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 + %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 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: [[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 +; 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:%.*]] +; 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 + %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 +}