From 43a6c5e2fccadb299c35cb3147d112f706922acd Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Tue, 31 Aug 2010 22:41:22 +0000 Subject: [PATCH] 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 --- lib/Target/ARM/ARMISelDAGToDAG.cpp | 97 +++++++++++++++++++++++++++++ lib/Target/ARM/ARMInstrInfo.td | 8 +++ lib/Target/ARM/ARMInstrThumb2.td | 12 ++++ test/CodeGen/ARM/mvncc.ll | 12 ++++ test/CodeGen/Thumb2/thumb2-mvncc.ll | 13 ++++ 5 files changed, 142 insertions(+) create mode 100644 test/CodeGen/ARM/mvncc.ll create mode 100644 test/CodeGen/Thumb2/thumb2-mvncc.ll diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index 4f4f4d08b41..0353ea920a8 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(TrueVal); + if (!True || True->getZExtValue() != 0) + return 0; + + // Bail if the false value isn't an immediate. + ConstantSDNode *False = dyn_cast(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(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 e66f9b9ad0a..d1d4390db07 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 6ba0a44be47..270eeb5fdaf 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 opcod, dag oops, dag iops, InstrItinClass itin, string opc, string asm, list pattern> : T2I { diff --git a/test/CodeGen/ARM/mvncc.ll b/test/CodeGen/ARM/mvncc.ll new file mode 100644 index 00000000000..4003a95c62b --- /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 00000000000..893728b512d --- /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 +} -- 2.34.1