aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/ARM/ARMISelLowering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/ARM/ARMISelLowering.cpp')
-rw-r--r--llvm/lib/Target/ARM/ARMISelLowering.cpp2579
1 files changed, 2016 insertions, 563 deletions
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 397979b4ab1e..900113244e41 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -21,6 +21,7 @@
#include "ARMRegisterInfo.h"
#include "ARMSelectionDAGInfo.h"
#include "ARMSubtarget.h"
+#include "ARMTargetTransformInfo.h"
#include "MCTargetDesc/ARMAddressingModes.h"
#include "MCTargetDesc/ARMBaseInfo.h"
#include "Utils/ARMBaseInfo.h"
@@ -153,8 +154,7 @@ static const MCPhysReg GPRArgRegs[] = {
ARM::R0, ARM::R1, ARM::R2, ARM::R3
};
-void ARMTargetLowering::addTypeForNEON(MVT VT, MVT PromotedLdStVT,
- MVT PromotedBitwiseVT) {
+void ARMTargetLowering::addTypeForNEON(MVT VT, MVT PromotedLdStVT) {
if (VT != PromotedLdStVT) {
setOperationAction(ISD::LOAD, VT, Promote);
AddPromotedToType (ISD::LOAD, VT, PromotedLdStVT);
@@ -193,16 +193,6 @@ void ARMTargetLowering::addTypeForNEON(MVT VT, MVT PromotedLdStVT,
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);
- }
-
// Neon does not support vector divide/remainder operations.
setOperationAction(ISD::SDIV, VT, Expand);
setOperationAction(ISD::UDIV, VT, Expand);
@@ -224,12 +214,12 @@ void ARMTargetLowering::addTypeForNEON(MVT VT, MVT PromotedLdStVT,
void ARMTargetLowering::addDRTypeForNEON(MVT VT) {
addRegisterClass(VT, &ARM::DPRRegClass);
- addTypeForNEON(VT, MVT::f64, MVT::v2i32);
+ addTypeForNEON(VT, MVT::f64);
}
void ARMTargetLowering::addQRTypeForNEON(MVT VT) {
addRegisterClass(VT, &ARM::DPairRegClass);
- addTypeForNEON(VT, MVT::v2f64, MVT::v4i32);
+ addTypeForNEON(VT, MVT::v2f64);
}
void ARMTargetLowering::setAllExpand(MVT VT) {
@@ -280,6 +270,8 @@ void ARMTargetLowering::addMVEVectorTypes(bool HasMVEFP) {
setOperationAction(ISD::UADDSAT, VT, Legal);
setOperationAction(ISD::SSUBSAT, VT, Legal);
setOperationAction(ISD::USUBSAT, VT, Legal);
+ setOperationAction(ISD::ABDS, VT, Legal);
+ setOperationAction(ISD::ABDU, VT, Legal);
// No native support for these.
setOperationAction(ISD::UDIV, VT, Expand);
@@ -396,6 +388,8 @@ void ARMTargetLowering::addMVEVectorTypes(bool HasMVEFP) {
setOperationAction(ISD::EXTRACT_VECTOR_ELT, VT, Custom);
setOperationAction(ISD::BUILD_VECTOR, VT, Custom);
}
+ setOperationAction(ISD::SCALAR_TO_VECTOR, MVT::v2f64, Legal);
+
// We can do bitwise operations on v2i64 vectors
setOperationAction(ISD::AND, MVT::v2i64, Legal);
setOperationAction(ISD::OR, MVT::v2i64, Legal);
@@ -447,6 +441,14 @@ void ARMTargetLowering::addMVEVectorTypes(bool HasMVEFP) {
setOperationAction(ISD::VSELECT, VT, Expand);
setOperationAction(ISD::SELECT, VT, Expand);
}
+ setOperationAction(ISD::SIGN_EXTEND, MVT::v8i32, Custom);
+ setOperationAction(ISD::SIGN_EXTEND, MVT::v16i16, Custom);
+ setOperationAction(ISD::SIGN_EXTEND, MVT::v16i32, Custom);
+ setOperationAction(ISD::ZERO_EXTEND, MVT::v8i32, Custom);
+ setOperationAction(ISD::ZERO_EXTEND, MVT::v16i16, Custom);
+ setOperationAction(ISD::ZERO_EXTEND, MVT::v16i32, Custom);
+ setOperationAction(ISD::TRUNCATE, MVT::v8i32, Custom);
+ setOperationAction(ISD::TRUNCATE, MVT::v16i16, Custom);
}
ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
@@ -541,6 +543,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setLibcallName(RTLIB::SHL_I128, nullptr);
setLibcallName(RTLIB::SRL_I128, nullptr);
setLibcallName(RTLIB::SRA_I128, nullptr);
+ setLibcallName(RTLIB::MUL_I128, nullptr);
// RTLIB
if (Subtarget->isAAPCS_ABI() &&
@@ -766,9 +769,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
addAllExtLoads(VT, InnerVT, Expand);
}
- setOperationAction(ISD::MULHS, VT, Expand);
setOperationAction(ISD::SMUL_LOHI, VT, Expand);
- setOperationAction(ISD::MULHU, VT, Expand);
setOperationAction(ISD::UMUL_LOHI, VT, Expand);
setOperationAction(ISD::BSWAP, VT, Expand);
@@ -947,6 +948,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::v4i32, Custom);
setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::v2i64, Custom);
+ for (MVT VT : MVT::fixedlen_vector_valuetypes()) {
+ setOperationAction(ISD::MULHS, VT, Expand);
+ setOperationAction(ISD::MULHU, VT, Expand);
+ }
+
// NEON only has FMA instructions as of VFP4.
if (!Subtarget->hasVFP4Base()) {
setOperationAction(ISD::FMA, MVT::v2f32, Expand);
@@ -977,6 +983,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setTargetDAGCombine(ISD::VECTOR_SHUFFLE);
setTargetDAGCombine(ISD::INSERT_VECTOR_ELT);
setTargetDAGCombine(ISD::EXTRACT_VECTOR_ELT);
+ setTargetDAGCombine(ISD::SIGN_EXTEND_INREG);
setTargetDAGCombine(ISD::STORE);
setTargetDAGCombine(ISD::SIGN_EXTEND);
setTargetDAGCombine(ISD::ZERO_EXTEND);
@@ -1103,6 +1110,10 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::SSUBSAT, MVT::i8, Custom);
setOperationAction(ISD::SADDSAT, MVT::i16, Custom);
setOperationAction(ISD::SSUBSAT, MVT::i16, Custom);
+ setOperationAction(ISD::UADDSAT, MVT::i8, Custom);
+ setOperationAction(ISD::USUBSAT, MVT::i8, Custom);
+ setOperationAction(ISD::UADDSAT, MVT::i16, Custom);
+ setOperationAction(ISD::USUBSAT, MVT::i16, Custom);
}
if (Subtarget->hasBaseDSP()) {
setOperationAction(ISD::SADDSAT, MVT::i32, Legal);
@@ -1340,6 +1351,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
// iff target supports vfp2.
setOperationAction(ISD::BITCAST, MVT::i64, Custom);
setOperationAction(ISD::FLT_ROUNDS_, MVT::i32, Custom);
+ setOperationAction(ISD::SET_ROUNDING, MVT::Other, Custom);
}
// We want to custom lower some of our intrinsics.
@@ -1604,209 +1616,215 @@ ARMTargetLowering::findRepresentativeClass(const TargetRegisterInfo *TRI,
}
const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
+#define MAKE_CASE(V) \
+ case V: \
+ return #V;
switch ((ARMISD::NodeType)Opcode) {
- case ARMISD::FIRST_NUMBER: break;
- case ARMISD::Wrapper: return "ARMISD::Wrapper";
- case ARMISD::WrapperPIC: return "ARMISD::WrapperPIC";
- case ARMISD::WrapperJT: return "ARMISD::WrapperJT";
- case ARMISD::COPY_STRUCT_BYVAL: return "ARMISD::COPY_STRUCT_BYVAL";
- case ARMISD::CALL: return "ARMISD::CALL";
- case ARMISD::CALL_PRED: return "ARMISD::CALL_PRED";
- case ARMISD::CALL_NOLINK: return "ARMISD::CALL_NOLINK";
- case ARMISD::tSECALL: return "ARMISD::tSECALL";
- 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::SERET_FLAG: return "ARMISD::SERET_FLAG";
- case ARMISD::INTRET_FLAG: return "ARMISD::INTRET_FLAG";
- case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD";
- case ARMISD::CMP: return "ARMISD::CMP";
- case ARMISD::CMN: return "ARMISD::CMN";
- case ARMISD::CMPZ: return "ARMISD::CMPZ";
- case ARMISD::CMPFP: return "ARMISD::CMPFP";
- case ARMISD::CMPFPE: return "ARMISD::CMPFPE";
- case ARMISD::CMPFPw0: return "ARMISD::CMPFPw0";
- case ARMISD::CMPFPEw0: return "ARMISD::CMPFPEw0";
- case ARMISD::BCC_i64: return "ARMISD::BCC_i64";
- case ARMISD::FMSTAT: return "ARMISD::FMSTAT";
-
- case ARMISD::CMOV: return "ARMISD::CMOV";
- case ARMISD::SUBS: return "ARMISD::SUBS";
-
- case ARMISD::SSAT: return "ARMISD::SSAT";
- case ARMISD::USAT: return "ARMISD::USAT";
-
- case ARMISD::ASRL: return "ARMISD::ASRL";
- case ARMISD::LSRL: return "ARMISD::LSRL";
- case ARMISD::LSLL: return "ARMISD::LSLL";
-
- case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG";
- case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG";
- case ARMISD::RRX: return "ARMISD::RRX";
-
- case ARMISD::ADDC: return "ARMISD::ADDC";
- case ARMISD::ADDE: return "ARMISD::ADDE";
- case ARMISD::SUBC: return "ARMISD::SUBC";
- case ARMISD::SUBE: return "ARMISD::SUBE";
- case ARMISD::LSLS: return "ARMISD::LSLS";
-
- case ARMISD::VMOVRRD: return "ARMISD::VMOVRRD";
- case ARMISD::VMOVDRR: return "ARMISD::VMOVDRR";
- case ARMISD::VMOVhr: return "ARMISD::VMOVhr";
- case ARMISD::VMOVrh: return "ARMISD::VMOVrh";
- case ARMISD::VMOVSR: return "ARMISD::VMOVSR";
-
- case ARMISD::EH_SJLJ_SETJMP: return "ARMISD::EH_SJLJ_SETJMP";
- case ARMISD::EH_SJLJ_LONGJMP: return "ARMISD::EH_SJLJ_LONGJMP";
- case ARMISD::EH_SJLJ_SETUP_DISPATCH: return "ARMISD::EH_SJLJ_SETUP_DISPATCH";
-
- case ARMISD::TC_RETURN: return "ARMISD::TC_RETURN";
-
- case ARMISD::THREAD_POINTER:return "ARMISD::THREAD_POINTER";
-
- case ARMISD::DYN_ALLOC: return "ARMISD::DYN_ALLOC";
-
- case ARMISD::MEMBARRIER_MCR: return "ARMISD::MEMBARRIER_MCR";
-
- case ARMISD::PRELOAD: return "ARMISD::PRELOAD";
-
- case ARMISD::LDRD: return "ARMISD::LDRD";
- case ARMISD::STRD: return "ARMISD::STRD";
-
- case ARMISD::WIN__CHKSTK: return "ARMISD::WIN__CHKSTK";
- case ARMISD::WIN__DBZCHK: return "ARMISD::WIN__DBZCHK";
-
- case ARMISD::PREDICATE_CAST: return "ARMISD::PREDICATE_CAST";
- case ARMISD::VECTOR_REG_CAST: return "ARMISD::VECTOR_REG_CAST";
- case ARMISD::VCMP: return "ARMISD::VCMP";
- case ARMISD::VCMPZ: return "ARMISD::VCMPZ";
- case ARMISD::VTST: return "ARMISD::VTST";
-
- case ARMISD::VSHLs: return "ARMISD::VSHLs";
- case ARMISD::VSHLu: return "ARMISD::VSHLu";
- case ARMISD::VSHLIMM: return "ARMISD::VSHLIMM";
- case ARMISD::VSHRsIMM: return "ARMISD::VSHRsIMM";
- case ARMISD::VSHRuIMM: return "ARMISD::VSHRuIMM";
- case ARMISD::VRSHRsIMM: return "ARMISD::VRSHRsIMM";
- case ARMISD::VRSHRuIMM: return "ARMISD::VRSHRuIMM";
- case ARMISD::VRSHRNIMM: return "ARMISD::VRSHRNIMM";
- case ARMISD::VQSHLsIMM: return "ARMISD::VQSHLsIMM";
- case ARMISD::VQSHLuIMM: return "ARMISD::VQSHLuIMM";
- case ARMISD::VQSHLsuIMM: return "ARMISD::VQSHLsuIMM";
- case ARMISD::VQSHRNsIMM: return "ARMISD::VQSHRNsIMM";
- case ARMISD::VQSHRNuIMM: return "ARMISD::VQSHRNuIMM";
- case ARMISD::VQSHRNsuIMM: return "ARMISD::VQSHRNsuIMM";
- case ARMISD::VQRSHRNsIMM: return "ARMISD::VQRSHRNsIMM";
- case ARMISD::VQRSHRNuIMM: return "ARMISD::VQRSHRNuIMM";
- case ARMISD::VQRSHRNsuIMM: return "ARMISD::VQRSHRNsuIMM";
- case ARMISD::VSLIIMM: return "ARMISD::VSLIIMM";
- case ARMISD::VSRIIMM: return "ARMISD::VSRIIMM";
- case ARMISD::VGETLANEu: return "ARMISD::VGETLANEu";
- case ARMISD::VGETLANEs: return "ARMISD::VGETLANEs";
- case ARMISD::VMOVIMM: return "ARMISD::VMOVIMM";
- case ARMISD::VMVNIMM: return "ARMISD::VMVNIMM";
- case ARMISD::VMOVFPIMM: return "ARMISD::VMOVFPIMM";
- case ARMISD::VDUP: return "ARMISD::VDUP";
- case ARMISD::VDUPLANE: return "ARMISD::VDUPLANE";
- case ARMISD::VEXT: return "ARMISD::VEXT";
- case ARMISD::VREV64: return "ARMISD::VREV64";
- case ARMISD::VREV32: return "ARMISD::VREV32";
- case ARMISD::VREV16: return "ARMISD::VREV16";
- case ARMISD::VZIP: return "ARMISD::VZIP";
- case ARMISD::VUZP: return "ARMISD::VUZP";
- case ARMISD::VTRN: return "ARMISD::VTRN";
- case ARMISD::VTBL1: return "ARMISD::VTBL1";
- case ARMISD::VTBL2: return "ARMISD::VTBL2";
- case ARMISD::VMOVN: return "ARMISD::VMOVN";
- case ARMISD::VQMOVNs: return "ARMISD::VQMOVNs";
- case ARMISD::VQMOVNu: return "ARMISD::VQMOVNu";
- case ARMISD::VCVTN: return "ARMISD::VCVTN";
- case ARMISD::VCVTL: return "ARMISD::VCVTL";
- case ARMISD::VMULLs: return "ARMISD::VMULLs";
- case ARMISD::VMULLu: return "ARMISD::VMULLu";
- case ARMISD::VQDMULH: return "ARMISD::VQDMULH";
- case ARMISD::VADDVs: return "ARMISD::VADDVs";
- case ARMISD::VADDVu: return "ARMISD::VADDVu";
- case ARMISD::VADDVps: return "ARMISD::VADDVps";
- case ARMISD::VADDVpu: return "ARMISD::VADDVpu";
- case ARMISD::VADDLVs: return "ARMISD::VADDLVs";
- case ARMISD::VADDLVu: return "ARMISD::VADDLVu";
- case ARMISD::VADDLVAs: return "ARMISD::VADDLVAs";
- case ARMISD::VADDLVAu: return "ARMISD::VADDLVAu";
- case ARMISD::VADDLVps: return "ARMISD::VADDLVps";
- case ARMISD::VADDLVpu: return "ARMISD::VADDLVpu";
- case ARMISD::VADDLVAps: return "ARMISD::VADDLVAps";
- case ARMISD::VADDLVApu: return "ARMISD::VADDLVApu";
- case ARMISD::VMLAVs: return "ARMISD::VMLAVs";
- case ARMISD::VMLAVu: return "ARMISD::VMLAVu";
- case ARMISD::VMLAVps: return "ARMISD::VMLAVps";
- case ARMISD::VMLAVpu: return "ARMISD::VMLAVpu";
- case ARMISD::VMLALVs: return "ARMISD::VMLALVs";
- case ARMISD::VMLALVu: return "ARMISD::VMLALVu";
- case ARMISD::VMLALVps: return "ARMISD::VMLALVps";
- case ARMISD::VMLALVpu: return "ARMISD::VMLALVpu";
- case ARMISD::VMLALVAs: return "ARMISD::VMLALVAs";
- case ARMISD::VMLALVAu: return "ARMISD::VMLALVAu";
- case ARMISD::VMLALVAps: return "ARMISD::VMLALVAps";
- case ARMISD::VMLALVApu: return "ARMISD::VMLALVApu";
- case ARMISD::VMINVu: return "ARMISD::VMINVu";
- case ARMISD::VMINVs: return "ARMISD::VMINVs";
- case ARMISD::VMAXVu: return "ARMISD::VMAXVu";
- case ARMISD::VMAXVs: return "ARMISD::VMAXVs";
- case ARMISD::UMAAL: return "ARMISD::UMAAL";
- case ARMISD::UMLAL: return "ARMISD::UMLAL";
- case ARMISD::SMLAL: return "ARMISD::SMLAL";
- case ARMISD::SMLALBB: return "ARMISD::SMLALBB";
- case ARMISD::SMLALBT: return "ARMISD::SMLALBT";
- case ARMISD::SMLALTB: return "ARMISD::SMLALTB";
- case ARMISD::SMLALTT: return "ARMISD::SMLALTT";
- case ARMISD::SMULWB: return "ARMISD::SMULWB";
- case ARMISD::SMULWT: return "ARMISD::SMULWT";
- case ARMISD::SMLALD: return "ARMISD::SMLALD";
- case ARMISD::SMLALDX: return "ARMISD::SMLALDX";
- case ARMISD::SMLSLD: return "ARMISD::SMLSLD";
- case ARMISD::SMLSLDX: return "ARMISD::SMLSLDX";
- case ARMISD::SMMLAR: return "ARMISD::SMMLAR";
- case ARMISD::SMMLSR: return "ARMISD::SMMLSR";
- case ARMISD::QADD16b: return "ARMISD::QADD16b";
- case ARMISD::QSUB16b: return "ARMISD::QSUB16b";
- case ARMISD::QADD8b: return "ARMISD::QADD8b";
- case ARMISD::QSUB8b: return "ARMISD::QSUB8b";
- case ARMISD::BUILD_VECTOR: return "ARMISD::BUILD_VECTOR";
- case ARMISD::BFI: return "ARMISD::BFI";
- case ARMISD::VORRIMM: return "ARMISD::VORRIMM";
- case ARMISD::VBICIMM: return "ARMISD::VBICIMM";
- case ARMISD::VBSP: return "ARMISD::VBSP";
- case ARMISD::MEMCPY: return "ARMISD::MEMCPY";
- case ARMISD::VLD1DUP: return "ARMISD::VLD1DUP";
- case ARMISD::VLD2DUP: return "ARMISD::VLD2DUP";
- case ARMISD::VLD3DUP: return "ARMISD::VLD3DUP";
- case ARMISD::VLD4DUP: return "ARMISD::VLD4DUP";
- case ARMISD::VLD1_UPD: return "ARMISD::VLD1_UPD";
- case ARMISD::VLD2_UPD: return "ARMISD::VLD2_UPD";
- case ARMISD::VLD3_UPD: return "ARMISD::VLD3_UPD";
- case ARMISD::VLD4_UPD: return "ARMISD::VLD4_UPD";
- case ARMISD::VLD2LN_UPD: return "ARMISD::VLD2LN_UPD";
- case ARMISD::VLD3LN_UPD: return "ARMISD::VLD3LN_UPD";
- case ARMISD::VLD4LN_UPD: return "ARMISD::VLD4LN_UPD";
- case ARMISD::VLD1DUP_UPD: return "ARMISD::VLD1DUP_UPD";
- case ARMISD::VLD2DUP_UPD: return "ARMISD::VLD2DUP_UPD";
- case ARMISD::VLD3DUP_UPD: return "ARMISD::VLD3DUP_UPD";
- case ARMISD::VLD4DUP_UPD: return "ARMISD::VLD4DUP_UPD";
- case ARMISD::VST1_UPD: return "ARMISD::VST1_UPD";
- case ARMISD::VST2_UPD: return "ARMISD::VST2_UPD";
- case ARMISD::VST3_UPD: return "ARMISD::VST3_UPD";
- case ARMISD::VST4_UPD: return "ARMISD::VST4_UPD";
- case ARMISD::VST2LN_UPD: return "ARMISD::VST2LN_UPD";
- case ARMISD::VST3LN_UPD: return "ARMISD::VST3LN_UPD";
- case ARMISD::VST4LN_UPD: return "ARMISD::VST4LN_UPD";
- case ARMISD::WLS: return "ARMISD::WLS";
- case ARMISD::LE: return "ARMISD::LE";
- case ARMISD::LOOP_DEC: return "ARMISD::LOOP_DEC";
- case ARMISD::CSINV: return "ARMISD::CSINV";
- case ARMISD::CSNEG: return "ARMISD::CSNEG";
- case ARMISD::CSINC: return "ARMISD::CSINC";
+ case ARMISD::FIRST_NUMBER:
+ break;
+ MAKE_CASE(ARMISD::Wrapper)
+ MAKE_CASE(ARMISD::WrapperPIC)
+ MAKE_CASE(ARMISD::WrapperJT)
+ MAKE_CASE(ARMISD::COPY_STRUCT_BYVAL)
+ MAKE_CASE(ARMISD::CALL)
+ MAKE_CASE(ARMISD::CALL_PRED)
+ MAKE_CASE(ARMISD::CALL_NOLINK)
+ MAKE_CASE(ARMISD::tSECALL)
+ MAKE_CASE(ARMISD::BRCOND)
+ MAKE_CASE(ARMISD::BR_JT)
+ MAKE_CASE(ARMISD::BR2_JT)
+ MAKE_CASE(ARMISD::RET_FLAG)
+ MAKE_CASE(ARMISD::SERET_FLAG)
+ MAKE_CASE(ARMISD::INTRET_FLAG)
+ MAKE_CASE(ARMISD::PIC_ADD)
+ MAKE_CASE(ARMISD::CMP)
+ MAKE_CASE(ARMISD::CMN)
+ MAKE_CASE(ARMISD::CMPZ)
+ MAKE_CASE(ARMISD::CMPFP)
+ MAKE_CASE(ARMISD::CMPFPE)
+ MAKE_CASE(ARMISD::CMPFPw0)
+ MAKE_CASE(ARMISD::CMPFPEw0)
+ MAKE_CASE(ARMISD::BCC_i64)
+ MAKE_CASE(ARMISD::FMSTAT)
+ MAKE_CASE(ARMISD::CMOV)
+ MAKE_CASE(ARMISD::SUBS)
+ MAKE_CASE(ARMISD::SSAT)
+ MAKE_CASE(ARMISD::USAT)
+ MAKE_CASE(ARMISD::ASRL)
+ MAKE_CASE(ARMISD::LSRL)
+ MAKE_CASE(ARMISD::LSLL)
+ MAKE_CASE(ARMISD::SRL_FLAG)
+ MAKE_CASE(ARMISD::SRA_FLAG)
+ MAKE_CASE(ARMISD::RRX)
+ MAKE_CASE(ARMISD::ADDC)
+ MAKE_CASE(ARMISD::ADDE)
+ MAKE_CASE(ARMISD::SUBC)
+ MAKE_CASE(ARMISD::SUBE)
+ MAKE_CASE(ARMISD::LSLS)
+ MAKE_CASE(ARMISD::VMOVRRD)
+ MAKE_CASE(ARMISD::VMOVDRR)
+ MAKE_CASE(ARMISD::VMOVhr)
+ MAKE_CASE(ARMISD::VMOVrh)
+ MAKE_CASE(ARMISD::VMOVSR)
+ MAKE_CASE(ARMISD::EH_SJLJ_SETJMP)
+ MAKE_CASE(ARMISD::EH_SJLJ_LONGJMP)
+ MAKE_CASE(ARMISD::EH_SJLJ_SETUP_DISPATCH)
+ MAKE_CASE(ARMISD::TC_RETURN)
+ MAKE_CASE(ARMISD::THREAD_POINTER)
+ MAKE_CASE(ARMISD::DYN_ALLOC)
+ MAKE_CASE(ARMISD::MEMBARRIER_MCR)
+ MAKE_CASE(ARMISD::PRELOAD)
+ MAKE_CASE(ARMISD::LDRD)
+ MAKE_CASE(ARMISD::STRD)
+ MAKE_CASE(ARMISD::WIN__CHKSTK)
+ MAKE_CASE(ARMISD::WIN__DBZCHK)
+ MAKE_CASE(ARMISD::PREDICATE_CAST)
+ MAKE_CASE(ARMISD::VECTOR_REG_CAST)
+ MAKE_CASE(ARMISD::MVESEXT)
+ MAKE_CASE(ARMISD::MVEZEXT)
+ MAKE_CASE(ARMISD::MVETRUNC)
+ MAKE_CASE(ARMISD::VCMP)
+ MAKE_CASE(ARMISD::VCMPZ)
+ MAKE_CASE(ARMISD::VTST)
+ MAKE_CASE(ARMISD::VSHLs)
+ MAKE_CASE(ARMISD::VSHLu)
+ MAKE_CASE(ARMISD::VSHLIMM)
+ MAKE_CASE(ARMISD::VSHRsIMM)
+ MAKE_CASE(ARMISD::VSHRuIMM)
+ MAKE_CASE(ARMISD::VRSHRsIMM)
+ MAKE_CASE(ARMISD::VRSHRuIMM)
+ MAKE_CASE(ARMISD::VRSHRNIMM)
+ MAKE_CASE(ARMISD::VQSHLsIMM)
+ MAKE_CASE(ARMISD::VQSHLuIMM)
+ MAKE_CASE(ARMISD::VQSHLsuIMM)
+ MAKE_CASE(ARMISD::VQSHRNsIMM)
+ MAKE_CASE(ARMISD::VQSHRNuIMM)
+ MAKE_CASE(ARMISD::VQSHRNsuIMM)
+ MAKE_CASE(ARMISD::VQRSHRNsIMM)
+ MAKE_CASE(ARMISD::VQRSHRNuIMM)
+ MAKE_CASE(ARMISD::VQRSHRNsuIMM)
+ MAKE_CASE(ARMISD::VSLIIMM)
+ MAKE_CASE(ARMISD::VSRIIMM)
+ MAKE_CASE(ARMISD::VGETLANEu)
+ MAKE_CASE(ARMISD::VGETLANEs)
+ MAKE_CASE(ARMISD::VMOVIMM)
+ MAKE_CASE(ARMISD::VMVNIMM)
+ MAKE_CASE(ARMISD::VMOVFPIMM)
+ MAKE_CASE(ARMISD::VDUP)
+ MAKE_CASE(ARMISD::VDUPLANE)
+ MAKE_CASE(ARMISD::VEXT)
+ MAKE_CASE(ARMISD::VREV64)
+ MAKE_CASE(ARMISD::VREV32)
+ MAKE_CASE(ARMISD::VREV16)
+ MAKE_CASE(ARMISD::VZIP)
+ MAKE_CASE(ARMISD::VUZP)
+ MAKE_CASE(ARMISD::VTRN)
+ MAKE_CASE(ARMISD::VTBL1)
+ MAKE_CASE(ARMISD::VTBL2)
+ MAKE_CASE(ARMISD::VMOVN)
+ MAKE_CASE(ARMISD::VQMOVNs)
+ MAKE_CASE(ARMISD::VQMOVNu)
+ MAKE_CASE(ARMISD::VCVTN)
+ MAKE_CASE(ARMISD::VCVTL)
+ MAKE_CASE(ARMISD::VIDUP)
+ MAKE_CASE(ARMISD::VMULLs)
+ MAKE_CASE(ARMISD::VMULLu)
+ MAKE_CASE(ARMISD::VQDMULH)
+ MAKE_CASE(ARMISD::VADDVs)
+ MAKE_CASE(ARMISD::VADDVu)
+ MAKE_CASE(ARMISD::VADDVps)
+ MAKE_CASE(ARMISD::VADDVpu)
+ MAKE_CASE(ARMISD::VADDLVs)
+ MAKE_CASE(ARMISD::VADDLVu)
+ MAKE_CASE(ARMISD::VADDLVAs)
+ MAKE_CASE(ARMISD::VADDLVAu)
+ MAKE_CASE(ARMISD::VADDLVps)
+ MAKE_CASE(ARMISD::VADDLVpu)
+ MAKE_CASE(ARMISD::VADDLVAps)
+ MAKE_CASE(ARMISD::VADDLVApu)
+ MAKE_CASE(ARMISD::VMLAVs)
+ MAKE_CASE(ARMISD::VMLAVu)
+ MAKE_CASE(ARMISD::VMLAVps)
+ MAKE_CASE(ARMISD::VMLAVpu)
+ MAKE_CASE(ARMISD::VMLALVs)
+ MAKE_CASE(ARMISD::VMLALVu)
+ MAKE_CASE(ARMISD::VMLALVps)
+ MAKE_CASE(ARMISD::VMLALVpu)
+ MAKE_CASE(ARMISD::VMLALVAs)
+ MAKE_CASE(ARMISD::VMLALVAu)
+ MAKE_CASE(ARMISD::VMLALVAps)
+ MAKE_CASE(ARMISD::VMLALVApu)
+ MAKE_CASE(ARMISD::VMINVu)
+ MAKE_CASE(ARMISD::VMINVs)
+ MAKE_CASE(ARMISD::VMAXVu)
+ MAKE_CASE(ARMISD::VMAXVs)
+ MAKE_CASE(ARMISD::UMAAL)
+ MAKE_CASE(ARMISD::UMLAL)
+ MAKE_CASE(ARMISD::SMLAL)
+ MAKE_CASE(ARMISD::SMLALBB)
+ MAKE_CASE(ARMISD::SMLALBT)
+ MAKE_CASE(ARMISD::SMLALTB)
+ MAKE_CASE(ARMISD::SMLALTT)
+ MAKE_CASE(ARMISD::SMULWB)
+ MAKE_CASE(ARMISD::SMULWT)
+ MAKE_CASE(ARMISD::SMLALD)
+ MAKE_CASE(ARMISD::SMLALDX)
+ MAKE_CASE(ARMISD::SMLSLD)
+ MAKE_CASE(ARMISD::SMLSLDX)
+ MAKE_CASE(ARMISD::SMMLAR)
+ MAKE_CASE(ARMISD::SMMLSR)
+ MAKE_CASE(ARMISD::QADD16b)
+ MAKE_CASE(ARMISD::QSUB16b)
+ MAKE_CASE(ARMISD::QADD8b)
+ MAKE_CASE(ARMISD::QSUB8b)
+ MAKE_CASE(ARMISD::UQADD16b)
+ MAKE_CASE(ARMISD::UQSUB16b)
+ MAKE_CASE(ARMISD::UQADD8b)
+ MAKE_CASE(ARMISD::UQSUB8b)
+ MAKE_CASE(ARMISD::BUILD_VECTOR)
+ MAKE_CASE(ARMISD::BFI)
+ MAKE_CASE(ARMISD::VORRIMM)
+ MAKE_CASE(ARMISD::VBICIMM)
+ MAKE_CASE(ARMISD::VBSP)
+ MAKE_CASE(ARMISD::MEMCPY)
+ MAKE_CASE(ARMISD::VLD1DUP)
+ MAKE_CASE(ARMISD::VLD2DUP)
+ MAKE_CASE(ARMISD::VLD3DUP)
+ MAKE_CASE(ARMISD::VLD4DUP)
+ MAKE_CASE(ARMISD::VLD1_UPD)
+ MAKE_CASE(ARMISD::VLD2_UPD)
+ MAKE_CASE(ARMISD::VLD3_UPD)
+ MAKE_CASE(ARMISD::VLD4_UPD)
+ MAKE_CASE(ARMISD::VLD1x2_UPD)
+ MAKE_CASE(ARMISD::VLD1x3_UPD)
+ MAKE_CASE(ARMISD::VLD1x4_UPD)
+ MAKE_CASE(ARMISD::VLD2LN_UPD)
+ MAKE_CASE(ARMISD::VLD3LN_UPD)
+ MAKE_CASE(ARMISD::VLD4LN_UPD)
+ MAKE_CASE(ARMISD::VLD1DUP_UPD)
+ MAKE_CASE(ARMISD::VLD2DUP_UPD)
+ MAKE_CASE(ARMISD::VLD3DUP_UPD)
+ MAKE_CASE(ARMISD::VLD4DUP_UPD)
+ MAKE_CASE(ARMISD::VST1_UPD)
+ MAKE_CASE(ARMISD::VST2_UPD)
+ MAKE_CASE(ARMISD::VST3_UPD)
+ MAKE_CASE(ARMISD::VST4_UPD)
+ MAKE_CASE(ARMISD::VST1x2_UPD)
+ MAKE_CASE(ARMISD::VST1x3_UPD)
+ MAKE_CASE(ARMISD::VST1x4_UPD)
+ MAKE_CASE(ARMISD::VST2LN_UPD)
+ MAKE_CASE(ARMISD::VST3LN_UPD)
+ MAKE_CASE(ARMISD::VST4LN_UPD)
+ MAKE_CASE(ARMISD::WLS)
+ MAKE_CASE(ARMISD::WLSSETUP)
+ MAKE_CASE(ARMISD::LE)
+ MAKE_CASE(ARMISD::LOOP_DEC)
+ MAKE_CASE(ARMISD::CSINV)
+ MAKE_CASE(ARMISD::CSNEG)
+ MAKE_CASE(ARMISD::CSINC)
+ MAKE_CASE(ARMISD::MEMCPYLOOP)
+ MAKE_CASE(ARMISD::MEMSETLOOP)
+#undef MAKE_CASE
}
return nullptr;
}
@@ -1817,8 +1835,9 @@ EVT ARMTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &,
return getPointerTy(DL);
// MVE has a predicate register.
- if (Subtarget->hasMVEIntegerOps() &&
- (VT == MVT::v4i32 || VT == MVT::v8i16 || VT == MVT::v16i8))
+ if ((Subtarget->hasMVEIntegerOps() &&
+ (VT == MVT::v4i32 || VT == MVT::v8i16 || VT == MVT::v16i8)) ||
+ (Subtarget->hasMVEFloatOps() && (VT == MVT::v4f32 || VT == MVT::v8f16)))
return MVT::getVectorVT(MVT::i1, VT.getVectorElementCount());
return VT.changeVectorElementTypeToInteger();
}
@@ -1998,8 +2017,10 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC,
return CallingConv::PreserveMost;
case CallingConv::ARM_AAPCS_VFP:
case CallingConv::Swift:
+ case CallingConv::SwiftTail:
return isVarArg ? CallingConv::ARM_AAPCS : CallingConv::ARM_AAPCS_VFP;
case CallingConv::C:
+ case CallingConv::Tail:
if (!Subtarget->isAAPCS_ABI())
return CallingConv::ARM_APCS;
else if (Subtarget->hasVFP2Base() && !Subtarget->isThumb1Only() &&
@@ -2176,19 +2197,31 @@ SDValue ARMTargetLowering::LowerCallResult(
return Chain;
}
-/// LowerMemOpCallTo - Store the argument to the stack.
-SDValue ARMTargetLowering::LowerMemOpCallTo(SDValue Chain, SDValue StackPtr,
- SDValue Arg, const SDLoc &dl,
- SelectionDAG &DAG,
- const CCValAssign &VA,
- ISD::ArgFlagsTy Flags) const {
- unsigned LocMemOffset = VA.getLocMemOffset();
- SDValue PtrOff = DAG.getIntPtrConstant(LocMemOffset, dl);
- PtrOff = DAG.getNode(ISD::ADD, dl, getPointerTy(DAG.getDataLayout()),
- StackPtr, PtrOff);
- return DAG.getStore(
- Chain, dl, Arg, PtrOff,
- MachinePointerInfo::getStack(DAG.getMachineFunction(), LocMemOffset));
+std::pair<SDValue, MachinePointerInfo> ARMTargetLowering::computeAddrForCallArg(
+ const SDLoc &dl, SelectionDAG &DAG, const CCValAssign &VA, SDValue StackPtr,
+ bool IsTailCall, int SPDiff) const {
+ SDValue DstAddr;
+ MachinePointerInfo DstInfo;
+ int32_t Offset = VA.getLocMemOffset();
+ MachineFunction &MF = DAG.getMachineFunction();
+
+ if (IsTailCall) {
+ Offset += SPDiff;
+ auto PtrVT = getPointerTy(DAG.getDataLayout());
+ int Size = VA.getLocVT().getFixedSizeInBits() / 8;
+ int FI = MF.getFrameInfo().CreateFixedObject(Size, Offset, true);
+ DstAddr = DAG.getFrameIndex(FI, PtrVT);
+ DstInfo =
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI);
+ } else {
+ SDValue PtrOff = DAG.getIntPtrConstant(Offset, dl);
+ DstAddr = DAG.getNode(ISD::ADD, dl, getPointerTy(DAG.getDataLayout()),
+ StackPtr, PtrOff);
+ DstInfo =
+ MachinePointerInfo::getStack(DAG.getMachineFunction(), Offset);
+ }
+
+ return std::make_pair(DstAddr, DstInfo);
}
void ARMTargetLowering::PassF64ArgInRegs(const SDLoc &dl, SelectionDAG &DAG,
@@ -2197,7 +2230,8 @@ void ARMTargetLowering::PassF64ArgInRegs(const SDLoc &dl, SelectionDAG &DAG,
CCValAssign &VA, CCValAssign &NextVA,
SDValue &StackPtr,
SmallVectorImpl<SDValue> &MemOpChains,
- ISD::ArgFlagsTy Flags) const {
+ bool IsTailCall,
+ int SPDiff) const {
SDValue fmrrd = DAG.getNode(ARMISD::VMOVRRD, dl,
DAG.getVTList(MVT::i32, MVT::i32), Arg);
unsigned id = Subtarget->isLittle() ? 0 : 1;
@@ -2211,12 +2245,20 @@ void ARMTargetLowering::PassF64ArgInRegs(const SDLoc &dl, SelectionDAG &DAG,
StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP,
getPointerTy(DAG.getDataLayout()));
- MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, fmrrd.getValue(1-id),
- dl, DAG, NextVA,
- Flags));
+ SDValue DstAddr;
+ MachinePointerInfo DstInfo;
+ std::tie(DstAddr, DstInfo) =
+ computeAddrForCallArg(dl, DAG, NextVA, StackPtr, IsTailCall, SPDiff);
+ MemOpChains.push_back(
+ DAG.getStore(Chain, dl, fmrrd.getValue(1 - id), DstAddr, DstInfo));
}
}
+static bool canGuaranteeTCO(CallingConv::ID CC, bool GuaranteeTailCalls) {
+ return (CC == CallingConv::Fast && GuaranteeTailCalls) ||
+ CC == CallingConv::Tail || CC == CallingConv::SwiftTail;
+}
+
/// LowerCall - Lowering a call into a callseq_start <-
/// ARMISD:CALL <- callseq_end chain. Also add input and output parameter
/// nodes.
@@ -2241,6 +2283,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
bool isStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet();
bool isThisReturn = false;
bool isCmseNSCall = false;
+ bool isSibCall = false;
bool PreferIndirect = false;
// Determine whether this is a non-secure function call.
@@ -2277,15 +2320,20 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
Callee, CallConv, isVarArg, isStructRet,
MF.getFunction().hasStructRetAttr(), Outs, OutVals, Ins, DAG,
PreferIndirect);
- if (!isTailCall && CLI.CB && CLI.CB->isMustTailCall())
- report_fatal_error("failed to perform tail call elimination on a call "
- "site marked musttail");
+
+ if (isTailCall && !getTargetMachine().Options.GuaranteedTailCallOpt &&
+ CallConv != CallingConv::Tail && CallConv != CallingConv::SwiftTail)
+ isSibCall = true;
+
// We don't support GuaranteedTailCallOpt for ARM, only automatically
// detected sibcalls.
if (isTailCall)
++NumTailCalls;
}
+ if (!isTailCall && CLI.CB && CLI.CB->isMustTailCall())
+ report_fatal_error("failed to perform tail call elimination on a call "
+ "site marked musttail");
// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), ArgLocs,
@@ -2295,13 +2343,40 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// Get a count of how many bytes are to be pushed on the stack.
unsigned NumBytes = CCInfo.getNextStackOffset();
- if (isTailCall) {
- // For tail calls, memory operands are available in our caller's stack.
+ // SPDiff is the byte offset of the call's argument area from the callee's.
+ // Stores to callee stack arguments will be placed in FixedStackSlots offset
+ // by this amount for a tail call. In a sibling call it must be 0 because the
+ // caller will deallocate the entire stack and the callee still expects its
+ // arguments to begin at SP+0. Completely unused for non-tail calls.
+ int SPDiff = 0;
+
+ if (isTailCall && !isSibCall) {
+ auto FuncInfo = MF.getInfo<ARMFunctionInfo>();
+ unsigned NumReusableBytes = FuncInfo->getArgumentStackSize();
+
+ // Since callee will pop argument stack as a tail call, we must keep the
+ // popped size 16-byte aligned.
+ Align StackAlign = DAG.getDataLayout().getStackAlignment();
+ NumBytes = alignTo(NumBytes, StackAlign);
+
+ // SPDiff will be negative if this tail call requires more space than we
+ // would automatically have in our incoming argument space. Positive if we
+ // can actually shrink the stack.
+ SPDiff = NumReusableBytes - NumBytes;
+
+ // If this call requires more stack than we have available from
+ // LowerFormalArguments, tell FrameLowering to reserve space for it.
+ if (SPDiff < 0 && AFI->getArgRegsSaveSize() < (unsigned)-SPDiff)
+ AFI->setArgRegsSaveSize(-SPDiff);
+ }
+
+ if (isSibCall) {
+ // For sibling tail calls, memory operands are available in our caller's stack.
NumBytes = 0;
} else {
// Adjust the stack pointer for the new arguments...
// These operations are automatically eliminated by the prolog/epilog pass
- Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, dl);
+ Chain = DAG.getCALLSEQ_START(Chain, isTailCall ? 0 : NumBytes, 0, dl);
}
SDValue StackPtr =
@@ -2310,6 +2385,13 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
RegsToPassVector RegsToPass;
SmallVector<SDValue, 8> MemOpChains;
+ // During a tail call, stores to the argument area must happen after all of
+ // the function's incoming arguments have been loaded because they may alias.
+ // This is done by folding in a TokenFactor from LowerFormalArguments, but
+ // there's no point in doing so repeatedly so this tracks whether that's
+ // happened yet.
+ bool AfterFormalArgLoads = false;
+
// 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();
@@ -2338,6 +2420,11 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
break;
}
+ if (isTailCall && VA.isMemLoc() && !AfterFormalArgLoads) {
+ Chain = DAG.getStackArgumentTokenFactor(Chain);
+ AfterFormalArgLoads = true;
+ }
+
// f16 arguments have their size extended to 4 bytes and passed as if they
// had been copied to the LSBs of a 32-bit register.
// For that, it's passed extended to i32 (soft ABI) or to f32 (hard ABI)
@@ -2367,21 +2454,23 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
DAG.getConstant(1, dl, MVT::i32));
PassF64ArgInRegs(dl, DAG, Chain, Op0, RegsToPass, VA, ArgLocs[++i],
- StackPtr, MemOpChains, Flags);
+ StackPtr, MemOpChains, isTailCall, SPDiff);
VA = ArgLocs[++i]; // skip ahead to next loc
if (VA.isRegLoc()) {
PassF64ArgInRegs(dl, DAG, Chain, Op1, RegsToPass, VA, ArgLocs[++i],
- StackPtr, MemOpChains, Flags);
+ StackPtr, MemOpChains, isTailCall, SPDiff);
} else {
assert(VA.isMemLoc());
-
- MemOpChains.push_back(
- LowerMemOpCallTo(Chain, StackPtr, Op1, dl, DAG, VA, Flags));
+ SDValue DstAddr;
+ MachinePointerInfo DstInfo;
+ std::tie(DstAddr, DstInfo) =
+ computeAddrForCallArg(dl, DAG, VA, StackPtr, isTailCall, SPDiff);
+ MemOpChains.push_back(DAG.getStore(Chain, dl, Op1, DstAddr, DstInfo));
}
} else if (VA.needsCustom() && VA.getLocVT() == MVT::f64) {
PassF64ArgInRegs(dl, DAG, Chain, Arg, RegsToPass, VA, ArgLocs[++i],
- StackPtr, MemOpChains, Flags);
+ StackPtr, MemOpChains, isTailCall, SPDiff);
} else if (VA.isRegLoc()) {
if (realArgIdx == 0 && Flags.isReturned() && !Flags.isSwiftSelf() &&
Outs[0].VT == MVT::i32) {
@@ -2431,9 +2520,10 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
if (Flags.getByValSize() > 4*offset) {
auto PtrVT = getPointerTy(DAG.getDataLayout());
- unsigned LocMemOffset = VA.getLocMemOffset();
- SDValue StkPtrOff = DAG.getIntPtrConstant(LocMemOffset, dl);
- SDValue Dst = DAG.getNode(ISD::ADD, dl, PtrVT, StackPtr, StkPtrOff);
+ SDValue Dst;
+ MachinePointerInfo DstInfo;
+ std::tie(Dst, DstInfo) =
+ computeAddrForCallArg(dl, DAG, VA, StackPtr, isTailCall, SPDiff);
SDValue SrcOffset = DAG.getIntPtrConstant(4*offset, dl);
SDValue Src = DAG.getNode(ISD::ADD, dl, PtrVT, Arg, SrcOffset);
SDValue SizeNode = DAG.getConstant(Flags.getByValSize() - 4*offset, dl,
@@ -2446,11 +2536,15 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
MemOpChains.push_back(DAG.getNode(ARMISD::COPY_STRUCT_BYVAL, dl, VTs,
Ops));
}
- } else if (!isTailCall) {
+ } else {
assert(VA.isMemLoc());
+ SDValue DstAddr;
+ MachinePointerInfo DstInfo;
+ std::tie(DstAddr, DstInfo) =
+ computeAddrForCallArg(dl, DAG, VA, StackPtr, isTailCall, SPDiff);
- MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, Arg,
- dl, DAG, VA, Flags));
+ SDValue Store = DAG.getStore(Chain, dl, Arg, DstAddr, DstInfo);
+ MemOpChains.push_back(Store);
}
}
@@ -2614,10 +2708,24 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
CallOpc = isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL;
}
+ // We don't usually want to end the call-sequence here because we would tidy
+ // the frame up *after* the call, however in the ABI-changing tail-call case
+ // we've carefully laid out the parameters so that when sp is reset they'll be
+ // in the correct location.
+ if (isTailCall && !isSibCall) {
+ Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(0, dl, true),
+ DAG.getIntPtrConstant(0, dl, true), InFlag, dl);
+ InFlag = Chain.getValue(1);
+ }
+
std::vector<SDValue> Ops;
Ops.push_back(Chain);
Ops.push_back(Callee);
+ if (isTailCall) {
+ Ops.push_back(DAG.getTargetConstant(SPDiff, dl, MVT::i32));
+ }
+
// 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)
@@ -2662,8 +2770,16 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
InFlag = Chain.getValue(1);
DAG.addCallSiteInfo(Chain.getNode(), std::move(CSInfo));
+ // If we're guaranteeing tail-calls will be honoured, the callee must
+ // pop its own argument stack on return. But this call is *not* a tail call so
+ // we need to undo that after it returns to restore the status-quo.
+ bool TailCallOpt = getTargetMachine().Options.GuaranteedTailCallOpt;
+ uint64_t CalleePopBytes =
+ canGuaranteeTCO(CallConv, TailCallOpt) ? alignTo(NumBytes, 16) : -1ULL;
+
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, dl, true),
- DAG.getIntPtrConstant(0, dl, true), InFlag, dl);
+ DAG.getIntPtrConstant(CalleePopBytes, dl, true),
+ InFlag, dl);
if (!Ins.empty())
InFlag = Chain.getValue(1);
@@ -2804,6 +2920,9 @@ bool ARMTargetLowering::IsEligibleForTailCallOptimization(
if (CallerF.hasFnAttribute("interrupt"))
return false;
+ if (canGuaranteeTCO(CalleeCC, getTargetMachine().Options.GuaranteedTailCallOpt))
+ return CalleeCC == CallerCC;
+
// Also avoid sibcall optimization if either caller or callee uses struct
// return semantics.
if (isCalleeStructRet || isCallerStructRet)
@@ -3550,9 +3669,7 @@ ARMTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const {
/// Return true if all users of V are within function F, looking through
/// ConstantExprs.
static bool allUsersAreInFunction(const Value *V, const Function *F) {
- SmallVector<const User*,4> Worklist;
- for (auto *U : V->users())
- Worklist.push_back(U);
+ SmallVector<const User*,4> Worklist(V->users());
while (!Worklist.empty()) {
auto *U = Worklist.pop_back_val();
if (isa<ConstantExpr>(U)) {
@@ -3599,7 +3716,7 @@ static SDValue promoteToConstantPool(const ARMTargetLowering *TLI,
// from .data to .text. This is not allowed in position-independent code.
auto *Init = GVar->getInitializer();
if ((TLI->isPositionIndependent() || TLI->getSubtarget()->isROPI()) &&
- Init->needsRelocation())
+ Init->needsDynamicRelocation())
return SDValue();
// The constant islands pass can only really deal with alignment requests
@@ -4454,7 +4571,17 @@ SDValue ARMTargetLowering::LowerFormalArguments(
}
}
- AFI->setArgumentStackSize(CCInfo.getNextStackOffset());
+ unsigned StackArgSize = CCInfo.getNextStackOffset();
+ bool TailCallOpt = MF.getTarget().Options.GuaranteedTailCallOpt;
+ if (canGuaranteeTCO(CallConv, TailCallOpt)) {
+ // The only way to guarantee a tail call is if the callee restores its
+ // argument area, but it must also keep the stack aligned when doing so.
+ const DataLayout &DL = DAG.getDataLayout();
+ StackArgSize = alignTo(StackArgSize, DL.getStackAlignment());
+
+ AFI->setArgumentStackToRestore(StackArgSize);
+ }
+ AFI->setArgumentStackSize(StackArgSize);
if (CCInfo.getNextStackOffset() > 0 && AFI->isCmseNSEntryFunction()) {
DiagnosticInfoUnsupported Diag(
@@ -4826,8 +4953,8 @@ SDValue ARMTargetLowering::LowerUnsignedALUO(SDValue Op,
return DAG.getNode(ISD::MERGE_VALUES, dl, VTs, Value, Overflow);
}
-static SDValue LowerSADDSUBSAT(SDValue Op, SelectionDAG &DAG,
- const ARMSubtarget *Subtarget) {
+static SDValue LowerADDSUBSAT(SDValue Op, SelectionDAG &DAG,
+ const ARMSubtarget *Subtarget) {
EVT VT = Op.getValueType();
if (!Subtarget->hasV6Ops() || !Subtarget->hasDSP())
return SDValue();
@@ -4835,15 +4962,40 @@ static SDValue LowerSADDSUBSAT(SDValue Op, SelectionDAG &DAG,
return SDValue();
unsigned NewOpcode;
- bool IsAdd = Op->getOpcode() == ISD::SADDSAT;
switch (VT.getSimpleVT().SimpleTy) {
default:
return SDValue();
case MVT::i8:
- NewOpcode = IsAdd ? ARMISD::QADD8b : ARMISD::QSUB8b;
+ switch (Op->getOpcode()) {
+ case ISD::UADDSAT:
+ NewOpcode = ARMISD::UQADD8b;
+ break;
+ case ISD::SADDSAT:
+ NewOpcode = ARMISD::QADD8b;
+ break;
+ case ISD::USUBSAT:
+ NewOpcode = ARMISD::UQSUB8b;
+ break;
+ case ISD::SSUBSAT:
+ NewOpcode = ARMISD::QSUB8b;
+ break;
+ }
break;
case MVT::i16:
- NewOpcode = IsAdd ? ARMISD::QADD16b : ARMISD::QSUB16b;
+ switch (Op->getOpcode()) {
+ case ISD::UADDSAT:
+ NewOpcode = ARMISD::UQADD16b;
+ break;
+ case ISD::SADDSAT:
+ NewOpcode = ARMISD::QADD16b;
+ break;
+ case ISD::USUBSAT:
+ NewOpcode = ARMISD::UQSUB16b;
+ break;
+ case ISD::SSUBSAT:
+ NewOpcode = ARMISD::QSUB16b;
+ break;
+ }
break;
}
@@ -5224,8 +5376,6 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
std::swap(TVal, FVal);
CC = ISD::getSetCCInverse(CC, LHS.getValueType());
}
- if (TVal == 0)
- TrueVal = DAG.getRegister(ARM::ZR, MVT::i32);
// Drops F's value because we can get it by inverting/negating TVal.
FalseVal = TrueVal;
@@ -6117,6 +6267,48 @@ SDValue ARMTargetLowering::LowerFLT_ROUNDS_(SDValue Op,
return DAG.getMergeValues({And, Chain}, dl);
}
+SDValue ARMTargetLowering::LowerSET_ROUNDING(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDLoc DL(Op);
+ SDValue Chain = Op->getOperand(0);
+ SDValue RMValue = Op->getOperand(1);
+
+ // The rounding mode is in bits 23:22 of the FPSCR.
+ // The llvm.set.rounding argument value to ARM rounding mode value mapping
+ // is 0->3, 1->0, 2->1, 3->2. The formula we use to implement this is
+ // ((arg - 1) & 3) << 22).
+ //
+ // It is expected that the argument of llvm.set.rounding is within the
+ // segment [0, 3], so NearestTiesToAway (4) is not handled here. It is
+ // responsibility of the code generated llvm.set.rounding to ensure this
+ // condition.
+
+ // Calculate new value of FPSCR[23:22].
+ RMValue = DAG.getNode(ISD::SUB, DL, MVT::i32, RMValue,
+ DAG.getConstant(1, DL, MVT::i32));
+ RMValue = DAG.getNode(ISD::AND, DL, MVT::i32, RMValue,
+ DAG.getConstant(0x3, DL, MVT::i32));
+ RMValue = DAG.getNode(ISD::SHL, DL, MVT::i32, RMValue,
+ DAG.getConstant(ARM::RoundingBitsPos, DL, MVT::i32));
+
+ // Get current value of FPSCR.
+ SDValue Ops[] = {Chain,
+ DAG.getConstant(Intrinsic::arm_get_fpscr, DL, MVT::i32)};
+ SDValue FPSCR =
+ DAG.getNode(ISD::INTRINSIC_W_CHAIN, DL, {MVT::i32, MVT::Other}, Ops);
+ Chain = FPSCR.getValue(1);
+ FPSCR = FPSCR.getValue(0);
+
+ // Put new rounding mode into FPSCR[23:22].
+ const unsigned RMMask = ~(ARM::Rounding::rmMask << ARM::RoundingBitsPos);
+ FPSCR = DAG.getNode(ISD::AND, DL, MVT::i32, FPSCR,
+ DAG.getConstant(RMMask, DL, MVT::i32));
+ FPSCR = DAG.getNode(ISD::OR, DL, MVT::i32, FPSCR, RMValue);
+ SDValue Ops2[] = {
+ Chain, DAG.getConstant(Intrinsic::arm_set_fpscr, DL, MVT::i32), FPSCR};
+ return DAG.getNode(ISD::INTRINSIC_VOID, DL, MVT::Other, Ops2);
+}
+
static SDValue LowerCTTZ(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *ST) {
SDLoc dl(N);
@@ -6706,12 +6898,10 @@ static SDValue isVMOVModifiedImm(uint64_t SplatBits, uint64_t SplatUndef,
return SDValue();
// NEON has a 64-bit VMOV splat where each byte is either 0 or 0xff.
uint64_t BitMask = 0xff;
- uint64_t Val = 0;
unsigned ImmMask = 1;
Imm = 0;
for (int ByteNum = 0; ByteNum < 8; ++ByteNum) {
if (((SplatBits | SplatUndef) & BitMask) == BitMask) {
- Val |= BitMask;
Imm |= ImmMask;
} else if ((SplatBits & BitMask) != 0) {
return SDValue();
@@ -6923,35 +7113,6 @@ static bool isVEXTMask(ArrayRef<int> M, EVT VT,
return true;
}
-/// 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.)
-static bool isVREVMask(ArrayRef<int> M, EVT VT, unsigned BlockSize) {
- assert((BlockSize==16 || BlockSize==32 || BlockSize==64) &&
- "Only possible block sizes for VREV are: 16, 32, 64");
-
- unsigned EltSz = VT.getScalarSizeInBits();
- if (EltSz == 64)
- return false;
-
- unsigned NumElts = VT.getVectorNumElements();
- unsigned BlockElts = M[0] + 1;
- // If the first shuffle index is UNDEF, be optimistic.
- if (M[0] < 0)
- BlockElts = BlockSize / EltSz;
-
- if (BlockSize <= EltSz || BlockSize != BlockElts * EltSz)
- return false;
-
- for (unsigned i = 0; i < NumElts; ++i) {
- if (M[i] < 0) continue; // ignore UNDEF indices
- if ((unsigned) M[i] != (i - i%BlockElts) + (BlockElts - 1 - i%BlockElts))
- return false;
- }
-
- return true;
-}
-
static bool isVTBLMask(ArrayRef<int> M, EVT VT) {
// We can handle <8 x i8> vector shuffles. If the index in the mask is out of
// range, then 0 is placed into the resulting vector. So pretty much any mask
@@ -7224,11 +7385,11 @@ static bool isReverseMask(ArrayRef<int> M, EVT VT) {
return true;
}
-static bool isVMOVNMask(ArrayRef<int> M, EVT VT, bool Top) {
+static bool isVMOVNMask(ArrayRef<int> M, EVT VT, bool Top, bool SingleSource) {
unsigned NumElts = VT.getVectorNumElements();
// Make sure the mask has the right size.
if (NumElts != M.size() || (VT != MVT::v8i16 && VT != MVT::v16i8))
- return false;
+ return false;
// If Top
// Look for <0, N, 2, N+2, 4, N+4, ..>.
@@ -7237,10 +7398,33 @@ static bool isVMOVNMask(ArrayRef<int> M, EVT VT, bool Top) {
// Look for <0, N+1, 2, N+3, 4, N+5, ..>
// This inserts Input1 into Input2
unsigned Offset = Top ? 0 : 1;
- for (unsigned i = 0; i < NumElts; i+=2) {
+ unsigned N = SingleSource ? 0 : NumElts;
+ for (unsigned i = 0; i < NumElts; i += 2) {
if (M[i] >= 0 && M[i] != (int)i)
return false;
- if (M[i+1] >= 0 && M[i+1] != (int)(NumElts + i + Offset))
+ if (M[i + 1] >= 0 && M[i + 1] != (int)(N + i + Offset))
+ return false;
+ }
+
+ return true;
+}
+
+static bool isVMOVNTruncMask(ArrayRef<int> M, EVT ToVT, bool rev) {
+ unsigned NumElts = ToVT.getVectorNumElements();
+ if (NumElts != M.size())
+ return false;
+
+ // Test if the Trunc can be convertable to a VMOVN with this shuffle. We are
+ // looking for patterns of:
+ // !rev: 0 N/2 1 N/2+1 2 N/2+2 ...
+ // rev: N/2 0 N/2+1 1 N/2+2 2 ...
+
+ unsigned Off0 = rev ? NumElts / 2 : 0;
+ unsigned Off1 = rev ? 0 : NumElts / 2;
+ for (unsigned i = 0; i < NumElts; i += 2) {
+ if (M[i] >= 0 && M[i] != (int)(Off0 + i / 2))
+ return false;
+ if (M[i + 1] >= 0 && M[i + 1] != (int)(Off1 + i / 2))
return false;
}
@@ -7425,6 +7609,39 @@ static SDValue LowerBUILD_VECTOR_i1(SDValue Op, SelectionDAG &DAG,
return Base;
}
+static SDValue LowerBUILD_VECTORToVIDUP(SDValue Op, SelectionDAG &DAG,
+ const ARMSubtarget *ST) {
+ if (!ST->hasMVEIntegerOps())
+ return SDValue();
+
+ // We are looking for a buildvector where each element is Op[0] + i*N
+ EVT VT = Op.getValueType();
+ SDValue Op0 = Op.getOperand(0);
+ unsigned NumElts = VT.getVectorNumElements();
+
+ // Get the increment value from operand 1
+ SDValue Op1 = Op.getOperand(1);
+ if (Op1.getOpcode() != ISD::ADD || Op1.getOperand(0) != Op0 ||
+ !isa<ConstantSDNode>(Op1.getOperand(1)))
+ return SDValue();
+ unsigned N = Op1.getConstantOperandVal(1);
+ if (N != 1 && N != 2 && N != 4 && N != 8)
+ return SDValue();
+
+ // Check that each other operand matches
+ for (unsigned I = 2; I < NumElts; I++) {
+ SDValue OpI = Op.getOperand(I);
+ if (OpI.getOpcode() != ISD::ADD || OpI.getOperand(0) != Op0 ||
+ !isa<ConstantSDNode>(OpI.getOperand(1)) ||
+ OpI.getConstantOperandVal(1) != I * N)
+ return SDValue();
+ }
+
+ SDLoc DL(Op);
+ return DAG.getNode(ARMISD::VIDUP, DL, DAG.getVTList(VT, MVT::i32), Op0,
+ DAG.getConstant(N, DL, MVT::i32));
+}
+
// If this is a case we can't handle, return null and let the default
// expansion code take care of it.
SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG,
@@ -7436,6 +7653,9 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG,
if (ST->hasMVEIntegerOps() && VT.getScalarSizeInBits() == 1)
return LowerBUILD_VECTOR_i1(Op, DAG, ST);
+ if (SDValue R = LowerBUILD_VECTORToVIDUP(Op, DAG, ST))
+ return R;
+
APInt SplatBits, SplatUndef;
unsigned SplatBitSize;
bool HasAnyUndefs;
@@ -7474,6 +7694,18 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG,
return DAG.getNode(ARMISD::VMOVFPIMM, dl, VT, Val);
}
}
+
+ // If we are under MVE, generate a VDUP(constant), bitcast to the original
+ // type.
+ if (ST->hasMVEIntegerOps() &&
+ (SplatBitSize == 8 || SplatBitSize == 16 || SplatBitSize == 32)) {
+ EVT DupVT = SplatBitSize == 32 ? MVT::v4i32
+ : SplatBitSize == 16 ? MVT::v8i16
+ : MVT::v16i8;
+ SDValue Const = DAG.getConstant(SplatBits.getZExtValue(), dl, MVT::i32);
+ SDValue VDup = DAG.getNode(ARMISD::VDUP, dl, DupVT, Const);
+ return DAG.getNode(ARMISD::VECTOR_REG_CAST, dl, VT, VDup);
+ }
}
}
@@ -7947,7 +8179,8 @@ bool ARMTargetLowering::isShuffleMaskLegal(ArrayRef<int> M, EVT VT) const {
isReverseMask(M, VT))
return true;
else if (Subtarget->hasMVEIntegerOps() &&
- (isVMOVNMask(M, VT, 0) || isVMOVNMask(M, VT, 1)))
+ (isVMOVNMask(M, VT, true, false) ||
+ isVMOVNMask(M, VT, false, false) || isVMOVNMask(M, VT, true, true)))
return true;
else
return false;
@@ -7981,7 +8214,8 @@ static SDValue GeneratePerfectShuffle(unsigned PFEntry, SDValue LHS,
VT.getVectorElementType() == MVT::f32)
return DAG.getNode(ARMISD::VREV64, dl, VT, OpLHS);
// vrev <4 x i16> -> VREV32
- if (VT.getVectorElementType() == MVT::i16)
+ if (VT.getVectorElementType() == MVT::i16 ||
+ VT.getVectorElementType() == MVT::f16)
return DAG.getNode(ARMISD::VREV32, dl, VT, OpLHS);
// vrev <4 x i8> -> VREV16
assert(VT.getVectorElementType() == MVT::i8);
@@ -8192,8 +8426,8 @@ static SDValue LowerVECTOR_SHUFFLEUsingMovs(SDValue Op,
Input = Op->getOperand(1);
Elt -= 4;
}
- SDValue BitCast = DAG.getBitcast(MVT::v4i32, Input);
- Parts[Part] = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::i32, BitCast,
+ SDValue BitCast = DAG.getBitcast(MVT::v4f32, Input);
+ Parts[Part] = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f32, BitCast,
DAG.getConstant(Elt, dl, MVT::i32));
}
}
@@ -8212,19 +8446,70 @@ static SDValue LowerVECTOR_SHUFFLEUsingMovs(SDValue Op,
Parts[Part] ? -1 : ShuffleMask[Part * QuarterSize + i]);
SDValue NewShuffle = DAG.getVectorShuffle(
VT, dl, Op->getOperand(0), Op->getOperand(1), NewShuffleMask);
- SDValue BitCast = DAG.getBitcast(MVT::v4i32, NewShuffle);
+ SDValue BitCast = DAG.getBitcast(MVT::v4f32, NewShuffle);
for (int Part = 0; Part < 4; ++Part)
if (!Parts[Part])
- Parts[Part] = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::i32,
+ Parts[Part] = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::f32,
BitCast, DAG.getConstant(Part, dl, MVT::i32));
}
// Build a vector out of the various parts and bitcast it back to the original
// type.
- SDValue NewVec = DAG.getBuildVector(MVT::v4i32, dl, Parts);
+ SDValue NewVec = DAG.getNode(ARMISD::BUILD_VECTOR, dl, MVT::v4f32, Parts);
return DAG.getBitcast(VT, NewVec);
}
+static SDValue LowerVECTOR_SHUFFLEUsingOneOff(SDValue Op,
+ ArrayRef<int> ShuffleMask,
+ SelectionDAG &DAG) {
+ SDValue V1 = Op.getOperand(0);
+ SDValue V2 = Op.getOperand(1);
+ EVT VT = Op.getValueType();
+ unsigned NumElts = VT.getVectorNumElements();
+
+ // An One-Off Identity mask is one that is mostly an identity mask from as
+ // single source but contains a single element out-of-place, either from a
+ // different vector or from another position in the same vector. As opposed to
+ // lowering this via a ARMISD::BUILD_VECTOR we can generate an extract/insert
+ // pair directly.
+ auto isOneOffIdentityMask = [](ArrayRef<int> Mask, EVT VT, int BaseOffset,
+ int &OffElement) {
+ OffElement = -1;
+ int NonUndef = 0;
+ for (int i = 0, NumMaskElts = Mask.size(); i < NumMaskElts; ++i) {
+ if (Mask[i] == -1)
+ continue;
+ NonUndef++;
+ if (Mask[i] != i + BaseOffset) {
+ if (OffElement == -1)
+ OffElement = i;
+ else
+ return false;
+ }
+ }
+ return NonUndef > 2 && OffElement != -1;
+ };
+ int OffElement;
+ SDValue VInput;
+ if (isOneOffIdentityMask(ShuffleMask, VT, 0, OffElement))
+ VInput = V1;
+ else if (isOneOffIdentityMask(ShuffleMask, VT, NumElts, OffElement))
+ VInput = V2;
+ else
+ return SDValue();
+
+ SDLoc dl(Op);
+ EVT SVT = VT.getScalarType() == MVT::i8 || VT.getScalarType() == MVT::i16
+ ? MVT::i32
+ : VT.getScalarType();
+ SDValue Elt = DAG.getNode(
+ ISD::EXTRACT_VECTOR_ELT, dl, SVT,
+ ShuffleMask[OffElement] < (int)NumElts ? V1 : V2,
+ DAG.getVectorIdxConstant(ShuffleMask[OffElement] % NumElts, dl));
+ return DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, VT, VInput, Elt,
+ DAG.getVectorIdxConstant(OffElement % NumElts, dl));
+}
+
static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG,
const ARMSubtarget *ST) {
SDValue V1 = Op.getOperand(0);
@@ -8311,12 +8596,15 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG,
}
}
if (ST->hasMVEIntegerOps()) {
- if (isVMOVNMask(ShuffleMask, VT, 0))
+ if (isVMOVNMask(ShuffleMask, VT, false, false))
return DAG.getNode(ARMISD::VMOVN, dl, VT, V2, V1,
DAG.getConstant(0, dl, MVT::i32));
- if (isVMOVNMask(ShuffleMask, VT, 1))
+ if (isVMOVNMask(ShuffleMask, VT, true, false))
return DAG.getNode(ARMISD::VMOVN, dl, VT, V1, V2,
DAG.getConstant(1, dl, MVT::i32));
+ if (isVMOVNMask(ShuffleMask, VT, true, true))
+ return DAG.getNode(ARMISD::VMOVN, dl, VT, V1, V1,
+ DAG.getConstant(1, dl, MVT::i32));
}
// Also check for these shuffles through CONCAT_VECTORS: we canonicalize
@@ -8358,6 +8646,10 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG,
}
}
+ if (ST->hasMVEIntegerOps() && EltSize <= 32)
+ if (SDValue V = LowerVECTOR_SHUFFLEUsingOneOff(Op, ShuffleMask, DAG))
+ return V;
+
// If the shuffle is not directly supported and it has 4 elements, use
// the PerfectShuffle-generated table to synthesize it from other shuffles.
unsigned NumElts = VT.getVectorNumElements();
@@ -8643,13 +8935,13 @@ static SDValue LowerEXTRACT_SUBVECTOR(SDValue Op, SelectionDAG &DAG,
}
// Turn a truncate into a predicate (an i1 vector) into icmp(and(x, 1), 0).
-static SDValue LowerTruncatei1(SDValue N, SelectionDAG &DAG,
+static SDValue LowerTruncatei1(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *ST) {
assert(ST->hasMVEIntegerOps() && "Expected MVE!");
- EVT VT = N.getValueType();
+ EVT VT = N->getValueType(0);
assert((VT == MVT::v16i1 || VT == MVT::v8i1 || VT == MVT::v4i1) &&
"Expected a vector i1 type!");
- SDValue Op = N.getOperand(0);
+ SDValue Op = N->getOperand(0);
EVT FromVT = Op.getValueType();
SDLoc DL(N);
@@ -8659,6 +8951,99 @@ static SDValue LowerTruncatei1(SDValue N, SelectionDAG &DAG,
DAG.getCondCode(ISD::SETNE));
}
+static SDValue LowerTruncate(SDNode *N, SelectionDAG &DAG,
+ const ARMSubtarget *Subtarget) {
+ if (!Subtarget->hasMVEIntegerOps())
+ return SDValue();
+
+ EVT ToVT = N->getValueType(0);
+ if (ToVT.getScalarType() == MVT::i1)
+ return LowerTruncatei1(N, DAG, Subtarget);
+
+ // MVE does not have a single instruction to perform the truncation of a v4i32
+ // into the lower half of a v8i16, in the same way that a NEON vmovn would.
+ // Most of the instructions in MVE follow the 'Beats' system, where moving
+ // values from different lanes is usually something that the instructions
+ // avoid.
+ //
+ // Instead it has top/bottom instructions such as VMOVLT/B and VMOVNT/B,
+ // which take a the top/bottom half of a larger lane and extend it (or do the
+ // opposite, truncating into the top/bottom lane from a larger lane). Note
+ // that because of the way we widen lanes, a v4i16 is really a v4i32 using the
+ // bottom 16bits from each vector lane. This works really well with T/B
+ // instructions, but that doesn't extend to v8i32->v8i16 where the lanes need
+ // to move order.
+ //
+ // But truncates and sext/zext are always going to be fairly common from llvm.
+ // We have several options for how to deal with them:
+ // - Wherever possible combine them into an instruction that makes them
+ // "free". This includes loads/stores, which can perform the trunc as part
+ // of the memory operation. Or certain shuffles that can be turned into
+ // VMOVN/VMOVL.
+ // - Lane Interleaving to transform blocks surrounded by ext/trunc. So
+ // trunc(mul(sext(a), sext(b))) may become
+ // VMOVNT(VMUL(VMOVLB(a), VMOVLB(b)), VMUL(VMOVLT(a), VMOVLT(b))). (Which in
+ // this case can use VMULL). This is performed in the
+ // MVELaneInterleavingPass.
+ // - Otherwise we have an option. By default we would expand the
+ // zext/sext/trunc into a series of lane extract/inserts going via GPR
+ // registers. One for each vector lane in the vector. This can obviously be
+ // very expensive.
+ // - The other option is to use the fact that loads/store can extend/truncate
+ // to turn a trunc into two truncating stack stores and a stack reload. This
+ // becomes 3 back-to-back memory operations, but at least that is less than
+ // all the insert/extracts.
+ //
+ // In order to do the last, we convert certain trunc's into MVETRUNC, which
+ // are either optimized where they can be, or eventually lowered into stack
+ // stores/loads. This prevents us from splitting a v8i16 trunc into two stores
+ // two early, where other instructions would be better, and stops us from
+ // having to reconstruct multiple buildvector shuffles into loads/stores.
+ if (ToVT != MVT::v8i16 && ToVT != MVT::v16i8)
+ return SDValue();
+ EVT FromVT = N->getOperand(0).getValueType();
+ if (FromVT != MVT::v8i32 && FromVT != MVT::v16i16)
+ return SDValue();
+
+ SDValue Lo, Hi;
+ std::tie(Lo, Hi) = DAG.SplitVectorOperand(N, 0);
+ SDLoc DL(N);
+ return DAG.getNode(ARMISD::MVETRUNC, DL, ToVT, Lo, Hi);
+}
+
+static SDValue LowerVectorExtend(SDNode *N, SelectionDAG &DAG,
+ const ARMSubtarget *Subtarget) {
+ if (!Subtarget->hasMVEIntegerOps())
+ return SDValue();
+
+ // See LowerTruncate above for an explanation of MVEEXT/MVETRUNC.
+
+ EVT ToVT = N->getValueType(0);
+ if (ToVT != MVT::v16i32 && ToVT != MVT::v8i32 && ToVT != MVT::v16i16)
+ return SDValue();
+ SDValue Op = N->getOperand(0);
+ EVT FromVT = Op.getValueType();
+ if (FromVT != MVT::v8i16 && FromVT != MVT::v16i8)
+ return SDValue();
+
+ SDLoc DL(N);
+ EVT ExtVT = ToVT.getHalfNumVectorElementsVT(*DAG.getContext());
+ if (ToVT.getScalarType() == MVT::i32 && FromVT.getScalarType() == MVT::i8)
+ ExtVT = MVT::v8i16;
+
+ unsigned Opcode =
+ N->getOpcode() == ISD::SIGN_EXTEND ? ARMISD::MVESEXT : ARMISD::MVEZEXT;
+ SDValue Ext = DAG.getNode(Opcode, DL, DAG.getVTList(ExtVT, ExtVT), Op);
+ SDValue Ext1 = Ext.getValue(1);
+
+ if (ToVT.getScalarType() == MVT::i32 && FromVT.getScalarType() == MVT::i8) {
+ Ext = DAG.getNode(N->getOpcode(), DL, MVT::v8i32, Ext);
+ Ext1 = DAG.getNode(N->getOpcode(), DL, MVT::v8i32, Ext1);
+ }
+
+ return DAG.getNode(ISD::CONCAT_VECTORS, DL, ToVT, Ext, Ext1);
+}
+
/// isExtendedBUILD_VECTOR - Check if N is a constant BUILD_VECTOR where each
/// element has been zero/sign-extended, depending on the isSigned parameter,
/// from an integer type half its size.
@@ -9394,13 +9779,20 @@ static SDValue LowerPredicateLoad(SDValue Op, SelectionDAG &DAG) {
// the bottom bits of the predicate.
// Equally, VLDR for an v16i1 will actually load 32bits (so will be incorrect
// for BE).
+ // Speaking of BE, apparently the rest of llvm will assume a reverse order to
+ // a natural VMSR(load), so needs to be reversed.
SDLoc dl(Op);
SDValue Load = DAG.getExtLoad(
ISD::EXTLOAD, dl, MVT::i32, LD->getChain(), LD->getBasePtr(),
EVT::getIntegerVT(*DAG.getContext(), MemVT.getSizeInBits()),
LD->getMemOperand());
- SDValue Pred = DAG.getNode(ARMISD::PREDICATE_CAST, dl, MVT::v16i1, Load);
+ SDValue Val = Load;
+ if (DAG.getDataLayout().isBigEndian())
+ Val = DAG.getNode(ISD::SRL, dl, MVT::i32,
+ DAG.getNode(ISD::BITREVERSE, dl, MVT::i32, Load),
+ DAG.getConstant(32 - MemVT.getSizeInBits(), dl, MVT::i32));
+ SDValue Pred = DAG.getNode(ARMISD::PREDICATE_CAST, dl, MVT::v16i1, Val);
if (MemVT != MVT::v16i1)
Pred = DAG.getNode(ISD::EXTRACT_SUBVECTOR, dl, MemVT, Pred,
DAG.getConstant(0, dl, MVT::i32));
@@ -9441,14 +9833,22 @@ static SDValue LowerPredicateStore(SDValue Op, SelectionDAG &DAG) {
SDValue Build = ST->getValue();
if (MemVT != MVT::v16i1) {
SmallVector<SDValue, 16> Ops;
- for (unsigned I = 0; I < MemVT.getVectorNumElements(); I++)
+ for (unsigned I = 0; I < MemVT.getVectorNumElements(); I++) {
+ unsigned Elt = DAG.getDataLayout().isBigEndian()
+ ? MemVT.getVectorNumElements() - I - 1
+ : I;
Ops.push_back(DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, MVT::i32, Build,
- DAG.getConstant(I, dl, MVT::i32)));
+ DAG.getConstant(Elt, dl, MVT::i32)));
+ }
for (unsigned I = MemVT.getVectorNumElements(); I < 16; I++)
Ops.push_back(DAG.getUNDEF(MVT::i32));
Build = DAG.getNode(ISD::BUILD_VECTOR, dl, MVT::v16i1, Ops);
}
SDValue GRP = DAG.getNode(ARMISD::PREDICATE_CAST, dl, MVT::i32, Build);
+ if (MemVT == MVT::v16i1 && DAG.getDataLayout().isBigEndian())
+ GRP = DAG.getNode(ISD::SRL, dl, MVT::i32,
+ DAG.getNode(ISD::BITREVERSE, dl, MVT::i32, GRP),
+ DAG.getConstant(16, dl, MVT::i32));
return DAG.getTruncStore(
ST->getChain(), dl, GRP, ST->getBasePtr(),
EVT::getIntegerVT(*DAG.getContext(), MemVT.getSizeInBits()),
@@ -9594,7 +9994,7 @@ static SDValue LowerVecReduceF(SDValue Op, SelectionDAG &DAG,
}
static SDValue LowerAtomicLoadStore(SDValue Op, SelectionDAG &DAG) {
- if (isStrongerThanMonotonic(cast<AtomicSDNode>(Op)->getOrdering()))
+ if (isStrongerThanMonotonic(cast<AtomicSDNode>(Op)->getSuccessOrdering()))
// Acquire/Release load/store is not legal for targets without a dmb or
// equivalent available.
return SDValue();
@@ -9770,8 +10170,11 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
case ISD::INSERT_VECTOR_ELT: return LowerINSERT_VECTOR_ELT(Op, DAG);
case ISD::EXTRACT_VECTOR_ELT: return LowerEXTRACT_VECTOR_ELT(Op, DAG, Subtarget);
case ISD::CONCAT_VECTORS: return LowerCONCAT_VECTORS(Op, DAG, Subtarget);
- case ISD::TRUNCATE: return LowerTruncatei1(Op, DAG, Subtarget);
+ case ISD::TRUNCATE: return LowerTruncate(Op.getNode(), DAG, Subtarget);
+ case ISD::SIGN_EXTEND:
+ case ISD::ZERO_EXTEND: return LowerVectorExtend(Op.getNode(), DAG, Subtarget);
case ISD::FLT_ROUNDS_: return LowerFLT_ROUNDS_(Op, DAG);
+ case ISD::SET_ROUNDING: return LowerSET_ROUNDING(Op, DAG);
case ISD::MUL: return LowerMUL(Op, DAG);
case ISD::SDIV:
if (Subtarget->isTargetWindows() && !Op.getValueType().isVector())
@@ -9791,7 +10194,9 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
return LowerUnsignedALUO(Op, DAG);
case ISD::SADDSAT:
case ISD::SSUBSAT:
- return LowerSADDSUBSAT(Op, DAG, Subtarget);
+ case ISD::UADDSAT:
+ case ISD::USUBSAT:
+ return LowerADDSUBSAT(Op, DAG, Subtarget);
case ISD::LOAD:
return LowerPredicateLoad(Op, DAG);
case ISD::STORE:
@@ -9891,7 +10296,9 @@ void ARMTargetLowering::ReplaceNodeResults(SDNode *N,
return;
case ISD::SADDSAT:
case ISD::SSUBSAT:
- Res = LowerSADDSUBSAT(SDValue(N, 0), DAG, Subtarget);
+ case ISD::UADDSAT:
+ case ISD::USUBSAT:
+ Res = LowerADDSUBSAT(SDValue(N, 0), DAG, Subtarget);
break;
case ISD::READCYCLECOUNTER:
ReplaceREADCYCLECOUNTER(N, Results, DAG, Subtarget);
@@ -9912,6 +10319,13 @@ void ARMTargetLowering::ReplaceNodeResults(SDNode *N,
case ISD::LOAD:
LowerLOAD(N, Results, DAG);
break;
+ case ISD::TRUNCATE:
+ Res = LowerTruncate(N, DAG, Subtarget);
+ break;
+ case ISD::SIGN_EXTEND:
+ case ISD::ZERO_EXTEND:
+ Res = LowerVectorExtend(N, DAG, Subtarget);
+ break;
}
if (Res.getNode())
Results.push_back(Res);
@@ -10979,6 +11393,145 @@ static bool checkAndUpdateCPSRKill(MachineBasicBlock::iterator SelectItr,
return true;
}
+/// Adds logic in loop entry MBB to calculate loop iteration count and adds
+/// t2WhileLoopSetup and t2WhileLoopStart to generate WLS loop
+static Register genTPEntry(MachineBasicBlock *TpEntry,
+ MachineBasicBlock *TpLoopBody,
+ MachineBasicBlock *TpExit, Register OpSizeReg,
+ const TargetInstrInfo *TII, DebugLoc Dl,
+ MachineRegisterInfo &MRI) {
+ // Calculates loop iteration count = ceil(n/16) = (n + 15) >> 4.
+ Register AddDestReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ BuildMI(TpEntry, Dl, TII->get(ARM::t2ADDri), AddDestReg)
+ .addUse(OpSizeReg)
+ .addImm(15)
+ .add(predOps(ARMCC::AL))
+ .addReg(0);
+
+ Register LsrDestReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ BuildMI(TpEntry, Dl, TII->get(ARM::t2LSRri), LsrDestReg)
+ .addUse(AddDestReg, RegState::Kill)
+ .addImm(4)
+ .add(predOps(ARMCC::AL))
+ .addReg(0);
+
+ Register TotalIterationsReg = MRI.createVirtualRegister(&ARM::GPRlrRegClass);
+ BuildMI(TpEntry, Dl, TII->get(ARM::t2WhileLoopSetup), TotalIterationsReg)
+ .addUse(LsrDestReg, RegState::Kill);
+
+ BuildMI(TpEntry, Dl, TII->get(ARM::t2WhileLoopStart))
+ .addUse(TotalIterationsReg)
+ .addMBB(TpExit);
+
+ BuildMI(TpEntry, Dl, TII->get(ARM::t2B))
+ .addMBB(TpLoopBody)
+ .add(predOps(ARMCC::AL));
+
+ return TotalIterationsReg;
+}
+
+/// Adds logic in the loopBody MBB to generate MVE_VCTP, t2DoLoopDec and
+/// t2DoLoopEnd. These are used by later passes to generate tail predicated
+/// loops.
+static void genTPLoopBody(MachineBasicBlock *TpLoopBody,
+ MachineBasicBlock *TpEntry, MachineBasicBlock *TpExit,
+ const TargetInstrInfo *TII, DebugLoc Dl,
+ MachineRegisterInfo &MRI, Register OpSrcReg,
+ Register OpDestReg, Register ElementCountReg,
+ Register TotalIterationsReg, bool IsMemcpy) {
+ // First insert 4 PHI nodes for: Current pointer to Src (if memcpy), Dest
+ // array, loop iteration counter, predication counter.
+
+ Register SrcPhiReg, CurrSrcReg;
+ if (IsMemcpy) {
+ // Current position in the src array
+ SrcPhiReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ CurrSrcReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::PHI), SrcPhiReg)
+ .addUse(OpSrcReg)
+ .addMBB(TpEntry)
+ .addUse(CurrSrcReg)
+ .addMBB(TpLoopBody);
+ }
+
+ // Current position in the dest array
+ Register DestPhiReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ Register CurrDestReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::PHI), DestPhiReg)
+ .addUse(OpDestReg)
+ .addMBB(TpEntry)
+ .addUse(CurrDestReg)
+ .addMBB(TpLoopBody);
+
+ // Current loop counter
+ Register LoopCounterPhiReg = MRI.createVirtualRegister(&ARM::GPRlrRegClass);
+ Register RemainingLoopIterationsReg =
+ MRI.createVirtualRegister(&ARM::GPRlrRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::PHI), LoopCounterPhiReg)
+ .addUse(TotalIterationsReg)
+ .addMBB(TpEntry)
+ .addUse(RemainingLoopIterationsReg)
+ .addMBB(TpLoopBody);
+
+ // Predication counter
+ Register PredCounterPhiReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ Register RemainingElementsReg = MRI.createVirtualRegister(&ARM::rGPRRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::PHI), PredCounterPhiReg)
+ .addUse(ElementCountReg)
+ .addMBB(TpEntry)
+ .addUse(RemainingElementsReg)
+ .addMBB(TpLoopBody);
+
+ // Pass predication counter to VCTP
+ Register VccrReg = MRI.createVirtualRegister(&ARM::VCCRRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::MVE_VCTP8), VccrReg)
+ .addUse(PredCounterPhiReg)
+ .addImm(ARMVCC::None)
+ .addReg(0);
+
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::t2SUBri), RemainingElementsReg)
+ .addUse(PredCounterPhiReg)
+ .addImm(16)
+ .add(predOps(ARMCC::AL))
+ .addReg(0);
+
+ // VLDRB (only if memcpy) and VSTRB instructions, predicated using VPR
+ Register SrcValueReg;
+ if (IsMemcpy) {
+ SrcValueReg = MRI.createVirtualRegister(&ARM::MQPRRegClass);
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::MVE_VLDRBU8_post))
+ .addDef(CurrSrcReg)
+ .addDef(SrcValueReg)
+ .addReg(SrcPhiReg)
+ .addImm(16)
+ .addImm(ARMVCC::Then)
+ .addUse(VccrReg);
+ } else
+ SrcValueReg = OpSrcReg;
+
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::MVE_VSTRBU8_post))
+ .addDef(CurrDestReg)
+ .addUse(SrcValueReg)
+ .addReg(DestPhiReg)
+ .addImm(16)
+ .addImm(ARMVCC::Then)
+ .addUse(VccrReg);
+
+ // Add the pseudoInstrs for decrementing the loop counter and marking the
+ // end:t2DoLoopDec and t2DoLoopEnd
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::t2LoopDec), RemainingLoopIterationsReg)
+ .addUse(LoopCounterPhiReg)
+ .addImm(1);
+
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::t2LoopEnd))
+ .addUse(RemainingLoopIterationsReg)
+ .addMBB(TpLoopBody);
+
+ BuildMI(TpLoopBody, Dl, TII->get(ARM::t2B))
+ .addMBB(TpExit)
+ .add(predOps(ARMCC::AL));
+}
+
MachineBasicBlock *
ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
MachineBasicBlock *BB) const {
@@ -11005,6 +11558,98 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
return BB;
}
+ case ARM::MVE_MEMCPYLOOPINST:
+ case ARM::MVE_MEMSETLOOPINST: {
+
+ // Transformation below expands MVE_MEMCPYLOOPINST/MVE_MEMSETLOOPINST Pseudo
+ // into a Tail Predicated (TP) Loop. It adds the instructions to calculate
+ // the iteration count =ceil(size_in_bytes/16)) in the TP entry block and
+ // adds the relevant instructions in the TP loop Body for generation of a
+ // WLSTP loop.
+
+ // Below is relevant portion of the CFG after the transformation.
+ // The Machine Basic Blocks are shown along with branch conditions (in
+ // brackets). Note that TP entry/exit MBBs depict the entry/exit of this
+ // portion of the CFG and may not necessarily be the entry/exit of the
+ // function.
+
+ // (Relevant) CFG after transformation:
+ // TP entry MBB
+ // |
+ // |-----------------|
+ // (n <= 0) (n > 0)
+ // | |
+ // | TP loop Body MBB<--|
+ // | | |
+ // \ |___________|
+ // \ /
+ // TP exit MBB
+
+ MachineFunction *MF = BB->getParent();
+ MachineFunctionProperties &Properties = MF->getProperties();
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+
+ Register OpDestReg = MI.getOperand(0).getReg();
+ Register OpSrcReg = MI.getOperand(1).getReg();
+ Register OpSizeReg = MI.getOperand(2).getReg();
+
+ // Allocate the required MBBs and add to parent function.
+ MachineBasicBlock *TpEntry = BB;
+ MachineBasicBlock *TpLoopBody = MF->CreateMachineBasicBlock();
+ MachineBasicBlock *TpExit;
+
+ MF->push_back(TpLoopBody);
+
+ // If any instructions are present in the current block after
+ // MVE_MEMCPYLOOPINST or MVE_MEMSETLOOPINST, split the current block and
+ // move the instructions into the newly created exit block. If there are no
+ // instructions add an explicit branch to the FallThrough block and then
+ // split.
+ //
+ // The split is required for two reasons:
+ // 1) A terminator(t2WhileLoopStart) will be placed at that site.
+ // 2) Since a TPLoopBody will be added later, any phis in successive blocks
+ // need to be updated. splitAt() already handles this.
+ TpExit = BB->splitAt(MI, false);
+ if (TpExit == BB) {
+ assert(BB->canFallThrough() && "Exit Block must be Fallthrough of the "
+ "block containing memcpy/memset Pseudo");
+ TpExit = BB->getFallThrough();
+ BuildMI(BB, dl, TII->get(ARM::t2B))
+ .addMBB(TpExit)
+ .add(predOps(ARMCC::AL));
+ TpExit = BB->splitAt(MI, false);
+ }
+
+ // Add logic for iteration count
+ Register TotalIterationsReg =
+ genTPEntry(TpEntry, TpLoopBody, TpExit, OpSizeReg, TII, dl, MRI);
+
+ // Add the vectorized (and predicated) loads/store instructions
+ bool IsMemcpy = MI.getOpcode() == ARM::MVE_MEMCPYLOOPINST;
+ genTPLoopBody(TpLoopBody, TpEntry, TpExit, TII, dl, MRI, OpSrcReg,
+ OpDestReg, OpSizeReg, TotalIterationsReg, IsMemcpy);
+
+ // Required to avoid conflict with the MachineVerifier during testing.
+ Properties.reset(MachineFunctionProperties::Property::NoPHIs);
+
+ // Connect the blocks
+ TpEntry->addSuccessor(TpLoopBody);
+ TpLoopBody->addSuccessor(TpLoopBody);
+ TpLoopBody->addSuccessor(TpExit);
+
+ // Reorder for a more natural layout
+ TpLoopBody->moveAfter(TpEntry);
+ TpExit->moveAfter(TpLoopBody);
+
+ // Finally, remove the memcpy Psuedo Instruction
+ MI.eraseFromParent();
+
+ // Return the exit block as it may contain other instructions requiring a
+ // custom inserter
+ return TpExit;
+ }
+
// The Thumb2 pre-indexed stores have the same MI operands, they just
// define them differently in the .td files from the isel patterns, so
// they need pseudos.
@@ -11266,14 +11911,6 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
return EmitLowered__chkstk(MI, BB);
case ARM::WIN__DBZCHK:
return EmitLowered__dbzchk(MI, BB);
- case ARM::t2DoLoopStart:
- // We are just here to set a register allocation hint, prefering lr for the
- // input register to make it more likely to be movable and removable, later
- // in the pipeline.
- Register R = MI.getOperand(1).getReg();
- MachineFunction *MF = MI.getParent()->getParent();
- MF->getRegInfo().setRegAllocationHint(R, ARMRI::RegLR, 0);
- return BB;
}
}
@@ -12285,7 +12922,7 @@ static SDValue PerformVQDMULHCombine(SDNode *N, SelectionDAG &DAG) {
Ext1.getOpcode() != ISD::SIGN_EXTEND)
return SDValue();
EVT VecVT = Ext0.getOperand(0).getValueType();
- if (VecVT != MVT::v4i32 && VecVT != MVT::v8i16 && VecVT != MVT::v16i8)
+ if (!VecVT.isPow2VectorType() || VecVT.getVectorNumElements() == 1)
return SDValue();
if (Ext1.getOperand(0).getValueType() != VecVT ||
VecVT.getScalarType() != ScalarType ||
@@ -12293,9 +12930,42 @@ static SDValue PerformVQDMULHCombine(SDNode *N, SelectionDAG &DAG) {
return SDValue();
SDLoc DL(Mul);
- SDValue VQDMULH = DAG.getNode(ARMISD::VQDMULH, DL, VecVT, Ext0.getOperand(0),
- Ext1.getOperand(0));
- return DAG.getNode(ISD::SIGN_EXTEND, DL, VT, VQDMULH);
+ unsigned LegalLanes = 128 / (ShftAmt + 1);
+ EVT LegalVecVT = MVT::getVectorVT(ScalarType, LegalLanes);
+ // For types smaller than legal vectors extend to be legal and only use needed
+ // lanes.
+ if (VecVT.getSizeInBits() < 128) {
+ EVT ExtVecVT =
+ MVT::getVectorVT(MVT::getIntegerVT(128 / VecVT.getVectorNumElements()),
+ VecVT.getVectorNumElements());
+ SDValue Inp0 =
+ DAG.getNode(ISD::ANY_EXTEND, DL, ExtVecVT, Ext0.getOperand(0));
+ SDValue Inp1 =
+ DAG.getNode(ISD::ANY_EXTEND, DL, ExtVecVT, Ext1.getOperand(0));
+ Inp0 = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, LegalVecVT, Inp0);
+ Inp1 = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, LegalVecVT, Inp1);
+ SDValue VQDMULH = DAG.getNode(ARMISD::VQDMULH, DL, LegalVecVT, Inp0, Inp1);
+ SDValue Trunc = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, ExtVecVT, VQDMULH);
+ Trunc = DAG.getNode(ISD::TRUNCATE, DL, VecVT, Trunc);
+ return DAG.getNode(ISD::SIGN_EXTEND, DL, VT, Trunc);
+ }
+
+ // For larger types, split into legal sized chunks.
+ assert(VecVT.getSizeInBits() % 128 == 0 && "Expected a power2 type");
+ unsigned NumParts = VecVT.getSizeInBits() / 128;
+ SmallVector<SDValue> Parts;
+ for (unsigned I = 0; I < NumParts; ++I) {
+ SDValue Inp0 =
+ DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, LegalVecVT, Ext0.getOperand(0),
+ DAG.getVectorIdxConstant(I * LegalLanes, DL));
+ SDValue Inp1 =
+ DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, LegalVecVT, Ext1.getOperand(0),
+ DAG.getVectorIdxConstant(I * LegalLanes, DL));
+ SDValue VQDMULH = DAG.getNode(ARMISD::VQDMULH, DL, LegalVecVT, Inp0, Inp1);
+ Parts.push_back(VQDMULH);
+ }
+ return DAG.getNode(ISD::SIGN_EXTEND, DL, VT,
+ DAG.getNode(ISD::CONCAT_VECTORS, DL, VecVT, Parts));
}
static SDValue PerformVSELECTCombine(SDNode *N,
@@ -12394,8 +13064,7 @@ static SDValue PerformADDCombineWithOperands(SDNode *N, SDValue N0, SDValue N1,
return SDValue();
}
-static SDValue PerformADDVecReduce(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI,
+static SDValue PerformADDVecReduce(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *Subtarget) {
if (!Subtarget->hasMVEIntegerOps() || N->getValueType(0) != MVT::i64)
return SDValue();
@@ -12408,28 +13077,39 @@ static SDValue PerformADDVecReduce(SDNode *N,
// t1: i32,i32 = ARMISD::VADDLVs x
// t2: i64 = build_pair t1, t1:1
// t3: i64 = add t2, y
+ // Otherwise we try to push the add up above VADDLVAx, to potentially allow
+ // the add to be simplified seperately.
// We also need to check for sext / zext and commutitive adds.
auto MakeVecReduce = [&](unsigned Opcode, unsigned OpcodeA, SDValue NA,
SDValue NB) {
if (NB->getOpcode() != ISD::BUILD_PAIR)
return SDValue();
SDValue VecRed = NB->getOperand(0);
- if (VecRed->getOpcode() != Opcode || VecRed.getResNo() != 0 ||
+ if ((VecRed->getOpcode() != Opcode && VecRed->getOpcode() != OpcodeA) ||
+ VecRed.getResNo() != 0 ||
NB->getOperand(1) != SDValue(VecRed.getNode(), 1))
return SDValue();
SDLoc dl(N);
+ if (VecRed->getOpcode() == OpcodeA) {
+ // add(NA, VADDLVA(Inp), Y) -> VADDLVA(add(NA, Inp), Y)
+ SDValue Inp = DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64,
+ VecRed.getOperand(0), VecRed.getOperand(1));
+ NA = DAG.getNode(ISD::ADD, dl, MVT::i64, Inp, NA);
+ }
+
SmallVector<SDValue, 4> Ops;
- Ops.push_back(DCI.DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, NA,
- DCI.DAG.getConstant(0, dl, MVT::i32)));
- Ops.push_back(DCI.DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, NA,
- DCI.DAG.getConstant(1, dl, MVT::i32)));
- for (unsigned i = 0, e = VecRed.getNumOperands(); i < e; i++)
- Ops.push_back(VecRed->getOperand(i));
- SDValue Red = DCI.DAG.getNode(OpcodeA, dl,
- DCI.DAG.getVTList({MVT::i32, MVT::i32}), Ops);
- return DCI.DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Red,
- SDValue(Red.getNode(), 1));
+ Ops.push_back(DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, NA,
+ DAG.getConstant(0, dl, MVT::i32)));
+ Ops.push_back(DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, NA,
+ DAG.getConstant(1, dl, MVT::i32)));
+ unsigned S = VecRed->getOpcode() == OpcodeA ? 2 : 0;
+ for (unsigned I = S, E = VecRed.getNumOperands(); I < E; I++)
+ Ops.push_back(VecRed->getOperand(I));
+ SDValue Red =
+ DAG.getNode(OpcodeA, dl, DAG.getVTList({MVT::i32, MVT::i32}), Ops);
+ return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Red,
+ SDValue(Red.getNode(), 1));
};
if (SDValue M = MakeVecReduce(ARMISD::VADDLVs, ARMISD::VADDLVAs, N0, N1))
@@ -12638,7 +13318,7 @@ static SDValue PerformADDCombine(SDNode *N,
if (SDValue Result = PerformSHLSimplify(N, DCI, Subtarget))
return Result;
- if (SDValue Result = PerformADDVecReduce(N, DCI, Subtarget))
+ if (SDValue Result = PerformADDVecReduce(N, DCI.DAG, Subtarget))
return Result;
// First try with the default operand order.
@@ -12649,6 +13329,26 @@ static SDValue PerformADDCombine(SDNode *N,
return PerformADDCombineWithOperands(N, N1, N0, DCI, Subtarget);
}
+// Combine (sub 0, (csinc X, Y, CC)) -> (csinv -X, Y, CC)
+// providing -X is as cheap as X (currently, just a constant).
+static SDValue PerformSubCSINCCombine(SDNode *N, SelectionDAG &DAG) {
+ if (N->getValueType(0) != MVT::i32 || !isNullConstant(N->getOperand(0)))
+ return SDValue();
+ SDValue CSINC = N->getOperand(1);
+ if (CSINC.getOpcode() != ARMISD::CSINC || !CSINC.hasOneUse())
+ return SDValue();
+
+ ConstantSDNode *X = dyn_cast<ConstantSDNode>(CSINC.getOperand(0));
+ if (!X)
+ return SDValue();
+
+ return DAG.getNode(ARMISD::CSINV, SDLoc(N), MVT::i32,
+ DAG.getNode(ISD::SUB, SDLoc(N), MVT::i32, N->getOperand(0),
+ CSINC.getOperand(0)),
+ CSINC.getOperand(1), CSINC.getOperand(2),
+ CSINC.getOperand(3));
+}
+
/// PerformSUBCombine - Target-specific dag combine xforms for ISD::SUB.
///
static SDValue PerformSUBCombine(SDNode *N,
@@ -12662,6 +13362,9 @@ static SDValue PerformSUBCombine(SDNode *N,
if (SDValue Result = combineSelectAndUse(N, N1, N0, DCI))
return Result;
+ if (SDValue R = PerformSubCSINCCombine(N, DCI.DAG))
+ return R;
+
if (!Subtarget->hasMVEIntegerOps() || !N->getValueType(0).isVector())
return SDValue();
@@ -13254,8 +13957,7 @@ static bool CanInvertMVEVCMP(SDValue N) {
return isValidMVECond(CC, N->getOperand(0).getValueType().isFloatingPoint());
}
-static SDValue PerformORCombine_i1(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI,
+static SDValue PerformORCombine_i1(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *Subtarget) {
// Try to invert "or A, B" -> "and ~A, ~B", as the "and" is easier to chain
// together with predicates
@@ -13274,10 +13976,10 @@ static SDValue PerformORCombine_i1(SDNode *N,
if (!(IsFreelyInvertable(N0) || IsFreelyInvertable(N1)))
return SDValue();
- SDValue NewN0 = DCI.DAG.getLogicalNOT(DL, N0, VT);
- SDValue NewN1 = DCI.DAG.getLogicalNOT(DL, N1, VT);
- SDValue And = DCI.DAG.getNode(ISD::AND, DL, VT, NewN0, NewN1);
- return DCI.DAG.getLogicalNOT(DL, And, VT);
+ SDValue NewN0 = DAG.getLogicalNOT(DL, N0, VT);
+ SDValue NewN1 = DAG.getLogicalNOT(DL, N1, VT);
+ SDValue And = DAG.getNode(ISD::AND, DL, VT, NewN0, NewN1);
+ return DAG.getLogicalNOT(DL, And, VT);
}
/// PerformORCombine - Target-specific dag combine xforms for ISD::OR
@@ -13295,7 +13997,7 @@ static SDValue PerformORCombine(SDNode *N,
if (Subtarget->hasMVEIntegerOps() &&
(VT == MVT::v4i1 || VT == MVT::v8i1 || VT == MVT::v16i1))
- return PerformORCombine_i1(N, DCI, Subtarget);
+ return PerformORCombine_i1(N, DAG, Subtarget);
APInt SplatBits, SplatUndef;
unsigned SplatBitSize;
@@ -13414,8 +14116,8 @@ static SDValue PerformXORCombine(SDNode *N,
Ops.push_back(N0->getOperand(0));
if (N0->getOpcode() == ARMISD::VCMP)
Ops.push_back(N0->getOperand(1));
- Ops.push_back(DCI.DAG.getConstant(CC, DL, MVT::i32));
- return DCI.DAG.getNode(N0->getOpcode(), DL, N0->getValueType(0), Ops);
+ Ops.push_back(DAG.getConstant(CC, DL, MVT::i32));
+ return DAG.getNode(N0->getOpcode(), DL, N0->getValueType(0), Ops);
}
}
}
@@ -13456,52 +14158,40 @@ static bool BitsProperlyConcatenate(const APInt &A, const APInt &B) {
}
static SDValue FindBFIToCombineWith(SDNode *N) {
- // We have a BFI in N. Follow a possible chain of BFIs and find a BFI it can combine with,
- // if one exists.
+ // We have a BFI in N. Find a BFI it can combine with, if one exists.
APInt ToMask, FromMask;
SDValue From = ParseBFI(N, ToMask, FromMask);
SDValue To = N->getOperand(0);
- // Now check for a compatible BFI to merge with. We can pass through BFIs that
- // aren't compatible, but not if they set the same bit in their destination as
- // we do (or that of any BFI we're going to combine with).
SDValue V = To;
- APInt CombinedToMask = ToMask;
- while (V.getOpcode() == ARMISD::BFI) {
- APInt NewToMask, NewFromMask;
- SDValue NewFrom = ParseBFI(V.getNode(), NewToMask, NewFromMask);
- if (NewFrom != From) {
- // This BFI has a different base. Keep going.
- CombinedToMask |= NewToMask;
- V = V.getOperand(0);
- continue;
- }
+ if (V.getOpcode() != ARMISD::BFI)
+ return SDValue();
- // Do the written bits conflict with any we've seen so far?
- if ((NewToMask & CombinedToMask).getBoolValue())
- // Conflicting bits - bail out because going further is unsafe.
- return SDValue();
+ APInt NewToMask, NewFromMask;
+ SDValue NewFrom = ParseBFI(V.getNode(), NewToMask, NewFromMask);
+ if (NewFrom != From)
+ return SDValue();
- // Are the new bits contiguous when combined with the old bits?
- if (BitsProperlyConcatenate(ToMask, NewToMask) &&
- BitsProperlyConcatenate(FromMask, NewFromMask))
- return V;
- if (BitsProperlyConcatenate(NewToMask, ToMask) &&
- BitsProperlyConcatenate(NewFromMask, FromMask))
- return V;
+ // Do the written bits conflict with any we've seen so far?
+ if ((NewToMask & ToMask).getBoolValue())
+ // Conflicting bits.
+ return SDValue();
- // We've seen a write to some bits, so track it.
- CombinedToMask |= NewToMask;
- // Keep going...
- V = V.getOperand(0);
- }
+ // Are the new bits contiguous when combined with the old bits?
+ if (BitsProperlyConcatenate(ToMask, NewToMask) &&
+ BitsProperlyConcatenate(FromMask, NewFromMask))
+ return V;
+ if (BitsProperlyConcatenate(NewToMask, ToMask) &&
+ BitsProperlyConcatenate(NewFromMask, FromMask))
+ return V;
return SDValue();
}
-static SDValue PerformBFICombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI) {
+static SDValue PerformBFICombine(SDNode *N, SelectionDAG &DAG) {
+ SDValue N0 = N->getOperand(0);
SDValue N1 = N->getOperand(1);
+
if (N1.getOpcode() == ISD::AND) {
// (bfi A, (and B, Mask1), Mask2) -> (bfi A, B, Mask2) iff
// the bits being cleared by the AND are not demanded by the BFI.
@@ -13517,17 +14207,13 @@ static SDValue PerformBFICombine(SDNode *N,
unsigned Mask = (1u << Width) - 1;
unsigned Mask2 = N11C->getZExtValue();
if ((Mask & (~Mask2)) == 0)
- return DCI.DAG.getNode(ARMISD::BFI, SDLoc(N), N->getValueType(0),
- N->getOperand(0), N1.getOperand(0),
- N->getOperand(2));
- } else if (N->getOperand(0).getOpcode() == ARMISD::BFI) {
- // We have a BFI of a BFI. Walk up the BFI chain to see how long it goes.
- // Keep track of any consecutive bits set that all come from the same base
- // value. We can combine these together into a single BFI.
- SDValue CombineBFI = FindBFIToCombineWith(N);
- if (CombineBFI == SDValue())
- return SDValue();
+ return DAG.getNode(ARMISD::BFI, SDLoc(N), N->getValueType(0),
+ N->getOperand(0), N1.getOperand(0), N->getOperand(2));
+ return SDValue();
+ }
+ // Look for another BFI to combine with.
+ if (SDValue CombineBFI = FindBFIToCombineWith(N)) {
// We've found a BFI.
APInt ToMask1, FromMask1;
SDValue From1 = ParseBFI(N, ToMask1, FromMask1);
@@ -13537,9 +14223,7 @@ static SDValue PerformBFICombine(SDNode *N,
assert(From1 == From2);
(void)From2;
- // First, unlink CombineBFI.
- DCI.DAG.ReplaceAllUsesWith(CombineBFI, CombineBFI.getOperand(0));
- // Then create a new BFI, combining the two together.
+ // Create a new BFI, combining the two together.
APInt NewFromMask = FromMask1 | FromMask2;
APInt NewToMask = ToMask1 | ToMask2;
@@ -13547,12 +14231,33 @@ static SDValue PerformBFICombine(SDNode *N,
SDLoc dl(N);
if (NewFromMask[0] == 0)
- From1 = DCI.DAG.getNode(
- ISD::SRL, dl, VT, From1,
- DCI.DAG.getConstant(NewFromMask.countTrailingZeros(), dl, VT));
- return DCI.DAG.getNode(ARMISD::BFI, dl, VT, N->getOperand(0), From1,
- DCI.DAG.getConstant(~NewToMask, dl, VT));
+ From1 = DAG.getNode(
+ ISD::SRL, dl, VT, From1,
+ DAG.getConstant(NewFromMask.countTrailingZeros(), dl, VT));
+ return DAG.getNode(ARMISD::BFI, dl, VT, CombineBFI.getOperand(0), From1,
+ DAG.getConstant(~NewToMask, dl, VT));
}
+
+ // Reassociate BFI(BFI (A, B, M1), C, M2) to BFI(BFI (A, C, M2), B, M1) so
+ // that lower bit insertions are performed first, providing that M1 and M2
+ // do no overlap. This can allow multiple BFI instructions to be combined
+ // together by the other folds above.
+ if (N->getOperand(0).getOpcode() == ARMISD::BFI) {
+ APInt ToMask1 = ~N->getConstantOperandAPInt(2);
+ APInt ToMask2 = ~N0.getConstantOperandAPInt(2);
+
+ if (!N0.hasOneUse() || (ToMask1 & ToMask2) != 0 ||
+ ToMask1.countLeadingZeros() < ToMask2.countLeadingZeros())
+ return SDValue();
+
+ EVT VT = N->getValueType(0);
+ SDLoc dl(N);
+ SDValue BFI1 = DAG.getNode(ARMISD::BFI, dl, VT, N0.getOperand(0),
+ N->getOperand(1), N->getOperand(2));
+ return DAG.getNode(ARMISD::BFI, dl, VT, BFI1, N0.getOperand(1),
+ N0.getOperand(2));
+ }
+
return SDValue();
}
@@ -13597,6 +14302,54 @@ static SDValue PerformVMOVRRDCombine(SDNode *N,
return Result;
}
+ // VMOVRRD(extract(..(build_vector(a, b, c, d)))) -> a,b or c,d
+ // VMOVRRD(extract(insert_vector(insert_vector(.., a, l1), b, l2))) -> a,b
+ if (InDouble.getOpcode() == ISD::EXTRACT_VECTOR_ELT &&
+ isa<ConstantSDNode>(InDouble.getOperand(1))) {
+ SDValue BV = InDouble.getOperand(0);
+ // Look up through any nop bitcasts and vector_reg_casts. bitcasts may
+ // change lane order under big endian.
+ bool BVSwap = BV.getOpcode() == ISD::BITCAST;
+ while (
+ (BV.getOpcode() == ISD::BITCAST ||
+ BV.getOpcode() == ARMISD::VECTOR_REG_CAST) &&
+ (BV.getValueType() == MVT::v2f64 || BV.getValueType() == MVT::v2i64)) {
+ BVSwap = BV.getOpcode() == ISD::BITCAST;
+ BV = BV.getOperand(0);
+ }
+ if (BV.getValueType() != MVT::v4i32)
+ return SDValue();
+
+ // Handle buildvectors, pulling out the correct lane depending on
+ // endianness.
+ unsigned Offset = InDouble.getConstantOperandVal(1) == 1 ? 2 : 0;
+ if (BV.getOpcode() == ISD::BUILD_VECTOR) {
+ SDValue Op0 = BV.getOperand(Offset);
+ SDValue Op1 = BV.getOperand(Offset + 1);
+ if (!Subtarget->isLittle() && BVSwap)
+ std::swap(Op0, Op1);
+
+ return DCI.DAG.getMergeValues({Op0, Op1}, SDLoc(N));
+ }
+
+ // A chain of insert_vectors, grabbing the correct value of the chain of
+ // inserts.
+ SDValue Op0, Op1;
+ while (BV.getOpcode() == ISD::INSERT_VECTOR_ELT) {
+ if (isa<ConstantSDNode>(BV.getOperand(2))) {
+ if (BV.getConstantOperandVal(2) == Offset)
+ Op0 = BV.getOperand(1);
+ if (BV.getConstantOperandVal(2) == Offset + 1)
+ Op1 = BV.getOperand(1);
+ }
+ BV = BV.getOperand(0);
+ }
+ if (!Subtarget->isLittle() && BVSwap)
+ std::swap(Op0, Op1);
+ if (Op0 && Op1)
+ return DCI.DAG.getMergeValues({Op0, Op1}, SDLoc(N));
+ }
+
return SDValue();
}
@@ -13618,7 +14371,8 @@ static SDValue PerformVMOVDRRCombine(SDNode *N, SelectionDAG &DAG) {
return SDValue();
}
-static SDValue PerformVMOVhrCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) {
+static SDValue PerformVMOVhrCombine(SDNode *N,
+ TargetLowering::DAGCombinerInfo &DCI) {
SDValue Op0 = N->getOperand(0);
// VMOVhr (VMOVrh (X)) -> X
@@ -13664,15 +14418,14 @@ static SDValue PerformVMOVhrCombine(SDNode *N, TargetLowering::DAGCombinerInfo &
return SDValue();
}
-static SDValue PerformVMOVrhCombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI) {
+static SDValue PerformVMOVrhCombine(SDNode *N, SelectionDAG &DAG) {
SDValue N0 = N->getOperand(0);
EVT VT = N->getValueType(0);
// fold (VMOVrh (fpconst x)) -> const x
if (ConstantFPSDNode *C = dyn_cast<ConstantFPSDNode>(N0)) {
APFloat V = C->getValueAPF();
- return DCI.DAG.getConstant(V.bitcastToAPInt().getZExtValue(), SDLoc(N), VT);
+ return DAG.getConstant(V.bitcastToAPInt().getZExtValue(), SDLoc(N), VT);
}
// fold (VMOVrh (load x)) -> (zextload (i16*)x)
@@ -13680,18 +14433,18 @@ static SDValue PerformVMOVrhCombine(SDNode *N,
LoadSDNode *LN0 = cast<LoadSDNode>(N0);
SDValue Load =
- DCI.DAG.getExtLoad(ISD::ZEXTLOAD, SDLoc(N), VT, LN0->getChain(),
- LN0->getBasePtr(), MVT::i16, LN0->getMemOperand());
- DCI.DAG.ReplaceAllUsesOfValueWith(SDValue(N, 0), Load.getValue(0));
- DCI.DAG.ReplaceAllUsesOfValueWith(N0.getValue(1), Load.getValue(1));
+ DAG.getExtLoad(ISD::ZEXTLOAD, SDLoc(N), VT, LN0->getChain(),
+ LN0->getBasePtr(), MVT::i16, LN0->getMemOperand());
+ DAG.ReplaceAllUsesOfValueWith(SDValue(N, 0), Load.getValue(0));
+ DAG.ReplaceAllUsesOfValueWith(N0.getValue(1), Load.getValue(1));
return Load;
}
// Fold VMOVrh(extract(x, n)) -> vgetlaneu(x, n)
if (N0->getOpcode() == ISD::EXTRACT_VECTOR_ELT &&
isa<ConstantSDNode>(N0->getOperand(1)))
- return DCI.DAG.getNode(ARMISD::VGETLANEu, SDLoc(N), VT, N0->getOperand(0),
- N0->getOperand(1));
+ return DAG.getNode(ARMISD::VGETLANEu, SDLoc(N), VT, N0->getOperand(0),
+ N0->getOperand(1));
return SDValue();
}
@@ -13869,30 +14622,32 @@ PerformPREDICATE_CASTCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) {
return SDValue();
}
-static SDValue
-PerformVECTOR_REG_CASTCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
- const ARMSubtarget *ST) {
+static SDValue PerformVECTOR_REG_CASTCombine(SDNode *N, SelectionDAG &DAG,
+ const ARMSubtarget *ST) {
EVT VT = N->getValueType(0);
SDValue Op = N->getOperand(0);
SDLoc dl(N);
// Under Little endian, a VECTOR_REG_CAST is equivalent to a BITCAST
if (ST->isLittle())
- return DCI.DAG.getNode(ISD::BITCAST, dl, VT, Op);
+ return DAG.getNode(ISD::BITCAST, dl, VT, Op);
+
+ // VECTOR_REG_CAST undef -> undef
+ if (Op.isUndef())
+ return DAG.getUNDEF(VT);
// VECTOR_REG_CAST(VECTOR_REG_CAST(x)) == VECTOR_REG_CAST(x)
if (Op->getOpcode() == ARMISD::VECTOR_REG_CAST) {
// If the valuetypes are the same, we can remove the cast entirely.
if (Op->getOperand(0).getValueType() == VT)
return Op->getOperand(0);
- return DCI.DAG.getNode(ARMISD::VECTOR_REG_CAST, dl, VT, Op->getOperand(0));
+ return DAG.getNode(ARMISD::VECTOR_REG_CAST, dl, VT, Op->getOperand(0));
}
return SDValue();
}
-static SDValue PerformVCMPCombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI,
+static SDValue PerformVCMPCombine(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *Subtarget) {
if (!Subtarget->hasMVEIntegerOps())
return SDValue();
@@ -13906,19 +14661,18 @@ static SDValue PerformVCMPCombine(SDNode *N,
// vcmp X, 0, cc -> vcmpz X, cc
if (isZeroVector(Op1))
- return DCI.DAG.getNode(ARMISD::VCMPZ, dl, VT, Op0,
- N->getOperand(2));
+ return DAG.getNode(ARMISD::VCMPZ, dl, VT, Op0, N->getOperand(2));
unsigned SwappedCond = getSwappedCondition(Cond);
if (isValidMVECond(SwappedCond, VT.isFloatingPoint())) {
// vcmp 0, X, cc -> vcmpz X, reversed(cc)
if (isZeroVector(Op0))
- return DCI.DAG.getNode(ARMISD::VCMPZ, dl, VT, Op1,
- DCI.DAG.getConstant(SwappedCond, dl, MVT::i32));
+ return DAG.getNode(ARMISD::VCMPZ, dl, VT, Op1,
+ DAG.getConstant(SwappedCond, dl, MVT::i32));
// vcmp vdup(Y), X, cc -> vcmp X, vdup(Y), reversed(cc)
if (Op0->getOpcode() == ARMISD::VDUP && Op1->getOpcode() != ARMISD::VDUP)
- return DCI.DAG.getNode(ARMISD::VCMP, dl, VT, Op1, Op0,
- DCI.DAG.getConstant(SwappedCond, dl, MVT::i32));
+ return DAG.getNode(ARMISD::VCMP, dl, VT, Op1, Op0,
+ DAG.getConstant(SwappedCond, dl, MVT::i32));
}
return SDValue();
@@ -13950,8 +14704,73 @@ static SDValue PerformInsertEltCombine(SDNode *N,
return DAG.getNode(ISD::BITCAST, dl, VT, InsElt);
}
+// Convert a pair of extracts from the same base vector to a VMOVRRD. Either
+// directly or bitcast to an integer if the original is a float vector.
+// extract(x, n); extract(x, n+1) -> VMOVRRD(extract v2f64 x, n/2)
+// bitcast(extract(x, n)); bitcast(extract(x, n+1)) -> VMOVRRD(extract x, n/2)
+static SDValue
+PerformExtractEltToVMOVRRD(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) {
+ EVT VT = N->getValueType(0);
+ SDLoc dl(N);
+
+ if (!DCI.isAfterLegalizeDAG() || VT != MVT::i32 ||
+ !DCI.DAG.getTargetLoweringInfo().isTypeLegal(MVT::f64))
+ return SDValue();
+
+ SDValue Ext = SDValue(N, 0);
+ if (Ext.getOpcode() == ISD::BITCAST &&
+ Ext.getOperand(0).getValueType() == MVT::f32)
+ Ext = Ext.getOperand(0);
+ if (Ext.getOpcode() != ISD::EXTRACT_VECTOR_ELT ||
+ !isa<ConstantSDNode>(Ext.getOperand(1)) ||
+ Ext.getConstantOperandVal(1) % 2 != 0)
+ return SDValue();
+ if (Ext->use_size() == 1 &&
+ (Ext->use_begin()->getOpcode() == ISD::SINT_TO_FP ||
+ Ext->use_begin()->getOpcode() == ISD::UINT_TO_FP))
+ return SDValue();
+
+ SDValue Op0 = Ext.getOperand(0);
+ EVT VecVT = Op0.getValueType();
+ unsigned Lane = Ext.getConstantOperandVal(1);
+ if (VecVT.getVectorNumElements() != 4)
+ return SDValue();
+
+ // Find another extract, of Lane + 1
+ auto OtherIt = find_if(Op0->uses(), [&](SDNode *V) {
+ return V->getOpcode() == ISD::EXTRACT_VECTOR_ELT &&
+ isa<ConstantSDNode>(V->getOperand(1)) &&
+ V->getConstantOperandVal(1) == Lane + 1;
+ });
+ if (OtherIt == Op0->uses().end())
+ return SDValue();
+
+ // For float extracts, we need to be converting to a i32 for both vector
+ // lanes.
+ SDValue OtherExt(*OtherIt, 0);
+ if (OtherExt.getValueType() != MVT::i32) {
+ if (OtherExt->use_size() != 1 ||
+ OtherExt->use_begin()->getOpcode() != ISD::BITCAST ||
+ OtherExt->use_begin()->getValueType(0) != MVT::i32)
+ return SDValue();
+ OtherExt = SDValue(*OtherExt->use_begin(), 0);
+ }
+
+ // Convert the type to a f64 and extract with a VMOVRRD.
+ SDValue F64 = DCI.DAG.getNode(
+ ISD::EXTRACT_VECTOR_ELT, dl, MVT::f64,
+ DCI.DAG.getNode(ARMISD::VECTOR_REG_CAST, dl, MVT::v2f64, Op0),
+ DCI.DAG.getConstant(Ext.getConstantOperandVal(1) / 2, dl, MVT::i32));
+ SDValue VMOVRRD =
+ DCI.DAG.getNode(ARMISD::VMOVRRD, dl, {MVT::i32, MVT::i32}, F64);
+
+ DCI.CombineTo(OtherExt.getNode(), SDValue(VMOVRRD.getNode(), 1));
+ return VMOVRRD;
+}
+
static SDValue PerformExtractEltCombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI) {
+ TargetLowering::DAGCombinerInfo &DCI,
+ const ARMSubtarget *ST) {
SDValue Op0 = N->getOperand(0);
EVT VT = N->getValueType(0);
SDLoc dl(N);
@@ -13963,6 +14782,8 @@ static SDValue PerformExtractEltCombine(SDNode *N,
return DCI.DAG.getNode(ARMISD::VMOVhr, dl, VT, X);
if (VT == MVT::i32 && X.getValueType() == MVT::f16)
return DCI.DAG.getNode(ARMISD::VMOVrh, dl, VT, X);
+ if (VT == MVT::f32 && X.getValueType() == MVT::i32)
+ return DCI.DAG.getNode(ISD::BITCAST, dl, VT, X);
while (X.getValueType() != VT && X->getOpcode() == ISD::BITCAST)
X = X->getOperand(0);
@@ -13970,12 +14791,131 @@ static SDValue PerformExtractEltCombine(SDNode *N,
return X;
}
+ // extract ARM_BUILD_VECTOR -> x
+ if (Op0->getOpcode() == ARMISD::BUILD_VECTOR &&
+ isa<ConstantSDNode>(N->getOperand(1)) &&
+ N->getConstantOperandVal(1) < Op0.getNumOperands()) {
+ return Op0.getOperand(N->getConstantOperandVal(1));
+ }
+
+ // extract(bitcast(BUILD_VECTOR(VMOVDRR(a, b), ..))) -> a or b
+ if (Op0.getValueType() == MVT::v4i32 &&
+ isa<ConstantSDNode>(N->getOperand(1)) &&
+ Op0.getOpcode() == ISD::BITCAST &&
+ Op0.getOperand(0).getOpcode() == ISD::BUILD_VECTOR &&
+ Op0.getOperand(0).getValueType() == MVT::v2f64) {
+ SDValue BV = Op0.getOperand(0);
+ unsigned Offset = N->getConstantOperandVal(1);
+ SDValue MOV = BV.getOperand(Offset < 2 ? 0 : 1);
+ if (MOV.getOpcode() == ARMISD::VMOVDRR)
+ return MOV.getOperand(ST->isLittle() ? Offset % 2 : 1 - Offset % 2);
+ }
+
+ // extract x, n; extract x, n+1 -> VMOVRRD x
+ if (SDValue R = PerformExtractEltToVMOVRRD(N, DCI))
+ return R;
+
+ // extract (MVETrunc(x)) -> extract x
+ if (Op0->getOpcode() == ARMISD::MVETRUNC) {
+ unsigned Idx = N->getConstantOperandVal(1);
+ unsigned Vec =
+ Idx / Op0->getOperand(0).getValueType().getVectorNumElements();
+ unsigned SubIdx =
+ Idx % Op0->getOperand(0).getValueType().getVectorNumElements();
+ return DCI.DAG.getNode(ISD::EXTRACT_VECTOR_ELT, dl, VT, Op0.getOperand(Vec),
+ DCI.DAG.getConstant(SubIdx, dl, MVT::i32));
+ }
+
+ return SDValue();
+}
+
+static SDValue PerformSignExtendInregCombine(SDNode *N, SelectionDAG &DAG) {
+ SDValue Op = N->getOperand(0);
+ EVT VT = N->getValueType(0);
+
+ // sext_inreg(VGETLANEu) -> VGETLANEs
+ if (Op.getOpcode() == ARMISD::VGETLANEu &&
+ cast<VTSDNode>(N->getOperand(1))->getVT() ==
+ Op.getOperand(0).getValueType().getScalarType())
+ return DAG.getNode(ARMISD::VGETLANEs, SDLoc(N), VT, Op.getOperand(0),
+ Op.getOperand(1));
+
+ return SDValue();
+}
+
+// When lowering complex nodes that we recognize, like VQDMULH and MULH, we
+// can end up with shuffle(binop(shuffle, shuffle)), that can be simplified to
+// binop as the shuffles cancel out.
+static SDValue FlattenVectorShuffle(ShuffleVectorSDNode *N, SelectionDAG &DAG) {
+ EVT VT = N->getValueType(0);
+ if (!N->getOperand(1).isUndef() || N->getOperand(0).getValueType() != VT)
+ return SDValue();
+ SDValue Op = N->getOperand(0);
+
+ // Looking for binary operators that will have been folded from
+ // truncates/extends.
+ switch (Op.getOpcode()) {
+ case ARMISD::VQDMULH:
+ case ISD::MULHS:
+ case ISD::MULHU:
+ case ISD::ABDS:
+ case ISD::ABDU:
+ break;
+ default:
+ return SDValue();
+ }
+
+ ShuffleVectorSDNode *Op0 = dyn_cast<ShuffleVectorSDNode>(Op.getOperand(0));
+ ShuffleVectorSDNode *Op1 = dyn_cast<ShuffleVectorSDNode>(Op.getOperand(1));
+ if (!Op0 || !Op1 || !Op0->getOperand(1).isUndef() ||
+ !Op1->getOperand(1).isUndef() || Op0->getMask() != Op1->getMask() ||
+ Op0->getOperand(0).getValueType() != VT)
+ return SDValue();
+
+ // Check the mask turns into an identity shuffle.
+ ArrayRef<int> NMask = N->getMask();
+ ArrayRef<int> OpMask = Op0->getMask();
+ for (int i = 0, e = NMask.size(); i != e; i++) {
+ if (NMask[i] > 0 && OpMask[NMask[i]] > 0 && OpMask[NMask[i]] != i)
+ return SDValue();
+ }
+
+ return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(),
+ Op0->getOperand(0), Op1->getOperand(0));
+}
+
+// shuffle(MVETrunc(x, y)) -> VMOVN(x, y)
+static SDValue PerformShuffleVMOVNCombine(ShuffleVectorSDNode *N,
+ SelectionDAG &DAG) {
+ SDValue Trunc = N->getOperand(0);
+ EVT VT = Trunc.getValueType();
+ if (Trunc.getOpcode() != ARMISD::MVETRUNC || !N->getOperand(1).isUndef())
+ return SDValue();
+
+ SDLoc DL(Trunc);
+ if (isVMOVNTruncMask(N->getMask(), VT, 0))
+ return DAG.getNode(
+ ARMISD::VMOVN, DL, VT,
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, Trunc.getOperand(0)),
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, Trunc.getOperand(1)),
+ DAG.getConstant(1, DL, MVT::i32));
+ else if (isVMOVNTruncMask(N->getMask(), VT, 1))
+ return DAG.getNode(
+ ARMISD::VMOVN, DL, VT,
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, Trunc.getOperand(1)),
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, Trunc.getOperand(0)),
+ DAG.getConstant(1, DL, MVT::i32));
return SDValue();
}
/// PerformVECTOR_SHUFFLECombine - Target-specific dag combine xforms for
/// ISD::VECTOR_SHUFFLE.
static SDValue PerformVECTOR_SHUFFLECombine(SDNode *N, SelectionDAG &DAG) {
+ if (SDValue R = FlattenVectorShuffle(cast<ShuffleVectorSDNode>(N), DAG))
+ return R;
+ if (SDValue R = PerformShuffleVMOVNCombine(cast<ShuffleVectorSDNode>(N), DAG))
+ return R;
+
// The LLVM shufflevector instruction does not require the shuffle mask
// length to match the operand vector length, but ISD::VECTOR_SHUFFLE does
// have that requirement. When translating to ISD::VECTOR_SHUFFLE, if the
@@ -14064,6 +15004,9 @@ static SDValue CombineBaseUpdate(SDNode *N,
// Find the new opcode for the updating load/store.
bool isLoadOp = true;
bool isLaneOp = false;
+ // Workaround for vst1x and vld1x intrinsics which do not have alignment
+ // as an operand.
+ bool hasAlignment = true;
unsigned NewOpc = 0;
unsigned NumVecs = 0;
if (isIntrinsic) {
@@ -14078,15 +15021,18 @@ static SDValue CombineBaseUpdate(SDNode *N,
NumVecs = 3; break;
case Intrinsic::arm_neon_vld4: NewOpc = ARMISD::VLD4_UPD;
NumVecs = 4; break;
- case Intrinsic::arm_neon_vld1x2:
- case Intrinsic::arm_neon_vld1x3:
- case Intrinsic::arm_neon_vld1x4:
- case Intrinsic::arm_neon_vld2dup:
- case Intrinsic::arm_neon_vld3dup:
- case Intrinsic::arm_neon_vld4dup:
- // TODO: Support updating VLD1x and VLDxDUP nodes. For now, we just skip
- // combining base updates for such intrinsics.
- continue;
+ case Intrinsic::arm_neon_vld1x2: NewOpc = ARMISD::VLD1x2_UPD;
+ NumVecs = 2; hasAlignment = false; break;
+ case Intrinsic::arm_neon_vld1x3: NewOpc = ARMISD::VLD1x3_UPD;
+ NumVecs = 3; hasAlignment = false; break;
+ case Intrinsic::arm_neon_vld1x4: NewOpc = ARMISD::VLD1x4_UPD;
+ NumVecs = 4; hasAlignment = false; break;
+ case Intrinsic::arm_neon_vld2dup: NewOpc = ARMISD::VLD2DUP_UPD;
+ NumVecs = 2; break;
+ case Intrinsic::arm_neon_vld3dup: NewOpc = ARMISD::VLD3DUP_UPD;
+ NumVecs = 3; break;
+ case Intrinsic::arm_neon_vld4dup: NewOpc = ARMISD::VLD4DUP_UPD;
+ NumVecs = 4; break;
case Intrinsic::arm_neon_vld2lane: NewOpc = ARMISD::VLD2LN_UPD;
NumVecs = 2; isLaneOp = true; break;
case Intrinsic::arm_neon_vld3lane: NewOpc = ARMISD::VLD3LN_UPD;
@@ -14107,6 +15053,12 @@ static SDValue CombineBaseUpdate(SDNode *N,
NumVecs = 3; isLoadOp = false; isLaneOp = true; break;
case Intrinsic::arm_neon_vst4lane: NewOpc = ARMISD::VST4LN_UPD;
NumVecs = 4; isLoadOp = false; isLaneOp = true; break;
+ case Intrinsic::arm_neon_vst1x2: NewOpc = ARMISD::VST1x2_UPD;
+ NumVecs = 2; isLoadOp = false; hasAlignment = false; break;
+ case Intrinsic::arm_neon_vst1x3: NewOpc = ARMISD::VST1x3_UPD;
+ NumVecs = 3; isLoadOp = false; hasAlignment = false; break;
+ case Intrinsic::arm_neon_vst1x4: NewOpc = ARMISD::VST1x4_UPD;
+ NumVecs = 4; isLoadOp = false; hasAlignment = false; break;
}
} else {
isLaneOp = true;
@@ -14134,8 +15086,12 @@ static SDValue CombineBaseUpdate(SDNode *N,
VecTy = N->getOperand(1).getValueType();
}
+ bool isVLDDUPOp =
+ NewOpc == ARMISD::VLD1DUP_UPD || NewOpc == ARMISD::VLD2DUP_UPD ||
+ NewOpc == ARMISD::VLD3DUP_UPD || NewOpc == ARMISD::VLD4DUP_UPD;
+
unsigned NumBytes = NumVecs * VecTy.getSizeInBits() / 8;
- if (isLaneOp)
+ if (isLaneOp || isVLDDUPOp)
NumBytes /= VecTy.getVectorNumElements();
// If the increment is a constant, it must match the memory ref size.
@@ -14210,7 +15166,9 @@ static SDValue CombineBaseUpdate(SDNode *N,
} else {
// Loads (and of course intrinsics) match the intrinsics' signature,
// so just add all but the alignment operand.
- for (unsigned i = AddrOpIdx + 1; i < N->getNumOperands() - 1; ++i)
+ unsigned LastOperand =
+ hasAlignment ? N->getNumOperands() - 1 : N->getNumOperands();
+ for (unsigned i = AddrOpIdx + 1; i < LastOperand; ++i)
Ops.push_back(N->getOperand(i));
}
@@ -14505,10 +15463,8 @@ static SDValue PerformVDUPLANECombine(SDNode *N,
}
/// PerformVDUPCombine - Target-specific dag combine xforms for ARMISD::VDUP.
-static SDValue PerformVDUPCombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI,
+static SDValue PerformVDUPCombine(SDNode *N, SelectionDAG &DAG,
const ARMSubtarget *Subtarget) {
- SelectionDAG &DAG = DCI.DAG;
SDValue Op = N->getOperand(0);
SDLoc dl(N);
@@ -14516,11 +15472,11 @@ static SDValue PerformVDUPCombine(SDNode *N,
// Convert VDUP f32 -> VDUP BITCAST i32 under MVE, as we know the value will
// need to come from a GPR.
if (Op.getValueType() == MVT::f32)
- return DCI.DAG.getNode(ARMISD::VDUP, dl, N->getValueType(0),
- DAG.getNode(ISD::BITCAST, dl, MVT::i32, Op));
+ return DAG.getNode(ARMISD::VDUP, dl, N->getValueType(0),
+ DAG.getNode(ISD::BITCAST, dl, MVT::i32, Op));
else if (Op.getValueType() == MVT::f16)
- return DCI.DAG.getNode(ARMISD::VDUP, dl, N->getValueType(0),
- DAG.getNode(ARMISD::VMOVrh, dl, MVT::i32, Op));
+ return DAG.getNode(ARMISD::VDUP, dl, N->getValueType(0),
+ DAG.getNode(ARMISD::VMOVrh, dl, MVT::i32, Op));
}
if (!Subtarget->hasNEON())
@@ -14532,12 +15488,12 @@ static SDValue PerformVDUPCombine(SDNode *N,
LoadSDNode *LD = dyn_cast<LoadSDNode>(Op.getNode());
if (LD && Op.hasOneUse() && LD->isUnindexed() &&
LD->getMemoryVT() == N->getValueType(0).getVectorElementType()) {
- SDValue Ops[] = { LD->getOperand(0), LD->getOperand(1),
- DAG.getConstant(LD->getAlignment(), SDLoc(N), MVT::i32) };
+ SDValue Ops[] = {LD->getOperand(0), LD->getOperand(1),
+ DAG.getConstant(LD->getAlignment(), SDLoc(N), MVT::i32)};
SDVTList SDTys = DAG.getVTList(N->getValueType(0), MVT::Other);
- SDValue VLDDup = DAG.getMemIntrinsicNode(ARMISD::VLD1DUP, SDLoc(N), SDTys,
- Ops, LD->getMemoryVT(),
- LD->getMemOperand());
+ SDValue VLDDup =
+ DAG.getMemIntrinsicNode(ARMISD::VLD1DUP, SDLoc(N), SDTys, Ops,
+ LD->getMemoryVT(), LD->getMemOperand());
DAG.ReplaceAllUsesOfValueWith(SDValue(LD, 1), VLDDup.getValue(1));
return VLDDup;
}
@@ -14642,7 +15598,7 @@ static SDValue PerformTruncatingStoreCombine(StoreSDNode *St,
return DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
}
-// Try taking a single vector store from an truncate (which would otherwise turn
+// Try taking a single vector store from an fpround (which would otherwise turn
// into an expensive buildvector) and splitting it into a series of narrowing
// stores.
static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
@@ -14650,7 +15606,7 @@ static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
if (!St->isSimple() || St->isTruncatingStore() || !St->isUnindexed())
return SDValue();
SDValue Trunc = St->getValue();
- if (Trunc->getOpcode() != ISD::TRUNCATE && Trunc->getOpcode() != ISD::FP_ROUND)
+ if (Trunc->getOpcode() != ISD::FP_ROUND)
return SDValue();
EVT FromVT = Trunc->getOperand(0).getValueType();
EVT ToVT = Trunc.getValueType();
@@ -14660,16 +15616,11 @@ static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
EVT ToEltVT = ToVT.getVectorElementType();
EVT FromEltVT = FromVT.getVectorElementType();
- unsigned NumElements = 0;
- if (FromEltVT == MVT::i32 && (ToEltVT == MVT::i16 || ToEltVT == MVT::i8))
- NumElements = 4;
- if (FromEltVT == MVT::i16 && ToEltVT == MVT::i8)
- NumElements = 8;
- if (FromEltVT == MVT::f32 && ToEltVT == MVT::f16)
- NumElements = 4;
- if (NumElements == 0 ||
- (FromEltVT != MVT::f32 && FromVT.getVectorNumElements() == NumElements) ||
- FromVT.getVectorNumElements() % NumElements != 0)
+ if (FromEltVT != MVT::f32 || ToEltVT != MVT::f16)
+ return SDValue();
+
+ unsigned NumElements = 4;
+ if (FromVT.getVectorNumElements() % NumElements != 0)
return SDValue();
// Test if the Trunc will be convertable to a VMOVN with a shuffle, and if so
@@ -14698,14 +15649,6 @@ static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
return true;
};
- // It may be preferable to keep the store unsplit as the trunc may end up
- // being removed. Check that here.
- if (Trunc.getOperand(0).getOpcode() == ISD::SMIN) {
- if (SDValue U = PerformVQDMULHCombine(Trunc.getOperand(0).getNode(), DAG)) {
- DAG.ReplaceAllUsesWith(Trunc.getOperand(0), U);
- return SDValue();
- }
- }
if (auto *Shuffle = dyn_cast<ShuffleVectorSDNode>(Trunc.getOperand(0)))
if (isVMOVNShuffle(Shuffle, false) || isVMOVNShuffle(Shuffle, true))
return SDValue();
@@ -14735,12 +15678,10 @@ static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, NewFromVT, Trunc.getOperand(0),
DAG.getConstant(i * NumElements, DL, MVT::i32));
- if (ToEltVT == MVT::f16) {
- SDValue FPTrunc =
- DAG.getNode(ARMISD::VCVTN, DL, MVT::v8f16, DAG.getUNDEF(MVT::v8f16),
- Extract, DAG.getConstant(0, DL, MVT::i32));
- Extract = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, MVT::v4i32, FPTrunc);
- }
+ SDValue FPTrunc =
+ DAG.getNode(ARMISD::VCVTN, DL, MVT::v8f16, DAG.getUNDEF(MVT::v8f16),
+ Extract, DAG.getConstant(0, DL, MVT::i32));
+ Extract = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, MVT::v4i32, FPTrunc);
SDValue Store = DAG.getTruncStore(
Ch, DL, Extract, NewPtr, St->getPointerInfo().getWithOffset(NewOffset),
@@ -14750,6 +15691,83 @@ static SDValue PerformSplittingToNarrowingStores(StoreSDNode *St,
return DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Stores);
}
+// Try taking a single vector store from an MVETRUNC (which would otherwise turn
+// into an expensive buildvector) and splitting it into a series of narrowing
+// stores.
+static SDValue PerformSplittingMVETruncToNarrowingStores(StoreSDNode *St,
+ SelectionDAG &DAG) {
+ if (!St->isSimple() || St->isTruncatingStore() || !St->isUnindexed())
+ return SDValue();
+ SDValue Trunc = St->getValue();
+ if (Trunc->getOpcode() != ARMISD::MVETRUNC)
+ return SDValue();
+ EVT FromVT = Trunc->getOperand(0).getValueType();
+ EVT ToVT = Trunc.getValueType();
+
+ LLVMContext &C = *DAG.getContext();
+ SDLoc DL(St);
+ // Details about the old store
+ SDValue Ch = St->getChain();
+ SDValue BasePtr = St->getBasePtr();
+ Align Alignment = St->getOriginalAlign();
+ MachineMemOperand::Flags MMOFlags = St->getMemOperand()->getFlags();
+ AAMDNodes AAInfo = St->getAAInfo();
+
+ EVT NewToVT = EVT::getVectorVT(C, ToVT.getVectorElementType(),
+ FromVT.getVectorNumElements());
+
+ SmallVector<SDValue, 4> Stores;
+ for (unsigned i = 0; i < Trunc.getNumOperands(); i++) {
+ unsigned NewOffset =
+ i * FromVT.getVectorNumElements() * ToVT.getScalarSizeInBits() / 8;
+ SDValue NewPtr =
+ DAG.getObjectPtrOffset(DL, BasePtr, TypeSize::Fixed(NewOffset));
+
+ SDValue Extract = Trunc.getOperand(i);
+ SDValue Store = DAG.getTruncStore(
+ Ch, DL, Extract, NewPtr, St->getPointerInfo().getWithOffset(NewOffset),
+ NewToVT, Alignment.value(), MMOFlags, AAInfo);
+ Stores.push_back(Store);
+ }
+ return DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Stores);
+}
+
+// Given a floating point store from an extracted vector, with an integer
+// VGETLANE that already exists, store the existing VGETLANEu directly. This can
+// help reduce fp register pressure, doesn't require the fp extract and allows
+// use of more integer post-inc stores not available with vstr.
+static SDValue PerformExtractFpToIntStores(StoreSDNode *St, SelectionDAG &DAG) {
+ if (!St->isSimple() || St->isTruncatingStore() || !St->isUnindexed())
+ return SDValue();
+ SDValue Extract = St->getValue();
+ EVT VT = Extract.getValueType();
+ // For now only uses f16. This may be useful for f32 too, but that will
+ // be bitcast(extract), not the VGETLANEu we currently check here.
+ if (VT != MVT::f16 || Extract->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
+ return SDValue();
+
+ SDNode *GetLane =
+ DAG.getNodeIfExists(ARMISD::VGETLANEu, DAG.getVTList(MVT::i32),
+ {Extract.getOperand(0), Extract.getOperand(1)});
+ if (!GetLane)
+ return SDValue();
+
+ LLVMContext &C = *DAG.getContext();
+ SDLoc DL(St);
+ // Create a new integer store to replace the existing floating point version.
+ SDValue Ch = St->getChain();
+ SDValue BasePtr = St->getBasePtr();
+ Align Alignment = St->getOriginalAlign();
+ MachineMemOperand::Flags MMOFlags = St->getMemOperand()->getFlags();
+ AAMDNodes AAInfo = St->getAAInfo();
+ EVT NewToVT = EVT::getIntegerVT(C, VT.getSizeInBits());
+ SDValue Store = DAG.getTruncStore(Ch, DL, SDValue(GetLane, 0), BasePtr,
+ St->getPointerInfo(), NewToVT,
+ Alignment.value(), MMOFlags, AAInfo);
+
+ return Store;
+}
+
/// PerformSTORECombine - Target-specific dag combine xforms for
/// ISD::STORE.
static SDValue PerformSTORECombine(SDNode *N,
@@ -14765,9 +15783,15 @@ static SDValue PerformSTORECombine(SDNode *N,
if (SDValue Store = PerformTruncatingStoreCombine(St, DCI.DAG))
return Store;
- if (Subtarget->hasMVEIntegerOps())
+ if (Subtarget->hasMVEIntegerOps()) {
if (SDValue NewToken = PerformSplittingToNarrowingStores(St, DCI.DAG))
return NewToken;
+ if (SDValue NewChain = PerformExtractFpToIntStores(St, DCI.DAG))
+ return NewChain;
+ if (SDValue NewToken =
+ PerformSplittingMVETruncToNarrowingStores(St, DCI.DAG))
+ return NewToken;
+ }
if (!ISD::isNormalStore(St))
return SDValue();
@@ -14953,12 +15977,12 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
SDLoc dl(N);
// We are looking for something that will have illegal types if left alone,
- // but that we can convert to a single instruction undef MVE. For example
+ // but that we can convert to a single instruction under MVE. For example
// vecreduce_add(sext(A, v8i32)) => VADDV.s16 A
// or
// vecreduce_add(mul(zext(A, v16i32), zext(B, v16i32))) => VMLADAV.u8 A, B
- // Cases:
+ // The legal cases are:
// VADDV u/s 8/16/32
// VMLAV u/s 8/16/32
// VADDLV u/s 32
@@ -14966,6 +15990,13 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
// If the input vector is smaller than legal (v4i8/v4i16 for example) we can
// extend it and use v4i32 instead.
+ auto ExtTypeMatches = [](SDValue A, ArrayRef<MVT> ExtTypes) {
+ EVT AVT = A.getValueType();
+ return any_of(ExtTypes, [&](MVT Ty) {
+ return AVT.getVectorNumElements() == Ty.getVectorNumElements() &&
+ AVT.bitsLE(Ty);
+ });
+ };
auto ExtendIfNeeded = [&](SDValue A, unsigned ExtendCode) {
EVT AVT = A.getValueType();
if (!AVT.is128BitVector())
@@ -14979,7 +16010,7 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
if (ResVT != RetTy || N0->getOpcode() != ExtendCode)
return SDValue();
SDValue A = N0->getOperand(0);
- if (llvm::any_of(ExtTypes, [&A](MVT Ty) { return A.getValueType() == Ty; }))
+ if (ExtTypeMatches(A, ExtTypes))
return ExtendIfNeeded(A, ExtendCode);
return SDValue();
};
@@ -14993,7 +16024,7 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
if (Ext->getOpcode() != ExtendCode)
return SDValue();
SDValue A = Ext->getOperand(0);
- if (llvm::any_of(ExtTypes, [&A](MVT Ty) { return A.getValueType() == Ty; }))
+ if (ExtTypeMatches(A, ExtTypes))
return ExtendIfNeeded(A, ExtendCode);
return SDValue();
};
@@ -15018,13 +16049,11 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return false;
SDValue ExtA = Mul->getOperand(0);
SDValue ExtB = Mul->getOperand(1);
- if (ExtA->getOpcode() != ExtendCode && ExtB->getOpcode() != ExtendCode)
+ if (ExtA->getOpcode() != ExtendCode || ExtB->getOpcode() != ExtendCode)
return false;
A = ExtA->getOperand(0);
B = ExtB->getOperand(0);
- if (A.getValueType() == B.getValueType() &&
- llvm::any_of(ExtTypes,
- [&A](MVT Ty) { return A.getValueType() == Ty; })) {
+ if (ExtTypeMatches(A, ExtTypes) && ExtTypeMatches(B, ExtTypes)) {
A = ExtendIfNeeded(A, ExtendCode);
B = ExtendIfNeeded(B, ExtendCode);
return true;
@@ -15052,13 +16081,11 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return false;
SDValue ExtA = Mul->getOperand(0);
SDValue ExtB = Mul->getOperand(1);
- if (ExtA->getOpcode() != ExtendCode && ExtB->getOpcode() != ExtendCode)
+ if (ExtA->getOpcode() != ExtendCode || ExtB->getOpcode() != ExtendCode)
return false;
A = ExtA->getOperand(0);
B = ExtB->getOperand(0);
- if (A.getValueType() == B.getValueType() &&
- llvm::any_of(ExtTypes,
- [&A](MVT Ty) { return A.getValueType() == Ty; })) {
+ if (ExtTypeMatches(A, ExtTypes) && ExtTypeMatches(B, ExtTypes)) {
A = ExtendIfNeeded(A, ExtendCode);
B = ExtendIfNeeded(B, ExtendCode);
return true;
@@ -15066,6 +16093,32 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return false;
};
auto Create64bitNode = [&](unsigned Opcode, ArrayRef<SDValue> Ops) {
+ // Split illegal MVT::v16i8->i64 vector reductions into two legal v8i16->i64
+ // reductions. The operands are extended with MVEEXT, but as they are
+ // reductions the lane orders do not matter. MVEEXT may be combined with
+ // loads to produce two extending loads, or else they will be expanded to
+ // VREV/VMOVL.
+ EVT VT = Ops[0].getValueType();
+ if (VT == MVT::v16i8) {
+ assert((Opcode == ARMISD::VMLALVs || Opcode == ARMISD::VMLALVu) &&
+ "Unexpected illegal long reduction opcode");
+ bool IsUnsigned = Opcode == ARMISD::VMLALVu;
+
+ SDValue Ext0 =
+ DAG.getNode(IsUnsigned ? ARMISD::MVEZEXT : ARMISD::MVESEXT, dl,
+ DAG.getVTList(MVT::v8i16, MVT::v8i16), Ops[0]);
+ SDValue Ext1 =
+ DAG.getNode(IsUnsigned ? ARMISD::MVEZEXT : ARMISD::MVESEXT, dl,
+ DAG.getVTList(MVT::v8i16, MVT::v8i16), Ops[1]);
+
+ SDValue MLA0 = DAG.getNode(Opcode, dl, DAG.getVTList(MVT::i32, MVT::i32),
+ Ext0, Ext1);
+ SDValue MLA1 =
+ DAG.getNode(IsUnsigned ? ARMISD::VMLALVAu : ARMISD::VMLALVAs, dl,
+ DAG.getVTList(MVT::i32, MVT::i32), MLA0, MLA0.getValue(1),
+ Ext0.getValue(1), Ext1.getValue(1));
+ return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, MLA1, MLA1.getValue(1));
+ }
SDValue Node = DAG.getNode(Opcode, dl, {MVT::i32, MVT::i32}, Ops);
return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Node,
SDValue(Node.getNode(), 1));
@@ -15075,11 +16128,9 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return DAG.getNode(ARMISD::VADDVs, dl, ResVT, A);
if (SDValue A = IsVADDV(MVT::i32, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v16i8}))
return DAG.getNode(ARMISD::VADDVu, dl, ResVT, A);
- if (SDValue A = IsVADDV(MVT::i64, ISD::SIGN_EXTEND,
- {MVT::v4i8, MVT::v4i16, MVT::v4i32}))
+ if (SDValue A = IsVADDV(MVT::i64, ISD::SIGN_EXTEND, {MVT::v4i32}))
return Create64bitNode(ARMISD::VADDLVs, {A});
- if (SDValue A = IsVADDV(MVT::i64, ISD::ZERO_EXTEND,
- {MVT::v4i8, MVT::v4i16, MVT::v4i32}))
+ if (SDValue A = IsVADDV(MVT::i64, ISD::ZERO_EXTEND, {MVT::v4i32}))
return Create64bitNode(ARMISD::VADDLVu, {A});
if (SDValue A = IsVADDV(MVT::i16, ISD::SIGN_EXTEND, {MVT::v16i8}))
return DAG.getNode(ISD::TRUNCATE, dl, ResVT,
@@ -15093,11 +16144,9 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return DAG.getNode(ARMISD::VADDVps, dl, ResVT, A, Mask);
if (SDValue A = IsPredVADDV(MVT::i32, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v16i8}, Mask))
return DAG.getNode(ARMISD::VADDVpu, dl, ResVT, A, Mask);
- if (SDValue A = IsPredVADDV(MVT::i64, ISD::SIGN_EXTEND,
- {MVT::v4i8, MVT::v4i16, MVT::v4i32}, Mask))
+ if (SDValue A = IsPredVADDV(MVT::i64, ISD::SIGN_EXTEND, {MVT::v4i32}, Mask))
return Create64bitNode(ARMISD::VADDLVps, {A, Mask});
- if (SDValue A = IsPredVADDV(MVT::i64, ISD::ZERO_EXTEND,
- {MVT::v4i8, MVT::v4i16, MVT::v4i32}, Mask))
+ if (SDValue A = IsPredVADDV(MVT::i64, ISD::ZERO_EXTEND, {MVT::v4i32}, Mask))
return Create64bitNode(ARMISD::VADDLVpu, {A, Mask});
if (SDValue A = IsPredVADDV(MVT::i16, ISD::SIGN_EXTEND, {MVT::v16i8}, Mask))
return DAG.getNode(ISD::TRUNCATE, dl, ResVT,
@@ -15111,11 +16160,11 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return DAG.getNode(ARMISD::VMLAVs, dl, ResVT, A, B);
if (IsVMLAV(MVT::i32, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v16i8}, A, B))
return DAG.getNode(ARMISD::VMLAVu, dl, ResVT, A, B);
- if (IsVMLAV(MVT::i64, ISD::SIGN_EXTEND,
- {MVT::v8i8, MVT::v8i16, MVT::v4i8, MVT::v4i16, MVT::v4i32}, A, B))
+ if (IsVMLAV(MVT::i64, ISD::SIGN_EXTEND, {MVT::v16i8, MVT::v8i16, MVT::v4i32},
+ A, B))
return Create64bitNode(ARMISD::VMLALVs, {A, B});
- if (IsVMLAV(MVT::i64, ISD::ZERO_EXTEND,
- {MVT::v8i8, MVT::v8i16, MVT::v4i8, MVT::v4i16, MVT::v4i32}, A, B))
+ if (IsVMLAV(MVT::i64, ISD::ZERO_EXTEND, {MVT::v16i8, MVT::v8i16, MVT::v4i32},
+ A, B))
return Create64bitNode(ARMISD::VMLALVu, {A, B});
if (IsVMLAV(MVT::i16, ISD::SIGN_EXTEND, {MVT::v16i8}, A, B))
return DAG.getNode(ISD::TRUNCATE, dl, ResVT,
@@ -15124,17 +16173,17 @@ static SDValue PerformVECREDUCE_ADDCombine(SDNode *N, SelectionDAG &DAG,
return DAG.getNode(ISD::TRUNCATE, dl, ResVT,
DAG.getNode(ARMISD::VMLAVu, dl, MVT::i32, A, B));
- if (IsPredVMLAV(MVT::i32, ISD::SIGN_EXTEND, {MVT::v8i16, MVT::v16i8}, A, B, Mask))
+ if (IsPredVMLAV(MVT::i32, ISD::SIGN_EXTEND, {MVT::v8i16, MVT::v16i8}, A, B,
+ Mask))
return DAG.getNode(ARMISD::VMLAVps, dl, ResVT, A, B, Mask);
- if (IsPredVMLAV(MVT::i32, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v16i8}, A, B, Mask))
+ if (IsPredVMLAV(MVT::i32, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v16i8}, A, B,
+ Mask))
return DAG.getNode(ARMISD::VMLAVpu, dl, ResVT, A, B, Mask);
- if (IsPredVMLAV(MVT::i64, ISD::SIGN_EXTEND,
- {MVT::v8i8, MVT::v8i16, MVT::v4i8, MVT::v4i16, MVT::v4i32}, A,
- B, Mask))
+ if (IsPredVMLAV(MVT::i64, ISD::SIGN_EXTEND, {MVT::v8i16, MVT::v4i32}, A, B,
+ Mask))
return Create64bitNode(ARMISD::VMLALVps, {A, B, Mask});
- if (IsPredVMLAV(MVT::i64, ISD::ZERO_EXTEND,
- {MVT::v8i8, MVT::v8i16, MVT::v4i8, MVT::v4i16, MVT::v4i32}, A,
- B, Mask))
+ if (IsPredVMLAV(MVT::i64, ISD::ZERO_EXTEND, {MVT::v8i16, MVT::v4i32}, A, B,
+ Mask))
return Create64bitNode(ARMISD::VMLALVpu, {A, B, Mask});
if (IsPredVMLAV(MVT::i16, ISD::SIGN_EXTEND, {MVT::v16i8}, A, B, Mask))
return DAG.getNode(ISD::TRUNCATE, dl, ResVT,
@@ -15171,6 +16220,14 @@ static SDValue PerformVMOVNCombine(SDNode *N,
SDValue Op1 = N->getOperand(1);
unsigned IsTop = N->getConstantOperandVal(2);
+ // VMOVNT a undef -> a
+ // VMOVNB a undef -> a
+ // VMOVNB undef a -> a
+ if (Op1->isUndef())
+ return Op0;
+ if (Op0->isUndef() && !IsTop)
+ return Op1;
+
// VMOVNt(c, VQMOVNb(a, b)) => VQMOVNt(c, b)
// VMOVNb(c, VQMOVNb(a, b)) => VQMOVNb(c, b)
if ((Op1->getOpcode() == ARMISD::VQMOVNs ||
@@ -15580,10 +16637,8 @@ static SDValue PerformSplittingToWideningLoad(SDNode *N, SelectionDAG &DAG) {
EVT FromEltVT = FromVT.getVectorElementType();
unsigned NumElements = 0;
- if (ToEltVT == MVT::i32 && (FromEltVT == MVT::i16 || FromEltVT == MVT::i8))
+ if (ToEltVT == MVT::i32 && FromEltVT == MVT::i8)
NumElements = 4;
- if (ToEltVT == MVT::i16 && FromEltVT == MVT::i8)
- NumElements = 8;
if (ToEltVT == MVT::f32 && FromEltVT == MVT::f16)
NumElements = 4;
if (NumElements == 0 ||
@@ -15934,7 +16989,7 @@ static SDValue SearchLoopIntrinsic(SDValue N, ISD::CondCode &CC, int &Imm,
}
case ISD::INTRINSIC_W_CHAIN: {
unsigned IntOp = cast<ConstantSDNode>(N.getOperand(1))->getZExtValue();
- if (IntOp != Intrinsic::test_set_loop_iterations &&
+ if (IntOp != Intrinsic::test_start_loop_iterations &&
IntOp != Intrinsic::loop_decrement_reg)
return SDValue();
return N;
@@ -15949,7 +17004,7 @@ static SDValue PerformHWLoopCombine(SDNode *N,
// The hwloop intrinsics that we're interested are used for control-flow,
// either for entering or exiting the loop:
- // - test.set.loop.iterations will test whether its operand is zero. If it
+ // - test.start.loop.iterations will test whether its operand is zero. If it
// is zero, the proceeding branch should not enter the loop.
// - loop.decrement.reg also tests whether its operand is zero. If it is
// zero, the proceeding branch should not branch back to the beginning of
@@ -16024,21 +17079,25 @@ static SDValue PerformHWLoopCombine(SDNode *N,
DAG.ReplaceAllUsesOfValueWith(SDValue(Br, 0), NewBr);
};
- if (IntOp == Intrinsic::test_set_loop_iterations) {
+ if (IntOp == Intrinsic::test_start_loop_iterations) {
SDValue Res;
+ SDValue Setup = DAG.getNode(ARMISD::WLSSETUP, dl, MVT::i32, Elements);
// We expect this 'instruction' to branch when the counter is zero.
if (IsTrueIfZero(CC, Imm)) {
- SDValue Ops[] = { Chain, Elements, Dest };
+ SDValue Ops[] = {Chain, Setup, Dest};
Res = DAG.getNode(ARMISD::WLS, dl, MVT::Other, Ops);
} else {
// The logic is the reverse of what we need for WLS, so find the other
// basic block target: the target of the proceeding br.
UpdateUncondBr(Br, Dest, DAG);
- SDValue Ops[] = { Chain, Elements, OtherTarget };
+ SDValue Ops[] = {Chain, Setup, OtherTarget};
Res = DAG.getNode(ARMISD::WLS, dl, MVT::Other, Ops);
}
- DAG.ReplaceAllUsesOfValueWith(Int.getValue(1), Int.getOperand(0));
+ // Update LR count to the new value
+ DAG.ReplaceAllUsesOfValueWith(Int.getValue(0), Setup);
+ // Update chain
+ DAG.ReplaceAllUsesOfValueWith(Int.getValue(2), Int.getOperand(0));
return Res;
} else {
SDValue Size = DAG.getTargetConstant(
@@ -16283,8 +17342,10 @@ ARMTargetLowering::PerformCMOVCombine(SDNode *N, SelectionDAG &DAG) const {
return Res;
}
-static SDValue PerformBITCASTCombine(SDNode *N, SelectionDAG &DAG,
- const ARMSubtarget *ST) {
+static SDValue PerformBITCASTCombine(SDNode *N,
+ TargetLowering::DAGCombinerInfo &DCI,
+ const ARMSubtarget *ST) {
+ SelectionDAG &DAG = DCI.DAG;
SDValue Src = N->getOperand(0);
EVT DstVT = N->getValueType(0);
@@ -16310,9 +17371,288 @@ static SDValue PerformBITCASTCombine(SDNode *N, SelectionDAG &DAG,
DAG.getDataLayout().isBigEndian())
return DAG.getNode(ARMISD::VECTOR_REG_CAST, SDLoc(N), DstVT, Src);
+ // bitcast(extract(x, n)); bitcast(extract(x, n+1)) -> VMOVRRD x
+ if (SDValue R = PerformExtractEltToVMOVRRD(N, DCI))
+ return R;
+
return SDValue();
}
+// Some combines for the MVETrunc truncations legalizer helper. Also lowers the
+// node into stack operations after legalizeOps.
+SDValue ARMTargetLowering::PerformMVETruncCombine(
+ SDNode *N, TargetLowering::DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+
+ // MVETrunc(Undef, Undef) -> Undef
+ if (all_of(N->ops(), [](SDValue Op) { return Op.isUndef(); }))
+ return DAG.getUNDEF(VT);
+
+ // MVETrunc(MVETrunc a b, MVETrunc c, d) -> MVETrunc
+ if (N->getNumOperands() == 2 &&
+ N->getOperand(0).getOpcode() == ARMISD::MVETRUNC &&
+ N->getOperand(1).getOpcode() == ARMISD::MVETRUNC)
+ return DAG.getNode(ARMISD::MVETRUNC, DL, VT, N->getOperand(0).getOperand(0),
+ N->getOperand(0).getOperand(1),
+ N->getOperand(1).getOperand(0),
+ N->getOperand(1).getOperand(1));
+
+ // MVETrunc(shuffle, shuffle) -> VMOVN
+ if (N->getNumOperands() == 2 &&
+ N->getOperand(0).getOpcode() == ISD::VECTOR_SHUFFLE &&
+ N->getOperand(1).getOpcode() == ISD::VECTOR_SHUFFLE) {
+ auto *S0 = cast<ShuffleVectorSDNode>(N->getOperand(0).getNode());
+ auto *S1 = cast<ShuffleVectorSDNode>(N->getOperand(1).getNode());
+
+ if (S0->getOperand(0) == S1->getOperand(0) &&
+ S0->getOperand(1) == S1->getOperand(1)) {
+ // Construct complete shuffle mask
+ SmallVector<int, 8> Mask(S0->getMask().begin(), S0->getMask().end());
+ Mask.append(S1->getMask().begin(), S1->getMask().end());
+
+ if (isVMOVNTruncMask(Mask, VT, 0))
+ return DAG.getNode(
+ ARMISD::VMOVN, DL, VT,
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, S0->getOperand(0)),
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, S0->getOperand(1)),
+ DAG.getConstant(1, DL, MVT::i32));
+ if (isVMOVNTruncMask(Mask, VT, 1))
+ return DAG.getNode(
+ ARMISD::VMOVN, DL, VT,
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, S0->getOperand(1)),
+ DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, S0->getOperand(0)),
+ DAG.getConstant(1, DL, MVT::i32));
+ }
+ }
+
+ // For MVETrunc of a buildvector or shuffle, it can be beneficial to lower the
+ // truncate to a buildvector to allow the generic optimisations to kick in.
+ if (all_of(N->ops(), [](SDValue Op) {
+ return Op.getOpcode() == ISD::BUILD_VECTOR ||
+ Op.getOpcode() == ISD::VECTOR_SHUFFLE ||
+ (Op.getOpcode() == ISD::BITCAST &&
+ Op.getOperand(0).getOpcode() == ISD::BUILD_VECTOR);
+ })) {
+ SmallVector<SDValue, 8> Extracts;
+ for (unsigned Op = 0; Op < N->getNumOperands(); Op++) {
+ SDValue O = N->getOperand(Op);
+ for (unsigned i = 0; i < O.getValueType().getVectorNumElements(); i++) {
+ SDValue Ext = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, MVT::i32, O,
+ DAG.getConstant(i, DL, MVT::i32));
+ Extracts.push_back(Ext);
+ }
+ }
+ return DAG.getBuildVector(VT, DL, Extracts);
+ }
+
+ // If we are late in the legalization process and nothing has optimised
+ // the trunc to anything better, lower it to a stack store and reload,
+ // performing the truncation whilst keeping the lanes in the correct order:
+ // VSTRH.32 a, stack; VSTRH.32 b, stack+8; VLDRW.32 stack;
+ if (!DCI.isAfterLegalizeDAG())
+ return SDValue();
+
+ SDValue StackPtr = DAG.CreateStackTemporary(TypeSize::Fixed(16), Align(4));
+ int SPFI = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+ int NumIns = N->getNumOperands();
+ assert((NumIns == 2 || NumIns == 4) &&
+ "Expected 2 or 4 inputs to an MVETrunc");
+ EVT StoreVT = VT.getHalfNumVectorElementsVT(*DAG.getContext());
+ if (N->getNumOperands() == 4)
+ StoreVT = StoreVT.getHalfNumVectorElementsVT(*DAG.getContext());
+
+ SmallVector<SDValue> Chains;
+ for (int I = 0; I < NumIns; I++) {
+ SDValue Ptr = DAG.getNode(
+ ISD::ADD, DL, StackPtr.getValueType(), StackPtr,
+ DAG.getConstant(I * 16 / NumIns, DL, StackPtr.getValueType()));
+ MachinePointerInfo MPI = MachinePointerInfo::getFixedStack(
+ DAG.getMachineFunction(), SPFI, I * 16 / NumIns);
+ SDValue Ch = DAG.getTruncStore(DAG.getEntryNode(), DL, N->getOperand(I),
+ Ptr, MPI, StoreVT, Align(4));
+ Chains.push_back(Ch);
+ }
+
+ SDValue Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
+ MachinePointerInfo MPI =
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI, 0);
+ return DAG.getLoad(VT, DL, Chain, StackPtr, MPI, Align(4));
+}
+
+// Take a MVEEXT(load x) and split that into (extload x, extload x+8)
+static SDValue PerformSplittingMVEEXTToWideningLoad(SDNode *N,
+ SelectionDAG &DAG) {
+ SDValue N0 = N->getOperand(0);
+ LoadSDNode *LD = dyn_cast<LoadSDNode>(N0.getNode());
+ if (!LD || !LD->isSimple() || !N0.hasOneUse() || LD->isIndexed())
+ return SDValue();
+
+ EVT FromVT = LD->getMemoryVT();
+ EVT ToVT = N->getValueType(0);
+ if (!ToVT.isVector())
+ return SDValue();
+ assert(FromVT.getVectorNumElements() == ToVT.getVectorNumElements() * 2);
+ EVT ToEltVT = ToVT.getVectorElementType();
+ EVT FromEltVT = FromVT.getVectorElementType();
+
+ unsigned NumElements = 0;
+ if (ToEltVT == MVT::i32 && (FromEltVT == MVT::i16 || FromEltVT == MVT::i8))
+ NumElements = 4;
+ if (ToEltVT == MVT::i16 && FromEltVT == MVT::i8)
+ NumElements = 8;
+ assert(NumElements != 0);
+
+ ISD::LoadExtType NewExtType =
+ N->getOpcode() == ARMISD::MVESEXT ? ISD::SEXTLOAD : ISD::ZEXTLOAD;
+ if (LD->getExtensionType() != ISD::NON_EXTLOAD &&
+ LD->getExtensionType() != ISD::EXTLOAD &&
+ LD->getExtensionType() != NewExtType)
+ return SDValue();
+
+ LLVMContext &C = *DAG.getContext();
+ SDLoc DL(LD);
+ // Details about the old load
+ SDValue Ch = LD->getChain();
+ SDValue BasePtr = LD->getBasePtr();
+ Align Alignment = LD->getOriginalAlign();
+ MachineMemOperand::Flags MMOFlags = LD->getMemOperand()->getFlags();
+ AAMDNodes AAInfo = LD->getAAInfo();
+
+ SDValue Offset = DAG.getUNDEF(BasePtr.getValueType());
+ EVT NewFromVT = EVT::getVectorVT(
+ C, EVT::getIntegerVT(C, FromEltVT.getScalarSizeInBits()), NumElements);
+ EVT NewToVT = EVT::getVectorVT(
+ C, EVT::getIntegerVT(C, ToEltVT.getScalarSizeInBits()), NumElements);
+
+ SmallVector<SDValue, 4> Loads;
+ SmallVector<SDValue, 4> Chains;
+ for (unsigned i = 0; i < FromVT.getVectorNumElements() / NumElements; i++) {
+ unsigned NewOffset = (i * NewFromVT.getSizeInBits()) / 8;
+ SDValue NewPtr =
+ DAG.getObjectPtrOffset(DL, BasePtr, TypeSize::Fixed(NewOffset));
+
+ SDValue NewLoad =
+ DAG.getLoad(ISD::UNINDEXED, NewExtType, NewToVT, DL, Ch, NewPtr, Offset,
+ LD->getPointerInfo().getWithOffset(NewOffset), NewFromVT,
+ Alignment, MMOFlags, AAInfo);
+ Loads.push_back(NewLoad);
+ Chains.push_back(SDValue(NewLoad.getNode(), 1));
+ }
+
+ SDValue NewChain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
+ DAG.ReplaceAllUsesOfValueWith(SDValue(LD, 1), NewChain);
+ return DAG.getMergeValues(Loads, DL);
+}
+
+// Perform combines for MVEEXT. If it has not be optimized to anything better
+// before lowering, it gets converted to stack store and extloads performing the
+// extend whilst still keeping the same lane ordering.
+SDValue ARMTargetLowering::PerformMVEExtCombine(
+ SDNode *N, TargetLowering::DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+ assert(N->getNumValues() == 2 && "Expected MVEEXT with 2 elements");
+ assert((VT == MVT::v4i32 || VT == MVT::v8i16) && "Unexpected MVEEXT type");
+
+ EVT ExtVT = N->getOperand(0).getValueType().getHalfNumVectorElementsVT(
+ *DAG.getContext());
+ auto Extend = [&](SDValue V) {
+ SDValue VVT = DAG.getNode(ARMISD::VECTOR_REG_CAST, DL, VT, V);
+ return N->getOpcode() == ARMISD::MVESEXT
+ ? DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, VT, VVT,
+ DAG.getValueType(ExtVT))
+ : DAG.getZeroExtendInReg(VVT, DL, ExtVT);
+ };
+
+ // MVEEXT(VDUP) -> SIGN_EXTEND_INREG(VDUP)
+ if (N->getOperand(0).getOpcode() == ARMISD::VDUP) {
+ SDValue Ext = Extend(N->getOperand(0));
+ return DAG.getMergeValues({Ext, Ext}, DL);
+ }
+
+ // MVEEXT(shuffle) -> SIGN_EXTEND_INREG/ZERO_EXTEND_INREG
+ if (auto *SVN = dyn_cast<ShuffleVectorSDNode>(N->getOperand(0))) {
+ ArrayRef<int> Mask = SVN->getMask();
+ assert(Mask.size() == 2 * VT.getVectorNumElements());
+ assert(Mask.size() == SVN->getValueType(0).getVectorNumElements());
+ unsigned Rev = VT == MVT::v4i32 ? ARMISD::VREV32 : ARMISD::VREV16;
+ SDValue Op0 = SVN->getOperand(0);
+ SDValue Op1 = SVN->getOperand(1);
+
+ auto CheckInregMask = [&](int Start, int Offset) {
+ for (int Idx = 0, E = VT.getVectorNumElements(); Idx < E; ++Idx)
+ if (Mask[Start + Idx] >= 0 && Mask[Start + Idx] != Idx * 2 + Offset)
+ return false;
+ return true;
+ };
+ SDValue V0 = SDValue(N, 0);
+ SDValue V1 = SDValue(N, 1);
+ if (CheckInregMask(0, 0))
+ V0 = Extend(Op0);
+ else if (CheckInregMask(0, 1))
+ V0 = Extend(DAG.getNode(Rev, DL, SVN->getValueType(0), Op0));
+ else if (CheckInregMask(0, Mask.size()))
+ V0 = Extend(Op1);
+ else if (CheckInregMask(0, Mask.size() + 1))
+ V0 = Extend(DAG.getNode(Rev, DL, SVN->getValueType(0), Op1));
+
+ if (CheckInregMask(VT.getVectorNumElements(), Mask.size()))
+ V1 = Extend(Op1);
+ else if (CheckInregMask(VT.getVectorNumElements(), Mask.size() + 1))
+ V1 = Extend(DAG.getNode(Rev, DL, SVN->getValueType(0), Op1));
+ else if (CheckInregMask(VT.getVectorNumElements(), 0))
+ V1 = Extend(Op0);
+ else if (CheckInregMask(VT.getVectorNumElements(), 1))
+ V1 = Extend(DAG.getNode(Rev, DL, SVN->getValueType(0), Op0));
+
+ if (V0.getNode() != N || V1.getNode() != N)
+ return DAG.getMergeValues({V0, V1}, DL);
+ }
+
+ // MVEEXT(load) -> extload, extload
+ if (N->getOperand(0)->getOpcode() == ISD::LOAD)
+ if (SDValue L = PerformSplittingMVEEXTToWideningLoad(N, DAG))
+ return L;
+
+ if (!DCI.isAfterLegalizeDAG())
+ return SDValue();
+
+ // Lower to a stack store and reload:
+ // VSTRW.32 a, stack; VLDRH.32 stack; VLDRH.32 stack+8;
+ SDValue StackPtr = DAG.CreateStackTemporary(TypeSize::Fixed(16), Align(4));
+ int SPFI = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+ int NumOuts = N->getNumValues();
+ assert((NumOuts == 2 || NumOuts == 4) &&
+ "Expected 2 or 4 outputs to an MVEEXT");
+ EVT LoadVT = N->getOperand(0).getValueType().getHalfNumVectorElementsVT(
+ *DAG.getContext());
+ if (N->getNumOperands() == 4)
+ LoadVT = LoadVT.getHalfNumVectorElementsVT(*DAG.getContext());
+
+ MachinePointerInfo MPI =
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI, 0);
+ SDValue Chain = DAG.getStore(DAG.getEntryNode(), DL, N->getOperand(0),
+ StackPtr, MPI, Align(4));
+
+ SmallVector<SDValue> Loads;
+ for (int I = 0; I < NumOuts; I++) {
+ SDValue Ptr = DAG.getNode(
+ ISD::ADD, DL, StackPtr.getValueType(), StackPtr,
+ DAG.getConstant(I * 16 / NumOuts, DL, StackPtr.getValueType()));
+ MachinePointerInfo MPI = MachinePointerInfo::getFixedStack(
+ DAG.getMachineFunction(), SPFI, I * 16 / NumOuts);
+ SDValue Load = DAG.getExtLoad(
+ N->getOpcode() == ARMISD::MVESEXT ? ISD::SEXTLOAD : ISD::ZEXTLOAD, DL,
+ VT, Chain, Ptr, MPI, LoadVT, Align(4));
+ Loads.push_back(Load);
+ }
+
+ return DAG.getMergeValues(Loads, DL);
+}
+
SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
DAGCombinerInfo &DCI) const {
switch (N->getOpcode()) {
@@ -16334,18 +17674,20 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
case ARMISD::ADDC:
case ARMISD::SUBC: return PerformAddcSubcCombine(N, DCI, Subtarget);
case ARMISD::SUBE: return PerformAddeSubeCombine(N, DCI, Subtarget);
- case ARMISD::BFI: return PerformBFICombine(N, DCI);
+ case ARMISD::BFI: return PerformBFICombine(N, DCI.DAG);
case ARMISD::VMOVRRD: return PerformVMOVRRDCombine(N, DCI, Subtarget);
case ARMISD::VMOVDRR: return PerformVMOVDRRCombine(N, DCI.DAG);
case ARMISD::VMOVhr: return PerformVMOVhrCombine(N, DCI);
- case ARMISD::VMOVrh: return PerformVMOVrhCombine(N, DCI);
+ case ARMISD::VMOVrh: return PerformVMOVrhCombine(N, DCI.DAG);
case ISD::STORE: return PerformSTORECombine(N, DCI, Subtarget);
case ISD::BUILD_VECTOR: return PerformBUILD_VECTORCombine(N, DCI, Subtarget);
case ISD::INSERT_VECTOR_ELT: return PerformInsertEltCombine(N, DCI);
- case ISD::EXTRACT_VECTOR_ELT: return PerformExtractEltCombine(N, DCI);
+ case ISD::EXTRACT_VECTOR_ELT:
+ return PerformExtractEltCombine(N, DCI, Subtarget);
+ case ISD::SIGN_EXTEND_INREG: return PerformSignExtendInregCombine(N, DCI.DAG);
case ISD::VECTOR_SHUFFLE: return PerformVECTOR_SHUFFLECombine(N, DCI.DAG);
case ARMISD::VDUPLANE: return PerformVDUPLANECombine(N, DCI, Subtarget);
- case ARMISD::VDUP: return PerformVDUPCombine(N, DCI, Subtarget);
+ case ARMISD::VDUP: return PerformVDUPCombine(N, DCI.DAG, Subtarget);
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT:
return PerformVCVTCombine(N, DCI.DAG, Subtarget);
@@ -16379,13 +17721,18 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
case ARMISD::BUILD_VECTOR:
return PerformARMBUILD_VECTORCombine(N, DCI);
case ISD::BITCAST:
- return PerformBITCASTCombine(N, DCI.DAG, Subtarget);
+ return PerformBITCASTCombine(N, DCI, Subtarget);
case ARMISD::PREDICATE_CAST:
return PerformPREDICATE_CASTCombine(N, DCI);
case ARMISD::VECTOR_REG_CAST:
- return PerformVECTOR_REG_CASTCombine(N, DCI, Subtarget);
+ return PerformVECTOR_REG_CASTCombine(N, DCI.DAG, Subtarget);
+ case ARMISD::MVETRUNC:
+ return PerformMVETruncCombine(N, DCI);
+ case ARMISD::MVESEXT:
+ case ARMISD::MVEZEXT:
+ return PerformMVEExtCombine(N, DCI);
case ARMISD::VCMP:
- return PerformVCMPCombine(N, DCI, Subtarget);
+ return PerformVCMPCombine(N, DCI.DAG, Subtarget);
case ISD::VECREDUCE_ADD:
return PerformVECREDUCE_ADDCombine(N, DCI.DAG, Subtarget);
case ARMISD::VMOVN:
@@ -16413,7 +17760,9 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
}
case ARMISD::SMLALBB:
case ARMISD::QADD16b:
- case ARMISD::QSUB16b: {
+ case ARMISD::QSUB16b:
+ case ARMISD::UQADD16b:
+ case ARMISD::UQSUB16b: {
unsigned BitWidth = N->getValueType(0).getSizeInBits();
APInt DemandedMask = APInt::getLowBitsSet(BitWidth, 16);
if ((SimplifyDemandedBits(N->getOperand(0), DemandedMask, DCI)) ||
@@ -16450,7 +17799,9 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N,
break;
}
case ARMISD::QADD8b:
- case ARMISD::QSUB8b: {
+ case ARMISD::QSUB8b:
+ case ARMISD::UQADD8b:
+ case ARMISD::UQSUB8b: {
unsigned BitWidth = N->getValueType(0).getSizeInBits();
APInt DemandedMask = APInt::getLowBitsSet(BitWidth, 8);
if ((SimplifyDemandedBits(N->getOperand(0), DemandedMask, DCI)) ||
@@ -16503,7 +17854,7 @@ bool ARMTargetLowering::isDesirableToTransformToIntegerOp(unsigned Opc,
}
bool ARMTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, unsigned,
- unsigned Alignment,
+ Align Alignment,
MachineMemOperand::Flags,
bool *Fast) const {
// Depends what it gets converted into if the type is weird.
@@ -16582,14 +17933,14 @@ EVT ARMTargetLowering::getOptimalMemOpType(
bool Fast;
if (Op.size() >= 16 &&
(Op.isAligned(Align(16)) ||
- (allowsMisalignedMemoryAccesses(MVT::v2f64, 0, 1,
+ (allowsMisalignedMemoryAccesses(MVT::v2f64, 0, Align(1),
MachineMemOperand::MONone, &Fast) &&
Fast))) {
return MVT::v2f64;
} else if (Op.size() >= 8 &&
(Op.isAligned(Align(8)) ||
(allowsMisalignedMemoryAccesses(
- MVT::f64, 0, 1, MachineMemOperand::MONone, &Fast) &&
+ MVT::f64, 0, Align(1), MachineMemOperand::MONone, &Fast) &&
Fast))) {
return MVT::f64;
}
@@ -16849,9 +18200,10 @@ bool ARMTargetLowering::allowTruncateForTailCall(Type *Ty1, Type *Ty2) const {
return true;
}
-int ARMTargetLowering::getScalingFactorCost(const DataLayout &DL,
- const AddrMode &AM, Type *Ty,
- unsigned AS) const {
+InstructionCost ARMTargetLowering::getScalingFactorCost(const DataLayout &DL,
+ const AddrMode &AM,
+ Type *Ty,
+ unsigned AS) const {
if (isLegalAddressingMode(DL, AM, Ty, AS)) {
if (Subtarget->hasFPAO())
return AM.Scale < 0 ? 1 : 0; // positive offsets execute faster
@@ -17403,6 +18755,8 @@ bool ARMTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op,
auto *RHS = dyn_cast<ConstantSDNode>(Op->getOperand(1));
if (!RHS || RHS->getZExtValue() != 4)
return false;
+ if (Alignment < Align(4))
+ return false;
Offset = Op->getOperand(1);
Base = Op->getOperand(0);
@@ -17539,6 +18893,28 @@ void ARMTargetLowering::computeKnownBitsForTargetNode(const SDValue Op,
Known = KnownOp.zext(32);
break;
}
+ case ARMISD::CSINC:
+ case ARMISD::CSINV:
+ case ARMISD::CSNEG: {
+ KnownBits KnownOp0 = DAG.computeKnownBits(Op->getOperand(0), Depth + 1);
+ KnownBits KnownOp1 = DAG.computeKnownBits(Op->getOperand(1), Depth + 1);
+
+ // The result is either:
+ // CSINC: KnownOp0 or KnownOp1 + 1
+ // CSINV: KnownOp0 or ~KnownOp1
+ // CSNEG: KnownOp0 or KnownOp1 * -1
+ if (Op.getOpcode() == ARMISD::CSINC)
+ KnownOp1 = KnownBits::computeForAddSub(
+ true, false, KnownOp1, KnownBits::makeConstant(APInt(32, 1)));
+ else if (Op.getOpcode() == ARMISD::CSINV)
+ std::swap(KnownOp1.Zero, KnownOp1.One);
+ else if (Op.getOpcode() == ARMISD::CSNEG)
+ KnownOp1 = KnownBits::mul(
+ KnownOp1, KnownBits::makeConstant(APInt(32, -1)));
+
+ Known = KnownBits::commonBits(KnownOp0, KnownOp1);
+ break;
+ }
}
}
@@ -17842,7 +19218,7 @@ RCPair ARMTargetLowering::getRegForInlineAsmConstraint(
break;
}
- if (StringRef("{cc}").equals_lower(Constraint))
+ if (StringRef("{cc}").equals_insensitive(Constraint))
return std::make_pair(unsigned(ARM::CPSR), &ARM::CCRRegClass);
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
@@ -18480,6 +19856,66 @@ bool ARMTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
Info.flags = MachineMemOperand::MOStore;
return true;
}
+ case Intrinsic::arm_mve_vldr_gather_base:
+ case Intrinsic::arm_mve_vldr_gather_base_predicated: {
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.ptrVal = nullptr;
+ Info.memVT = MVT::getVT(I.getType());
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOLoad;
+ return true;
+ }
+ case Intrinsic::arm_mve_vldr_gather_base_wb:
+ case Intrinsic::arm_mve_vldr_gather_base_wb_predicated: {
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.ptrVal = nullptr;
+ Info.memVT = MVT::getVT(I.getType()->getContainedType(0));
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOLoad;
+ return true;
+ }
+ case Intrinsic::arm_mve_vldr_gather_offset:
+ case Intrinsic::arm_mve_vldr_gather_offset_predicated: {
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.ptrVal = nullptr;
+ MVT DataVT = MVT::getVT(I.getType());
+ unsigned MemSize = cast<ConstantInt>(I.getArgOperand(2))->getZExtValue();
+ Info.memVT = MVT::getVectorVT(MVT::getIntegerVT(MemSize),
+ DataVT.getVectorNumElements());
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOLoad;
+ return true;
+ }
+ case Intrinsic::arm_mve_vstr_scatter_base:
+ case Intrinsic::arm_mve_vstr_scatter_base_predicated: {
+ Info.opc = ISD::INTRINSIC_VOID;
+ Info.ptrVal = nullptr;
+ Info.memVT = MVT::getVT(I.getArgOperand(2)->getType());
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOStore;
+ return true;
+ }
+ case Intrinsic::arm_mve_vstr_scatter_base_wb:
+ case Intrinsic::arm_mve_vstr_scatter_base_wb_predicated: {
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.ptrVal = nullptr;
+ Info.memVT = MVT::getVT(I.getArgOperand(2)->getType());
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOStore;
+ return true;
+ }
+ case Intrinsic::arm_mve_vstr_scatter_offset:
+ case Intrinsic::arm_mve_vstr_scatter_offset_predicated: {
+ Info.opc = ISD::INTRINSIC_VOID;
+ Info.ptrVal = nullptr;
+ MVT DataVT = MVT::getVT(I.getArgOperand(2)->getType());
+ unsigned MemSize = cast<ConstantInt>(I.getArgOperand(3))->getZExtValue();
+ Info.memVT = MVT::getVectorVT(MVT::getIntegerVT(MemSize),
+ DataVT.getVectorNumElements());
+ Info.align = Align(1);
+ Info.flags |= MachineMemOperand::MOStore;
+ return true;
+ }
case Intrinsic::arm_ldaex:
case Intrinsic::arm_ldrex: {
auto &DL = I.getCalledFunction()->getParent()->getDataLayout();
@@ -18551,7 +19987,7 @@ bool ARMTargetLowering::isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
return (Index == 0 || Index == ResVT.getVectorNumElements());
}
-Instruction* ARMTargetLowering::makeDMB(IRBuilder<> &Builder,
+Instruction *ARMTargetLowering::makeDMB(IRBuilderBase &Builder,
ARM_MB::MemBOpt Domain) const {
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
@@ -18581,7 +20017,7 @@ Instruction* ARMTargetLowering::makeDMB(IRBuilder<> &Builder,
}
// Based on http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
-Instruction *ARMTargetLowering::emitLeadingFence(IRBuilder<> &Builder,
+Instruction *ARMTargetLowering::emitLeadingFence(IRBuilderBase &Builder,
Instruction *Inst,
AtomicOrdering Ord) const {
switch (Ord) {
@@ -18606,7 +20042,7 @@ Instruction *ARMTargetLowering::emitLeadingFence(IRBuilder<> &Builder,
llvm_unreachable("Unknown fence ordering in emitLeadingFence");
}
-Instruction *ARMTargetLowering::emitTrailingFence(IRBuilder<> &Builder,
+Instruction *ARMTargetLowering::emitTrailingFence(IRBuilderBase &Builder,
Instruction *Inst,
AtomicOrdering Ord) const {
switch (Ord) {
@@ -18654,6 +20090,14 @@ ARMTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const {
if (AI->isFloatingPointOperation())
return AtomicExpansionKind::CmpXChg;
+ // At -O0, fast-regalloc cannot cope with the live vregs necessary to
+ // implement atomicrmw without spilling. If the target address is also on the
+ // stack and close enough to the spill slot, this can lead to a situation
+ // where the monitor always gets cleared and the atomic operation can never
+ // succeed. So at -O0 lower this operation to a CAS loop.
+ if (getTargetMachine().getOptLevel() == CodeGenOpt::None)
+ return AtomicExpansionKind::CmpXChg;
+
unsigned Size = AI->getType()->getPrimitiveSizeInBits();
bool hasAtomicRMW = !Subtarget->isThumb() || Subtarget->hasV8MBaselineOps();
return (Size <= (Subtarget->isMClass() ? 32U : 64U) && hasAtomicRMW)
@@ -18661,6 +20105,8 @@ ARMTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const {
: AtomicExpansionKind::None;
}
+// Similar to shouldExpandAtomicRMWInIR, ldrex/strex can be used up to 32
+// bits, and up to 64 bits on the non-M profiles.
TargetLowering::AtomicExpansionKind
ARMTargetLowering::shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *AI) const {
// At -O0, fast-regalloc cannot cope with the live vregs necessary to
@@ -18668,9 +20114,11 @@ ARMTargetLowering::shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *AI) const {
// on the stack and close enough to the spill slot, this can lead to a
// situation where the monitor always gets cleared and the atomic operation
// can never succeed. So at -O0 we need a late-expanded pseudo-inst instead.
+ unsigned Size = AI->getOperand(1)->getType()->getPrimitiveSizeInBits();
bool HasAtomicCmpXchg =
!Subtarget->isThumb() || Subtarget->hasV8MBaselineOps();
- if (getTargetMachine().getOptLevel() != 0 && HasAtomicCmpXchg)
+ if (getTargetMachine().getOptLevel() != 0 && HasAtomicCmpXchg &&
+ Size <= (Subtarget->isMClass() ? 32U : 64U))
return AtomicExpansionKind::LLSC;
return AtomicExpansionKind::None;
}
@@ -18756,16 +20204,16 @@ bool ARMTargetLowering::shouldExpandShift(SelectionDAG &DAG, SDNode *N) const {
return !Subtarget->hasMinSize() || Subtarget->isTargetWindows();
}
-Value *ARMTargetLowering::emitLoadLinked(IRBuilder<> &Builder, Value *Addr,
+Value *ARMTargetLowering::emitLoadLinked(IRBuilderBase &Builder, Type *ValueTy,
+ Value *Addr,
AtomicOrdering Ord) const {
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
- Type *ValTy = cast<PointerType>(Addr->getType())->getElementType();
bool IsAcquire = isAcquireOrStronger(Ord);
// Since i64 isn't legal and intrinsics don't get type-lowered, the ldrexd
// intrinsic must return {i32, i32} and we have to recombine them into a
// single i64 here.
- if (ValTy->getPrimitiveSizeInBits() == 64) {
+ if (ValueTy->getPrimitiveSizeInBits() == 64) {
Intrinsic::ID Int =
IsAcquire ? Intrinsic::arm_ldaexd : Intrinsic::arm_ldrexd;
Function *Ldrex = Intrinsic::getDeclaration(M, Int);
@@ -18777,31 +20225,29 @@ Value *ARMTargetLowering::emitLoadLinked(IRBuilder<> &Builder, Value *Addr,
Value *Hi = Builder.CreateExtractValue(LoHi, 1, "hi");
if (!Subtarget->isLittle())
std::swap (Lo, Hi);
- Lo = Builder.CreateZExt(Lo, ValTy, "lo64");
- Hi = Builder.CreateZExt(Hi, ValTy, "hi64");
+ Lo = Builder.CreateZExt(Lo, ValueTy, "lo64");
+ Hi = Builder.CreateZExt(Hi, ValueTy, "hi64");
return Builder.CreateOr(
- Lo, Builder.CreateShl(Hi, ConstantInt::get(ValTy, 32)), "val64");
+ Lo, Builder.CreateShl(Hi, ConstantInt::get(ValueTy, 32)), "val64");
}
Type *Tys[] = { Addr->getType() };
Intrinsic::ID Int = IsAcquire ? Intrinsic::arm_ldaex : Intrinsic::arm_ldrex;
Function *Ldrex = Intrinsic::getDeclaration(M, Int, Tys);
- return Builder.CreateTruncOrBitCast(
- Builder.CreateCall(Ldrex, Addr),
- cast<PointerType>(Addr->getType())->getElementType());
+ return Builder.CreateTruncOrBitCast(Builder.CreateCall(Ldrex, Addr), ValueTy);
}
void ARMTargetLowering::emitAtomicCmpXchgNoStoreLLBalance(
- IRBuilder<> &Builder) const {
+ IRBuilderBase &Builder) const {
if (!Subtarget->hasV7Ops())
return;
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
Builder.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::arm_clrex));
}
-Value *ARMTargetLowering::emitStoreConditional(IRBuilder<> &Builder, Value *Val,
- Value *Addr,
+Value *ARMTargetLowering::emitStoreConditional(IRBuilderBase &Builder,
+ Value *Val, Value *Addr,
AtomicOrdering Ord) const {
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
bool IsRelease = isReleaseOrStronger(Ord);
@@ -18847,7 +20293,8 @@ ARMTargetLowering::getNumInterleavedAccesses(VectorType *VecTy,
}
bool ARMTargetLowering::isLegalInterleavedAccessType(
- unsigned Factor, FixedVectorType *VecTy, const DataLayout &DL) const {
+ unsigned Factor, FixedVectorType *VecTy, Align Alignment,
+ const DataLayout &DL) const {
unsigned VecSize = DL.getTypeSizeInBits(VecTy);
unsigned ElSize = DL.getTypeSizeInBits(VecTy->getElementType());
@@ -18870,6 +20317,9 @@ bool ARMTargetLowering::isLegalInterleavedAccessType(
// Ensure the element type is legal.
if (ElSize != 8 && ElSize != 16 && ElSize != 32)
return false;
+ // And the alignment if high enough under MVE.
+ if (Subtarget->hasMVEIntegerOps() && Alignment < ElSize / 8)
+ return false;
// Ensure the total vector size is 64 or a multiple of 128. Types larger than
// 128 will be split into multiple interleaved accesses.
@@ -18910,11 +20360,12 @@ bool ARMTargetLowering::lowerInterleavedLoad(
Type *EltTy = VecTy->getElementType();
const DataLayout &DL = LI->getModule()->getDataLayout();
+ Align Alignment = LI->getAlign();
// Skip if we do not have NEON and skip illegal vector types. We can
// "legalize" wide vector types into multiple interleaved accesses as long as
// the vector types are divisible by 128.
- if (!isLegalInterleavedAccessType(Factor, VecTy, DL))
+ if (!isLegalInterleavedAccessType(Factor, VecTy, Alignment, DL))
return false;
unsigned NumLoads = getNumInterleavedAccesses(VecTy, DL);
@@ -19063,11 +20514,12 @@ bool ARMTargetLowering::lowerInterleavedStore(StoreInst *SI,
auto *SubVecTy = FixedVectorType::get(EltTy, LaneLen);
const DataLayout &DL = SI->getModule()->getDataLayout();
+ Align Alignment = SI->getAlign();
// Skip if we do not have NEON and skip illegal vector types. We can
// "legalize" wide vector types into multiple interleaved accesses as long as
// the vector types are divisible by 128.
- if (!isLegalInterleavedAccessType(Factor, SubVecTy, DL))
+ if (!isLegalInterleavedAccessType(Factor, SubVecTy, Alignment, DL))
return false;
unsigned NumStores = getNumInterleavedAccesses(SubVecTy, DL);
@@ -19250,8 +20702,8 @@ static bool isHomogeneousAggregate(Type *Ty, HABaseType &Base,
}
/// Return the correct alignment for the current calling convention.
-Align ARMTargetLowering::getABIAlignmentForCallingConv(Type *ArgTy,
- DataLayout DL) const {
+Align ARMTargetLowering::getABIAlignmentForCallingConv(
+ Type *ArgTy, const DataLayout &DL) const {
const Align ABITypeAlign = DL.getABITypeAlign(ArgTy);
if (!ArgTy->isVectorTy())
return ABITypeAlign;
@@ -19265,7 +20717,8 @@ Align ARMTargetLowering::getABIAlignmentForCallingConv(Type *ArgTy,
/// [N x i32] or [N x i64]. This allows front-ends to skip emitting padding when
/// passing according to AAPCS rules.
bool ARMTargetLowering::functionArgumentNeedsConsecutiveRegisters(
- Type *Ty, CallingConv::ID CallConv, bool isVarArg) const {
+ Type *Ty, CallingConv::ID CallConv, bool isVarArg,
+ const DataLayout &DL) const {
if (getEffectiveCallingConv(CallConv, isVarArg) !=
CallingConv::ARM_AAPCS_VFP)
return false;