[SelectionDAG] Move ISD:PARITY formation from DAGCombine to SimplifyDemandedBits.

Previously, we formed ISD::PARITY by looking for (and (ctpop X), 1)
but the AND might be separated from the ctpop. For example if the
parity result is multiplied by 2, we'll pull the AND through the
shift.

So to handle more cases, move to SimplifyDemandedBits where we
can handle more cases that result in only the LSB of the CTPOP
being used.
This commit is contained in:
Craig Topper
2020-09-13 19:51:20 -07:00
parent 783ba64a89
commit 56b33391d3
4 changed files with 110 additions and 19 deletions

View File

@@ -5574,25 +5574,6 @@ SDValue DAGCombiner::visitAND(SDNode *N) {
if (SDValue V = combineShiftAnd1ToBitTest(N, DAG))
return V;
// fold (and (ctpop X), 1) -> parity X
// Only do this before op legalization as it might be turned back into ctpop.
// TODO: Support vectors?
if (!LegalOperations && isOneConstant(N1) && N0.hasOneUse()) {
SDValue Tmp = N0;
// It's possible the ctpop has been truncated, but since we only care about
// the LSB we can look through it.
if (Tmp.getOpcode() == ISD::TRUNCATE && Tmp.getOperand(0).hasOneUse())
Tmp = Tmp.getOperand(0);
if (Tmp.getOpcode() == ISD::CTPOP) {
SDLoc dl(N);
SDValue Parity =
DAG.getNode(ISD::PARITY, dl, Tmp.getValueType(), Tmp.getOperand(0));
return DAG.getNode(ISD::TRUNCATE, dl, VT, Parity);
}
}
return SDValue();
}

View File

