Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2476,7 +2476,7 @@ /// We have an expression of the form (A & C) | (B & D). If A is a scalar or /// vector composed of all-zeros or all-ones values and is the bitwise 'not' of /// B, it can be used as the condition operand of a select instruction. -Value *InstCombinerImpl::getSelectCondition(Value *A, Value *B) { +Value *InstCombinerImpl::getSelectCondition(Value *A, Value *B, bool AisB) { // We may have peeked through bitcasts in the caller. // Exit immediately if we don't have (vector) integer types. Type *Ty = A->getType(); @@ -2484,7 +2484,7 @@ return nullptr; // If A is the 'not' operand of B and has enough signbits, we have our answer. - if (match(B, m_Not(m_Specific(A)))) { + if (AisB ? (A == B) : match(B, m_Not(m_Specific(A)))) { // If these are scalars or vectors of i1, A can be used directly. if (Ty->isIntOrIntVectorTy(1)) return A; @@ -2504,6 +2504,10 @@ return nullptr; } + // TODO: add support for sext and constant case + if (AisB) + return nullptr; + // If both operands are constants, see if the constants are inverse bitmasks. Constant *AConst, *BConst; if (match(A, m_Constant(AConst)) && match(B, m_Constant(BConst))) @@ -2553,13 +2557,13 @@ /// We have an expression of the form (A & C) | (B & D). Try to simplify this /// to "A' ? C : D", where A' is a boolean or vector of booleans. Value *InstCombinerImpl::matchSelectFromAndOr(Value *A, Value *C, Value *B, - Value *D) { + Value *D, bool NeedNotD) { // The potential condition of the select may be bitcasted. In that case, look // through its bitcast and the corresponding bitcast of the 'not' condition. Type *OrigType = A->getType(); A = peekThroughBitcast(A, true); B = peekThroughBitcast(B, true); - if (Value *Cond = getSelectCondition(A, B)) { + if (Value *Cond = getSelectCondition(A, B, NeedNotD)) { // ((bc Cond) & C) | ((bc ~Cond) & D) --> bc (select Cond, (bc C), (bc D)) // If this is a vector, we may need to cast to match the condition's length. // The bitcasts will either all exist or all not exist. The builder will @@ -2575,6 +2579,8 @@ SelTy = VectorType::get(EltTy, VecTy->getElementCount()); } Value *BitcastC = Builder.CreateBitCast(C, SelTy); + if (NeedNotD) + D = Builder.CreateNot(D); Value *BitcastD = Builder.CreateBitCast(D, SelTy); Value *Select = Builder.CreateSelect(Cond, BitcastC, BitcastD); return Builder.CreateBitCast(Select, OrigType); @@ -2959,6 +2965,21 @@ } } + if (match(&I, m_c_BinOp(m_And(m_Value(A), m_Value(C)), + m_Not(m_Or(m_Value(B), m_Value(D)))))) { + if (Op0->hasOneUse() || Op1->hasOneUse()) { + // (Cond & C) | ~(Cond | D) -> Cond ? C : ~D + if (Value *V = matchSelectFromAndOr(A, C, B, D, true)) + return replaceInstUsesWith(I, V); + if (Value *V = matchSelectFromAndOr(A, C, D, B, true)) + return replaceInstUsesWith(I, V); + if (Value *V = matchSelectFromAndOr(C, A, B, D, true)) + return replaceInstUsesWith(I, V); + if (Value *V = matchSelectFromAndOr(C, A, D, B, true)) + return replaceInstUsesWith(I, V); + } + } + // (A ^ B) | ((B ^ C) ^ A) -> (A ^ B) | C if (match(Op0, m_Xor(m_Value(A), m_Value(B)))) if (match(Op1, m_Xor(m_Xor(m_Specific(B), m_Value(C)), m_Specific(A)))) Index: llvm/lib/Transforms/InstCombine/InstCombineInternal.h =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -364,8 +364,9 @@ Value *foldAndOrOfICmpsOfAndWithPow2(ICmpInst *LHS, ICmpInst *RHS, Instruction *CxtI, bool IsAnd, bool IsLogical = false); - Value *matchSelectFromAndOr(Value *A, Value *B, Value *C, Value *D); - Value *getSelectCondition(Value *A, Value *B); + Value *matchSelectFromAndOr(Value *A, Value *B, Value *C, Value *D, + bool NeedNotD = false); + Value *getSelectCondition(Value *A, Value *B, bool AisB); Instruction *foldExtractOfOverflowIntrinsic(ExtractValueInst &EV); Instruction *foldIntrinsicWithOverflowCommon(IntrinsicInst *II); Index: llvm/test/Transforms/InstCombine/logical-select.ll =================================================================== --- llvm/test/Transforms/InstCombine/logical-select.ll +++ llvm/test/Transforms/InstCombine/logical-select.ll @@ -988,3 +988,135 @@ %and2 = select <2 x i1> %or, <2 x i1> %nand, <2 x i1> ret <2 x i1> %and2 } + +define i1 @not_d_bools_commute00(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_commute00( +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y:%.*]], true +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i1 [[X:%.*]], i1 [[TMP1]] +; CHECK-NEXT: ret i1 [[TMP2]] +; + %y_c = or i1 %c, %y + %and2 = xor i1 %y_c, true + %and1 = and i1 %c, %x + %r = or i1 %and1, %and2 + ret i1 %r +} + +define i1 @not_d_bools_commute01(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_commute01( +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y:%.*]], true +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i1 [[X:%.*]], i1 [[TMP1]] +; CHECK-NEXT: ret i1 [[TMP2]] +; + %y_c = or i1 %y, %c + %and2 = xor i1 %y_c, true + %and1 = and i1 %c, %x + %r = or i1 %and1, %and2 + ret i1 %r +} + +define i1 @not_d_bools_commute10(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_commute10( +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y:%.*]], true +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i1 [[X:%.*]], i1 [[TMP1]] +; CHECK-NEXT: ret i1 [[TMP2]] +; + %y_c = or i1 %c, %y + %and2 = xor i1 %y_c, true + %and1 = and i1 %x, %c + %r = or i1 %and1, %and2 + ret i1 %r +} + +define i1 @not_d_bools_commute11(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_commute11( +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y:%.*]], true +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i1 [[X:%.*]], i1 [[TMP1]] +; CHECK-NEXT: ret i1 [[TMP2]] +; + %y_c = or i1 %y, %c + %and2 = xor i1 %y_c, true + %and1 = and i1 %x, %c + %r = or i1 %and1, %and2 + ret i1 %r +} + +define <2 x i1> @not_d_bools_vector(<2 x i1> %c, <2 x i1> %x, <2 x i1> %y) { +; CHECK-LABEL: @not_d_bools_vector( +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i1> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[X:%.*]], <2 x i1> [[TMP1]] +; CHECK-NEXT: ret <2 x i1> [[TMP2]] +; + %y_c = or <2 x i1> %y, %c + %and2 = xor <2 x i1> %y_c, + %and1 = and <2 x i1> %x, %c + %r = or <2 x i1> %and1, %and2 + ret <2 x i1> %r +} + +define <2 x i1> @not_d_bools_vector_poison(<2 x i1> %c, <2 x i1> %x, <2 x i1> %y) { +; CHECK-LABEL: @not_d_bools_vector_poison( +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i1> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[X:%.*]], <2 x i1> [[TMP1]] +; CHECK-NEXT: ret <2 x i1> [[TMP2]] +; + %y_c = or <2 x i1> %y, %c + %and2 = xor <2 x i1> %y_c, + %and1 = and <2 x i1> %x, %c + %r = or <2 x i1> %and1, %and2 + ret <2 x i1> %r +} + +define i32 @not_d_allSignBits(i32 %cond, i32 %tval, i32 %fval) { +; CHECK-LABEL: @not_d_allSignBits( +; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[FVAL:%.*]], -1 +; CHECK-NEXT: [[DOTNOT2:%.*]] = icmp slt i32 [[COND:%.*]], 0 +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[DOTNOT2]], i32 [[TVAL:%.*]], i32 [[TMP1]] +; CHECK-NEXT: ret i32 [[TMP2]] +; + %bitmask = ashr i32 %cond, 31 + %a1 = and i32 %tval, %bitmask + %or = or i32 %bitmask, %fval + %a2 = xor i32 %or, -1 + %sel = or i32 %a1, %a2 + ret i32 %sel +} + +define i1 @not_d_bools_use2(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_use2( +; CHECK-NEXT: [[Y_C:%.*]] = or i1 [[C:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[AND1:%.*]] = and i1 [[C]], [[X:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y]], true +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C]], i1 [[X]], i1 [[TMP1]] +; CHECK-NEXT: call void @use1(i1 [[AND1]]) +; CHECK-NEXT: call void @use1(i1 [[Y_C]]) +; CHECK-NEXT: ret i1 [[TMP2]] +; + %y_c = or i1 %c, %y + %and2 = xor i1 %y_c, true + %and1 = and i1 %c, %x + %r = or i1 %and1, %and2 + call void @use1(i1 %and1) + call void @use1(i1 %y_c) + ret i1 %r +} + +define i1 @not_d_bools_negative_use2(i1 %c, i1 %x, i1 %y) { +; CHECK-LABEL: @not_d_bools_negative_use2( +; CHECK-NEXT: [[Y_C:%.*]] = or i1 [[C:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[AND2:%.*]] = xor i1 [[Y_C]], true +; CHECK-NEXT: [[AND1:%.*]] = and i1 [[C]], [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = or i1 [[AND1]], [[AND2]] +; CHECK-NEXT: call void @use1(i1 [[AND2]]) +; CHECK-NEXT: call void @use1(i1 [[AND1]]) +; CHECK-NEXT: ret i1 [[R]] +; + %y_c = or i1 %c, %y + %and2 = xor i1 %y_c, true + %and1 = and i1 %c, %x + %r = or i1 %and1, %and2 + call void @use1(i1 %and2) + call void @use1(i1 %and1) + ret i1 %r +} +