diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index cbabdc99cf0ab..73d0f91fba841 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -5978,44 +5978,64 @@ static SDValue combineShiftAnd1ToBitTest(SDNode *And, SelectionDAG &DAG) { if (!TLI.isTypeLegal(VT)) return SDValue(); - // Look through an optional extension and find a 'not'. - // TODO: Should we favor test+set even without the 'not' op? - SDValue Not = And->getOperand(0), And1 = And->getOperand(1); - if (Not.getOpcode() == ISD::ANY_EXTEND) - Not = Not.getOperand(0); - if (!isBitwiseNot(Not) || !Not.hasOneUse() || !isOneConstant(And1)) + // Look through an optional extension. + SDValue And0 = And->getOperand(0), And1 = And->getOperand(1); + if (And0.getOpcode() == ISD::ANY_EXTEND && And0.hasOneUse()) + And0 = And0.getOperand(0); + if (!isOneConstant(And1) || !And0.hasOneUse()) return SDValue(); - // Look though an optional truncation. The source operand may not be the same - // type as the original 'and', but that is ok because we are masking off - // everything but the low bit. - SDValue Srl = Not.getOperand(0); - if (Srl.getOpcode() == ISD::TRUNCATE) - Srl = Srl.getOperand(0); + SDValue Src = And0; + + // Attempt to find a 'not' op. + // TODO: Should we favor test+set even without the 'not' op? + bool FoundNot = false; + if (isBitwiseNot(Src)) { + FoundNot = true; + Src = Src.getOperand(0); + + // Look though an optional truncation. The source operand may not be the + // same type as the original 'and', but that is ok because we are masking + // off everything but the low bit. + if (Src.getOpcode() == ISD::TRUNCATE && Src.hasOneUse()) + Src = Src.getOperand(0); + } // Match a shift-right by constant. - if (Srl.getOpcode() != ISD::SRL || !Srl.hasOneUse() || - !isa(Srl.getOperand(1))) + if (Src.getOpcode() != ISD::SRL || !Src.hasOneUse()) return SDValue(); // We might have looked through casts that make this transform invalid. // TODO: If the source type is wider than the result type, do the mask and // compare in the source type. - const APInt &ShiftAmt = Srl.getConstantOperandAPInt(1); - unsigned VTBitWidth = VT.getSizeInBits(); - if (ShiftAmt.uge(VTBitWidth)) + unsigned VTBitWidth = VT.getScalarSizeInBits(); + SDValue ShiftAmt = Src.getOperand(1); + auto *ShiftAmtC = dyn_cast(ShiftAmt); + if (!ShiftAmtC || !ShiftAmtC->getAPIntValue().ult(VTBitWidth)) return SDValue(); - if (!TLI.hasBitTest(Srl.getOperand(0), Srl.getOperand(1))) + // Set source to shift source. + Src = Src.getOperand(0); + + // Try again to find a 'not' op. + // TODO: Should we favor test+set even with two 'not' ops? + if (!FoundNot) { + if (!isBitwiseNot(Src)) + return SDValue(); + Src = Src.getOperand(0); + } + + if (!TLI.hasBitTest(Src, ShiftAmt)) return SDValue(); // Turn this into a bit-test pattern using mask op + setcc: // and (not (srl X, C)), 1 --> (and X, 1< (and X, 1<getZExtValue()), DL, VT); SDValue NewAnd = DAG.getNode(ISD::AND, DL, VT, X, Mask); SDValue Zero = DAG.getConstant(0, DL, VT); SDValue Setcc = DAG.getSetCC(DL, CCVT, NewAnd, Zero, ISD::SETEQ); diff --git a/llvm/test/CodeGen/X86/test-vs-bittest.ll b/llvm/test/CodeGen/X86/test-vs-bittest.ll index 76cf265db8f10..77ec67ea1b9a6 100644 --- a/llvm/test/CodeGen/X86/test-vs-bittest.ll +++ b/llvm/test/CodeGen/X86/test-vs-bittest.ll @@ -574,10 +574,9 @@ define i64 @is_upper_bit_clear_i64(i64 %x) { define i64 @is_upper_bit_clear_i64_not(i64 %x) { ; CHECK-LABEL: is_upper_bit_clear_i64_not: ; CHECK: # %bb.0: -; CHECK-NEXT: movq %rdi, %rax -; CHECK-NEXT: notq %rax -; CHECK-NEXT: shrq $39, %rax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: btq $39, %rdi +; CHECK-NEXT: setae %al ; CHECK-NEXT: retq %n = xor i64 %x, -1 %sh = lshr i64 %n, 39 @@ -601,10 +600,9 @@ define i64 @is_lower_bit_clear_i64(i64 %x) { define i64 @is_lower_bit_clear_i64_not(i64 %x) { ; CHECK-LABEL: is_lower_bit_clear_i64_not: ; CHECK: # %bb.0: -; CHECK-NEXT: movq %rdi, %rax -; CHECK-NEXT: notl %eax -; CHECK-NEXT: shrl $16, %eax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: testl $65536, %edi # imm = 0x10000 +; CHECK-NEXT: sete %al ; CHECK-NEXT: retq %n = xor i64 %x, -1 %sh = lshr i64 %n, 16 @@ -628,10 +626,9 @@ define i32 @is_bit_clear_i32(i32 %x) { define i32 @is_bit_clear_i32_not(i32 %x) { ; CHECK-LABEL: is_bit_clear_i32_not: ; CHECK: # %bb.0: -; CHECK-NEXT: movl %edi, %eax -; CHECK-NEXT: notl %eax -; CHECK-NEXT: shrl $27, %eax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: testl $134217728, %edi # imm = 0x8000000 +; CHECK-NEXT: sete %al ; CHECK-NEXT: retq %n = xor i32 %x, -1 %sh = lshr i32 %n, 27 @@ -656,10 +653,9 @@ define i16 @is_bit_clear_i16(i16 %x) { define i16 @is_bit_clear_i16_not(i16 %x) { ; CHECK-LABEL: is_bit_clear_i16_not: ; CHECK: # %bb.0: -; CHECK-NEXT: movl %edi, %eax -; CHECK-NEXT: notl %eax -; CHECK-NEXT: shrl $2, %eax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: testb $4, %dil +; CHECK-NEXT: sete %al ; CHECK-NEXT: # kill: def $ax killed $ax killed $eax ; CHECK-NEXT: retq %n = xor i16 %x, -1 @@ -683,11 +679,8 @@ define i8 @is_bit_clear_i8(i8 %x) { define i8 @is_bit_clear_i8_not(i8 %x) { ; CHECK-LABEL: is_bit_clear_i8_not: ; CHECK: # %bb.0: -; CHECK-NEXT: movl %edi, %eax -; CHECK-NEXT: notb %al -; CHECK-NEXT: shrb $2, %al -; CHECK-NEXT: andb $1, %al -; CHECK-NEXT: # kill: def $al killed $al killed $eax +; CHECK-NEXT: testb $4, %dil +; CHECK-NEXT: sete %al ; CHECK-NEXT: retq %n = xor i8 %x, -1 %sh = lshr i8 %n, 2