[llvm-commits] [llvm] r112664 - in /llvm/trunk: lib/Target/ARM/ARMISelDAGToDAG.cpp lib/Target/ARM/ARMInstrInfo.td lib/Target/ARM/ARMInstrThumb2.td test/CodeGen/ARM/mvncc.ll test/CodeGen/Thumb2/thumb2-mvncc.ll

Bill Wendling isanbard at gmail.com
Tue Aug 31 15:41:22 PDT 2010


Author: void
Date: Tue Aug 31 17:41:22 2010
New Revision: 112664

URL: http://llvm.org/viewvc/llvm-project?rev=112664&view=rev
Log:
We have a chance for an optimization. Consider this code:

int x(int t) {
  if (t & 256)
    return -26;
  return 0;
}

We generate this:

     tst.w   r0, #256
     mvn     r0, #25
     it      eq
     moveq   r0, #0

while gcc generates this:

     ands    r0, r0, #256
     it      ne
     mvnne   r0, #25
     bx      lr

Scandalous really!

During ISel time, we can look for this particular pattern. One where we have a
"MOVCC" that uses the flag off of a CMPZ that itself is comparing an AND
instruction to 0. Something like this (greatly simplified):

  %r0 = ISD::AND ...
  ARMISD::CMPZ %r0, 0         @ sets [CPSR]
  %r0 = ARMISD::MOVCC 0, -26  @ reads [CPSR]

All we have to do is convert the "ISD::AND" into an "ARM::ANDS" that sets [CPSR]
when it's zero. The zero value will all ready be in the %r0 register and we only
need to change it if the AND wasn't zero. Easy!

Added:
    llvm/trunk/test/CodeGen/ARM/mvncc.ll
    llvm/trunk/test/CodeGen/Thumb2/thumb2-mvncc.ll
Modified:
    llvm/trunk/lib/Target/ARM/ARMISelDAGToDAG.cpp
    llvm/trunk/lib/Target/ARM/ARMInstrInfo.td
    llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td

Modified: llvm/trunk/lib/Target/ARM/ARMISelDAGToDAG.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMISelDAGToDAG.cpp?rev=112664&r1=112663&r2=112664&view=diff
==============================================================================
--- llvm/trunk/lib/Target/ARM/ARMISelDAGToDAG.cpp (original)
+++ llvm/trunk/lib/Target/ARM/ARMISelDAGToDAG.cpp Tue Aug 31 17:41:22 2010
@@ -180,6 +180,9 @@
   SDNode *SelectARMCMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
                                ARMCC::CondCodes CCVal, SDValue CCR,
                                SDValue InFlag);
+  SDNode *OptimizeCMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
+                              ARMCC::CondCodes CCVal, SDValue CCR,
+                              SDValue InFlag, bool IsThumb2);
 
   SDNode *SelectConcatVector(SDNode *N);
 
@@ -1641,6 +1644,92 @@
   return 0;
 }
 
