mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-09-02 11:30:10 +00:00
This patch takes pain to ensure all the PEI lowering code does the right thing when lowering frame indices, insert code to manipulate stack pointers, etc. It's also custom lowering dynamic stack alloc into pseudo instructions so we can insert the right instructions at scheduling time. This fixes PR4659 and PR4682. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@78361 91177308-0d34-0410-b5e6-96231b3b80d8
3597 lines
138 KiB
C++
3597 lines
138 KiB
C++
//===-- ARMISelLowering.cpp - ARM DAG Lowering Implementation -------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the interfaces that ARM uses to lower LLVM code into a
|
|
// selection DAG.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ARM.h"
|
|
#include "ARMAddressingModes.h"
|
|
#include "ARMConstantPoolValue.h"
|
|
#include "ARMISelLowering.h"
|
|
#include "ARMMachineFunctionInfo.h"
|
|
#include "ARMRegisterInfo.h"
|
|
#include "ARMSubtarget.h"
|
|
#include "ARMTargetMachine.h"
|
|
#include "ARMTargetObjectFile.h"
|
|
#include "llvm/CallingConv.h"
|
|
#include "llvm/Constants.h"
|
|
#include "llvm/Function.h"
|
|
#include "llvm/Instruction.h"
|
|
#include "llvm/Intrinsics.h"
|
|
#include "llvm/GlobalValue.h"
|
|
#include "llvm/CodeGen/CallingConvLower.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/PseudoSourceValue.h"
|
|
#include "llvm/CodeGen/SelectionDAG.h"
|
|
#include "llvm/Target/TargetOptions.h"
|
|
#include "llvm/ADT/VectorExtras.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
using namespace llvm;
|
|
|
|
static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State);
|
|
static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State);
|
|
static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State);
|
|
static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State);
|
|
|
|
void ARMTargetLowering::addTypeForNEON(MVT VT, MVT PromotedLdStVT,
|
|
MVT PromotedBitwiseVT) {
|
|
if (VT != PromotedLdStVT) {
|
|
setOperationAction(ISD::LOAD, VT, Promote);
|
|
AddPromotedToType (ISD::LOAD, VT, PromotedLdStVT);
|
|
|
|
setOperationAction(ISD::STORE, VT, Promote);
|
|
AddPromotedToType (ISD::STORE, VT, PromotedLdStVT);
|
|
}
|
|
|
|
MVT ElemTy = VT.getVectorElementType();
|
|
if (ElemTy != MVT::i64 && ElemTy != MVT::f64)
|
|
setOperationAction(ISD::VSETCC, VT, Custom);
|
|
if (ElemTy == MVT::i8 || ElemTy == MVT::i16)
|
|
setOperationAction(ISD::EXTRACT_VECTOR_ELT, VT, Custom);
|
|
setOperationAction(ISD::BUILD_VECTOR, VT, Custom);
|
|
setOperationAction(ISD::VECTOR_SHUFFLE, VT, Custom);
|
|
setOperationAction(ISD::SCALAR_TO_VECTOR, VT, Custom);
|
|
setOperationAction(ISD::CONCAT_VECTORS, VT, Custom);
|
|
if (VT.isInteger()) {
|
|
setOperationAction(ISD::SHL, VT, Custom);
|
|
setOperationAction(ISD::SRA, VT, Custom);
|
|
setOperationAction(ISD::SRL, VT, Custom);
|
|
}
|
|
|
|
// Promote all bit-wise operations.
|
|
if (VT.isInteger() && VT != PromotedBitwiseVT) {
|
|
setOperationAction(ISD::AND, VT, Promote);
|
|
AddPromotedToType (ISD::AND, VT, PromotedBitwiseVT);
|
|
setOperationAction(ISD::OR, VT, Promote);
|
|
AddPromotedToType (ISD::OR, VT, PromotedBitwiseVT);
|
|
setOperationAction(ISD::XOR, VT, Promote);
|
|
AddPromotedToType (ISD::XOR, VT, PromotedBitwiseVT);
|
|
}
|
|
}
|
|
|
|
void ARMTargetLowering::addDRTypeForNEON(MVT VT) {
|
|
addRegisterClass(VT, ARM::DPRRegisterClass);
|
|
addTypeForNEON(VT, MVT::f64, MVT::v2i32);
|
|
}
|
|
|
|
void ARMTargetLowering::addQRTypeForNEON(MVT VT) {
|
|
addRegisterClass(VT, ARM::QPRRegisterClass);
|
|
addTypeForNEON(VT, MVT::v2f64, MVT::v4i32);
|
|
}
|
|
|
|
static TargetLoweringObjectFile *createTLOF(TargetMachine &TM) {
|
|
if (TM.getSubtarget<ARMSubtarget>().isTargetDarwin())
|
|
return new TargetLoweringObjectFileMachO();
|
|
return new ARMElfTargetObjectFile();
|
|
}
|
|
|
|
ARMTargetLowering::ARMTargetLowering(TargetMachine &TM)
|
|
: TargetLowering(TM, createTLOF(TM)), ARMPCLabelIndex(0) {
|
|
Subtarget = &TM.getSubtarget<ARMSubtarget>();
|
|
|
|
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");
|
|
|
|
// Double-precision floating-point arithmetic.
|
|
setLibcallName(RTLIB::ADD_F64, "__adddf3vfp");
|
|
setLibcallName(RTLIB::SUB_F64, "__subdf3vfp");
|
|
setLibcallName(RTLIB::MUL_F64, "__muldf3vfp");
|
|
setLibcallName(RTLIB::DIV_F64, "__divdf3vfp");
|
|
|
|
// Single-precision comparisons.
|
|
setLibcallName(RTLIB::OEQ_F32, "__eqsf2vfp");
|
|
setLibcallName(RTLIB::UNE_F32, "__nesf2vfp");
|
|
setLibcallName(RTLIB::OLT_F32, "__ltsf2vfp");
|
|
setLibcallName(RTLIB::OLE_F32, "__lesf2vfp");
|
|
setLibcallName(RTLIB::OGE_F32, "__gesf2vfp");
|
|
setLibcallName(RTLIB::OGT_F32, "__gtsf2vfp");
|
|
setLibcallName(RTLIB::UO_F32, "__unordsf2vfp");
|
|
setLibcallName(RTLIB::O_F32, "__unordsf2vfp");
|
|
|
|
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);
|
|
|
|
// 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");
|
|
|
|
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);
|
|
|
|
// Floating-point to integer conversions.
|
|
// i64 conversions are done via library routines even when generating VFP
|
|
// instructions, so use the same ones.
|
|
setLibcallName(RTLIB::FPTOSINT_F64_I32, "__fixdfsivfp");
|
|
setLibcallName(RTLIB::FPTOUINT_F64_I32, "__fixunsdfsivfp");
|
|
setLibcallName(RTLIB::FPTOSINT_F32_I32, "__fixsfsivfp");
|
|
setLibcallName(RTLIB::FPTOUINT_F32_I32, "__fixunssfsivfp");
|
|
|
|
// Conversions between floating types.
|
|
setLibcallName(RTLIB::FPROUND_F64_F32, "__truncdfsf2vfp");
|
|
setLibcallName(RTLIB::FPEXT_F32_F64, "__extendsfdf2vfp");
|
|
|
|
// Integer to floating-point conversions.
|
|
// i64 conversions are done via library routines even when generating VFP
|
|
// instructions, so use the same ones.
|
|
// FIXME: There appears to be some naming inconsistency in ARM libgcc:
|
|
// e.g., __floatunsidf vs. __floatunssidfvfp.
|
|
setLibcallName(RTLIB::SINTTOFP_I32_F64, "__floatsidfvfp");
|
|
setLibcallName(RTLIB::UINTTOFP_I32_F64, "__floatunssidfvfp");
|
|
setLibcallName(RTLIB::SINTTOFP_I32_F32, "__floatsisfvfp");
|
|
setLibcallName(RTLIB::UINTTOFP_I32_F32, "__floatunssisfvfp");
|
|
}
|
|
}
|
|
|
|
// These libcalls are not available in 32-bit.
|
|
setLibcallName(RTLIB::SHL_I128, 0);
|
|
setLibcallName(RTLIB::SRL_I128, 0);
|
|
setLibcallName(RTLIB::SRA_I128, 0);
|
|
|
|
if (Subtarget->isThumb1Only())
|
|
addRegisterClass(MVT::i32, ARM::tGPRRegisterClass);
|
|
else
|
|
addRegisterClass(MVT::i32, ARM::GPRRegisterClass);
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb1Only()) {
|
|
addRegisterClass(MVT::f32, ARM::SPRRegisterClass);
|
|
addRegisterClass(MVT::f64, ARM::DPRRegisterClass);
|
|
|
|
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
|
|
}
|
|
|
|
if (Subtarget->hasNEON()) {
|
|
addDRTypeForNEON(MVT::v2f32);
|
|
addDRTypeForNEON(MVT::v8i8);
|
|
addDRTypeForNEON(MVT::v4i16);
|
|
addDRTypeForNEON(MVT::v2i32);
|
|
addDRTypeForNEON(MVT::v1i64);
|
|
|
|
addQRTypeForNEON(MVT::v4f32);
|
|
addQRTypeForNEON(MVT::v2f64);
|
|
addQRTypeForNEON(MVT::v16i8);
|
|
addQRTypeForNEON(MVT::v8i16);
|
|
addQRTypeForNEON(MVT::v4i32);
|
|
addQRTypeForNEON(MVT::v2i64);
|
|
|
|
setTargetDAGCombine(ISD::INTRINSIC_WO_CHAIN);
|
|
setTargetDAGCombine(ISD::SHL);
|
|
setTargetDAGCombine(ISD::SRL);
|
|
setTargetDAGCombine(ISD::SRA);
|
|
setTargetDAGCombine(ISD::SIGN_EXTEND);
|
|
setTargetDAGCombine(ISD::ZERO_EXTEND);
|
|
setTargetDAGCombine(ISD::ANY_EXTEND);
|
|
}
|
|
|
|
computeRegisterProperties();
|
|
|
|
// ARM does not have f32 extending load.
|
|
setLoadExtAction(ISD::EXTLOAD, MVT::f32, Expand);
|
|
|
|
// ARM does not have i1 sign extending load.
|
|
setLoadExtAction(ISD::SEXTLOAD, MVT::i1, Promote);
|
|
|
|
// ARM supports all 4 flavors of integer indexed load / store.
|
|
if (!Subtarget->isThumb1Only()) {
|
|
for (unsigned im = (unsigned)ISD::PRE_INC;
|
|
im != (unsigned)ISD::LAST_INDEXED_MODE; ++im) {
|
|
setIndexedLoadAction(im, MVT::i1, Legal);
|
|
setIndexedLoadAction(im, MVT::i8, Legal);
|
|
setIndexedLoadAction(im, MVT::i16, Legal);
|
|
setIndexedLoadAction(im, MVT::i32, Legal);
|
|
setIndexedStoreAction(im, MVT::i1, Legal);
|
|
setIndexedStoreAction(im, MVT::i8, Legal);
|
|
setIndexedStoreAction(im, MVT::i16, Legal);
|
|
setIndexedStoreAction(im, MVT::i32, Legal);
|
|
}
|
|
}
|
|
|
|
// i64 operation support.
|
|
if (Subtarget->isThumb1Only()) {
|
|
setOperationAction(ISD::MUL, MVT::i64, Expand);
|
|
setOperationAction(ISD::MULHU, MVT::i32, Expand);
|
|
setOperationAction(ISD::MULHS, MVT::i32, Expand);
|
|
setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand);
|
|
setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand);
|
|
} else {
|
|
setOperationAction(ISD::MUL, MVT::i64, Expand);
|
|
setOperationAction(ISD::MULHU, MVT::i32, Expand);
|
|
if (!Subtarget->hasV6Ops())
|
|
setOperationAction(ISD::MULHS, MVT::i32, Expand);
|
|
}
|
|
setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand);
|
|
setOperationAction(ISD::SRA_PARTS, MVT::i32, Expand);
|
|
setOperationAction(ISD::SRL_PARTS, MVT::i32, Expand);
|
|
setOperationAction(ISD::SRL, MVT::i64, Custom);
|
|
setOperationAction(ISD::SRA, MVT::i64, Custom);
|
|
|
|
// ARM does not have ROTL.
|
|
setOperationAction(ISD::ROTL, MVT::i32, Expand);
|
|
setOperationAction(ISD::CTTZ, MVT::i32, Expand);
|
|
setOperationAction(ISD::CTPOP, MVT::i32, Expand);
|
|
if (!Subtarget->hasV5TOps() || Subtarget->isThumb1Only())
|
|
setOperationAction(ISD::CTLZ, MVT::i32, Expand);
|
|
|
|
// Only ARMv6 has BSWAP.
|
|
if (!Subtarget->hasV6Ops())
|
|
setOperationAction(ISD::BSWAP, MVT::i32, Expand);
|
|
|
|
// These are expanded into libcalls.
|
|
setOperationAction(ISD::SDIV, MVT::i32, Expand);
|
|
setOperationAction(ISD::UDIV, MVT::i32, Expand);
|
|
setOperationAction(ISD::SREM, MVT::i32, Expand);
|
|
setOperationAction(ISD::UREM, MVT::i32, Expand);
|
|
setOperationAction(ISD::SDIVREM, MVT::i32, Expand);
|
|
setOperationAction(ISD::UDIVREM, MVT::i32, Expand);
|
|
|
|
// Support label based line numbers.
|
|
setOperationAction(ISD::DBG_STOPPOINT, MVT::Other, Expand);
|
|
setOperationAction(ISD::DEBUG_LOC, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::GlobalAddress, MVT::i32, Custom);
|
|
setOperationAction(ISD::ConstantPool, MVT::i32, Custom);
|
|
setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom);
|
|
setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom);
|
|
|
|
// Use the default implementation.
|
|
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);
|
|
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
|
|
if (Subtarget->isThumb())
|
|
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Custom);
|
|
else
|
|
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand);
|
|
setOperationAction(ISD::MEMBARRIER, MVT::Other, Expand);
|
|
|
|
if (!Subtarget->hasV6Ops() && !Subtarget->isThumb2()) {
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand);
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand);
|
|
}
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand);
|
|
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb1Only())
|
|
// Turn f64->i64 into FMRRD, i64 -> f64 to FMDRR iff target supports vfp2.
|
|
setOperationAction(ISD::BIT_CONVERT, MVT::i64, Custom);
|
|
|
|
// We want to custom lower some of our intrinsics.
|
|
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
|
|
setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
|
|
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
|
|
|
|
setOperationAction(ISD::SETCC, MVT::i32, Expand);
|
|
setOperationAction(ISD::SETCC, MVT::f32, Expand);
|
|
setOperationAction(ISD::SETCC, MVT::f64, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::i32, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::f32, Expand);
|
|
setOperationAction(ISD::SELECT, MVT::f64, Expand);
|
|
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
|
|
setOperationAction(ISD::SELECT_CC, MVT::f32, Custom);
|
|
setOperationAction(ISD::SELECT_CC, MVT::f64, Custom);
|
|
|
|
setOperationAction(ISD::BRCOND, MVT::Other, Expand);
|
|
setOperationAction(ISD::BR_CC, MVT::i32, Custom);
|
|
setOperationAction(ISD::BR_CC, MVT::f32, Custom);
|
|
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
|
|
setOperationAction(ISD::BR_JT, MVT::Other, Custom);
|
|
|
|
// We don't support sin/cos/fmod/copysign/pow
|
|
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);
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb1Only()) {
|
|
setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom);
|
|
setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom);
|
|
}
|
|
setOperationAction(ISD::FPOW, MVT::f64, Expand);
|
|
setOperationAction(ISD::FPOW, MVT::f32, Expand);
|
|
|
|
// int <-> fp are custom expanded into bit_convert + ARMISD ops.
|
|
if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb1Only()) {
|
|
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);
|
|
}
|
|
|
|
// We have target-specific dag combine patterns for the following nodes:
|
|
// ARMISD::FMRRD - No need to call setTargetDAGCombine
|
|
setTargetDAGCombine(ISD::ADD);
|
|
setTargetDAGCombine(ISD::SUB);
|
|
|
|
setStackPointerRegisterToSaveRestore(ARM::SP);
|
|
setSchedulingPreference(SchedulingForRegPressure);
|
|
setIfCvtBlockSizeLimit(Subtarget->isThumb() ? 0 : 10);
|
|
setIfCvtDupBlockSizeLimit(Subtarget->isThumb() ? 0 : 2);
|
|
|
|
if (!Subtarget->isThumb()) {
|
|
// Use branch latency information to determine if-conversion limits.
|
|
// FIXME: If-converter should use instruction latency of the branch being
|
|
// eliminated to compute the threshold. For ARMv6, the branch "latency"
|
|
// varies depending on whether it's dynamically or statically predicted
|
|
// and on whether the destination is in the prefetch buffer.
|
|
const TargetInstrInfo *TII = getTargetMachine().getInstrInfo();
|
|
const InstrItineraryData &InstrItins = Subtarget->getInstrItineraryData();
|
|
unsigned Latency= InstrItins.getLatency(TII->get(ARM::Bcc).getSchedClass());
|
|
if (Latency > 1) {
|
|
setIfCvtBlockSizeLimit(Latency-1);
|
|
if (Latency > 2)
|
|
setIfCvtDupBlockSizeLimit(Latency-2);
|
|
} else {
|
|
setIfCvtBlockSizeLimit(10);
|
|
setIfCvtDupBlockSizeLimit(2);
|
|
}
|
|
}
|
|
|
|
maxStoresPerMemcpy = 1; //// temporary - rewrite interface to use type
|
|
// Do not enable CodePlacementOpt for now: it currently runs after the
|
|
// ARMConstantIslandPass and messes up branch relaxation and placement
|
|
// of constant islands.
|
|
// benefitFromCodePlacementOpt = true;
|
|
}
|
|
|
|
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";
|
|
case ARMISD::CALL_PRED: return "ARMISD::CALL_PRED";
|
|
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::BR2_JT: return "ARMISD::BR2_JT";
|
|
case ARMISD::RET_FLAG: return "ARMISD::RET_FLAG";
|
|
case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD";
|
|
case ARMISD::CMP: return "ARMISD::CMP";
|
|
case ARMISD::CMPZ: return "ARMISD::CMPZ";
|
|
case ARMISD::CMPFP: return "ARMISD::CMPFP";
|
|
case ARMISD::CMPFPw0: return "ARMISD::CMPFPw0";
|
|
case ARMISD::FMSTAT: return "ARMISD::FMSTAT";
|
|
case ARMISD::CMOV: return "ARMISD::CMOV";
|
|
case ARMISD::CNEG: return "ARMISD::CNEG";
|
|
|
|
case ARMISD::FTOSI: return "ARMISD::FTOSI";
|
|
case ARMISD::FTOUI: return "ARMISD::FTOUI";
|
|
case ARMISD::SITOF: return "ARMISD::SITOF";
|
|
case ARMISD::UITOF: return "ARMISD::UITOF";
|
|
|
|
case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG";
|
|
case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG";
|
|
case ARMISD::RRX: return "ARMISD::RRX";
|
|
|
|
case ARMISD::FMRRD: return "ARMISD::FMRRD";
|
|
case ARMISD::FMDRR: return "ARMISD::FMDRR";
|
|
|
|
case ARMISD::THREAD_POINTER:return "ARMISD::THREAD_POINTER";
|
|
|
|
case ARMISD::DYN_ALLOC: return "ARMISD::DYN_ALLOC";
|
|
|
|
case ARMISD::VCEQ: return "ARMISD::VCEQ";
|
|
case ARMISD::VCGE: return "ARMISD::VCGE";
|
|
case ARMISD::VCGEU: return "ARMISD::VCGEU";
|
|
case ARMISD::VCGT: return "ARMISD::VCGT";
|
|
case ARMISD::VCGTU: return "ARMISD::VCGTU";
|
|
case ARMISD::VTST: return "ARMISD::VTST";
|
|
|
|
case ARMISD::VSHL: return "ARMISD::VSHL";
|
|
case ARMISD::VSHRs: return "ARMISD::VSHRs";
|
|
case ARMISD::VSHRu: return "ARMISD::VSHRu";
|
|
case ARMISD::VSHLLs: return "ARMISD::VSHLLs";
|
|
case ARMISD::VSHLLu: return "ARMISD::VSHLLu";
|
|
case ARMISD::VSHLLi: return "ARMISD::VSHLLi";
|
|
case ARMISD::VSHRN: return "ARMISD::VSHRN";
|
|
case ARMISD::VRSHRs: return "ARMISD::VRSHRs";
|
|
case ARMISD::VRSHRu: return "ARMISD::VRSHRu";
|
|
case ARMISD::VRSHRN: return "ARMISD::VRSHRN";
|
|
case ARMISD::VQSHLs: return "ARMISD::VQSHLs";
|
|
case ARMISD::VQSHLu: return "ARMISD::VQSHLu";
|
|
case ARMISD::VQSHLsu: return "ARMISD::VQSHLsu";
|
|
case ARMISD::VQSHRNs: return "ARMISD::VQSHRNs";
|
|
case ARMISD::VQSHRNu: return "ARMISD::VQSHRNu";
|
|
case ARMISD::VQSHRNsu: return "ARMISD::VQSHRNsu";
|
|
case ARMISD::VQRSHRNs: return "ARMISD::VQRSHRNs";
|
|
case ARMISD::VQRSHRNu: return "ARMISD::VQRSHRNu";
|
|
case ARMISD::VQRSHRNsu: return "ARMISD::VQRSHRNsu";
|
|
case ARMISD::VGETLANEu: return "ARMISD::VGETLANEu";
|
|
case ARMISD::VGETLANEs: return "ARMISD::VGETLANEs";
|
|
case ARMISD::VDUPLANEQ: return "ARMISD::VDUPLANEQ";
|
|
case ARMISD::VLD2D: return "ARMISD::VLD2D";
|
|
case ARMISD::VLD3D: return "ARMISD::VLD3D";
|
|
case ARMISD::VLD4D: return "ARMISD::VLD4D";
|
|
case ARMISD::VST2D: return "ARMISD::VST2D";
|
|
case ARMISD::VST3D: return "ARMISD::VST3D";
|
|
case ARMISD::VST4D: return "ARMISD::VST4D";
|
|
}
|
|
}
|
|
|
|
/// getFunctionAlignment - Return the Log2 alignment of this function.
|
|
unsigned ARMTargetLowering::getFunctionAlignment(const Function *F) const {
|
|
return getTargetMachine().getSubtarget<ARMSubtarget>().isThumb() ? 1 : 2;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Lowering Code
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// IntCCToARMCC - Convert a DAG integer condition code to an ARM CC
|
|
static ARMCC::CondCodes IntCCToARMCC(ISD::CondCode CC) {
|
|
switch (CC) {
|
|
default: llvm_unreachable("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: llvm_unreachable("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;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Calling Convention Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ARMGenCallingConv.inc"
|
|
|
|
// APCS f64 is in register pairs, possibly split to stack
|
|
static bool f64AssignAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
CCState &State, bool CanFail) {
|
|
static const unsigned RegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 };
|
|
|
|
// Try to get the first register.
|
|
if (unsigned Reg = State.AllocateReg(RegList, 4))
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
|
else {
|
|
// For the 2nd half of a v2f64, do not fail.
|
|
if (CanFail)
|
|
return false;
|
|
|
|
// Put the whole thing on the stack.
|
|
State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT,
|
|
State.AllocateStack(8, 4),
|
|
LocVT, LocInfo));
|
|
return true;
|
|
}
|
|
|
|
// Try to get the second register.
|
|
if (unsigned Reg = State.AllocateReg(RegList, 4))
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
|
else
|
|
State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT,
|
|
State.AllocateStack(4, 4),
|
|
LocVT, LocInfo));
|
|
return true;
|
|
}
|
|
|
|
static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State) {
|
|
if (!f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, true))
|
|
return false;
|
|
if (LocVT == MVT::v2f64 &&
|
|
!f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, false))
|
|
return false;
|
|
return true; // we handled it
|
|
}
|
|
|
|
// AAPCS f64 is in aligned register pairs
|
|
static bool f64AssignAAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
CCState &State, bool CanFail) {
|
|
static const unsigned HiRegList[] = { ARM::R0, ARM::R2 };
|
|
static const unsigned LoRegList[] = { ARM::R1, ARM::R3 };
|
|
|
|
unsigned Reg = State.AllocateReg(HiRegList, LoRegList, 2);
|
|
if (Reg == 0) {
|
|
// For the 2nd half of a v2f64, do not just fail.
|
|
if (CanFail)
|
|
return false;
|
|
|
|
// Put the whole thing on the stack.
|
|
State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT,
|
|
State.AllocateStack(8, 8),
|
|
LocVT, LocInfo));
|
|
return true;
|
|
}
|
|
|
|
unsigned i;
|
|
for (i = 0; i < 2; ++i)
|
|
if (HiRegList[i] == Reg)
|
|
break;
|
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i],
|
|
LocVT, LocInfo));
|
|
return true;
|
|
}
|
|
|
|
static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State) {
|
|
if (!f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, true))
|
|
return false;
|
|
if (LocVT == MVT::v2f64 &&
|
|
!f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, false))
|
|
return false;
|
|
return true; // we handled it
|
|
}
|
|
|
|
static bool f64RetAssign(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo, CCState &State) {
|
|
static const unsigned HiRegList[] = { ARM::R0, ARM::R2 };
|
|
static const unsigned LoRegList[] = { ARM::R1, ARM::R3 };
|
|
|
|
unsigned Reg = State.AllocateReg(HiRegList, LoRegList, 2);
|
|
if (Reg == 0)
|
|
return false; // we didn't handle it
|
|
|
|
unsigned i;
|
|
for (i = 0; i < 2; ++i)
|
|
if (HiRegList[i] == Reg)
|
|
break;
|
|
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
|
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i],
|
|
LocVT, LocInfo));
|
|
return true;
|
|
}
|
|
|
|
static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State) {
|
|
if (!f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State))
|
|
return false;
|
|
if (LocVT == MVT::v2f64 && !f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State))
|
|
return false;
|
|
return true; // we handled it
|
|
}
|
|
|
|
static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
|
CCValAssign::LocInfo &LocInfo,
|
|
ISD::ArgFlagsTy &ArgFlags,
|
|
CCState &State) {
|
|
return RetCC_ARM_APCS_Custom_f64(ValNo, ValVT, LocVT, LocInfo, ArgFlags,
|
|
State);
|
|
}
|
|
|
|
/// CCAssignFnForNode - Selects the correct CCAssignFn for a the
|
|
/// given CallingConvention value.
|
|
CCAssignFn *ARMTargetLowering::CCAssignFnForNode(unsigned CC,
|
|
bool Return,
|
|
bool isVarArg) const {
|
|
switch (CC) {
|
|
default:
|
|
llvm_unreachable("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 && !isVarArg)
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// LowerCallResult - Lower the result values of a call into the
|
|
/// appropriate copies out of appropriate physical registers.
|
|
SDValue
|
|
ARMTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag,
|
|
unsigned CallConv, bool isVarArg,
|
|
const SmallVectorImpl<ISD::InputArg> &Ins,
|
|
DebugLoc dl, SelectionDAG &DAG,
|
|
SmallVectorImpl<SDValue> &InVals) {
|
|
|
|
// Assign locations to each value returned by this call.
|
|
SmallVector<CCValAssign, 16> RVLocs;
|
|
CCState CCInfo(CallConv, isVarArg, getTargetMachine(),
|
|
RVLocs, *DAG.getContext());
|
|
CCInfo.AnalyzeCallResult(Ins,
|
|
CCAssignFnForNode(CallConv, /* Return*/ true,
|
|
isVarArg));
|
|
|
|
// Copy all of the result registers out of their specified physreg.
|
|
for (unsigned i = 0; i != RVLocs.size(); ++i) {
|
|
CCValAssign VA = RVLocs[i];
|
|
|
|
SDValue Val;
|
|
if (VA.needsCustom()) {
|
|
// Handle f64 or half of a v2f64.
|
|
SDValue Lo = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32,
|
|
InFlag);
|
|
Chain = Lo.getValue(1);
|
|
InFlag = Lo.getValue(2);
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
SDValue Hi = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32,
|
|
InFlag);
|
|
Chain = Hi.getValue(1);
|
|
InFlag = Hi.getValue(2);
|
|
Val = DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, Lo, Hi);
|
|
|
|
if (VA.getLocVT() == MVT::v2f64) {
|
|
SDValue Vec = DAG.getNode(ISD::UNDEF, dl, MVT::v2f64);
|
|
Vec = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64, Vec, Val,
|
|
DAG.getConstant(0, MVT::i32));
|
|
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
Lo = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32, InFlag);
|
|
Chain = Lo.getValue(1);
|
|
InFlag = Lo.getValue(2);
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
Hi = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), MVT::i32, InFlag);
|
|
Chain = Hi.getValue(1);
|
|
InFlag = Hi.getValue(2);
|
|
Val = DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, Lo, Hi);
|
|
Val = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64, Vec, Val,
|
|
DAG.getConstant(1, MVT::i32));
|
|
}
|
|
} else {
|
|
Val = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), VA.getLocVT(),
|
|
InFlag);
|
|
Chain = Val.getValue(1);
|
|
InFlag = Val.getValue(2);
|
|
}
|
|
|
|
switch (VA.getLocInfo()) {
|
|
default: llvm_unreachable("Unknown loc info!");
|
|
case CCValAssign::Full: break;
|
|
case CCValAssign::BCvt:
|
|
Val = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getValVT(), Val);
|
|
break;
|
|
}
|
|
|
|
InVals.push_back(Val);
|
|
}
|
|
|
|
return Chain;
|
|
}
|
|
|
|
/// CreateCopyOfByValArgument - Make a copy of an aggregate at address specified
|
|
/// by "Src" to address "Dst" of size "Size". Alignment information is
|
|
/// specified by the specific parameter attribute. The copy will be passed as
|
|
/// 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);
|
|
}
|
|
|
|
/// LowerMemOpCallTo - Store the argument to the stack.
|
|
SDValue
|
|
ARMTargetLowering::LowerMemOpCallTo(SDValue Chain,
|
|
SDValue StackPtr, SDValue Arg,
|
|
DebugLoc dl, SelectionDAG &DAG,
|
|
const CCValAssign &VA,
|
|
ISD::ArgFlagsTy Flags) {
|
|
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);
|
|
}
|
|
|
|
void ARMTargetLowering::PassF64ArgInRegs(DebugLoc dl, SelectionDAG &DAG,
|
|
SDValue Chain, SDValue &Arg,
|
|
RegsToPassVector &RegsToPass,
|
|
CCValAssign &VA, CCValAssign &NextVA,
|
|
SDValue &StackPtr,
|
|
SmallVector<SDValue, 8> &MemOpChains,
|
|
ISD::ArgFlagsTy Flags) {
|
|
|
|
SDValue fmrrd = DAG.getNode(ARMISD::FMRRD, dl,
|
|
DAG.getVTList(MVT::i32, MVT::i32), Arg);
|
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), fmrrd));
|
|
|
|
if (NextVA.isRegLoc())
|
|
RegsToPass.push_back(std::make_pair(NextVA.getLocReg(), fmrrd.getValue(1)));
|
|
else {
|
|
assert(NextVA.isMemLoc());
|
|
if (StackPtr.getNode() == 0)
|
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy());
|
|
|
|
MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, fmrrd.getValue(1),
|
|
dl, DAG, NextVA,
|
|
Flags));
|
|
}
|
|
}
|
|
|
|
/// LowerCall - Lowering a call into a callseq_start <-
|
|
/// ARMISD:CALL <- callseq_end chain. Also add input and output parameter
|
|
/// nodes.
|
|
SDValue
|
|
ARMTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|
unsigned CallConv, bool isVarArg,
|
|
bool isTailCall,
|
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
|
const SmallVectorImpl<ISD::InputArg> &Ins,
|
|
DebugLoc dl, SelectionDAG &DAG,
|
|
SmallVectorImpl<SDValue> &InVals) {
|
|
|
|
// Analyze operands of the call, assigning locations to each operand.
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
CCState CCInfo(CallConv, isVarArg, getTargetMachine(), ArgLocs,
|
|
*DAG.getContext());
|
|
CCInfo.AnalyzeCallOperands(Outs,
|
|
CCAssignFnForNode(CallConv, /* Return*/ false,
|
|
isVarArg));
|
|
|
|
// Get a count of how many bytes are to be pushed on the stack.
|
|
unsigned NumBytes = CCInfo.getNextStackOffset();
|
|
|
|
// Adjust the stack pointer for the new arguments...
|
|
// These operations are automatically eliminated by the prolog/epilog pass
|
|
Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(NumBytes, true));
|
|
|
|
SDValue StackPtr = DAG.getRegister(ARM::SP, MVT::i32);
|
|
|
|
RegsToPassVector RegsToPass;
|
|
SmallVector<SDValue, 8> MemOpChains;
|
|
|
|
// Walk the register/memloc assignments, inserting copies/loads. In the case
|
|
// of tail call optimization, arguments are handled later.
|
|
for (unsigned i = 0, realArgIdx = 0, e = ArgLocs.size();
|
|
i != e;
|
|
++i, ++realArgIdx) {
|
|
CCValAssign &VA = ArgLocs[i];
|
|
SDValue Arg = Outs[realArgIdx].Val;
|
|
ISD::ArgFlagsTy Flags = Outs[realArgIdx].Flags;
|
|
|
|
// Promote the value if needed.
|
|
switch (VA.getLocInfo()) {
|
|
default: llvm_unreachable("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 and v2f64 might be passed in i32 pairs and must be split into pieces
|
|
if (VA.needsCustom()) {
|
|
if (VA.getLocVT() == MVT::v2f64) {
|
|
SDValue Op0 = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f64, Arg,
|
|
DAG.getConstant(0, MVT::i32));
|
|
SDValue Op1 = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f64, Arg,
|
|
DAG.getConstant(1, MVT::i32));
|
|
|
|
PassF64ArgInRegs(dl, DAG, Chain, Op0, RegsToPass,
|
|
VA, ArgLocs[++i], StackPtr, MemOpChains, Flags);
|
|
|
|
VA = ArgLocs[++i]; // skip ahead to next loc
|
|
if (VA.isRegLoc()) {
|
|
PassF64ArgInRegs(dl, DAG, Chain, Op1, RegsToPass,
|
|
VA, ArgLocs[++i], StackPtr, MemOpChains, Flags);
|
|
} else {
|
|
assert(VA.isMemLoc());
|
|
if (StackPtr.getNode() == 0)
|
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy());
|
|
|
|
MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, Op1,
|
|
dl, DAG, VA, Flags));
|
|
}
|
|
} else {
|
|
PassF64ArgInRegs(dl, DAG, Chain, Arg, RegsToPass, VA, ArgLocs[++i],
|
|
StackPtr, MemOpChains, Flags);
|
|
}
|
|
} else if (VA.isRegLoc()) {
|
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg));
|
|
} else {
|
|
assert(VA.isMemLoc());
|
|
if (StackPtr.getNode() == 0)
|
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy());
|
|
|
|
MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, Arg,
|
|
dl, DAG, VA, Flags));
|
|
}
|
|
}
|
|
|
|
if (!MemOpChains.empty())
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
|
&MemOpChains[0], MemOpChains.size());
|
|
|
|
// Build a sequence of copy-to-reg nodes chained together with token chain
|
|
// and flag operands which copy the outgoing args into the appropriate regs.
|
|
SDValue InFlag;
|
|
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) {
|
|
Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first,
|
|
RegsToPass[i].second, InFlag);
|
|
InFlag = Chain.getValue(1);
|
|
}
|
|
|
|
// If the callee is a GlobalAddress/ExternalSymbol node (quite common, every
|
|
// direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol
|
|
// node so that legalize doesn't hack it.
|
|
bool isDirect = false;
|
|
bool isARMFunc = false;
|
|
bool isLocalARMFunc = false;
|
|
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
|
|
GlobalValue *GV = G->getGlobal();
|
|
isDirect = true;
|
|
bool isExt = GV->isDeclaration() || GV->isWeakForLinker();
|
|
bool isStub = (isExt && Subtarget->isTargetDarwin()) &&
|
|
getTargetMachine().getRelocationModel() != Reloc::Static;
|
|
isARMFunc = !Subtarget->isThumb() || isStub;
|
|
// ARM call to a local ARM function is predicable.
|
|
isLocalARMFunc = !Subtarget->isThumb() && !isExt;
|
|
// tBX takes a register source operand.
|
|
if (isARMFunc && Subtarget->isThumb1Only() && !Subtarget->hasV5TOps()) {
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex,
|
|
ARMCP::CPStub, 4);
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 4);
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
Callee = DAG.getLoad(getPointerTy(), dl,
|
|
DAG.getEntryNode(), CPAddr, NULL, 0);
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
Callee = DAG.getNode(ARMISD::PIC_ADD, dl,
|
|
getPointerTy(), Callee, PICLabel);
|
|
} else
|
|
Callee = DAG.getTargetGlobalAddress(GV, getPointerTy());
|
|
} else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
|
|
isDirect = true;
|
|
bool isStub = Subtarget->isTargetDarwin() &&
|
|
getTargetMachine().getRelocationModel() != Reloc::Static;
|
|
isARMFunc = !Subtarget->isThumb() || isStub;
|
|
// tBX takes a register source operand.
|
|
const char *Sym = S->getSymbol();
|
|
if (isARMFunc && Subtarget->isThumb1Only() && !Subtarget->hasV5TOps()) {
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(Sym, ARMPCLabelIndex,
|
|
ARMCP::CPStub, 4);
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 4);
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
Callee = DAG.getLoad(getPointerTy(), dl,
|
|
DAG.getEntryNode(), CPAddr, NULL, 0);
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
Callee = DAG.getNode(ARMISD::PIC_ADD, dl,
|
|
getPointerTy(), Callee, PICLabel);
|
|
} else
|
|
Callee = DAG.getTargetExternalSymbol(Sym, getPointerTy());
|
|
}
|
|
|
|
// FIXME: handle tail calls differently.
|
|
unsigned CallOpc;
|
|
if (Subtarget->isThumb()) {
|
|
if ((!isDirect || isARMFunc) && !Subtarget->hasV5TOps())
|
|
CallOpc = ARMISD::CALL_NOLINK;
|
|
else
|
|
CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL;
|
|
} else {
|
|
CallOpc = (isDirect || Subtarget->hasV5TOps())
|
|
? (isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL)
|
|
: ARMISD::CALL_NOLINK;
|
|
}
|
|
if (CallOpc == ARMISD::CALL_NOLINK && !Subtarget->isThumb1Only()) {
|
|
// implicit def LR - LR mustn't be allocated as GRP:$dst of CALL_NOLINK
|
|
Chain = DAG.getCopyToReg(Chain, dl, ARM::LR, DAG.getUNDEF(MVT::i32),InFlag);
|
|
InFlag = Chain.getValue(1);
|
|
}
|
|
|
|
std::vector<SDValue> Ops;
|
|
Ops.push_back(Chain);
|
|
Ops.push_back(Callee);
|
|
|
|
// Add argument registers to the end of the list so that they are known live
|
|
// into the call.
|
|
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i)
|
|
Ops.push_back(DAG.getRegister(RegsToPass[i].first,
|
|
RegsToPass[i].second.getValueType()));
|
|
|
|
if (InFlag.getNode())
|
|
Ops.push_back(InFlag);
|
|
// Returns a chain and a flag for retval copy to use.
|
|
Chain = DAG.getNode(CallOpc, dl, DAG.getVTList(MVT::Other, MVT::Flag),
|
|
&Ops[0], Ops.size());
|
|
InFlag = Chain.getValue(1);
|
|
|
|
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, true),
|
|
DAG.getIntPtrConstant(0, true), InFlag);
|
|
if (!Ins.empty())
|
|
InFlag = Chain.getValue(1);
|
|
|
|
// Handle result values, copying them out of physregs into vregs that we
|
|
// return.
|
|
return LowerCallResult(Chain, InFlag, CallConv, isVarArg, Ins,
|
|
dl, DAG, InVals);
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerReturn(SDValue Chain,
|
|
unsigned CallConv, bool isVarArg,
|
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
|
DebugLoc dl, SelectionDAG &DAG) {
|
|
|
|
// CCValAssign - represent the assignment of the return value to a location.
|
|
SmallVector<CCValAssign, 16> RVLocs;
|
|
|
|
// CCState - Info about the registers and stack slots.
|
|
CCState CCInfo(CallConv, isVarArg, getTargetMachine(), RVLocs,
|
|
*DAG.getContext());
|
|
|
|
// Analyze outgoing return values.
|
|
CCInfo.AnalyzeReturn(Outs, CCAssignFnForNode(CallConv, /* Return */ true,
|
|
isVarArg));
|
|
|
|
// 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());
|
|
}
|
|
|
|
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!");
|
|
|
|
SDValue Arg = Outs[realRVLocIdx].Val;
|
|
|
|
switch (VA.getLocInfo()) {
|
|
default: llvm_unreachable("Unknown loc info!");
|
|
case CCValAssign::Full: break;
|
|
case CCValAssign::BCvt:
|
|
Arg = DAG.getNode(ISD::BIT_CONVERT, dl, VA.getLocVT(), Arg);
|
|
break;
|
|
}
|
|
|
|
if (VA.needsCustom()) {
|
|
if (VA.getLocVT() == MVT::v2f64) {
|
|
// Extract the first half and return it in two registers.
|
|
SDValue Half = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f64, Arg,
|
|
DAG.getConstant(0, MVT::i32));
|
|
SDValue HalfGPRs = DAG.getNode(ARMISD::FMRRD, dl,
|
|
DAG.getVTList(MVT::i32, MVT::i32), Half);
|
|
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), HalfGPRs, Flag);
|
|
Flag = Chain.getValue(1);
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(),
|
|
HalfGPRs.getValue(1), Flag);
|
|
Flag = Chain.getValue(1);
|
|
VA = RVLocs[++i]; // skip ahead to next loc
|
|
|
|
// Extract the 2nd half and fall through to handle it as an f64 value.
|
|
Arg = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f64, Arg,
|
|
DAG.getConstant(1, MVT::i32));
|
|
}
|
|
// Legalize ret f64 -> ret 2 x i32. We always have fmrrd if f64 is
|
|
// available.
|
|
SDValue fmrrd = DAG.getNode(ARMISD::FMRRD, dl,
|
|
DAG.getVTList(MVT::i32, MVT::i32), &Arg, 1);
|
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), fmrrd, Flag);
|
|
Flag = Chain.getValue(1);
|
|
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);
|
|
|
|
// Guarantee that all emitted copies are
|
|
// stuck together, avoiding something bad.
|
|
Flag = Chain.getValue(1);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as
|
|
// their target counterpart 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.
|
|
static SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) {
|
|
MVT PtrVT = Op.getValueType();
|
|
// FIXME there is no actual debug info here
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op);
|
|
SDValue Res;
|
|
if (CP->isMachineConstantPoolEntry())
|
|
Res = DAG.getTargetConstantPool(CP->getMachineCPVal(), PtrVT,
|
|
CP->getAlignment());
|
|
else
|
|
Res = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT,
|
|
CP->getAlignment());
|
|
return DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Res);
|
|
}
|
|
|
|
// Lower ISD::GlobalTLSAddress using the "general dynamic" model
|
|
SDValue
|
|
ARMTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
|
|
SelectionDAG &DAG) {
|
|
DebugLoc dl = GA->getDebugLoc();
|
|
MVT PtrVT = getPointerTy();
|
|
unsigned char PCAdj = Subtarget->isThumb() ? 4 : 8;
|
|
ARMConstantPoolValue *CPV =
|
|
new ARMConstantPoolValue(GA->getGlobal(), ARMPCLabelIndex, ARMCP::CPValue,
|
|
PCAdj, "tlsgd", true);
|
|
SDValue Argument = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
Argument = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Argument);
|
|
Argument = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Argument, NULL, 0);
|
|
SDValue Chain = Argument.getValue(1);
|
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
Argument = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Argument, PICLabel);
|
|
|
|
// call __tls_get_addr.
|
|
ArgListTy Args;
|
|
ArgListEntry Entry;
|
|
Entry.Node = Argument;
|
|
Entry.Ty = (const Type *) Type::Int32Ty;
|
|
Args.push_back(Entry);
|
|
// FIXME: is there useful debug info available here?
|
|
std::pair<SDValue, SDValue> CallResult =
|
|
LowerCallTo(Chain, (const Type *) Type::Int32Ty, false, false, false, false,
|
|
0, CallingConv::C, false, /*isReturnValueUsed=*/true,
|
|
DAG.getExternalSymbol("__tls_get_addr", PtrVT), Args, DAG, dl);
|
|
return CallResult.first;
|
|
}
|
|
|
|
// Lower ISD::GlobalTLSAddress using the "initial exec" or
|
|
// "local exec" model.
|
|
SDValue
|
|
ARMTargetLowering::LowerToTLSExecModels(GlobalAddressSDNode *GA,
|
|
SelectionDAG &DAG) {
|
|
GlobalValue *GV = GA->getGlobal();
|
|
DebugLoc dl = GA->getDebugLoc();
|
|
SDValue Offset;
|
|
SDValue Chain = DAG.getEntryNode();
|
|
MVT PtrVT = getPointerTy();
|
|
// Get the Thread Pointer
|
|
SDValue ThreadPointer = DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT);
|
|
|
|
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);
|
|
Offset = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
Offset = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Offset);
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
|
Chain = Offset.getValue(1);
|
|
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
Offset = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Offset, PICLabel);
|
|
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
|
} else {
|
|
// local exec model
|
|
ARMConstantPoolValue *CPV =
|
|
new ARMConstantPoolValue(GV, ARMCP::CPValue, "tpoff");
|
|
Offset = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
Offset = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Offset);
|
|
Offset = DAG.getLoad(PtrVT, dl, Chain, Offset, NULL, 0);
|
|
}
|
|
|
|
// The address of the thread local variable is the add of the thread
|
|
// pointer with the offset of the variable.
|
|
return DAG.getNode(ISD::ADD, dl, PtrVT, ThreadPointer, Offset);
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) {
|
|
// 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);
|
|
}
|
|
|
|
SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op,
|
|
SelectionDAG &DAG) {
|
|
MVT PtrVT = getPointerTy();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal();
|
|
Reloc::Model RelocM = getTargetMachine().getRelocationModel();
|
|
if (RelocM == Reloc::PIC_) {
|
|
bool UseGOTOFF = GV->hasLocalLinkage() || GV->hasHiddenVisibility();
|
|
ARMConstantPoolValue *CPV =
|
|
new ARMConstantPoolValue(GV, ARMCP::CPValue, UseGOTOFF ? "GOTOFF":"GOT");
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(),
|
|
CPAddr, NULL, 0);
|
|
SDValue Chain = Result.getValue(1);
|
|
SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(PtrVT);
|
|
Result = DAG.getNode(ISD::ADD, dl, PtrVT, Result, GOT);
|
|
if (!UseGOTOFF)
|
|
Result = DAG.getLoad(PtrVT, dl, Chain, Result, NULL, 0);
|
|
return Result;
|
|
} else {
|
|
SDValue CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 4);
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
return DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
|
}
|
|
}
|
|
|
|
/// GVIsIndirectSymbol - true if the GV will be accessed via an indirect symbol
|
|
/// even in non-static mode.
|
|
static bool GVIsIndirectSymbol(GlobalValue *GV, Reloc::Model RelocM) {
|
|
// 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->hasAvailableExternallyLinkage();
|
|
if (GV->hasHiddenVisibility() && (!isDecl && !GV->hasCommonLinkage()))
|
|
return false;
|
|
return RelocM != Reloc::Static && (isDecl || GV->isWeakForLinker());
|
|
}
|
|
|
|
SDValue ARMTargetLowering::LowerGlobalAddressDarwin(SDValue Op,
|
|
SelectionDAG &DAG) {
|
|
MVT PtrVT = getPointerTy();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal();
|
|
Reloc::Model RelocM = getTargetMachine().getRelocationModel();
|
|
bool IsIndirect = GVIsIndirectSymbol(GV, RelocM);
|
|
SDValue CPAddr;
|
|
if (RelocM == Reloc::Static)
|
|
CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 4);
|
|
else {
|
|
unsigned PCAdj = (RelocM != Reloc::PIC_)
|
|
? 0 : (Subtarget->isThumb() ? 4 : 8);
|
|
ARMCP::ARMCPKind Kind = IsIndirect ? ARMCP::CPNonLazyPtr
|
|
: ARMCP::CPValue;
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex,
|
|
Kind, PCAdj);
|
|
CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
}
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
|
SDValue Chain = Result.getValue(1);
|
|
|
|
if (RelocM == Reloc::PIC_) {
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
Result = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Result, PICLabel);
|
|
}
|
|
if (IsIndirect)
|
|
Result = DAG.getLoad(PtrVT, dl, Chain, Result, NULL, 0);
|
|
|
|
return Result;
|
|
}
|
|
|
|
SDValue ARMTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDValue Op,
|
|
SelectionDAG &DAG){
|
|
assert(Subtarget->isTargetELF() &&
|
|
"GLOBAL OFFSET TABLE not implemented for non-ELF targets");
|
|
MVT PtrVT = getPointerTy();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
unsigned PCAdj = Subtarget->isThumb() ? 4 : 8;
|
|
ARMConstantPoolValue *CPV = new ARMConstantPoolValue("_GLOBAL_OFFSET_TABLE_",
|
|
ARMPCLabelIndex,
|
|
ARMCP::CPValue, PCAdj);
|
|
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 4);
|
|
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
|
|
SDValue Result = DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), CPAddr, NULL, 0);
|
|
SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32);
|
|
return DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Result, PICLabel);
|
|
}
|
|
|
|
static SDValue LowerNeonVLDIntrinsic(SDValue Op, SelectionDAG &DAG,
|
|
unsigned Opcode) {
|
|
SDNode *Node = Op.getNode();
|
|
MVT VT = Node->getValueType(0);
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
if (!VT.is64BitVector())
|
|
return SDValue(); // unimplemented
|
|
|
|
SDValue Ops[] = { Node->getOperand(0),
|
|
Node->getOperand(2) };
|
|
return DAG.getNode(Opcode, dl, Node->getVTList(), Ops, 2);
|
|
}
|
|
|
|
static SDValue LowerNeonVSTIntrinsic(SDValue Op, SelectionDAG &DAG,
|
|
unsigned Opcode, unsigned NumVecs) {
|
|
SDNode *Node = Op.getNode();
|
|
MVT VT = Node->getOperand(3).getValueType();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
if (!VT.is64BitVector())
|
|
return SDValue(); // unimplemented
|
|
|
|
SmallVector<SDValue, 6> Ops;
|
|
Ops.push_back(Node->getOperand(0));
|
|
Ops.push_back(Node->getOperand(2));
|
|
for (unsigned N = 0; N < NumVecs; ++N)
|
|
Ops.push_back(Node->getOperand(N + 3));
|
|
return DAG.getNode(Opcode, dl, MVT::Other, Ops.data(), Ops.size());
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerINTRINSIC_W_CHAIN(SDValue Op, SelectionDAG &DAG) {
|
|
unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
|
|
switch (IntNo) {
|
|
case Intrinsic::arm_neon_vld2i:
|
|
case Intrinsic::arm_neon_vld2f:
|
|
return LowerNeonVLDIntrinsic(Op, DAG, ARMISD::VLD2D);
|
|
case Intrinsic::arm_neon_vld3i:
|
|
case Intrinsic::arm_neon_vld3f:
|
|
return LowerNeonVLDIntrinsic(Op, DAG, ARMISD::VLD3D);
|
|
case Intrinsic::arm_neon_vld4i:
|
|
case Intrinsic::arm_neon_vld4f:
|
|
return LowerNeonVLDIntrinsic(Op, DAG, ARMISD::VLD4D);
|
|
case Intrinsic::arm_neon_vst2i:
|
|
case Intrinsic::arm_neon_vst2f:
|
|
return LowerNeonVSTIntrinsic(Op, DAG, ARMISD::VST2D, 2);
|
|
case Intrinsic::arm_neon_vst3i:
|
|
case Intrinsic::arm_neon_vst3f:
|
|
return LowerNeonVSTIntrinsic(Op, DAG, ARMISD::VST3D, 3);
|
|
case Intrinsic::arm_neon_vst4i:
|
|
case Intrinsic::arm_neon_vst4f:
|
|
return LowerNeonVSTIntrinsic(Op, DAG, ARMISD::VST4D, 4);
|
|
default: return SDValue(); // Don't custom lower most intrinsics.
|
|
}
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) {
|
|
unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
switch (IntNo) {
|
|
default: return SDValue(); // Don't custom lower most intrinsics.
|
|
case Intrinsic::arm_thread_pointer: {
|
|
MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
|
return DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT);
|
|
}
|
|
case Intrinsic::eh_sjlj_setjmp:
|
|
return DAG.getNode(ARMISD::EH_SJLJ_SETJMP, dl, MVT::i32, Op.getOperand(1));
|
|
}
|
|
}
|
|
|
|
static SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG,
|
|
unsigned VarArgsFrameIndex) {
|
|
// vastart just stores the address of the VarArgsFrameIndex slot into the
|
|
// memory location argument.
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
|
SDValue FR = DAG.getFrameIndex(VarArgsFrameIndex, PtrVT);
|
|
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
|
|
return DAG.getStore(Op.getOperand(0), dl, FR, Op.getOperand(1), SV, 0);
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) {
|
|
SDNode *Node = Op.getNode();
|
|
DebugLoc dl = Node->getDebugLoc();
|
|
MVT VT = Node->getValueType(0);
|
|
SDValue Chain = Op.getOperand(0);
|
|
SDValue Size = Op.getOperand(1);
|
|
SDValue Align = Op.getOperand(2);
|
|
|
|
// Chain the dynamic stack allocation so that it doesn't modify the stack
|
|
// pointer when other instructions are using the stack.
|
|
Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(0, true));
|
|
|
|
unsigned AlignVal = cast<ConstantSDNode>(Align)->getZExtValue();
|
|
unsigned StackAlign = getTargetMachine().getFrameInfo()->getStackAlignment();
|
|
if (AlignVal > StackAlign)
|
|
// Do this now since selection pass cannot introduce new target
|
|
// independent node.
|
|
Align = DAG.getConstant(-(uint64_t)AlignVal, VT);
|
|
|
|
// In Thumb1 mode, there isn't a "sub r, sp, r" instruction, we will end up
|
|
// using a "add r, sp, r" instead. Negate the size now so we don't have to
|
|
// do even more horrible hack later.
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
|
|
if (AFI->isThumb1OnlyFunction()) {
|
|
bool Negate = true;
|
|
ConstantSDNode *C = dyn_cast<ConstantSDNode>(Size);
|
|
if (C) {
|
|
uint32_t Val = C->getZExtValue();
|
|
if (Val <= 508 && ((Val & 3) == 0))
|
|
Negate = false;
|
|
}
|
|
if (Negate)
|
|
Size = DAG.getNode(ISD::SUB, dl, VT, DAG.getConstant(0, VT), Size);
|
|
}
|
|
|
|
SDVTList VTList = DAG.getVTList(VT, MVT::Other);
|
|
SDValue Ops1[] = { Chain, Size, Align };
|
|
SDValue Res = DAG.getNode(ARMISD::DYN_ALLOC, dl, VTList, Ops1, 3);
|
|
Chain = Res.getValue(1);
|
|
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(0, true),
|
|
DAG.getIntPtrConstant(0, true), SDValue());
|
|
SDValue Ops2[] = { Res, Chain };
|
|
return DAG.getMergeValues(Ops2, 2, dl);
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::GetF64FormalArgument(CCValAssign &VA, CCValAssign &NextVA,
|
|
SDValue &Root, SelectionDAG &DAG,
|
|
DebugLoc dl) {
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
|
|
|
|
TargetRegisterClass *RC;
|
|
if (AFI->isThumb1OnlyFunction())
|
|
RC = ARM::tGPRRegisterClass;
|
|
else
|
|
RC = ARM::GPRRegisterClass;
|
|
|
|
// Transform the arguments stored in physical registers into virtual ones.
|
|
unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC);
|
|
SDValue ArgValue = DAG.getCopyFromReg(Root, dl, Reg, MVT::i32);
|
|
|
|
SDValue ArgValue2;
|
|
if (NextVA.isMemLoc()) {
|
|
unsigned ArgSize = NextVA.getLocVT().getSizeInBits()/8;
|
|
MachineFrameInfo *MFI = MF.getFrameInfo();
|
|
int FI = MFI->CreateFixedObject(ArgSize, NextVA.getLocMemOffset());
|
|
|
|
// Create load node to retrieve arguments from the stack.
|
|
SDValue FIN = DAG.getFrameIndex(FI, getPointerTy());
|
|
ArgValue2 = DAG.getLoad(MVT::i32, dl, Root, FIN, NULL, 0);
|
|
} else {
|
|
Reg = MF.addLiveIn(NextVA.getLocReg(), RC);
|
|
ArgValue2 = DAG.getCopyFromReg(Root, dl, Reg, MVT::i32);
|
|
}
|
|
|
|
return DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, ArgValue, ArgValue2);
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::LowerFormalArguments(SDValue Chain,
|
|
unsigned CallConv, bool isVarArg,
|
|
const SmallVectorImpl<ISD::InputArg>
|
|
&Ins,
|
|
DebugLoc dl, SelectionDAG &DAG,
|
|
SmallVectorImpl<SDValue> &InVals) {
|
|
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
MachineFrameInfo *MFI = MF.getFrameInfo();
|
|
|
|
ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
|
|
|
|
// Assign locations to all of the incoming arguments.
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
CCState CCInfo(CallConv, isVarArg, getTargetMachine(), ArgLocs,
|
|
*DAG.getContext());
|
|
CCInfo.AnalyzeFormalArguments(Ins,
|
|
CCAssignFnForNode(CallConv, /* Return*/ false,
|
|
isVarArg));
|
|
|
|
SmallVector<SDValue, 16> ArgValues;
|
|
|
|
for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
|
|
CCValAssign &VA = ArgLocs[i];
|
|
|
|
// Arguments stored in registers.
|
|
if (VA.isRegLoc()) {
|
|
MVT RegVT = VA.getLocVT();
|
|
|
|
SDValue ArgValue;
|
|
if (VA.needsCustom()) {
|
|
// f64 and vector types are split up into multiple registers or
|
|
// combinations of registers and stack slots.
|
|
RegVT = MVT::i32;
|
|
|
|
if (VA.getLocVT() == MVT::v2f64) {
|
|
SDValue ArgValue1 = GetF64FormalArgument(VA, ArgLocs[++i],
|
|
Chain, DAG, dl);
|
|
VA = ArgLocs[++i]; // skip ahead to next loc
|
|
SDValue ArgValue2 = GetF64FormalArgument(VA, ArgLocs[++i],
|
|
Chain, DAG, dl);
|
|
ArgValue = DAG.getNode(ISD::UNDEF, dl, MVT::v2f64);
|
|
ArgValue = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64,
|
|
ArgValue, ArgValue1, DAG.getIntPtrConstant(0));
|
|
ArgValue = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64,
|
|
ArgValue, ArgValue2, DAG.getIntPtrConstant(1));
|
|
} else
|
|
ArgValue = GetF64FormalArgument(VA, ArgLocs[++i], Chain, DAG, dl);
|
|
|
|
} else {
|
|
TargetRegisterClass *RC;
|
|
|
|
if (RegVT == MVT::f32)
|
|
RC = ARM::SPRRegisterClass;
|
|
else if (RegVT == MVT::f64)
|
|
RC = ARM::DPRRegisterClass;
|
|
else if (RegVT == MVT::v2f64)
|
|
RC = ARM::QPRRegisterClass;
|
|
else if (RegVT == MVT::i32)
|
|
RC = (AFI->isThumb1OnlyFunction() ?
|
|
ARM::tGPRRegisterClass : ARM::GPRRegisterClass);
|
|
else
|
|
llvm_unreachable("RegVT not supported by FORMAL_ARGUMENTS Lowering");
|
|
|
|
// Transform the arguments in physical registers into virtual ones.
|
|
unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC);
|
|
ArgValue = DAG.getCopyFromReg(Chain, dl, Reg, RegVT);
|
|
}
|
|
|
|
// 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: llvm_unreachable("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;
|
|
}
|
|
|
|
InVals.push_back(ArgValue);
|
|
|
|
} else { // VA.isRegLoc()
|
|
|
|
// sanity check
|
|
assert(VA.isMemLoc());
|
|
assert(VA.getValVT() != MVT::i64 && "i64 should already be lowered");
|
|
|
|
unsigned ArgSize = VA.getLocVT().getSizeInBits()/8;
|
|
int FI = MFI->CreateFixedObject(ArgSize, VA.getLocMemOffset());
|
|
|
|
// Create load nodes to retrieve arguments from the stack.
|
|
SDValue FIN = DAG.getFrameIndex(FI, getPointerTy());
|
|
InVals.push_back(DAG.getLoad(VA.getValVT(), dl, Chain, FIN, NULL, 0));
|
|
}
|
|
}
|
|
|
|
// varargs
|
|
if (isVarArg) {
|
|
static const unsigned GPRArgRegs[] = {
|
|
ARM::R0, ARM::R1, ARM::R2, ARM::R3
|
|
};
|
|
|
|
unsigned NumGPRs = CCInfo.getFirstUnallocated
|
|
(GPRArgRegs, sizeof(GPRArgRegs) / sizeof(GPRArgRegs[0]));
|
|
|
|
unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
|
|
unsigned VARegSize = (4 - NumGPRs) * 4;
|
|
unsigned VARegSaveSize = (VARegSize + Align - 1) & ~(Align - 1);
|
|
unsigned ArgOffset = 0;
|
|
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);
|
|
ArgOffset = CCInfo.getNextStackOffset();
|
|
VarArgsFrameIndex = MFI->CreateFixedObject(VARegSaveSize, ArgOffset +
|
|
VARegSaveSize - VARegSize);
|
|
SDValue FIN = DAG.getFrameIndex(VarArgsFrameIndex, getPointerTy());
|
|
|
|
SmallVector<SDValue, 4> MemOps;
|
|
for (; NumGPRs < 4; ++NumGPRs) {
|
|
TargetRegisterClass *RC;
|
|
if (AFI->isThumb1OnlyFunction())
|
|
RC = ARM::tGPRRegisterClass;
|
|
else
|
|
RC = ARM::GPRRegisterClass;
|
|
|
|
unsigned VReg = MF.addLiveIn(GPRArgRegs[NumGPRs], RC);
|
|
SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::i32);
|
|
SDValue Store = DAG.getStore(Val.getValue(1), dl, Val, FIN, NULL, 0);
|
|
MemOps.push_back(Store);
|
|
FIN = DAG.getNode(ISD::ADD, dl, getPointerTy(), FIN,
|
|
DAG.getConstant(4, getPointerTy()));
|
|
}
|
|
if (!MemOps.empty())
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
|
&MemOps[0], MemOps.size());
|
|
} else
|
|
// This will point to the next argument passed via stack.
|
|
VarArgsFrameIndex = MFI->CreateFixedObject(4, ArgOffset);
|
|
}
|
|
|
|
return Chain;
|
|
}
|
|
|
|
/// isFloatingPointZero - Return true if this is +0.0.
|
|
static bool isFloatingPointZero(SDValue Op) {
|
|
if (ConstantFPSDNode *CFP = dyn_cast<ConstantFPSDNode>(Op))
|
|
return CFP->getValueAPF().isPosZero();
|
|
else if (ISD::isEXTLoad(Op.getNode()) || ISD::isNON_EXTLoad(Op.getNode())) {
|
|
// Maybe this has already been legalized into the constant pool?
|
|
if (Op.getOperand(1).getOpcode() == ARMISD::Wrapper) {
|
|
SDValue WrapperOp = Op.getOperand(1).getOperand(0);
|
|
if (ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(WrapperOp))
|
|
if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal()))
|
|
return CFP->getValueAPF().isPosZero();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isLegalCmpImmediate(unsigned C, bool isThumb1Only) {
|
|
return ( isThumb1Only && (C & ~255U) == 0) ||
|
|
(!isThumb1Only && ARM_AM::getSOImmVal(C) != -1);
|
|
}
|
|
|
|
/// Returns appropriate ARM CMP (cmp) and corresponding condition code for
|
|
/// the given operands.
|
|
static SDValue getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
|
|
SDValue &ARMCC, SelectionDAG &DAG, bool isThumb1Only,
|
|
DebugLoc dl) {
|
|
if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS.getNode())) {
|
|
unsigned C = RHSC->getZExtValue();
|
|
if (!isLegalCmpImmediate(C, isThumb1Only)) {
|
|
// Constant does not fit, try adjusting it by one?
|
|
switch (CC) {
|
|
default: break;
|
|
case ISD::SETLT:
|
|
case ISD::SETGE:
|
|
if (isLegalCmpImmediate(C-1, isThumb1Only)) {
|
|
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, isThumb1Only)) {
|
|
CC = (CC == ISD::SETULT) ? ISD::SETULE : ISD::SETUGT;
|
|
RHS = DAG.getConstant(C-1, MVT::i32);
|
|
}
|
|
break;
|
|
case ISD::SETLE:
|
|
case ISD::SETGT:
|
|
if (isLegalCmpImmediate(C+1, isThumb1Only)) {
|
|
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, isThumb1Only)) {
|
|
CC = (CC == ISD::SETULE) ? ISD::SETULT : ISD::SETUGE;
|
|
RHS = DAG.getConstant(C+1, MVT::i32);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ARMCC::CondCodes CondCode = IntCCToARMCC(CC);
|
|
ARMISD::NodeType CompareType;
|
|
switch (CondCode) {
|
|
default:
|
|
CompareType = ARMISD::CMP;
|
|
break;
|
|
case ARMCC::EQ:
|
|
case ARMCC::NE:
|
|
// Uses only Z Flag
|
|
CompareType = ARMISD::CMPZ;
|
|
break;
|
|
}
|
|
ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
|
return DAG.getNode(CompareType, dl, MVT::Flag, LHS, RHS);
|
|
}
|
|
|
|
/// Returns a appropriate VFP CMP (fcmp{s|d}+fmstat) for the given operands.
|
|
static SDValue getVFPCmp(SDValue LHS, SDValue RHS, SelectionDAG &DAG,
|
|
DebugLoc dl) {
|
|
SDValue Cmp;
|
|
if (!isFloatingPointZero(RHS))
|
|
Cmp = DAG.getNode(ARMISD::CMPFP, dl, MVT::Flag, LHS, RHS);
|
|
else
|
|
Cmp = DAG.getNode(ARMISD::CMPFPw0, dl, MVT::Flag, LHS);
|
|
return DAG.getNode(ARMISD::FMSTAT, dl, MVT::Flag, Cmp);
|
|
}
|
|
|
|
static SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG,
|
|
const ARMSubtarget *ST) {
|
|
MVT VT = Op.getValueType();
|
|
SDValue LHS = Op.getOperand(0);
|
|
SDValue RHS = Op.getOperand(1);
|
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get();
|
|
SDValue TrueVal = Op.getOperand(2);
|
|
SDValue FalseVal = Op.getOperand(3);
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
if (LHS.getValueType() == MVT::i32) {
|
|
SDValue ARMCC;
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
|
SDValue Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb1Only(), dl);
|
|
return DAG.getNode(ARMISD::CMOV, dl, VT, FalseVal, TrueVal, ARMCC, CCR,Cmp);
|
|
}
|
|
|
|
ARMCC::CondCodes CondCode, CondCode2;
|
|
if (FPCCToARMCC(CC, CondCode, CondCode2))
|
|
std::swap(TrueVal, FalseVal);
|
|
|
|
SDValue ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
|
SDValue Cmp = getVFPCmp(LHS, RHS, DAG, dl);
|
|
SDValue Result = DAG.getNode(ARMISD::CMOV, dl, VT, FalseVal, TrueVal,
|
|
ARMCC, CCR, Cmp);
|
|
if (CondCode2 != ARMCC::AL) {
|
|
SDValue ARMCC2 = DAG.getConstant(CondCode2, MVT::i32);
|
|
// FIXME: Needs another CMP because flag can have but one use.
|
|
SDValue Cmp2 = getVFPCmp(LHS, RHS, DAG, dl);
|
|
Result = DAG.getNode(ARMISD::CMOV, dl, VT,
|
|
Result, TrueVal, ARMCC2, CCR, Cmp2);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
static SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG,
|
|
const ARMSubtarget *ST) {
|
|
SDValue Chain = Op.getOperand(0);
|
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get();
|
|
SDValue LHS = Op.getOperand(2);
|
|
SDValue RHS = Op.getOperand(3);
|
|
SDValue Dest = Op.getOperand(4);
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
if (LHS.getValueType() == MVT::i32) {
|
|
SDValue ARMCC;
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
|
SDValue Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb1Only(), dl);
|
|
return DAG.getNode(ARMISD::BRCOND, dl, MVT::Other,
|
|
Chain, Dest, ARMCC, CCR,Cmp);
|
|
}
|
|
|
|
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);
|
|
|
|
SDValue Cmp = getVFPCmp(LHS, RHS, DAG, dl);
|
|
SDValue ARMCC = DAG.getConstant(CondCode, MVT::i32);
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
|
SDVTList VTList = DAG.getVTList(MVT::Other, MVT::Flag);
|
|
SDValue Ops[] = { Chain, Dest, ARMCC, CCR, Cmp };
|
|
SDValue Res = DAG.getNode(ARMISD::BRCOND, dl, VTList, Ops, 5);
|
|
if (CondCode2 != ARMCC::AL) {
|
|
ARMCC = DAG.getConstant(CondCode2, MVT::i32);
|
|
SDValue Ops[] = { Res, Dest, ARMCC, CCR, Res.getValue(1) };
|
|
Res = DAG.getNode(ARMISD::BRCOND, dl, VTList, Ops, 5);
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
SDValue ARMTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) {
|
|
SDValue Chain = Op.getOperand(0);
|
|
SDValue Table = Op.getOperand(1);
|
|
SDValue Index = Op.getOperand(2);
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
MVT PTy = getPointerTy();
|
|
JumpTableSDNode *JT = cast<JumpTableSDNode>(Table);
|
|
ARMFunctionInfo *AFI = DAG.getMachineFunction().getInfo<ARMFunctionInfo>();
|
|
SDValue UId = DAG.getConstant(AFI->createJumpTableUId(), PTy);
|
|
SDValue JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy);
|
|
Table = DAG.getNode(ARMISD::WrapperJT, dl, MVT::i32, JTI, UId);
|
|
Index = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, PTy));
|
|
SDValue Addr = DAG.getNode(ISD::ADD, dl, PTy, Index, Table);
|
|
if (Subtarget->isThumb2()) {
|
|
// Thumb2 uses a two-level jump. That is, it jumps into the jump table
|
|
// which does another jump to the destination. This also makes it easier
|
|
// to translate it to TBB / TBH later.
|
|
// FIXME: This might not work if the function is extremely large.
|
|
return DAG.getNode(ARMISD::BR2_JT, dl, MVT::Other, Chain,
|
|
Addr, Op.getOperand(2), JTI, UId);
|
|
}
|
|
if (getTargetMachine().getRelocationModel() == Reloc::PIC_) {
|
|
Addr = DAG.getLoad((MVT)MVT::i32, dl, Chain, Addr, NULL, 0);
|
|
Chain = Addr.getValue(1);
|
|
Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr, Table);
|
|
return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, Addr, JTI, UId);
|
|
} else {
|
|
Addr = DAG.getLoad(PTy, dl, Chain, Addr, NULL, 0);
|
|
Chain = Addr.getValue(1);
|
|
return DAG.getNode(ARMISD::BR_JT, dl, MVT::Other, Chain, Addr, JTI, UId);
|
|
}
|
|
}
|
|
|
|
static SDValue LowerFP_TO_INT(SDValue Op, SelectionDAG &DAG) {
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
unsigned Opc =
|
|
Op.getOpcode() == ISD::FP_TO_SINT ? ARMISD::FTOSI : ARMISD::FTOUI;
|
|
Op = DAG.getNode(Opc, dl, MVT::f32, Op.getOperand(0));
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, MVT::i32, Op);
|
|
}
|
|
|
|
static SDValue LowerINT_TO_FP(SDValue Op, SelectionDAG &DAG) {
|
|
MVT VT = Op.getValueType();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
unsigned Opc =
|
|
Op.getOpcode() == ISD::SINT_TO_FP ? ARMISD::SITOF : ARMISD::UITOF;
|
|
|
|
Op = DAG.getNode(ISD::BIT_CONVERT, dl, MVT::f32, Op.getOperand(0));
|
|
return DAG.getNode(Opc, dl, VT, Op);
|
|
}
|
|
|
|
static SDValue LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) {
|
|
// Implement fcopysign with a fabs and a conditional fneg.
|
|
SDValue Tmp0 = Op.getOperand(0);
|
|
SDValue Tmp1 = Op.getOperand(1);
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
MVT VT = Op.getValueType();
|
|
MVT SrcVT = Tmp1.getValueType();
|
|
SDValue AbsVal = DAG.getNode(ISD::FABS, dl, VT, Tmp0);
|
|
SDValue Cmp = getVFPCmp(Tmp1, DAG.getConstantFP(0.0, SrcVT), DAG, dl);
|
|
SDValue ARMCC = DAG.getConstant(ARMCC::LT, MVT::i32);
|
|
SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32);
|
|
return DAG.getNode(ARMISD::CNEG, dl, VT, AbsVal, AbsVal, ARMCC, CCR, Cmp);
|
|
}
|
|
|
|
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();
|
|
unsigned FrameReg = (Subtarget->isThumb() || Subtarget->isTargetDarwin())
|
|
? 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;
|
|
}
|
|
|
|
SDValue
|
|
ARMTargetLowering::EmitTargetCodeForMemcpy(SelectionDAG &DAG, DebugLoc dl,
|
|
SDValue Chain,
|
|
SDValue Dst, SDValue Src,
|
|
SDValue Size, unsigned Align,
|
|
bool AlwaysInline,
|
|
const Value *DstSV, uint64_t DstSVOff,
|
|
const Value *SrcSV, uint64_t SrcSVOff){
|
|
// Do repeated 4-byte loads and stores. To be improved.
|
|
// This requires 4-byte alignment.
|
|
if ((Align & 3) != 0)
|
|
return SDValue();
|
|
// This requires the copy size to be a constant, preferrably
|
|
// within a subtarget-specific limit.
|
|
ConstantSDNode *ConstantSize = dyn_cast<ConstantSDNode>(Size);
|
|
if (!ConstantSize)
|
|
return SDValue();
|
|
uint64_t SizeVal = ConstantSize->getZExtValue();
|
|
if (!AlwaysInline && SizeVal > getSubtarget()->getMaxInlineSizeThreshold())
|
|
return SDValue();
|
|
|
|
unsigned BytesLeft = SizeVal & 3;
|
|
unsigned NumMemOps = SizeVal >> 2;
|
|
unsigned EmittedNumMemOps = 0;
|
|
MVT VT = MVT::i32;
|
|
unsigned VTSize = 4;
|
|
unsigned i = 0;
|
|
const unsigned MAX_LOADS_IN_LDM = 6;
|
|
SDValue TFOps[MAX_LOADS_IN_LDM];
|
|
SDValue Loads[MAX_LOADS_IN_LDM];
|
|
uint64_t SrcOff = 0, DstOff = 0;
|
|
|
|
// 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
|
|
// ldm/stm later on.
|
|
while (EmittedNumMemOps < NumMemOps) {
|
|
for (i = 0;
|
|
i < MAX_LOADS_IN_LDM && EmittedNumMemOps + i < NumMemOps; ++i) {
|
|
Loads[i] = DAG.getLoad(VT, dl, Chain,
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Src,
|
|
DAG.getConstant(SrcOff, MVT::i32)),
|
|
SrcSV, SrcSVOff + SrcOff);
|
|
TFOps[i] = Loads[i].getValue(1);
|
|
SrcOff += VTSize;
|
|
}
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
|
|
|
for (i = 0;
|
|
i < MAX_LOADS_IN_LDM && EmittedNumMemOps + i < NumMemOps; ++i) {
|
|
TFOps[i] = DAG.getStore(Chain, dl, Loads[i],
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Dst,
|
|
DAG.getConstant(DstOff, MVT::i32)),
|
|
DstSV, DstSVOff + DstOff);
|
|
DstOff += VTSize;
|
|
}
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
|
|
|
EmittedNumMemOps += i;
|
|
}
|
|
|
|
if (BytesLeft == 0)
|
|
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;
|
|
}
|
|
|
|
Loads[i] = DAG.getLoad(VT, dl, Chain,
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Src,
|
|
DAG.getConstant(SrcOff, MVT::i32)),
|
|
SrcSV, SrcSVOff + SrcOff);
|
|
TFOps[i] = Loads[i].getValue(1);
|
|
++i;
|
|
SrcOff += VTSize;
|
|
BytesLeft -= VTSize;
|
|
}
|
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
|
|
|
i = 0;
|
|
BytesLeft = BytesLeftSave;
|
|
while (BytesLeft) {
|
|
if (BytesLeft >= 2) {
|
|
VT = MVT::i16;
|
|
VTSize = 2;
|
|
} else {
|
|
VT = MVT::i8;
|
|
VTSize = 1;
|
|
}
|
|
|
|
TFOps[i] = DAG.getStore(Chain, dl, Loads[i],
|
|
DAG.getNode(ISD::ADD, dl, MVT::i32, Dst,
|
|
DAG.getConstant(DstOff, MVT::i32)),
|
|
DstSV, DstSVOff + DstOff);
|
|
++i;
|
|
DstOff += VTSize;
|
|
BytesLeft -= VTSize;
|
|
}
|
|
return DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
|
|
}
|
|
|
|
static SDValue ExpandBIT_CONVERT(SDNode *N, SelectionDAG &DAG) {
|
|
SDValue Op = N->getOperand(0);
|
|
DebugLoc dl = N->getDebugLoc();
|
|
if (N->getValueType(0) == MVT::f64) {
|
|
// Turn i64->f64 into FMDRR.
|
|
SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, Op,
|
|
DAG.getConstant(0, MVT::i32));
|
|
SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, Op,
|
|
DAG.getConstant(1, MVT::i32));
|
|
return DAG.getNode(ARMISD::FMDRR, dl, MVT::f64, Lo, Hi);
|
|
}
|
|
|
|
// Turn f64->i64 into FMRRD.
|
|
SDValue Cvt = DAG.getNode(ARMISD::FMRRD, dl,
|
|
DAG.getVTList(MVT::i32, MVT::i32), &Op, 1);
|
|
|
|
// Merge the pieces into a single i64 value.
|
|
return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Cvt, Cvt.getValue(1));
|
|
}
|
|
|
|
/// getZeroVector - Returns a vector of specified type with all zero elements.
|
|
///
|
|
static SDValue getZeroVector(MVT VT, SelectionDAG &DAG, DebugLoc dl) {
|
|
assert(VT.isVector() && "Expected a vector type");
|
|
|
|
// Zero vectors are used to represent vector negation and in those cases
|
|
// will be implemented with the NEON VNEG instruction. However, VNEG does
|
|
// not support i64 elements, so sometimes the zero vectors will need to be
|
|
// explicitly constructed. For those cases, and potentially other uses in
|
|
// the future, always build zero vectors as <4 x i32> or <2 x i32> bitcasted
|
|
// to their dest type. This ensures they get CSE'd.
|
|
SDValue Vec;
|
|
SDValue Cst = DAG.getTargetConstant(0, MVT::i32);
|
|
if (VT.getSizeInBits() == 64)
|
|
Vec = DAG.getNode(ISD::BUILD_VECTOR, dl, MVT::v2i32, Cst, Cst);
|
|
else
|
|
Vec = DAG.getNode(ISD::BUILD_VECTOR, dl, MVT::v4i32, Cst, Cst, Cst, Cst);
|
|
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, VT, Vec);
|
|
}
|
|
|
|
/// getOnesVector - Returns a vector of specified type with all bits set.
|
|
///
|
|
static SDValue getOnesVector(MVT VT, SelectionDAG &DAG, DebugLoc dl) {
|
|
assert(VT.isVector() && "Expected a vector type");
|
|
|
|
// Always build ones vectors as <4 x i32> or <2 x i32> bitcasted to their dest
|
|
// type. This ensures they get CSE'd.
|
|
SDValue Vec;
|
|
SDValue Cst = DAG.getTargetConstant(~0U, MVT::i32);
|
|
if (VT.getSizeInBits() == 64)
|
|
Vec = DAG.getNode(ISD::BUILD_VECTOR, dl, MVT::v2i32, Cst, Cst);
|
|
else
|
|
Vec = DAG.getNode(ISD::BUILD_VECTOR, dl, MVT::v4i32, Cst, Cst, Cst, Cst);
|
|
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, VT, Vec);
|
|
}
|
|
|
|
static SDValue LowerShift(SDNode *N, SelectionDAG &DAG,
|
|
const ARMSubtarget *ST) {
|
|
MVT VT = N->getValueType(0);
|
|
DebugLoc dl = N->getDebugLoc();
|
|
|
|
// Lower vector shifts on NEON to use VSHL.
|
|
if (VT.isVector()) {
|
|
assert(ST->hasNEON() && "unexpected vector shift");
|
|
|
|
// Left shifts translate directly to the vshiftu intrinsic.
|
|
if (N->getOpcode() == ISD::SHL)
|
|
return DAG.getNode(ISD::INTRINSIC_WO_CHAIN, dl, VT,
|
|
DAG.getConstant(Intrinsic::arm_neon_vshiftu, MVT::i32),
|
|
N->getOperand(0), N->getOperand(1));
|
|
|
|
assert((N->getOpcode() == ISD::SRA ||
|
|
N->getOpcode() == ISD::SRL) && "unexpected vector shift opcode");
|
|
|
|
// NEON uses the same intrinsics for both left and right shifts. For
|
|
// right shifts, the shift amounts are negative, so negate the vector of
|
|
// shift amounts.
|
|
MVT ShiftVT = N->getOperand(1).getValueType();
|
|
SDValue NegatedCount = DAG.getNode(ISD::SUB, dl, ShiftVT,
|
|
getZeroVector(ShiftVT, DAG, dl),
|
|
N->getOperand(1));
|
|
Intrinsic::ID vshiftInt = (N->getOpcode() == ISD::SRA ?
|
|
Intrinsic::arm_neon_vshifts :
|
|
Intrinsic::arm_neon_vshiftu);
|
|
return DAG.getNode(ISD::INTRINSIC_WO_CHAIN, dl, VT,
|
|
DAG.getConstant(vshiftInt, MVT::i32),
|
|
N->getOperand(0), NegatedCount);
|
|
}
|
|
|
|
assert(VT == MVT::i64 &&
|
|
(N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) &&
|
|
"Unknown shift to lower!");
|
|
|
|
// We only lower SRA, SRL of 1 here, all others use generic lowering.
|
|
if (!isa<ConstantSDNode>(N->getOperand(1)) ||
|
|
cast<ConstantSDNode>(N->getOperand(1))->getZExtValue() != 1)
|
|
return SDValue();
|
|
|
|
// If we are in thumb mode, we don't have RRX.
|
|
if (ST->isThumb1Only()) return SDValue();
|
|
|
|
// Okay, we have a 64-bit SRA or SRL of 1. Lower this to an RRX expr.
|
|
SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0),
|
|
DAG.getConstant(0, MVT::i32));
|
|
SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0),
|
|
DAG.getConstant(1, MVT::i32));
|
|
|
|
// 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;
|
|
Hi = DAG.getNode(Opc, dl, DAG.getVTList(MVT::i32, MVT::Flag), &Hi, 1);
|
|
|
|
// The low part is an ARMISD::RRX operand, which shifts the carry in.
|
|
Lo = DAG.getNode(ARMISD::RRX, dl, MVT::i32, Lo, Hi.getValue(1));
|
|
|
|
// Merge the pieces into a single i64 value.
|
|
return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Lo, Hi);
|
|
}
|
|
|
|
static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) {
|
|
SDValue TmpOp0, TmpOp1;
|
|
bool Invert = false;
|
|
bool Swap = false;
|
|
unsigned Opc = 0;
|
|
|
|
SDValue Op0 = Op.getOperand(0);
|
|
SDValue Op1 = Op.getOperand(1);
|
|
SDValue CC = Op.getOperand(2);
|
|
MVT VT = Op.getValueType();
|
|
ISD::CondCode SetCCOpcode = cast<CondCodeSDNode>(CC)->get();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
|
|
if (Op.getOperand(1).getValueType().isFloatingPoint()) {
|
|
switch (SetCCOpcode) {
|
|
default: llvm_unreachable("Illegal FP comparison"); break;
|
|
case ISD::SETUNE:
|
|
case ISD::SETNE: Invert = true; // Fallthrough
|
|
case ISD::SETOEQ:
|
|
case ISD::SETEQ: Opc = ARMISD::VCEQ; break;
|
|
case ISD::SETOLT:
|
|
case ISD::SETLT: Swap = true; // Fallthrough
|
|
case ISD::SETOGT:
|
|
case ISD::SETGT: Opc = ARMISD::VCGT; break;
|
|
case ISD::SETOLE:
|
|
case ISD::SETLE: Swap = true; // Fallthrough
|
|
case ISD::SETOGE:
|
|
case ISD::SETGE: Opc = ARMISD::VCGE; break;
|
|
case ISD::SETUGE: Swap = true; // Fallthrough
|
|
case ISD::SETULE: Invert = true; Opc = ARMISD::VCGT; break;
|
|
case ISD::SETUGT: Swap = true; // Fallthrough
|
|
case ISD::SETULT: Invert = true; Opc = ARMISD::VCGE; break;
|
|
case ISD::SETUEQ: Invert = true; // Fallthrough
|
|
case ISD::SETONE:
|
|
// Expand this to (OLT | OGT).
|
|
TmpOp0 = Op0;
|
|
TmpOp1 = Op1;
|
|
Opc = ISD::OR;
|
|
Op0 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp1, TmpOp0);
|
|
Op1 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp0, TmpOp1);
|
|
break;
|
|
case ISD::SETUO: Invert = true; // Fallthrough
|
|
case ISD::SETO:
|
|
// Expand this to (OLT | OGE).
|
|
TmpOp0 = Op0;
|
|
TmpOp1 = Op1;
|
|
Opc = ISD::OR;
|
|
Op0 = DAG.getNode(ARMISD::VCGT, dl, VT, TmpOp1, TmpOp0);
|
|
Op1 = DAG.getNode(ARMISD::VCGE, dl, VT, TmpOp0, TmpOp1);
|
|
break;
|
|
}
|
|
} else {
|
|
// Integer comparisons.
|
|
switch (SetCCOpcode) {
|
|
default: llvm_unreachable("Illegal integer comparison"); break;
|
|
case ISD::SETNE: Invert = true;
|
|
case ISD::SETEQ: Opc = ARMISD::VCEQ; break;
|
|
case ISD::SETLT: Swap = true;
|
|
case ISD::SETGT: Opc = ARMISD::VCGT; break;
|
|
case ISD::SETLE: Swap = true;
|
|
case ISD::SETGE: Opc = ARMISD::VCGE; break;
|
|
case ISD::SETULT: Swap = true;
|
|
case ISD::SETUGT: Opc = ARMISD::VCGTU; break;
|
|
case ISD::SETULE: Swap = true;
|
|
case ISD::SETUGE: Opc = ARMISD::VCGEU; break;
|
|
}
|
|
|
|
// Detect VTST (Vector Test Bits) = icmp ne (and (op0, op1), zero).
|
|
if (Opc == ARMISD::VCEQ) {
|
|
|
|
SDValue AndOp;
|
|
if (ISD::isBuildVectorAllZeros(Op1.getNode()))
|
|
AndOp = Op0;
|
|
else if (ISD::isBuildVectorAllZeros(Op0.getNode()))
|
|
AndOp = Op1;
|
|
|
|
// Ignore bitconvert.
|
|
if (AndOp.getNode() && AndOp.getOpcode() == ISD::BIT_CONVERT)
|
|
AndOp = AndOp.getOperand(0);
|
|
|
|
if (AndOp.getNode() && AndOp.getOpcode() == ISD::AND) {
|
|
Opc = ARMISD::VTST;
|
|
Op0 = DAG.getNode(ISD::BIT_CONVERT, dl, VT, AndOp.getOperand(0));
|
|
Op1 = DAG.getNode(ISD::BIT_CONVERT, dl, VT, AndOp.getOperand(1));
|
|
Invert = !Invert;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Swap)
|
|
std::swap(Op0, Op1);
|
|
|
|
SDValue Result = DAG.getNode(Opc, dl, VT, Op0, Op1);
|
|
|
|
if (Invert)
|
|
Result = DAG.getNOT(dl, Result, VT);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// isVMOVSplat - Check if the specified splat value corresponds to an immediate
|
|
/// VMOV instruction, and if so, return the constant being splatted.
|
|
static SDValue isVMOVSplat(uint64_t SplatBits, uint64_t SplatUndef,
|
|
unsigned SplatBitSize, SelectionDAG &DAG) {
|
|
switch (SplatBitSize) {
|
|
case 8:
|
|
// Any 1-byte value is OK.
|
|
assert((SplatBits & ~0xff) == 0 && "one byte splat value is too big");
|
|
return DAG.getTargetConstant(SplatBits, MVT::i8);
|
|
|
|
case 16:
|
|
// NEON's 16-bit VMOV supports splat values where only one byte is nonzero.
|
|
if ((SplatBits & ~0xff) == 0 ||
|
|
(SplatBits & ~0xff00) == 0)
|
|
return DAG.getTargetConstant(SplatBits, MVT::i16);
|
|
break;
|
|
|
|
case 32:
|
|
// NEON's 32-bit VMOV supports splat values where:
|
|
// * only one byte is nonzero, or
|
|
// * the least significant byte is 0xff and the second byte is nonzero, or
|
|
// * the least significant 2 bytes are 0xff and the third is nonzero.
|
|
if ((SplatBits & ~0xff) == 0 ||
|
|
(SplatBits & ~0xff00) == 0 ||
|
|
(SplatBits & ~0xff0000) == 0 ||
|
|
(SplatBits & ~0xff000000) == 0)
|
|
return DAG.getTargetConstant(SplatBits, MVT::i32);
|
|
|
|
if ((SplatBits & ~0xffff) == 0 &&
|
|
((SplatBits | SplatUndef) & 0xff) == 0xff)
|
|
return DAG.getTargetConstant(SplatBits | 0xff, MVT::i32);
|
|
|
|
if ((SplatBits & ~0xffffff) == 0 &&
|
|
((SplatBits | SplatUndef) & 0xffff) == 0xffff)
|
|
return DAG.getTargetConstant(SplatBits | 0xffff, MVT::i32);
|
|
|
|
// Note: there are a few 32-bit splat values (specifically: 00ffff00,
|
|
// ff000000, ff0000ff, and ffff00ff) that are valid for VMOV.I64 but not
|
|
// VMOV.I32. A (very) minor optimization would be to replicate the value
|
|
// and fall through here to test for a valid 64-bit splat. But, then the
|
|
// caller would also need to check and handle the change in size.
|
|
break;
|
|
|
|
case 64: {
|
|
// NEON has a 64-bit VMOV splat where each byte is either 0 or 0xff.
|
|
uint64_t BitMask = 0xff;
|
|
uint64_t Val = 0;
|
|
for (int ByteNum = 0; ByteNum < 8; ++ByteNum) {
|
|
if (((SplatBits | SplatUndef) & BitMask) == BitMask)
|
|
Val |= BitMask;
|
|
else if ((SplatBits & BitMask) != 0)
|
|
return SDValue();
|
|
BitMask <<= 8;
|
|
}
|
|
return DAG.getTargetConstant(Val, MVT::i64);
|
|
}
|
|
|
|
default:
|
|
llvm_unreachable("unexpected size for isVMOVSplat");
|
|
break;
|
|
}
|
|
|
|
return SDValue();
|
|
}
|
|
|
|
/// getVMOVImm - If this is a build_vector of constants which can be
|
|
/// formed by using a VMOV instruction of the specified element size,
|
|
/// return the constant being splatted. The ByteSize field indicates the
|
|
/// number of bytes of each element [1248].
|
|
SDValue ARM::getVMOVImm(SDNode *N, unsigned ByteSize, SelectionDAG &DAG) {
|
|
BuildVectorSDNode *BVN = dyn_cast<BuildVectorSDNode>(N);
|
|
APInt SplatBits, SplatUndef;
|
|
unsigned SplatBitSize;
|
|
bool HasAnyUndefs;
|
|
if (! BVN || ! BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize,
|
|
HasAnyUndefs, ByteSize * 8))
|
|
return SDValue();
|
|
|
|
if (SplatBitSize > ByteSize * 8)
|
|
return SDValue();
|
|
|
|
return isVMOVSplat(SplatBits.getZExtValue(), SplatUndef.getZExtValue(),
|
|
SplatBitSize, DAG);
|
|
}
|
|
|
|
/// isVREVMask - Check if a vector shuffle corresponds to a VREV
|
|
/// instruction with the specified blocksize. (The order of the elements
|
|
/// within each block of the vector is reversed.)
|
|
bool ARM::isVREVMask(ShuffleVectorSDNode *N, unsigned BlockSize) {
|
|
assert((BlockSize==16 || BlockSize==32 || BlockSize==64) &&
|
|
"Only possible block sizes for VREV are: 16, 32, 64");
|
|
|
|
MVT VT = N->getValueType(0);
|
|
unsigned NumElts = VT.getVectorNumElements();
|
|
unsigned EltSz = VT.getVectorElementType().getSizeInBits();
|
|
unsigned BlockElts = N->getMaskElt(0) + 1;
|
|
|
|
if (BlockSize <= EltSz || BlockSize != BlockElts * EltSz)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < NumElts; ++i) {
|
|
if ((unsigned) N->getMaskElt(i) !=
|
|
(i - i%BlockElts) + (BlockElts - 1 - i%BlockElts))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static SDValue BuildSplat(SDValue Val, MVT VT, SelectionDAG &DAG, DebugLoc dl) {
|
|
// Canonicalize all-zeros and all-ones vectors.
|
|
ConstantSDNode *ConstVal = dyn_cast<ConstantSDNode>(Val.getNode());
|
|
if (ConstVal->isNullValue())
|
|
return getZeroVector(VT, DAG, dl);
|
|
if (ConstVal->isAllOnesValue())
|
|
return getOnesVector(VT, DAG, dl);
|
|
|
|
MVT CanonicalVT;
|
|
if (VT.is64BitVector()) {
|
|
switch (Val.getValueType().getSizeInBits()) {
|
|
case 8: CanonicalVT = MVT::v8i8; break;
|
|
case 16: CanonicalVT = MVT::v4i16; break;
|
|
case 32: CanonicalVT = MVT::v2i32; break;
|
|
case 64: CanonicalVT = MVT::v1i64; break;
|
|
default: llvm_unreachable("unexpected splat element type"); break;
|
|
}
|
|
} else {
|
|
assert(VT.is128BitVector() && "unknown splat vector size");
|
|
switch (Val.getValueType().getSizeInBits()) {
|
|
case 8: CanonicalVT = MVT::v16i8; break;
|
|
case 16: CanonicalVT = MVT::v8i16; break;
|
|
case 32: CanonicalVT = MVT::v4i32; break;
|
|
case 64: CanonicalVT = MVT::v2i64; break;
|
|
default: llvm_unreachable("unexpected splat element type"); break;
|
|
}
|
|
}
|
|
|
|
// Build a canonical splat for this value.
|
|
SmallVector<SDValue, 8> Ops;
|
|
Ops.assign(CanonicalVT.getVectorNumElements(), Val);
|
|
SDValue Res = DAG.getNode(ISD::BUILD_VECTOR, dl, CanonicalVT, &Ops[0],
|
|
Ops.size());
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, VT, Res);
|
|
}
|
|
|
|
// If this is a case we can't handle, return null and let the default
|
|
// expansion code take care of it.
|
|
static SDValue LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG) {
|
|
BuildVectorSDNode *BVN = dyn_cast<BuildVectorSDNode>(Op.getNode());
|
|
assert(BVN != 0 && "Expected a BuildVectorSDNode in LowerBUILD_VECTOR");
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
MVT VT = Op.getValueType();
|
|
|
|
APInt SplatBits, SplatUndef;
|
|
unsigned SplatBitSize;
|
|
bool HasAnyUndefs;
|
|
if (BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize, HasAnyUndefs)) {
|
|
SDValue Val = isVMOVSplat(SplatBits.getZExtValue(),
|
|
SplatUndef.getZExtValue(), SplatBitSize, DAG);
|
|
if (Val.getNode())
|
|
return BuildSplat(Val, VT, DAG, dl);
|
|
}
|
|
|
|
// If there are only 2 elements in a 128-bit vector, insert them into an
|
|
// undef vector. This handles the common case for 128-bit vector argument
|
|
// passing, where the insertions should be translated to subreg accesses
|
|
// with no real instructions.
|
|
if (VT.is128BitVector() && Op.getNumOperands() == 2) {
|
|
SDValue Val = DAG.getUNDEF(VT);
|
|
SDValue Op0 = Op.getOperand(0);
|
|
SDValue Op1 = Op.getOperand(1);
|
|
if (Op0.getOpcode() != ISD::UNDEF)
|
|
Val = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, VT, Val, Op0,
|
|
DAG.getIntPtrConstant(0));
|
|
if (Op1.getOpcode() != ISD::UNDEF)
|
|
Val = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, VT, Val, Op1,
|
|
DAG.getIntPtrConstant(1));
|
|
return Val;
|
|
}
|
|
|
|
return SDValue();
|
|
}
|
|
|
|
static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) {
|
|
return Op;
|
|
}
|
|
|
|
static SDValue LowerSCALAR_TO_VECTOR(SDValue Op, SelectionDAG &DAG) {
|
|
return Op;
|
|
}
|
|
|
|
static SDValue LowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) {
|
|
MVT VT = Op.getValueType();
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
assert((VT == MVT::i8 || VT == MVT::i16) &&
|
|
"unexpected type for custom-lowering vector extract");
|
|
SDValue Vec = Op.getOperand(0);
|
|
SDValue Lane = Op.getOperand(1);
|
|
Op = DAG.getNode(ARMISD::VGETLANEu, dl, MVT::i32, Vec, Lane);
|
|
Op = DAG.getNode(ISD::AssertZext, dl, MVT::i32, Op, DAG.getValueType(VT));
|
|
return DAG.getNode(ISD::TRUNCATE, dl, VT, Op);
|
|
}
|
|
|
|
static SDValue LowerCONCAT_VECTORS(SDValue Op, SelectionDAG &DAG) {
|
|
// The only time a CONCAT_VECTORS operation can have legal types is when
|
|
// two 64-bit vectors are concatenated to a 128-bit vector.
|
|
assert(Op.getValueType().is128BitVector() && Op.getNumOperands() == 2 &&
|
|
"unexpected CONCAT_VECTORS");
|
|
DebugLoc dl = Op.getDebugLoc();
|
|
SDValue Val = DAG.getUNDEF(MVT::v2f64);
|
|
SDValue Op0 = Op.getOperand(0);
|
|
SDValue Op1 = Op.getOperand(1);
|
|
if (Op0.getOpcode() != ISD::UNDEF)
|
|
Val = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64, Val,
|
|
DAG.getNode(ISD::BIT_CONVERT, dl, MVT::f64, Op0),
|
|
DAG.getIntPtrConstant(0));
|
|
if (Op1.getOpcode() != ISD::UNDEF)
|
|
Val = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, MVT::v2f64, Val,
|
|
DAG.getNode(ISD::BIT_CONVERT, dl, MVT::f64, Op1),
|
|
DAG.getIntPtrConstant(1));
|
|
return DAG.getNode(ISD::BIT_CONVERT, dl, Op.getValueType(), Val);
|
|
}
|
|
|
|
SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) {
|
|
switch (Op.getOpcode()) {
|
|
default: llvm_unreachable("Don't know how to custom lower this!");
|
|
case ISD::ConstantPool: return LowerConstantPool(Op, DAG);
|
|
case ISD::GlobalAddress:
|
|
return Subtarget->isTargetDarwin() ? LowerGlobalAddressDarwin(Op, DAG) :
|
|
LowerGlobalAddressELF(Op, DAG);
|
|
case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(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::DYNAMIC_STACKALLOC: return LowerDYNAMIC_STACKALLOC(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);
|
|
case ISD::RETURNADDR: break;
|
|
case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG);
|
|
case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG);
|
|
case ISD::INTRINSIC_VOID:
|
|
case ISD::INTRINSIC_W_CHAIN: return LowerINTRINSIC_W_CHAIN(Op, DAG);
|
|
case ISD::INTRINSIC_WO_CHAIN: return LowerINTRINSIC_WO_CHAIN(Op, DAG);
|
|
case ISD::BIT_CONVERT: return ExpandBIT_CONVERT(Op.getNode(), DAG);
|
|
case ISD::SHL:
|
|
case ISD::SRL:
|
|
case ISD::SRA: return LowerShift(Op.getNode(), DAG, Subtarget);
|
|
case ISD::VSETCC: return LowerVSETCC(Op, DAG);
|
|
case ISD::BUILD_VECTOR: return LowerBUILD_VECTOR(Op, DAG);
|
|
case ISD::VECTOR_SHUFFLE: return LowerVECTOR_SHUFFLE(Op, DAG);
|
|
case ISD::SCALAR_TO_VECTOR: return LowerSCALAR_TO_VECTOR(Op, DAG);
|
|
case ISD::EXTRACT_VECTOR_ELT: return LowerEXTRACT_VECTOR_ELT(Op, DAG);
|
|
case ISD::CONCAT_VECTORS: return LowerCONCAT_VECTORS(Op, DAG);
|
|
}
|
|
return SDValue();
|
|
}
|
|
|
|
/// 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) {
|
|
switch (N->getOpcode()) {
|
|
default:
|
|
llvm_unreachable("Don't know how to custom expand this!");
|
|
return;
|
|
case ISD::BIT_CONVERT:
|
|
Results.push_back(ExpandBIT_CONVERT(N, DAG));
|
|
return;
|
|
case ISD::SRL:
|
|
case ISD::SRA: {
|
|
SDValue Res = LowerShift(N, DAG, Subtarget);
|
|
if (Res.getNode())
|
|
Results.push_back(Res);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ARM Scheduler Hooks
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
MachineBasicBlock *
|
|
ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI,
|
|
MachineBasicBlock *BB) const {
|
|
const TargetInstrInfo *TII = getTargetMachine().getInstrInfo();
|
|
DebugLoc dl = MI->getDebugLoc();
|
|
switch (MI->getOpcode()) {
|
|
default:
|
|
llvm_unreachable("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();
|
|
MachineFunction::iterator It = BB;
|
|
++It;
|
|
|
|
// thisMBB:
|
|
// ...
|
|
// TrueVal = ...
|
|
// cmpTY ccX, r1, r2
|
|
// bCC copy1MBB
|
|
// fallthrough --> copy0MBB
|
|
MachineBasicBlock *thisMBB = BB;
|
|
MachineFunction *F = BB->getParent();
|
|
MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB);
|
|
MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB);
|
|
BuildMI(BB, dl, TII->get(ARM::tBcc)).addMBB(sinkMBB)
|
|
.addImm(MI->getOperand(3).getImm()).addReg(MI->getOperand(4).getReg());
|
|
F->insert(It, copy0MBB);
|
|
F->insert(It, sinkMBB);
|
|
// 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;
|
|
BuildMI(BB, dl, TII->get(ARM::PHI), MI->getOperand(0).getReg())
|
|
.addReg(MI->getOperand(1).getReg()).addMBB(copy0MBB)
|
|
.addReg(MI->getOperand(2).getReg()).addMBB(thisMBB);
|
|
|
|
F->DeleteMachineInstr(MI); // The pseudo instruction is gone now.
|
|
return BB;
|
|
}
|
|
|
|
case ARM::tANDsp:
|
|
case ARM::tADDspr_:
|
|
case ARM::tSUBspi_:
|
|
case ARM::t2SUBrSPi_:
|
|
case ARM::t2SUBrSPi12_:
|
|
case ARM::t2SUBrSPs_: {
|
|
MachineFunction *MF = BB->getParent();
|
|
unsigned DstReg = MI->getOperand(0).getReg();
|
|
unsigned SrcReg = MI->getOperand(1).getReg();
|
|
bool DstIsDead = MI->getOperand(0).isDead();
|
|
bool SrcIsKill = MI->getOperand(1).isKill();
|
|
|
|
if (SrcReg != ARM::SP) {
|
|
// Copy the source to SP from virtual register.
|
|
const TargetRegisterClass *RC = MF->getRegInfo().getRegClass(SrcReg);
|
|
unsigned CopyOpc = (RC == ARM::tGPRRegisterClass)
|
|
? ARM::tMOVtgpr2gpr : ARM::tMOVgpr2gpr;
|
|
BuildMI(BB, dl, TII->get(CopyOpc), ARM::SP)
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill));
|
|
}
|
|
|
|
unsigned OpOpc = 0;
|
|
bool NeedPred = false, NeedCC = false, NeedOp3 = false;
|
|
switch (MI->getOpcode()) {
|
|
default:
|
|
llvm_unreachable("Unexpected pseudo instruction!");
|
|
case ARM::tANDsp:
|
|
OpOpc = ARM::tAND;
|
|
NeedPred = true;
|
|
break;
|
|
case ARM::tADDspr_:
|
|
OpOpc = ARM::tADDspr;
|
|
break;
|
|
case ARM::tSUBspi_:
|
|
OpOpc = ARM::tSUBspi;
|
|
break;
|
|
case ARM::t2SUBrSPi_:
|
|
OpOpc = ARM::t2SUBrSPi;
|
|
NeedPred = true; NeedCC = true;
|
|
break;
|
|
case ARM::t2SUBrSPi12_:
|
|
OpOpc = ARM::t2SUBrSPi12;
|
|
NeedPred = true;
|
|
break;
|
|
case ARM::t2SUBrSPs_:
|
|
OpOpc = ARM::t2SUBrSPs;
|
|
NeedPred = true; NeedCC = true; NeedOp3 = true;
|
|
break;
|
|
}
|
|
MachineInstrBuilder MIB = BuildMI(BB, dl, TII->get(OpOpc), ARM::SP);
|
|
if (OpOpc == ARM::tAND)
|
|
AddDefaultT1CC(MIB);
|
|
MIB.addReg(ARM::SP);
|
|
MIB.addOperand(MI->getOperand(2));
|
|
if (NeedOp3)
|
|
MIB.addOperand(MI->getOperand(3));
|
|
if (NeedPred)
|
|
AddDefaultPred(MIB);
|
|
if (NeedCC)
|
|
AddDefaultCC(MIB);
|
|
|
|
// Copy the result from SP to virtual register.
|
|
const TargetRegisterClass *RC = MF->getRegInfo().getRegClass(DstReg);
|
|
unsigned CopyOpc = (RC == ARM::tGPRRegisterClass)
|
|
? ARM::tMOVgpr2tgpr : ARM::tMOVgpr2gpr;
|
|
BuildMI(BB, dl, TII->get(CopyOpc))
|
|
.addReg(DstReg, getDefRegState(true) | getDeadRegState(DstIsDead))
|
|
.addReg(ARM::SP);
|
|
MF->DeleteMachineInstr(MI); // The pseudo instruction is gone now.
|
|
return BB;
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ARM Optimization Hooks
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
return SDValue();
|
|
}
|
|
|
|
|
|
/// PerformFMRRDCombine - Target-specific dag combine xforms for ARMISD::FMRRD.
|
|
static SDValue PerformFMRRDCombine(SDNode *N,
|
|
TargetLowering::DAGCombinerInfo &DCI) {
|
|
// fmrrd(fmdrr x, y) -> x,y
|
|
SDValue InDouble = N->getOperand(0);
|
|
if (InDouble.getOpcode() == ARMISD::FMDRR)
|
|
return DCI.CombineTo(N, InDouble.getOperand(0), InDouble.getOperand(1));
|
|
return SDValue();
|
|
}
|
|
|
|
/// getVShiftImm - Check if this is a valid build_vector for the immediate
|
|
/// operand of a vector shift operation, where all the elements of the
|
|
/// build_vector must have the same constant integer value.
|
|
static bool getVShiftImm(SDValue Op, unsigned ElementBits, int64_t &Cnt) {
|
|
// Ignore bit_converts.
|
|
while (Op.getOpcode() == ISD::BIT_CONVERT)
|
|
Op = Op.getOperand(0);
|
|
BuildVectorSDNode *BVN = dyn_cast<BuildVectorSDNode>(Op.getNode());
|
|
APInt SplatBits, SplatUndef;
|
|
unsigned SplatBitSize;
|
|
bool HasAnyUndefs;
|
|
if (! BVN || ! BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize,
|
|
HasAnyUndefs, ElementBits) ||
|
|
SplatBitSize > ElementBits)
|
|
return false;
|
|
Cnt = SplatBits.getSExtValue();
|
|
return true;
|
|
}
|
|
|
|
/// isVShiftLImm - Check if this is a valid build_vector for the immediate
|
|
/// operand of a vector shift left operation. That value must be in the range:
|
|
/// 0 <= Value < ElementBits for a left shift; or
|
|
/// 0 <= Value <= ElementBits for a long left shift.
|
|
static bool isVShiftLImm(SDValue Op, MVT VT, bool isLong, int64_t &Cnt) {
|
|
assert(VT.isVector() && "vector shift count is not a vector type");
|
|
unsigned ElementBits = VT.getVectorElementType().getSizeInBits();
|
|
if (! getVShiftImm(Op, ElementBits, Cnt))
|
|
return false;
|
|
return (Cnt >= 0 && (isLong ? Cnt-1 : Cnt) < ElementBits);
|
|
}
|
|
|
|
/// isVShiftRImm - Check if this is a valid build_vector for the immediate
|
|
/// operand of a vector shift right operation. For a shift opcode, the value
|
|
/// is positive, but for an intrinsic the value count must be negative. The
|
|
/// absolute value must be in the range:
|
|
/// 1 <= |Value| <= ElementBits for a right shift; or
|
|
/// 1 <= |Value| <= ElementBits/2 for a narrow right shift.
|
|
static bool isVShiftRImm(SDValue Op, MVT VT, bool isNarrow, bool isIntrinsic,
|
|
int64_t &Cnt) {
|
|
assert(VT.isVector() && "vector shift count is not a vector type");
|
|
unsigned ElementBits = VT.getVectorElementType().getSizeInBits();
|
|
if (! getVShiftImm(Op, ElementBits, Cnt))
|
|
return false;
|
|
if (isIntrinsic)
|
|
Cnt = -Cnt;
|
|
return (Cnt >= 1 && Cnt <= (isNarrow ? ElementBits/2 : ElementBits));
|
|
}
|
|
|
|
/// PerformIntrinsicCombine - ARM-specific DAG combining for intrinsics.
|
|
static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) {
|
|
unsigned IntNo = cast<ConstantSDNode>(N->getOperand(0))->getZExtValue();
|
|
switch (IntNo) {
|
|
default:
|
|
// Don't do anything for most intrinsics.
|
|
break;
|
|
|
|
// Vector shifts: check for immediate versions and lower them.
|
|
// Note: This is done during DAG combining instead of DAG legalizing because
|
|
// the build_vectors for 64-bit vector element shift counts are generally
|
|
// not legal, and it is hard to see their values after they get legalized to
|
|
// loads from a constant pool.
|
|
case Intrinsic::arm_neon_vshifts:
|
|
case Intrinsic::arm_neon_vshiftu:
|
|
case Intrinsic::arm_neon_vshiftls:
|
|
case Intrinsic::arm_neon_vshiftlu:
|
|
case Intrinsic::arm_neon_vshiftn:
|
|
case Intrinsic::arm_neon_vrshifts:
|
|
case Intrinsic::arm_neon_vrshiftu:
|
|
case Intrinsic::arm_neon_vrshiftn:
|
|
case Intrinsic::arm_neon_vqshifts:
|
|
case Intrinsic::arm_neon_vqshiftu:
|
|
case Intrinsic::arm_neon_vqshiftsu:
|
|
case Intrinsic::arm_neon_vqshiftns:
|
|
case Intrinsic::arm_neon_vqshiftnu:
|
|
case Intrinsic::arm_neon_vqshiftnsu:
|
|
case Intrinsic::arm_neon_vqrshiftns:
|
|
case Intrinsic::arm_neon_vqrshiftnu:
|
|
case Intrinsic::arm_neon_vqrshiftnsu: {
|
|
MVT VT = N->getOperand(1).getValueType();
|
|
int64_t Cnt;
|
|
unsigned VShiftOpc = 0;
|
|
|
|
switch (IntNo) {
|
|
case Intrinsic::arm_neon_vshifts:
|
|
case Intrinsic::arm_neon_vshiftu:
|
|
if (isVShiftLImm(N->getOperand(2), VT, false, Cnt)) {
|
|
VShiftOpc = ARMISD::VSHL;
|
|
break;
|
|
}
|
|
if (isVShiftRImm(N->getOperand(2), VT, false, true, Cnt)) {
|
|
VShiftOpc = (IntNo == Intrinsic::arm_neon_vshifts ?
|
|
ARMISD::VSHRs : ARMISD::VSHRu);
|
|
break;
|
|
}
|
|
return SDValue();
|
|
|
|
case Intrinsic::arm_neon_vshiftls:
|
|
case Intrinsic::arm_neon_vshiftlu:
|
|
if (isVShiftLImm(N->getOperand(2), VT, true, Cnt))
|
|
break;
|
|
llvm_unreachable("invalid shift count for vshll intrinsic");
|
|
|
|
case Intrinsic::arm_neon_vrshifts:
|
|
case Intrinsic::arm_neon_vrshiftu:
|
|
if (isVShiftRImm(N->getOperand(2), VT, false, true, Cnt))
|
|
break;
|
|
return SDValue();
|
|
|
|
case Intrinsic::arm_neon_vqshifts:
|
|
case Intrinsic::arm_neon_vqshiftu:
|
|
if (isVShiftLImm(N->getOperand(2), VT, false, Cnt))
|
|
break;
|
|
return SDValue();
|
|
|
|
case Intrinsic::arm_neon_vqshiftsu:
|
|
if (isVShiftLImm(N->getOperand(2), VT, false, Cnt))
|
|
break;
|
|
llvm_unreachable("invalid shift count for vqshlu intrinsic");
|
|
|
|
case Intrinsic::arm_neon_vshiftn:
|
|
case Intrinsic::arm_neon_vrshiftn:
|
|
case Intrinsic::arm_neon_vqshiftns:
|
|
case Intrinsic::arm_neon_vqshiftnu:
|
|
case Intrinsic::arm_neon_vqshiftnsu:
|
|
case Intrinsic::arm_neon_vqrshiftns:
|
|
case Intrinsic::arm_neon_vqrshiftnu:
|
|
case Intrinsic::arm_neon_vqrshiftnsu:
|
|
// Narrowing shifts require an immediate right shift.
|
|
if (isVShiftRImm(N->getOperand(2), VT, true, true, Cnt))
|
|
break;
|
|
llvm_unreachable("invalid shift count for narrowing vector shift intrinsic");
|
|
|
|
default:
|
|
llvm_unreachable("unhandled vector shift");
|
|
}
|
|
|
|
switch (IntNo) {
|
|
case Intrinsic::arm_neon_vshifts:
|
|
case Intrinsic::arm_neon_vshiftu:
|
|
// Opcode already set above.
|
|
break;
|
|
case Intrinsic::arm_neon_vshiftls:
|
|
case Intrinsic::arm_neon_vshiftlu:
|
|
if (Cnt == VT.getVectorElementType().getSizeInBits())
|
|
VShiftOpc = ARMISD::VSHLLi;
|
|
else
|
|
VShiftOpc = (IntNo == Intrinsic::arm_neon_vshiftls ?
|
|
ARMISD::VSHLLs : ARMISD::VSHLLu);
|
|
break;
|
|
case Intrinsic::arm_neon_vshiftn:
|
|
VShiftOpc = ARMISD::VSHRN; break;
|
|
case Intrinsic::arm_neon_vrshifts:
|
|
VShiftOpc = ARMISD::VRSHRs; break;
|
|
case Intrinsic::arm_neon_vrshiftu:
|
|
VShiftOpc = ARMISD::VRSHRu; break;
|
|
case Intrinsic::arm_neon_vrshiftn:
|
|
VShiftOpc = ARMISD::VRSHRN; break;
|
|
case Intrinsic::arm_neon_vqshifts:
|
|
VShiftOpc = ARMISD::VQSHLs; break;
|
|
case Intrinsic::arm_neon_vqshiftu:
|
|
VShiftOpc = ARMISD::VQSHLu; break;
|
|
case Intrinsic::arm_neon_vqshiftsu:
|
|
VShiftOpc = ARMISD::VQSHLsu; break;
|
|
case Intrinsic::arm_neon_vqshiftns:
|
|
VShiftOpc = ARMISD::VQSHRNs; break;
|
|
case Intrinsic::arm_neon_vqshiftnu:
|
|
VShiftOpc = ARMISD::VQSHRNu; break;
|
|
case Intrinsic::arm_neon_vqshiftnsu:
|
|
VShiftOpc = ARMISD::VQSHRNsu; break;
|
|
case Intrinsic::arm_neon_vqrshiftns:
|
|
VShiftOpc = ARMISD::VQRSHRNs; break;
|
|
case Intrinsic::arm_neon_vqrshiftnu:
|
|
VShiftOpc = ARMISD::VQRSHRNu; break;
|
|
case Intrinsic::arm_neon_vqrshiftnsu:
|
|
VShiftOpc = ARMISD::VQRSHRNsu; break;
|
|
}
|
|
|
|
return DAG.getNode(VShiftOpc, N->getDebugLoc(), N->getValueType(0),
|
|
N->getOperand(1), DAG.getConstant(Cnt, MVT::i32));
|
|
}
|
|
|
|
case Intrinsic::arm_neon_vshiftins: {
|
|
MVT VT = N->getOperand(1).getValueType();
|
|
int64_t Cnt;
|
|
unsigned VShiftOpc = 0;
|
|
|
|
if (isVShiftLImm(N->getOperand(3), VT, false, Cnt))
|
|
VShiftOpc = ARMISD::VSLI;
|
|
else if (isVShiftRImm(N->getOperand(3), VT, false, true, Cnt))
|
|
VShiftOpc = ARMISD::VSRI;
|
|
else {
|
|
llvm_unreachable("invalid shift count for vsli/vsri intrinsic");
|
|
}
|
|
|
|
return DAG.getNode(VShiftOpc, N->getDebugLoc(), N->getValueType(0),
|
|
N->getOperand(1), N->getOperand(2),
|
|
DAG.getConstant(Cnt, MVT::i32));
|
|
}
|
|
|
|
case Intrinsic::arm_neon_vqrshifts:
|
|
case Intrinsic::arm_neon_vqrshiftu:
|
|
// No immediate versions of these to check for.
|
|
break;
|
|
}
|
|
|
|
return SDValue();
|
|
}
|
|
|
|
/// PerformShiftCombine - Checks for immediate versions of vector shifts and
|
|
/// lowers them. As with the vector shift intrinsics, this is done during DAG
|
|
/// combining instead of DAG legalizing because the build_vectors for 64-bit
|
|
/// vector element shift counts are generally not legal, and it is hard to see
|
|
/// their values after they get legalized to loads from a constant pool.
|
|
static SDValue PerformShiftCombine(SDNode *N, SelectionDAG &DAG,
|
|
const ARMSubtarget *ST) {
|
|
MVT VT = N->getValueType(0);
|
|
|
|
// Nothing to be done for scalar shifts.
|
|
if (! VT.isVector())
|
|
return SDValue();
|
|
|
|
assert(ST->hasNEON() && "unexpected vector shift");
|
|
int64_t Cnt;
|
|
|
|
switch (N->getOpcode()) {
|
|
default: llvm_unreachable("unexpected shift opcode");
|
|
|
|
case ISD::SHL:
|
|
if (isVShiftLImm(N->getOperand(1), VT, false, Cnt))
|
|
return DAG.getNode(ARMISD::VSHL, N->getDebugLoc(), VT, N->getOperand(0),
|
|
DAG.getConstant(Cnt, MVT::i32));
|
|
break;
|
|
|
|
case ISD::SRA:
|
|
case ISD::SRL:
|
|
if (isVShiftRImm(N->getOperand(1), VT, false, false, Cnt)) {
|
|
unsigned VShiftOpc = (N->getOpcode() == ISD::SRA ?
|
|
ARMISD::VSHRs : ARMISD::VSHRu);
|
|
return DAG.getNode(VShiftOpc, N->getDebugLoc(), VT, N->getOperand(0),
|
|
DAG.getConstant(Cnt, MVT::i32));
|
|
}
|
|
}
|
|
return SDValue();
|
|
}
|
|
|
|
/// PerformExtendCombine - Target-specific DAG combining for ISD::SIGN_EXTEND,
|
|
/// ISD::ZERO_EXTEND, and ISD::ANY_EXTEND.
|
|
static SDValue PerformExtendCombine(SDNode *N, SelectionDAG &DAG,
|
|
const ARMSubtarget *ST) {
|
|
SDValue N0 = N->getOperand(0);
|
|
|
|
// Check for sign- and zero-extensions of vector extract operations of 8-
|
|
// and 16-bit vector elements. NEON supports these directly. They are
|
|
// handled during DAG combining because type legalization will promote them
|
|
// to 32-bit types and it is messy to recognize the operations after that.
|
|
if (ST->hasNEON() && N0.getOpcode() == ISD::EXTRACT_VECTOR_ELT) {
|
|
SDValue Vec = N0.getOperand(0);
|
|
SDValue Lane = N0.getOperand(1);
|
|
MVT VT = N->getValueType(0);
|
|
MVT EltVT = N0.getValueType();
|
|
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
|
|
|
|
if (VT == MVT::i32 &&
|
|
(EltVT == MVT::i8 || EltVT == MVT::i16) &&
|
|
TLI.isTypeLegal(Vec.getValueType())) {
|
|
|
|
unsigned Opc = 0;
|
|
switch (N->getOpcode()) {
|
|
default: llvm_unreachable("unexpected opcode");
|
|
case ISD::SIGN_EXTEND:
|
|
Opc = ARMISD::VGETLANEs;
|
|
break;
|
|
case ISD::ZERO_EXTEND:
|
|
case ISD::ANY_EXTEND:
|
|
Opc = ARMISD::VGETLANEu;
|
|
break;
|
|
}
|
|
return DAG.getNode(Opc, N->getDebugLoc(), VT, Vec, Lane);
|
|
}
|
|
}
|
|
|
|
return SDValue();
|
|
}
|
|
|
|
SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
|
|
DAGCombinerInfo &DCI) const {
|
|
switch (N->getOpcode()) {
|
|
default: break;
|
|
case ISD::ADD: return PerformADDCombine(N, DCI);
|
|
case ISD::SUB: return PerformSUBCombine(N, DCI);
|
|
case ARMISD::FMRRD: return PerformFMRRDCombine(N, DCI);
|
|
case ISD::INTRINSIC_WO_CHAIN:
|
|
return PerformIntrinsicCombine(N, DCI.DAG);
|
|
case ISD::SHL:
|
|
case ISD::SRA:
|
|
case ISD::SRL:
|
|
return PerformShiftCombine(N, DCI.DAG, Subtarget);
|
|
case ISD::SIGN_EXTEND:
|
|
case ISD::ZERO_EXTEND:
|
|
case ISD::ANY_EXTEND:
|
|
return PerformExtendCombine(N, DCI.DAG, Subtarget);
|
|
}
|
|
return SDValue();
|
|
}
|
|
|
|
/// 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.
|
|
static bool isLegalAddressImmediate(int64_t V, MVT VT,
|
|
const ARMSubtarget *Subtarget) {
|
|
if (V == 0)
|
|
return true;
|
|
|
|
if (!VT.isSimple())
|
|
return false;
|
|
|
|
if (Subtarget->isThumb()) { // FIXME for thumb2
|
|
if (V < 0)
|
|
return false;
|
|
|
|
unsigned Scale = 1;
|
|
switch (VT.getSimpleVT()) {
|
|
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;
|
|
return V == (V & ((1LL << 5) - 1));
|
|
}
|
|
|
|
if (V < 0)
|
|
V = - V;
|
|
switch (VT.getSimpleVT()) {
|
|
default: return false;
|
|
case MVT::i1:
|
|
case MVT::i8:
|
|
case MVT::i32:
|
|
// +- imm12
|
|
return V == (V & ((1LL << 12) - 1));
|
|
case MVT::i16:
|
|
// +- imm8
|
|
return V == (V & ((1LL << 8) - 1));
|
|
case MVT::f32:
|
|
case MVT::f64:
|
|
if (!Subtarget->hasVFP2())
|
|
return false;
|
|
if ((V & 3) != 0)
|
|
return false;
|
|
V >>= 2;
|
|
return V == (V & ((1LL << 8) - 1));
|
|
}
|
|
}
|
|
|
|
/// isLegalAddressingMode - Return true if the addressing mode represented
|
|
/// by AM is legal for this target, for a load/store of the specified type.
|
|
bool ARMTargetLowering::isLegalAddressingMode(const AddrMode &AM,
|
|
const Type *Ty) const {
|
|
MVT VT = getValueType(Ty, true);
|
|
if (!isLegalAddressImmediate(AM.BaseOffs, VT, Subtarget))
|
|
return false;
|
|
|
|
// Can never fold addr of global into load/store.
|
|
if (AM.BaseGV)
|
|
return false;
|
|
|
|
switch (AM.Scale) {
|
|
case 0: // no scale reg, must be "r+i" or "r", or "i".
|
|
break;
|
|
case 1:
|
|
if (Subtarget->isThumb()) // FIXME for thumb2
|
|
return false;
|
|
// FALL THROUGH.
|
|
default:
|
|
// ARM doesn't support any R+R*scale+imm addr modes.
|
|
if (AM.BaseOffs)
|
|
return false;
|
|
|
|
if (!VT.isSimple())
|
|
return false;
|
|
|
|
int Scale = AM.Scale;
|
|
switch (VT.getSimpleVT()) {
|
|
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
|
|
if (Scale < 0) Scale = -Scale;
|
|
if (Scale == 1)
|
|
return true;
|
|
// r + r << imm
|
|
return isPowerOf2_32(Scale & ~1);
|
|
case MVT::i16:
|
|
// r + r
|
|
if (((unsigned)AM.HasBaseReg + Scale) <= 2)
|
|
return true;
|
|
return false;
|
|
|
|
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.
|
|
|
|
// 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;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool getARMIndexedAddressParts(SDNode *Ptr, MVT VT,
|
|
bool isSEXTLoad, SDValue &Base,
|
|
SDValue &Offset, bool &isInc,
|
|
SelectionDAG &DAG) {
|
|
if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB)
|
|
return false;
|
|
|
|
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))) {
|
|
int RHSC = (int)RHS->getZExtValue();
|
|
if (RHSC < 0 && RHSC > -256) {
|
|
assert(Ptr->getOpcode() == ISD::ADD);
|
|
isInc = false;
|
|
Offset = DAG.getConstant(-RHSC, RHS->getValueType(0));
|
|
return true;
|
|
}
|
|
}
|
|
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))) {
|
|
int RHSC = (int)RHS->getZExtValue();
|
|
if (RHSC < 0 && RHSC > -0x1000) {
|
|
assert(Ptr->getOpcode() == ISD::ADD);
|
|
isInc = false;
|
|
Offset = DAG.getConstant(-RHSC, RHS->getValueType(0));
|
|
Base = Ptr->getOperand(0);
|
|
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;
|
|
}
|
|
|
|
static bool getT2IndexedAddressParts(SDNode *Ptr, MVT VT,
|
|
bool isSEXTLoad, SDValue &Base,
|
|
SDValue &Offset, bool &isInc,
|
|
SelectionDAG &DAG) {
|
|
if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB)
|
|
return false;
|
|
|
|
Base = Ptr->getOperand(0);
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Ptr->getOperand(1))) {
|
|
int RHSC = (int)RHS->getZExtValue();
|
|
if (RHSC < 0 && RHSC > -0x100) { // 8 bits.
|
|
assert(Ptr->getOpcode() == ISD::ADD);
|
|
isInc = false;
|
|
Offset = DAG.getConstant(-RHSC, RHS->getValueType(0));
|
|
return true;
|
|
} else if (RHSC > 0 && RHSC < 0x100) { // 8 bit, no zero.
|
|
isInc = Ptr->getOpcode() == ISD::ADD;
|
|
Offset = DAG.getConstant(RHSC, RHS->getValueType(0));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// getPreIndexedAddressParts - returns true by value, base pointer and
|
|
/// offset pointer and addressing mode by reference if the node's address
|
|
/// can be legally represented as pre-indexed load / store address.
|
|
bool
|
|
ARMTargetLowering::getPreIndexedAddressParts(SDNode *N, SDValue &Base,
|
|
SDValue &Offset,
|
|
ISD::MemIndexedMode &AM,
|
|
SelectionDAG &DAG) const {
|
|
if (Subtarget->isThumb1Only())
|
|
return false;
|
|
|
|
MVT VT;
|
|
SDValue Ptr;
|
|
bool isSEXTLoad = false;
|
|
if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) {
|
|
Ptr = LD->getBasePtr();
|
|
VT = LD->getMemoryVT();
|
|
isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD;
|
|
} else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) {
|
|
Ptr = ST->getBasePtr();
|
|
VT = ST->getMemoryVT();
|
|
} else
|
|
return false;
|
|
|
|
bool isInc;
|
|
bool isLegal = false;
|
|
if (Subtarget->isThumb() && Subtarget->hasThumb2())
|
|
isLegal = getT2IndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base,
|
|
Offset, isInc, DAG);
|
|
else
|
|
isLegal = getARMIndexedAddressParts(Ptr.getNode(), VT, isSEXTLoad, Base,
|
|
Offset, isInc, DAG);
|
|
if (!isLegal)
|
|
return false;
|
|
|
|
AM = isInc ? ISD::PRE_INC : ISD::PRE_DEC;
|
|
return true;
|
|
}
|
|
|
|
/// 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,
|
|
SDValue &Base,
|
|
SDValue &Offset,
|
|
ISD::MemIndexedMode &AM,
|
|
SelectionDAG &DAG) const {
|
|
if (Subtarget->isThumb1Only())
|
|
return false;
|
|
|
|
MVT VT;
|
|
SDValue Ptr;
|
|
bool isSEXTLoad = false;
|
|
if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) {
|
|
VT = LD->getMemoryVT();
|
|
isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD;
|
|
} else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) {
|
|
VT = ST->getMemoryVT();
|
|
} else
|
|
return false;
|
|
|
|
bool isInc;
|
|
bool isLegal = false;
|
|
if (Subtarget->isThumb() && Subtarget->hasThumb2())
|
|
isLegal = getT2IndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset,
|
|
isInc, DAG);
|
|
else
|
|
isLegal = getARMIndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset,
|
|
isInc, DAG);
|
|
if (!isLegal)
|
|
return false;
|
|
|
|
AM = isInc ? ISD::POST_INC : ISD::POST_DEC;
|
|
return true;
|
|
}
|
|
|
|
void ARMTargetLowering::computeMaskedBitsForTargetNode(const SDValue Op,
|
|
const APInt &Mask,
|
|
APInt &KnownZero,
|
|
APInt &KnownOne,
|
|
const SelectionDAG &DAG,
|
|
unsigned Depth) const {
|
|
KnownZero = KnownOne = APInt(Mask.getBitWidth(), 0);
|
|
switch (Op.getOpcode()) {
|
|
default: break;
|
|
case ARMISD::CMOV: {
|
|
// Bits are known zero/one if known on the LHS and RHS.
|
|
DAG.ComputeMaskedBits(Op.getOperand(0), Mask, KnownZero, KnownOne, Depth+1);
|
|
if (KnownZero == 0 && KnownOne == 0) return;
|
|
|
|
APInt KnownZeroRHS, KnownOneRHS;
|
|
DAG.ComputeMaskedBits(Op.getOperand(1), Mask,
|
|
KnownZeroRHS, KnownOneRHS, Depth+1);
|
|
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
|
|
ARMTargetLowering::getConstraintType(const std::string &Constraint) const {
|
|
if (Constraint.size() == 1) {
|
|
switch (Constraint[0]) {
|
|
default: break;
|
|
case 'l': return C_RegisterClass;
|
|
case 'w': return C_RegisterClass;
|
|
}
|
|
}
|
|
return TargetLowering::getConstraintType(Constraint);
|
|
}
|
|
|
|
std::pair<unsigned, const TargetRegisterClass*>
|
|
ARMTargetLowering::getRegForInlineAsmConstraint(const std::string &Constraint,
|
|
MVT VT) const {
|
|
if (Constraint.size() == 1) {
|
|
// GCC RS6000 Constraint Letters
|
|
switch (Constraint[0]) {
|
|
case 'l':
|
|
if (Subtarget->isThumb1Only())
|
|
return std::make_pair(0U, ARM::tGPRRegisterClass);
|
|
else
|
|
return std::make_pair(0U, ARM::GPRRegisterClass);
|
|
case 'r':
|
|
return std::make_pair(0U, ARM::GPRRegisterClass);
|
|
case 'w':
|
|
if (VT == MVT::f32)
|
|
return std::make_pair(0U, ARM::SPRRegisterClass);
|
|
if (VT == MVT::f64)
|
|
return std::make_pair(0U, ARM::DPRRegisterClass);
|
|
break;
|
|
}
|
|
}
|
|
return TargetLowering::getRegForInlineAsmConstraint(Constraint, VT);
|
|
}
|
|
|
|
std::vector<unsigned> ARMTargetLowering::
|
|
getRegClassForInlineAsmConstraint(const std::string &Constraint,
|
|
MVT VT) const {
|
|
if (Constraint.size() != 1)
|
|
return std::vector<unsigned>();
|
|
|
|
switch (Constraint[0]) { // GCC ARM Constraint Letters
|
|
default: break;
|
|
case 'l':
|
|
return make_vector<unsigned>(ARM::R0, ARM::R1, ARM::R2, ARM::R3,
|
|
ARM::R4, ARM::R5, ARM::R6, ARM::R7,
|
|
0);
|
|
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);
|
|
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;
|
|
}
|
|
|
|
return std::vector<unsigned>();
|
|
}
|
|
|
|
/// 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->isThumb1Only()) {
|
|
// This must be a constant between 0 and 255, for ADD
|
|
// immediates.
|
|
if (CVal >= 0 && CVal <= 255)
|
|
break;
|
|
} else if (Subtarget->isThumb2()) {
|
|
// A constant that can be used as an immediate value in a
|
|
// data-processing instruction.
|
|
if (ARM_AM::getT2SOImmVal(CVal) != -1)
|
|
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()) { // FIXME thumb2
|
|
// 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->isThumb1Only()) {
|
|
// 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 if (Subtarget->isThumb2()) {
|
|
// 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::getT2SOImmVal(~CVal) != -1)
|
|
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->isThumb1Only()) {
|
|
// This must be a constant between -7 and 7,
|
|
// for 3-operand ADD/SUB immediate instructions.
|
|
if (CVal >= -7 && CVal < 7)
|
|
break;
|
|
} else if (Subtarget->isThumb2()) {
|
|
// 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::getT2SOImmVal(-CVal) != -1)
|
|
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()) { // FIXME thumb2
|
|
// 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()) { // FIXME thumb2
|
|
// This must be a constant between 0 and 31, for shift amounts.
|
|
if (CVal >= 0 && CVal <= 31)
|
|
break;
|
|
}
|
|
return;
|
|
|
|
case 'O':
|
|
if (Subtarget->isThumb()) { // FIXME thumb2
|
|
// 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);
|
|
}
|