From d8217b23ffb95e1affbe47f6d54f695d0eebfb30 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 24 Dec 2018 05:27:13 +0000 Subject: [PATCH] [X86] Move the optimization that turns 'CMP (AND+IMM64), 0' into SRL/SHL+TEST to X86ISelDAGToDAG. This cleans more code out of EmitTest. llvm-svn: 350041 --- llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 75 +++++++++++++++++++++++++++++++-- llvm/lib/Target/X86/X86ISelLowering.cpp | 55 ++---------------------- 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp index 90fc39f..7e171d7 100644 --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -473,6 +473,7 @@ namespace { bool tryOptimizeRem8Extend(SDNode *N); + bool onlyUsesZeroFlag(SDValue Flags) const; bool hasNoSignFlagUses(SDValue Flags) const; bool hasNoCarryFlagUses(SDValue Flags) const; }; @@ -2250,6 +2251,42 @@ static X86::CondCode getCondFromOpc(unsigned Opc) { return CC; } +/// Test whether the given X86ISD::CMP node has any users that use a flag +/// other than ZF. +bool X86DAGToDAGISel::onlyUsesZeroFlag(SDValue Flags) const { + // Examine each user of the node. + for (SDNode::use_iterator UI = Flags->use_begin(), UE = Flags->use_end(); + UI != UE; ++UI) { + // Only check things that use the flags. + if (UI.getUse().getResNo() != Flags.getResNo()) + continue; + // Only examine CopyToReg uses that copy to EFLAGS. + if (UI->getOpcode() != ISD::CopyToReg || + cast(UI->getOperand(1))->getReg() != X86::EFLAGS) + return false; + // Examine each user of the CopyToReg use. + for (SDNode::use_iterator FlagUI = UI->use_begin(), + FlagUE = UI->use_end(); FlagUI != FlagUE; ++FlagUI) { + // Only examine the Flag result. + if (FlagUI.getUse().getResNo() != 1) continue; + // Anything unusual: assume conservatively. + if (!FlagUI->isMachineOpcode()) return false; + // Examine the condition code of the user. + X86::CondCode CC = getCondFromOpc(FlagUI->getMachineOpcode()); + + switch (CC) { + // Comparisons which only use the zero flag. + case X86::COND_E: case X86::COND_NE: + continue; + // Anything else: assume conservatively. + default: + return false; + } + } + } + return true; +} + /// Test whether the given X86ISD::CMP node has any uses which require the SF /// flag to be accurate. bool X86DAGToDAGISel::hasNoSignFlagUses(SDValue Flags) const { @@ -3685,6 +3722,10 @@ void X86DAGToDAGISel::Select(SDNode *Node) { SDValue N0 = Node->getOperand(0); SDValue N1 = Node->getOperand(1); + // Optimizations for TEST compares. + if (!isNullConstant(N1)) + break; + // Save the original VT of the compare. MVT CmpVT = N0.getSimpleValueType(); @@ -3692,7 +3733,7 @@ void X86DAGToDAGISel::Select(SDNode *Node) { // by a test instruction. The test should be removed later by // analyzeCompare if we are using only the zero flag. // TODO: Should we check the users and use the BEXTR flags directly? - if (isNullConstant(N1) && N0.getOpcode() == ISD::AND && N0.hasOneUse()) { + if (N0.getOpcode() == ISD::AND && N0.hasOneUse()) { if (MachineSDNode *NewNode = matchBEXTRFromAndImm(N0.getNode())) { unsigned TestOpc = CmpVT == MVT::i64 ? X86::TEST64rr : X86::TEST32rr; @@ -3713,12 +3754,40 @@ void X86DAGToDAGISel::Select(SDNode *Node) { // Look past the truncate if CMP is the only use of it. if (N0.getOpcode() == ISD::AND && N0.getNode()->hasOneUse() && - N0.getValueType() != MVT::i8 && - isNullConstant(N1)) { + N0.getValueType() != MVT::i8) { ConstantSDNode *C = dyn_cast(N0.getOperand(1)); if (!C) break; uint64_t Mask = C->getZExtValue(); + // Check if we can replace AND+IMM64 with a shift. This is possible for + // masks/ like 0xFF000000 or 0x00FFFFFF and if we care only about the zero + // flag. + if (CmpVT == MVT::i64 && !isInt<32>(Mask) && + onlyUsesZeroFlag(SDValue(Node, 0))) { + if (isMask_64(~Mask)) { + unsigned TrailingZeros = countTrailingZeros(Mask); + SDValue Imm = CurDAG->getTargetConstant(TrailingZeros, dl, MVT::i64); + SDValue Shift = + SDValue(CurDAG->getMachineNode(X86::SHR64ri, dl, MVT::i64, + N0.getOperand(0), Imm), 0); + MachineSDNode *Test = CurDAG->getMachineNode(X86::TEST64rr, dl, + MVT::i32, Shift, Shift); + ReplaceNode(Node, Test); + return; + } + if (isMask_64(Mask)) { + unsigned LeadingZeros = countLeadingZeros(Mask); + SDValue Imm = CurDAG->getTargetConstant(LeadingZeros, dl, MVT::i64); + SDValue Shift = + SDValue(CurDAG->getMachineNode(X86::SHL64ri, dl, MVT::i64, + N0.getOperand(0), Imm), 0); + MachineSDNode *Test = CurDAG->getMachineNode(X86::TEST64rr, dl, + MVT::i32, Shift, Shift); + ReplaceNode(Node, Test); + return; + } + } + MVT VT; int SubRegOp; unsigned ROpc, MOpc; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index c2230ee..85bcb8d 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -18690,59 +18690,10 @@ static SDValue EmitTest(SDValue Op, unsigned X86CC, const SDLoc &dl, case ISD::AND: // If the primary 'and' result isn't used, don't bother using X86ISD::AND, - // because a TEST instruction will be better. However, AND should be - // preferred if the instruction can be combined into ANDN. - if (!hasNonFlagsUse(Op)) { - SDValue Op0 = ArithOp->getOperand(0); - SDValue Op1 = ArithOp->getOperand(1); - EVT VT = ArithOp.getValueType(); - - // Check if we can replace AND+IMM64 with a shift before giving up. This - // is possible for masks/ like 0xFF000000 or 0x00FFFFFF and if we care - // only about the zero flag. - if (!ZeroCheck) - break; - - // And with constant should be canonicalized unless we're dealing - // with opaque constants. - assert((!isa(Op0) || - (isa(Op1) && - (cast(Op0)->isOpaque() || - cast(Op1)->isOpaque()))) && - "AND node isn't canonicalized"); - auto *CN = dyn_cast(Op1); - if (!CN) - break; - - const APInt &Mask = CN->getAPIntValue(); - if (Mask.isSignedIntN(ShiftToAndMaxMaskWidth)) - break; // Prefer TEST instruction. - - unsigned BitWidth = Mask.getBitWidth(); - unsigned LeadingOnes = Mask.countLeadingOnes(); - unsigned TrailingZeros = Mask.countTrailingZeros(); - - if (LeadingOnes + TrailingZeros == BitWidth) { - assert(TrailingZeros < VT.getSizeInBits() && - "Shift amount should be less than the type width"); - SDValue ShAmt = DAG.getConstant(TrailingZeros, dl, MVT::i8); - Op = DAG.getNode(ISD::SRL, dl, VT, Op0, ShAmt); - break; - } - - unsigned LeadingZeros = Mask.countLeadingZeros(); - unsigned TrailingOnes = Mask.countTrailingOnes(); - - if (LeadingZeros + TrailingOnes == BitWidth) { - assert(LeadingZeros < VT.getSizeInBits() && - "Shift amount should be less than the type width"); - SDValue ShAmt = DAG.getConstant(LeadingZeros, dl, MVT::i8); - Op = DAG.getNode(ISD::SHL, dl, VT, Op0, ShAmt); - break; - } - + // because a TEST instruction will be better. + if (!hasNonFlagsUse(Op)) break; - } + LLVM_FALLTHROUGH; case ISD::SUB: case ISD::OR: -- 2.7.4