2007-01-19 15:51:42 +08:00
|
|
|
//===-- ARMISelLowering.cpp - ARM DAG Lowering Implementation -------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
2007-12-30 04:36:04 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2007-01-19 15:51:42 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// 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"
|
2009-04-18 03:07:39 +08:00
|
|
|
#include "llvm/Function.h"
|
2007-03-16 16:43:56 +08:00
|
|
|
#include "llvm/Instruction.h"
|
2007-11-09 01:20:05 +08:00
|
|
|
#include "llvm/Intrinsics.h"
|
2007-12-31 12:13:23 +08:00
|
|
|
#include "llvm/GlobalValue.h"
|
2009-04-18 03:07:39 +08:00
|
|
|
#include "llvm/CodeGen/CallingConvLower.h"
|
2007-01-19 15:51:42 +08:00
|
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
2007-12-31 12:13:23 +08:00
|
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
2009-04-18 03:07:39 +08:00
|
|
|
#include "llvm/CodeGen/PseudoSourceValue.h"
|
2007-01-19 15:51:42 +08:00
|
|
|
#include "llvm/CodeGen/SelectionDAG.h"
|
2007-01-31 16:40:13 +08:00
|
|
|
#include "llvm/Target/TargetOptions.h"
|
2007-01-19 15:51:42 +08:00
|
|
|
#include "llvm/ADT/VectorExtras.h"
|
2007-03-13 07:30:29 +08:00
|
|
|
#include "llvm/Support/MathExtras.h"
|
2007-01-19 15:51:42 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State);
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State);
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State);
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State);
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
ARMTargetLowering::ARMTargetLowering(TargetMachine &TM)
|
|
|
|
: TargetLowering(TM), ARMPCLabelIndex(0) {
|
|
|
|
Subtarget = &TM.getSubtarget<ARMSubtarget>();
|
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
if (Subtarget->isTargetDarwin()) {
|
|
|
|
// 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");
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
// 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");
|
2007-01-31 17:30:58 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
// 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");
|
|
|
|
setLibcallName(RTLIB::O_F32, "__unordsf2vfp");
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
setCmpLibcallCC(RTLIB::OEQ_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::UNE_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OLT_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OLE_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OGE_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OGT_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::UO_F32, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::O_F32, ISD::SETEQ);
|
2007-01-31 17:30:58 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
// 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");
|
|
|
|
setLibcallName(RTLIB::O_F64, "__unorddf2vfp");
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
setCmpLibcallCC(RTLIB::OEQ_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::UNE_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OLT_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OLE_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OGE_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::OGT_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::UO_F64, ISD::SETNE);
|
|
|
|
setCmpLibcallCC(RTLIB::O_F64, ISD::SETEQ);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
// 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");
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-04-27 16:15:43 +08:00
|
|
|
// 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.
|
2009-03-21 07:16:43 +08:00
|
|
|
// FIXME: There appears to be some naming inconsistency in ARM libgcc:
|
|
|
|
// e.g., __floatunsidf vs. __floatunssidfvfp.
|
2007-04-27 16:15:43 +08:00
|
|
|
setLibcallName(RTLIB::SINTTOFP_I32_F64, "__floatsidfvfp");
|
|
|
|
setLibcallName(RTLIB::UINTTOFP_I32_F64, "__floatunssidfvfp");
|
|
|
|
setLibcallName(RTLIB::SINTTOFP_I32_F32, "__floatsisfvfp");
|
|
|
|
setLibcallName(RTLIB::UINTTOFP_I32_F32, "__floatunssisfvfp");
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-05-23 01:38:41 +08:00
|
|
|
// These libcalls are not available in 32-bit.
|
|
|
|
setLibcallName(RTLIB::SHL_I128, 0);
|
|
|
|
setLibcallName(RTLIB::SRL_I128, 0);
|
|
|
|
setLibcallName(RTLIB::SRA_I128, 0);
|
|
|
|
|
2009-04-08 04:34:09 +08:00
|
|
|
if (Subtarget->isThumb())
|
|
|
|
addRegisterClass(MVT::i32, ARM::tGPRRegisterClass);
|
|
|
|
else
|
|
|
|
addRegisterClass(MVT::i32, ARM::GPRRegisterClass);
|
2007-01-31 16:40:13 +08:00
|
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb()) {
|
2007-01-19 15:51:42 +08:00
|
|
|
addRegisterClass(MVT::f32, ARM::SPRRegisterClass);
|
|
|
|
addRegisterClass(MVT::f64, ARM::DPRRegisterClass);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2008-01-18 03:59:44 +08:00
|
|
|
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2007-05-18 08:19:34 +08:00
|
|
|
computeRegisterProperties();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
// ARM does not have f32 extending load.
|
2008-10-15 05:26:46 +08:00
|
|
|
setLoadExtAction(ISD::EXTLOAD, MVT::f32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2008-01-24 04:39:46 +08:00
|
|
|
// ARM does not have i1 sign extending load.
|
2008-10-15 05:26:46 +08:00
|
|
|
setLoadExtAction(ISD::SEXTLOAD, MVT::i1, Promote);
|
2008-01-24 04:39:46 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// 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);
|
2007-10-09 02:33:35 +08:00
|
|
|
setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand);
|
|
|
|
setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
} else {
|
2007-10-09 02:33:35 +08:00
|
|
|
setOperationAction(ISD::MUL, MVT::i64, Expand);
|
|
|
|
setOperationAction(ISD::MULHU, MVT::i32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (!Subtarget->hasV6Ops())
|
2007-10-09 02:33:35 +08:00
|
|
|
setOperationAction(ISD::MULHS, MVT::i32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
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);
|
2009-03-21 06:42:55 +08:00
|
|
|
setOperationAction(ISD::CTTZ, MVT::i32, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::CTPOP, MVT::i32, Expand);
|
2007-02-02 07:34:03 +08:00
|
|
|
if (!Subtarget->hasV5TOps() || Subtarget->isThumb())
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::CTLZ, MVT::i32, Expand);
|
|
|
|
|
2007-03-17 06:54:16 +08:00
|
|
|
// Only ARMv6 has BSWAP.
|
|
|
|
if (!Subtarget->hasV6Ops())
|
2007-03-20 10:25:53 +08:00
|
|
|
setOperationAction(ISD::BSWAP, MVT::i32, Expand);
|
2007-03-17 06:54:16 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// 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);
|
2007-10-09 02:33:35 +08:00
|
|
|
setOperationAction(ISD::SDIVREM, MVT::i32, Expand);
|
|
|
|
setOperationAction(ISD::UDIVREM, MVT::i32, Expand);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// Support label based line numbers.
|
2008-07-01 04:59:49 +08:00
|
|
|
setOperationAction(ISD::DBG_STOPPOINT, MVT::Other, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::DEBUG_LOC, MVT::Other, Expand);
|
|
|
|
|
|
|
|
setOperationAction(ISD::RET, MVT::Other, Custom);
|
|
|
|
setOperationAction(ISD::GlobalAddress, MVT::i32, Custom);
|
|
|
|
setOperationAction(ISD::ConstantPool, MVT::i32, Custom);
|
2007-04-22 08:04:12 +08:00
|
|
|
setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom);
|
2007-04-27 21:54:47 +08:00
|
|
|
setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
// Use the default implementation.
|
2009-03-21 06:42:55 +08:00
|
|
|
setOperationAction(ISD::VASTART, MVT::Other, Custom);
|
|
|
|
setOperationAction(ISD::VAARG, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::VAEND, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
|
2009-03-21 06:42:55 +08:00
|
|
|
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand);
|
|
|
|
setOperationAction(ISD::MEMBARRIER, MVT::Other, Expand);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2007-01-31 16:40:13 +08:00
|
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb())
|
2008-11-05 03:57:48 +08:00
|
|
|
// Turn f64->i64 into FMRRD, i64 -> f64 to FMDRR iff target supports vfp2.
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::BIT_CONVERT, MVT::i64, Custom);
|
2007-11-09 01:20:05 +08:00
|
|
|
|
|
|
|
// We want to custom lower some of our intrinsics.
|
|
|
|
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
|
|
|
|
|
2009-03-21 06:42:55 +08:00
|
|
|
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);
|
2007-01-19 15:51:42 +08:00
|
|
|
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
|
|
|
|
setOperationAction(ISD::SELECT_CC, MVT::f32, Custom);
|
|
|
|
setOperationAction(ISD::SELECT_CC, MVT::f64, Custom);
|
|
|
|
|
2009-03-21 06:42:55 +08:00
|
|
|
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);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-10-12 07:21:31 +08:00
|
|
|
// We don't support sin/cos/fmod/copysign/pow
|
2009-03-21 06:42:55 +08:00
|
|
|
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);
|
2008-04-01 09:50:16 +08:00
|
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb()) {
|
|
|
|
setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom);
|
|
|
|
setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom);
|
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
setOperationAction(ISD::FPOW, MVT::f64, Expand);
|
|
|
|
setOperationAction(ISD::FPOW, MVT::f32, Expand);
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// int <-> fp are custom expanded into bit_convert + ARMISD ops.
|
2008-04-01 09:50:16 +08:00
|
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb()) {
|
|
|
|
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);
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-11-28 06:36:16 +08:00
|
|
|
// We have target-specific dag combine patterns for the following nodes:
|
|
|
|
// ARMISD::FMRRD - No need to call setTargetDAGCombine
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
setTargetDAGCombine(ISD::ADD);
|
|
|
|
setTargetDAGCombine(ISD::SUB);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
setStackPointerRegisterToSaveRestore(ARM::SP);
|
|
|
|
setSchedulingPreference(SchedulingForRegPressure);
|
2007-05-18 08:19:34 +08:00
|
|
|
setIfCvtBlockSizeLimit(Subtarget->isThumb() ? 0 : 10);
|
2007-06-20 07:55:02 +08:00
|
|
|
setIfCvtDupBlockSizeLimit(Subtarget->isThumb() ? 0 : 2);
|
2007-05-18 05:31:21 +08:00
|
|
|
|
2009-06-19 09:51:50 +08:00
|
|
|
if (!Subtarget->isThumb()) {
|
|
|
|
// Use branch latency information to determine if-conversion limits.
|
|
|
|
const TargetInstrInfo *TII = getTargetMachine().getInstrInfo();
|
|
|
|
const InstrItineraryData &InstrItins = Subtarget->getInstrItineraryData();
|
|
|
|
unsigned Latency = InstrItins.getLatency(TII->get(ARM::BL).getSchedClass());
|
|
|
|
if (Latency > 1) {
|
|
|
|
setIfCvtBlockSizeLimit(Latency-1);
|
|
|
|
if (Latency > 2)
|
|
|
|
setIfCvtDupBlockSizeLimit(Latency-2);
|
|
|
|
} else {
|
|
|
|
setIfCvtBlockSizeLimit(10);
|
|
|
|
setIfCvtDupBlockSizeLimit(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-18 05:31:21 +08:00
|
|
|
maxStoresPerMemcpy = 1; //// temporary - rewrite interface to use type
|
2009-05-19 04:55:32 +08:00
|
|
|
// Do not enable CodePlacementOpt for now: it currently runs after the
|
|
|
|
// ARMConstantIslandPass and messes up branch relaxation and placement
|
|
|
|
// of constant islands.
|
|
|
|
// benefitFromCodePlacementOpt = true;
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
|
|
|
switch (Opcode) {
|
|
|
|
default: return 0;
|
|
|
|
case ARMISD::Wrapper: return "ARMISD::Wrapper";
|
|
|
|
case ARMISD::WrapperJT: return "ARMISD::WrapperJT";
|
|
|
|
case ARMISD::CALL: return "ARMISD::CALL";
|
2007-06-20 05:05:09 +08:00
|
|
|
case ARMISD::CALL_PRED: return "ARMISD::CALL_PRED";
|
2007-01-19 15:51:42 +08:00
|
|
|
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";
|
2007-04-02 09:30:03 +08:00
|
|
|
case ARMISD::CMPNZ: return "ARMISD::CMPNZ";
|
2007-01-19 15:51:42 +08:00
|
|
|
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";
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
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::SRL_FLAG: return "ARMISD::SRL_FLAG";
|
|
|
|
case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG";
|
|
|
|
case ARMISD::RRX: return "ARMISD::RRX";
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
case ARMISD::FMRRD: return "ARMISD::FMRRD";
|
|
|
|
case ARMISD::FMDRR: return "ARMISD::FMDRR";
|
2007-04-27 21:54:47 +08:00
|
|
|
|
|
|
|
case ARMISD::THREAD_POINTER:return "ARMISD::THREAD_POINTER";
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Calling Convention Implementation
|
|
|
|
//
|
|
|
|
// The lower operations present on calling convention works on this order:
|
|
|
|
// LowerCALL (virt regs --> phys regs, virt regs --> stack)
|
|
|
|
// LowerFORMAL_ARGUMENTS (phys --> virt regs, stack --> virt regs)
|
|
|
|
// LowerRET (virt regs --> phys regs)
|
|
|
|
// LowerCALL (phys regs --> virt regs)
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "ARMGenCallingConv.inc"
|
|
|
|
|
|
|
|
// APCS f64 is in register pairs, possibly split to stack
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State) {
|
|
|
|
static const unsigned HiRegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 };
|
|
|
|
static const unsigned LoRegList[] = { ARM::R1,
|
|
|
|
ARM::R2,
|
|
|
|
ARM::R3,
|
|
|
|
ARM::NoRegister };
|
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned Reg = State.AllocateReg(HiRegList, LoRegList, 4);
|
2009-06-17 02:50:49 +08:00
|
|
|
if (Reg == 0)
|
2009-04-18 04:40:45 +08:00
|
|
|
return false; // we didn't handle it
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
if (HiRegList[i] == Reg)
|
|
|
|
break;
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, MVT::i32, LocInfo));
|
|
|
|
if (LoRegList[i] != ARM::NoRegister)
|
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i],
|
|
|
|
MVT::i32, LocInfo));
|
|
|
|
else
|
|
|
|
State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT,
|
|
|
|
State.AllocateStack(4, 4),
|
|
|
|
MVT::i32, LocInfo));
|
|
|
|
return true; // we handled it
|
2009-04-18 03:07:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// AAPCS f64 is in aligned register pairs
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State) {
|
|
|
|
static const unsigned HiRegList[] = { ARM::R0, ARM::R2 };
|
|
|
|
static const unsigned LoRegList[] = { ARM::R1, ARM::R3 };
|
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned Reg = State.AllocateReg(HiRegList, LoRegList, 2);
|
|
|
|
if (Reg == 0)
|
|
|
|
return false; // we didn't handle it
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
|
|
if (HiRegList[i] == Reg)
|
|
|
|
break;
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, MVT::i32, LocInfo));
|
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i],
|
|
|
|
MVT::i32, LocInfo));
|
|
|
|
return true; // we handled it
|
2009-04-18 03:07:39 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State) {
|
|
|
|
static const unsigned HiRegList[] = { ARM::R0, ARM::R2 };
|
|
|
|
static const unsigned LoRegList[] = { ARM::R1, ARM::R3 };
|
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned Reg = State.AllocateReg(HiRegList, LoRegList, 2);
|
|
|
|
if (Reg == 0)
|
|
|
|
return false; // we didn't handle it
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
|
|
if (HiRegList[i] == Reg)
|
|
|
|
break;
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:40:45 +08:00
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, MVT::i32, LocInfo));
|
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i],
|
|
|
|
MVT::i32, LocInfo));
|
|
|
|
return true; // we handled it
|
2009-04-18 03:07:39 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
2009-04-18 03:07:39 +08:00
|
|
|
CCValAssign::LocInfo &LocInfo,
|
|
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
|
|
CCState &State) {
|
|
|
|
return RetCC_ARM_APCS_Custom_f64(ValNo, ValVT, LocVT, LocInfo, ArgFlags,
|
|
|
|
State);
|
|
|
|
}
|
|
|
|
|
2009-06-17 02:50:49 +08:00
|
|
|
/// CCAssignFnForNode - Selects the correct CCAssignFn for a the
|
|
|
|
/// given CallingConvention value.
|
|
|
|
CCAssignFn *ARMTargetLowering::CCAssignFnForNode(unsigned CC,
|
|
|
|
bool Return) const {
|
|
|
|
switch (CC) {
|
|
|
|
default:
|
|
|
|
assert(0 && "Unsupported calling convention");
|
|
|
|
case CallingConv::C:
|
|
|
|
case CallingConv::Fast:
|
|
|
|
// Use target triple & subtarget features to do actual dispatch.
|
|
|
|
if (Subtarget->isAAPCS_ABI()) {
|
|
|
|
if (Subtarget->hasVFP2() &&
|
|
|
|
FloatABIType == FloatABI::Hard)
|
|
|
|
return (Return ? RetCC_ARM_AAPCS_VFP: CC_ARM_AAPCS_VFP);
|
|
|
|
else
|
|
|
|
return (Return ? RetCC_ARM_AAPCS: CC_ARM_AAPCS);
|
|
|
|
} else
|
|
|
|
return (Return ? RetCC_ARM_APCS: CC_ARM_APCS);
|
|
|
|
case CallingConv::ARM_AAPCS_VFP:
|
|
|
|
return (Return ? RetCC_ARM_AAPCS_VFP: CC_ARM_AAPCS_VFP);
|
|
|
|
case CallingConv::ARM_AAPCS:
|
|
|
|
return (Return ? RetCC_ARM_AAPCS: CC_ARM_AAPCS);
|
|
|
|
case CallingConv::ARM_APCS:
|
|
|
|
return (Return ? RetCC_ARM_APCS: CC_ARM_APCS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
/// LowerCallResult - Lower the result values of an ISD::CALL into the
|
|
|
|
/// appropriate copies out of appropriate physical registers. This assumes that
|
|
|
|
/// Chain/InFlag are the input chain/flag to use, and that TheCall is the call
|
|
|
|
/// being lowered. The returns a SDNode with the same number of values as the
|
|
|
|
/// ISD::CALL.
|
|
|
|
SDNode *ARMTargetLowering::
|
|
|
|
LowerCallResult(SDValue Chain, SDValue InFlag, CallSDNode *TheCall,
|
|
|
|
unsigned CallingConv, SelectionDAG &DAG) {
|
|
|
|
|
|
|
|
DebugLoc dl = TheCall->getDebugLoc();
|
|
|
|
// Assign locations to each value returned by this call.
|
|
|
|
SmallVector<CCValAssign, 16> RVLocs;
|
|
|
|
bool isVarArg = TheCall->isVarArg();
|
|
|
|
CCState CCInfo(CallingConv, isVarArg, getTargetMachine(), RVLocs);
|
2009-06-17 02:50:49 +08:00
|
|
|
CCInfo.AnalyzeCallResult(TheCall,
|
|
|
|
CCAssignFnForNode(CallingConv, /* Return*/ true));
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
SmallVector<SDValue, 8> ResultVals;
|
|
|
|
|
|
|
|
// Copy all of the result registers out of their specified physreg.
|
|
|
|
for (unsigned i = 0; i != RVLocs.size(); ++i) {
|
|
|
|
CCValAssign VA = RVLocs[i];
|
|
|
|
|
2009-04-25 08:33:20 +08:00
|
|
|
SDValue Val;
|
2009-04-18 03:07:39 +08:00
|
|
|
if (VA.needsCustom()) {
|
2009-04-25 08:33:20 +08:00
|
|
|
// Handle f64 as custom.
|
|
|
|
SDValue Lo = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32,
|
2009-04-18 03:07:39 +08:00
|
|
|
InFlag);
|
2009-04-25 01:00:36 +08:00
|
|
|
Chain = Lo.getValue(1);
|
|
|
|
InFlag = Lo.getValue(2);
|
2009-04-18 03:07:39 +08:00
|
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
2009-04-25 08:33:20 +08:00
|
|
|
SDValue Hi = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32,
|
2009-04-25 01:00:36 +08:00
|
|
|
InFlag);
|
|
|
|
Chain = Hi.getValue(1);
|
|
|
|
InFlag = Hi.getValue(2);
|
2009-04-25 08:33:20 +08:00
|
|
|
Val = DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, Lo, Hi);
|
2009-04-18 03:07:39 +08:00
|
|
|
} else {
|
2009-04-25 08:33:20 +08:00
|
|
|
Val = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), VA.getLocVT(),
|
|
|
|
InFlag);
|
2009-04-25 01:00:36 +08:00
|
|
|
Chain = Val.getValue(1);
|
|
|
|
InFlag = Val.getValue(2);
|
2009-04-25 08:33:20 +08:00
|
|
|
}
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-25 08:33:20 +08:00
|
|
|
switch (VA.getLocInfo()) {
|
|
|
|
default: assert(0 && "Unknown loc info!");
|
|
|
|
case CCValAssign::Full: break;
|
|
|
|
case CCValAssign::BCvt:
|
|
|
|
Val = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getValVT(), Val);
|
|
|
|
break;
|
2009-04-18 03:07:39 +08:00
|
|
|
}
|
2009-04-25 08:33:20 +08:00
|
|
|
|
|
|
|
ResultVals.push_back(Val);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
// Merge everything together with a MERGE_VALUES node.
|
|
|
|
ResultVals.push_back(Chain);
|
|
|
|
return DAG.getNode(ISD::MERGE_VALUES, dl, TheCall->getVTList(),
|
|
|
|
&ResultVals[0], ResultVals.size()).getNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CreateCopyOfByValArgument - Make a copy of an aggregate at address specified
|
|
|
|
/// by "Src" to address "Dst" of size "Size". Alignment information is
|
2009-04-18 04:35:10 +08:00
|
|
|
/// specified by the specific parameter attribute. The copy will be passed as
|
2009-04-18 03:07:39 +08:00
|
|
|
/// a byval function parameter.
|
|
|
|
/// Sometimes what we are copying is the end of a larger object, the part that
|
|
|
|
/// does not fit in registers.
|
|
|
|
static SDValue
|
|
|
|
CreateCopyOfByValArgument(SDValue Src, SDValue Dst, SDValue Chain,
|
|
|
|
ISD::ArgFlagsTy Flags, SelectionDAG &DAG,
|
|
|
|
DebugLoc dl) {
|
|
|
|
SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), MVT::i32);
|
|
|
|
return DAG.getMemcpy(Chain, dl, Dst, Src, SizeNode, Flags.getByValAlign(),
|
|
|
|
/*AlwaysInline=*/false, NULL, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
/// LowerMemOpCallTo - Store the argument to the stack.
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue
|
|
|
|
ARMTargetLowering::LowerMemOpCallTo(CallSDNode *TheCall, SelectionDAG &DAG,
|
|
|
|
const SDValue &StackPtr,
|
2009-04-18 04:35:10 +08:00
|
|
|
const CCValAssign &VA, SDValue Chain,
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue Arg, ISD::ArgFlagsTy Flags) {
|
|
|
|
DebugLoc dl = TheCall->getDebugLoc();
|
|
|
|
unsigned LocMemOffset = VA.getLocMemOffset();
|
|
|
|
SDValue PtrOff = DAG.getIntPtrConstant(LocMemOffset);
|
|
|
|
PtrOff = DAG.getNode(ISD::ADD, dl, getPointerTy(), StackPtr, PtrOff);
|
|
|
|
if (Flags.isByVal()) {
|
|
|
|
return CreateCopyOfByValArgument(Arg, PtrOff, Chain, Flags, DAG, dl);
|
|
|
|
}
|
|
|
|
return DAG.getStore(Chain, dl, Arg, PtrOff,
|
|
|
|
PseudoSourceValue::getStack(), LocMemOffset);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2007-02-03 16:53:01 +08:00
|
|
|
/// LowerCALL - Lowering a ISD::CALL node into a callseq_start <-
|
|
|
|
/// ARMISD:CALL <- callseq_end chain. Also add input and output parameter
|
|
|
|
/// nodes.
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerCALL(SDValue Op, SelectionDAG &DAG) {
|
2008-09-13 09:54:27 +08:00
|
|
|
CallSDNode *TheCall = cast<CallSDNode>(Op.getNode());
|
2009-04-18 03:07:39 +08:00
|
|
|
MVT RetVT = TheCall->getRetValType(0);
|
|
|
|
SDValue Chain = TheCall->getChain();
|
|
|
|
unsigned CC = TheCall->getCallingConv();
|
|
|
|
bool isVarArg = TheCall->isVarArg();
|
|
|
|
SDValue Callee = TheCall->getCallee();
|
|
|
|
DebugLoc dl = TheCall->getDebugLoc();
|
|
|
|
|
|
|
|
// Analyze operands of the call, assigning locations to each operand.
|
|
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
|
|
CCState CCInfo(CC, isVarArg, getTargetMachine(), ArgLocs);
|
2009-06-17 02:50:49 +08:00
|
|
|
CCInfo.AnalyzeCallOperands(TheCall, CCAssignFnForNode(CC, /* Return*/ false));
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
// Get a count of how many bytes are to be pushed on the stack.
|
|
|
|
unsigned NumBytes = CCInfo.getNextStackOffset();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
// Adjust the stack pointer for the new arguments...
|
|
|
|
// These operations are automatically eliminated by the prolog/epilog pass
|
2008-10-12 06:08:30 +08:00
|
|
|
Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(NumBytes, true));
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue StackPtr = DAG.getRegister(ARM::SP, MVT::i32);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass;
|
|
|
|
SmallVector<SDValue, 8> MemOpChains;
|
|
|
|
|
|
|
|
// Walk the register/memloc assignments, inserting copies/loads. In the case
|
2009-04-18 04:35:10 +08:00
|
|
|
// of tail call optimization, arguments are handled later.
|
2009-04-18 03:07:39 +08:00
|
|
|
for (unsigned i = 0, realArgIdx = 0, e = ArgLocs.size();
|
|
|
|
i != e;
|
|
|
|
++i, ++realArgIdx) {
|
|
|
|
CCValAssign &VA = ArgLocs[i];
|
|
|
|
SDValue Arg = TheCall->getArg(realArgIdx);
|
|
|
|
ISD::ArgFlagsTy Flags = TheCall->getArgFlags(realArgIdx);
|
|
|
|
|
|
|
|
// Promote the value if needed.
|
|
|
|
switch (VA.getLocInfo()) {
|
|
|
|
default: assert(0 && "Unknown loc info!");
|
|
|
|
case CCValAssign::Full: break;
|
|
|
|
case CCValAssign::SExt:
|
|
|
|
Arg = DAG.getNode(ISD::SIGN_EXTEND, dl, VA.getLocVT(), Arg);
|
|
|
|
break;
|
|
|
|
case CCValAssign::ZExt:
|
|
|
|
Arg = DAG.getNode(ISD::ZERO_EXTEND, dl, VA.getLocVT(), Arg);
|
|
|
|
break;
|
|
|
|
case CCValAssign::AExt:
|
|
|
|
Arg = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Arg);
|
|
|
|
break;
|
|
|
|
case CCValAssign::BCvt:
|
|
|
|
Arg = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getLocVT(), Arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// f64 is passed in i32 pairs and must be combined
|
|
|
|
if (VA.needsCustom()) {
|
|
|
|
SDValue fmrrd = DAG.getNode(ARMISD::FMRRD, dl,
|
|
|
|
DAG.getVTList(MVT::i32, MVT::i32), &Arg, 1);
|
|
|
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), fmrrd));
|
|
|
|
VA = ArgLocs[++i]; // skip ahead to next loc
|
|
|
|
if (VA.isRegLoc())
|
2009-04-18 04:35:10 +08:00
|
|
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), fmrrd.getValue(1)));
|
2009-04-18 03:07:39 +08:00
|
|
|
else {
|
|
|
|
assert(VA.isMemLoc());
|
|
|
|
if (StackPtr.getNode() == 0)
|
|
|
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy());
|
|
|
|
|
|
|
|
MemOpChains.push_back(LowerMemOpCallTo(TheCall, DAG, StackPtr, VA,
|
|
|
|
Chain, fmrrd.getValue(1),
|
|
|
|
Flags));
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2009-04-18 03:07:39 +08:00
|
|
|
} else if (VA.isRegLoc()) {
|
|
|
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg));
|
2007-01-19 15:51:42 +08:00
|
|
|
} else {
|
2009-04-18 03:07:39 +08:00
|
|
|
assert(VA.isMemLoc());
|
|
|
|
if (StackPtr.getNode() == 0)
|
|
|
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy());
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
MemOpChains.push_back(LowerMemOpCallTo(TheCall, DAG, StackPtr, VA,
|
|
|
|
Chain, Arg, Flags));
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!MemOpChains.empty())
|
2009-02-05 04:06:27 +08:00
|
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
2007-01-19 15:51:42 +08:00
|
|
|
&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.
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue InFlag;
|
2007-01-19 15:51:42 +08:00
|
|
|
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) {
|
2009-03-21 06:42:55 +08:00
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first,
|
2009-02-05 04:06:27 +08:00
|
|
|
RegsToPass[i].second, InFlag);
|
2007-01-19 15:51:42 +08:00
|
|
|
InFlag = Chain.getValue(1);
|
|
|
|
}
|
|
|
|
|
2008-09-17 05:48:12 +08:00
|
|
|
// 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.
|
2007-01-19 15:51:42 +08:00
|
|
|
bool isDirect = false;
|
|
|
|
bool isARMFunc = false;
|
2007-06-20 05:05:09 +08:00
|
|
|
bool isLocalARMFunc = false;
|
2007-01-19 15:51:42 +08:00
|
|
|
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
|
|
|
|
GlobalValue *GV = G->getGlobal();
|
|
|
|
isDirect = true;
|
2007-01-31 04:08:39 +08:00
|
|
|
bool isExt = (GV->isDeclaration() || GV->hasWeakLinkage() ||
|
2007-01-19 15:51:42 +08:00
|
|
|
GV->hasLinkOnceLinkage());
|
2007-01-20 03:28:01 +08:00
|
|
|
bool isStub = (isExt && Subtarget->isTargetDarwin()) &&
|
2007-01-19 15:51:42 +08:00
|
|
|
getTargetMachine().getRelocationModel() != Reloc::Static;
|
|
|
|
isARMFunc = !Subtarget->isThumb() || isStub;
|
2007-06-20 05:05:09 +08:00
|
|
|
// ARM call to a local ARM function is predicable.
|
|
|
|
isLocalARMFunc = !Subtarget->isThumb() && !isExt;
|
2007-01-31 04:37:08 +08:00
|
|
|
// tBX takes a register source operand.
|
|
|
|
if (isARMFunc && Subtarget->isThumb() && !Subtarget->hasV5TOps()) {
|
|
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex,
|
|
|
|
ARMCP::CPStub, 4);
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2009-03-21 06:42:55 +08:00
|
|
|
Callee = DAG.getLoad(getPointerTy(), dl,
|
|
|
|
DAG.getEntryNode(), CPAddr, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-03-21 06:42:55 +08:00
|
|
|
Callee = DAG.getNode(ARMISD::PIC_ADD, dl,
|
2009-02-05 04:06:27 +08:00
|
|
|
getPointerTy(), Callee, PICLabel);
|
2007-01-31 04:37:08 +08:00
|
|
|
} else
|
|
|
|
Callee = DAG.getTargetGlobalAddress(GV, getPointerTy());
|
2008-09-17 05:48:12 +08:00
|
|
|
} else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
|
2007-01-19 15:51:42 +08:00
|
|
|
isDirect = true;
|
2007-01-20 03:28:01 +08:00
|
|
|
bool isStub = Subtarget->isTargetDarwin() &&
|
2007-01-19 15:51:42 +08:00
|
|
|
getTargetMachine().getRelocationModel() != Reloc::Static;
|
|
|
|
isARMFunc = !Subtarget->isThumb() || isStub;
|
2007-01-31 04:37:08 +08:00
|
|
|
// tBX takes a register source operand.
|
|
|
|
const char *Sym = S->getSymbol();
|
|
|
|
if (isARMFunc && Subtarget->isThumb() && !Subtarget->hasV5TOps()) {
|
|
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(Sym, ARMPCLabelIndex,
|
|
|
|
ARMCP::CPStub, 4);
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2009-02-05 04:06:27 +08:00
|
|
|
Callee = DAG.getLoad(getPointerTy(), dl,
|
2009-03-21 06:42:55 +08:00
|
|
|
DAG.getEntryNode(), CPAddr, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-03-21 06:42:55 +08:00
|
|
|
Callee = DAG.getNode(ARMISD::PIC_ADD, dl,
|
2009-02-05 04:06:27 +08:00
|
|
|
getPointerTy(), Callee, PICLabel);
|
2007-01-31 04:37:08 +08:00
|
|
|
} else
|
2008-09-17 05:48:12 +08:00
|
|
|
Callee = DAG.getTargetExternalSymbol(Sym, getPointerTy());
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2007-03-21 01:57:23 +08:00
|
|
|
// 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())
|
2007-06-20 05:05:09 +08:00
|
|
|
? (isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL)
|
|
|
|
: ARMISD::CALL_NOLINK;
|
2007-03-21 01:57:23 +08:00
|
|
|
}
|
2007-03-28 00:19:21 +08:00
|
|
|
if (CallOpc == ARMISD::CALL_NOLINK && !Subtarget->isThumb()) {
|
|
|
|
// implicit def LR - LR mustn't be allocated as GRP:$dst of CALL_NOLINK
|
2009-02-07 07:05:02 +08:00
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, ARM::LR, DAG.getUNDEF(MVT::i32),InFlag);
|
2007-03-21 01:57:23 +08:00
|
|
|
InFlag = Chain.getValue(1);
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
std::vector<SDValue> Ops;
|
2007-01-19 15:51:42 +08:00
|
|
|
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()));
|
|
|
|
|
2008-08-29 05:40:38 +08:00
|
|
|
if (InFlag.getNode())
|
2007-01-19 15:51:42 +08:00
|
|
|
Ops.push_back(InFlag);
|
2008-07-03 01:40:58 +08:00
|
|
|
// Returns a chain and a flag for retval copy to use.
|
2009-02-05 04:06:27 +08:00
|
|
|
Chain = DAG.getNode(CallOpc, dl, DAG.getVTList(MVT::Other, MVT::Flag),
|
2008-07-03 01:40:58 +08:00
|
|
|
&Ops[0], Ops.size());
|
2007-01-19 15:51:42 +08:00
|
|
|
InFlag = Chain.getValue(1);
|
|
|
|
|
2008-10-12 06:08:30 +08:00
|
|
|
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, true),
|
|
|
|
DAG.getIntPtrConstant(0, true), InFlag);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (RetVT != MVT::Other)
|
|
|
|
InFlag = Chain.getValue(1);
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
// Handle result values, copying them out of physregs into vregs that we
|
|
|
|
// return.
|
|
|
|
return SDValue(LowerCallResult(Chain, InFlag, TheCall, CC, DAG),
|
|
|
|
Op.getResNo());
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue ARMTargetLowering::LowerRET(SDValue Op, SelectionDAG &DAG) {
|
|
|
|
// The chain is always operand #0
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain = Op.getOperand(0);
|
2009-02-05 07:02:30 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// CCValAssign - represent the assignment of the return value to a location.
|
2009-04-18 03:07:39 +08:00
|
|
|
SmallVector<CCValAssign, 16> RVLocs;
|
|
|
|
unsigned CC = DAG.getMachineFunction().getFunction()->getCallingConv();
|
|
|
|
bool isVarArg = DAG.getMachineFunction().getFunction()->isVarArg();
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// CCState - Info about the registers and stack slots.
|
2009-04-18 03:07:39 +08:00
|
|
|
CCState CCInfo(CC, isVarArg, getTargetMachine(), RVLocs);
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Analyze return values of ISD::RET.
|
2009-06-17 02:50:49 +08:00
|
|
|
CCInfo.AnalyzeReturn(Op.getNode(), CCAssignFnForNode(CC, /* Return */ true));
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
// If this is the first return lowered for this function, add
|
|
|
|
// the regs to the liveout set for the function.
|
|
|
|
if (DAG.getMachineFunction().getRegInfo().liveout_empty()) {
|
|
|
|
for (unsigned i = 0; i != RVLocs.size(); ++i)
|
|
|
|
if (RVLocs[i].isRegLoc())
|
|
|
|
DAG.getMachineFunction().getRegInfo().addLiveOut(RVLocs[i].getLocReg());
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
SDValue Flag;
|
|
|
|
|
|
|
|
// Copy the result values into the output registers.
|
|
|
|
for (unsigned i = 0, realRVLocIdx = 0;
|
|
|
|
i != RVLocs.size();
|
|
|
|
++i, ++realRVLocIdx) {
|
|
|
|
CCValAssign &VA = RVLocs[i];
|
|
|
|
assert(VA.isRegLoc() && "Can only return in registers!");
|
|
|
|
|
|
|
|
// ISD::RET => ret chain, (regnum1,val1), ...
|
|
|
|
// So i*2+1 index only the regnums
|
|
|
|
SDValue Arg = Op.getOperand(realRVLocIdx*2+1);
|
|
|
|
|
|
|
|
switch (VA.getLocInfo()) {
|
|
|
|
default: assert(0 && "Unknown loc info!");
|
|
|
|
case CCValAssign::Full: break;
|
|
|
|
case CCValAssign::BCvt:
|
|
|
|
Arg = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getLocVT(), Arg);
|
|
|
|
break;
|
2008-07-12 04:53:00 +08:00
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
// Legalize ret f64 -> ret 2 x i32. We always have fmrrd if f64 is
|
|
|
|
// available.
|
|
|
|
if (VA.needsCustom()) {
|
|
|
|
SDValue fmrrd = DAG.getNode(ARMISD::FMRRD, dl,
|
|
|
|
DAG.getVTList(MVT::i32, MVT::i32), &Arg, 1);
|
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), fmrrd, Flag);
|
2009-04-25 01:00:36 +08:00
|
|
|
Flag = Chain.getValue(1);
|
2009-04-18 03:07:39 +08:00
|
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), fmrrd.getValue(1),
|
|
|
|
Flag);
|
|
|
|
} else
|
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), Arg, Flag);
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Guarantee that all emitted copies are
|
|
|
|
// stuck together, avoiding something bad.
|
2009-04-18 03:07:39 +08:00
|
|
|
Flag = Chain.getValue(1);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue result;
|
|
|
|
if (Flag.getNode())
|
|
|
|
result = DAG.getNode(ARMISD::RET_FLAG, dl, MVT::Other, Chain, Flag);
|
|
|
|
else // Return Void
|
|
|
|
result = DAG.getNode(ARMISD::RET_FLAG, dl, MVT::Other, Chain);
|
|
|
|
|
|
|
|
return result;
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-03-21 06:42:55 +08:00
|
|
|
// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as
|
2008-09-17 05:48:12 +08:00
|
|
|
// 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 MOVi.
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = Op.getValueType();
|
2009-02-07 08:55:49 +08:00
|
|
|
// FIXME there is no actual debug info here
|
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Res;
|
2007-01-19 15:51:42 +08:00
|
|
|
if (CP->isMachineConstantPoolEntry())
|
|
|
|
Res = DAG.getTargetConstantPool(CP->getMachineCPVal(), PtrVT,
|
|
|
|
CP->getAlignment());
|
|
|
|
else
|
|
|
|
Res = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT,
|
|
|
|
CP->getAlignment());
|
2009-02-07 08:55:49 +08:00
|
|
|
return DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Res);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2007-04-27 21:54:47 +08:00
|
|
|
// Lower ISD::GlobalTLSAddress using the "general dynamic" model
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue
|
2007-04-27 21:54:47 +08:00
|
|
|
ARMTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
|
|
|
|
SelectionDAG &DAG) {
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = GA->getDebugLoc();
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = getPointerTy();
|
2007-04-27 21:54:47 +08:00
|
|
|
unsigned char PCAdj = Subtarget->isThumb() ? 4 : 8;
|
|
|
|
ARMConstantPoolValue *CPV =
|
|
|
|
new ARMConstantPoolValue(GA->getGlobal(), ARMPCLabelIndex, ARMCP::CPValue,
|
|
|
|
PCAdj, "tlsgd", true);
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue Argument = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
Argument = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Argument);
|
2009-02-05 04:06:27 +08:00
|
|
|
Argument = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Argument, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain = Argument.getValue(1);
|
2007-04-27 21:54:47 +08:00
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-02-05 04:06:27 +08:00
|
|
|
Argument = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Argument, PICLabel);
|
2007-04-27 21:54:47 +08:00
|
|
|
|
|
|
|
// call __tls_get_addr.
|
|
|
|
ArgListTy Args;
|
|
|
|
ArgListEntry Entry;
|
|
|
|
Entry.Node = Argument;
|
|
|
|
Entry.Ty = (const Type *) Type::Int32Ty;
|
|
|
|
Args.push_back(Entry);
|
2009-01-31 07:10:59 +08:00
|
|
|
// FIXME: is there useful debug info available here?
|
2008-07-28 05:46:04 +08:00
|
|
|
std::pair<SDValue, SDValue> CallResult =
|
2008-09-27 03:31:26 +08:00
|
|
|
LowerCallTo(Chain, (const Type *) Type::Int32Ty, false, false, false, false,
|
2007-04-27 21:54:47 +08:00
|
|
|
CallingConv::C, false,
|
2009-02-05 04:06:27 +08:00
|
|
|
DAG.getExternalSymbol("__tls_get_addr", PtrVT), Args, DAG, dl);
|
2007-04-27 21:54:47 +08:00
|
|
|
return CallResult.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lower ISD::GlobalTLSAddress using the "initial exec" or
|
|
|
|
// "local exec" model.
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue
|
2007-04-27 21:54:47 +08:00
|
|
|
ARMTargetLowering::LowerToTLSExecModels(GlobalAddressSDNode *GA,
|
2009-03-21 06:42:55 +08:00
|
|
|
SelectionDAG &DAG) {
|
2007-04-27 21:54:47 +08:00
|
|
|
GlobalValue *GV = GA->getGlobal();
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = GA->getDebugLoc();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Offset;
|
|
|
|
SDValue Chain = DAG.getEntryNode();
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = getPointerTy();
|
2007-04-27 21:54:47 +08:00
|
|
|
// Get the Thread Pointer
|
2009-02-05 04:06:27 +08:00
|
|
|
SDValue ThreadPointer = DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT);
|
2007-04-27 21:54:47 +08:00
|
|
|
|
|
|
|
if (GV->isDeclaration()){
|
|
|
|
// initial exec model
|
|
|
|
unsigned char PCAdj = Subtarget->isThumb() ? 4 : 8;
|
|
|
|
ARMConstantPoolValue *CPV =
|
|
|
|
new ARMConstantPoolValue(GA->getGlobal(), ARMPCLabelIndex, ARMCP::CPValue,
|
|
|
|
PCAdj, "gottpoff", true);
|
2009-03-13 15:51:59 +08:00
|
|
|
Offset = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
Offset = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Offset);
|
2009-02-05 04:06:27 +08:00
|
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
2007-04-27 21:54:47 +08:00
|
|
|
Chain = Offset.getValue(1);
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-02-05 04:06:27 +08:00
|
|
|
Offset = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Offset, PICLabel);
|
2007-04-27 21:54:47 +08:00
|
|
|
|
2009-02-05 04:06:27 +08:00
|
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
2007-04-27 21:54:47 +08:00
|
|
|
} else {
|
|
|
|
// local exec model
|
|
|
|
ARMConstantPoolValue *CPV =
|
|
|
|
new ARMConstantPoolValue(GV, ARMCP::CPValue, "tpoff");
|
2009-03-13 15:51:59 +08:00
|
|
|
Offset = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
Offset = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Offset);
|
2009-02-05 04:06:27 +08:00
|
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
2007-04-27 21:54:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// The address of the thread local variable is the add of the thread
|
|
|
|
// pointer with the offset of the variable.
|
2009-02-05 04:06:27 +08:00
|
|
|
return DAG.getNode(ISD::ADD, dl, PtrVT, ThreadPointer, Offset);
|
2007-04-27 21:54:47 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue
|
|
|
|
ARMTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) {
|
2007-04-27 21:54:47 +08:00
|
|
|
// TODO: implement the "local dynamic" model
|
|
|
|
assert(Subtarget->isTargetELF() &&
|
|
|
|
"TLS not implemented for non-ELF targets");
|
|
|
|
GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
|
|
|
|
// If the relocation model is PIC, use the "General Dynamic" TLS Model,
|
|
|
|
// otherwise use the "Local Exec" TLS Model
|
|
|
|
if (getTargetMachine().getRelocationModel() == Reloc::PIC_)
|
|
|
|
return LowerToTLSGeneralDynamicModel(GA, DAG);
|
|
|
|
else
|
|
|
|
return LowerToTLSExecModels(GA, DAG);
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op,
|
2009-03-21 06:42:55 +08:00
|
|
|
SelectionDAG &DAG) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = getPointerTy();
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-04-22 08:04:12 +08:00
|
|
|
GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal();
|
|
|
|
Reloc::Model RelocM = getTargetMachine().getRelocationModel();
|
|
|
|
if (RelocM == Reloc::PIC_) {
|
2009-01-16 04:18:42 +08:00
|
|
|
bool UseGOTOFF = GV->hasLocalLinkage() || GV->hasHiddenVisibility();
|
2007-04-22 08:04:12 +08:00
|
|
|
ARMConstantPoolValue *CPV =
|
|
|
|
new ARMConstantPoolValue(GV, ARMCP::CPValue, UseGOTOFF ? "GOTOFF":"GOT");
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2009-03-21 06:42:55 +08:00
|
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(),
|
2009-02-05 04:06:27 +08:00
|
|
|
CPAddr, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain = Result.getValue(1);
|
2009-02-07 08:55:49 +08:00
|
|
|
SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(PtrVT);
|
2009-02-05 04:06:27 +08:00
|
|
|
Result = DAG.getNode(ISD::ADD, dl, PtrVT, Result, GOT);
|
2007-04-22 08:04:12 +08:00
|
|
|
if (!UseGOTOFF)
|
2009-02-05 04:06:27 +08:00
|
|
|
Result = DAG.getLoad(PtrVT, dl, Chain, Result, NULL, 0);
|
2007-04-22 08:04:12 +08:00
|
|
|
return Result;
|
|
|
|
} else {
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2009-02-05 04:06:27 +08:00
|
|
|
return DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
2007-04-22 08:04:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
/// GVIsIndirectSymbol - true if the GV will be accessed via an indirect symbol
|
2007-05-04 08:26:58 +08:00
|
|
|
/// even in non-static mode.
|
|
|
|
static bool GVIsIndirectSymbol(GlobalValue *GV, Reloc::Model RelocM) {
|
2008-12-05 09:06:39 +08:00
|
|
|
// If symbol visibility is hidden, the extra load is not needed if
|
|
|
|
// the symbol is definitely defined in the current translation unit.
|
|
|
|
bool isDecl = GV->isDeclaration() && !GV->hasNotBeenReadFromBitcode();
|
|
|
|
if (GV->hasHiddenVisibility() && (!isDecl && !GV->hasCommonLinkage()))
|
|
|
|
return false;
|
Introduce new linkage types linkonce_odr, weak_odr, common_odr
and extern_weak_odr. These are the same as the non-odr versions,
except that they indicate that the global will only be overridden
by an *equivalent* global. In C, a function with weak linkage can
be overridden by a function which behaves completely differently.
This means that IP passes have to skip weak functions, since any
deductions made from the function definition might be wrong, since
the definition could be replaced by something completely different
at link time. This is not allowed in C++, thanks to the ODR
(One-Definition-Rule): if a function is replaced by another at
link-time, then the new function must be the same as the original
function. If a language knows that a function or other global can
only be overridden by an equivalent global, it can give it the
weak_odr linkage type, and the optimizers will understand that it
is alright to make deductions based on the function body. The
code generators on the other hand map weak and weak_odr linkage
to the same thing.
llvm-svn: 66339
2009-03-07 23:45:40 +08:00
|
|
|
return RelocM != Reloc::Static && (isDecl || GV->isWeakForLinker());
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerGlobalAddressDarwin(SDValue Op,
|
2009-03-21 06:42:55 +08:00
|
|
|
SelectionDAG &DAG) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = getPointerTy();
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal();
|
|
|
|
Reloc::Model RelocM = getTargetMachine().getRelocationModel();
|
2007-05-04 08:26:58 +08:00
|
|
|
bool IsIndirect = GVIsIndirectSymbol(GV, RelocM);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue CPAddr;
|
2007-01-19 15:51:42 +08:00
|
|
|
if (RelocM == Reloc::Static)
|
2009-03-13 15:51:59 +08:00
|
|
|
CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 4);
|
2007-01-19 15:51:42 +08:00
|
|
|
else {
|
|
|
|
unsigned PCAdj = (RelocM != Reloc::PIC_)
|
|
|
|
? 0 : (Subtarget->isThumb() ? 4 : 8);
|
2007-01-31 04:37:08 +08:00
|
|
|
ARMCP::ARMCPKind Kind = IsIndirect ? ARMCP::CPNonLazyPtr
|
|
|
|
: ARMCP::CPValue;
|
2007-01-19 15:51:42 +08:00
|
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex,
|
2007-01-31 04:37:08 +08:00
|
|
|
Kind, PCAdj);
|
2009-03-13 15:51:59 +08:00
|
|
|
CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-02-05 04:06:27 +08:00
|
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain = Result.getValue(1);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
if (RelocM == Reloc::PIC_) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-02-05 04:06:27 +08:00
|
|
|
Result = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Result, PICLabel);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
if (IsIndirect)
|
2009-02-05 04:06:27 +08:00
|
|
|
Result = DAG.getLoad(PtrVT, dl, Chain, Result, NULL, 0);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDValue Op,
|
2009-03-21 06:42:55 +08:00
|
|
|
SelectionDAG &DAG){
|
2007-04-22 08:04:12 +08:00
|
|
|
assert(Subtarget->isTargetELF() &&
|
|
|
|
"GLOBAL OFFSET TABLE not implemented for non-ELF targets");
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = getPointerTy();
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-04-22 08:04:12 +08:00
|
|
|
unsigned PCAdj = Subtarget->isThumb() ? 4 : 8;
|
|
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue("_GLOBAL_OFFSET_TABLE_",
|
|
|
|
ARMPCLabelIndex,
|
|
|
|
ARMCP::CPValue, PCAdj);
|
2009-03-13 15:51:59 +08:00
|
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
2009-02-07 08:55:49 +08:00
|
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
2009-02-05 04:06:27 +08:00
|
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
2009-02-05 04:06:27 +08:00
|
|
|
return DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Result, PICLabel);
|
2007-04-22 08:04:12 +08:00
|
|
|
}
|
|
|
|
|
2009-05-13 07:59:14 +08:00
|
|
|
SDValue
|
|
|
|
ARMTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
2008-09-13 00:56:44 +08:00
|
|
|
unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
|
2009-05-13 07:59:14 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-11-09 01:20:05 +08:00
|
|
|
switch (IntNo) {
|
2008-07-28 05:46:04 +08:00
|
|
|
default: return SDValue(); // Don't custom lower most intrinsics.
|
2007-11-09 01:20:05 +08:00
|
|
|
case Intrinsic::arm_thread_pointer:
|
2009-05-13 07:59:14 +08:00
|
|
|
return DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT);
|
2009-05-14 08:46:35 +08:00
|
|
|
case Intrinsic::eh_sjlj_setjmp:
|
|
|
|
SDValue Res = DAG.getNode(ARMISD::EH_SJLJ_SETJMP, dl, MVT::i32,
|
2009-05-13 07:59:14 +08:00
|
|
|
Op.getOperand(1));
|
|
|
|
return Res;
|
2007-11-09 01:20:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG,
|
2009-03-21 06:42:55 +08:00
|
|
|
unsigned VarArgsFrameIndex) {
|
2007-01-19 15:51:42 +08:00
|
|
|
// vastart just stores the address of the VarArgsFrameIndex slot into the
|
|
|
|
// memory location argument.
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue FR = DAG.getFrameIndex(VarArgsFrameIndex, PtrVT);
|
2008-02-07 06:27:42 +08:00
|
|
|
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
|
2009-02-05 04:06:27 +08:00
|
|
|
return DAG.getStore(Op.getOperand(0), dl, FR, Op.getOperand(1), SV, 0);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue
|
|
|
|
ARMTargetLowering::LowerFORMAL_ARGUMENTS(SDValue Op, SelectionDAG &DAG) {
|
2007-01-19 15:51:42 +08:00
|
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
2009-04-18 03:07:39 +08:00
|
|
|
MachineFrameInfo *MFI = MF.getFrameInfo();
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Root = Op.getOperand(0);
|
2009-04-18 03:07:39 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
bool isVarArg = cast<ConstantSDNode>(Op.getOperand(2))->getZExtValue() != 0;
|
|
|
|
unsigned CC = MF.getFunction()->getCallingConv();
|
2009-04-08 04:34:09 +08:00
|
|
|
ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
// Assign locations to all of the incoming arguments.
|
|
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
|
|
CCState CCInfo(CC, isVarArg, getTargetMachine(), ArgLocs);
|
2009-06-17 02:50:49 +08:00
|
|
|
CCInfo.AnalyzeFormalArguments(Op.getNode(),
|
|
|
|
CCAssignFnForNode(CC, /* Return*/ false));
|
2009-04-18 03:07:39 +08:00
|
|
|
|
|
|
|
SmallVector<SDValue, 16> ArgValues;
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
|
|
|
|
CCValAssign &VA = ArgLocs[i];
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Arguments stored in registers.
|
2009-04-18 03:07:39 +08:00
|
|
|
if (VA.isRegLoc()) {
|
|
|
|
MVT RegVT = VA.getLocVT();
|
|
|
|
TargetRegisterClass *RC;
|
|
|
|
if (AFI->isThumbFunction())
|
|
|
|
RC = ARM::tGPRRegisterClass;
|
|
|
|
else
|
|
|
|
RC = ARM::GPRRegisterClass;
|
|
|
|
|
2009-06-09 06:53:56 +08:00
|
|
|
if (FloatABIType == FloatABI::Hard) {
|
|
|
|
if (RegVT == MVT::f32)
|
|
|
|
RC = ARM::SPRRegisterClass;
|
|
|
|
else if (RegVT == MVT::f64)
|
|
|
|
RC = ARM::DPRRegisterClass;
|
|
|
|
} else if (RegVT == MVT::f64) {
|
2009-04-18 04:35:10 +08:00
|
|
|
// f64 is passed in pairs of GPRs and must be combined.
|
2009-04-18 03:07:39 +08:00
|
|
|
RegVT = MVT::i32;
|
|
|
|
} else if (!((RegVT == MVT::i32) || (RegVT == MVT::f32)))
|
|
|
|
assert(0 && "RegVT not supported by FORMAL_ARGUMENTS Lowering");
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Transform the arguments stored in physical registers into virtual ones.
|
2009-04-21 02:36:57 +08:00
|
|
|
unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC);
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue ArgValue = DAG.getCopyFromReg(Root, dl, Reg, RegVT);
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// f64 is passed in i32 pairs and must be combined.
|
2009-04-18 03:07:39 +08:00
|
|
|
if (VA.needsCustom()) {
|
|
|
|
SDValue ArgValue2;
|
|
|
|
|
|
|
|
VA = ArgLocs[++i]; // skip ahead to next loc
|
|
|
|
if (VA.isMemLoc()) {
|
2009-04-25 01:05:01 +08:00
|
|
|
// must be APCS to split like this
|
2009-04-18 03:07:39 +08:00
|
|
|
unsigned ArgSize = VA.getLocVT().getSizeInBits()/8;
|
|
|
|
int FI = MFI->CreateFixedObject(ArgSize, VA.getLocMemOffset());
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Create load node to retrieve arguments from the stack.
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue FIN = DAG.getFrameIndex(FI, getPointerTy());
|
|
|
|
ArgValue2 = DAG.getLoad(MVT::i32, dl, Root, FIN, NULL, 0);
|
|
|
|
} else {
|
2009-04-21 02:36:57 +08:00
|
|
|
Reg = MF.addLiveIn(VA.getLocReg(), RC);
|
2009-04-18 03:07:39 +08:00
|
|
|
ArgValue2 = DAG.getCopyFromReg(Root, dl, Reg, MVT::i32);
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
ArgValue = DAG.getNode(ARMISD::FMDRR, dl, MVT::f64,
|
|
|
|
ArgValue, ArgValue2);
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
// If this is an 8 or 16-bit value, it is really passed promoted
|
|
|
|
// to 32 bits. Insert an assert[sz]ext to capture this, then
|
|
|
|
// truncate to the right size.
|
|
|
|
switch (VA.getLocInfo()) {
|
|
|
|
default: assert(0 && "Unknown loc info!");
|
|
|
|
case CCValAssign::Full: break;
|
|
|
|
case CCValAssign::BCvt:
|
|
|
|
ArgValue = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getValVT(), ArgValue);
|
|
|
|
break;
|
|
|
|
case CCValAssign::SExt:
|
|
|
|
ArgValue = DAG.getNode(ISD::AssertSext, dl, RegVT, ArgValue,
|
|
|
|
DAG.getValueType(VA.getValVT()));
|
|
|
|
ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue);
|
|
|
|
break;
|
|
|
|
case CCValAssign::ZExt:
|
|
|
|
ArgValue = DAG.getNode(ISD::AssertZext, dl, RegVT, ArgValue,
|
|
|
|
DAG.getValueType(VA.getValVT()));
|
|
|
|
ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue);
|
|
|
|
break;
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
ArgValues.push_back(ArgValue);
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
} else { // VA.isRegLoc()
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
// sanity check
|
|
|
|
assert(VA.isMemLoc());
|
|
|
|
assert(VA.getValVT() != MVT::i64 && "i64 should already be lowered");
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 03:07:39 +08:00
|
|
|
unsigned ArgSize = VA.getLocVT().getSizeInBits()/8;
|
|
|
|
int FI = MFI->CreateFixedObject(ArgSize, VA.getLocMemOffset());
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
// Create load nodes to retrieve arguments from the stack.
|
2009-04-18 03:07:39 +08:00
|
|
|
SDValue FIN = DAG.getFrameIndex(FI, getPointerTy());
|
|
|
|
ArgValues.push_back(DAG.getLoad(VA.getValVT(), dl, Root, FIN, NULL, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// varargs
|
2007-01-19 15:51:42 +08:00
|
|
|
if (isVarArg) {
|
|
|
|
static const unsigned GPRArgRegs[] = {
|
|
|
|
ARM::R0, ARM::R1, ARM::R2, ARM::R3
|
|
|
|
};
|
|
|
|
|
2009-04-18 04:35:10 +08:00
|
|
|
unsigned NumGPRs = CCInfo.getFirstUnallocated
|
|
|
|
(GPRArgRegs, sizeof(GPRArgRegs) / sizeof(GPRArgRegs[0]));
|
2009-04-18 03:07:39 +08:00
|
|
|
|
2007-02-24 04:32:57 +08:00
|
|
|
unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
|
|
|
|
unsigned VARegSize = (4 - NumGPRs) * 4;
|
|
|
|
unsigned VARegSaveSize = (VARegSize + Align - 1) & ~(Align - 1);
|
2009-04-18 03:07:39 +08:00
|
|
|
unsigned ArgOffset = 0;
|
2007-01-19 15:51:42 +08:00
|
|
|
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);
|
2009-04-18 03:07:39 +08:00
|
|
|
ArgOffset = CCInfo.getNextStackOffset();
|
2007-02-24 04:32:57 +08:00
|
|
|
VarArgsFrameIndex = MFI->CreateFixedObject(VARegSaveSize, ArgOffset +
|
|
|
|
VARegSaveSize - VARegSize);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue FIN = DAG.getFrameIndex(VarArgsFrameIndex, getPointerTy());
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SmallVector<SDValue, 4> MemOps;
|
2007-01-19 15:51:42 +08:00
|
|
|
for (; NumGPRs < 4; ++NumGPRs) {
|
2009-04-18 03:07:39 +08:00
|
|
|
TargetRegisterClass *RC;
|
2009-04-08 04:34:09 +08:00
|
|
|
if (AFI->isThumbFunction())
|
2009-04-18 03:07:39 +08:00
|
|
|
RC = ARM::tGPRRegisterClass;
|
2009-04-08 04:34:09 +08:00
|
|
|
else
|
2009-04-18 03:07:39 +08:00
|
|
|
RC = ARM::GPRRegisterClass;
|
|
|
|
|
2009-04-21 02:36:57 +08:00
|
|
|
unsigned VReg = MF.addLiveIn(GPRArgRegs[NumGPRs], RC);
|
2009-02-05 04:06:27 +08:00
|
|
|
SDValue Val = DAG.getCopyFromReg(Root, dl, VReg, MVT::i32);
|
|
|
|
SDValue Store = DAG.getStore(Val.getValue(1), dl, Val, FIN, NULL, 0);
|
2007-01-19 15:51:42 +08:00
|
|
|
MemOps.push_back(Store);
|
2009-02-05 04:06:27 +08:00
|
|
|
FIN = DAG.getNode(ISD::ADD, dl, getPointerTy(), FIN,
|
2007-01-19 15:51:42 +08:00
|
|
|
DAG.getConstant(4, getPointerTy()));
|
|
|
|
}
|
|
|
|
if (!MemOps.empty())
|
2009-02-05 04:06:27 +08:00
|
|
|
Root = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
2007-01-19 15:51:42 +08:00
|
|
|
&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.
|
2009-02-05 04:06:27 +08:00
|
|
|
return DAG.getNode(ISD::MERGE_VALUES, dl, Op.getNode()->getVTList(),
|
2009-04-18 03:07:39 +08:00
|
|
|
&ArgValues[0], ArgValues.size()).getValue(Op.getResNo());
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// isFloatingPointZero - Return true if this is +0.0.
|
2008-07-28 05:46:04 +08:00
|
|
|
static bool isFloatingPointZero(SDValue Op) {
|
2007-01-19 15:51:42 +08:00
|
|
|
if (ConstantFPSDNode *CFP = dyn_cast<ConstantFPSDNode>(Op))
|
2007-08-31 12:03:46 +08:00
|
|
|
return CFP->getValueAPF().isPosZero();
|
2008-08-29 05:40:38 +08:00
|
|
|
else if (ISD::isEXTLoad(Op.getNode()) || ISD::isNON_EXTLoad(Op.getNode())) {
|
2007-01-19 15:51:42 +08:00
|
|
|
// Maybe this has already been legalized into the constant pool?
|
|
|
|
if (Op.getOperand(1).getOpcode() == ARMISD::Wrapper) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue WrapperOp = Op.getOperand(1).getOperand(0);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(WrapperOp))
|
|
|
|
if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal()))
|
2007-08-31 12:03:46 +08:00
|
|
|
return CFP->getValueAPF().isPosZero();
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-02-02 09:53:26 +08:00
|
|
|
static bool isLegalCmpImmediate(unsigned C, bool isThumb) {
|
2007-01-19 15:51:42 +08:00
|
|
|
return ( isThumb && (C & ~255U) == 0) ||
|
|
|
|
(!isThumb && ARM_AM::getSOImmVal(C) != -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns appropriate ARM CMP (cmp) and corresponding condition code for
|
|
|
|
/// the given operands.
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue &ARMCC, SelectionDAG &DAG, bool isThumb,
|
|
|
|
DebugLoc dl) {
|
2008-08-29 05:40:38 +08:00
|
|
|
if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS.getNode())) {
|
2008-09-13 00:56:44 +08:00
|
|
|
unsigned C = RHSC->getZExtValue();
|
2007-01-19 15:51:42 +08:00
|
|
|
if (!isLegalCmpImmediate(C, isThumb)) {
|
|
|
|
// Constant does not fit, try adjusting it by one?
|
|
|
|
switch (CC) {
|
|
|
|
default: break;
|
|
|
|
case ISD::SETLT:
|
|
|
|
case ISD::SETGE:
|
|
|
|
if (isLegalCmpImmediate(C-1, isThumb)) {
|
2007-02-02 09:53:26 +08:00
|
|
|
CC = (CC == ISD::SETLT) ? ISD::SETLE : ISD::SETGT;
|
|
|
|
RHS = DAG.getConstant(C-1, MVT::i32);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISD::SETULT:
|
|
|
|
case ISD::SETUGE:
|
|
|
|
if (C > 0 && isLegalCmpImmediate(C-1, isThumb)) {
|
|
|
|
CC = (CC == ISD::SETULT) ? ISD::SETULE : ISD::SETUGT;
|
2007-01-19 15:51:42 +08:00
|
|
|
RHS = DAG.getConstant(C-1, MVT::i32);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISD::SETLE:
|
|
|
|
case ISD::SETGT:
|
|
|
|
if (isLegalCmpImmediate(C+1, isThumb)) {
|
2007-02-02 09:53:26 +08:00
|
|
|
CC = (CC == ISD::SETLE) ? ISD::SETLT : ISD::SETGE;
|
|
|
|
RHS = DAG.getConstant(C+1, MVT::i32);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISD::SETULE:
|
|
|
|
case ISD::SETUGT:
|
|
|
|
if (C < 0xffffffff && isLegalCmpImmediate(C+1, isThumb)) {
|
|
|
|
CC = (CC == ISD::SETULE) ? ISD::SETULT : ISD::SETUGE;
|
2007-01-19 15:51:42 +08:00
|
|
|
RHS = DAG.getConstant(C+1, MVT::i32);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ARMCC::CondCodes CondCode = IntCCToARMCC(CC);
|
2007-04-02 09:30:03 +08:00
|
|
|
ARMISD::NodeType CompareType;
|
|
|
|
switch (CondCode) {
|
|
|
|
default:
|
|
|
|
CompareType = ARMISD::CMP;
|
|
|
|
break;
|
|
|
|
case ARMCC::EQ:
|
|
|
|
case ARMCC::NE:
|
|
|
|
case ARMCC::MI:
|
|
|
|
case ARMCC::PL:
|
|
|
|
// Uses only N and Z Flags
|
|
|
|
CompareType = ARMISD::CMPNZ;
|
|
|
|
break;
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
2009-02-07 05:50:26 +08:00
|
|
|
return DAG.getNode(CompareType, dl, MVT::Flag, LHS, RHS);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a appropriate VFP CMP (fcmp{s|d}+fmstat) for the given operands.
|
2009-03-21 06:42:55 +08:00
|
|
|
static SDValue getVFPCmp(SDValue LHS, SDValue RHS, SelectionDAG &DAG,
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Cmp;
|
2007-01-19 15:51:42 +08:00
|
|
|
if (!isFloatingPointZero(RHS))
|
2009-02-07 05:50:26 +08:00
|
|
|
Cmp = DAG.getNode(ARMISD::CMPFP, dl, MVT::Flag, LHS, RHS);
|
2007-01-19 15:51:42 +08:00
|
|
|
else
|
2009-02-07 05:50:26 +08:00
|
|
|
Cmp = DAG.getNode(ARMISD::CMPFPw0, dl, MVT::Flag, LHS);
|
|
|
|
return DAG.getNode(ARMISD::FMSTAT, dl, MVT::Flag, Cmp);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG,
|
2009-03-21 06:42:55 +08:00
|
|
|
const ARMSubtarget *ST) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT = Op.getValueType();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue LHS = Op.getOperand(0);
|
|
|
|
SDValue RHS = Op.getOperand(1);
|
2007-01-19 15:51:42 +08:00
|
|
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue TrueVal = Op.getOperand(2);
|
|
|
|
SDValue FalseVal = Op.getOperand(3);
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
if (LHS.getValueType() == MVT::i32) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC;
|
|
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb(), dl);
|
|
|
|
return DAG.getNode(ARMISD::CMOV, dl, VT, FalseVal, TrueVal, ARMCC, CCR,Cmp);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ARMCC::CondCodes CondCode, CondCode2;
|
|
|
|
if (FPCCToARMCC(CC, CondCode, CondCode2))
|
|
|
|
std::swap(TrueVal, FalseVal);
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
|
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Cmp = getVFPCmp(LHS, RHS, DAG, dl);
|
|
|
|
SDValue Result = DAG.getNode(ARMISD::CMOV, dl, VT, FalseVal, TrueVal,
|
2007-07-05 15:18:20 +08:00
|
|
|
ARMCC, CCR, Cmp);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (CondCode2 != ARMCC::AL) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC2 = DAG.getConstant(CondCode2, MVT::i32);
|
2007-01-19 15:51:42 +08:00
|
|
|
// FIXME: Needs another CMP because flag can have but one use.
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Cmp2 = getVFPCmp(LHS, RHS, DAG, dl);
|
2009-03-21 06:42:55 +08:00
|
|
|
Result = DAG.getNode(ARMISD::CMOV, dl, VT,
|
2009-02-07 05:50:26 +08:00
|
|
|
Result, TrueVal, ARMCC2, CCR, Cmp2);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG,
|
2009-03-21 06:42:55 +08:00
|
|
|
const ARMSubtarget *ST) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain = Op.getOperand(0);
|
2007-01-19 15:51:42 +08:00
|
|
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue LHS = Op.getOperand(2);
|
|
|
|
SDValue RHS = Op.getOperand(3);
|
|
|
|
SDValue Dest = Op.getOperand(4);
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
if (LHS.getValueType() == MVT::i32) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC;
|
|
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb(), dl);
|
2009-03-21 06:42:55 +08:00
|
|
|
return DAG.getNode(ARMISD::BRCOND, dl, MVT::Other,
|
2009-02-07 05:50:26 +08:00
|
|
|
Chain, Dest, ARMCC, CCR,Cmp);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(LHS.getValueType() == MVT::f32 || LHS.getValueType() == MVT::f64);
|
|
|
|
ARMCC::CondCodes CondCode, CondCode2;
|
|
|
|
if (FPCCToARMCC(CC, CondCode, CondCode2))
|
|
|
|
// Swap the LHS/RHS of the comparison if needed.
|
|
|
|
std::swap(LHS, RHS);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Cmp = getVFPCmp(LHS, RHS, DAG, dl);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
|
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
2007-01-19 15:51:42 +08:00
|
|
|
SDVTList VTList = DAG.getVTList(MVT::Other, MVT::Flag);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Ops[] = { Chain, Dest, ARMCC, CCR, Cmp };
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Res = DAG.getNode(ARMISD::BRCOND, dl, VTList, Ops, 5);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (CondCode2 != ARMCC::AL) {
|
|
|
|
ARMCC = DAG.getConstant(CondCode2, MVT::i32);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Ops[] = { Res, Dest, ARMCC, CCR, Res.getValue(1) };
|
2009-02-07 05:50:26 +08:00
|
|
|
Res = DAG.getNode(ARMISD::BRCOND, dl, VTList, Ops, 5);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
return Res;
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) {
|
|
|
|
SDValue Chain = Op.getOperand(0);
|
|
|
|
SDValue Table = Op.getOperand(1);
|
|
|
|
SDValue Index = Op.getOperand(2);
|
2009-02-05 04:06:27 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT PTy = getPointerTy();
|
2007-01-19 15:51:42 +08:00
|
|
|
JumpTableSDNode *JT = cast<JumpTableSDNode>(Table);
|
|
|
|
ARMFunctionInfo *AFI = DAG.getMachineFunction().getInfo<ARMFunctionInfo>();
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue UId = DAG.getConstant(AFI->createJumpTableUId(), PTy);
|
|
|
|
SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy);
|
2009-02-07 05:50:26 +08:00
|
|
|
Table = DAG.getNode(ARMISD::WrapperJT, dl, MVT::i32, JTI, UId);
|
2009-02-05 04:06:27 +08:00
|
|
|
Index = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, PTy));
|
|
|
|
SDValue Addr = DAG.getNode(ISD::ADD, dl, PTy, Index, Table);
|
2007-01-19 15:51:42 +08:00
|
|
|
bool isPIC = getTargetMachine().getRelocationModel() == Reloc::PIC_;
|
2009-02-05 04:06:27 +08:00
|
|
|
Addr = DAG.getLoad(isPIC ? (MVT)MVT::i32 : PTy, dl,
|
2007-06-27 02:31:22 +08:00
|
|
|
Chain, Addr, NULL, 0);
|
2007-01-19 15:51:42 +08:00
|
|
|
Chain = Addr.getValue(1);
|
|
|
|
if (isPIC)
|
2009-02-05 04:06:27 +08:00
|
|
|
Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr, Table);
|
|
|
|
return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, Addr, JTI, UId);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerFP_TO_INT(SDValue Op, SelectionDAG &DAG) {
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
unsigned Opc =
|
|
|
|
Op.getOpcode() == ISD::FP_TO_SINT ? ARMISD::FTOSI : ARMISD::FTOUI;
|
2009-02-07 05:50:26 +08:00
|
|
|
Op = DAG.getNode(Opc, dl, MVT::f32, Op.getOperand(0));
|
|
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, MVT::i32, Op);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerINT_TO_FP(SDValue Op, SelectionDAG &DAG) {
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT = Op.getValueType();
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
unsigned Opc =
|
|
|
|
Op.getOpcode() == ISD::SINT_TO_FP ? ARMISD::SITOF : ARMISD::UITOF;
|
|
|
|
|
2009-02-07 05:50:26 +08:00
|
|
|
Op = DAG.getNode(ISD::BIT_CONVERT, dl, MVT::f32, Op.getOperand(0));
|
|
|
|
return DAG.getNode(Opc, dl, VT, Op);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
static SDValue LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) {
|
2007-01-19 15:51:42 +08:00
|
|
|
// Implement fcopysign with a fabs and a conditional fneg.
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Tmp0 = Op.getOperand(0);
|
|
|
|
SDValue Tmp1 = Op.getOperand(1);
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = Op.getDebugLoc();
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT = Op.getValueType();
|
|
|
|
MVT SrcVT = Tmp1.getValueType();
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue AbsVal = DAG.getNode(ISD::FABS, dl, VT, Tmp0);
|
|
|
|
SDValue Cmp = getVFPCmp(Tmp1, DAG.getConstantFP(0.0, SrcVT), DAG, dl);
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMCC = DAG.getConstant(ARMCC::LT, MVT::i32);
|
|
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
2009-02-07 05:50:26 +08:00
|
|
|
return DAG.getNode(ARMISD::CNEG, dl, VT, AbsVal, AbsVal, ARMCC, CCR, Cmp);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-05-13 07:59:14 +08:00
|
|
|
SDValue ARMTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) {
|
|
|
|
MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo();
|
|
|
|
MFI->setFrameAddressIsTaken(true);
|
|
|
|
MVT VT = Op.getValueType();
|
|
|
|
DebugLoc dl = Op.getDebugLoc(); // FIXME probably not meaningful
|
|
|
|
unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
|
2009-06-19 07:14:30 +08:00
|
|
|
unsigned FrameReg = (Subtarget->isThumb() || Subtarget->isTargetDarwin())
|
2009-05-13 07:59:14 +08:00
|
|
|
? ARM::R7 : ARM::R11;
|
|
|
|
SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), dl, FrameReg, VT);
|
|
|
|
while (Depth--)
|
|
|
|
FrameAddr = DAG.getLoad(VT, dl, DAG.getEntryNode(), FrameAddr, NULL, 0);
|
|
|
|
return FrameAddr;
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue
|
2009-02-04 06:26:09 +08:00
|
|
|
ARMTargetLowering::EmitTargetCodeForMemcpy(SelectionDAG &DAG, DebugLoc dl,
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Chain,
|
|
|
|
SDValue Dst, SDValue Src,
|
|
|
|
SDValue Size, unsigned Align,
|
Drop ISD::MEMSET, ISD::MEMMOVE, and ISD::MEMCPY, which are not Legal
on any current target and aren't optimized in DAGCombiner. Instead
of using intermediate nodes, expand the operations, choosing between
simple loads/stores, target-specific code, and library calls,
immediately.
Previously, the code to emit optimized code for these operations
was only used at initial SelectionDAG construction time; now it is
used at all times. This fixes some cases where rep;movs was being
used for small copies where simple loads/stores would be better.
This also cleans up code that checks for alignments less than 4;
let the targets make that decision instead of doing it in
target-independent code. This allows x86 to use rep;movs in
low-alignment cases.
Also, this fixes a bug that resulted in the use of rep;stos for
memsets of 0 with non-constant memory size when the alignment was
at least 4. It's better to use the library in this case, which
can be significantly faster when the size is large.
This also preserves more SourceValue information when memory
intrinsics are lowered into simple loads/stores.
llvm-svn: 49572
2008-04-12 12:36:06 +08:00
|
|
|
bool AlwaysInline,
|
2008-04-29 01:15:20 +08:00
|
|
|
const Value *DstSV, uint64_t DstSVOff,
|
|
|
|
const Value *SrcSV, uint64_t SrcSVOff){
|
2007-10-23 06:11:27 +08:00
|
|
|
// Do repeated 4-byte loads and stores. To be improved.
|
Drop ISD::MEMSET, ISD::MEMMOVE, and ISD::MEMCPY, which are not Legal
on any current target and aren't optimized in DAGCombiner. Instead
of using intermediate nodes, expand the operations, choosing between
simple loads/stores, target-specific code, and library calls,
immediately.
Previously, the code to emit optimized code for these operations
was only used at initial SelectionDAG construction time; now it is
used at all times. This fixes some cases where rep;movs was being
used for small copies where simple loads/stores would be better.
This also cleans up code that checks for alignments less than 4;
let the targets make that decision instead of doing it in
target-independent code. This allows x86 to use rep;movs in
low-alignment cases.
Also, this fixes a bug that resulted in the use of rep;stos for
memsets of 0 with non-constant memory size when the alignment was
at least 4. It's better to use the library in this case, which
can be significantly faster when the size is large.
This also preserves more SourceValue information when memory
intrinsics are lowered into simple loads/stores.
llvm-svn: 49572
2008-04-12 12:36:06 +08:00
|
|
|
// This requires 4-byte alignment.
|
|
|
|
if ((Align & 3) != 0)
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
Drop ISD::MEMSET, ISD::MEMMOVE, and ISD::MEMCPY, which are not Legal
on any current target and aren't optimized in DAGCombiner. Instead
of using intermediate nodes, expand the operations, choosing between
simple loads/stores, target-specific code, and library calls,
immediately.
Previously, the code to emit optimized code for these operations
was only used at initial SelectionDAG construction time; now it is
used at all times. This fixes some cases where rep;movs was being
used for small copies where simple loads/stores would be better.
This also cleans up code that checks for alignments less than 4;
let the targets make that decision instead of doing it in
target-independent code. This allows x86 to use rep;movs in
low-alignment cases.
Also, this fixes a bug that resulted in the use of rep;stos for
memsets of 0 with non-constant memory size when the alignment was
at least 4. It's better to use the library in this case, which
can be significantly faster when the size is large.
This also preserves more SourceValue information when memory
intrinsics are lowered into simple loads/stores.
llvm-svn: 49572
2008-04-12 12:36:06 +08:00
|
|
|
// This requires the copy size to be a constant, preferrably
|
|
|
|
// within a subtarget-specific limit.
|
|
|
|
ConstantSDNode *ConstantSize = dyn_cast<ConstantSDNode>(Size);
|
|
|
|
if (!ConstantSize)
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
2008-09-13 00:56:44 +08:00
|
|
|
uint64_t SizeVal = ConstantSize->getZExtValue();
|
Drop ISD::MEMSET, ISD::MEMMOVE, and ISD::MEMCPY, which are not Legal
on any current target and aren't optimized in DAGCombiner. Instead
of using intermediate nodes, expand the operations, choosing between
simple loads/stores, target-specific code, and library calls,
immediately.
Previously, the code to emit optimized code for these operations
was only used at initial SelectionDAG construction time; now it is
used at all times. This fixes some cases where rep;movs was being
used for small copies where simple loads/stores would be better.
This also cleans up code that checks for alignments less than 4;
let the targets make that decision instead of doing it in
target-independent code. This allows x86 to use rep;movs in
low-alignment cases.
Also, this fixes a bug that resulted in the use of rep;stos for
memsets of 0 with non-constant memory size when the alignment was
at least 4. It's better to use the library in this case, which
can be significantly faster when the size is large.
This also preserves more SourceValue information when memory
intrinsics are lowered into simple loads/stores.
llvm-svn: 49572
2008-04-12 12:36:06 +08:00
|
|
|
if (!AlwaysInline && SizeVal > getSubtarget()->getMaxInlineSizeThreshold())
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
Drop ISD::MEMSET, ISD::MEMMOVE, and ISD::MEMCPY, which are not Legal
on any current target and aren't optimized in DAGCombiner. Instead
of using intermediate nodes, expand the operations, choosing between
simple loads/stores, target-specific code, and library calls,
immediately.
Previously, the code to emit optimized code for these operations
was only used at initial SelectionDAG construction time; now it is
used at all times. This fixes some cases where rep;movs was being
used for small copies where simple loads/stores would be better.
This also cleans up code that checks for alignments less than 4;
let the targets make that decision instead of doing it in
target-independent code. This allows x86 to use rep;movs in
low-alignment cases.
Also, this fixes a bug that resulted in the use of rep;stos for
memsets of 0 with non-constant memory size when the alignment was
at least 4. It's better to use the library in this case, which
can be significantly faster when the size is large.
This also preserves more SourceValue information when memory
intrinsics are lowered into simple loads/stores.
llvm-svn: 49572
2008-04-12 12:36:06 +08:00
|
|
|
|
|
|
|
unsigned BytesLeft = SizeVal & 3;
|
|
|
|
unsigned NumMemOps = SizeVal >> 2;
|
2007-05-18 05:31:21 +08:00
|
|
|
unsigned EmittedNumMemOps = 0;
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT = MVT::i32;
|
2007-05-18 05:31:21 +08:00
|
|
|
unsigned VTSize = 4;
|
2007-10-23 06:11:27 +08:00
|
|
|
unsigned i = 0;
|
2007-05-18 09:19:57 +08:00
|
|
|
const unsigned MAX_LOADS_IN_LDM = 6;
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue TFOps[MAX_LOADS_IN_LDM];
|
|
|
|
SDValue Loads[MAX_LOADS_IN_LDM];
|
2008-04-29 01:15:20 +08:00
|
|
|
uint64_t SrcOff = 0, DstOff = 0;
|
2007-05-18 05:31:21 +08:00
|
|
|
|
2007-10-23 06:11:27 +08:00
|
|
|
// Emit up to MAX_LOADS_IN_LDM loads, then a TokenFactor barrier, then the
|
|
|
|
// same number of stores. The loads and stores will get combined into
|
2007-05-18 05:31:21 +08:00
|
|
|
// ldm/stm later on.
|
2007-10-23 06:11:27 +08:00
|
|
|
while (EmittedNumMemOps < NumMemOps) {
|
|
|
|
for (i = 0;
|
|
|
|
i < MAX_LOADS_IN_LDM && EmittedNumMemOps + i < NumMemOps; ++i) {
|
2009-02-04 06:26:09 +08:00
|
|
|
Loads[i] = DAG.getLoad(VT, dl, Chain,
|
|
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Src,
|
2007-10-23 06:11:27 +08:00
|
|
|
DAG.getConstant(SrcOff, MVT::i32)),
|
2008-04-29 01:15:20 +08:00
|
|
|
SrcSV, SrcSVOff + SrcOff);
|
2007-10-23 06:11:27 +08:00
|
|
|
TFOps[i] = Loads[i].getValue(1);
|
2007-05-18 05:31:21 +08:00
|
|
|
SrcOff += VTSize;
|
|
|
|
}
|
2009-02-04 06:26:09 +08:00
|
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
2007-05-18 05:31:21 +08:00
|
|
|
|
2007-10-23 06:11:27 +08:00
|
|
|
for (i = 0;
|
|
|
|
i < MAX_LOADS_IN_LDM && EmittedNumMemOps + i < NumMemOps; ++i) {
|
2009-02-04 06:26:09 +08:00
|
|
|
TFOps[i] = DAG.getStore(Chain, dl, Loads[i],
|
2009-03-21 06:42:55 +08:00
|
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Dst,
|
2007-10-23 06:11:27 +08:00
|
|
|
DAG.getConstant(DstOff, MVT::i32)),
|
2008-04-29 01:15:20 +08:00
|
|
|
DstSV, DstSVOff + DstOff);
|
2007-05-18 05:31:21 +08:00
|
|
|
DstOff += VTSize;
|
|
|
|
}
|
2009-02-04 06:26:09 +08:00
|
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
2007-10-23 06:11:27 +08:00
|
|
|
|
2007-05-18 05:31:21 +08:00
|
|
|
EmittedNumMemOps += i;
|
|
|
|
}
|
|
|
|
|
2009-03-21 06:42:55 +08:00
|
|
|
if (BytesLeft == 0)
|
2007-10-23 06:11:27 +08:00
|
|
|
return Chain;
|
|
|
|
|
|
|
|
// Issue loads / stores for the trailing (1 - 3) bytes.
|
|
|
|
unsigned BytesLeftSave = BytesLeft;
|
|
|
|
i = 0;
|
|
|
|
while (BytesLeft) {
|
|
|
|
if (BytesLeft >= 2) {
|
|
|
|
VT = MVT::i16;
|
|
|
|
VTSize = 2;
|
|
|
|
} else {
|
|
|
|
VT = MVT::i8;
|
|
|
|
VTSize = 1;
|
|
|
|
}
|
|
|
|
|
2009-02-04 06:26:09 +08:00
|
|
|
Loads[i] = DAG.getLoad(VT, dl, Chain,
|
|
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Src,
|
2007-10-23 06:11:27 +08:00
|
|
|
DAG.getConstant(SrcOff, MVT::i32)),
|
2008-04-29 01:15:20 +08:00
|
|
|
SrcSV, SrcSVOff + SrcOff);
|
2007-10-23 06:11:27 +08:00
|
|
|
TFOps[i] = Loads[i].getValue(1);
|
|
|
|
++i;
|
|
|
|
SrcOff += VTSize;
|
|
|
|
BytesLeft -= VTSize;
|
|
|
|
}
|
2009-02-04 06:26:09 +08:00
|
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
2007-10-23 06:11:27 +08:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
BytesLeft = BytesLeftSave;
|
|
|
|
while (BytesLeft) {
|
|
|
|
if (BytesLeft >= 2) {
|
|
|
|
VT = MVT::i16;
|
|
|
|
VTSize = 2;
|
|
|
|
} else {
|
|
|
|
VT = MVT::i8;
|
|
|
|
VTSize = 1;
|
|
|
|
}
|
|
|
|
|
2009-02-04 06:26:09 +08:00
|
|
|
TFOps[i] = DAG.getStore(Chain, dl, Loads[i],
|
2009-03-21 06:42:55 +08:00
|
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Dst,
|
2007-10-23 06:11:27 +08:00
|
|
|
DAG.getConstant(DstOff, MVT::i32)),
|
2008-04-29 01:15:20 +08:00
|
|
|
DstSV, DstSVOff + DstOff);
|
2007-10-23 06:11:27 +08:00
|
|
|
++i;
|
|
|
|
DstOff += VTSize;
|
|
|
|
BytesLeft -= VTSize;
|
|
|
|
}
|
2009-02-04 06:26:09 +08:00
|
|
|
return DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
2007-05-18 05:31:21 +08:00
|
|
|
}
|
|
|
|
|
2008-12-01 19:39:25 +08:00
|
|
|
static SDValue ExpandBIT_CONVERT(SDNode *N, SelectionDAG &DAG) {
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Op = N->getOperand(0);
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = N->getDebugLoc();
|
2008-11-05 03:57:48 +08:00
|
|
|
if (N->getValueType(0) == MVT::f64) {
|
|
|
|
// Turn i64->f64 into FMDRR.
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, Op,
|
2008-11-05 03:57:48 +08:00
|
|
|
DAG.getConstant(0, MVT::i32));
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, Op,
|
2008-11-05 03:57:48 +08:00
|
|
|
DAG.getConstant(1, MVT::i32));
|
2009-02-07 05:50:26 +08:00
|
|
|
return DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, Lo, Hi);
|
2008-11-05 03:57:48 +08:00
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2008-11-05 03:57:48 +08:00
|
|
|
// Turn f64->i64 into FMRRD.
|
2009-03-21 06:42:55 +08:00
|
|
|
SDValue Cvt = DAG.getNode(ARMISD::FMRRD, dl,
|
2009-02-07 05:50:26 +08:00
|
|
|
DAG.getVTList(MVT::i32, MVT::i32), &Op, 1);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// Merge the pieces into a single i64 value.
|
2009-02-07 05:50:26 +08:00
|
|
|
return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Cvt, Cvt.getValue(1));
|
2007-11-24 15:07:01 +08:00
|
|
|
}
|
|
|
|
|
2008-12-01 19:39:25 +08:00
|
|
|
static SDValue ExpandSRx(SDNode *N, SelectionDAG &DAG, const ARMSubtarget *ST) {
|
2007-11-24 15:07:01 +08:00
|
|
|
assert(N->getValueType(0) == MVT::i64 &&
|
|
|
|
(N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) &&
|
|
|
|
"Unknown shift to lower!");
|
2008-12-01 19:39:25 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// We only lower SRA, SRL of 1 here, all others use generic lowering.
|
|
|
|
if (!isa<ConstantSDNode>(N->getOperand(1)) ||
|
2008-09-13 00:56:44 +08:00
|
|
|
cast<ConstantSDNode>(N->getOperand(1))->getZExtValue() != 1)
|
2008-12-01 19:39:25 +08:00
|
|
|
return SDValue();
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// If we are in thumb mode, we don't have RRX.
|
2008-12-01 19:39:25 +08:00
|
|
|
if (ST->isThumb()) return SDValue();
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// Okay, we have a 64-bit SRA or SRL of 1. Lower this to an RRX expr.
|
2009-02-07 05:50:26 +08:00
|
|
|
DebugLoc dl = N->getDebugLoc();
|
|
|
|
SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0),
|
2007-11-24 15:07:01 +08:00
|
|
|
DAG.getConstant(0, MVT::i32));
|
2009-02-07 05:50:26 +08:00
|
|
|
SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0),
|
2007-11-24 15:07:01 +08:00
|
|
|
DAG.getConstant(1, MVT::i32));
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// First, build a SRA_FLAG/SRL_FLAG op, which shifts the top part by one and
|
|
|
|
// captures the result into a carry flag.
|
|
|
|
unsigned Opc = N->getOpcode() == ISD::SRL ? ARMISD::SRL_FLAG:ARMISD::SRA_FLAG;
|
2009-02-07 05:50:26 +08:00
|
|
|
Hi = DAG.getNode(Opc, dl, DAG.getVTList(MVT::i32, MVT::Flag), &Hi, 1);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// The low part is an ARMISD::RRX operand, which shifts the carry in.
|
2009-02-07 05:50:26 +08:00
|
|
|
Lo = DAG.getNode(ARMISD::RRX, dl, MVT::i32, Lo, Hi.getValue(1));
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-11-24 15:07:01 +08:00
|
|
|
// Merge the pieces into a single i64 value.
|
2009-02-07 05:50:26 +08:00
|
|
|
return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Lo, Hi);
|
2007-11-24 15:07:01 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) {
|
2007-01-19 15:51:42 +08:00
|
|
|
switch (Op.getOpcode()) {
|
|
|
|
default: assert(0 && "Don't know how to custom lower this!"); abort();
|
|
|
|
case ISD::ConstantPool: return LowerConstantPool(Op, DAG);
|
2007-04-22 08:04:12 +08:00
|
|
|
case ISD::GlobalAddress:
|
|
|
|
return Subtarget->isTargetDarwin() ? LowerGlobalAddressDarwin(Op, DAG) :
|
|
|
|
LowerGlobalAddressELF(Op, DAG);
|
2007-04-27 21:54:47 +08:00
|
|
|
case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG);
|
2007-01-19 15:51:42 +08:00
|
|
|
case ISD::CALL: return LowerCALL(Op, DAG);
|
|
|
|
case ISD::RET: return LowerRET(Op, DAG);
|
|
|
|
case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG, Subtarget);
|
|
|
|
case ISD::BR_CC: return LowerBR_CC(Op, DAG, Subtarget);
|
|
|
|
case ISD::BR_JT: return LowerBR_JT(Op, DAG);
|
|
|
|
case ISD::VASTART: return LowerVASTART(Op, DAG, VarArgsFrameIndex);
|
|
|
|
case ISD::SINT_TO_FP:
|
|
|
|
case ISD::UINT_TO_FP: return LowerINT_TO_FP(Op, DAG);
|
|
|
|
case ISD::FP_TO_SINT:
|
|
|
|
case ISD::FP_TO_UINT: return LowerFP_TO_INT(Op, DAG);
|
|
|
|
case ISD::FCOPYSIGN: return LowerFCOPYSIGN(Op, DAG);
|
2007-11-24 15:07:01 +08:00
|
|
|
case ISD::FORMAL_ARGUMENTS: return LowerFORMAL_ARGUMENTS(Op, DAG);
|
2007-01-30 06:58:52 +08:00
|
|
|
case ISD::RETURNADDR: break;
|
2009-05-13 07:59:14 +08:00
|
|
|
case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG);
|
2007-04-22 08:04:12 +08:00
|
|
|
case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG);
|
2007-11-09 01:20:05 +08:00
|
|
|
case ISD::INTRINSIC_WO_CHAIN: return LowerINTRINSIC_WO_CHAIN(Op, DAG);
|
2008-12-01 19:39:25 +08:00
|
|
|
case ISD::BIT_CONVERT: return ExpandBIT_CONVERT(Op.getNode(), DAG);
|
2007-11-24 15:07:01 +08:00
|
|
|
case ISD::SRL:
|
2008-12-01 19:39:25 +08:00
|
|
|
case ISD::SRA: return ExpandSRx(Op.getNode(), DAG,Subtarget);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2008-12-01 19:39:25 +08:00
|
|
|
/// ReplaceNodeResults - Replace the results of node with an illegal result
|
|
|
|
/// type with new values built out of custom code.
|
|
|
|
void ARMTargetLowering::ReplaceNodeResults(SDNode *N,
|
|
|
|
SmallVectorImpl<SDValue>&Results,
|
|
|
|
SelectionDAG &DAG) {
|
2007-11-24 15:07:01 +08:00
|
|
|
switch (N->getOpcode()) {
|
2008-12-01 19:39:25 +08:00
|
|
|
default:
|
|
|
|
assert(0 && "Don't know how to custom expand this!");
|
|
|
|
return;
|
|
|
|
case ISD::BIT_CONVERT:
|
|
|
|
Results.push_back(ExpandBIT_CONVERT(N, DAG));
|
|
|
|
return;
|
2007-11-24 15:07:01 +08:00
|
|
|
case ISD::SRL:
|
2008-12-01 19:39:25 +08:00
|
|
|
case ISD::SRA: {
|
|
|
|
SDValue Res = ExpandSRx(N, DAG, Subtarget);
|
|
|
|
if (Res.getNode())
|
|
|
|
Results.push_back(Res);
|
|
|
|
return;
|
|
|
|
}
|
2007-11-24 15:07:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ARM Scheduler Hooks
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
MachineBasicBlock *
|
2008-01-31 02:18:23 +08:00
|
|
|
ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI,
|
2009-02-08 00:15:20 +08:00
|
|
|
MachineBasicBlock *BB) const {
|
2007-01-19 15:51:42 +08:00
|
|
|
const TargetInstrInfo *TII = getTargetMachine().getInstrInfo();
|
2009-02-13 10:25:56 +08:00
|
|
|
DebugLoc dl = MI->getDebugLoc();
|
2007-01-19 15:51:42 +08:00
|
|
|
switch (MI->getOpcode()) {
|
|
|
|
default: assert(false && "Unexpected instr type to insert");
|
|
|
|
case ARM::tMOVCCr: {
|
|
|
|
// To "insert" a SELECT_CC instruction, we actually have to insert the
|
|
|
|
// diamond control-flow pattern. The incoming instruction knows the
|
|
|
|
// destination vreg to set, the condition code register to branch on, the
|
|
|
|
// true/false values to select between, and a branch opcode to use.
|
|
|
|
const BasicBlock *LLVM_BB = BB->getBasicBlock();
|
2008-07-08 07:14:23 +08:00
|
|
|
MachineFunction::iterator It = BB;
|
2007-01-19 15:51:42 +08:00
|
|
|
++It;
|
|
|
|
|
|
|
|
// thisMBB:
|
|
|
|
// ...
|
|
|
|
// TrueVal = ...
|
|
|
|
// cmpTY ccX, r1, r2
|
|
|
|
// bCC copy1MBB
|
|
|
|
// fallthrough --> copy0MBB
|
|
|
|
MachineBasicBlock *thisMBB = BB;
|
2008-07-08 07:14:23 +08:00
|
|
|
MachineFunction *F = BB->getParent();
|
|
|
|
MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB);
|
|
|
|
MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB);
|
2009-02-13 10:25:56 +08:00
|
|
|
BuildMI(BB, dl, TII->get(ARM::tBcc)).addMBB(sinkMBB)
|
2007-07-05 15:18:20 +08:00
|
|
|
.addImm(MI->getOperand(3).getImm()).addReg(MI->getOperand(4).getReg());
|
2008-07-08 07:14:23 +08:00
|
|
|
F->insert(It, copy0MBB);
|
|
|
|
F->insert(It, sinkMBB);
|
2007-01-19 15:51:42 +08:00
|
|
|
// Update machine-CFG edges by first adding all successors of the current
|
|
|
|
// block to the new block which will contain the Phi node for the select.
|
|
|
|
for(MachineBasicBlock::succ_iterator i = BB->succ_begin(),
|
|
|
|
e = BB->succ_end(); i != e; ++i)
|
|
|
|
sinkMBB->addSuccessor(*i);
|
|
|
|
// Next, remove all successors of the current block, and add the true
|
|
|
|
// and fallthrough blocks as its successors.
|
|
|
|
while(!BB->succ_empty())
|
|
|
|
BB->removeSuccessor(BB->succ_begin());
|
|
|
|
BB->addSuccessor(copy0MBB);
|
|
|
|
BB->addSuccessor(sinkMBB);
|
|
|
|
|
|
|
|
// copy0MBB:
|
|
|
|
// %FalseValue = ...
|
|
|
|
// # fallthrough to sinkMBB
|
|
|
|
BB = copy0MBB;
|
|
|
|
|
|
|
|
// Update machine-CFG edges
|
|
|
|
BB->addSuccessor(sinkMBB);
|
|
|
|
|
|
|
|
// sinkMBB:
|
|
|
|
// %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ]
|
|
|
|
// ...
|
|
|
|
BB = sinkMBB;
|
2009-02-13 10:25:56 +08:00
|
|
|
BuildMI(BB, dl, TII->get(ARM::PHI), MI->getOperand(0).getReg())
|
2007-01-19 15:51:42 +08:00
|
|
|
.addReg(MI->getOperand(1).getReg()).addMBB(copy0MBB)
|
|
|
|
.addReg(MI->getOperand(2).getReg()).addMBB(thisMBB);
|
|
|
|
|
2008-07-08 07:14:23 +08:00
|
|
|
F->DeleteMachineInstr(MI); // The pseudo instruction is gone now.
|
2007-01-19 15:51:42 +08:00
|
|
|
return BB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ARM Optimization Hooks
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
static
|
|
|
|
SDValue combineSelectAndUse(SDNode *N, SDValue Slct, SDValue OtherOp,
|
|
|
|
TargetLowering::DAGCombinerInfo &DCI) {
|
|
|
|
SelectionDAG &DAG = DCI.DAG;
|
|
|
|
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
|
|
|
|
MVT VT = N->getValueType(0);
|
|
|
|
unsigned Opc = N->getOpcode();
|
|
|
|
bool isSlctCC = Slct.getOpcode() == ISD::SELECT_CC;
|
|
|
|
SDValue LHS = isSlctCC ? Slct.getOperand(2) : Slct.getOperand(1);
|
|
|
|
SDValue RHS = isSlctCC ? Slct.getOperand(3) : Slct.getOperand(2);
|
|
|
|
ISD::CondCode CC = ISD::SETCC_INVALID;
|
|
|
|
|
|
|
|
if (isSlctCC) {
|
|
|
|
CC = cast<CondCodeSDNode>(Slct.getOperand(4))->get();
|
|
|
|
} else {
|
|
|
|
SDValue CCOp = Slct.getOperand(0);
|
|
|
|
if (CCOp.getOpcode() == ISD::SETCC)
|
|
|
|
CC = cast<CondCodeSDNode>(CCOp.getOperand(2))->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DoXform = false;
|
|
|
|
bool InvCC = false;
|
|
|
|
assert ((Opc == ISD::ADD || (Opc == ISD::SUB && Slct == N->getOperand(1))) &&
|
|
|
|
"Bad input!");
|
|
|
|
|
|
|
|
if (LHS.getOpcode() == ISD::Constant &&
|
|
|
|
cast<ConstantSDNode>(LHS)->isNullValue()) {
|
|
|
|
DoXform = true;
|
|
|
|
} else if (CC != ISD::SETCC_INVALID &&
|
|
|
|
RHS.getOpcode() == ISD::Constant &&
|
|
|
|
cast<ConstantSDNode>(RHS)->isNullValue()) {
|
|
|
|
std::swap(LHS, RHS);
|
|
|
|
SDValue Op0 = Slct.getOperand(0);
|
|
|
|
MVT OpVT = isSlctCC ? Op0.getValueType() :
|
|
|
|
Op0.getOperand(0).getValueType();
|
|
|
|
bool isInt = OpVT.isInteger();
|
|
|
|
CC = ISD::getSetCCInverse(CC, isInt);
|
|
|
|
|
|
|
|
if (!TLI.isCondCodeLegal(CC, OpVT))
|
|
|
|
return SDValue(); // Inverse operator isn't legal.
|
|
|
|
|
|
|
|
DoXform = true;
|
|
|
|
InvCC = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DoXform) {
|
|
|
|
SDValue Result = DAG.getNode(Opc, RHS.getDebugLoc(), VT, OtherOp, RHS);
|
|
|
|
if (isSlctCC)
|
|
|
|
return DAG.getSelectCC(N->getDebugLoc(), OtherOp, Result,
|
|
|
|
Slct.getOperand(0), Slct.getOperand(1), CC);
|
|
|
|
SDValue CCOp = Slct.getOperand(0);
|
|
|
|
if (InvCC)
|
|
|
|
CCOp = DAG.getSetCC(Slct.getDebugLoc(), CCOp.getValueType(),
|
|
|
|
CCOp.getOperand(0), CCOp.getOperand(1), CC);
|
|
|
|
return DAG.getNode(ISD::SELECT, N->getDebugLoc(), VT,
|
|
|
|
CCOp, OtherOp, Result);
|
|
|
|
}
|
|
|
|
return SDValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// PerformADDCombine - Target-specific dag combine xforms for ISD::ADD.
|
|
|
|
static SDValue PerformADDCombine(SDNode *N,
|
|
|
|
TargetLowering::DAGCombinerInfo &DCI) {
|
|
|
|
// added by evan in r37685 with no testcase.
|
|
|
|
SDValue N0 = N->getOperand(0), N1 = N->getOperand(1);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
// fold (add (select cc, 0, c), x) -> (select cc, x, (add, x, c))
|
|
|
|
if (N0.getOpcode() == ISD::SELECT && N0.getNode()->hasOneUse()) {
|
|
|
|
SDValue Result = combineSelectAndUse(N, N0, N1, DCI);
|
|
|
|
if (Result.getNode()) return Result;
|
|
|
|
}
|
|
|
|
if (N1.getOpcode() == ISD::SELECT && N1.getNode()->hasOneUse()) {
|
|
|
|
SDValue Result = combineSelectAndUse(N, N1, N0, DCI);
|
|
|
|
if (Result.getNode()) return Result;
|
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
return SDValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// PerformSUBCombine - Target-specific dag combine xforms for ISD::SUB.
|
|
|
|
static SDValue PerformSUBCombine(SDNode *N,
|
|
|
|
TargetLowering::DAGCombinerInfo &DCI) {
|
|
|
|
// added by evan in r37685 with no testcase.
|
|
|
|
SDValue N0 = N->getOperand(0), N1 = N->getOperand(1);
|
2009-03-21 06:42:55 +08:00
|
|
|
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
// fold (sub x, (select cc, 0, c)) -> (select cc, x, (sub, x, c))
|
|
|
|
if (N1.getOpcode() == ISD::SELECT && N1.getNode()->hasOneUse()) {
|
|
|
|
SDValue Result = combineSelectAndUse(N, N1, N0, DCI);
|
|
|
|
if (Result.getNode()) return Result;
|
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
return SDValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-28 06:36:16 +08:00
|
|
|
/// PerformFMRRDCombine - Target-specific dag combine xforms for ARMISD::FMRRD.
|
2009-03-21 06:42:55 +08:00
|
|
|
static SDValue PerformFMRRDCombine(SDNode *N,
|
|
|
|
TargetLowering::DAGCombinerInfo &DCI) {
|
2007-11-28 06:36:16 +08:00
|
|
|
// fmrrd(fmdrr x, y) -> x,y
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue InDouble = N->getOperand(0);
|
2007-11-28 06:36:16 +08:00
|
|
|
if (InDouble.getOpcode() == ARMISD::FMDRR)
|
|
|
|
return DCI.CombineTo(N, InDouble.getOperand(0), InDouble.getOperand(1));
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
2007-11-28 06:36:16 +08:00
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
|
2009-03-21 06:42:55 +08:00
|
|
|
DAGCombinerInfo &DCI) const {
|
2007-11-28 06:36:16 +08:00
|
|
|
switch (N->getOpcode()) {
|
|
|
|
default: break;
|
Move 3 "(add (select cc, 0, c), x) -> (select cc, x, (add, x, c))"
related transformations out of target-specific dag combine into the
ARM backend. These were added by Evan in r37685 with no testcases
and only seems to help ARM (e.g. test/CodeGen/ARM/select_xform.ll).
Add some simple X86-specific (for now) DAG combines that turn things
like cond ? 8 : 0 -> (zext(cond) << 3). This happens frequently
with the recently added cp constant select optimization, but is a
very general xform. For example, we now compile the second example
in const-select.ll to:
_test:
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
seta %al
movzbl %al, %eax
movl 4(%esp), %ecx
movsbl (%ecx,%eax,4), %eax
ret
instead of:
_test:
movl 4(%esp), %eax
leal 4(%eax), %ecx
movsd LCPI2_0, %xmm0
ucomisd 8(%esp), %xmm0
cmovbe %eax, %ecx
movsbl (%ecx), %eax
ret
This passes multisource and dejagnu.
llvm-svn: 66779
2009-03-12 14:52:53 +08:00
|
|
|
case ISD::ADD: return PerformADDCombine(N, DCI);
|
|
|
|
case ISD::SUB: return PerformSUBCombine(N, DCI);
|
2007-11-28 06:36:16 +08:00
|
|
|
case ARMISD::FMRRD: return PerformFMRRDCombine(N, DCI);
|
|
|
|
}
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
return SDValue();
|
2007-11-28 06:36:16 +08:00
|
|
|
}
|
|
|
|
|
2007-03-13 07:30:29 +08:00
|
|
|
/// isLegalAddressImmediate - Return true if the integer value can be used
|
|
|
|
/// as the offset of the target addressing mode for load / store of the
|
|
|
|
/// given type.
|
2008-06-06 20:08:01 +08:00
|
|
|
static bool isLegalAddressImmediate(int64_t V, MVT VT,
|
2007-04-10 07:33:39 +08:00
|
|
|
const ARMSubtarget *Subtarget) {
|
2007-03-14 04:37:59 +08:00
|
|
|
if (V == 0)
|
|
|
|
return true;
|
|
|
|
|
2009-03-10 03:15:00 +08:00
|
|
|
if (!VT.isSimple())
|
|
|
|
return false;
|
|
|
|
|
2007-03-13 07:30:29 +08:00
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
if (V < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
unsigned Scale = 1;
|
2008-06-06 20:08:01 +08:00
|
|
|
switch (VT.getSimpleVT()) {
|
2007-03-13 07:30:29 +08:00
|
|
|
default: return false;
|
|
|
|
case MVT::i1:
|
|
|
|
case MVT::i8:
|
|
|
|
// Scale == 1;
|
|
|
|
break;
|
|
|
|
case MVT::i16:
|
|
|
|
// Scale == 2;
|
|
|
|
Scale = 2;
|
|
|
|
break;
|
|
|
|
case MVT::i32:
|
|
|
|
// Scale == 4;
|
|
|
|
Scale = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((V & (Scale - 1)) != 0)
|
|
|
|
return false;
|
|
|
|
V /= Scale;
|
2008-02-20 19:22:39 +08:00
|
|
|
return V == (V & ((1LL << 5) - 1));
|
2007-03-13 07:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (V < 0)
|
|
|
|
V = - V;
|
2008-06-06 20:08:01 +08:00
|
|
|
switch (VT.getSimpleVT()) {
|
2007-03-13 07:30:29 +08:00
|
|
|
default: return false;
|
|
|
|
case MVT::i1:
|
|
|
|
case MVT::i8:
|
|
|
|
case MVT::i32:
|
|
|
|
// +- imm12
|
2008-02-20 19:22:39 +08:00
|
|
|
return V == (V & ((1LL << 12) - 1));
|
2007-03-13 07:30:29 +08:00
|
|
|
case MVT::i16:
|
|
|
|
// +- imm8
|
2008-02-20 19:22:39 +08:00
|
|
|
return V == (V & ((1LL << 8) - 1));
|
2007-03-13 07:30:29 +08:00
|
|
|
case MVT::f32:
|
|
|
|
case MVT::f64:
|
|
|
|
if (!Subtarget->hasVFP2())
|
|
|
|
return false;
|
2007-05-03 10:00:18 +08:00
|
|
|
if ((V & 3) != 0)
|
2007-03-13 07:30:29 +08:00
|
|
|
return false;
|
|
|
|
V >>= 2;
|
2008-02-20 19:22:39 +08:00
|
|
|
return V == (V & ((1LL << 8) - 1));
|
2007-03-13 07:30:29 +08:00
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2007-04-10 07:33:39 +08:00
|
|
|
/// isLegalAddressingMode - Return true if the addressing mode represented
|
|
|
|
/// by AM is legal for this target, for a load/store of the specified type.
|
2009-03-21 06:42:55 +08:00
|
|
|
bool ARMTargetLowering::isLegalAddressingMode(const AddrMode &AM,
|
2007-04-10 07:33:39 +08:00
|
|
|
const Type *Ty) const {
|
2009-04-09 01:55:28 +08:00
|
|
|
MVT VT = getValueType(Ty, true);
|
|
|
|
if (!isLegalAddressImmediate(AM.BaseOffs, VT, Subtarget))
|
2007-03-13 07:30:29 +08:00
|
|
|
return false;
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-04-10 07:33:39 +08:00
|
|
|
// Can never fold addr of global into load/store.
|
2009-03-21 06:42:55 +08:00
|
|
|
if (AM.BaseGV)
|
2007-04-10 07:33:39 +08:00
|
|
|
return false;
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-04-10 07:33:39 +08:00
|
|
|
switch (AM.Scale) {
|
|
|
|
case 0: // no scale reg, must be "r+i" or "r", or "i".
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (Subtarget->isThumb())
|
|
|
|
return false;
|
2007-04-13 14:50:55 +08:00
|
|
|
// FALL THROUGH.
|
2007-04-10 07:33:39 +08:00
|
|
|
default:
|
2007-04-13 14:50:55 +08:00
|
|
|
// ARM doesn't support any R+R*scale+imm addr modes.
|
|
|
|
if (AM.BaseOffs)
|
|
|
|
return false;
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2009-04-09 01:55:28 +08:00
|
|
|
if (!VT.isSimple())
|
|
|
|
return false;
|
|
|
|
|
2007-04-10 11:48:29 +08:00
|
|
|
int Scale = AM.Scale;
|
2009-04-09 01:55:28 +08:00
|
|
|
switch (VT.getSimpleVT()) {
|
2007-04-10 07:33:39 +08:00
|
|
|
default: return false;
|
|
|
|
case MVT::i1:
|
|
|
|
case MVT::i8:
|
|
|
|
case MVT::i32:
|
|
|
|
case MVT::i64:
|
|
|
|
// This assumes i64 is legalized to a pair of i32. If not (i.e.
|
|
|
|
// ldrd / strd are used, then its address mode is same as i16.
|
|
|
|
// r + r
|
2007-04-10 11:48:29 +08:00
|
|
|
if (Scale < 0) Scale = -Scale;
|
|
|
|
if (Scale == 1)
|
2007-04-10 07:33:39 +08:00
|
|
|
return true;
|
|
|
|
// r + r << imm
|
2007-04-12 00:17:12 +08:00
|
|
|
return isPowerOf2_32(Scale & ~1);
|
2007-04-10 07:33:39 +08:00
|
|
|
case MVT::i16:
|
|
|
|
// r + r
|
2007-04-10 11:48:29 +08:00
|
|
|
if (((unsigned)AM.HasBaseReg + Scale) <= 2)
|
2007-04-10 07:33:39 +08:00
|
|
|
return true;
|
2007-04-12 00:17:12 +08:00
|
|
|
return false;
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-04-10 07:33:39 +08:00
|
|
|
case MVT::isVoid:
|
|
|
|
// Note, we allow "void" uses (basically, uses that aren't loads or
|
|
|
|
// stores), because arm allows folding a scale into many arithmetic
|
|
|
|
// operations. This should be made more precise and revisited later.
|
2009-03-21 06:42:55 +08:00
|
|
|
|
2007-04-10 07:33:39 +08:00
|
|
|
// Allow r << imm, but the imm has to be a multiple of two.
|
|
|
|
if (AM.Scale & 1) return false;
|
|
|
|
return isPowerOf2_32(AM.Scale);
|
|
|
|
}
|
|
|
|
break;
|
2007-03-13 07:30:29 +08:00
|
|
|
}
|
2007-04-10 07:33:39 +08:00
|
|
|
return true;
|
2007-03-13 07:30:29 +08:00
|
|
|
}
|
|
|
|
|
2008-06-06 20:08:01 +08:00
|
|
|
static bool getIndexedAddressParts(SDNode *Ptr, MVT VT,
|
2008-07-28 05:46:04 +08:00
|
|
|
bool isSEXTLoad, SDValue &Base,
|
|
|
|
SDValue &Offset, bool &isInc,
|
2007-01-19 15:51:42 +08:00
|
|
|
SelectionDAG &DAG) {
|
|
|
|
if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (VT == MVT::i16 || ((VT == MVT::i8 || VT == MVT::i1) && isSEXTLoad)) {
|
|
|
|
// AddressingMode 3
|
|
|
|
Base = Ptr->getOperand(0);
|
|
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Ptr->getOperand(1))) {
|
2008-09-13 00:56:44 +08:00
|
|
|
int RHSC = (int)RHS->getZExtValue();
|
2007-01-19 15:51:42 +08:00
|
|
|
if (RHSC < 0 && RHSC > -256) {
|
|
|
|
isInc = false;
|
|
|
|
Offset = DAG.getConstant(-RHSC, RHS->getValueType(0));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isInc = (Ptr->getOpcode() == ISD::ADD);
|
|
|
|
Offset = Ptr->getOperand(1);
|
|
|
|
return true;
|
|
|
|
} else if (VT == MVT::i32 || VT == MVT::i8 || VT == MVT::i1) {
|
|
|
|
// AddressingMode 2
|
|
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Ptr->getOperand(1))) {
|
2008-09-13 00:56:44 +08:00
|
|
|
int RHSC = (int)RHS->getZExtValue();
|
2007-01-19 15:51:42 +08:00
|
|
|
if (RHSC < 0 && RHSC > -0x1000) {
|
|
|
|
isInc = false;
|
|
|
|
Offset = DAG.getConstant(-RHSC, RHS->getValueType(0));
|
|
|
|
Base = Ptr->getOperand(0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Ptr->getOpcode() == ISD::ADD) {
|
|
|
|
isInc = true;
|
|
|
|
ARM_AM::ShiftOpc ShOpcVal= ARM_AM::getShiftOpcForNode(Ptr->getOperand(0));
|
|
|
|
if (ShOpcVal != ARM_AM::no_shift) {
|
|
|
|
Base = Ptr->getOperand(1);
|
|
|
|
Offset = Ptr->getOperand(0);
|
|
|
|
} else {
|
|
|
|
Base = Ptr->getOperand(0);
|
|
|
|
Offset = Ptr->getOperand(1);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
isInc = (Ptr->getOpcode() == ISD::ADD);
|
|
|
|
Base = Ptr->getOperand(0);
|
|
|
|
Offset = Ptr->getOperand(1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Use FLDM / FSTM to emulate indexed FP load / store.
|
|
|
|
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.
|
|
|
|
bool
|
2008-07-28 05:46:04 +08:00
|
|
|
ARMTargetLowering::getPreIndexedAddressParts(SDNode *N, SDValue &Base,
|
|
|
|
SDValue &Offset,
|
2007-01-19 15:51:42 +08:00
|
|
|
ISD::MemIndexedMode &AM,
|
2009-01-16 00:29:45 +08:00
|
|
|
SelectionDAG &DAG) const {
|
2007-01-19 15:51:42 +08:00
|
|
|
if (Subtarget->isThumb())
|
|
|
|
return false;
|
|
|
|
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT;
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Ptr;
|
2007-01-19 15:51:42 +08:00
|
|
|
bool isSEXTLoad = false;
|
|
|
|
if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) {
|
|
|
|
Ptr = LD->getBasePtr();
|
2008-01-30 08:15:11 +08:00
|
|
|
VT = LD->getMemoryVT();
|
2007-01-19 15:51:42 +08:00
|
|
|
isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD;
|
|
|
|
} else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) {
|
|
|
|
Ptr = ST->getBasePtr();
|
2008-01-30 08:15:11 +08:00
|
|
|
VT = ST->getMemoryVT();
|
2007-01-19 15:51:42 +08:00
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool isInc;
|
2008-08-29 05:40:38 +08:00
|
|
|
bool isLegal = getIndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base, Offset,
|
2007-01-19 15:51:42 +08:00
|
|
|
isInc, DAG);
|
|
|
|
if (isLegal) {
|
|
|
|
AM = isInc ? ISD::PRE_INC : ISD::PRE_DEC;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// getPostIndexedAddressParts - returns true by value, base pointer and
|
|
|
|
/// offset pointer and addressing mode by reference if this node can be
|
|
|
|
/// combined with a load / store to form a post-indexed load / store.
|
|
|
|
bool ARMTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op,
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue &Base,
|
|
|
|
SDValue &Offset,
|
2007-01-19 15:51:42 +08:00
|
|
|
ISD::MemIndexedMode &AM,
|
2009-01-16 00:29:45 +08:00
|
|
|
SelectionDAG &DAG) const {
|
2007-01-19 15:51:42 +08:00
|
|
|
if (Subtarget->isThumb())
|
|
|
|
return false;
|
|
|
|
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT;
|
2008-07-28 05:46:04 +08:00
|
|
|
SDValue Ptr;
|
2007-01-19 15:51:42 +08:00
|
|
|
bool isSEXTLoad = false;
|
|
|
|
if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) {
|
2008-01-30 08:15:11 +08:00
|
|
|
VT = LD->getMemoryVT();
|
2007-01-19 15:51:42 +08:00
|
|
|
isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD;
|
|
|
|
} else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) {
|
2008-01-30 08:15:11 +08:00
|
|
|
VT = ST->getMemoryVT();
|
2007-01-19 15:51:42 +08:00
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool isInc;
|
|
|
|
bool isLegal = getIndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset,
|
|
|
|
isInc, DAG);
|
|
|
|
if (isLegal) {
|
|
|
|
AM = isInc ? ISD::POST_INC : ISD::POST_DEC;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-07-28 05:46:04 +08:00
|
|
|
void ARMTargetLowering::computeMaskedBitsForTargetNode(const SDValue Op,
|
2008-02-14 06:28:48 +08:00
|
|
|
const APInt &Mask,
|
2009-03-21 06:42:55 +08:00
|
|
|
APInt &KnownZero,
|
2008-02-13 08:35:47 +08:00
|
|
|
APInt &KnownOne,
|
2007-06-22 22:59:07 +08:00
|
|
|
const SelectionDAG &DAG,
|
2007-01-19 15:51:42 +08:00
|
|
|
unsigned Depth) const {
|
2008-02-13 08:35:47 +08:00
|
|
|
KnownZero = KnownOne = APInt(Mask.getBitWidth(), 0);
|
2007-01-19 15:51:42 +08:00
|
|
|
switch (Op.getOpcode()) {
|
|
|
|
default: break;
|
|
|
|
case ARMISD::CMOV: {
|
|
|
|
// Bits are known zero/one if known on the LHS and RHS.
|
2007-06-22 22:59:07 +08:00
|
|
|
DAG.ComputeMaskedBits(Op.getOperand(0), Mask, KnownZero, KnownOne, Depth+1);
|
2007-01-19 15:51:42 +08:00
|
|
|
if (KnownZero == 0 && KnownOne == 0) return;
|
|
|
|
|
2008-02-13 08:35:47 +08:00
|
|
|
APInt KnownZeroRHS, KnownOneRHS;
|
2007-06-22 22:59:07 +08:00
|
|
|
DAG.ComputeMaskedBits(Op.getOperand(1), Mask,
|
|
|
|
KnownZeroRHS, KnownOneRHS, Depth+1);
|
2007-01-19 15:51:42 +08:00
|
|
|
KnownZero &= KnownZeroRHS;
|
|
|
|
KnownOne &= KnownOneRHS;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ARM Inline Assembly Support
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// getConstraintType - Given a constraint letter, return the type of
|
|
|
|
/// constraint it is for this target.
|
|
|
|
ARMTargetLowering::ConstraintType
|
2007-03-25 10:14:49 +08:00
|
|
|
ARMTargetLowering::getConstraintType(const std::string &Constraint) const {
|
|
|
|
if (Constraint.size() == 1) {
|
|
|
|
switch (Constraint[0]) {
|
|
|
|
default: break;
|
|
|
|
case 'l': return C_RegisterClass;
|
2007-04-03 01:24:08 +08:00
|
|
|
case 'w': return C_RegisterClass;
|
2007-03-25 10:14:49 +08:00
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2007-03-25 10:14:49 +08:00
|
|
|
return TargetLowering::getConstraintType(Constraint);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2009-03-21 06:42:55 +08:00
|
|
|
std::pair<unsigned, const TargetRegisterClass*>
|
2007-01-19 15:51:42 +08:00
|
|
|
ARMTargetLowering::getRegForInlineAsmConstraint(const std::string &Constraint,
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT) const {
|
2007-01-19 15:51:42 +08:00
|
|
|
if (Constraint.size() == 1) {
|
|
|
|
// GCC RS6000 Constraint Letters
|
|
|
|
switch (Constraint[0]) {
|
2007-04-03 01:24:08 +08:00
|
|
|
case 'l':
|
2009-04-08 04:34:09 +08:00
|
|
|
if (Subtarget->isThumb())
|
|
|
|
return std::make_pair(0U, ARM::tGPRRegisterClass);
|
|
|
|
else
|
|
|
|
return std::make_pair(0U, ARM::GPRRegisterClass);
|
2007-04-03 01:24:08 +08:00
|
|
|
case 'r':
|
|
|
|
return std::make_pair(0U, ARM::GPRRegisterClass);
|
|
|
|
case 'w':
|
|
|
|
if (VT == MVT::f32)
|
|
|
|
return std::make_pair(0U, ARM::SPRRegisterClass);
|
2007-04-04 08:06:07 +08:00
|
|
|
if (VT == MVT::f64)
|
2007-04-03 01:24:08 +08:00
|
|
|
return std::make_pair(0U, ARM::DPRRegisterClass);
|
|
|
|
break;
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return TargetLowering::getRegForInlineAsmConstraint(Constraint, VT);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<unsigned> ARMTargetLowering::
|
|
|
|
getRegClassForInlineAsmConstraint(const std::string &Constraint,
|
2008-06-06 20:08:01 +08:00
|
|
|
MVT VT) const {
|
2007-01-19 15:51:42 +08:00
|
|
|
if (Constraint.size() != 1)
|
|
|
|
return std::vector<unsigned>();
|
|
|
|
|
|
|
|
switch (Constraint[0]) { // GCC ARM Constraint Letters
|
|
|
|
default: break;
|
|
|
|
case 'l':
|
2009-04-08 04:34:09 +08:00
|
|
|
return make_vector<unsigned>(ARM::R0, ARM::R1, ARM::R2, ARM::R3,
|
|
|
|
ARM::R4, ARM::R5, ARM::R6, ARM::R7,
|
|
|
|
0);
|
2007-01-19 15:51:42 +08:00
|
|
|
case 'r':
|
|
|
|
return make_vector<unsigned>(ARM::R0, ARM::R1, ARM::R2, ARM::R3,
|
|
|
|
ARM::R4, ARM::R5, ARM::R6, ARM::R7,
|
|
|
|
ARM::R8, ARM::R9, ARM::R10, ARM::R11,
|
|
|
|
ARM::R12, ARM::LR, 0);
|
2007-04-03 01:24:08 +08:00
|
|
|
case 'w':
|
|
|
|
if (VT == MVT::f32)
|
|
|
|
return make_vector<unsigned>(ARM::S0, ARM::S1, ARM::S2, ARM::S3,
|
|
|
|
ARM::S4, ARM::S5, ARM::S6, ARM::S7,
|
|
|
|
ARM::S8, ARM::S9, ARM::S10, ARM::S11,
|
|
|
|
ARM::S12,ARM::S13,ARM::S14,ARM::S15,
|
|
|
|
ARM::S16,ARM::S17,ARM::S18,ARM::S19,
|
|
|
|
ARM::S20,ARM::S21,ARM::S22,ARM::S23,
|
|
|
|
ARM::S24,ARM::S25,ARM::S26,ARM::S27,
|
|
|
|
ARM::S28,ARM::S29,ARM::S30,ARM::S31, 0);
|
|
|
|
if (VT == MVT::f64)
|
|
|
|
return make_vector<unsigned>(ARM::D0, ARM::D1, ARM::D2, ARM::D3,
|
|
|
|
ARM::D4, ARM::D5, ARM::D6, ARM::D7,
|
|
|
|
ARM::D8, ARM::D9, ARM::D10,ARM::D11,
|
|
|
|
ARM::D12,ARM::D13,ARM::D14,ARM::D15, 0);
|
|
|
|
break;
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::vector<unsigned>();
|
|
|
|
}
|
2009-04-02 01:58:54 +08:00
|
|
|
|
|
|
|
/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops
|
|
|
|
/// vector. If it is invalid, don't add anything to Ops.
|
|
|
|
void ARMTargetLowering::LowerAsmOperandForConstraint(SDValue Op,
|
|
|
|
char Constraint,
|
|
|
|
bool hasMemory,
|
|
|
|
std::vector<SDValue>&Ops,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
SDValue Result(0, 0);
|
|
|
|
|
|
|
|
switch (Constraint) {
|
|
|
|
default: break;
|
|
|
|
case 'I': case 'J': case 'K': case 'L':
|
|
|
|
case 'M': case 'N': case 'O':
|
|
|
|
ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op);
|
|
|
|
if (!C)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int64_t CVal64 = C->getSExtValue();
|
|
|
|
int CVal = (int) CVal64;
|
|
|
|
// None of these constraints allow values larger than 32 bits. Check
|
|
|
|
// that the value fits in an int.
|
|
|
|
if (CVal != CVal64)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (Constraint) {
|
|
|
|
case 'I':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a constant between 0 and 255, for ADD immediates.
|
|
|
|
if (CVal >= 0 && CVal <= 255)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// A constant that can be used as an immediate value in a
|
|
|
|
// data-processing instruction.
|
|
|
|
if (ARM_AM::getSOImmVal(CVal) != -1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'J':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a constant between -255 and -1, for negated ADD
|
|
|
|
// immediates. This can be used in GCC with an "n" modifier that
|
|
|
|
// prints the negated value, for use with SUB instructions. It is
|
|
|
|
// not useful otherwise but is implemented for compatibility.
|
|
|
|
if (CVal >= -255 && CVal <= -1)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// This must be a constant between -4095 and 4095. It is not clear
|
|
|
|
// what this constraint is intended for. Implemented for
|
|
|
|
// compatibility with GCC.
|
|
|
|
if (CVal >= -4095 && CVal <= 4095)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'K':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// A 32-bit value where only one byte has a nonzero value. Exclude
|
|
|
|
// zero to match GCC. This constraint is used by GCC internally for
|
|
|
|
// constants that can be loaded with a move/shift combination.
|
|
|
|
// It is not useful otherwise but is implemented for compatibility.
|
|
|
|
if (CVal != 0 && ARM_AM::isThumbImmShiftedVal(CVal))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// A constant whose bitwise inverse can be used as an immediate
|
|
|
|
// value in a data-processing instruction. This can be used in GCC
|
|
|
|
// with a "B" modifier that prints the inverted value, for use with
|
|
|
|
// BIC and MVN instructions. It is not useful otherwise but is
|
|
|
|
// implemented for compatibility.
|
|
|
|
if (ARM_AM::getSOImmVal(~CVal) != -1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a constant between -7 and 7,
|
|
|
|
// for 3-operand ADD/SUB immediate instructions.
|
|
|
|
if (CVal >= -7 && CVal < 7)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// A constant whose negation can be used as an immediate value in a
|
|
|
|
// data-processing instruction. This can be used in GCC with an "n"
|
|
|
|
// modifier that prints the negated value, for use with SUB
|
|
|
|
// instructions. It is not useful otherwise but is implemented for
|
|
|
|
// compatibility.
|
|
|
|
if (ARM_AM::getSOImmVal(-CVal) != -1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'M':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a multiple of 4 between 0 and 1020, for
|
|
|
|
// ADD sp + immediate.
|
|
|
|
if ((CVal >= 0 && CVal <= 1020) && ((CVal & 3) == 0))
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// A power of two or a constant between 0 and 32. This is used in
|
|
|
|
// GCC for the shift amount on shifted register operands, but it is
|
|
|
|
// useful in general for any shift amounts.
|
|
|
|
if ((CVal >= 0 && CVal <= 32) || ((CVal & (CVal - 1)) == 0))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'N':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a constant between 0 and 31, for shift amounts.
|
|
|
|
if (CVal >= 0 && CVal <= 31)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'O':
|
|
|
|
if (Subtarget->isThumb()) {
|
|
|
|
// This must be a multiple of 4 between -508 and 508, for
|
|
|
|
// ADD/SUB sp = sp + immediate.
|
|
|
|
if ((CVal >= -508 && CVal <= 508) && ((CVal & 3) == 0))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Result = DAG.getTargetConstant(CVal, Op.getValueType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Result.getNode()) {
|
|
|
|
Ops.push_back(Result);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, hasMemory,
|
|
|
|
Ops, DAG);
|
|
|
|
}
|