diff options
Diffstat (limited to 'lib/Target/ARM/ARMISelLowering.cpp')
-rw-r--r-- | lib/Target/ARM/ARMISelLowering.cpp | 1414 |
1 files changed, 1414 insertions, 0 deletions
diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp new file mode 100644 index 0000000000..f4cba4b336 --- /dev/null +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -0,0 +1,1414 @@ +//===-- ARMISelLowering.cpp - ARM DAG Lowering Implementation -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that ARM uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMAddressingModes.h" +#include "ARMConstantPoolValue.h" +#include "ARMISelLowering.h" +#include "ARMMachineFunctionInfo.h" +#include "ARMRegisterInfo.h" +#include "ARMSubtarget.h" +#include "ARMTargetMachine.h" +#include "llvm/CallingConv.h" +#include "llvm/Constants.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SSARegMap.h" +#include "llvm/ADT/VectorExtras.h" +using namespace llvm; + +ARMTargetLowering::ARMTargetLowering(TargetMachine &TM) + : TargetLowering(TM), ARMPCLabelIndex(0) { + Subtarget = &TM.getSubtarget<ARMSubtarget>(); + + // Uses VFP for Thumb libfuncs if available. + if (Subtarget->isThumb() && Subtarget->hasVFP2()) { + // Single-precision floating-point arithmetic. + setLibcallName(RTLIB::ADD_F32, "__addsf3vfp"); + setLibcallName(RTLIB::SUB_F32, "__subsf3vfp"); + setLibcallName(RTLIB::MUL_F32, "__mulsf3vfp"); + setLibcallName(RTLIB::DIV_F32, "__divsf3vfp"); + + // Double-precision floating-point arithmetic. + setLibcallName(RTLIB::ADD_F64, "__adddf3vfp"); + setLibcallName(RTLIB::SUB_F64, "__subdf3vfp"); + setLibcallName(RTLIB::MUL_F64, "__muldf3vfp"); + setLibcallName(RTLIB::DIV_F64, "__divdf3vfp"); + + // Single-precision comparisons. + setLibcallName(RTLIB::OEQ_F32, "__eqsf2vfp"); + setLibcallName(RTLIB::UNE_F32, "__nesf2vfp"); + setLibcallName(RTLIB::OLT_F32, "__ltsf2vfp"); + setLibcallName(RTLIB::OLE_F32, "__lesf2vfp"); + setLibcallName(RTLIB::OGE_F32, "__gesf2vfp"); + setLibcallName(RTLIB::OGT_F32, "__gtsf2vfp"); + setLibcallName(RTLIB::UO_F32, "__unordsf2vfp"); + + // Double-precision comparisons. + setLibcallName(RTLIB::OEQ_F64, "__eqdf2vfp"); + setLibcallName(RTLIB::UNE_F64, "__nedf2vfp"); + setLibcallName(RTLIB::OLT_F64, "__ltdf2vfp"); + setLibcallName(RTLIB::OLE_F64, "__ledf2vfp"); + setLibcallName(RTLIB::OGE_F64, "__gedf2vfp"); + setLibcallName(RTLIB::OGT_F64, "__gtdf2vfp"); + setLibcallName(RTLIB::UO_F64, "__unorddf2vfp"); + + // Floating-point to integer conversions. + // i64 conversions are done via library routines even when generating VFP + // instructions, so use the same ones. + setLibcallName(RTLIB::FPTOSINT_F64_I32, "__fixdfsivfp"); + setLibcallName(RTLIB::FPTOUINT_F64_I32, "__fixunsdfsivfp"); + setLibcallName(RTLIB::FPTOSINT_F32_I32, "__fixsfsivfp"); + setLibcallName(RTLIB::FPTOUINT_F32_I32, "__fixunssfsivfp"); + + // Conversions between floating types. + setLibcallName(RTLIB::FPROUND_F64_F32, "__truncdfsf2vfp"); + setLibcallName(RTLIB::FPEXT_F32_F64, "__extendsfdf2vfp"); + + // Integer to floating-point conversions. + // i64 conversions are done via library routines even when generating VFP + // instructions, so use the same ones. + // FIXME: There appears to be some naming inconsistency in ARM libgcc: e.g. + // __floatunsidf vs. __floatunssidfvfp. + setLibcallName(RTLIB::SINTTOFP_I32_F64, "__floatsidfvfp"); + setLibcallName(RTLIB::UINTTOFP_I32_F64, "__floatunssidfvfp"); + setLibcallName(RTLIB::SINTTOFP_I32_F32, "__floatsisfvfp"); + setLibcallName(RTLIB::UINTTOFP_I32_F32, "__floatunssisfvfp"); + } + + addRegisterClass(MVT::i32, ARM::GPRRegisterClass); + if (Subtarget->hasVFP2() && !Subtarget->isThumb()) { + addRegisterClass(MVT::f32, ARM::SPRRegisterClass); + addRegisterClass(MVT::f64, ARM::DPRRegisterClass); + } + + // ARM does not have f32 extending load. + setLoadXAction(ISD::EXTLOAD, MVT::f32, Expand); + + // 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); + } + + // i64 operation support. + if (Subtarget->isThumb()) { + setOperationAction(ISD::MUL, MVT::i64, Expand); + setOperationAction(ISD::MULHU, MVT::i32, Expand); + setOperationAction(ISD::MULHS, MVT::i32, Expand); + } else { + setOperationAction(ISD::MUL, MVT::i64, Custom); + setOperationAction(ISD::MULHU, MVT::i32, Custom); + if (!Subtarget->hasV6Ops()) + setOperationAction(ISD::MULHS, MVT::i32, Custom); + } + setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRA_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRL_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRL, MVT::i64, Custom); + setOperationAction(ISD::SRA, MVT::i64, Custom); + + // ARM does not have ROTL. + setOperationAction(ISD::ROTL, MVT::i32, Expand); + setOperationAction(ISD::CTTZ , MVT::i32, Expand); + setOperationAction(ISD::CTPOP, MVT::i32, Expand); + if (!Subtarget->hasV5TOps()) + setOperationAction(ISD::CTLZ, MVT::i32, Expand); + + // These are expanded into libcalls. + setOperationAction(ISD::SDIV, MVT::i32, Expand); + setOperationAction(ISD::UDIV, MVT::i32, Expand); + setOperationAction(ISD::SREM, MVT::i32, Expand); + setOperationAction(ISD::UREM, MVT::i32, Expand); + + // Support label based line numbers. + setOperationAction(ISD::LOCATION, MVT::Other, Expand); + setOperationAction(ISD::DEBUG_LOC, MVT::Other, Expand); + // FIXME - use subtarget debug flags + if (Subtarget->isDarwin()) + setOperationAction(ISD::DEBUG_LABEL, MVT::Other, Expand); + + setOperationAction(ISD::RET, MVT::Other, Custom); + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + setOperationAction(ISD::ConstantPool, MVT::i32, Custom); + + // Expand mem operations genericly. + setOperationAction(ISD::MEMSET , MVT::Other, Expand); + setOperationAction(ISD::MEMCPY , MVT::Other, Expand); + setOperationAction(ISD::MEMMOVE , MVT::Other, Expand); + + // Use the default implementation. + setOperationAction(ISD::VASTART , MVT::Other, Expand); + setOperationAction(ISD::VAARG , MVT::Other, Expand); + setOperationAction(ISD::VACOPY , MVT::Other, Expand); + setOperationAction(ISD::VAEND , MVT::Other, Expand); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32 , Expand); + + if (!Subtarget->hasV6Ops()) { + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + } + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + + if (Subtarget->hasVFP2() && !Subtarget->isThumb()) + // Turn f64->i64 into FMRRD iff target supports vfp2. + setOperationAction(ISD::BIT_CONVERT, MVT::i64, Custom); + + setOperationAction(ISD::SETCC , MVT::i32, Expand); + setOperationAction(ISD::SETCC , MVT::f32, Expand); + setOperationAction(ISD::SETCC , MVT::f64, Expand); + setOperationAction(ISD::SELECT , MVT::i32, Expand); + setOperationAction(ISD::SELECT , MVT::f32, Expand); + setOperationAction(ISD::SELECT , MVT::f64, Expand); + setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f64, Custom); + + setOperationAction(ISD::BRCOND , MVT::Other, Expand); + setOperationAction(ISD::BR_CC , MVT::i32, Custom); + setOperationAction(ISD::BR_CC , MVT::f32, Custom); + setOperationAction(ISD::BR_CC , MVT::f64, Custom); + setOperationAction(ISD::BR_JT , MVT::Other, Custom); + + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + // FP Constants can't be immediates. + setOperationAction(ISD::ConstantFP, MVT::f64, Expand); + setOperationAction(ISD::ConstantFP, MVT::f32, Expand); + + // We don't support sin/cos/fmod/copysign + setOperationAction(ISD::FSIN , MVT::f64, Expand); + setOperationAction(ISD::FSIN , MVT::f32, Expand); + setOperationAction(ISD::FCOS , MVT::f32, Expand); + setOperationAction(ISD::FCOS , MVT::f64, Expand); + setOperationAction(ISD::FREM , MVT::f64, Expand); + setOperationAction(ISD::FREM , MVT::f32, Expand); + setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom); + setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom); + + // int <-> fp are custom expanded into bit_convert + ARMISD ops. + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Custom); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Custom); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Custom); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); + + setStackPointerRegisterToSaveRestore(ARM::SP); + + setSchedulingPreference(SchedulingForRegPressure); + computeRegisterProperties(); +} + + +const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + default: return 0; + case ARMISD::Wrapper: return "ARMISD::Wrapper"; + case ARMISD::WrapperCall: return "ARMISD::WrapperCall"; + case ARMISD::WrapperJT: return "ARMISD::WrapperJT"; + case ARMISD::CALL: return "ARMISD::CALL"; + case ARMISD::CALL_NOLINK: return "ARMISD::CALL_NOLINK"; + case ARMISD::tCALL: return "ARMISD::tCALL"; + case ARMISD::BRCOND: return "ARMISD::BRCOND"; + case ARMISD::BR_JT: return "ARMISD::BR_JT"; + case ARMISD::RET_FLAG: return "ARMISD::RET_FLAG"; + case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD"; + case ARMISD::CMP: return "ARMISD::CMP"; + case ARMISD::CMPFP: return "ARMISD::CMPFP"; + case ARMISD::CMPFPw0: return "ARMISD::CMPFPw0"; + case ARMISD::FMSTAT: return "ARMISD::FMSTAT"; + case ARMISD::CMOV: return "ARMISD::CMOV"; + case ARMISD::CNEG: return "ARMISD::CNEG"; + + case ARMISD::FTOSI: return "ARMISD::FTOSI"; + case ARMISD::FTOUI: return "ARMISD::FTOUI"; + case ARMISD::SITOF: return "ARMISD::SITOF"; + case ARMISD::UITOF: return "ARMISD::UITOF"; + case ARMISD::MULHILOU: return "ARMISD::MULHILOU"; + case ARMISD::MULHILOS: return "ARMISD::MULHILOS"; + + case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG"; + case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG"; + case ARMISD::RRX: return "ARMISD::RRX"; + + case ARMISD::FMRRD: return "ARMISD::FMRRD"; + case ARMISD::FMDRR: return "ARMISD::FMDRR"; + } +} + +//===----------------------------------------------------------------------===// +// Lowering Code +//===----------------------------------------------------------------------===// + + +/// IntCCToARMCC - Convert a DAG integer condition code to an ARM CC +static ARMCC::CondCodes IntCCToARMCC(ISD::CondCode CC) { + switch (CC) { + default: assert(0 && "Unknown condition code!"); + case ISD::SETNE: return ARMCC::NE; + case ISD::SETEQ: return ARMCC::EQ; + case ISD::SETGT: return ARMCC::GT; + case ISD::SETGE: return ARMCC::GE; + case ISD::SETLT: return ARMCC::LT; + case ISD::SETLE: return ARMCC::LE; + case ISD::SETUGT: return ARMCC::HI; + case ISD::SETUGE: return ARMCC::HS; + case ISD::SETULT: return ARMCC::LO; + case ISD::SETULE: return ARMCC::LS; + } +} + +/// FPCCToARMCC - Convert a DAG fp condition code to an ARM CC. It +/// returns true if the operands should be inverted to form the proper +/// comparison. +static bool FPCCToARMCC(ISD::CondCode CC, ARMCC::CondCodes &CondCode, + ARMCC::CondCodes &CondCode2) { + bool Invert = false; + CondCode2 = ARMCC::AL; + switch (CC) { + default: assert(0 && "Unknown FP condition!"); + case ISD::SETEQ: + case ISD::SETOEQ: CondCode = ARMCC::EQ; break; + case ISD::SETGT: + case ISD::SETOGT: CondCode = ARMCC::GT; break; + case ISD::SETGE: + case ISD::SETOGE: CondCode = ARMCC::GE; break; + case ISD::SETOLT: CondCode = ARMCC::MI; break; + case ISD::SETOLE: CondCode = ARMCC::GT; Invert = true; break; + case ISD::SETONE: CondCode = ARMCC::MI; CondCode2 = ARMCC::GT; break; + case ISD::SETO: CondCode = ARMCC::VC; break; + case ISD::SETUO: CondCode = ARMCC::VS; break; + case ISD::SETUEQ: CondCode = ARMCC::EQ; CondCode2 = ARMCC::VS; break; + case ISD::SETUGT: CondCode = ARMCC::HI; break; + case ISD::SETUGE: CondCode = ARMCC::PL; break; + case ISD::SETLT: + case ISD::SETULT: CondCode = ARMCC::LT; break; + case ISD::SETLE: + case ISD::SETULE: CondCode = ARMCC::LE; break; + case ISD::SETNE: + case ISD::SETUNE: CondCode = ARMCC::NE; break; + } + return Invert; +} + +static void +HowToPassArgument(MVT::ValueType ObjectVT, + unsigned NumGPRs, unsigned &ObjSize, unsigned &ObjGPRs) { + ObjSize = 0; + ObjGPRs = 0; + + switch (ObjectVT) { + default: assert(0 && "Unhandled argument type!"); + case MVT::i32: + case MVT::f32: + if (NumGPRs < 4) + ObjGPRs = 1; + else + ObjSize = 4; + break; + case MVT::i64: + case MVT::f64: + if (NumGPRs < 3) + ObjGPRs = 2; + else if (NumGPRs == 3) { + ObjGPRs = 1; + ObjSize = 4; + } else + ObjSize = 8; + } +} + +// This transforms a ISD::CALL node into a +// callseq_star <- ARMISD:CALL <- callseq_end +// chain +SDOperand ARMTargetLowering::LowerCALL(SDOperand Op, SelectionDAG &DAG) { + MVT::ValueType RetVT= Op.Val->getValueType(0); + SDOperand Chain = Op.getOperand(0); + unsigned CallConv = cast<ConstantSDNode>(Op.getOperand(1))->getValue(); + assert((CallConv == CallingConv::C || + CallConv == CallingConv::CSRet || + CallConv == CallingConv::Fast) && "unknown calling convention"); + SDOperand Callee = Op.getOperand(4); + unsigned NumOps = (Op.getNumOperands() - 5) / 2; + unsigned ArgOffset = 0; // Frame mechanisms handle retaddr slot + unsigned NumGPRs = 0; // GPRs used for parameter passing. + + // Count how many bytes are to be pushed on the stack. + unsigned NumBytes = 0; + + // Add up all the space actually used. + for (unsigned i = 0; i < NumOps; ++i) { + unsigned ObjSize = 0; + unsigned ObjGPRs = 0; + MVT::ValueType ObjectVT = Op.getOperand(5+2*i).getValueType(); + HowToPassArgument(ObjectVT, NumGPRs, ObjSize, ObjGPRs); + NumBytes += ObjSize; + NumGPRs += ObjGPRs; + } + + // Adjust the stack pointer for the new arguments... + // These operations are automatically eliminated by the prolog/epilog pass + Chain = DAG.getCALLSEQ_START(Chain, + DAG.getConstant(NumBytes, MVT::i32)); + + SDOperand StackPtr = DAG.getRegister(ARM::SP, MVT::i32); + + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + NumGPRs = 0; + std::vector<std::pair<unsigned, SDOperand> > RegsToPass; + std::vector<SDOperand> MemOpChains; + for (unsigned i = 0; i != NumOps; ++i) { + SDOperand Arg = Op.getOperand(5+2*i); + MVT::ValueType ArgVT = Arg.getValueType(); + + unsigned ObjSize = 0; + unsigned ObjGPRs = 0; + HowToPassArgument(ArgVT, NumGPRs, ObjSize, ObjGPRs); + if (ObjGPRs > 0) { + switch (ArgVT) { + default: assert(0 && "Unexpected ValueType for argument!"); + case MVT::i32: + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Arg)); + break; + case MVT::f32: + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], + DAG.getNode(ISD::BIT_CONVERT, MVT::i32, Arg))); + break; + case MVT::i64: { + SDOperand Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Arg, + DAG.getConstant(0, getPointerTy())); + SDOperand Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Arg, + DAG.getConstant(1, getPointerTy())); + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Lo)); + if (ObjGPRs == 2) + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs+1], Hi)); + else { + SDOperand PtrOff= DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Hi, PtrOff, NULL, 0)); + } + break; + } + case MVT::f64: { + SDOperand Cvt = DAG.getNode(ARMISD::FMRRD, + DAG.getVTList(MVT::i32, MVT::i32), + &Arg, 1); + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Cvt)); + if (ObjGPRs == 2) + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs+1], + Cvt.getValue(1))); + else { + SDOperand PtrOff= DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Cvt.getValue(1), PtrOff, + NULL, 0)); + } + break; + } + } + } else { + assert(ObjSize != 0); + SDOperand PtrOff = DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Arg, PtrOff, NULL, 0)); + } + + NumGPRs += ObjGPRs; + ArgOffset += ObjSize; + } + + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, MVT::Other, + &MemOpChains[0], MemOpChains.size()); + + // Build a sequence of copy-to-reg nodes chained together with token chain + // and flag operands which copy the outgoing args into the appropriate regs. + SDOperand InFlag; + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, RegsToPass[i].first, RegsToPass[i].second, + InFlag); + InFlag = Chain.getValue(1); + } + + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every + // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol + // node so that legalize doesn't hack it. + bool isDirect = false; + bool isARMFunc = false; + if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + GlobalValue *GV = G->getGlobal(); + Callee = DAG.getTargetGlobalAddress(GV, getPointerTy()); + isDirect = true; + bool isExt = (GV->isExternal() || GV->hasWeakLinkage() || + GV->hasLinkOnceLinkage()); + bool isStub = (isExt && Subtarget->isDarwin()) && + getTargetMachine().getRelocationModel() != Reloc::Static; + isARMFunc = !Subtarget->isThumb() || isStub; + // Wrap it since tBX takes a register source operand. + if (isARMFunc && Subtarget->isThumb() && !Subtarget->hasV5TOps()) + Callee = DAG.getNode(ARMISD::WrapperCall, MVT::i32, Callee); + } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { + Callee = DAG.getTargetExternalSymbol(S->getSymbol(), getPointerTy()); + isDirect = true; + bool isStub = Subtarget->isDarwin() && + getTargetMachine().getRelocationModel() != Reloc::Static; + isARMFunc = !Subtarget->isThumb() || isStub; + // Wrap it since tBX takes a register source operand. + if (!Subtarget->hasV5TOps() && Subtarget->isThumb()) + Callee = DAG.getNode(ARMISD::WrapperCall, MVT::i32, Callee); + } + + std::vector<MVT::ValueType> NodeTys; + NodeTys.push_back(MVT::Other); // Returns a chain + NodeTys.push_back(MVT::Flag); // Returns a flag for retval copy to use. + + std::vector<SDOperand> Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are known live + // into the call. + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) + Ops.push_back(DAG.getRegister(RegsToPass[i].first, + RegsToPass[i].second.getValueType())); + + // FIXME: handle tail calls differently. + unsigned CallOpc; + if (Subtarget->isThumb()) { + if (!Subtarget->hasV5TOps() && (!isDirect || isARMFunc)) + CallOpc = ARMISD::CALL_NOLINK; + else + CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL; + } else { + CallOpc = (isDirect || Subtarget->hasV5TOps()) + ? ARMISD::CALL : ARMISD::CALL_NOLINK; + } + if (InFlag.Val) + Ops.push_back(InFlag); + Chain = DAG.getNode(CallOpc, NodeTys, &Ops[0], Ops.size()); + InFlag = Chain.getValue(1); + + SDOperand CSOps[] = { Chain, DAG.getConstant(NumBytes, MVT::i32), InFlag }; + Chain = DAG.getNode(ISD::CALLSEQ_END, + DAG.getNodeValueTypes(MVT::Other, MVT::Flag), + ((RetVT != MVT::Other) ? 2 : 1), CSOps, 3); + if (RetVT != MVT::Other) + InFlag = Chain.getValue(1); + + std::vector<SDOperand> ResultVals; + NodeTys.clear(); + + // If the call has results, copy the values out of the ret val registers. + switch (RetVT) { + default: assert(0 && "Unexpected ret value!"); + case MVT::Other: + break; + case MVT::i32: + Chain = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag).getValue(1); + ResultVals.push_back(Chain.getValue(0)); + if (Op.Val->getValueType(1) == MVT::i32) { + // Returns a i64 value. + Chain = DAG.getCopyFromReg(Chain, ARM::R1, MVT::i32, + Chain.getValue(2)).getValue(1); + ResultVals.push_back(Chain.getValue(0)); + NodeTys.push_back(MVT::i32); + } + NodeTys.push_back(MVT::i32); + break; + case MVT::f32: + Chain = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag).getValue(1); + ResultVals.push_back(DAG.getNode(ISD::BIT_CONVERT, MVT::f32, + Chain.getValue(0))); + NodeTys.push_back(MVT::f32); + break; + case MVT::f64: { + SDOperand Lo = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag); + SDOperand Hi = DAG.getCopyFromReg(Lo, ARM::R1, MVT::i32, Lo.getValue(2)); + ResultVals.push_back(DAG.getNode(ARMISD::FMDRR, MVT::f64, Lo, Hi)); + NodeTys.push_back(MVT::f64); + break; + } + } + + NodeTys.push_back(MVT::Other); + + if (ResultVals.empty()) + return Chain; + + ResultVals.push_back(Chain); + SDOperand Res = DAG.getNode(ISD::MERGE_VALUES, NodeTys, &ResultVals[0], + ResultVals.size()); + return Res.getValue(Op.ResNo); +} + +static SDOperand LowerRET(SDOperand Op, SelectionDAG &DAG) { + SDOperand Copy; + SDOperand Chain = Op.getOperand(0); + switch(Op.getNumOperands()) { + default: + assert(0 && "Do not know how to return this many arguments!"); + abort(); + case 1: { + SDOperand LR = DAG.getRegister(ARM::LR, MVT::i32); + return DAG.getNode(ARMISD::RET_FLAG, MVT::Other, Chain); + } + case 3: + Op = Op.getOperand(1); + if (Op.getValueType() == MVT::f32) { + Op = DAG.getNode(ISD::BIT_CONVERT, MVT::i32, Op); + } else if (Op.getValueType() == MVT::f64) { + // Recursively legalize f64 -> i64. + Op = DAG.getNode(ISD::BIT_CONVERT, MVT::i64, Op); + return DAG.getNode(ISD::RET, MVT::Other, Chain, Op, + DAG.getConstant(0, MVT::i32)); + } + Copy = DAG.getCopyToReg(Chain, ARM::R0, Op, SDOperand()); + if (DAG.getMachineFunction().liveout_empty()) + DAG.getMachineFunction().addLiveOut(ARM::R0); + break; + case 5: + Copy = DAG.getCopyToReg(Chain, ARM::R1, Op.getOperand(3), SDOperand()); + Copy = DAG.getCopyToReg(Copy, ARM::R0, Op.getOperand(1), Copy.getValue(1)); + // If we haven't noted the R0+R1 are live out, do so now. + if (DAG.getMachineFunction().liveout_empty()) { + DAG.getMachineFunction().addLiveOut(ARM::R0); + DAG.getMachineFunction().addLiveOut(ARM::R1); + } + break; + } + + //We must use RET_FLAG instead of BRIND because BRIND doesn't have a flag + return DAG.getNode(ARMISD::RET_FLAG, MVT::Other, Copy, Copy.getValue(1)); +} + +// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as +// their target countpart wrapped in the ARMISD::Wrapper node. Suppose N is +// one of the above mentioned nodes. It has to be wrapped because otherwise +// Select(N) returns N. So the raw TargetGlobalAddress nodes, etc. can only +// be used to form addressing mode. These wrapped nodes will be selected +// into MOVri. +static SDOperand LowerConstantPool(SDOperand Op, SelectionDAG &DAG) { + MVT::ValueType PtrVT = Op.getValueType(); + ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op); + SDOperand Res; + if (CP->isMachineConstantPoolEntry()) + Res = DAG.getTargetConstantPool(CP->getMachineCPVal(), PtrVT, + CP->getAlignment()); + else + Res = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT, + CP->getAlignment()); + return DAG.getNode(ARMISD::Wrapper, MVT::i32, Res); +} + +/// GVIsIndirectSymbol - true if the GV will be accessed via an indirect symbol +/// even in dynamic-no-pic mode. +static bool GVIsIndirectSymbol(GlobalValue *GV) { + return (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage() || + (GV->isExternal() && !GV->hasNotBeenReadFromBytecode())); +} + +SDOperand ARMTargetLowering::LowerGlobalAddress(SDOperand Op, + SelectionDAG &DAG) { + MVT::ValueType PtrVT = getPointerTy(); + GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); + Reloc::Model RelocM = getTargetMachine().getRelocationModel(); + bool IsIndirect = Subtarget->isDarwin() && GVIsIndirectSymbol(GV); + SDOperand CPAddr; + if (RelocM == Reloc::Static) + CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 2); + else { + unsigned PCAdj = (RelocM != Reloc::PIC_) + ? 0 : (Subtarget->isThumb() ? 4 : 8); + ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex, + IsIndirect, PCAdj); + CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 2); + } + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + + SDOperand Result = DAG.getLoad(PtrVT, DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand Chain = Result.getValue(1); + + if (RelocM == Reloc::PIC_) { + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Result = DAG.getNode(ARMISD::PIC_ADD, PtrVT, Result, PICLabel); + } + if (IsIndirect) + Result = DAG.getLoad(PtrVT, Chain, Result, NULL, 0); + + return Result; +} + +static SDOperand LowerVASTART(SDOperand Op, SelectionDAG &DAG, + unsigned VarArgsFrameIndex) { + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + MVT::ValueType PtrVT = DAG.getTargetLoweringInfo().getPointerTy(); + SDOperand FR = DAG.getFrameIndex(VarArgsFrameIndex, PtrVT); + SrcValueSDNode *SV = cast<SrcValueSDNode>(Op.getOperand(2)); + return DAG.getStore(Op.getOperand(0), FR, Op.getOperand(1), SV->getValue(), + SV->getOffset()); +} + +static SDOperand LowerFORMAL_ARGUMENT(SDOperand Op, SelectionDAG &DAG, + unsigned *vRegs, unsigned ArgNo, + unsigned &NumGPRs, unsigned &ArgOffset) { + MachineFunction &MF = DAG.getMachineFunction(); + MVT::ValueType ObjectVT = Op.getValue(ArgNo).getValueType(); + SDOperand Root = Op.getOperand(0); + std::vector<SDOperand> ArgValues; + SSARegMap *RegMap = MF.getSSARegMap(); + + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + unsigned ObjSize = 0; + unsigned ObjGPRs = 0; + HowToPassArgument(ObjectVT, NumGPRs, ObjSize, ObjGPRs); + + SDOperand ArgValue; + if (ObjGPRs == 1) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + vRegs[NumGPRs] = VReg; + ArgValue = DAG.getCopyFromReg(Root, VReg, MVT::i32); + if (ObjectVT == MVT::f32) + ArgValue = DAG.getNode(ISD::BIT_CONVERT, MVT::f32, ArgValue); + } else if (ObjGPRs == 2) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + vRegs[NumGPRs] = VReg; + ArgValue = DAG.getCopyFromReg(Root, VReg, MVT::i32); + + VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs+1], VReg); + vRegs[NumGPRs+1] = VReg; + SDOperand ArgValue2 = DAG.getCopyFromReg(Root, VReg, MVT::i32); + + if (ObjectVT == MVT::i64) + ArgValue = DAG.getNode(ISD::BUILD_PAIR, MVT::i64, ArgValue, ArgValue2); + else + ArgValue = DAG.getNode(ARMISD::FMDRR, MVT::f64, ArgValue, ArgValue2); + } + NumGPRs += ObjGPRs; + + if (ObjSize) { + // If the argument is actually used, emit a load from the right stack + // slot. + if (!Op.Val->hasNUsesOfValue(0, ArgNo)) { + MachineFrameInfo *MFI = MF.getFrameInfo(); + int FI = MFI->CreateFixedObject(ObjSize, ArgOffset); + SDOperand FIN = DAG.getFrameIndex(FI, MVT::i32); + if (ObjGPRs == 0) + ArgValue = DAG.getLoad(ObjectVT, Root, FIN, NULL, 0); + else { + SDOperand ArgValue2 = + DAG.getLoad(MVT::i32, Root, FIN, NULL, 0); + if (ObjectVT == MVT::i64) + ArgValue= DAG.getNode(ISD::BUILD_PAIR, MVT::i64, ArgValue, ArgValue2); + else + ArgValue= DAG.getNode(ARMISD::FMDRR, MVT::f64, ArgValue, ArgValue2); + } + } else { + // Don't emit a dead load. + ArgValue = DAG.getNode(ISD::UNDEF, ObjectVT); + } + + ArgOffset += ObjSize; // Move on to the next argument. + } + + return ArgValue; +} + +SDOperand +ARMTargetLowering::LowerFORMAL_ARGUMENTS(SDOperand Op, SelectionDAG &DAG) { + std::vector<SDOperand> ArgValues; + SDOperand Root = Op.getOperand(0); + unsigned ArgOffset = 0; // Frame mechanisms handle retaddr slot + unsigned NumGPRs = 0; // GPRs used for parameter passing. + unsigned VRegs[4]; + + unsigned NumArgs = Op.Val->getNumValues()-1; + for (unsigned ArgNo = 0; ArgNo < NumArgs; ++ArgNo) + ArgValues.push_back(LowerFORMAL_ARGUMENT(Op, DAG, VRegs, ArgNo, + NumGPRs, ArgOffset)); + + bool isVarArg = cast<ConstantSDNode>(Op.getOperand(2))->getValue() != 0; + if (isVarArg) { + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + MachineFunction &MF = DAG.getMachineFunction(); + SSARegMap *RegMap = MF.getSSARegMap(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + unsigned VARegSaveSize = (4 - NumGPRs) * 4; + if (VARegSaveSize) { + // If this function is vararg, store any remaining integer argument regs + // to their spots on the stack so that they may be loaded by deferencing + // the result of va_next. + AFI->setVarArgsRegSaveSize(VARegSaveSize); + VarArgsFrameIndex = MFI->CreateFixedObject(VARegSaveSize, ArgOffset); + SDOperand FIN = DAG.getFrameIndex(VarArgsFrameIndex, getPointerTy()); + + SmallVector<SDOperand, 4> MemOps; + for (; NumGPRs < 4; ++NumGPRs) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + SDOperand Val = DAG.getCopyFromReg(Root, VReg, MVT::i32); + SDOperand Store = DAG.getStore(Val.getValue(1), Val, FIN, NULL, 0); + MemOps.push_back(Store); + FIN = DAG.getNode(ISD::ADD, getPointerTy(), FIN, + DAG.getConstant(4, getPointerTy())); + } + if (!MemOps.empty()) + Root = DAG.getNode(ISD::TokenFactor, MVT::Other, + &MemOps[0], MemOps.size()); + } else + // This will point to the next argument passed via stack. + VarArgsFrameIndex = MFI->CreateFixedObject(4, ArgOffset); + } + + ArgValues.push_back(Root); + + // Return the new list of results. + std::vector<MVT::ValueType> RetVT(Op.Val->value_begin(), + Op.Val->value_end()); + return DAG.getNode(ISD::MERGE_VALUES, RetVT, &ArgValues[0], ArgValues.size()); +} + +/// isFloatingPointZero - Return true if this is +0.0. +static bool isFloatingPointZero(SDOperand Op) { + if (ConstantFPSDNode *CFP = dyn_cast<ConstantFPSDNode>(Op)) + return CFP->isExactlyValue(0.0); + else if (ISD::isEXTLoad(Op.Val) || ISD::isNON_EXTLoad(Op.Val)) { + // Maybe this has already been legalized into the constant pool? + if (Op.getOperand(1).getOpcode() == ARMISD::Wrapper) { + SDOperand WrapperOp = Op.getOperand(1).getOperand(0); + if (ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(WrapperOp)) + if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal())) + return CFP->isExactlyValue(0.0); + } + } + return false; +} + +static bool isLegalCmpImmediate(int C, bool isThumb) { + return ( isThumb && (C & ~255U) == 0) || + (!isThumb && ARM_AM::getSOImmVal(C) != -1); +} + +/// Returns appropriate ARM CMP (cmp) and corresponding condition code for +/// the given operands. +static SDOperand getARMCmp(SDOperand LHS, SDOperand RHS, ISD::CondCode CC, + SDOperand &ARMCC, SelectionDAG &DAG, bool isThumb) { + if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS.Val)) { + int C = (int)RHSC->getValue(); + if (!isLegalCmpImmediate(C, isThumb)) { + // Constant does not fit, try adjusting it by one? + switch (CC) { + default: break; + case ISD::SETLT: + case ISD::SETULT: + case ISD::SETGE: + case ISD::SETUGE: + if (isLegalCmpImmediate(C-1, isThumb)) { + switch (CC) { + default: break; + case ISD::SETLT: CC = ISD::SETLE; break; + case ISD::SETULT: CC = ISD::SETULE; break; + case ISD::SETGE: CC = ISD::SETGT; break; + case ISD::SETUGE: CC = ISD::SETUGT; break; + } + RHS = DAG.getConstant(C-1, MVT::i32); + } + break; + case ISD::SETLE: + case ISD::SETULE: + case ISD::SETGT: + case ISD::SETUGT: + if (isLegalCmpImmediate(C+1, isThumb)) { + switch (CC) { + default: break; + case ISD::SETLE: CC = ISD::SETLT; break; + case ISD::SETULE: CC = ISD::SETULT; break; + case ISD::SETGT: CC = ISD::SETGE; break; + case ISD::SETUGT: CC = ISD::SETUGE; break; + } + RHS = DAG.getConstant(C+1, MVT::i32); + } + break; + } + } + } + + ARMCC::CondCodes CondCode = IntCCToARMCC(CC); + ARMCC = DAG.getConstant(CondCode, MVT::i32); + return DAG.getNode(ARMISD::CMP, MVT::Flag, LHS, RHS); +} + +/// Returns a appropriate VFP CMP (fcmp{s|d}+fmstat) for the given operands. +static SDOperand getVFPCmp(SDOperand LHS, SDOperand RHS, SelectionDAG &DAG) { + SDOperand Cmp; + if (!isFloatingPointZero(RHS)) + Cmp = DAG.getNode(ARMISD::CMPFP, MVT::Flag, LHS, RHS); + else + Cmp = DAG.getNode(ARMISD::CMPFPw0, MVT::Flag, LHS); + return DAG.getNode(ARMISD::FMSTAT, MVT::Flag, Cmp); +} + +static SDOperand LowerSELECT_CC(SDOperand Op, SelectionDAG &DAG, + const ARMSubtarget *ST) { + MVT::ValueType VT = Op.getValueType(); + SDOperand LHS = Op.getOperand(0); + SDOperand RHS = Op.getOperand(1); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get(); + SDOperand TrueVal = Op.getOperand(2); + SDOperand FalseVal = Op.getOperand(3); + + if (LHS.getValueType() == MVT::i32) { + SDOperand ARMCC; + SDOperand Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb()); + return DAG.getNode(ARMISD::CMOV, VT, FalseVal, TrueVal, ARMCC, Cmp); + } + + ARMCC::CondCodes CondCode, CondCode2; + if (FPCCToARMCC(CC, CondCode, CondCode2)) + std::swap(TrueVal, FalseVal); + + SDOperand ARMCC = DAG.getConstant(CondCode, MVT::i32); + SDOperand Cmp = getVFPCmp(LHS, RHS, DAG); + SDOperand Result = DAG.getNode(ARMISD::CMOV, VT, FalseVal, TrueVal, + ARMCC, Cmp); + if (CondCode2 != ARMCC::AL) { + SDOperand ARMCC2 = DAG.getConstant(CondCode2, MVT::i32); + // FIXME: Needs another CMP because flag can have but one use. + SDOperand Cmp2 = getVFPCmp(LHS, |