@@ -3053,6 +3053,11 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
Known.Zero.setBitsFrom(Log2_32(PossibleOnes) + 1);
break;
}
case ISD::PARITY: {
// Parity returns 0 everywhere but the LSB.
Known.Zero.setBitsFrom(1);
break;
}
case ISD::LOAD: {
LoadSDNode *LD = cast<LoadSDNode>(Op);
const Constant *Cst = TLI->getTargetConstantFromLoad(LD);

View File

@@ -1748,6 +1748,17 @@ bool TargetLowering::SimplifyDemandedBits(
Known.Zero = Known2.Zero.byteSwap();
break;
}
case ISD::CTPOP: {
// If only 1 bit is demanded, replace with PARITY as long as we're before
// op legalization.
// FIXME: Limit to scalars for now.
if (DemandedBits.isOneValue() && !TLO.LegalOps && !VT.isVector())
return TLO.CombineTo(Op, TLO.DAG.getNode(ISD::PARITY, dl, VT,
Op.getOperand(0)));
Known = TLO.DAG.computeKnownBits(Op, DemandedElts, Depth);
break;
}
case ISD::SIGN_EXTEND_INREG: {
SDValue Op0 = Op.getOperand(0);
EVT ExVT = cast<VTSDNode>(Op.getOperand(1))->getVT();

View File

@@ -422,6 +422,100 @@ define i32 @parity_8_mask(i32 %x) {
ret i32 %c
}
define i32 @parity_32_shift(i32 %0) {
; X86-NOPOPCNT-LABEL: parity_32_shift:
; X86-NOPOPCNT: # %bb.0:
; X86-NOPOPCNT-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NOPOPCNT-NEXT: movl %eax, %ecx
; X86-NOPOPCNT-NEXT: shrl $16, %ecx
; X86-NOPOPCNT-NEXT: xorl %eax, %ecx
; X86-NOPOPCNT-NEXT: xorl %eax, %eax
; X86-NOPOPCNT-NEXT: xorb %ch, %cl
; X86-NOPOPCNT-NEXT: setnp %al
; X86-NOPOPCNT-NEXT: addl %eax, %eax
; X86-NOPOPCNT-NEXT: retl
;
; X64-NOPOPCNT-LABEL: parity_32_shift:
; X64-NOPOPCNT: # %bb.0:
; X64-NOPOPCNT-NEXT: movl %edi, %ecx
; X64-NOPOPCNT-NEXT: shrl $16, %ecx
; X64-NOPOPCNT-NEXT: xorl %edi, %ecx
; X64-NOPOPCNT-NEXT: xorl %eax, %eax
; X64-NOPOPCNT-NEXT: xorb %ch, %cl
; X64-NOPOPCNT-NEXT: setnp %al
; X64-NOPOPCNT-NEXT: addl %eax, %eax
; X64-NOPOPCNT-NEXT: retq
;
; X86-POPCNT-LABEL: parity_32_shift:
; X86-POPCNT: # %bb.0:
; X86-POPCNT-NEXT: popcntl {{[0-9]+}}(%esp), %eax
; X86-POPCNT-NEXT: andl $1, %eax
; X86-POPCNT-NEXT: addl %eax, %eax
; X86-POPCNT-NEXT: retl
;
; X64-POPCNT-LABEL: parity_32_shift:
; X64-POPCNT: # %bb.0:
; X64-POPCNT-NEXT: popcntl %edi, %eax
; X64-POPCNT-NEXT: andl $1, %eax
; X64-POPCNT-NEXT: addl %eax, %eax
; X64-POPCNT-NEXT: retq
%2 = tail call i32 @llvm.ctpop.i32(i32 %0)
%3 = shl nuw nsw i32 %2, 1
%4 = and i32 %3, 2
ret i32 %4
}
define i64 @parity_64_shift(i64 %0) {
; X86-NOPOPCNT-LABEL: parity_64_shift:
; X86-NOPOPCNT: # %bb.0:
; X86-NOPOPCNT-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NOPOPCNT-NEXT: xorl {{[0-9]+}}(%esp), %eax
; X86-NOPOPCNT-NEXT: movl %eax, %ecx
; X86-NOPOPCNT-NEXT: shrl $16, %ecx
; X86-NOPOPCNT-NEXT: xorl %eax, %ecx
; X86-NOPOPCNT-NEXT: xorl %eax, %eax
; X86-NOPOPCNT-NEXT: xorb %ch, %cl
; X86-NOPOPCNT-NEXT: setnp %al
; X86-NOPOPCNT-NEXT: addl %eax, %eax
; X86-NOPOPCNT-NEXT: xorl %edx, %edx
; X86-NOPOPCNT-NEXT: retl
;
; X64-NOPOPCNT-LABEL: parity_64_shift:
; X64-NOPOPCNT: # %bb.0:
; X64-NOPOPCNT-NEXT: movq %rdi, %rax
; X64-NOPOPCNT-NEXT: shrq $32, %rax
; X64-NOPOPCNT-NEXT: xorl %edi, %eax
; X64-NOPOPCNT-NEXT: movl %eax, %ecx
; X64-NOPOPCNT-NEXT: shrl $16, %ecx
; X64-NOPOPCNT-NEXT: xorl %eax, %ecx
; X64-NOPOPCNT-NEXT: xorl %eax, %eax
; X64-NOPOPCNT-NEXT: xorb %ch, %cl
; X64-NOPOPCNT-NEXT: setnp %al
; X64-NOPOPCNT-NEXT: addq %rax, %rax
; X64-NOPOPCNT-NEXT: retq
;
; X86-POPCNT-LABEL: parity_64_shift:
; X86-POPCNT: # %bb.0:
; X86-POPCNT-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-POPCNT-NEXT: xorl {{[0-9]+}}(%esp), %eax
; X86-POPCNT-NEXT: popcntl %eax, %eax
; X86-POPCNT-NEXT: andl $1, %eax
; X86-POPCNT-NEXT: addl %eax, %eax
; X86-POPCNT-NEXT: xorl %edx, %edx
; X86-POPCNT-NEXT: retl
;
; X64-POPCNT-LABEL: parity_64_shift:
; X64-POPCNT: # %bb.0:
; X64-POPCNT-NEXT: popcntq %rdi, %rax
; X64-POPCNT-NEXT: andl $1, %eax
; X64-POPCNT-NEXT: addq %rax, %rax
; X64-POPCNT-NEXT: retq
%2 = tail call i64 @llvm.ctpop.i64(i64 %0)
%3 = shl nuw nsw i64 %2, 1
%4 = and i64 %3, 2
ret i64 %4
}
declare i4 @llvm.ctpop.i4(i4 %x)
declare i8 @llvm.ctpop.i8(i8 %x)
declare i16 @llvm.ctpop.i16(i16 %x)