+/// OptimizeCMOVSoImmOp - It's possible to save an instruction or two be
+/// recognizing that the TST and AND instructions perform the same function
+/// (they "and" the two values). See inside for more details.
+SDNode *ARMDAGToDAGISel::
+OptimizeCMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
+                    ARMCC::CondCodes CCVal, SDValue CCR, SDValue InFlag,
+                    bool IsThumb2) {
+  // Convert:
+  //
+  //   tst.w    r0, #256
+  //   mvn      r0, #25
+  //   it       eq
+  //   moveq    r0, #0
+  //
+  // into:
+  //
+  //   ands.w   r0, r0, #256
+  //   it       ne
+  //   mvnne.w  r0, #25
+  //
+  if (InFlag.getOpcode() != ARMISD::CMPZ ||
+      InFlag.getOperand(0).getOpcode() != ISD::AND)
+    return 0;
+
+  // The true value needs to be zero, as that's the result of the AND
+  // instruction.
+  ConstantSDNode *True = dyn_cast<ConstantSDNode>(TrueVal);
+  if (!True || True->getZExtValue() != 0)
+    return 0;
+
+  // Bail if the false value isn't an immediate.
+  ConstantSDNode *False = dyn_cast<ConstantSDNode>(FalseVal);
+  if (!False)
+    return 0;
+
+  bool UseMVN = false;
+  if ((IsThumb2 && !Pred_t2_so_imm(FalseVal.getNode())) ||
+      (!IsThumb2 && !Pred_so_imm(FalseVal.getNode()))) {
+    // The false value isn't a proper immediate. Check to see if we can use the
+    // bitwise NOT version.
+    if ((IsThumb2 && ARM_AM::getT2SOImmVal(~False->getZExtValue()) != -1) ||
+        (!IsThumb2 && ARM_AM::getSOImmVal(~False->getZExtValue()) != -1)) {
+      UseMVN = true;
+      FalseVal = CurDAG->getTargetConstant(~False->getZExtValue(), MVT::i32);
+    } else {
+      return 0;
+    }
+  } else {
+    FalseVal = CurDAG->getTargetConstant(False->getZExtValue(), MVT::i32);
+  }
+
+  // A comparison against zero corresponds with the flag AND sets if the result
+  // is zero.
+  ConstantSDNode *CmpVal = dyn_cast<ConstantSDNode>(InFlag.getOperand(1));
+  if (!CmpVal || CmpVal->getZExtValue() != 0)
+    return 0;
+
+  ARMCC::CondCodes NegCC = ARMCC::getOppositeCondition(CCVal);
+  SDValue OrigAnd = InFlag.getOperand(0);
+  SDValue NewAnd =
+    CurDAG->getNode(ARMISD::AND, N->getDebugLoc(),
+                    CurDAG->getVTList(OrigAnd.getValueType(), MVT::Flag),
+                    OrigAnd->getOperand(0), OrigAnd->getOperand(1));
+
+  unsigned Opcode = !UseMVN ?
+    (IsThumb2 ? ARM::t2MOVCCi : ARM::MOVCCi) :
+    (IsThumb2 ? ARM::t2MVNCCi : ARM::MVNCCi);
+
+  SDValue Ops[] = {
+    NewAnd.getValue(0),
+    FalseVal,
+    CurDAG->getTargetConstant(NegCC, MVT::i32),
+    CCR, NewAnd.getValue(1)
+  };
+  SDNode *ResNode = CurDAG->SelectNodeTo(N, Opcode, MVT::i32, Ops, 5);
+
+  // Manually run "Select" on the newly created "ARMISD::AND" node to make
+  // sure that it's converted properly.
+  SDNode *AndNode = Select(NewAnd.getNode());
+  if (AndNode && NewAnd.getNode() != AndNode &&
+      NewAnd.getNode()->getOpcode() != ISD::DELETED_NODE)
+    ReplaceUses(NewAnd.getNode(), AndNode);
+
+  return ResNode;
+}
+
 SDNode *ARMDAGToDAGISel::
 SelectT2CMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
                     ARMCC::CondCodes CCVal, SDValue CCR, SDValue InFlag) {
@@ -1649,6 +1738,10 @@
     return 0;
 
   if (Pred_t2_so_imm(TrueVal.getNode())) {
+    SDNode *ResNode = OptimizeCMOVSoImmOp(N, FalseVal, TrueVal, CCVal, CCR,
+                                          InFlag, true);
+    if (ResNode) return ResNode;
+
     SDValue True = CurDAG->getTargetConstant(T->getZExtValue(), MVT::i32);
     SDValue CC = CurDAG->getTargetConstant(CCVal, MVT::i32);
     SDValue Ops[] = { FalseVal, True, CC, CCR, InFlag };
@@ -1666,6 +1759,10 @@
     return 0;
 
   if (Pred_so_imm(TrueVal.getNode())) {
+    SDNode *ResNode = OptimizeCMOVSoImmOp(N, FalseVal, TrueVal, CCVal, CCR,
+                                          InFlag, false);
+    if (ResNode) return ResNode;
+
     SDValue True = CurDAG->getTargetConstant(T->getZExtValue(), MVT::i32);
     SDValue CC = CurDAG->getTargetConstant(CCVal, MVT::i32);
     SDValue Ops[] = { FalseVal, True, CC, CCR, InFlag };

Modified: llvm/trunk/lib/Target/ARM/ARMInstrInfo.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMInstrInfo.td?rev=112664&r1=112663&r2=112664&view=diff
==============================================================================
--- llvm/trunk/lib/Target/ARM/ARMInstrInfo.td (original)
+++ llvm/trunk/lib/Target/ARM/ARMInstrInfo.td Tue Aug 31 17:41:22 2010
@@ -2419,6 +2419,14 @@
                 RegConstraint<"$false = $dst">, UnaryDP {
   let Inst{25} = 1;
 }
+
+def MVNCCi : AI1<0b1111, (outs GPR:$dst),
+                        (ins GPR:$false, so_imm:$true), DPFrm, IIC_iCMOVi,
+                "mvn", "\t$dst, $true",
+  [/*(set GPR:$dst, (ARMcmov GPR:$false,so_imm_not:$true,imm:$cc,CCR:$ccr))*/]>,
+                RegConstraint<"$false = $dst">, UnaryDP {
+  let Inst{25} = 0;
+}
 } // neverHasSideEffects
 
 //===----------------------------------------------------------------------===//

Modified: llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td?rev=112664&r1=112663&r2=112664&view=diff
==============================================================================
--- llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td (original)
+++ llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td Tue Aug 31 17:41:22 2010
@@ -2195,6 +2195,18 @@
   let Inst{15} = 0;
 }
 
+def t2MVNCCi : T2I<(outs rGPR:$dst), (ins rGPR:$false, t2_so_imm:$true),
+                   IIC_iCMOVi, "mvn", ".w\t$dst, $true",
+[/*(set rGPR:$dst,(ARMcmov rGPR:$false,t2_so_imm_not:$true,imm:$cc,CCR:$ccr))*/]>,
+                   RegConstraint<"$false = $dst"> {
+  let Inst{31-27} = 0b11110;
+  let Inst{25} = 0;
+  let Inst{24-21} = 0b0011;
+  let Inst{20} = 0; // The S bit.
+  let Inst{19-16} = 0b1111; // Rn
+  let Inst{15} = 0;
+}
+
 class T2I_movcc_sh<bits<2> opcod, dag oops, dag iops, InstrItinClass itin,
                    string opc, string asm, list<dag> pattern>
   : T2I<oops, iops, itin, opc, asm, pattern> {

Added: llvm/trunk/test/CodeGen/ARM/mvncc.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/mvncc.ll?rev=112664&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/ARM/mvncc.ll (added)
+++ llvm/trunk/test/CodeGen/ARM/mvncc.ll Tue Aug 31 17:41:22 2010
@@ -0,0 +1,12 @@
+; RUN: llc < %s -mtriple=arm-apple-darwin | FileCheck %s
+
+define i32 @f1(i32 %t) nounwind {
+; CHECK: f1
+; CHECK-NOT: tst
+; CHECK: and
+; CHECK: mvnne
+  %and = and i32 %t, 256
+  %tobool = icmp eq i32 %and, 0
+  %retval.0 = select i1 %tobool, i32 0, i32 -26
+  ret i32 %retval.0
+}

Added: llvm/trunk/test/CodeGen/Thumb2/thumb2-mvncc.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/Thumb2/thumb2-mvncc.ll?rev=112664&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/Thumb2/thumb2-mvncc.ll (added)
+++ llvm/trunk/test/CodeGen/Thumb2/thumb2-mvncc.ll Tue Aug 31 17:41:22 2010
@@ -0,0 +1,13 @@
+; RUN: llc < %s -mtriple=thumbv7-apple-darwin | FileCheck %s
+
+define i32 @f1(i32 %t) nounwind {
+; CHECK: f1
+; CHECK-NOT: tst
+; CHECK: ands
+; CHECK: it ne
+; CHECK: mvnne
+  %and = and i32 %t, 256
+  %tobool = icmp eq i32 %and, 0
+  %retval.0 = select i1 %tobool, i32 0, i32 -26
+  ret i32 %retval.0
+}





More information about the llvm-commits mailing list