From e88d5cee9d6b02bc786df806395a718464908064 Mon Sep 17 00:00:00 2001 From: Evan Cheng Date: Thu, 2 Jul 2009 07:28:31 +0000 Subject: [PATCH] Thumb2 pre/post indexed loads. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@74696 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMISelDAGToDAG.cpp | 71 ++++++++++++++- lib/Target/ARM/ARMISelLowering.cpp | 95 +++++++++++++++------ lib/Target/ARM/ARMInstrFormats.td | 12 +++ lib/Target/ARM/ARMInstrThumb2.td | 48 +++++++++-- lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp | 12 +++ test/CodeGen/Thumb2/thumb2-ldr_post.ll | 12 +++ test/CodeGen/Thumb2/thumb2-ldr_pre.ll | 19 +++++ 7 files changed, 235 insertions(+), 34 deletions(-) create mode 100644 test/CodeGen/Thumb2/thumb2-ldr_post.ll create mode 100644 test/CodeGen/Thumb2/thumb2-ldr_pre.ll diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index da327e4b080..4b73ba21d74 100644 --- a/lib/Target/ARM/ARMISelDAGToDAG.cpp +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -102,6 +102,8 @@ public: SDValue &OffImm); bool SelectT2AddrModeImm8(SDValue Op, SDValue N, SDValue &Base, SDValue &OffImm); + bool SelectT2AddrModeImm8Offset(SDValue Op, SDValue N, + SDValue &OffImm); bool SelectT2AddrModeImm8s4(SDValue Op, SDValue N, SDValue &Base, SDValue &OffImm); bool SelectT2AddrModeSoReg(SDValue Op, SDValue N, SDValue &Base, @@ -111,7 +113,11 @@ public: #include "ARMGenDAGISel.inc" private: + /// SelectARMIndexedLoad - Indexed (pre/post inc/dec) load matching code for + /// ARM. SDNode *SelectARMIndexedLoad(SDValue Op); + SDNode *SelectT2IndexedLoad(SDValue Op); + /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. @@ -628,6 +634,25 @@ bool ARMDAGToDAGISel::SelectT2AddrModeImm8(SDValue Op, SDValue N, return false; } +bool ARMDAGToDAGISel::SelectT2AddrModeImm8Offset(SDValue Op, SDValue N, + SDValue &OffImm){ + unsigned Opcode = Op.getOpcode(); + ISD::MemIndexedMode AM = (Opcode == ISD::LOAD) + ? cast(Op)->getAddressingMode() + : cast(Op)->getAddressingMode(); + if (ConstantSDNode *RHS = dyn_cast(N)) { + int RHSC = (int)RHS->getZExtValue(); + if (RHSC >= 0 && RHSC < 0x100) { // 8 bits. + OffImm = (AM == ISD::PRE_INC) + ? CurDAG->getTargetConstant(RHSC, MVT::i32) + : CurDAG->getTargetConstant(-RHSC, MVT::i32); + return true; + } + } + + return false; +} + bool ARMDAGToDAGISel::SelectT2AddrModeImm8s4(SDValue Op, SDValue N, SDValue &Base, SDValue &OffImm) { if (N.getOpcode() == ISD::ADD) { @@ -762,6 +787,46 @@ SDNode *ARMDAGToDAGISel::SelectARMIndexedLoad(SDValue Op) { return NULL; } +SDNode *ARMDAGToDAGISel::SelectT2IndexedLoad(SDValue Op) { + LoadSDNode *LD = cast(Op); + ISD::MemIndexedMode AM = LD->getAddressingMode(); + if (AM == ISD::UNINDEXED) + return NULL; + + MVT LoadedVT = LD->getMemoryVT(); + SDValue Offset; + bool isPre = (AM == ISD::PRE_INC) || (AM == ISD::PRE_DEC); + unsigned Opcode = 0; + bool Match = false; + if (SelectT2AddrModeImm8Offset(Op, LD->getOffset(), Offset)) { + switch (LoadedVT.getSimpleVT()) { + case MVT::i32: + Opcode = isPre ? ARM::t2LDR_PRE : ARM::t2LDR_POST; + break; + case MVT::i16: + Opcode = isPre ? ARM::t2LDRH_PRE : ARM::t2LDRH_POST; + break; + case MVT::i8: + Opcode = isPre ? ARM::t2LDRB_PRE : ARM::t2LDRB_POST; + break; + default: + return NULL; + } + Match = true; + } + + if (Match) { + SDValue Chain = LD->getChain(); + SDValue Base = LD->getBasePtr(); + SDValue Ops[]= { Base, Offset, getAL(CurDAG), + CurDAG->getRegister(0, MVT::i32), Chain }; + return CurDAG->getTargetNode(Opcode, Op.getDebugLoc(), MVT::i32, MVT::i32, + MVT::Other, Ops, 5); + } + + return NULL; +} + SDNode *ARMDAGToDAGISel::Select(SDValue Op) { SDNode *N = Op.getNode(); @@ -892,7 +957,11 @@ SDNode *ARMDAGToDAGISel::Select(SDValue Op) { return CurDAG->getTargetNode(ARM::SMULL, dl, MVT::i32, MVT::i32, Ops, 5); } case ISD::LOAD: { - SDNode *ResNode = SelectARMIndexedLoad(Op); + SDNode *ResNode = 0; + if (Subtarget->isThumb2()) + ResNode = SelectT2IndexedLoad(Op); + else + ResNode = SelectARMIndexedLoad(Op); if (ResNode) return ResNode; // Other cases are autogenerated. diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp index 2d115cba487..85cd2e13ce4 100644 --- a/lib/Target/ARM/ARMISelLowering.cpp +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -231,16 +231,18 @@ ARMTargetLowering::ARMTargetLowering(TargetMachine &TM) setLoadExtAction(ISD::SEXTLOAD, MVT::i1, Promote); // ARM supports all 4 flavors of integer indexed load / store. - for (unsigned im = (unsigned)ISD::PRE_INC; - im != (unsigned)ISD::LAST_INDEXED_MODE; ++im) { - setIndexedLoadAction(im, MVT::i1, Legal); - setIndexedLoadAction(im, MVT::i8, Legal); - setIndexedLoadAction(im, MVT::i16, Legal); - setIndexedLoadAction(im, MVT::i32, Legal); - setIndexedStoreAction(im, MVT::i1, Legal); - setIndexedStoreAction(im, MVT::i8, Legal); - setIndexedStoreAction(im, MVT::i16, Legal); - setIndexedStoreAction(im, MVT::i32, Legal); + if (!Subtarget->isThumb1Only()) { + for (unsigned im = (unsigned)ISD::PRE_INC; + im != (unsigned)ISD::LAST_INDEXED_MODE; ++im) { + setIndexedLoadAction(im, MVT::i1, Legal); + setIndexedLoadAction(im, MVT::i8, Legal); + setIndexedLoadAction(im, MVT::i16, Legal); + setIndexedLoadAction(im, MVT::i32, Legal); + setIndexedStoreAction(im, MVT::i1, Legal); + setIndexedStoreAction(im, MVT::i8, Legal); + setIndexedStoreAction(im, MVT::i16, Legal); + setIndexedStoreAction(im, MVT::i32, Legal); + } } // i64 operation support. @@ -2923,10 +2925,10 @@ bool ARMTargetLowering::isLegalAddressingMode(const AddrMode &AM, return true; } -static bool getIndexedAddressParts(SDNode *Ptr, MVT VT, - bool isSEXTLoad, SDValue &Base, - SDValue &Offset, bool &isInc, - SelectionDAG &DAG) { +static bool getARMIndexedAddressParts(SDNode *Ptr, MVT VT, + bool isSEXTLoad, SDValue &Base, + SDValue &Offset, bool &isInc, + SelectionDAG &DAG) { if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB) return false; @@ -2936,6 +2938,7 @@ static bool getIndexedAddressParts(SDNode *Ptr, MVT VT, if (ConstantSDNode *RHS = dyn_cast(Ptr->getOperand(1))) { int RHSC = (int)RHS->getZExtValue(); if (RHSC < 0 && RHSC > -256) { + assert(Ptr->getOpcode() == ISD::ADD); isInc = false; Offset = DAG.getConstant(-RHSC, RHS->getValueType(0)); return true; @@ -2949,6 +2952,7 @@ static bool getIndexedAddressParts(SDNode *Ptr, MVT VT, if (ConstantSDNode *RHS = dyn_cast(Ptr->getOperand(1))) { int RHSC = (int)RHS->getZExtValue(); if (RHSC < 0 && RHSC > -0x1000) { + assert(Ptr->getOpcode() == ISD::ADD); isInc = false; Offset = DAG.getConstant(-RHSC, RHS->getValueType(0)); Base = Ptr->getOperand(0); @@ -2979,6 +2983,31 @@ static bool getIndexedAddressParts(SDNode *Ptr, MVT VT, return false; } +static bool getT2IndexedAddressParts(SDNode *Ptr, MVT VT, + bool isSEXTLoad, SDValue &Base, + SDValue &Offset, bool &isInc, + SelectionDAG &DAG) { + if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB) + return false; + + Base = Ptr->getOperand(0); + if (ConstantSDNode *RHS = dyn_cast(Ptr->getOperand(1))) { + int RHSC = (int)RHS->getZExtValue(); + if (RHSC < 0 && RHSC > -0x100) { // 8 bits. + assert(Ptr->getOpcode() == ISD::ADD); + isInc = false; + Offset = DAG.getConstant(-RHSC, RHS->getValueType(0)); + return true; + } else if (RHSC > 0 && RHSC < 0x100) { // 8 bit, no zero. + isInc = Ptr->getOpcode() == ISD::ADD; + Offset = DAG.getConstant(RHSC, RHS->getValueType(0)); + return true; + } + } + + return false; +} + /// getPreIndexedAddressParts - returns true by value, base pointer and /// offset pointer and addressing mode by reference if the node's address /// can be legally represented as pre-indexed load / store address. @@ -2987,7 +3016,7 @@ ARMTargetLowering::getPreIndexedAddressParts(SDNode *N, SDValue &Base, SDValue &Offset, ISD::MemIndexedMode &AM, SelectionDAG &DAG) const { - if (Subtarget->isThumb()) + if (Subtarget->isThumb1Only()) return false; MVT VT; @@ -3004,13 +3033,18 @@ ARMTargetLowering::getPreIndexedAddressParts(SDNode *N, SDValue &Base, return false; bool isInc; - bool isLegal = getIndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base, + bool isLegal = false; + if (Subtarget->isThumb2()) + isLegal = getT2IndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base, + Offset, isInc, DAG); + else + isLegal = getARMIndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base, Offset, isInc, DAG); - if (isLegal) { - AM = isInc ? ISD::PRE_INC : ISD::PRE_DEC; - return true; - } - return false; + if (!isLegal) + return false; + + AM = isInc ? ISD::PRE_INC : ISD::PRE_DEC; + return true; } /// getPostIndexedAddressParts - returns true by value, base pointer and @@ -3021,7 +3055,7 @@ bool ARMTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, SDValue &Offset, ISD::MemIndexedMode &AM, SelectionDAG &DAG) const { - if (Subtarget->isThumb()) + if (Subtarget->isThumb1Only()) return false; MVT VT; @@ -3036,13 +3070,18 @@ bool ARMTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, return false; bool isInc; - bool isLegal = getIndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset, + bool isLegal = false; + if (Subtarget->isThumb2()) + isLegal = getT2IndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset, isInc, DAG); - if (isLegal) { - AM = isInc ? ISD::POST_INC : ISD::POST_DEC; - return true; - } - return false; + else + isLegal = getARMIndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset, + isInc, DAG); + if (!isLegal) + return false; + + AM = isInc ? ISD::POST_INC : ISD::POST_DEC; + return true; } void ARMTargetLowering::computeMaskedBitsForTargetNode(const SDValue Op, diff --git a/lib/Target/ARM/ARMInstrFormats.td b/lib/Target/ARM/ARMInstrFormats.td index d7b02c3a4ec..301a6c1a5cc 100644 --- a/lib/Target/ARM/ARMInstrFormats.td +++ b/lib/Target/ARM/ARMInstrFormats.td @@ -873,6 +873,18 @@ class T2XI pattern> class T2JTI pattern> : Thumb2XI; +// T2Iidxldst - Thumb2 indexed load / store instructions. +class T2Iidxldst pattern> + : InstARM { + let OutOperandList = oops; + let InOperandList = !con(iops, (ops pred:$p)); + let AsmString = !strconcat(opc, !strconcat("${p}", asm)); + let Pattern = pattern; + list Predicates = [IsThumb2]; +} + + // T2Pat - Same as Pat<>, but requires that the compiler be in Thumb2 mode. class T2Pat : Pat { list Predicates = [IsThumb2]; diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 526c31286fb..127b274072e 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -89,7 +89,6 @@ def imm0_65535 : PatLeaf<(i32 imm), [{ return (uint32_t)N->getZExtValue() < 65536; }]>; - /// bf_inv_mask_imm predicate - An AND mask to clear an arbitrary width bitfield /// e.g., 0xf000ffff def bf_inv_mask_imm : Operand, @@ -136,13 +135,17 @@ def t2addrmode_imm12 : Operand, let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } -// t2addrmode_imm8 := reg - imm8 (also reg + imm8 for some instructions) +// t2addrmode_imm8 := reg - imm8 def t2addrmode_imm8 : Operand, ComplexPattern { let PrintMethod = "printT2AddrModeImm8Operand"; let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } +def t2am_imm8_offset : Operand { + let PrintMethod = "printT2AddrModeImm8OffsetOperand"; +} + // t2addrmode_imm8s4 := reg + (imm8 << 2) def t2addrmode_imm8s4 : Operand, ComplexPattern { @@ -541,10 +544,45 @@ def : T2Pat<(extloadi16 t2addrmode_so_reg:$addr), def : T2Pat<(extloadi16 (ARMWrapper tconstpool:$addr)), (t2LDRHpci tconstpool:$addr)>; +// Indexed loads +def t2LDR_PRE : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins t2addrmode_imm8:$addr), + AddrModeT2_i8, IndexModePre, + "ldr", " $dst, $addr!", "$addr.base = $base_wb", + []>; + +def t2LDR_POST : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins GPR:$base, t2am_imm8_offset:$offset), + AddrModeT2_i8, IndexModePost, + "ldr", " $dst, [$base], $offset", "$base = $base_wb", + []>; + +def t2LDRB_PRE : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins t2addrmode_imm8:$addr), + AddrModeT2_i8, IndexModePre, + "ldrb", " $dst, $addr!", "$addr.base = $base_wb", + []>; +def t2LDRB_POST : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins GPR:$base, t2am_imm8_offset:$offset), + AddrModeT2_i8, IndexModePost, + "ldrb", " $dst, [$base], $offset", "$base = $base_wb", + []>; + +def t2LDRH_PRE : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins t2addrmode_imm8:$addr), + AddrModeT2_i8, IndexModePre, + "ldrh", " $dst, $addr!", "$addr.base = $base_wb", + []>; +def t2LDRH_POST : T2Iidxldst<(outs GPR:$dst, GPR:$base_wb), + (ins GPR:$base, t2am_imm8_offset:$offset), + AddrModeT2_i8, IndexModePost, + "ldrh", " $dst, [$base], $offset", "$base = $base_wb", + []>; + // Store -defm t2STR : T2I_st<"str", BinOpFrag<(store node:$LHS, node:$RHS)>>; -defm t2STRB : T2I_st<"strb", BinOpFrag<(truncstorei8 node:$LHS, node:$RHS)>>; -defm t2STRH : T2I_st<"strh", BinOpFrag<(truncstorei16 node:$LHS, node:$RHS)>>; +defm t2STR : T2I_st<"str", BinOpFrag<(store node:$LHS, node:$RHS)>>; +defm t2STRB : T2I_st<"strb", BinOpFrag<(truncstorei8 node:$LHS, node:$RHS)>>; +defm t2STRH : T2I_st<"strh", BinOpFrag<(truncstorei16 node:$LHS, node:$RHS)>>; // Store doubleword let mayLoad = 1 in diff --git a/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp b/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp index 20a1a93c05f..434a19abef6 100644 --- a/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp +++ b/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp @@ -122,6 +122,7 @@ namespace { void printT2SOOperand(const MachineInstr *MI, int OpNum); void printT2AddrModeImm12Operand(const MachineInstr *MI, int OpNum); void printT2AddrModeImm8Operand(const MachineInstr *MI, int OpNum); + void printT2AddrModeImm8OffsetOperand(const MachineInstr *MI, int OpNum); void printT2AddrModeSoRegOperand(const MachineInstr *MI, int OpNum); void printPredicateOperand(const MachineInstr *MI, int OpNum); @@ -747,6 +748,17 @@ void ARMAsmPrinter::printT2AddrModeImm8Operand(const MachineInstr *MI, O << "]"; } +void ARMAsmPrinter::printT2AddrModeImm8OffsetOperand(const MachineInstr *MI, + int OpNum) { + const MachineOperand &MO1 = MI->getOperand(OpNum); + int32_t OffImm = (int32_t)MO1.getImm(); + // Don't print +0. + if (OffImm < 0) + O << "#-" << -OffImm; + else if (OffImm > 0) + O << "#+" << OffImm; +} + void ARMAsmPrinter::printT2AddrModeSoRegOperand(const MachineInstr *MI, int OpNum) { const MachineOperand &MO1 = MI->getOperand(OpNum); diff --git a/test/CodeGen/Thumb2/thumb2-ldr_post.ll b/test/CodeGen/Thumb2/thumb2-ldr_post.ll new file mode 100644 index 00000000000..79ffa829352 --- /dev/null +++ b/test/CodeGen/Thumb2/thumb2-ldr_post.ll @@ -0,0 +1,12 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | \ +; RUN: grep {ldr.*\\\[.*\],} | count 1 + +define i32 @test(i32 %a, i32 %b, i32 %c) { + %tmp1 = mul i32 %a, %b ; [#uses=2] + %tmp2 = inttoptr i32 %tmp1 to i32* ; [#uses=1] + %tmp3 = load i32* %tmp2 ; [#uses=1] + %tmp4 = sub i32 %tmp1, 8 ; [#uses=1] + %tmp5 = mul i32 %tmp4, %tmp3 ; [#uses=1] + ret i32 %tmp5 +} + diff --git a/test/CodeGen/Thumb2/thumb2-ldr_pre.ll b/test/CodeGen/Thumb2/thumb2-ldr_pre.ll new file mode 100644 index 00000000000..7738fd74d37 --- /dev/null +++ b/test/CodeGen/Thumb2/thumb2-ldr_pre.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | \ +; RUN: grep {ldr.*\\!} | count 2 + +define i32* @test1(i32* %X, i32* %dest) { + %Y = getelementptr i32* %X, i32 4 ; [#uses=2] + %A = load i32* %Y ; [#uses=1] + store i32 %A, i32* %dest + ret i32* %Y +} + +define i32 @test2(i32 %a, i32 %b) { + %tmp1 = sub i32 %a, 64 ; [#uses=2] + %tmp2 = inttoptr i32 %tmp1 to i32* ; [#uses=1] + %tmp3 = load i32* %tmp2 ; [#uses=1] + %tmp4 = sub i32 %tmp1, %b ; [#uses=1] + %tmp5 = add i32 %tmp4, %tmp3 ; [#uses=1] + ret i32 %tmp5 +} +