Index: llvm/lib/Target/X86/X86ISelLowering.cpp =================================================================== --- llvm/lib/Target/X86/X86ISelLowering.cpp +++ llvm/lib/Target/X86/X86ISelLowering.cpp @@ -38966,6 +38966,51 @@ return DAG.getNode(ISD::ZERO_EXTEND, DL, N->getValueType(0), Setnp); } +/// Try to replace shift/logic that tests if a bit is clear with mask + setcc. +/// This is expected to become bt/test + set and save at least 1 instruction. +static SDValue combineShiftAnd1ToTestSet(SDNode *And, SelectionDAG &DAG) { + assert(And->getOpcode() == ISD::AND && "Expected an 'and' op"); + + // This is probably not worthwhile without a supported type. + EVT VT = And->getValueType(0); + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + 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)) + 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); + + // Match a shift-right by constant. + if (Srl.getOpcode() != ISD::SRL || !isa(Srl.getOperand(1)) || + !Srl.hasOneUse()) + return SDValue(); + + // Turn this into the standard form to produce x86 test+set. + // and (not (srl X, C)), 1 --> (and X, 1<getOperand(1)))); } + if (SDValue R = combineShiftAnd1ToTestSet(N, DAG)) + return R; + // Use a 32-bit and+zext if upper bits known zero. if (VT == MVT::i64 && Subtarget.is64Bit() && !isa(N->getOperand(1))) { Index: llvm/test/CodeGen/X86/test-vs-bittest.ll =================================================================== --- llvm/test/CodeGen/X86/test-vs-bittest.ll +++ llvm/test/CodeGen/X86/test-vs-bittest.ll @@ -393,10 +393,9 @@ define i64 @is_upper_bit_clear_i64(i64 %x) { ; CHECK-LABEL: is_upper_bit_clear_i64: ; CHECK: # %bb.0: -; CHECK-NEXT: movq %rdi, %rax -; CHECK-NEXT: shrq $37, %rax -; CHECK-NEXT: notl %eax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: btq $37, %rdi +; CHECK-NEXT: setae %al ; CHECK-NEXT: retq %sh = lshr i64 %x, 37 %m = and i64 %sh, 1 @@ -407,10 +406,9 @@ define i64 @is_lower_bit_clear_i64(i64 %x) { ; CHECK-LABEL: is_lower_bit_clear_i64: ; CHECK: # %bb.0: -; CHECK-NEXT: movq %rdi, %rax -; CHECK-NEXT: shrl $27, %eax -; CHECK-NEXT: notl %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 %sh = lshr i64 %x, 27 %m = and i64 %sh, 1 @@ -421,10 +419,9 @@ define i32 @is_bit_clear_i32(i32 %x) { ; CHECK-LABEL: is_bit_clear_i32: ; CHECK: # %bb.0: -; CHECK-NEXT: movl %edi, %eax -; CHECK-NEXT: shrl $27, %eax -; CHECK-NEXT: notl %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 %sh = lshr i32 %x, 27 %n = xor i32 %sh, -1 @@ -435,10 +432,9 @@ define i16 @is_bit_clear_i16(i16 %x) { ; CHECK-LABEL: is_bit_clear_i16: ; CHECK: # %bb.0: -; CHECK-NEXT: movzwl %di, %eax -; CHECK-NEXT: shrl $7, %eax -; CHECK-NEXT: notl %eax -; CHECK-NEXT: andl $1, %eax +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: testb $-128, %dil +; CHECK-NEXT: sete %al ; CHECK-NEXT: # kill: def $ax killed $ax killed $eax ; CHECK-NEXT: retq %sh = lshr i16 %x, 7 @@ -450,11 +446,8 @@ define i8 @is_bit_clear_i8(i8 %x) { ; CHECK-LABEL: is_bit_clear_i8: ; CHECK: # %bb.0: -; CHECK-NEXT: movl %edi, %eax -; CHECK-NEXT: shrb $3, %al -; CHECK-NEXT: notb %al -; CHECK-NEXT: andb $1, %al -; CHECK-NEXT: # kill: def $al killed $al killed $eax +; CHECK-NEXT: testb $8, %dil +; CHECK-NEXT: sete %al ; CHECK-NEXT: retq %sh = lshr i8 %x, 3 %m = and i8 %sh, 1