Index: lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -2689,7 +2689,18 @@ return DAG.getSetCC(dl, VT, And, DAG.getConstant(0, dl, CTVT), CC); } - // TODO: (ctpop x) == 1 -> x && (x & x-1) == 0 iff ctpop is illegal. + // (ctpop x) == 1 -> x && (x & x-1) == 0 iff ctpop is illegal. + if (Cond == ISD::SETEQ && C1 == 1 && + !isOperationLegalOrCustom(ISD::CTPOP, CTVT)) { + SDValue Sub = + DAG.getNode(ISD::SUB, dl, CTVT, CTOp, DAG.getConstant(1, dl, CTVT)); + SDValue And = DAG.getNode(ISD::AND, dl, CTVT, CTOp, Sub); + SDValue LHS = DAG.getSetCC(dl, VT, CTOp, DAG.getConstant(0, dl, CTVT), + ISD::SETUGT); + SDValue RHS = + DAG.getSetCC(dl, VT, And, DAG.getConstant(0, dl, CTVT), ISD::SETEQ); + return DAG.getNode(ISD::AND, dl, VT, LHS, RHS); + } } // (zext x) == C --> x == (trunc C) Index: test/CodeGen/X86/ctpop-combine.ll =================================================================== --- test/CodeGen/X86/ctpop-combine.ll +++ test/CodeGen/X86/ctpop-combine.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple=x86_64-unknown -mcpu=corei7 | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-unknown -mcpu=corei7 -mattr=+popcnt | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-unknown -mcpu=corei7 -mattr=-popcnt | FileCheck %s -check-prefix=NO-POPCOUNT declare i8 @llvm.ctpop.i8(i8) nounwind readnone declare i64 @llvm.ctpop.i64(i64) nounwind readnone @@ -12,6 +13,14 @@ ; CHECK-NEXT: testq %rcx, %rdi ; CHECK-NEXT: setne %al ; CHECK-NEXT: retq +; +; NO-POPCOUNT-LABEL: test1: +; NO-POPCOUNT: # %bb.0: +; NO-POPCOUNT-NEXT: leaq -1(%rdi), %rcx +; NO-POPCOUNT-NEXT: xorl %eax, %eax +; NO-POPCOUNT-NEXT: testq %rcx, %rdi +; NO-POPCOUNT-NEXT: setne %al +; NO-POPCOUNT-NEXT: retq %count = tail call i64 @llvm.ctpop.i64(i64 %x) %cast = trunc i64 %count to i32 %cmp = icmp ugt i32 %cast, 1 @@ -28,6 +37,14 @@ ; CHECK-NEXT: testq %rcx, %rdi ; CHECK-NEXT: sete %al ; CHECK-NEXT: retq +; +; NO-POPCOUNT-LABEL: test2: +; NO-POPCOUNT: # %bb.0: +; NO-POPCOUNT-NEXT: leaq -1(%rdi), %rcx +; NO-POPCOUNT-NEXT: xorl %eax, %eax +; NO-POPCOUNT-NEXT: testq %rcx, %rdi +; NO-POPCOUNT-NEXT: sete %al +; NO-POPCOUNT-NEXT: retq %count = tail call i64 @llvm.ctpop.i64(i64 %x) %cmp = icmp ult i64 %count, 2 %conv = zext i1 %cmp to i32 @@ -43,6 +60,33 @@ ; CHECK-NEXT: cmpb $2, %cl ; CHECK-NEXT: setb %al ; CHECK-NEXT: retq +; +; NO-POPCOUNT-LABEL: test3: +; NO-POPCOUNT: # %bb.0: +; NO-POPCOUNT-NEXT: movq %rdi, %rax +; NO-POPCOUNT-NEXT: shrq %rax +; NO-POPCOUNT-NEXT: movabsq $6148914691236517205, %rcx # imm = 0x5555555555555555 +; NO-POPCOUNT-NEXT: andq %rax, %rcx +; NO-POPCOUNT-NEXT: subq %rcx, %rdi +; NO-POPCOUNT-NEXT: movabsq $3689348814741910323, %rax # imm = 0x3333333333333333 +; NO-POPCOUNT-NEXT: movq %rdi, %rcx +; NO-POPCOUNT-NEXT: andq %rax, %rcx +; NO-POPCOUNT-NEXT: shrq $2, %rdi +; NO-POPCOUNT-NEXT: andq %rax, %rdi +; NO-POPCOUNT-NEXT: addq %rcx, %rdi +; NO-POPCOUNT-NEXT: movq %rdi, %rax +; NO-POPCOUNT-NEXT: shrq $4, %rax +; NO-POPCOUNT-NEXT: addq %rdi, %rax +; NO-POPCOUNT-NEXT: movabsq $1085102592571150095, %rcx # imm = 0xF0F0F0F0F0F0F0F +; NO-POPCOUNT-NEXT: andq %rax, %rcx +; NO-POPCOUNT-NEXT: movabsq $72340172838076673, %rdx # imm = 0x101010101010101 +; NO-POPCOUNT-NEXT: imulq %rcx, %rdx +; NO-POPCOUNT-NEXT: shrq $56, %rdx +; NO-POPCOUNT-NEXT: andb $63, %dl +; NO-POPCOUNT-NEXT: xorl %eax, %eax +; NO-POPCOUNT-NEXT: cmpb $2, %dl +; NO-POPCOUNT-NEXT: setb %al +; NO-POPCOUNT-NEXT: retq %count = tail call i64 @llvm.ctpop.i64(i64 %x) %cast = trunc i64 %count to i6 ; Too small for 0-64 %cmp = icmp ult i6 %cast, 2 @@ -57,8 +101,53 @@ ; CHECK-NEXT: popcntl %edi, %eax ; CHECK-NEXT: # kill: def $al killed $al killed $eax ; CHECK-NEXT: retq +; +; NO-POPCOUNT-LABEL: test4: +; NO-POPCOUNT: # %bb.0: +; NO-POPCOUNT-NEXT: # kill: def $edi killed $edi def $rdi +; NO-POPCOUNT-NEXT: andb $127, %dil +; NO-POPCOUNT-NEXT: movl %edi, %eax +; NO-POPCOUNT-NEXT: shrb %al +; NO-POPCOUNT-NEXT: andb $21, %al +; NO-POPCOUNT-NEXT: subb %al, %dil +; NO-POPCOUNT-NEXT: movl %edi, %eax +; NO-POPCOUNT-NEXT: andb $51, %al +; NO-POPCOUNT-NEXT: shrb $2, %dil +; NO-POPCOUNT-NEXT: andb $51, %dil +; NO-POPCOUNT-NEXT: addb %al, %dil +; NO-POPCOUNT-NEXT: movl %edi, %eax +; NO-POPCOUNT-NEXT: shrb $4, %al +; NO-POPCOUNT-NEXT: addl %edi, %eax +; NO-POPCOUNT-NEXT: andb $15, %al +; NO-POPCOUNT-NEXT: # kill: def $al killed $al killed $eax +; NO-POPCOUNT-NEXT: retq %x2 = and i8 %x, 127 %count = tail call i8 @llvm.ctpop.i8(i8 %x2) %and = and i8 %count, 7 ret i8 %and } + +define i32 @test5(i64 %x) nounwind readnone { +; CHECK-LABEL: test5: +; CHECK: # %bb.0: +; CHECK-NEXT: popcntq %rdi, %rcx +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: cmpq $1, %rcx +; CHECK-NEXT: sete %al +; CHECK-NEXT: retq +; +; NO-POPCOUNT-LABEL: test5: +; NO-POPCOUNT: # %bb.0: +; NO-POPCOUNT-NEXT: leaq -1(%rdi), %rax +; NO-POPCOUNT-NEXT: testq %rax, %rdi +; NO-POPCOUNT-NEXT: sete %al +; NO-POPCOUNT-NEXT: testq %rdi, %rdi +; NO-POPCOUNT-NEXT: setne %cl +; NO-POPCOUNT-NEXT: andb %al, %cl +; NO-POPCOUNT-NEXT: movzbl %cl, %eax +; NO-POPCOUNT-NEXT: retq + %count = tail call i64 @llvm.ctpop.i64(i64 %x) + %cmp = icmp eq i64 %count, 1 + %conv = zext i1 %cmp to i32 + ret i32 %conv +}