OSDN Git Service

[X86] Move the optimization that turns 'CMP (AND+IMM64), 0' into SRL/SHL+TEST to...
authorCraig Topper <craig.topper@intel.com>
Mon, 24 Dec 2018 05:27:13 +0000 (05:27 +0000)
committerCraig Topper <craig.topper@intel.com>
Mon, 24 Dec 2018 05:27:13 +0000 (05:27 +0000)
This cleans more code out of EmitTest.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@350041 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/X86/X86ISelDAGToDAG.cpp
lib/Target/X86/X86ISelLowering.cpp

index 90fc39f..7e171d7 100644 (file)
@@ -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<RegisterSDNode>(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<ConstantSDNode>(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;
index c2230ee..85bcb8d 100644 (file)
@@ -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<ConstantSDNode>(Op0) ||
-              (isa<ConstantSDNode>(Op1) &&
-               (cast<ConstantSDNode>(Op0)->isOpaque() ||
-                cast<ConstantSDNode>(Op1)->isOpaque()))) &&
-             "AND node isn't canonicalized");
-      auto *CN = dyn_cast<ConstantSDNode>(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: