aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Wendling <isanbard@gmail.com>2010-08-31 22:41:22 +0000
committerBill Wendling <isanbard@gmail.com>2010-08-31 22:41:22 +0000
commit43a6c5e2fccadb299c35cb3147d112f706922acd (patch)
tree9aa70d04e8235ebdbbe4fba3c0c6cf6006832918
parent1d76ab7f56398c1ccc000e66a78402c928a3eb6b (diff)
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! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@112664 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/Target/ARM/ARMISelDAGToDAG.cpp97
-rw-r--r--lib/Target/ARM/ARMInstrInfo.td8
-rw-r--r--lib/Target/ARM/ARMInstrThumb2.td12
-rw-r--r--test/CodeGen/ARM/mvncc.ll12
-rw-r--r--test/CodeGen/Thumb2/thumb2-mvncc.ll13
5 files changed, 142 insertions, 0 deletions
diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp
index 4f4f4d08b4..0353ea920a 100644
--- a/lib/Target/ARM/ARMISelDAGToDAG.cpp
+++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp
@@ -180,6 +180,9 @@ private:
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 @@ SelectARMCMOVShiftOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
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 @@ SelectT2CMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
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 @@ SelectARMCMOVSoImmOp(SDNode *N, SDValue FalseVal, SDValue TrueVal,
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 };
diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td
index e66f9b9ad0..d1d4390db0 100644
--- a/lib/Target/ARM/ARMInstrInfo.td
+++ b/lib/Target/ARM/ARMInstrInfo.td
@@ -2419,6 +2419,14 @@ def MOVCCi : AI1<0b1101, (outs GPR:$dst),
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
//===----------------------------------------------------------------------===//
diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td
index 6ba0a44be4..270eeb5fda 100644
--- a/lib/Target/ARM/ARMInstrThumb2.td
+++ b/lib/Target/ARM/ARMInstrThumb2.td
@@ -2195,6 +2195,18 @@ def t2MOVCCi : T2I<(outs rGPR:$dst), (ins rGPR:$false, t2_so_imm:$true),
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> {
diff --git a/test/CodeGen/ARM/mvncc.ll b/test/CodeGen/ARM/mvncc.ll
new file mode 100644
index 0000000000..4003a95c62
--- /dev/null
+++ b/test/CodeGen/ARM/mvncc.ll
@@ -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
+}
diff --git a/test/CodeGen/Thumb2/thumb2-mvncc.ll b/test/CodeGen/Thumb2/thumb2-mvncc.ll
new file mode 100644
index 0000000000..893728b512
--- /dev/null
+++ b/test/CodeGen/Thumb2/thumb2-mvncc.ll
@@ -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
+}