diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
| commit | d8e91e46262bc44006913e6796843909f1ac7bcd (patch) | |
| tree | 7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Target/AArch64 | |
| parent | b7eb8e35e481a74962664b63dfb09483b200209a (diff) | |
Notes
Diffstat (limited to 'lib/Target/AArch64')
69 files changed, 8166 insertions, 1992 deletions
diff --git a/lib/Target/AArch64/AArch64.h b/lib/Target/AArch64/AArch64.h index edda13ce97ef..c36d9354f3ba 100644 --- a/lib/Target/AArch64/AArch64.h +++ b/lib/Target/AArch64/AArch64.h @@ -32,12 +32,14 @@ class MachineFunctionPass; FunctionPass *createAArch64DeadRegisterDefinitions(); FunctionPass *createAArch64RedundantCopyEliminationPass(); FunctionPass *createAArch64CondBrTuning(); +FunctionPass *createAArch64CompressJumpTablesPass(); FunctionPass *createAArch64ConditionalCompares(); FunctionPass *createAArch64AdvSIMDScalar(); FunctionPass *createAArch64ISelDag(AArch64TargetMachine &TM, CodeGenOpt::Level OptLevel); FunctionPass *createAArch64StorePairSuppressPass(); FunctionPass *createAArch64ExpandPseudoPass(); +FunctionPass *createAArch64SpeculationHardeningPass(); FunctionPass *createAArch64LoadStoreOptimizationPass(); FunctionPass *createAArch64SIMDInstrOptPass(); ModulePass *createAArch64PromoteConstantPass(); @@ -46,6 +48,7 @@ FunctionPass *createAArch64A57FPLoadBalancing(); FunctionPass *createAArch64A53Fix835769(); FunctionPass *createFalkorHWPFFixPass(); FunctionPass *createFalkorMarkStridedAccessesPass(); +FunctionPass *createAArch64BranchTargetsPass(); FunctionPass *createAArch64CleanupLocalDynamicTLSPass(); @@ -53,18 +56,23 @@ FunctionPass *createAArch64CollectLOHPass(); InstructionSelector * createAArch64InstructionSelector(const AArch64TargetMachine &, AArch64Subtarget &, AArch64RegisterBankInfo &); +FunctionPass *createAArch64PreLegalizeCombiner(); void initializeAArch64A53Fix835769Pass(PassRegistry&); void initializeAArch64A57FPLoadBalancingPass(PassRegistry&); void initializeAArch64AdvSIMDScalarPass(PassRegistry&); +void initializeAArch64BranchTargetsPass(PassRegistry&); void initializeAArch64CollectLOHPass(PassRegistry&); void initializeAArch64CondBrTuningPass(PassRegistry &); +void initializeAArch64CompressJumpTablesPass(PassRegistry&); void initializeAArch64ConditionalComparesPass(PassRegistry&); void initializeAArch64ConditionOptimizerPass(PassRegistry&); void initializeAArch64DeadRegisterDefinitionsPass(PassRegistry&); void initializeAArch64ExpandPseudoPass(PassRegistry&); +void initializeAArch64SpeculationHardeningPass(PassRegistry&); void initializeAArch64LoadStoreOptPass(PassRegistry&); void initializeAArch64SIMDInstrOptPass(PassRegistry&); +void initializeAArch64PreLegalizerCombinerPass(PassRegistry&); void initializeAArch64PromoteConstantPass(PassRegistry&); void initializeAArch64RedundantCopyEliminationPass(PassRegistry&); void initializeAArch64StorePairSuppressPass(PassRegistry&); diff --git a/lib/Target/AArch64/AArch64.td b/lib/Target/AArch64/AArch64.td index a69d38144c78..8f79140cba64 100644 --- a/lib/Target/AArch64/AArch64.td +++ b/lib/Target/AArch64/AArch64.td @@ -65,25 +65,56 @@ def FeatureLSE : SubtargetFeature<"lse", "HasLSE", "true", def FeatureRDM : SubtargetFeature<"rdm", "HasRDM", "true", "Enable ARMv8.1 Rounding Double Multiply Add/Subtract instructions">; +def FeaturePAN : SubtargetFeature< + "pan", "HasPAN", "true", + "Enables ARM v8.1 Privileged Access-Never extension">; + +def FeatureLOR : SubtargetFeature< + "lor", "HasLOR", "true", + "Enables ARM v8.1 Limited Ordering Regions extension">; + +def FeatureVH : SubtargetFeature< + "vh", "HasVH", "true", + "Enables ARM v8.1 Virtual Host extension">; + def FeaturePerfMon : SubtargetFeature<"perfmon", "HasPerfMon", "true", "Enable ARMv8 PMUv3 Performance Monitors extension">; def FeatureFullFP16 : SubtargetFeature<"fullfp16", "HasFullFP16", "true", "Full FP16", [FeatureFPARMv8]>; +def FeatureFP16FML : SubtargetFeature<"fp16fml", "HasFP16FML", "true", + "Enable FP16 FML instructions", [FeatureFullFP16]>; + def FeatureSPE : SubtargetFeature<"spe", "HasSPE", "true", "Enable Statistical Profiling extension">; +def FeaturePAN_RWV : SubtargetFeature< + "pan-rwv", "HasPAN_RWV", "true", + "Enable v8.2 PAN s1e1R and s1e1W Variants", + [FeaturePAN]>; + +// UAO PState +def FeaturePsUAO : SubtargetFeature< "uaops", "HasPsUAO", "true", + "Enable v8.2 UAO PState">; + +def FeatureCCPP : SubtargetFeature<"ccpp", "HasCCPP", + "true", "Enable v8.2 data Cache Clean to Point of Persistence" >; + def FeatureSVE : SubtargetFeature<"sve", "HasSVE", "true", "Enable Scalable Vector Extension (SVE) instructions">; -/// Cyclone has register move instructions which are "free". def FeatureZCRegMove : SubtargetFeature<"zcm", "HasZeroCycleRegMove", "true", "Has zero-cycle register moves">; +def FeatureZCZeroingGP : SubtargetFeature<"zcz-gp", "HasZeroCycleZeroingGP", "true", + "Has zero-cycle zeroing instructions for generic registers">; + +def FeatureZCZeroingFP : SubtargetFeature<"zcz-fp", "HasZeroCycleZeroingFP", "true", + "Has zero-cycle zeroing instructions for FP registers">; -/// Cyclone has instructions which zero registers for "free". def FeatureZCZeroing : SubtargetFeature<"zcz", "HasZeroCycleZeroing", "true", - "Has zero-cycle zeroing instructions">; + "Has zero-cycle zeroing instructions", + [FeatureZCZeroingGP, FeatureZCZeroingFP]>; /// ... but the floating-point version doesn't quite work in rare cases on older /// CPUs. @@ -96,13 +127,14 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align", "Disallow all unaligned memory " "access">; -def FeatureReserveX18 : SubtargetFeature<"reserve-x18", "ReserveX18", "true", - "Reserve X18, making it unavailable " - "as a GPR">; +foreach i = {1-7,18,20} in + def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true", + "Reserve X"#i#", making it unavailable " + "as a GPR">; -def FeatureReserveX20 : SubtargetFeature<"reserve-x20", "ReserveX20", "true", - "Reserve X20, making it unavailable " - "as a GPR">; +foreach i = {8-15,18} in + def FeatureCallSavedX#i : SubtargetFeature<"call-saved-x"#i, + "CustomCallSavedXRegs["#i#"]", "true", "Make X"#i#" callee saved.">; def FeatureUseAA : SubtargetFeature<"use-aa", "UseAA", "true", "Use alias analysis during codegen">; @@ -117,11 +149,11 @@ def FeaturePredictableSelectIsExpensive : SubtargetFeature< def FeatureCustomCheapAsMoveHandling : SubtargetFeature<"custom-cheap-as-move", "CustomAsCheapAsMove", "true", - "Use custom code for TargetInstrInfo::isAsCheapAsAMove()">; + "Use custom handling of cheap instructions">; def FeatureExynosCheapAsMoveHandling : SubtargetFeature<"exynos-cheap-as-move", "ExynosAsCheapAsMove", "true", - "Use Exynos specific code in TargetInstrInfo::isAsCheapAsAMove()", + "Use Exynos specific handling of cheap instructions", [FeatureCustomCheapAsMoveHandling]>; def FeaturePostRAScheduler : SubtargetFeature<"use-postra-scheduler", @@ -156,10 +188,18 @@ def FeatureFuseAES : SubtargetFeature< "fuse-aes", "HasFuseAES", "true", "CPU fuses AES crypto operations">; +def FeatureFuseArithmeticLogic : SubtargetFeature< + "fuse-arith-logic", "HasFuseArithmeticLogic", "true", + "CPU fuses arithmetic and logic operations">; + def FeatureFuseCCSelect : SubtargetFeature< "fuse-csel", "HasFuseCCSelect", "true", "CPU fuses conditional select operations">; +def FeatureFuseCryptoEOR : SubtargetFeature< + "fuse-crypto-eor", "HasFuseCryptoEOR", "true", + "CPU fuses AES/PMULL and EOR operations">; + def FeatureFuseLiterals : SubtargetFeature< "fuse-literals", "HasFuseLiterals", "true", "CPU fuses literal generation operations">; @@ -168,6 +208,10 @@ def FeatureDisableLatencySchedHeuristic : SubtargetFeature< "disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true", "Disable latency scheduling heuristic">; +def FeatureForce32BitJumpTables + : SubtargetFeature<"force-32bit-jump-tables", "Force32BitJumpTables", "true", + "Force jump table entries to be 32-bits wide except at MinSize">; + def FeatureRCPC : SubtargetFeature<"rcpc", "HasRCPC", "true", "Enable support for RCPC extension">; @@ -179,6 +223,66 @@ def FeatureDotProd : SubtargetFeature< "dotprod", "HasDotProd", "true", "Enable dot product support">; +def FeaturePA : SubtargetFeature< + "pa", "HasPA", "true", + "Enable v8.3-A Pointer Authentication enchancement">; + +def FeatureJS : SubtargetFeature< + "jsconv", "HasJS", "true", + "Enable v8.3-A JavaScript FP conversion enchancement", + [FeatureFPARMv8]>; + +def FeatureCCIDX : SubtargetFeature< + "ccidx", "HasCCIDX", "true", + "Enable v8.3-A Extend of the CCSIDR number of sets">; + +def FeatureComplxNum : SubtargetFeature< + "complxnum", "HasComplxNum", "true", + "Enable v8.3-A Floating-point complex number support", + [FeatureNEON]>; + +def FeatureNV : SubtargetFeature< + "nv", "HasNV", "true", + "Enable v8.4-A Nested Virtualization Enchancement">; + +def FeatureRASv8_4 : SubtargetFeature< + "rasv8_4", "HasRASv8_4", "true", + "Enable v8.4-A Reliability, Availability and Serviceability extension", + [FeatureRAS]>; + +def FeatureMPAM : SubtargetFeature< + "mpam", "HasMPAM", "true", + "Enable v8.4-A Memory system Partitioning and Monitoring extension">; + +def FeatureDIT : SubtargetFeature< + "dit", "HasDIT", "true", + "Enable v8.4-A Data Independent Timing instructions">; + +def FeatureTRACEV8_4 : SubtargetFeature< + "tracev8.4", "HasTRACEV8_4", "true", + "Enable v8.4-A Trace extension">; + +def FeatureAM : SubtargetFeature< + "am", "HasAM", "true", + "Enable v8.4-A Activity Monitors extension">; + +def FeatureSEL2 : SubtargetFeature< + "sel2", "HasSEL2", "true", + "Enable v8.4-A Secure Exception Level 2 extension">; + +def FeatureTLB_RMI : SubtargetFeature< + "tlb-rmi", "HasTLB_RMI", "true", + "Enable v8.4-A TLB Range and Maintenance Instructions">; + +def FeatureFMI : SubtargetFeature< + "fmi", "HasFMI", "true", + "Enable v8.4-A Flag Manipulation Instructions">; + +// 8.4 RCPC enchancements: LDAPR & STLR instructions with Immediate Offset +def FeatureRCPC_IMMO : SubtargetFeature<"rcpc-immo", "HasRCPC_IMMO", "true", + "Enable v8.4-A RCPC instructions with Immediate Offsets", + [FeatureRCPC]>; + def FeatureNoNegativeImmediates : SubtargetFeature<"no-neg-immediates", "NegativeImmediates", "false", "Convert immediates and instructions " @@ -196,21 +300,65 @@ def FeatureAggressiveFMA : "true", "Enable Aggressive FMA for floating-point.">; +def FeatureAltFPCmp : SubtargetFeature<"altnzcv", "HasAlternativeNZCV", "true", + "Enable alternative NZCV format for floating point comparisons">; + +def FeatureFRInt3264 : SubtargetFeature<"fptoint", "HasFRInt3264", "true", + "Enable FRInt[32|64][Z|X] instructions that round a floating-point number to " + "an integer (in FP format) forcing it to fit into a 32- or 64-bit int" >; + +def FeatureSpecRestrict : SubtargetFeature<"specrestrict", "HasSpecRestrict", + "true", "Enable architectural speculation restriction" >; + +def FeatureSB : SubtargetFeature<"sb", "HasSB", + "true", "Enable v8.5 Speculation Barrier" >; + +def FeatureSSBS : SubtargetFeature<"ssbs", "HasSSBS", + "true", "Enable Speculative Store Bypass Safe bit" >; + +def FeaturePredRes : SubtargetFeature<"predres", "HasPredRes", "true", + "Enable v8.5a execution and data prediction invalidation instructions" >; + +def FeatureCacheDeepPersist : SubtargetFeature<"ccdp", "HasCCDP", + "true", "Enable v8.5 Cache Clean to Point of Deep Persistence" >; + +def FeatureBranchTargetId : SubtargetFeature<"bti", "HasBTI", + "true", "Enable Branch Target Identification" >; + +def FeatureRandGen : SubtargetFeature<"rand", "HasRandGen", + "true", "Enable Random Number generation instructions" >; + +def FeatureMTE : SubtargetFeature<"mte", "HasMTE", + "true", "Enable Memory Tagging Extension" >; + //===----------------------------------------------------------------------===// // Architectures. // def HasV8_1aOps : SubtargetFeature<"v8.1a", "HasV8_1aOps", "true", - "Support ARM v8.1a instructions", [FeatureCRC, FeatureLSE, FeatureRDM]>; + "Support ARM v8.1a instructions", [FeatureCRC, FeatureLSE, FeatureRDM, + FeaturePAN, FeatureLOR, FeatureVH]>; def HasV8_2aOps : SubtargetFeature<"v8.2a", "HasV8_2aOps", "true", - "Support ARM v8.2a instructions", [HasV8_1aOps, FeatureRAS]>; + "Support ARM v8.2a instructions", [HasV8_1aOps, FeaturePsUAO, + FeaturePAN_RWV, FeatureRAS, FeatureCCPP]>; def HasV8_3aOps : SubtargetFeature<"v8.3a", "HasV8_3aOps", "true", - "Support ARM v8.3a instructions", [HasV8_2aOps, FeatureRCPC]>; + "Support ARM v8.3a instructions", [HasV8_2aOps, FeatureRCPC, FeaturePA, + FeatureJS, FeatureCCIDX, FeatureComplxNum]>; def HasV8_4aOps : SubtargetFeature<"v8.4a", "HasV8_4aOps", "true", - "Support ARM v8.4a instructions", [HasV8_3aOps, FeatureDotProd]>; + "Support ARM v8.4a instructions", [HasV8_3aOps, FeatureDotProd, + FeatureNV, FeatureRASv8_4, FeatureMPAM, FeatureDIT, + FeatureTRACEV8_4, FeatureAM, FeatureSEL2, FeatureTLB_RMI, + FeatureFMI, FeatureRCPC_IMMO]>; + +def HasV8_5aOps : SubtargetFeature< + "v8.5a", "HasV8_5aOps", "true", "Support ARM v8.5a instructions", + [HasV8_4aOps, FeatureAltFPCmp, FeatureFRInt3264, FeatureSpecRestrict, + FeatureSSBS, FeatureSB, FeaturePredRes, FeatureCacheDeepPersist, + FeatureBranchTargetId] +>; //===----------------------------------------------------------------------===// // Register File Description @@ -226,6 +374,8 @@ include "AArch64CallingConvention.td" include "AArch64Schedule.td" include "AArch64InstrInfo.td" +include "AArch64SchedPredicates.td" +include "AArch64SchedPredExynos.td" def AArch64InstrInfo : InstrInfo; @@ -245,6 +395,7 @@ include "AArch64SchedFalkor.td" include "AArch64SchedKryo.td" include "AArch64SchedExynosM1.td" include "AArch64SchedExynosM3.td" +include "AArch64SchedExynosM4.td" include "AArch64SchedThunderX.td" include "AArch64SchedThunderX2T99.td" @@ -343,6 +494,7 @@ def ProcCyclone : SubtargetFeature<"cyclone", "ARMProcFamily", "Cyclone", FeatureDisableLatencySchedHeuristic, FeatureFPARMv8, FeatureFuseAES, + FeatureFuseCryptoEOR, FeatureNEON, FeaturePerfMon, FeatureZCRegMove, @@ -356,14 +508,13 @@ def ProcExynosM1 : SubtargetFeature<"exynosm1", "ARMProcFamily", "ExynosM1", FeatureCRC, FeatureCrypto, FeatureExynosCheapAsMoveHandling, - FeatureFPARMv8, + FeatureForce32BitJumpTables, FeatureFuseAES, - FeatureNEON, FeaturePerfMon, FeaturePostRAScheduler, FeatureSlowMisaligned128Store, FeatureUseRSqrt, - FeatureZCZeroing]>; + FeatureZCZeroingFP]>; def ProcExynosM2 : SubtargetFeature<"exynosm2", "ARMProcFamily", "ExynosM1", "Samsung Exynos-M2 processors", @@ -371,29 +522,47 @@ def ProcExynosM2 : SubtargetFeature<"exynosm2", "ARMProcFamily", "ExynosM1", FeatureCRC, FeatureCrypto, FeatureExynosCheapAsMoveHandling, - FeatureFPARMv8, + FeatureForce32BitJumpTables, FeatureFuseAES, - FeatureNEON, FeaturePerfMon, FeaturePostRAScheduler, FeatureSlowMisaligned128Store, - FeatureZCZeroing]>; + FeatureZCZeroingFP]>; def ProcExynosM3 : SubtargetFeature<"exynosm3", "ARMProcFamily", "ExynosM3", "Samsung Exynos-M3 processors", [FeatureCRC, FeatureCrypto, FeatureExynosCheapAsMoveHandling, - FeatureFPARMv8, + FeatureForce32BitJumpTables, FeatureFuseAddress, FeatureFuseAES, FeatureFuseCCSelect, FeatureFuseLiterals, FeatureLSLFast, - FeatureNEON, FeaturePerfMon, FeaturePostRAScheduler, FeaturePredictableSelectIsExpensive, + FeatureZCZeroingFP]>; + +def ProcExynosM4 : SubtargetFeature<"exynosm4", "ARMProcFamily", "ExynosM3", + "Samsung Exynos-M4 processors", + [HasV8_2aOps, + FeatureArithmeticBccFusion, + FeatureArithmeticCbzFusion, + FeatureCrypto, + FeatureDotProd, + FeatureExynosCheapAsMoveHandling, + FeatureForce32BitJumpTables, + FeatureFP16FML, + FeatureFuseAddress, + FeatureFuseAES, + FeatureFuseArithmeticLogic, + FeatureFuseCCSelect, + FeatureFuseLiterals, + FeatureLSLFast, + FeaturePerfMon, + FeaturePostRAScheduler, FeatureZCZeroing]>; def ProcKryo : SubtargetFeature<"kryo", "ARMProcFamily", "Kryo", @@ -438,7 +607,7 @@ def ProcSaphira : SubtargetFeature<"saphira", "ARMProcFamily", "Saphira", FeaturePredictableSelectIsExpensive, FeatureZCZeroing, FeatureLSLFast, - HasV8_3aOps]>; + HasV8_4aOps]>; def ProcThunderX2T99 : SubtargetFeature<"thunderx2t99", "ARMProcFamily", "ThunderX2T99", @@ -497,6 +666,21 @@ def ProcThunderXT83 : SubtargetFeature<"thunderxt83", "ARMProcFamily", FeaturePredictableSelectIsExpensive, FeatureNEON]>; +def ProcTSV110 : SubtargetFeature<"tsv110", "ARMProcFamily", "TSV110", + "HiSilicon TS-V110 processors", [ + HasV8_2aOps, + FeatureCrypto, + FeatureCustomCheapAsMoveHandling, + FeatureFPARMv8, + FeatureFuseAES, + FeatureNEON, + FeaturePerfMon, + FeaturePostRAScheduler, + FeatureSPE, + FeatureFullFP16, + FeatureFP16FML, + FeatureDotProd]>; + def : ProcessorModel<"generic", NoSchedModel, [ FeatureFPARMv8, FeatureFuseAES, @@ -518,7 +702,7 @@ def : ProcessorModel<"cyclone", CycloneModel, [ProcCyclone]>; def : ProcessorModel<"exynos-m1", ExynosM1Model, [ProcExynosM1]>; def : ProcessorModel<"exynos-m2", ExynosM1Model, [ProcExynosM2]>; def : ProcessorModel<"exynos-m3", ExynosM3Model, [ProcExynosM3]>; -def : ProcessorModel<"exynos-m4", ExynosM3Model, [ProcExynosM3]>; +def : ProcessorModel<"exynos-m4", ExynosM4Model, [ProcExynosM4]>; def : ProcessorModel<"falkor", FalkorModel, [ProcFalkor]>; def : ProcessorModel<"saphira", FalkorModel, [ProcSaphira]>; def : ProcessorModel<"kryo", KryoModel, [ProcKryo]>; @@ -529,6 +713,8 @@ def : ProcessorModel<"thunderxt81", ThunderXT8XModel, [ProcThunderXT81]>; def : ProcessorModel<"thunderxt83", ThunderXT8XModel, [ProcThunderXT83]>; // Cavium ThunderX2T9X Processors. Formerly Broadcom Vulcan. def : ProcessorModel<"thunderx2t99", ThunderX2T99Model, [ProcThunderX2T99]>; +// FIXME: HiSilicon TSV110 is currently modeled as a Cortex-A57. +def : ProcessorModel<"tsv110", CortexA57Model, [ProcTSV110]>; //===----------------------------------------------------------------------===// // Assembly parser @@ -577,3 +763,9 @@ def AArch64 : Target { let AssemblyWriters = [GenericAsmWriter, AppleAsmWriter]; let AllowRegisterRenaming = 1; } + +//===----------------------------------------------------------------------===// +// Pfm Counters +//===----------------------------------------------------------------------===// + +include "AArch64PfmCounters.td" diff --git a/lib/Target/AArch64/AArch64A57FPLoadBalancing.cpp b/lib/Target/AArch64/AArch64A57FPLoadBalancing.cpp index a95476b91187..452fbd3488b0 100644 --- a/lib/Target/AArch64/AArch64A57FPLoadBalancing.cpp +++ b/lib/Target/AArch64/AArch64A57FPLoadBalancing.cpp @@ -377,11 +377,10 @@ bool AArch64A57FPLoadBalancing::runOnBasicBlock(MachineBasicBlock &MBB) { // Now we have a set of sets, order them by start address so // we can iterate over them sequentially. - llvm::sort(V.begin(), V.end(), - [](const std::vector<Chain*> &A, - const std::vector<Chain*> &B) { - return A.front()->startsBefore(B.front()); - }); + llvm::sort(V, + [](const std::vector<Chain *> &A, const std::vector<Chain *> &B) { + return A.front()->startsBefore(B.front()); + }); // As we only have two colors, we can track the global (BB-level) balance of // odds versus evens. We aim to keep this near zero to keep both execution @@ -453,16 +452,16 @@ bool AArch64A57FPLoadBalancing::colorChainSet(std::vector<Chain*> GV, // change them to! // Final tie-break with instruction order so pass output is stable (i.e. not // dependent on malloc'd pointer values). - llvm::sort(GV.begin(), GV.end(), [](const Chain *G1, const Chain *G2) { - if (G1->size() != G2->size()) - return G1->size() > G2->size(); - if (G1->requiresFixup() != G2->requiresFixup()) - return G1->requiresFixup() > G2->requiresFixup(); - // Make sure startsBefore() produces a stable final order. - assert((G1 == G2 || (G1->startsBefore(G2) ^ G2->startsBefore(G1))) && - "Starts before not total order!"); - return G1->startsBefore(G2); - }); + llvm::sort(GV, [](const Chain *G1, const Chain *G2) { + if (G1->size() != G2->size()) + return G1->size() > G2->size(); + if (G1->requiresFixup() != G2->requiresFixup()) + return G1->requiresFixup() > G2->requiresFixup(); + // Make sure startsBefore() produces a stable final order. + assert((G1 == G2 || (G1->startsBefore(G2) ^ G2->startsBefore(G1))) && + "Starts before not total order!"); + return G1->startsBefore(G2); + }); Color PreferredColor = Parity < 0 ? Color::Even : Color::Odd; while (Chain *G = getAndEraseNext(PreferredColor, GV)) { diff --git a/lib/Target/AArch64/AArch64AsmPrinter.cpp b/lib/Target/AArch64/AArch64AsmPrinter.cpp index 52819dedc23d..0442076992e2 100644 --- a/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -21,16 +21,20 @@ #include "InstPrinter/AArch64InstPrinter.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" +#include "MCTargetDesc/AArch64TargetStreamer.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/StackMaps.h" #include "llvm/CodeGen/TargetRegisterInfo.h" @@ -77,6 +81,12 @@ public: return MCInstLowering.lowerOperand(MO, MCOp); } + void EmitJumpTableInfo() override; + void emitJumpTableEntry(const MachineJumpTableInfo *MJTI, + const MachineBasicBlock *MBB, unsigned JTI); + + void LowerJumpTableDestSmall(MCStreamer &OutStreamer, const MachineInstr &MI); + void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, const MachineInstr &MI); void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, @@ -100,12 +110,33 @@ public: AU.setPreservesAll(); } - bool runOnMachineFunction(MachineFunction &F) override { - AArch64FI = F.getInfo<AArch64FunctionInfo>(); - STI = static_cast<const AArch64Subtarget*>(&F.getSubtarget()); - bool Result = AsmPrinter::runOnMachineFunction(F); + bool runOnMachineFunction(MachineFunction &MF) override { + AArch64FI = MF.getInfo<AArch64FunctionInfo>(); + STI = static_cast<const AArch64Subtarget*>(&MF.getSubtarget()); + + SetupMachineFunction(MF); + + if (STI->isTargetCOFF()) { + bool Internal = MF.getFunction().hasInternalLinkage(); + COFF::SymbolStorageClass Scl = Internal ? COFF::IMAGE_SYM_CLASS_STATIC + : COFF::IMAGE_SYM_CLASS_EXTERNAL; + int Type = + COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT; + + OutStreamer->BeginCOFFSymbolDef(CurrentFnSym); + OutStreamer->EmitCOFFSymbolStorageClass(Scl); + OutStreamer->EmitCOFFSymbolType(Type); + OutStreamer->EndCOFFSymbolDef(); + } + + // Emit the rest of the function body. + EmitFunctionBody(); + + // Emit the XRay table for this function. emitXRayTable(); - return Result; + + // We didn't modify anything. + return false; } private: @@ -208,7 +239,7 @@ void AArch64AsmPrinter::EmitEndOfAsmFile(Module &M) { // linker can safely perform dead code stripping. Since LLVM never // generates code that does this, it is always safe to set. OutStreamer->EmitAssemblerFlag(MCAF_SubsectionsViaSymbols); - SM.serializeToStackMapSection(); + emitStackMaps(SM); } } @@ -433,6 +464,104 @@ void AArch64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI, printOperand(MI, NOps - 2, OS); } +void AArch64AsmPrinter::EmitJumpTableInfo() { + const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + if (!MJTI) return; + + const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables(); + if (JT.empty()) return; + + const TargetLoweringObjectFile &TLOF = getObjFileLowering(); + MCSection *ReadOnlySec = TLOF.getSectionForJumpTable(MF->getFunction(), TM); + OutStreamer->SwitchSection(ReadOnlySec); + + auto AFI = MF->getInfo<AArch64FunctionInfo>(); + for (unsigned JTI = 0, e = JT.size(); JTI != e; ++JTI) { + const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs; + + // If this jump table was deleted, ignore it. + if (JTBBs.empty()) continue; + + unsigned Size = AFI->getJumpTableEntrySize(JTI); + EmitAlignment(Log2_32(Size)); + OutStreamer->EmitLabel(GetJTISymbol(JTI)); + + for (auto *JTBB : JTBBs) + emitJumpTableEntry(MJTI, JTBB, JTI); + } +} + +void AArch64AsmPrinter::emitJumpTableEntry(const MachineJumpTableInfo *MJTI, + const MachineBasicBlock *MBB, + unsigned JTI) { + const MCExpr *Value = MCSymbolRefExpr::create(MBB->getSymbol(), OutContext); + auto AFI = MF->getInfo<AArch64FunctionInfo>(); + unsigned Size = AFI->getJumpTableEntrySize(JTI); + + if (Size == 4) { + // .word LBB - LJTI + const TargetLowering *TLI = MF->getSubtarget().getTargetLowering(); + const MCExpr *Base = TLI->getPICJumpTableRelocBaseExpr(MF, JTI, OutContext); + Value = MCBinaryExpr::createSub(Value, Base, OutContext); + } else { + // .byte (LBB - LBB) >> 2 (or .hword) + const MCSymbol *BaseSym = AFI->getJumpTableEntryPCRelSymbol(JTI); + const MCExpr *Base = MCSymbolRefExpr::create(BaseSym, OutContext); + Value = MCBinaryExpr::createSub(Value, Base, OutContext); + Value = MCBinaryExpr::createLShr( + Value, MCConstantExpr::create(2, OutContext), OutContext); + } + + OutStreamer->EmitValue(Value, Size); +} + +/// Small jump tables contain an unsigned byte or half, representing the offset +/// from the lowest-addressed possible destination to the desired basic +/// block. Since all instructions are 4-byte aligned, this is further compressed +/// by counting in instructions rather than bytes (i.e. divided by 4). So, to +/// materialize the correct destination we need: +/// +/// adr xDest, .LBB0_0 +/// ldrb wScratch, [xTable, xEntry] (with "lsl #1" for ldrh). +/// add xDest, xDest, xScratch, lsl #2 +void AArch64AsmPrinter::LowerJumpTableDestSmall(llvm::MCStreamer &OutStreamer, + const llvm::MachineInstr &MI) { + unsigned DestReg = MI.getOperand(0).getReg(); + unsigned ScratchReg = MI.getOperand(1).getReg(); + unsigned ScratchRegW = + STI->getRegisterInfo()->getSubReg(ScratchReg, AArch64::sub_32); + unsigned TableReg = MI.getOperand(2).getReg(); + unsigned EntryReg = MI.getOperand(3).getReg(); + int JTIdx = MI.getOperand(4).getIndex(); + bool IsByteEntry = MI.getOpcode() == AArch64::JumpTableDest8; + + // This has to be first because the compression pass based its reachability + // calculations on the start of the JumpTableDest instruction. + auto Label = + MF->getInfo<AArch64FunctionInfo>()->getJumpTableEntryPCRelSymbol(JTIdx); + EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::ADR) + .addReg(DestReg) + .addExpr(MCSymbolRefExpr::create( + Label, MF->getContext()))); + + // Load the number of instruction-steps to offset from the label. + unsigned LdrOpcode = IsByteEntry ? AArch64::LDRBBroX : AArch64::LDRHHroX; + EmitToStreamer(OutStreamer, MCInstBuilder(LdrOpcode) + .addReg(ScratchRegW) + .addReg(TableReg) + .addReg(EntryReg) + .addImm(0) + .addImm(IsByteEntry ? 0 : 1)); + + // Multiply the steps by 4 and add to the already materialized base label + // address. + EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(DestReg) + .addReg(DestReg) + .addReg(ScratchReg) + .addImm(2)); +} + void AArch64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, const MachineInstr &MI) { unsigned NumNOPBytes = StackMapOpers(&MI).getNumPatchBytes(); @@ -503,7 +632,7 @@ void AArch64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, void AArch64AsmPrinter::EmitFMov0(const MachineInstr &MI) { unsigned DestReg = MI.getOperand(0).getReg(); - if (STI->hasZeroCycleZeroing() && !STI->hasZeroCycleZeroingFPWorkaround()) { + if (STI->hasZeroCycleZeroingFP() && !STI->hasZeroCycleZeroingFPWorkaround()) { // Convert H/S/D register to corresponding Q register if (AArch64::H0 <= DestReg && DestReg <= AArch64::H31) DestReg = AArch64::Q0 + (DestReg - AArch64::H0); @@ -559,6 +688,8 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { OutStreamer->EmitLabel(LOHLabel); } + AArch64TargetStreamer *TS = + static_cast<AArch64TargetStreamer *>(OutStreamer->getTargetStreamer()); // Do any manual lowerings. switch (MI->getOpcode()) { default: @@ -585,12 +716,27 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { OutStreamer->EmitRawText(StringRef(OS.str())); } return; + + case AArch64::EMITBKEY: { + ExceptionHandling ExceptionHandlingType = MAI->getExceptionHandlingType(); + if (ExceptionHandlingType != ExceptionHandling::DwarfCFI && + ExceptionHandlingType != ExceptionHandling::ARM) + return; + + if (needsCFIMoves() == CFI_M_None) + return; + + OutStreamer->EmitCFIBKeyFrame(); + return; + } } // Tail calls use pseudo instructions so they have the proper code-gen // attributes (isCall, isReturn, etc.). We lower them to the real // instruction here. - case AArch64::TCRETURNri: { + case AArch64::TCRETURNri: + case AArch64::TCRETURNriBTI: + case AArch64::TCRETURNriALL: { MCInst TmpInst; TmpInst.setOpcode(AArch64::BR); TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); @@ -660,6 +806,32 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { return; } + case AArch64::JumpTableDest32: { + // We want: + // ldrsw xScratch, [xTable, xEntry, lsl #2] + // add xDest, xTable, xScratch + unsigned DestReg = MI->getOperand(0).getReg(), + ScratchReg = MI->getOperand(1).getReg(), + TableReg = MI->getOperand(2).getReg(), + EntryReg = MI->getOperand(3).getReg(); + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWroX) + .addReg(ScratchReg) + .addReg(TableReg) + .addReg(EntryReg) + .addImm(0) + .addImm(1)); + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(DestReg) + .addReg(TableReg) + .addReg(ScratchReg) + .addImm(0)); + return; + } + case AArch64::JumpTableDest16: + case AArch64::JumpTableDest8: + LowerJumpTableDestSmall(*OutStreamer, *MI); + return; + case AArch64::FMOVH0: case AArch64::FMOVS0: case AArch64::FMOVD0: @@ -683,6 +855,100 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { case TargetOpcode::PATCHABLE_TAIL_CALL: LowerPATCHABLE_TAIL_CALL(*MI); return; + + case AArch64::SEH_StackAlloc: + TS->EmitARM64WinCFIAllocStack(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveFPLR: + TS->EmitARM64WinCFISaveFPLR(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveFPLR_X: + assert(MI->getOperand(0).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFPLRX(-MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveReg: + TS->EmitARM64WinCFISaveReg(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveReg_X: + assert(MI->getOperand(1).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveRegX(MI->getOperand(0).getImm(), + -MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveRegP: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp"); + TS->EmitARM64WinCFISaveRegP(MI->getOperand(0).getImm(), + MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveRegP_X: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp_x"); + assert(MI->getOperand(2).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveRegPX(MI->getOperand(0).getImm(), + -MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveFReg: + TS->EmitARM64WinCFISaveFReg(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveFReg_X: + assert(MI->getOperand(1).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFRegX(MI->getOperand(0).getImm(), + -MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveFRegP: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp"); + TS->EmitARM64WinCFISaveFRegP(MI->getOperand(0).getImm(), + MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveFRegP_X: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp_x"); + assert(MI->getOperand(2).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFRegPX(MI->getOperand(0).getImm(), + -MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SetFP: + TS->EmitARM64WinCFISetFP(); + return; + + case AArch64::SEH_AddFP: + TS->EmitARM64WinCFIAddFP(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_Nop: + TS->EmitARM64WinCFINop(); + return; + + case AArch64::SEH_PrologEnd: + TS->EmitARM64WinCFIPrologEnd(); + return; + + case AArch64::SEH_EpilogStart: + TS->EmitARM64WinCFIEpilogStart(); + return; + + case AArch64::SEH_EpilogEnd: + TS->EmitARM64WinCFIEpilogEnd(); + return; } // Finally, do the automated lowerings for everything else. diff --git a/lib/Target/AArch64/AArch64BranchTargets.cpp b/lib/Target/AArch64/AArch64BranchTargets.cpp new file mode 100644 index 000000000000..da70a624c5be --- /dev/null +++ b/lib/Target/AArch64/AArch64BranchTargets.cpp @@ -0,0 +1,130 @@ +//===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass inserts BTI instructions at the start of every function and basic +// block which could be indirectly called. The hardware will (when enabled) +// trap when an indirect branch or call instruction targets an instruction +// which is not a valid BTI instruction. This is intended to guard against +// control-flow hijacking attacks. Note that this does not do anything for RET +// instructions, as they can be more precisely protected by return address +// signing. +// +//===----------------------------------------------------------------------===// + +#include "AArch64Subtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "aarch64-branch-targets" +#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets" + +namespace { +class AArch64BranchTargets : public MachineFunctionPass { +public: + static char ID; + AArch64BranchTargets() : MachineFunctionPass(ID) {} + void getAnalysisUsage(AnalysisUsage &AU) const override; + bool runOnMachineFunction(MachineFunction &MF) override; + StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; } + +private: + void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump); +}; +} // end anonymous namespace + +char AArch64BranchTargets::ID = 0; + +INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets", + AARCH64_BRANCH_TARGETS_NAME, false, false) + +void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +FunctionPass *llvm::createAArch64BranchTargetsPass() { + return new AArch64BranchTargets(); +} + +bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) { + const Function &F = MF.getFunction(); + if (!F.hasFnAttribute("branch-target-enforcement")) + return false; + + LLVM_DEBUG( + dbgs() << "********** AArch64 Branch Targets **********\n" + << "********** Function: " << MF.getName() << '\n'); + + // LLVM does not consider basic blocks which are the targets of jump tables + // to be address-taken (the address can't escape anywhere else), but they are + // used for indirect branches, so need BTI instructions. + SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets; + if (auto *JTI = MF.getJumpTableInfo()) + for (auto &JTE : JTI->getJumpTables()) + for (auto *MBB : JTE.MBBs) + JumpTableTargets.insert(MBB); + + bool MadeChange = false; + for (MachineBasicBlock &MBB : MF) { + bool CouldCall = false, CouldJump = false; + // If the function is address-taken or externally-visible, it could be + // indirectly called. PLT entries and tail-calls use BR, but when they are + // are in guarded pages should all use x16 or x17 to hold the called + // address, so we don't need to set CouldJump here. BR instructions in + // non-guarded pages (which might be non-BTI-aware code) are allowed to + // branch to a "BTI c" using any register. + if (&MBB == &*MF.begin() && (F.hasAddressTaken() || !F.hasLocalLinkage())) + CouldCall = true; + + // If the block itself is address-taken, it could be indirectly branched + // to, but not called. + if (MBB.hasAddressTaken() || JumpTableTargets.count(&MBB)) + CouldJump = true; + + if (CouldCall || CouldJump) { + addBTI(MBB, CouldCall, CouldJump); + MadeChange = true; + } + } + + return MadeChange; +} + +void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall, + bool CouldJump) { + LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "") + << (CouldCall ? "c" : "") << " to " << MBB.getName() + << "\n"); + + const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>( + MBB.getParent()->getSubtarget().getInstrInfo()); + + unsigned HintNum = 32; + if (CouldCall) + HintNum |= 2; + if (CouldJump) + HintNum |= 4; + assert(HintNum != 32 && "No target kinds!"); + + auto MBBI = MBB.begin(); + + // PACI[AB]SP are implicitly BTI JC, so no BTI instruction needed there. + if (MBBI != MBB.end() && (MBBI->getOpcode() == AArch64::PACIASP || + MBBI->getOpcode() == AArch64::PACIBSP)) + return; + + BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()), + TII->get(AArch64::HINT)) + .addImm(HintNum); +} diff --git a/lib/Target/AArch64/AArch64CallLowering.cpp b/lib/Target/AArch64/AArch64CallLowering.cpp index 26d532555e78..5980e5684e89 100644 --- a/lib/Target/AArch64/AArch64CallLowering.cpp +++ b/lib/Target/AArch64/AArch64CallLowering.cpp @@ -227,32 +227,45 @@ void AArch64CallLowering::splitToValueTypes( } bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, - const Value *Val, unsigned VReg) const { - MachineFunction &MF = MIRBuilder.getMF(); - const Function &F = MF.getFunction(); - + const Value *Val, + ArrayRef<unsigned> VRegs) const { auto MIB = MIRBuilder.buildInstrNoInsert(AArch64::RET_ReallyLR); - assert(((Val && VReg) || (!Val && !VReg)) && "Return value without a vreg"); - bool Success = true; - if (VReg) { - MachineRegisterInfo &MRI = MF.getRegInfo(); + assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) && + "Return value without a vreg"); - // We zero-extend i1s to i8. - if (MRI.getType(VReg).getSizeInBits() == 1) - VReg = MIRBuilder.buildZExt(LLT::scalar(8), VReg)->getOperand(0).getReg(); + bool Success = true; + if (!VRegs.empty()) { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(F.getCallingConv()); auto &DL = F.getParent()->getDataLayout(); + LLVMContext &Ctx = Val->getType()->getContext(); - ArgInfo OrigArg{VReg, Val->getType()}; - setArgFlags(OrigArg, AttributeList::ReturnIndex, DL, F); + SmallVector<EVT, 4> SplitEVTs; + ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs); + assert(VRegs.size() == SplitEVTs.size() && + "For each split Type there should be exactly one VReg."); SmallVector<ArgInfo, 8> SplitArgs; - splitToValueTypes(OrigArg, SplitArgs, DL, MRI, F.getCallingConv(), - [&](unsigned Reg, uint64_t Offset) { - MIRBuilder.buildExtract(Reg, VReg, Offset); - }); + for (unsigned i = 0; i < SplitEVTs.size(); ++i) { + // We zero-extend i1s to i8. + unsigned CurVReg = VRegs[i]; + if (MRI.getType(VRegs[i]).getSizeInBits() == 1) { + CurVReg = MIRBuilder.buildZExt(LLT::scalar(8), CurVReg) + ->getOperand(0) + .getReg(); + } + + ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVTs[i].getTypeForEVT(Ctx)}; + setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F); + splitToValueTypes(CurArgInfo, SplitArgs, DL, MRI, F.getCallingConv(), + [&](unsigned Reg, uint64_t Offset) { + MIRBuilder.buildExtract(Reg, CurVReg, Offset); + }); + } OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFn, AssignFn); Success = handleAssignments(MIRBuilder, SplitArgs, Handler); @@ -324,6 +337,10 @@ bool AArch64CallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, FuncInfo->setVarArgsStackIndex(MFI.CreateFixedObject(4, StackOffset, true)); } + auto &Subtarget = MF.getSubtarget<AArch64Subtarget>(); + if (Subtarget.hasCustomCallingConv()) + Subtarget.getRegisterInfo()->UpdateCustomCalleeSavedRegs(MF); + // Move back to the end of the basic block. MIRBuilder.setMBB(MBB); @@ -364,8 +381,14 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, MIB.add(Callee); // Tell the call which registers are clobbered. - auto TRI = MF.getSubtarget().getRegisterInfo(); - MIB.addRegMask(TRI->getCallPreservedMask(MF, F.getCallingConv())); + auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(MF, F.getCallingConv()); + if (MF.getSubtarget<AArch64Subtarget>().hasCustomCallingConv()) + TRI->UpdateCustomCallPreservedMask(MF, &Mask); + MIB.addRegMask(Mask); + + if (TRI->isAnyArgRegReserved(MF)) + TRI->emitReservedArgRegCallError(MF); // Do the actual argument marshalling. SmallVector<unsigned, 8> PhysRegs; diff --git a/lib/Target/AArch64/AArch64CallLowering.h b/lib/Target/AArch64/AArch64CallLowering.h index 68c127fc42e5..1c2bd6a4de5d 100644 --- a/lib/Target/AArch64/AArch64CallLowering.h +++ b/lib/Target/AArch64/AArch64CallLowering.h @@ -34,8 +34,8 @@ class AArch64CallLowering: public CallLowering { public: AArch64CallLowering(const AArch64TargetLowering &TLI); - bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val, - unsigned VReg) const override; + bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, + ArrayRef<unsigned> VRegs) const override; bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, ArrayRef<unsigned> VRegs) const override; diff --git a/lib/Target/AArch64/AArch64CallingConvention.td b/lib/Target/AArch64/AArch64CallingConvention.td index 30492003df14..5db941e9dac7 100644 --- a/lib/Target/AArch64/AArch64CallingConvention.td +++ b/lib/Target/AArch64/AArch64CallingConvention.td @@ -123,7 +123,7 @@ def RetCC_AArch64_AAPCS : CallingConv<[ // Vararg functions on windows pass floats in integer registers def CC_AArch64_Win64_VarArg : CallingConv<[ - CCIfType<[f16, f32], CCPromoteToType<f64>>, + CCIfType<[f16, f32], CCPromoteToType<f64>>, CCIfType<[f64], CCBitConvertToType<i64>>, CCDelegateTo<CC_AArch64_AAPCS> ]>; @@ -288,6 +288,20 @@ def CSR_AArch64_AAPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, D8, D9, D10, D11, D12, D13, D14, D15)>; +// Win64 has unwinding codes for an (FP,LR) pair, save_fplr and save_fplr_x. +// We put FP before LR, so that frame lowering logic generates (FP,LR) pairs, +// and not (LR,FP) pairs. +def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add FP, LR, X19, X20, X21, X22, + X23, X24, X25, X26, X27, X28, + D8, D9, D10, D11, + D12, D13, D14, D15)>; + +// AArch64 PCS for vector functions (VPCS) +// must (additionally) preserve full Q8-Q23 registers +def CSR_AArch64_AAVPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, + X23, X24, X25, X26, X27, X28, + (sequence "Q%u", 8, 23))>; + // Constructors and destructors return 'this' in the iOS 64-bit C++ ABI; since // 'this' and the pointer return value are both passed in X0 in these cases, // this can be partially modelled by treating X0 as a callee-saved register; @@ -362,5 +376,7 @@ def CSR_AArch64_AAPCS_SwiftError_SCS : CalleeSavedRegs<(add CSR_AArch64_AAPCS_SwiftError, X18)>; def CSR_AArch64_RT_MostRegs_SCS : CalleeSavedRegs<(add CSR_AArch64_RT_MostRegs, X18)>; +def CSR_AArch64_AAVPCS_SCS + : CalleeSavedRegs<(add CSR_AArch64_AAVPCS, X18)>; def CSR_AArch64_AAPCS_SCS : CalleeSavedRegs<(add CSR_AArch64_AAPCS, X18)>; diff --git a/lib/Target/AArch64/AArch64CompressJumpTables.cpp b/lib/Target/AArch64/AArch64CompressJumpTables.cpp new file mode 100644 index 000000000000..0924a27e2586 --- /dev/null +++ b/lib/Target/AArch64/AArch64CompressJumpTables.cpp @@ -0,0 +1,162 @@ +//==-- AArch64CompressJumpTables.cpp - Compress jump tables for AArch64 --====// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// This pass looks at the basic blocks each jump-table refers to and works out +// whether they can be emitted in a compressed form (with 8 or 16-bit +// entries). If so, it changes the opcode and flags them in the associated +// AArch64FunctionInfo. +// +//===----------------------------------------------------------------------===// + +#include "AArch64.h" +#include "AArch64MachineFunctionInfo.h" +#include "AArch64Subtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "aarch64-jump-tables" + +STATISTIC(NumJT8, "Number of jump-tables with 1-byte entries"); +STATISTIC(NumJT16, "Number of jump-tables with 2-byte entries"); +STATISTIC(NumJT32, "Number of jump-tables with 4-byte entries"); + +namespace { +class AArch64CompressJumpTables : public MachineFunctionPass { + const TargetInstrInfo *TII; + MachineFunction *MF; + SmallVector<int, 8> BlockInfo; + + int computeBlockSize(MachineBasicBlock &MBB); + void scanFunction(); + + bool compressJumpTable(MachineInstr &MI, int Offset); + +public: + static char ID; + AArch64CompressJumpTables() : MachineFunctionPass(ID) { + initializeAArch64CompressJumpTablesPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + StringRef getPassName() const override { + return "AArch64 Compress Jump Tables"; + } +}; +char AArch64CompressJumpTables::ID = 0; +} + +INITIALIZE_PASS(AArch64CompressJumpTables, DEBUG_TYPE, + "AArch64 compress jump tables pass", false, false) + +int AArch64CompressJumpTables::computeBlockSize(MachineBasicBlock &MBB) { + int Size = 0; + for (const MachineInstr &MI : MBB) + Size += TII->getInstSizeInBytes(MI); + return Size; +} + +void AArch64CompressJumpTables::scanFunction() { + BlockInfo.clear(); + BlockInfo.resize(MF->getNumBlockIDs()); + + int Offset = 0; + for (MachineBasicBlock &MBB : *MF) { + BlockInfo[MBB.getNumber()] = Offset; + Offset += computeBlockSize(MBB); + } +} + +bool AArch64CompressJumpTables::compressJumpTable(MachineInstr &MI, + int Offset) { + if (MI.getOpcode() != AArch64::JumpTableDest32) + return false; + + int JTIdx = MI.getOperand(4).getIndex(); + auto &JTInfo = *MF->getJumpTableInfo(); + const MachineJumpTableEntry &JT = JTInfo.getJumpTables()[JTIdx]; + + // The jump-table might have been optimized away. + if (JT.MBBs.empty()) + return false; + + int MaxOffset = std::numeric_limits<int>::min(), + MinOffset = std::numeric_limits<int>::max(); + MachineBasicBlock *MinBlock = nullptr; + for (auto Block : JT.MBBs) { + int BlockOffset = BlockInfo[Block->getNumber()]; + assert(BlockOffset % 4 == 0 && "misaligned basic block"); + + MaxOffset = std::max(MaxOffset, BlockOffset); + if (BlockOffset <= MinOffset) { + MinOffset = BlockOffset; + MinBlock = Block; + } + } + + // The ADR instruction needed to calculate the address of the first reachable + // basic block can address +/-1MB. + if (!isInt<21>(MinOffset - Offset)) { + ++NumJT32; + return false; + } + + int Span = MaxOffset - MinOffset; + auto AFI = MF->getInfo<AArch64FunctionInfo>(); + if (isUInt<8>(Span / 4)) { + AFI->setJumpTableEntryInfo(JTIdx, 1, MinBlock->getSymbol()); + MI.setDesc(TII->get(AArch64::JumpTableDest8)); + ++NumJT8; + return true; + } else if (isUInt<16>(Span / 4)) { + AFI->setJumpTableEntryInfo(JTIdx, 2, MinBlock->getSymbol()); + MI.setDesc(TII->get(AArch64::JumpTableDest16)); + ++NumJT16; + return true; + } + + ++NumJT32; + return false; +} + +bool AArch64CompressJumpTables::runOnMachineFunction(MachineFunction &MFIn) { + bool Changed = false; + MF = &MFIn; + + const auto &ST = MF->getSubtarget<AArch64Subtarget>(); + TII = ST.getInstrInfo(); + + if (ST.force32BitJumpTables() && !MF->getFunction().optForMinSize()) + return false; + + scanFunction(); + + for (MachineBasicBlock &MBB : *MF) { + int Offset = BlockInfo[MBB.getNumber()]; + for (MachineInstr &MI : MBB) { + Changed |= compressJumpTable(MI, Offset); + Offset += TII->getInstSizeInBytes(MI); + } + } + + return Changed; +} + +FunctionPass *llvm::createAArch64CompressJumpTablesPass() { + return new AArch64CompressJumpTables(); +} diff --git a/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp b/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp index 9226a9dd879b..f7190d58fbf9 100644 --- a/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ b/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -835,36 +835,55 @@ bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB, } case AArch64::LOADgot: { - // Expand into ADRP + LDR. + MachineFunction *MF = MBB.getParent(); unsigned DstReg = MI.getOperand(0).getReg(); const MachineOperand &MO1 = MI.getOperand(1); unsigned Flags = MO1.getTargetFlags(); - MachineInstrBuilder MIB1 = - BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADRP), DstReg); - MachineInstrBuilder MIB2 = - BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::LDRXui)) - .add(MI.getOperand(0)) - .addReg(DstReg); - if (MO1.isGlobal()) { - MIB1.addGlobalAddress(MO1.getGlobal(), 0, Flags | AArch64II::MO_PAGE); - MIB2.addGlobalAddress(MO1.getGlobal(), 0, - Flags | AArch64II::MO_PAGEOFF | AArch64II::MO_NC); - } else if (MO1.isSymbol()) { - MIB1.addExternalSymbol(MO1.getSymbolName(), Flags | AArch64II::MO_PAGE); - MIB2.addExternalSymbol(MO1.getSymbolName(), - Flags | AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + if (MF->getTarget().getCodeModel() == CodeModel::Tiny) { + // Tiny codemodel expand to LDR + MachineInstrBuilder MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(), + TII->get(AArch64::LDRXl), DstReg); + + if (MO1.isGlobal()) { + MIB.addGlobalAddress(MO1.getGlobal(), 0, Flags); + } else if (MO1.isSymbol()) { + MIB.addExternalSymbol(MO1.getSymbolName(), Flags); + } else { + assert(MO1.isCPI() && + "Only expect globals, externalsymbols, or constant pools"); + MIB.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), Flags); + } } else { - assert(MO1.isCPI() && - "Only expect globals, externalsymbols, or constant pools"); - MIB1.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), - Flags | AArch64II::MO_PAGE); - MIB2.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), - Flags | AArch64II::MO_PAGEOFF | - AArch64II::MO_NC); - } + // Small codemodel expand into ADRP + LDR. + MachineInstrBuilder MIB1 = + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADRP), DstReg); + MachineInstrBuilder MIB2 = + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::LDRXui)) + .add(MI.getOperand(0)) + .addReg(DstReg); - transferImpOps(MI, MIB1, MIB2); + if (MO1.isGlobal()) { + MIB1.addGlobalAddress(MO1.getGlobal(), 0, Flags | AArch64II::MO_PAGE); + MIB2.addGlobalAddress(MO1.getGlobal(), 0, + Flags | AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + } else if (MO1.isSymbol()) { + MIB1.addExternalSymbol(MO1.getSymbolName(), Flags | AArch64II::MO_PAGE); + MIB2.addExternalSymbol(MO1.getSymbolName(), Flags | + AArch64II::MO_PAGEOFF | + AArch64II::MO_NC); + } else { + assert(MO1.isCPI() && + "Only expect globals, externalsymbols, or constant pools"); + MIB1.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), + Flags | AArch64II::MO_PAGE); + MIB2.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), + Flags | AArch64II::MO_PAGEOFF | + AArch64II::MO_NC); + } + + transferImpOps(MI, MIB1, MIB2); + } MI.eraseFromParent(); return true; } diff --git a/lib/Target/AArch64/AArch64FastISel.cpp b/lib/Target/AArch64/AArch64FastISel.cpp index 572d1c22feea..47550cabb9f0 100644 --- a/lib/Target/AArch64/AArch64FastISel.cpp +++ b/lib/Target/AArch64/AArch64FastISel.cpp @@ -2016,8 +2016,9 @@ bool AArch64FastISel::selectLoad(const Instruction *I) { if (RetVT == MVT::i64 && VT <= MVT::i32) { if (WantZExt) { // Delete the last emitted instruction from emitLoad (SUBREG_TO_REG). - std::prev(FuncInfo.InsertPt)->eraseFromParent(); - ResultReg = std::prev(FuncInfo.InsertPt)->getOperand(0).getReg(); + MachineBasicBlock::iterator I(std::prev(FuncInfo.InsertPt)); + ResultReg = std::prev(I)->getOperand(0).getReg(); + removeDeadCode(I, std::next(I)); } else ResultReg = fastEmitInst_extractsubreg(MVT::i32, ResultReg, /*IsKill=*/true, @@ -2038,7 +2039,8 @@ bool AArch64FastISel::selectLoad(const Instruction *I) { break; } } - MI->eraseFromParent(); + MachineBasicBlock::iterator I(MI); + removeDeadCode(I, std::next(I)); MI = nullptr; if (Reg) MI = MRI.getUniqueVRegDef(Reg); @@ -2256,6 +2258,13 @@ static AArch64CC::CondCode getCompareCC(CmpInst::Predicate Pred) { /// Try to emit a combined compare-and-branch instruction. bool AArch64FastISel::emitCompareAndBranch(const BranchInst *BI) { + // Speculation tracking/SLH assumes that optimized TB(N)Z/CB(N)Z instructions + // will not be produced, as they are conditional branch instructions that do + // not set flags. + if (FuncInfo.MF->getFunction().hasFnAttribute( + Attribute::SpeculativeLoadHardening)) + return false; + assert(isa<CmpInst>(BI->getCondition()) && "Expected cmp instruction"); const CmpInst *CI = cast<CmpInst>(BI->getCondition()); CmpInst::Predicate Predicate = optimizeCmpPredicate(CI); @@ -2918,6 +2927,9 @@ bool AArch64FastISel::fastLowerArguments() { if (CC != CallingConv::C && CC != CallingConv::Swift) return false; + if (Subtarget->hasCustomCallingConv()) + return false; + // Only handle simple cases of up to 8 GPR and FPR each. unsigned GPRCnt = 0; unsigned FPRCnt = 0; @@ -3208,6 +3220,10 @@ bool AArch64FastISel::fastLowerCall(CallLoweringInfo &CLI) { if (!processCallArgs(CLI, OutVTs, NumBytes)) return false; + const AArch64RegisterInfo *RegInfo = Subtarget->getRegisterInfo(); + if (RegInfo->isAnyArgRegReserved(*MF)) + RegInfo->emitReservedArgRegCallError(*MF); + // Issue the call. MachineInstrBuilder MIB; if (Subtarget->useSmallAddressing()) { @@ -3443,6 +3459,21 @@ bool AArch64FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { updateValueMap(II, SrcReg); return true; } + case Intrinsic::sponentry: { + MachineFrameInfo &MFI = FuncInfo.MF->getFrameInfo(); + + // SP = FP + Fixed Object + 16 + int FI = MFI.CreateFixedObject(4, 0, false); + unsigned ResultReg = createResultReg(&AArch64::GPR64spRegClass); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(AArch64::ADDXri), ResultReg) + .addFrameIndex(FI) + .addImm(0) + .addImm(0); + + updateValueMap(II, ResultReg); + return true; + } case Intrinsic::memcpy: case Intrinsic::memmove: { const auto *MTI = cast<MemTransferInst>(II); @@ -3738,6 +3769,9 @@ bool AArch64FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { TII.get(TargetOpcode::COPY), ResultReg1).addReg(MulReg); } + if (!ResultReg1) + return false; + ResultReg2 = fastEmitInst_rri(AArch64::CSINCWr, &AArch64::GPR32RegClass, AArch64::WZR, /*IsKill=*/true, AArch64::WZR, /*IsKill=*/true, getInvertedCondCode(CC)); @@ -4483,7 +4517,8 @@ bool AArch64FastISel::optimizeIntExtLoad(const Instruction *I, MVT RetVT, MI->getOperand(1).getSubReg() == AArch64::sub_32) && "Expected copy instruction"); Reg = MI->getOperand(1).getReg(); - MI->eraseFromParent(); + MachineBasicBlock::iterator I(MI); + removeDeadCode(I, std::next(I)); } updateValueMap(I, Reg); return true; diff --git a/lib/Target/AArch64/AArch64FrameLowering.cpp b/lib/Target/AArch64/AArch64FrameLowering.cpp index 6dc5d19862a9..538a8d7e8fbc 100644 --- a/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -98,6 +98,7 @@ #include "AArch64Subtarget.h" #include "AArch64TargetMachine.h" #include "MCTargetDesc/AArch64AddressingModes.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/LivePhysRegs.h" @@ -114,11 +115,13 @@ #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Function.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCDwarf.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -201,6 +204,11 @@ bool AArch64FrameLowering::canUseRedZone(const MachineFunction &MF) const { bool AArch64FrameLowering::hasFP(const MachineFunction &MF) const { const MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + // Win64 EH requires a frame pointer if funclets are present, as the locals + // are accessed off the frame pointer in both the parent function and the + // funclets. + if (MF.hasEHFunclets()) + return true; // Retain behavior of always omitting the FP for leaf functions when possible. if (MFI.hasCalls() && MF.getTarget().Options.DisableFramePointerElim(MF)) return true; @@ -279,6 +287,31 @@ MachineBasicBlock::iterator AArch64FrameLowering::eliminateCallFramePseudoInstr( return MBB.erase(I); } +static bool ShouldSignReturnAddress(MachineFunction &MF) { + // The function should be signed in the following situations: + // - sign-return-address=all + // - sign-return-address=non-leaf and the functions spills the LR + + const Function &F = MF.getFunction(); + if (!F.hasFnAttribute("sign-return-address")) + return false; + + StringRef Scope = F.getFnAttribute("sign-return-address").getValueAsString(); + if (Scope.equals("none")) + return false; + + if (Scope.equals("all")) + return true; + + assert(Scope.equals("non-leaf") && "Expected all, none or non-leaf"); + + for (const auto &Info : MF.getFrameInfo().getCalleeSavedInfo()) + if (Info.getReg() == AArch64::LR) + return true; + + return false; +} + void AArch64FrameLowering::emitCalleeSavedFrameMoves( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const { MachineFunction &MF = *MBB.getParent(); @@ -330,7 +363,7 @@ static unsigned findScratchNonCalleeSaveRegister(MachineBasicBlock *MBB) { LiveRegs.addLiveIns(*MBB); // Mark callee saved registers as used so we will not choose them. - const MCPhysReg *CSRegs = TRI.getCalleeSavedRegs(MF); + const MCPhysReg *CSRegs = MF->getRegInfo().getCalleeSavedRegs(); for (unsigned i = 0; CSRegs[i]; ++i) LiveRegs.addReg(CSRegs[i]); @@ -408,54 +441,217 @@ bool AArch64FrameLowering::shouldCombineCSRLocalStackBump( return true; } +// Given a load or a store instruction, generate an appropriate unwinding SEH +// code on Windows. +static MachineBasicBlock::iterator InsertSEH(MachineBasicBlock::iterator MBBI, + const TargetInstrInfo &TII, + MachineInstr::MIFlag Flag) { + unsigned Opc = MBBI->getOpcode(); + MachineBasicBlock *MBB = MBBI->getParent(); + MachineFunction &MF = *MBB->getParent(); + DebugLoc DL = MBBI->getDebugLoc(); + unsigned ImmIdx = MBBI->getNumOperands() - 1; + int Imm = MBBI->getOperand(ImmIdx).getImm(); + MachineInstrBuilder MIB; + const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>(); + const AArch64RegisterInfo *RegInfo = Subtarget.getRegisterInfo(); + + switch (Opc) { + default: + llvm_unreachable("No SEH Opcode for this instruction"); + case AArch64::LDPDpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STPDpre: { + unsigned Reg0 = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + unsigned Reg1 = RegInfo->getSEHRegNum(MBBI->getOperand(2).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFRegP_X)) + .addImm(Reg0) + .addImm(Reg1) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::LDPXpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STPXpre: { + unsigned Reg0 = MBBI->getOperand(1).getReg(); + unsigned Reg1 = MBBI->getOperand(2).getReg(); + if (Reg0 == AArch64::FP && Reg1 == AArch64::LR) + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFPLR_X)) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveRegP_X)) + .addImm(RegInfo->getSEHRegNum(Reg0)) + .addImm(RegInfo->getSEHRegNum(Reg1)) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::LDRDpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STRDpre: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFReg_X)) + .addImm(Reg) + .addImm(Imm) + .setMIFlag(Flag); + break; + } + case AArch64::LDRXpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STRXpre: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg_X)) + .addImm(Reg) + .addImm(Imm) + .setMIFlag(Flag); + break; + } + case AArch64::STPDi: + case AArch64::LDPDi: { + unsigned Reg0 = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + unsigned Reg1 = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFRegP)) + .addImm(Reg0) + .addImm(Reg1) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STPXi: + case AArch64::LDPXi: { + unsigned Reg0 = MBBI->getOperand(0).getReg(); + unsigned Reg1 = MBBI->getOperand(1).getReg(); + if (Reg0 == AArch64::FP && Reg1 == AArch64::LR) + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFPLR)) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveRegP)) + .addImm(RegInfo->getSEHRegNum(Reg0)) + .addImm(RegInfo->getSEHRegNum(Reg1)) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STRXui: + case AArch64::LDRXui: { + int Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STRDui: + case AArch64::LDRDui: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFReg)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + } + auto I = MBB->insertAfter(MBBI, MIB); + return I; +} + +// Fix up the SEH opcode associated with the save/restore instruction. +static void fixupSEHOpcode(MachineBasicBlock::iterator MBBI, + unsigned LocalStackSize) { + MachineOperand *ImmOpnd = nullptr; + unsigned ImmIdx = MBBI->getNumOperands() - 1; + switch (MBBI->getOpcode()) { + default: + llvm_unreachable("Fix the offset in the SEH instruction"); + case AArch64::SEH_SaveFPLR: + case AArch64::SEH_SaveRegP: + case AArch64::SEH_SaveReg: + case AArch64::SEH_SaveFRegP: + case AArch64::SEH_SaveFReg: + ImmOpnd = &MBBI->getOperand(ImmIdx); + break; + } + if (ImmOpnd) + ImmOpnd->setImm(ImmOpnd->getImm() + LocalStackSize); +} + // Convert callee-save register save/restore instruction to do stack pointer // decrement/increment to allocate/deallocate the callee-save stack area by // converting store/load to use pre/post increment version. static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - const DebugLoc &DL, const TargetInstrInfo *TII, int CSStackSizeInc) { + const DebugLoc &DL, const TargetInstrInfo *TII, int CSStackSizeInc, + bool NeedsWinCFI, bool InProlog = true) { // Ignore instructions that do not operate on SP, i.e. shadow call stack - // instructions. + // instructions and associated CFI instruction. while (MBBI->getOpcode() == AArch64::STRXpost || - MBBI->getOpcode() == AArch64::LDRXpre) { - assert(MBBI->getOperand(0).getReg() != AArch64::SP); + MBBI->getOpcode() == AArch64::LDRXpre || + MBBI->getOpcode() == AArch64::CFI_INSTRUCTION) { + if (MBBI->getOpcode() != AArch64::CFI_INSTRUCTION) + assert(MBBI->getOperand(0).getReg() != AArch64::SP); ++MBBI; } - unsigned NewOpc; - bool NewIsUnscaled = false; + int Scale = 1; switch (MBBI->getOpcode()) { default: llvm_unreachable("Unexpected callee-save save/restore opcode!"); case AArch64::STPXi: NewOpc = AArch64::STPXpre; + Scale = 8; break; case AArch64::STPDi: NewOpc = AArch64::STPDpre; + Scale = 8; + break; + case AArch64::STPQi: + NewOpc = AArch64::STPQpre; + Scale = 16; break; case AArch64::STRXui: NewOpc = AArch64::STRXpre; - NewIsUnscaled = true; break; case AArch64::STRDui: NewOpc = AArch64::STRDpre; - NewIsUnscaled = true; + break; + case AArch64::STRQui: + NewOpc = AArch64::STRQpre; break; case AArch64::LDPXi: NewOpc = AArch64::LDPXpost; + Scale = 8; break; case AArch64::LDPDi: NewOpc = AArch64::LDPDpost; + Scale = 8; + break; + case AArch64::LDPQi: + NewOpc = AArch64::LDPQpost; + Scale = 16; break; case AArch64::LDRXui: NewOpc = AArch64::LDRXpost; - NewIsUnscaled = true; break; case AArch64::LDRDui: NewOpc = AArch64::LDRDpost; - NewIsUnscaled = true; + break; + case AArch64::LDRQui: + NewOpc = AArch64::LDRQpost; break; } + // Get rid of the SEH code associated with the old instruction. + if (NeedsWinCFI) { + auto SEH = std::next(MBBI); + if (AArch64InstrInfo::isSEHInstruction(*SEH)) + SEH->eraseFromParent(); + } MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(NewOpc)); MIB.addReg(AArch64::SP, RegState::Define); @@ -471,15 +667,16 @@ static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( "instruction!"); assert(MBBI->getOperand(OpndIdx - 1).getReg() == AArch64::SP && "Unexpected base register in callee-save save/restore instruction!"); - // Last operand is immediate offset that needs fixing. - assert(CSStackSizeInc % 8 == 0); - int64_t CSStackSizeIncImm = CSStackSizeInc; - if (!NewIsUnscaled) - CSStackSizeIncImm /= 8; - MIB.addImm(CSStackSizeIncImm); + assert(CSStackSizeInc % Scale == 0); + MIB.addImm(CSStackSizeInc / Scale); MIB.setMIFlags(MBBI->getFlags()); - MIB.setMemRefs(MBBI->memoperands_begin(), MBBI->memoperands_end()); + MIB.setMemRefs(MBBI->memoperands()); + + // Generate a new SEH code that corresponds to the new instruction. + if (NeedsWinCFI) + InsertSEH(*MIB, *TII, + InProlog ? MachineInstr::FrameSetup : MachineInstr::FrameDestroy); return std::prev(MBB.erase(MBBI)); } @@ -487,22 +684,43 @@ static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( // Fixup callee-save register save/restore instructions to take into account // combined SP bump by adding the local stack size to the stack offsets. static void fixupCalleeSaveRestoreStackOffset(MachineInstr &MI, - unsigned LocalStackSize) { + unsigned LocalStackSize, + bool NeedsWinCFI) { + if (AArch64InstrInfo::isSEHInstruction(MI)) + return; + unsigned Opc = MI.getOpcode(); // Ignore instructions that do not operate on SP, i.e. shadow call stack - // instructions. - if (Opc == AArch64::STRXpost || Opc == AArch64::LDRXpre) { - assert(MI.getOperand(0).getReg() != AArch64::SP); + // instructions and associated CFI instruction. + if (Opc == AArch64::STRXpost || Opc == AArch64::LDRXpre || + Opc == AArch64::CFI_INSTRUCTION) { + if (Opc != AArch64::CFI_INSTRUCTION) + assert(MI.getOperand(0).getReg() != AArch64::SP); return; } - (void)Opc; - assert((Opc == AArch64::STPXi || Opc == AArch64::STPDi || - Opc == AArch64::STRXui || Opc == AArch64::STRDui || - Opc == AArch64::LDPXi || Opc == AArch64::LDPDi || - Opc == AArch64::LDRXui || Opc == AArch64::LDRDui) && - "Unexpected callee-save save/restore opcode!"); + unsigned Scale; + switch (Opc) { + case AArch64::STPXi: + case AArch64::STRXui: + case AArch64::STPDi: + case AArch64::STRDui: + case AArch64::LDPXi: + case AArch64::LDRXui: + case AArch64::LDPDi: + case AArch64::LDRDui: + Scale = 8; + break; + case AArch64::STPQi: + case AArch64::STRQui: + case AArch64::LDPQi: + case AArch64::LDRQui: + Scale = 16; + break; + default: + llvm_unreachable("Unexpected callee-save save/restore opcode!"); + } unsigned OffsetIdx = MI.getNumExplicitOperands() - 1; assert(MI.getOperand(OffsetIdx - 1).getReg() == AArch64::SP && @@ -510,8 +728,16 @@ static void fixupCalleeSaveRestoreStackOffset(MachineInstr &MI, // Last operand is immediate offset that needs fixing. MachineOperand &OffsetOpnd = MI.getOperand(OffsetIdx); // All generated opcodes have scaled offsets. - assert(LocalStackSize % 8 == 0); - OffsetOpnd.setImm(OffsetOpnd.getImm() + LocalStackSize / 8); + assert(LocalStackSize % Scale == 0); + OffsetOpnd.setImm(OffsetOpnd.getImm() + LocalStackSize / Scale); + + if (NeedsWinCFI) { + auto MBBI = std::next(MachineBasicBlock::iterator(MI)); + assert(MBBI != MI.getParent()->end() && "Expecting a valid instruction"); + assert(AArch64InstrInfo::isSEHInstruction(*MBBI) && + "Expecting a SEH instruction"); + fixupSEHOpcode(MBBI, LocalStackSize); + } } static void adaptForLdStOpt(MachineBasicBlock &MBB, @@ -546,6 +772,23 @@ static void adaptForLdStOpt(MachineBasicBlock &MBB, // } +static bool ShouldSignWithAKey(MachineFunction &MF) { + const Function &F = MF.getFunction(); + if (!F.hasFnAttribute("sign-return-address-key")) + return true; + + const StringRef Key = + F.getFnAttribute("sign-return-address-key").getValueAsString(); + assert(Key.equals_lower("a_key") || Key.equals_lower("b_key")); + return Key.equals_lower("a_key"); +} + +static bool needsWinCFI(const MachineFunction &MF) { + const Function &F = MF.getFunction(); + return MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + F.needsUnwindTableEntry(); +} + void AArch64FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.begin(); @@ -556,8 +799,12 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, const TargetInstrInfo *TII = Subtarget.getInstrInfo(); MachineModuleInfo &MMI = MF.getMMI(); AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); - bool needsFrameMoves = MMI.hasDebugInfo() || F.needsUnwindTableEntry(); + bool needsFrameMoves = (MMI.hasDebugInfo() || F.needsUnwindTableEntry()) && + !MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); bool HasFP = hasFP(MF); + bool NeedsWinCFI = needsWinCFI(MF); + MF.setHasWinCFI(NeedsWinCFI); + bool IsFunclet = MBB.isEHFuncletEntry(); // At this point, we're going to decide whether or not the function uses a // redzone. In most cases, the function doesn't have a redzone so let's @@ -568,18 +815,41 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, // to determine the end of the prologue. DebugLoc DL; + if (ShouldSignReturnAddress(MF)) { + if (ShouldSignWithAKey(MF)) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIASP)) + .setMIFlag(MachineInstr::FrameSetup); + else { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY)) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIBSP)) + .setMIFlag(MachineInstr::FrameSetup); + } + + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } + // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. if (MF.getFunction().getCallingConv() == CallingConv::GHC) return; - int NumBytes = (int)MFI.getStackSize(); + // getStackSize() includes all the locals in its size calculation. We don't + // include these locals when computing the stack size of a funclet, as they + // are allocated in the parent's stack frame and accessed via the frame + // pointer from the funclet. We only save the callee saved registers in the + // funclet, which are really the callee saved registers of the parent + // function, including the funclet. + int NumBytes = IsFunclet ? (int)getWinEHFuncletFrameSize(MF) + : (int)MFI.getStackSize(); if (!AFI->hasStackFrame() && !windowsRequiresStackProbe(MF, NumBytes)) { assert(!HasFP && "unexpected function without stack frame but with FP"); - // All of the stack allocation is for locals. AFI->setLocalStackSize(NumBytes); - if (!NumBytes) return; // REDZONE: If the stack size is less than 128 bytes, we don't need @@ -589,36 +859,44 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, ++NumRedZoneFunctions; } else { emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); - - // Label used to tie together the PROLOG_LABEL and the MachineMoves. - MCSymbol *FrameLabel = MMI.getContext().createTempSymbol(); - // Encode the stack size of the leaf function. - unsigned CFIIndex = MF.addFrameInst( - MCCFIInstruction::createDefCfaOffset(FrameLabel, -NumBytes)); - BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) - .addCFIIndex(CFIIndex) - .setMIFlags(MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); + if (!NeedsWinCFI) { + // Label used to tie together the PROLOG_LABEL and the MachineMoves. + MCSymbol *FrameLabel = MMI.getContext().createTempSymbol(); + // Encode the stack size of the leaf function. + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createDefCfaOffset(FrameLabel, -NumBytes)); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } } + + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + return; } bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()); - unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0; + // Var args are accounted for in the containing function, so don't + // include them for funclets. + unsigned FixedObject = (IsWin64 && !IsFunclet) ? + alignTo(AFI->getVarArgsGPRSize(), 16) : 0; auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject; // All of the remaining stack allocations are for locals. AFI->setLocalStackSize(NumBytes - PrologueSaveSize); - bool CombineSPBump = shouldCombineCSRLocalStackBump(MF, NumBytes); if (CombineSPBump) { emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); NumBytes = 0; } else if (PrologueSaveSize != 0) { - MBBI = convertCalleeSaveRestoreToSPPrePostIncDec(MBB, MBBI, DL, TII, - -PrologueSaveSize); + MBBI = convertCalleeSaveRestoreToSPPrePostIncDec( + MBB, MBBI, DL, TII, -PrologueSaveSize, NeedsWinCFI); NumBytes -= PrologueSaveSize; } assert(NumBytes >= 0 && "Negative stack allocation size!?"); @@ -629,9 +907,21 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock::iterator End = MBB.end(); while (MBBI != End && MBBI->getFlag(MachineInstr::FrameSetup)) { if (CombineSPBump) - fixupCalleeSaveRestoreStackOffset(*MBBI, AFI->getLocalStackSize()); + fixupCalleeSaveRestoreStackOffset(*MBBI, AFI->getLocalStackSize(), + NeedsWinCFI); ++MBBI; } + + // The code below is not applicable to funclets. We have emitted all the SEH + // opcodes that we needed to emit. The FP and BP belong to the containing + // function. + if (IsFunclet) { + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + return; + } + if (HasFP) { // Only set up FP if we actually need to. Frame pointer is fp = // sp - fixedobject - 16. @@ -644,24 +934,58 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, // Note: All stores of callee-saved registers are marked as "FrameSetup". // This code marks the instruction(s) that set the FP also. emitFrameOffset(MBB, MBBI, DL, AArch64::FP, AArch64::SP, FPOffset, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); } if (windowsRequiresStackProbe(MF, NumBytes)) { uint32_t NumWords = NumBytes >> 4; + if (NeedsWinCFI) { + // alloc_l can hold at most 256MB, so assume that NumBytes doesn't + // exceed this amount. We need to move at most 2^24 - 1 into x15. + // This is at most two instructions, MOVZ follwed by MOVK. + // TODO: Fix to use multiple stack alloc unwind codes for stacks + // exceeding 256MB in size. + if (NumBytes >= (1 << 28)) + report_fatal_error("Stack size cannot exceed 256MB for stack " + "unwinding purposes"); - BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVi64imm), AArch64::X15) - .addImm(NumWords) - .setMIFlags(MachineInstr::FrameSetup); + uint32_t LowNumWords = NumWords & 0xFFFF; + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVZXi), AArch64::X15) + .addImm(LowNumWords) + .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); + if ((NumWords & 0xFFFF0000) != 0) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), AArch64::X15) + .addReg(AArch64::X15) + .addImm((NumWords & 0xFFFF0000) >> 16) // High half + .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 16)) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); + } + } else { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVi64imm), AArch64::X15) + .addImm(NumWords) + .setMIFlags(MachineInstr::FrameSetup); + } switch (MF.getTarget().getCodeModel()) { + case CodeModel::Tiny: case CodeModel::Small: case CodeModel::Medium: case CodeModel::Kernel: BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL)) .addExternalSymbol("__chkstk") .addReg(AArch64::X15, RegState::Implicit) + .addReg(AArch64::X16, RegState::Implicit | RegState::Define | RegState::Dead) + .addReg(AArch64::X17, RegState::Implicit | RegState::Define | RegState::Dead) + .addReg(AArch64::NZCV, RegState::Implicit | RegState::Define | RegState::Dead) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); break; case CodeModel::Large: BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVaddrEXT)) @@ -669,11 +993,20 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, .addExternalSymbol("__chkstk") .addExternalSymbol("__chkstk") .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); BuildMI(MBB, MBBI, DL, TII->get(AArch64::BLR)) .addReg(AArch64::X16, RegState::Kill) .addReg(AArch64::X15, RegState::Implicit | RegState::Define) + .addReg(AArch64::X16, RegState::Implicit | RegState::Define | RegState::Dead) + .addReg(AArch64::X17, RegState::Implicit | RegState::Define | RegState::Dead) + .addReg(AArch64::NZCV, RegState::Implicit | RegState::Define | RegState::Dead) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); break; } @@ -682,6 +1015,10 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, .addReg(AArch64::X15, RegState::Kill) .addImm(AArch64_AM::getArithExtendImm(AArch64_AM::UXTX, 4)) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(NumBytes) + .setMIFlag(MachineInstr::FrameSetup); NumBytes = 0; } @@ -701,7 +1038,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, // the correct value here, as NumBytes also includes padding bytes, // which shouldn't be counted here. emitFrameOffset(MBB, MBBI, DL, scratchSPReg, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); if (NeedsRealignment) { const unsigned Alignment = MFI.getMaxAlignment(); @@ -724,6 +1061,10 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, .addReg(scratchSPReg, RegState::Kill) .addImm(andMaskEncoded); AFI->setStackRealigned(true); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(NumBytes & andMaskEncoded) + .setMIFlag(MachineInstr::FrameSetup); } } @@ -737,8 +1078,17 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, if (RegInfo->hasBasePointer(MF)) { TII->copyPhysReg(MBB, MBBI, DL, RegInfo->getBaseRegister(), AArch64::SP, false); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); } + // The very last FrameSetup instruction indicates the end of prologue. Emit a + // SEH opcode indicating the prologue end. + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + if (needsFrameMoves) { const DataLayout &TD = MF.getDataLayout(); const int StackGrowth = -TD.getPointerSize(0); @@ -832,6 +1182,46 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, } } +static void InsertReturnAddressAuth(MachineFunction &MF, + MachineBasicBlock &MBB) { + if (!ShouldSignReturnAddress(MF)) + return; + const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>(); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + + MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); + DebugLoc DL; + if (MBBI != MBB.end()) + DL = MBBI->getDebugLoc(); + + // The AUTIASP instruction assembles to a hint instruction before v8.3a so + // this instruction can safely used for any v8a architecture. + // From v8.3a onwards there are optimised authenticate LR and return + // instructions, namely RETA{A,B}, that can be used instead. + if (Subtarget.hasV8_3aOps() && MBBI != MBB.end() && + MBBI->getOpcode() == AArch64::RET_ReallyLR) { + BuildMI(MBB, MBBI, DL, + TII->get(ShouldSignWithAKey(MF) ? AArch64::RETAA : AArch64::RETAB)) + .copyImplicitOps(*MBBI); + MBB.erase(MBBI); + } else { + BuildMI( + MBB, MBBI, DL, + TII->get(ShouldSignWithAKey(MF) ? AArch64::AUTIASP : AArch64::AUTIBSP)) + .setMIFlag(MachineInstr::FrameDestroy); + } +} + +static bool isFuncletReturnInstr(const MachineInstr &MI) { + switch (MI.getOpcode()) { + default: + return false; + case AArch64::CATCHRET: + case AArch64::CLEANUPRET: + return true; + } +} + void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); @@ -840,14 +1230,21 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL; bool IsTailCallReturn = false; + bool NeedsWinCFI = needsWinCFI(MF); + bool IsFunclet = false; + if (MBB.end() != MBBI) { DL = MBBI->getDebugLoc(); unsigned RetOpcode = MBBI->getOpcode(); IsTailCallReturn = RetOpcode == AArch64::TCRETURNdi || - RetOpcode == AArch64::TCRETURNri; + RetOpcode == AArch64::TCRETURNri || + RetOpcode == AArch64::TCRETURNriBTI; + IsFunclet = isFuncletReturnInstr(*MBBI); } - int NumBytes = MFI.getStackSize(); - const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); + + int NumBytes = IsFunclet ? (int)getWinEHFuncletFrameSize(MF) + : MFI.getStackSize(); + AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. @@ -899,25 +1296,38 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, // AArch64TargetLowering::LowerCall figures out ArgumentPopSize and keeps // it as the 2nd argument of AArch64ISD::TC_RETURN. + auto Cleanup = make_scope_exit([&] { InsertReturnAddressAuth(MF, MBB); }); + bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()); - unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0; + // Var args are accounted for in the containing function, so don't + // include them for funclets. + unsigned FixedObject = + (IsWin64 && !IsFunclet) ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0; uint64_t AfterCSRPopSize = ArgumentPopSize; auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject; + // We cannot rely on the local stack size set in emitPrologue if the function + // has funclets, as funclets have different local stack size requirements, and + // the current value set in emitPrologue may be that of the containing + // function. + if (MF.hasEHFunclets()) + AFI->setLocalStackSize(NumBytes - PrologueSaveSize); bool CombineSPBump = shouldCombineCSRLocalStackBump(MF, NumBytes); // Assume we can't combine the last pop with the sp restore. if (!CombineSPBump && PrologueSaveSize != 0) { MachineBasicBlock::iterator Pop = std::prev(MBB.getFirstTerminator()); + while (AArch64InstrInfo::isSEHInstruction(*Pop)) + Pop = std::prev(Pop); // Converting the last ldp to a post-index ldp is valid only if the last // ldp's offset is 0. const MachineOperand &OffsetOp = Pop->getOperand(Pop->getNumOperands() - 1); // If the offset is 0, convert it to a post-index ldp. - if (OffsetOp.getImm() == 0) { - convertCalleeSaveRestoreToSPPrePostIncDec(MBB, Pop, DL, TII, - PrologueSaveSize); - } else { + if (OffsetOp.getImm() == 0) + convertCalleeSaveRestoreToSPPrePostIncDec( + MBB, Pop, DL, TII, PrologueSaveSize, NeedsWinCFI, false); + else { // If not, make sure to emit an add after the last ldp. // We're doing this by transfering the size to be restored from the // adjustment *before* the CSR pops to the adjustment *after* the CSR @@ -937,14 +1347,23 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, ++LastPopI; break; } else if (CombineSPBump) - fixupCalleeSaveRestoreStackOffset(*LastPopI, AFI->getLocalStackSize()); + fixupCalleeSaveRestoreStackOffset(*LastPopI, AFI->getLocalStackSize(), + NeedsWinCFI); } + if (NeedsWinCFI) + BuildMI(MBB, LastPopI, DL, TII->get(AArch64::SEH_EpilogStart)) + .setMIFlag(MachineInstr::FrameDestroy); + // If there is a single SP update, insert it before the ret and we're done. if (CombineSPBump) { emitFrameOffset(MBB, MBB.getFirstTerminator(), DL, AArch64::SP, AArch64::SP, - NumBytes + AfterCSRPopSize, TII, - MachineInstr::FrameDestroy); + NumBytes + AfterCSRPopSize, TII, MachineInstr::FrameDestroy, + false, NeedsWinCFI); + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, + TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); return; } @@ -972,9 +1391,15 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, adaptForLdStOpt(MBB, MBB.getFirstTerminator(), LastPopI); emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::SP, - StackRestoreBytes, TII, MachineInstr::FrameDestroy); - if (Done) + StackRestoreBytes, TII, MachineInstr::FrameDestroy, false, + NeedsWinCFI); + if (Done) { + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, + TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); return; + } NumBytes = 0; } @@ -983,13 +1408,13 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, // FIXME: Rather than doing the math here, we should instead just use // non-post-indexed loads for the restores if we aren't actually going to // be able to save any instructions. - if (MFI.hasVarSizedObjects() || AFI->isStackRealigned()) + if (!IsFunclet && (MFI.hasVarSizedObjects() || AFI->isStackRealigned())) emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::FP, -AFI->getCalleeSavedStackSize() + 16, TII, - MachineInstr::FrameDestroy); + MachineInstr::FrameDestroy, false, NeedsWinCFI); else if (NumBytes) emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::SP, NumBytes, TII, - MachineInstr::FrameDestroy); + MachineInstr::FrameDestroy, false, NeedsWinCFI); // This must be placed after the callee-save restore code because that code // assumes the SP is at the same location as it was after the callee-save save @@ -1010,8 +1435,12 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, adaptForLdStOpt(MBB, FirstSPPopI, LastPopI); emitFrameOffset(MBB, FirstSPPopI, DL, AArch64::SP, AArch64::SP, - AfterCSRPopSize, TII, MachineInstr::FrameDestroy); + AfterCSRPopSize, TII, MachineInstr::FrameDestroy, false, + NeedsWinCFI); } + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); } /// getFrameIndexReference - Provide a base+offset reference to an FI slot for @@ -1084,6 +1513,14 @@ int AArch64FrameLowering::resolveFrameIndexReference(const MachineFunction &MF, // being in range for direct access. If the FPOffset is positive, // that'll always be best, as the SP will be even further away. UseFP = true; + } else if (MF.hasEHFunclets() && !RegInfo->hasBasePointer(MF)) { + // Funclets access the locals contained in the parent's stack frame + // via the frame pointer, so we have to use the FP in the parent + // function. + assert( + Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()) && + "Funclets should only be present on Win64"); + UseFP = true; } else { // We have the choice between FP and (SP or BP). if (FPOffsetFits && PreferFP) // If FP is the best fit, use it. @@ -1136,6 +1573,23 @@ static bool produceCompactUnwindFrame(MachineFunction &MF) { Attrs.hasAttrSomewhere(Attribute::SwiftError)); } +static bool invalidateWindowsRegisterPairing(unsigned Reg1, unsigned Reg2, + bool NeedsWinCFI) { + // If we are generating register pairs for a Windows function that requires + // EH support, then pair consecutive registers only. There are no unwind + // opcodes for saves/restores of non-consectuve register pairs. + // The unwind opcodes are save_regp, save_regp_x, save_fregp, save_frepg_x. + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + + // TODO: LR can be paired with any register. We don't support this yet in + // the MCLayer. We need to add support for the save_lrpair unwind code. + if (!NeedsWinCFI) + return false; + if (Reg2 == Reg1 + 1) + return false; + return true; +} + namespace { struct RegPairInfo { @@ -1143,7 +1597,7 @@ struct RegPairInfo { unsigned Reg2 = AArch64::NoRegister; int FrameIdx; int Offset; - bool IsGPR; + enum RegType { GPR, FPR64, FPR128 } Type; RegPairInfo() = default; @@ -1160,6 +1614,7 @@ static void computeCalleeSaveRegisterPairs( if (CSI.empty()) return; + bool NeedsWinCFI = needsWinCFI(MF); AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); MachineFrameInfo &MFI = MF.getFrameInfo(); CallingConv::ID CC = MF.getFunction().getCallingConv(); @@ -1172,28 +1627,50 @@ static void computeCalleeSaveRegisterPairs( (Count & 1) == 0) && "Odd number of callee-saved regs to spill!"); int Offset = AFI->getCalleeSavedStackSize(); - + // On Linux, we will have either one or zero non-paired register. On Windows + // with CFI, we can have multiple unpaired registers in order to utilize the + // available unwind codes. This flag assures that the alignment fixup is done + // only once, as intened. + bool FixupDone = false; for (unsigned i = 0; i < Count; ++i) { RegPairInfo RPI; RPI.Reg1 = CSI[i].getReg(); - assert(AArch64::GPR64RegClass.contains(RPI.Reg1) || - AArch64::FPR64RegClass.contains(RPI.Reg1)); - RPI.IsGPR = AArch64::GPR64RegClass.contains(RPI.Reg1); + if (AArch64::GPR64RegClass.contains(RPI.Reg1)) + RPI.Type = RegPairInfo::GPR; + else if (AArch64::FPR64RegClass.contains(RPI.Reg1)) + RPI.Type = RegPairInfo::FPR64; + else if (AArch64::FPR128RegClass.contains(RPI.Reg1)) + RPI.Type = RegPairInfo::FPR128; + else + llvm_unreachable("Unsupported register class."); // Add the next reg to the pair if it is in the same register class. if (i + 1 < Count) { unsigned NextReg = CSI[i + 1].getReg(); - if ((RPI.IsGPR && AArch64::GPR64RegClass.contains(NextReg)) || - (!RPI.IsGPR && AArch64::FPR64RegClass.contains(NextReg))) - RPI.Reg2 = NextReg; + switch (RPI.Type) { + case RegPairInfo::GPR: + if (AArch64::GPR64RegClass.contains(NextReg) && + !invalidateWindowsRegisterPairing(RPI.Reg1, NextReg, NeedsWinCFI)) + RPI.Reg2 = NextReg; + break; + case RegPairInfo::FPR64: + if (AArch64::FPR64RegClass.contains(NextReg) && + !invalidateWindowsRegisterPairing(RPI.Reg1, NextReg, NeedsWinCFI)) + RPI.Reg2 = NextReg; + break; + case RegPairInfo::FPR128: + if (AArch64::FPR128RegClass.contains(NextReg)) + RPI.Reg2 = NextReg; + break; + } } // If either of the registers to be saved is the lr register, it means that // we also need to save lr in the shadow call stack. if ((RPI.Reg1 == AArch64::LR || RPI.Reg2 == AArch64::LR) && MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) { - if (!MF.getSubtarget<AArch64Subtarget>().isX18Reserved()) + if (!MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(18)) report_fatal_error("Must reserve x18 to use shadow call stack"); NeedShadowCallStackProlog = true; } @@ -1219,17 +1696,22 @@ static void computeCalleeSaveRegisterPairs( RPI.FrameIdx = CSI[i].getFrameIdx(); - if (Count * 8 != AFI->getCalleeSavedStackSize() && !RPI.isPaired()) { - // Round up size of non-pair to pair size if we need to pad the - // callee-save area to ensure 16-byte alignment. - Offset -= 16; + int Scale = RPI.Type == RegPairInfo::FPR128 ? 16 : 8; + Offset -= RPI.isPaired() ? 2 * Scale : Scale; + + // Round up size of non-pair to pair size if we need to pad the + // callee-save area to ensure 16-byte alignment. + if (AFI->hasCalleeSaveStackFreeSpace() && !FixupDone && + RPI.Type != RegPairInfo::FPR128 && !RPI.isPaired()) { + FixupDone = true; + Offset -= 8; + assert(Offset % 16 == 0); assert(MFI.getObjectAlignment(RPI.FrameIdx) <= 16); MFI.setObjectAlignment(RPI.FrameIdx, 16); - AFI->setCalleeSaveStackHasFreeSpace(true); - } else - Offset -= RPI.isPaired() ? 16 : 8; - assert(Offset % 8 == 0); - RPI.Offset = Offset / 8; + } + + assert(Offset % Scale == 0); + RPI.Offset = Offset / Scale; assert((RPI.Offset >= -64 && RPI.Offset <= 63) && "Offset out of bounds for LDP/STP immediate"); @@ -1245,6 +1727,7 @@ bool AArch64FrameLowering::spillCalleeSavedRegisters( const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + bool NeedsWinCFI = needsWinCFI(MF); DebugLoc DL; SmallVector<RegPairInfo, 8> RegPairs; @@ -1262,6 +1745,27 @@ bool AArch64FrameLowering::spillCalleeSavedRegisters( .addImm(8) .setMIFlag(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MI, DL, TII.get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); + + if (!MF.getFunction().hasFnAttribute(Attribute::NoUnwind)) { + // Emit a CFI instruction that causes 8 to be subtracted from the value of + // x18 when unwinding past this frame. + static const char CFIInst[] = { + dwarf::DW_CFA_val_expression, + 18, // register + 2, // length + static_cast<char>(unsigned(dwarf::DW_OP_breg18)), + static_cast<char>(-8) & 0x7f, // addend (sleb128) + }; + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createEscape(nullptr, CFIInst)); + BuildMI(MBB, MI, DL, TII.get(AArch64::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlag(MachineInstr::FrameSetup); + } + // This instruction also makes x18 live-in to the entry block. MBB.addLiveIn(AArch64::X18); } @@ -1283,16 +1787,41 @@ bool AArch64FrameLowering::spillCalleeSavedRegisters( // Rationale: This sequence saves uop updates compared to a sequence of // pre-increment spills like stp xi,xj,[sp,#-16]! // Note: Similar rationale and sequence for restores in epilog. - if (RPI.IsGPR) - StrOpc = RPI.isPaired() ? AArch64::STPXi : AArch64::STRXui; - else - StrOpc = RPI.isPaired() ? AArch64::STPDi : AArch64::STRDui; + unsigned Size, Align; + switch (RPI.Type) { + case RegPairInfo::GPR: + StrOpc = RPI.isPaired() ? AArch64::STPXi : AArch64::STRXui; + Size = 8; + Align = 8; + break; + case RegPairInfo::FPR64: + StrOpc = RPI.isPaired() ? AArch64::STPDi : AArch64::STRDui; + Size = 8; + Align = 8; + break; + case RegPairInfo::FPR128: + StrOpc = RPI.isPaired() ? AArch64::STPQi : AArch64::STRQui; + Size = 16; + Align = 16; + break; + } LLVM_DEBUG(dbgs() << "CSR spill: (" << printReg(Reg1, TRI); if (RPI.isPaired()) dbgs() << ", " << printReg(Reg2, TRI); dbgs() << ") -> fi#(" << RPI.FrameIdx; if (RPI.isPaired()) dbgs() << ", " << RPI.FrameIdx + 1; dbgs() << ")\n"); + assert((!NeedsWinCFI || !(Reg1 == AArch64::LR && Reg2 == AArch64::FP)) && + "Windows unwdinding requires a consecutive (FP,LR) pair"); + // Windows unwind codes require consecutive registers if registers are + // paired. Make the switch here, so that the code below will save (x,x+1) + // and not (x+1,x). + unsigned FrameIdxReg1 = RPI.FrameIdx; + unsigned FrameIdxReg2 = RPI.FrameIdx + 1; + if (NeedsWinCFI && RPI.isPaired()) { + std::swap(Reg1, Reg2); + std::swap(FrameIdxReg1, FrameIdxReg2); + } MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(StrOpc)); if (!MRI.isReserved(Reg1)) MBB.addLiveIn(Reg1); @@ -1301,16 +1830,20 @@ bool AArch64FrameLowering::spillCalleeSavedRegisters( MBB.addLiveIn(Reg2); MIB.addReg(Reg2, getPrologueDeath(MF, Reg2)); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx + 1), - MachineMemOperand::MOStore, 8, 8)); + MachinePointerInfo::getFixedStack(MF, FrameIdxReg2), + MachineMemOperand::MOStore, Size, Align)); } MIB.addReg(Reg1, getPrologueDeath(MF, Reg1)) .addReg(AArch64::SP) - .addImm(RPI.Offset) // [sp, #offset*8], where factor*8 is implicit + .addImm(RPI.Offset) // [sp, #offset*scale], + // where factor*scale is implicit .setMIFlag(MachineInstr::FrameSetup); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx), - MachineMemOperand::MOStore, 8, 8)); + MachinePointerInfo::getFixedStack(MF,FrameIdxReg1), + MachineMemOperand::MOStore, Size, Align)); + if (NeedsWinCFI) + InsertSEH(MIB, TII, MachineInstr::FrameSetup); + } return true; } @@ -1323,6 +1856,7 @@ bool AArch64FrameLowering::restoreCalleeSavedRegisters( const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); DebugLoc DL; SmallVector<RegPairInfo, 8> RegPairs; + bool NeedsWinCFI = needsWinCFI(MF); if (MI != MBB.end()) DL = MI->getDebugLoc(); @@ -1344,32 +1878,57 @@ bool AArch64FrameLowering::restoreCalleeSavedRegisters( // ldp x22, x21, [sp, #0] // addImm(+0) // Note: see comment in spillCalleeSavedRegisters() unsigned LdrOpc; - if (RPI.IsGPR) - LdrOpc = RPI.isPaired() ? AArch64::LDPXi : AArch64::LDRXui; - else - LdrOpc = RPI.isPaired() ? AArch64::LDPDi : AArch64::LDRDui; + unsigned Size, Align; + switch (RPI.Type) { + case RegPairInfo::GPR: + LdrOpc = RPI.isPaired() ? AArch64::LDPXi : AArch64::LDRXui; + Size = 8; + Align = 8; + break; + case RegPairInfo::FPR64: + LdrOpc = RPI.isPaired() ? AArch64::LDPDi : AArch64::LDRDui; + Size = 8; + Align = 8; + break; + case RegPairInfo::FPR128: + LdrOpc = RPI.isPaired() ? AArch64::LDPQi : AArch64::LDRQui; + Size = 16; + Align = 16; + break; + } LLVM_DEBUG(dbgs() << "CSR restore: (" << printReg(Reg1, TRI); if (RPI.isPaired()) dbgs() << ", " << printReg(Reg2, TRI); dbgs() << ") -> fi#(" << RPI.FrameIdx; if (RPI.isPaired()) dbgs() << ", " << RPI.FrameIdx + 1; dbgs() << ")\n"); + // Windows unwind codes require consecutive registers if registers are + // paired. Make the switch here, so that the code below will save (x,x+1) + // and not (x+1,x). + unsigned FrameIdxReg1 = RPI.FrameIdx; + unsigned FrameIdxReg2 = RPI.FrameIdx + 1; + if (NeedsWinCFI && RPI.isPaired()) { + std::swap(Reg1, Reg2); + std::swap(FrameIdxReg1, FrameIdxReg2); + } MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(LdrOpc)); if (RPI.isPaired()) { MIB.addReg(Reg2, getDefRegState(true)); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx + 1), - MachineMemOperand::MOLoad, 8, 8)); + MachinePointerInfo::getFixedStack(MF, FrameIdxReg2), + MachineMemOperand::MOLoad, Size, Align)); } MIB.addReg(Reg1, getDefRegState(true)) .addReg(AArch64::SP) - .addImm(RPI.Offset) // [sp, #offset*8] where the factor*8 is implicit + .addImm(RPI.Offset) // [sp, #offset*scale] + // where factor*scale is implicit .setMIFlag(MachineInstr::FrameDestroy); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx), - MachineMemOperand::MOLoad, 8, 8)); + MachinePointerInfo::getFixedStack(MF, FrameIdxReg1), + MachineMemOperand::MOLoad, Size, Align)); + if (NeedsWinCFI) + InsertSEH(MIB, TII, MachineInstr::FrameDestroy); }; - if (ReverseCSRRestoreSeq) for (const RegPairInfo &RPI : reverse(RegPairs)) EmitMI(RPI); @@ -1406,30 +1965,12 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, unsigned UnspilledCSGPRPaired = AArch64::NoRegister; MachineFrameInfo &MFI = MF.getFrameInfo(); - const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegs(&MF); + const MCPhysReg *CSRegs = MF.getRegInfo().getCalleeSavedRegs(); unsigned BasePointerReg = RegInfo->hasBasePointer(MF) ? RegInfo->getBaseRegister() : (unsigned)AArch64::NoRegister; - unsigned SpillEstimate = SavedRegs.count(); - for (unsigned i = 0; CSRegs[i]; ++i) { - unsigned Reg = CSRegs[i]; - unsigned PairedReg = CSRegs[i ^ 1]; - if (Reg == BasePointerReg) - SpillEstimate++; - if (produceCompactUnwindFrame(MF) && !SavedRegs.test(PairedReg)) - SpillEstimate++; - } - SpillEstimate += 2; // Conservatively include FP+LR in the estimate - unsigned StackEstimate = MFI.estimateStackSize(MF) + 8 * SpillEstimate; - - // The frame record needs to be created by saving the appropriate registers - if (hasFP(MF) || windowsRequiresStackProbe(MF, StackEstimate)) { - SavedRegs.set(AArch64::FP); - SavedRegs.set(AArch64::LR); - } - unsigned ExtraCSSpill = 0; // Figure out which callee-saved registers to save/restore. for (unsigned i = 0; CSRegs[i]; ++i) { @@ -1453,7 +1994,8 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, // MachO's compact unwind format relies on all registers being stored in // pairs. // FIXME: the usual format is actually better if unwinding isn't needed. - if (produceCompactUnwindFrame(MF) && !SavedRegs.test(PairedReg)) { + if (produceCompactUnwindFrame(MF) && PairedReg != AArch64::NoRegister && + !SavedRegs.test(PairedReg)) { SavedRegs.set(PairedReg); if (AArch64::GPR64RegClass.contains(PairedReg) && !RegInfo->isReservedReg(MF, PairedReg)) @@ -1461,6 +2003,24 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, } } + // Calculates the callee saved stack size. + unsigned CSStackSize = 0; + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + for (unsigned Reg : SavedRegs.set_bits()) + CSStackSize += TRI->getRegSizeInBits(Reg, MRI) / 8; + + // Save number of saved regs, so we can easily update CSStackSize later. + unsigned NumSavedRegs = SavedRegs.count(); + + // The frame record needs to be created by saving the appropriate registers + unsigned EstimatedStackSize = MFI.estimateStackSize(MF); + if (hasFP(MF) || + windowsRequiresStackProbe(MF, EstimatedStackSize + CSStackSize + 16)) { + SavedRegs.set(AArch64::FP); + SavedRegs.set(AArch64::LR); + } + LLVM_DEBUG(dbgs() << "*** determineCalleeSaves\nUsed CSRs:"; for (unsigned Reg : SavedRegs.set_bits()) dbgs() @@ -1468,15 +2028,12 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, dbgs() << "\n";); // If any callee-saved registers are used, the frame cannot be eliminated. - unsigned NumRegsSpilled = SavedRegs.count(); - bool CanEliminateFrame = NumRegsSpilled == 0; + bool CanEliminateFrame = SavedRegs.count() == 0; // The CSR spill slots have not been allocated yet, so estimateStackSize // won't include them. - unsigned CFSize = MFI.estimateStackSize(MF) + 8 * NumRegsSpilled; - LLVM_DEBUG(dbgs() << "Estimated stack frame size: " << CFSize << " bytes.\n"); unsigned EstimatedStackSizeLimit = estimateRSStackSizeLimit(MF); - bool BigStack = (CFSize > EstimatedStackSizeLimit); + bool BigStack = (EstimatedStackSize + CSStackSize) > EstimatedStackSizeLimit; if (BigStack || !CanEliminateFrame || RegInfo->cannotEliminateFrame(MF)) AFI->setHasStackFrame(true); @@ -1497,7 +2054,6 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, if (produceCompactUnwindFrame(MF)) SavedRegs.set(UnspilledCSGPRPaired); ExtraCSSpill = UnspilledCSGPRPaired; - NumRegsSpilled = SavedRegs.count(); } // If we didn't find an extra callee-saved register to spill, create @@ -1514,9 +2070,17 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, } } + // Adding the size of additional 64bit GPR saves. + CSStackSize += 8 * (SavedRegs.count() - NumSavedRegs); + unsigned AlignedCSStackSize = alignTo(CSStackSize, 16); + LLVM_DEBUG(dbgs() << "Estimated stack frame size: " + << EstimatedStackSize + AlignedCSStackSize + << " bytes.\n"); + // Round up to register pair alignment to avoid additional SP adjustment // instructions. - AFI->setCalleeSavedStackSize(alignTo(8 * NumRegsSpilled, 16)); + AFI->setCalleeSavedStackSize(AlignedCSStackSize); + AFI->setCalleeSaveStackHasFreeSpace(AlignedCSStackSize != CSStackSize); } bool AArch64FrameLowering::enableStackSlotScavenging( @@ -1524,3 +2088,69 @@ bool AArch64FrameLowering::enableStackSlotScavenging( const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); return AFI->hasCalleeSaveStackFreeSpace(); } + +void AArch64FrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + // If this function isn't doing Win64-style C++ EH, we don't need to do + // anything. + if (!MF.hasEHFunclets()) + return; + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + WinEHFuncInfo &EHInfo = *MF.getWinEHFuncInfo(); + + MachineBasicBlock &MBB = MF.front(); + auto MBBI = MBB.begin(); + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) + ++MBBI; + + if (MBBI->isTerminator()) + return; + + // Create an UnwindHelp object. + int UnwindHelpFI = + MFI.CreateStackObject(/*size*/8, /*alignment*/16, false); + EHInfo.UnwindHelpFrameIdx = UnwindHelpFI; + // We need to store -2 into the UnwindHelp object at the start of the + // function. + DebugLoc DL; + RS->enterBasicBlock(MBB); + unsigned DstReg = RS->scavengeRegister(&AArch64::GPR64RegClass, MBBI, 0); + BuildMI(MBB, MBBI, DL, TII.get(AArch64::MOVi64imm), DstReg).addImm(-2); + BuildMI(MBB, MBBI, DL, TII.get(AArch64::STURXi)) + .addReg(DstReg, getKillRegState(true)) + .addFrameIndex(UnwindHelpFI) + .addImm(0); +} + +/// For Win64 AArch64 EH, the offset to the Unwind object is from the SP before +/// the update. This is easily retrieved as it is exactly the offset that is set +/// in processFunctionBeforeFrameFinalized. +int AArch64FrameLowering::getFrameIndexReferencePreferSP( + const MachineFunction &MF, int FI, unsigned &FrameReg, + bool IgnoreSPUpdates) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is " + << MFI.getObjectOffset(FI) << "\n"); + FrameReg = AArch64::SP; + return MFI.getObjectOffset(FI); +} + +/// The parent frame offset (aka dispFrame) is only used on X86_64 to retrieve +/// the parent's frame pointer +unsigned AArch64FrameLowering::getWinEHParentFrameOffset( + const MachineFunction &MF) const { + return 0; +} + +/// Funclets only need to account for space for the callee saved registers, +/// as the locals are accounted for in the parent's stack frame. +unsigned AArch64FrameLowering::getWinEHFuncletFrameSize( + const MachineFunction &MF) const { + // This is the size of the pushed CSRs. + unsigned CSSize = + MF.getInfo<AArch64FunctionInfo>()->getCalleeSavedStackSize(); + // This is the amount of stack a funclet needs to allocate. + return alignTo(CSSize + MF.getFrameInfo().getMaxCallFrameSize(), + getStackAlignment()); +} diff --git a/lib/Target/AArch64/AArch64FrameLowering.h b/lib/Target/AArch64/AArch64FrameLowering.h index 104e52b5f1f3..0d0385acf46e 100644 --- a/lib/Target/AArch64/AArch64FrameLowering.h +++ b/lib/Target/AArch64/AArch64FrameLowering.h @@ -69,6 +69,17 @@ public: bool enableStackSlotScavenging(const MachineFunction &MF) const override; + void processFunctionBeforeFrameFinalized(MachineFunction &MF, + RegScavenger *RS) const override; + + unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const override; + + unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; + + int getFrameIndexReferencePreferSP(const MachineFunction &MF, int FI, + unsigned &FrameReg, + bool IgnoreSPUpdates) const override; + private: bool shouldCombineCSRLocalStackBump(MachineFunction &MF, unsigned StackBumpBytes) const; diff --git a/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index c1a9ee333b62..fc9855f6a0da 100644 --- a/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -1208,9 +1208,8 @@ void AArch64DAGToDAGISel::SelectLoad(SDNode *N, unsigned NumVecs, unsigned Opc, ReplaceUses(SDValue(N, NumVecs), SDValue(Ld, 1)); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(Ld)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemIntrinsicSDNode>(N)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(Ld), {MemOp}); CurDAG->RemoveDeadNode(N); } @@ -1261,9 +1260,8 @@ void AArch64DAGToDAGISel::SelectStore(SDNode *N, unsigned NumVecs, SDNode *St = CurDAG->getMachineNode(Opc, dl, N->getValueType(0), Ops); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(St)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemIntrinsicSDNode>(N)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(St), {MemOp}); ReplaceNode(N, St); } @@ -1441,9 +1439,8 @@ void AArch64DAGToDAGISel::SelectStoreLane(SDNode *N, unsigned NumVecs, SDNode *St = CurDAG->getMachineNode(Opc, dl, MVT::Other, Ops); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(St)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemIntrinsicSDNode>(N)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(St), {MemOp}); ReplaceNode(N, St); } @@ -1476,9 +1473,8 @@ void AArch64DAGToDAGISel::SelectPostStoreLane(SDNode *N, unsigned NumVecs, SDNode *St = CurDAG->getMachineNode(Opc, dl, ResTys, Ops); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(St)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemIntrinsicSDNode>(N)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(St), {MemOp}); ReplaceNode(N, St); } @@ -2091,8 +2087,7 @@ static bool isBitfieldPositioningOp(SelectionDAG *CurDAG, SDValue Op, (void)BitWidth; assert(BitWidth == 32 || BitWidth == 64); - KnownBits Known; - CurDAG->computeKnownBits(Op, Known); + KnownBits Known = CurDAG->computeKnownBits(Op); // Non-zero in the sense that they're not provably zero, which is the key // point if we want to use this value @@ -2171,8 +2166,7 @@ static bool tryBitfieldInsertOpFromOrAndImm(SDNode *N, SelectionDAG *CurDAG) { // Compute the Known Zero for the AND as this allows us to catch more general // cases than just looking for AND with imm. - KnownBits Known; - CurDAG->computeKnownBits(And, Known); + KnownBits Known = CurDAG->computeKnownBits(And); // Non-zero in the sense that they're not provably zero, which is the key // point if we want to use this value. @@ -2313,8 +2307,7 @@ static bool tryBitfieldInsertOpFromOr(SDNode *N, const APInt &UsefulBits, // This allows to catch more general case than just looking for // AND with imm. Indeed, simplify-demanded-bits may have removed // the AND instruction because it proves it was useless. - KnownBits Known; - CurDAG->computeKnownBits(OrOpd1Val, Known); + KnownBits Known = CurDAG->computeKnownBits(OrOpd1Val); // Check if there is enough room for the second operand to appear // in the first one @@ -2690,7 +2683,7 @@ bool AArch64DAGToDAGISel::tryWriteRegister(SDNode *N) { unsigned Reg = PMapper->Encoding; uint64_t Immed = cast<ConstantSDNode>(N->getOperand(2))->getZExtValue(); unsigned State; - if (Reg == AArch64PState::PAN || Reg == AArch64PState::UAO) { + if (Reg == AArch64PState::PAN || Reg == AArch64PState::UAO || Reg == AArch64PState::SSBS) { assert(Immed < 2 && "Bad imm"); State = AArch64::MSRpstateImm1; } else { @@ -2751,9 +2744,8 @@ bool AArch64DAGToDAGISel::SelectCMP_SWAP(SDNode *N) { Opcode, SDLoc(N), CurDAG->getVTList(RegTy, MVT::i32, MVT::Other), Ops); - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(CmpSwap)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemSDNode>(N)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(CmpSwap), {MemOp}); ReplaceUses(SDValue(N, 0), SDValue(CmpSwap, 0)); ReplaceUses(SDValue(N, 1), SDValue(CmpSwap, 2)); @@ -2923,9 +2915,9 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { MVT::Other, MemAddr, Chain); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(Node)->getMemOperand(); - cast<MachineSDNode>(Ld)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = + cast<MemIntrinsicSDNode>(Node)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(Ld), {MemOp}); ReplaceNode(Node, Ld); return; } @@ -2944,9 +2936,9 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { SDNode *St = CurDAG->getMachineNode(Op, DL, MVT::i32, MVT::Other, Ops); // Transfer memoperands. - MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); - MemOp[0] = cast<MemIntrinsicSDNode>(Node)->getMemOperand(); - cast<MachineSDNode>(St)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = + cast<MemIntrinsicSDNode>(Node)->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(St), {MemOp}); ReplaceNode(Node, St); return; diff --git a/lib/Target/AArch64/AArch64ISelLowering.cpp b/lib/Target/AArch64/AArch64ISelLowering.cpp index de762a7bb1d4..e01ca14d7f63 100644 --- a/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -187,7 +187,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::SELECT_CC, MVT::f16, Custom); setOperationAction(ISD::SELECT_CC, MVT::f32, Custom); setOperationAction(ISD::SELECT_CC, MVT::f64, Custom); - setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::BR_JT, MVT::Other, Custom); setOperationAction(ISD::JumpTable, MVT::i64, Custom); setOperationAction(ISD::SHL_PARTS, MVT::i64, Custom); @@ -333,36 +333,38 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::FCOPYSIGN, MVT::f16, Promote); setOperationAction(ISD::FREM, MVT::f16, Promote); - setOperationAction(ISD::FREM, MVT::v4f16, Promote); - setOperationAction(ISD::FREM, MVT::v8f16, Promote); + setOperationAction(ISD::FREM, MVT::v4f16, Expand); + setOperationAction(ISD::FREM, MVT::v8f16, Expand); setOperationAction(ISD::FPOW, MVT::f16, Promote); - setOperationAction(ISD::FPOW, MVT::v4f16, Promote); - setOperationAction(ISD::FPOW, MVT::v8f16, Promote); + setOperationAction(ISD::FPOW, MVT::v4f16, Expand); + setOperationAction(ISD::FPOW, MVT::v8f16, Expand); setOperationAction(ISD::FPOWI, MVT::f16, Promote); + setOperationAction(ISD::FPOWI, MVT::v4f16, Expand); + setOperationAction(ISD::FPOWI, MVT::v8f16, Expand); setOperationAction(ISD::FCOS, MVT::f16, Promote); - setOperationAction(ISD::FCOS, MVT::v4f16, Promote); - setOperationAction(ISD::FCOS, MVT::v8f16, Promote); + setOperationAction(ISD::FCOS, MVT::v4f16, Expand); + setOperationAction(ISD::FCOS, MVT::v8f16, Expand); setOperationAction(ISD::FSIN, MVT::f16, Promote); - setOperationAction(ISD::FSIN, MVT::v4f16, Promote); - setOperationAction(ISD::FSIN, MVT::v8f16, Promote); + setOperationAction(ISD::FSIN, MVT::v4f16, Expand); + setOperationAction(ISD::FSIN, MVT::v8f16, Expand); setOperationAction(ISD::FSINCOS, MVT::f16, Promote); - setOperationAction(ISD::FSINCOS, MVT::v4f16, Promote); - setOperationAction(ISD::FSINCOS, MVT::v8f16, Promote); + setOperationAction(ISD::FSINCOS, MVT::v4f16, Expand); + setOperationAction(ISD::FSINCOS, MVT::v8f16, Expand); setOperationAction(ISD::FEXP, MVT::f16, Promote); - setOperationAction(ISD::FEXP, MVT::v4f16, Promote); - setOperationAction(ISD::FEXP, MVT::v8f16, Promote); + setOperationAction(ISD::FEXP, MVT::v4f16, Expand); + setOperationAction(ISD::FEXP, MVT::v8f16, Expand); setOperationAction(ISD::FEXP2, MVT::f16, Promote); - setOperationAction(ISD::FEXP2, MVT::v4f16, Promote); - setOperationAction(ISD::FEXP2, MVT::v8f16, Promote); + setOperationAction(ISD::FEXP2, MVT::v4f16, Expand); + setOperationAction(ISD::FEXP2, MVT::v8f16, Expand); setOperationAction(ISD::FLOG, MVT::f16, Promote); - setOperationAction(ISD::FLOG, MVT::v4f16, Promote); - setOperationAction(ISD::FLOG, MVT::v8f16, Promote); + setOperationAction(ISD::FLOG, MVT::v4f16, Expand); + setOperationAction(ISD::FLOG, MVT::v8f16, Expand); setOperationAction(ISD::FLOG2, MVT::f16, Promote); - setOperationAction(ISD::FLOG2, MVT::v4f16, Promote); - setOperationAction(ISD::FLOG2, MVT::v8f16, Promote); + setOperationAction(ISD::FLOG2, MVT::v4f16, Expand); + setOperationAction(ISD::FLOG2, MVT::v8f16, Expand); setOperationAction(ISD::FLOG10, MVT::f16, Promote); - setOperationAction(ISD::FLOG10, MVT::v4f16, Promote); - setOperationAction(ISD::FLOG10, MVT::v8f16, Promote); + setOperationAction(ISD::FLOG10, MVT::v4f16, Expand); + setOperationAction(ISD::FLOG10, MVT::v8f16, Expand); if (!Subtarget->hasFullFP16()) { setOperationAction(ISD::SELECT, MVT::f16, Promote); @@ -385,8 +387,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::FTRUNC, MVT::f16, Promote); setOperationAction(ISD::FMINNUM, MVT::f16, Promote); setOperationAction(ISD::FMAXNUM, MVT::f16, Promote); - setOperationAction(ISD::FMINNAN, MVT::f16, Promote); - setOperationAction(ISD::FMAXNAN, MVT::f16, Promote); + setOperationAction(ISD::FMINIMUM, MVT::f16, Promote); + setOperationAction(ISD::FMAXIMUM, MVT::f16, Promote); // promote v4f16 to v4f32 when that is known to be safe. setOperationAction(ISD::FADD, MVT::v4f16, Promote); @@ -450,8 +452,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::FROUND, Ty, Legal); setOperationAction(ISD::FMINNUM, Ty, Legal); setOperationAction(ISD::FMAXNUM, Ty, Legal); - setOperationAction(ISD::FMINNAN, Ty, Legal); - setOperationAction(ISD::FMAXNAN, Ty, Legal); + setOperationAction(ISD::FMINIMUM, Ty, Legal); + setOperationAction(ISD::FMAXIMUM, Ty, Legal); } if (Subtarget->hasFullFP16()) { @@ -463,8 +465,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::FROUND, MVT::f16, Legal); setOperationAction(ISD::FMINNUM, MVT::f16, Legal); setOperationAction(ISD::FMAXNUM, MVT::f16, Legal); - setOperationAction(ISD::FMINNAN, MVT::f16, Legal); - setOperationAction(ISD::FMAXNAN, MVT::f16, Legal); + setOperationAction(ISD::FMINIMUM, MVT::f16, Legal); + setOperationAction(ISD::FMAXIMUM, MVT::f16, Legal); } setOperationAction(ISD::PREFETCH, MVT::Other, Custom); @@ -714,8 +716,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand); if (VT == MVT::v16i8 || VT == MVT::v8i16 || VT == MVT::v4i32) { - setOperationAction(ISD::MULHS, VT, Custom); - setOperationAction(ISD::MULHU, VT, Custom); + setOperationAction(ISD::MULHS, VT, Legal); + setOperationAction(ISD::MULHU, VT, Legal); } else { setOperationAction(ISD::MULHS, VT, Expand); setOperationAction(ISD::MULHU, VT, Expand); @@ -792,9 +794,9 @@ void AArch64TargetLowering::addTypeForNEON(MVT VT, MVT PromotedBitwiseVT) { for (MVT InnerVT : MVT::all_valuetypes()) setLoadExtAction(ISD::EXTLOAD, InnerVT, VT, Expand); - // CNT supports only B element sizes. + // CNT supports only B element sizes, then use UADDLP to widen. if (VT != MVT::v8i8 && VT != MVT::v16i8) - setOperationAction(ISD::CTPOP, VT, Expand); + setOperationAction(ISD::CTPOP, VT, Custom); setOperationAction(ISD::UDIV, VT, Expand); setOperationAction(ISD::SDIV, VT, Expand); @@ -816,8 +818,8 @@ void AArch64TargetLowering::addTypeForNEON(MVT VT, MVT PromotedBitwiseVT) { // F[MIN|MAX][NUM|NAN] are available for all FP NEON types. if (VT.isFloatingPoint() && (VT.getVectorElementType() != MVT::f16 || Subtarget->hasFullFP16())) - for (unsigned Opcode : {ISD::FMINNAN, ISD::FMAXNAN, - ISD::FMINNUM, ISD::FMAXNUM}) + for (unsigned Opcode : + {ISD::FMINIMUM, ISD::FMAXIMUM, ISD::FMINNUM, ISD::FMAXNUM}) setOperationAction(Opcode, VT, Legal); if (Subtarget->isLittleEndian()) { @@ -993,8 +995,8 @@ void AArch64TargetLowering::computeKnownBitsForTargetNode( break; case AArch64ISD::CSEL: { KnownBits Known2; - DAG.computeKnownBits(Op->getOperand(0), Known, Depth + 1); - DAG.computeKnownBits(Op->getOperand(1), Known2, Depth + 1); + Known = DAG.computeKnownBits(Op->getOperand(0), Depth + 1); + Known2 = DAG.computeKnownBits(Op->getOperand(1), Depth + 1); Known.Zero &= Known2.Zero; Known.One &= Known2.One; break; @@ -1086,6 +1088,7 @@ const char *AArch64TargetLowering::getTargetNodeName(unsigned Opcode) const { case AArch64ISD::FIRST_NUMBER: break; case AArch64ISD::CALL: return "AArch64ISD::CALL"; case AArch64ISD::ADRP: return "AArch64ISD::ADRP"; + case AArch64ISD::ADR: return "AArch64ISD::ADR"; case AArch64ISD::ADDlow: return "AArch64ISD::ADDlow"; case AArch64ISD::LOADgot: return "AArch64ISD::LOADgot"; case AArch64ISD::RET_FLAG: return "AArch64ISD::RET_FLAG"; @@ -1272,6 +1275,20 @@ AArch64TargetLowering::EmitF128CSEL(MachineInstr &MI, return EndBB; } +MachineBasicBlock *AArch64TargetLowering::EmitLoweredCatchRet( + MachineInstr &MI, MachineBasicBlock *BB) const { + assert(!isAsynchronousEHPersonality(classifyEHPersonality( + BB->getParent()->getFunction().getPersonalityFn())) && + "SEH does not use catchret!"); + return BB; +} + +MachineBasicBlock *AArch64TargetLowering::EmitLoweredCatchPad( + MachineInstr &MI, MachineBasicBlock *BB) const { + MI.eraseFromParent(); + return BB; +} + MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter( MachineInstr &MI, MachineBasicBlock *BB) const { switch (MI.getOpcode()) { @@ -1287,6 +1304,11 @@ MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter( case TargetOpcode::STACKMAP: case TargetOpcode::PATCHPOINT: return emitPatchPoint(MI, BB); + + case AArch64::CATCHRET: + return EmitLoweredCatchRet(MI, BB); + case AArch64::CATCHPAD: + return EmitLoweredCatchPad(MI, BB); } } @@ -1459,6 +1481,21 @@ static bool isLegalArithImmed(uint64_t C) { return IsLegal; } +// Can a (CMP op1, (sub 0, op2) be turned into a CMN instruction on +// the grounds that "op1 - (-op2) == op1 + op2" ? Not always, the C and V flags +// can be set differently by this operation. It comes down to whether +// "SInt(~op2)+1 == SInt(~op2+1)" (and the same for UInt). If they are then +// everything is fine. If not then the optimization is wrong. Thus general +// comparisons are only valid if op2 != 0. +// +// So, finally, the only LLVM-native comparisons that don't mention C and V +// are SETEQ and SETNE. They're the only ones we can safely use CMN for in +// the absence of information about op2. +static bool isCMN(SDValue Op, ISD::CondCode CC) { + return Op.getOpcode() == ISD::SUB && isNullConstant(Op.getOperand(0)) && + (CC == ISD::SETEQ || CC == ISD::SETNE); +} + static SDValue emitComparison(SDValue LHS, SDValue RHS, ISD::CondCode CC, const SDLoc &dl, SelectionDAG &DAG) { EVT VT = LHS.getValueType(); @@ -1481,20 +1518,15 @@ static SDValue emitComparison(SDValue LHS, SDValue RHS, ISD::CondCode CC, // register to WZR/XZR if it ends up being unused. unsigned Opcode = AArch64ISD::SUBS; - if (RHS.getOpcode() == ISD::SUB && isNullConstant(RHS.getOperand(0)) && - (CC == ISD::SETEQ || CC == ISD::SETNE)) { - // We'd like to combine a (CMP op1, (sub 0, op2) into a CMN instruction on - // the grounds that "op1 - (-op2) == op1 + op2". However, the C and V flags - // can be set differently by this operation. It comes down to whether - // "SInt(~op2)+1 == SInt(~op2+1)" (and the same for UInt). If they are then - // everything is fine. If not then the optimization is wrong. Thus general - // comparisons are only valid if op2 != 0. - - // So, finally, the only LLVM-native comparisons that don't mention C and V - // are SETEQ and SETNE. They're the only ones we can safely use CMN for in - // the absence of information about op2. + if (isCMN(RHS, CC)) { + // Can we combine a (CMP op1, (sub 0, op2) into a CMN instruction ? Opcode = AArch64ISD::ADDS; RHS = RHS.getOperand(1); + } else if (isCMN(LHS, CC)) { + // As we are looking for EQ/NE compares, the operands can be commuted ; can + // we combine a (CMP (sub 0, op1), op2) into a CMN instruction ? + Opcode = AArch64ISD::ADDS; + LHS = LHS.getOperand(1); } else if (LHS.getOpcode() == ISD::AND && isNullConstant(RHS) && !isUnsignedIntSetCC(CC)) { // Similarly, (CMP (and X, Y), 0) can be implemented with a TST @@ -1515,39 +1547,50 @@ static SDValue emitComparison(SDValue LHS, SDValue RHS, ISD::CondCode CC, /// The CCMP/CCMN/FCCMP/FCCMPE instructions allow the conditional execution of /// a comparison. They set the NZCV flags to a predefined value if their /// predicate is false. This allows to express arbitrary conjunctions, for -/// example "cmp 0 (and (setCA (cmp A)) (setCB (cmp B))))" +/// example "cmp 0 (and (setCA (cmp A)) (setCB (cmp B)))" /// expressed as: /// cmp A /// ccmp B, inv(CB), CA /// check for CB flags /// -/// In general we can create code for arbitrary "... (and (and A B) C)" -/// sequences. We can also implement some "or" expressions, because "(or A B)" -/// is equivalent to "not (and (not A) (not B))" and we can implement some -/// negation operations: -/// We can negate the results of a single comparison by inverting the flags -/// used when the predicate fails and inverting the flags tested in the next -/// instruction; We can also negate the results of the whole previous -/// conditional compare sequence by inverting the flags tested in the next -/// instruction. However there is no way to negate the result of a partial -/// sequence. +/// This naturally lets us implement chains of AND operations with SETCC +/// operands. And we can even implement some other situations by transforming +/// them: +/// - We can implement (NEG SETCC) i.e. negating a single comparison by +/// negating the flags used in a CCMP/FCCMP operations. +/// - We can negate the result of a whole chain of CMP/CCMP/FCCMP operations +/// by negating the flags we test for afterwards. i.e. +/// NEG (CMP CCMP CCCMP ...) can be implemented. +/// - Note that we can only ever negate all previously processed results. +/// What we can not implement by flipping the flags to test is a negation +/// of two sub-trees (because the negation affects all sub-trees emitted so +/// far, so the 2nd sub-tree we emit would also affect the first). +/// With those tools we can implement some OR operations: +/// - (OR (SETCC A) (SETCC B)) can be implemented via: +/// NEG (AND (NEG (SETCC A)) (NEG (SETCC B))) +/// - After transforming OR to NEG/AND combinations we may be able to use NEG +/// elimination rules from earlier to implement the whole thing as a +/// CCMP/FCCMP chain. /// -/// Therefore on encountering an "or" expression we can negate the subtree on -/// one side and have to be able to push the negate to the leafs of the subtree -/// on the other side (see also the comments in code). As complete example: -/// "or (or (setCA (cmp A)) (setCB (cmp B))) -/// (and (setCC (cmp C)) (setCD (cmp D)))" -/// is transformed to -/// "not (and (not (and (setCC (cmp C)) (setCC (cmp D)))) -/// (and (not (setCA (cmp A)) (not (setCB (cmp B))))))" -/// and implemented as: +/// As complete example: +/// or (or (setCA (cmp A)) (setCB (cmp B))) +/// (and (setCC (cmp C)) (setCD (cmp D)))" +/// can be reassociated to: +/// or (and (setCC (cmp C)) setCD (cmp D)) +// (or (setCA (cmp A)) (setCB (cmp B))) +/// can be transformed to: +/// not (and (not (and (setCC (cmp C)) (setCD (cmp D)))) +/// (and (not (setCA (cmp A)) (not (setCB (cmp B))))))" +/// which can be implemented as: /// cmp C /// ccmp D, inv(CD), CC /// ccmp A, CA, inv(CD) /// ccmp B, CB, inv(CA) /// check for CB flags -/// A counterexample is "or (and A B) (and C D)" which cannot be implemented -/// by conditional compare sequences. +/// +/// A counterexample is "or (and A B) (and C D)" which translates to +/// not (and (not (and (not A) (not B))) (not (and (not C) (not D)))), we +/// can only implement 1 of the inner (not) operations, but not both! /// @{ /// Create a conditional comparison; Use CCMP, CCMN or FCCMP as appropriate. @@ -1585,14 +1628,23 @@ static SDValue emitConditionalComparison(SDValue LHS, SDValue RHS, return DAG.getNode(Opcode, DL, MVT_CC, LHS, RHS, NZCVOp, Condition, CCOp); } -/// Returns true if @p Val is a tree of AND/OR/SETCC operations. -/// CanPushNegate is set to true if we can push a negate operation through -/// the tree in a was that we are left with AND operations and negate operations -/// at the leafs only. i.e. "not (or (or x y) z)" can be changed to -/// "and (and (not x) (not y)) (not z)"; "not (or (and x y) z)" cannot be -/// brought into such a form. -static bool isConjunctionDisjunctionTree(const SDValue Val, bool &CanNegate, - unsigned Depth = 0) { +/// Returns true if @p Val is a tree of AND/OR/SETCC operations that can be +/// expressed as a conjunction. See \ref AArch64CCMP. +/// \param CanNegate Set to true if we can negate the whole sub-tree just by +/// changing the conditions on the SETCC tests. +/// (this means we can call emitConjunctionRec() with +/// Negate==true on this sub-tree) +/// \param MustBeFirst Set to true if this subtree needs to be negated and we +/// cannot do the negation naturally. We are required to +/// emit the subtree first in this case. +/// \param WillNegate Is true if are called when the result of this +/// subexpression must be negated. This happens when the +/// outer expression is an OR. We can use this fact to know +/// that we have a double negation (or (or ...) ...) that +/// can be implemented for free. +static bool canEmitConjunction(const SDValue Val, bool &CanNegate, + bool &MustBeFirst, bool WillNegate, + unsigned Depth = 0) { if (!Val.hasOneUse()) return false; unsigned Opcode = Val->getOpcode(); @@ -1600,39 +1652,44 @@ static bool isConjunctionDisjunctionTree(const SDValue Val, bool &CanNegate, if (Val->getOperand(0).getValueType() == MVT::f128) return false; CanNegate = true; + MustBeFirst = false; return true; } // Protect against exponential runtime and stack overflow. if (Depth > 6) return false; if (Opcode == ISD::AND || Opcode == ISD::OR) { + bool IsOR = Opcode == ISD::OR; SDValue O0 = Val->getOperand(0); SDValue O1 = Val->getOperand(1); bool CanNegateL; - if (!isConjunctionDisjunctionTree(O0, CanNegateL, Depth+1)) + bool MustBeFirstL; + if (!canEmitConjunction(O0, CanNegateL, MustBeFirstL, IsOR, Depth+1)) return false; bool CanNegateR; - if (!isConjunctionDisjunctionTree(O1, CanNegateR, Depth+1)) + bool MustBeFirstR; + if (!canEmitConjunction(O1, CanNegateR, MustBeFirstR, IsOR, Depth+1)) return false; - if (Opcode == ISD::OR) { - // For an OR expression we need to be able to negate at least one side or - // we cannot do the transformation at all. + if (MustBeFirstL && MustBeFirstR) + return false; + + if (IsOR) { + // For an OR expression we need to be able to naturally negate at least + // one side or we cannot do the transformation at all. if (!CanNegateL && !CanNegateR) return false; - // We can however change a (not (or x y)) to (and (not x) (not y)) if we - // can negate the x and y subtrees. - CanNegate = CanNegateL && CanNegateR; + // If we the result of the OR will be negated and we can naturally negate + // the leafs, then this sub-tree as a whole negates naturally. + CanNegate = WillNegate && CanNegateL && CanNegateR; + // If we cannot naturally negate the whole sub-tree, then this must be + // emitted first. + MustBeFirst = !CanNegate; } else { - // If the operands are OR expressions then we finally need to negate their - // outputs, we can only do that for the operand with emitted last by - // negating OutCC, not for both operands. - bool NeedsNegOutL = O0->getOpcode() == ISD::OR; - bool NeedsNegOutR = O1->getOpcode() == ISD::OR; - if (NeedsNegOutL && NeedsNegOutR) - return false; - // We cannot negate an AND operation (it would become an OR), + assert(Opcode == ISD::AND && "Must be OR or AND"); + // We cannot naturally negate an AND operation. CanNegate = false; + MustBeFirst = MustBeFirstL || MustBeFirstR; } return true; } @@ -1645,11 +1702,9 @@ static bool isConjunctionDisjunctionTree(const SDValue Val, bool &CanNegate, /// and conditional compare operations. @returns an NZCV flags producing node /// and sets @p OutCC to the flags that should be tested or returns SDValue() if /// transformation was not possible. -/// On recursive invocations @p PushNegate may be set to true to have negation -/// effects pushed to the tree leafs; @p Predicate is an NZCV flag predicate -/// for the comparisons in the current subtree; @p Depth limits the search -/// depth to avoid stack overflow. -static SDValue emitConjunctionDisjunctionTreeRec(SelectionDAG &DAG, SDValue Val, +/// \p Negate is true if we want this sub-tree being negated just by changing +/// SETCC conditions. +static SDValue emitConjunctionRec(SelectionDAG &DAG, SDValue Val, AArch64CC::CondCode &OutCC, bool Negate, SDValue CCOp, AArch64CC::CondCode Predicate) { // We're at a tree leaf, produce a conditional comparison operation. @@ -1690,80 +1745,125 @@ static SDValue emitConjunctionDisjunctionTreeRec(SelectionDAG &DAG, SDValue Val, return emitConditionalComparison(LHS, RHS, CC, CCOp, Predicate, OutCC, DL, DAG); } - assert((Opcode == ISD::AND || (Opcode == ISD::OR && Val->hasOneUse())) && - "Valid conjunction/disjunction tree"); + assert(Val->hasOneUse() && "Valid conjunction/disjunction tree"); + + bool IsOR = Opcode == ISD::OR; - // Check if both sides can be transformed. SDValue LHS = Val->getOperand(0); - SDValue RHS = Val->getOperand(1); + bool CanNegateL; + bool MustBeFirstL; + bool ValidL = canEmitConjunction(LHS, CanNegateL, MustBeFirstL, IsOR); + assert(ValidL && "Valid conjunction/disjunction tree"); + (void)ValidL; - // In case of an OR we need to negate our operands and the result. - // (A v B) <=> not(not(A) ^ not(B)) - bool NegateOpsAndResult = Opcode == ISD::OR; - // We can negate the results of all previous operations by inverting the - // predicate flags giving us a free negation for one side. The other side - // must be negatable by itself. - if (NegateOpsAndResult) { - // See which side we can negate. - bool CanNegateL; - bool isValidL = isConjunctionDisjunctionTree(LHS, CanNegateL); - assert(isValidL && "Valid conjunction/disjunction tree"); - (void)isValidL; + SDValue RHS = Val->getOperand(1); + bool CanNegateR; + bool MustBeFirstR; + bool ValidR = canEmitConjunction(RHS, CanNegateR, MustBeFirstR, IsOR); + assert(ValidR && "Valid conjunction/disjunction tree"); + (void)ValidR; -#ifndef NDEBUG - bool CanNegateR; - bool isValidR = isConjunctionDisjunctionTree(RHS, CanNegateR); - assert(isValidR && "Valid conjunction/disjunction tree"); - assert((CanNegateL || CanNegateR) && "Valid conjunction/disjunction tree"); -#endif + // Swap sub-tree that must come first to the right side. + if (MustBeFirstL) { + assert(!MustBeFirstR && "Valid conjunction/disjunction tree"); + std::swap(LHS, RHS); + std::swap(CanNegateL, CanNegateR); + std::swap(MustBeFirstL, MustBeFirstR); + } - // Order the side which we cannot negate to RHS so we can emit it first. - if (!CanNegateL) + bool NegateR; + bool NegateAfterR; + bool NegateL; + bool NegateAfterAll; + if (Opcode == ISD::OR) { + // Swap the sub-tree that we can negate naturally to the left. + if (!CanNegateL) { + assert(CanNegateR && "at least one side must be negatable"); + assert(!MustBeFirstR && "invalid conjunction/disjunction tree"); + assert(!Negate); std::swap(LHS, RHS); + NegateR = false; + NegateAfterR = true; + } else { + // Negate the left sub-tree if possible, otherwise negate the result. + NegateR = CanNegateR; + NegateAfterR = !CanNegateR; + } + NegateL = true; + NegateAfterAll = !Negate; } else { - bool NeedsNegOutL = LHS->getOpcode() == ISD::OR; - assert((!NeedsNegOutL || RHS->getOpcode() != ISD::OR) && - "Valid conjunction/disjunction tree"); - // Order the side where we need to negate the output flags to RHS so it - // gets emitted first. - if (NeedsNegOutL) - std::swap(LHS, RHS); + assert(Opcode == ISD::AND && "Valid conjunction/disjunction tree"); + assert(!Negate && "Valid conjunction/disjunction tree"); + + NegateL = false; + NegateR = false; + NegateAfterR = false; + NegateAfterAll = false; } - // Emit RHS. If we want to negate the tree we only need to push a negate - // through if we are already in a PushNegate case, otherwise we can negate - // the "flags to test" afterwards. + // Emit sub-trees. AArch64CC::CondCode RHSCC; - SDValue CmpR = emitConjunctionDisjunctionTreeRec(DAG, RHS, RHSCC, Negate, - CCOp, Predicate); - if (NegateOpsAndResult && !Negate) + SDValue CmpR = emitConjunctionRec(DAG, RHS, RHSCC, NegateR, CCOp, Predicate); + if (NegateAfterR) RHSCC = AArch64CC::getInvertedCondCode(RHSCC); - // Emit LHS. We may need to negate it. - SDValue CmpL = emitConjunctionDisjunctionTreeRec(DAG, LHS, OutCC, - NegateOpsAndResult, CmpR, - RHSCC); - // If we transformed an OR to and AND then we have to negate the result - // (or absorb the Negate parameter). - if (NegateOpsAndResult && !Negate) + SDValue CmpL = emitConjunctionRec(DAG, LHS, OutCC, NegateL, CmpR, RHSCC); + if (NegateAfterAll) OutCC = AArch64CC::getInvertedCondCode(OutCC); return CmpL; } -/// Emit conjunction or disjunction tree with the CMP/FCMP followed by a chain -/// of CCMP/CFCMP ops. See @ref AArch64CCMP. -/// \see emitConjunctionDisjunctionTreeRec(). -static SDValue emitConjunctionDisjunctionTree(SelectionDAG &DAG, SDValue Val, - AArch64CC::CondCode &OutCC) { - bool CanNegate; - if (!isConjunctionDisjunctionTree(Val, CanNegate)) +/// Emit expression as a conjunction (a series of CCMP/CFCMP ops). +/// In some cases this is even possible with OR operations in the expression. +/// See \ref AArch64CCMP. +/// \see emitConjunctionRec(). +static SDValue emitConjunction(SelectionDAG &DAG, SDValue Val, + AArch64CC::CondCode &OutCC) { + bool DummyCanNegate; + bool DummyMustBeFirst; + if (!canEmitConjunction(Val, DummyCanNegate, DummyMustBeFirst, false)) return SDValue(); - return emitConjunctionDisjunctionTreeRec(DAG, Val, OutCC, false, SDValue(), - AArch64CC::AL); + return emitConjunctionRec(DAG, Val, OutCC, false, SDValue(), AArch64CC::AL); } /// @} +/// Returns how profitable it is to fold a comparison's operand's shift and/or +/// extension operations. +static unsigned getCmpOperandFoldingProfit(SDValue Op) { + auto isSupportedExtend = [&](SDValue V) { + if (V.getOpcode() == ISD::SIGN_EXTEND_INREG) + return true; + + if (V.getOpcode() == ISD::AND) + if (ConstantSDNode *MaskCst = dyn_cast<ConstantSDNode>(V.getOperand(1))) { + uint64_t Mask = MaskCst->getZExtValue(); + return (Mask == 0xFF || Mask == 0xFFFF || Mask == 0xFFFFFFFF); + } + + return false; + }; + + if (!Op.hasOneUse()) + return 0; + + if (isSupportedExtend(Op)) + return 1; + + unsigned Opc = Op.getOpcode(); + if (Opc == ISD::SHL || Opc == ISD::SRL || Opc == ISD::SRA) + if (ConstantSDNode *ShiftCst = dyn_cast<ConstantSDNode>(Op.getOperand(1))) { + uint64_t Shift = ShiftCst->getZExtValue(); + if (isSupportedExtend(Op.getOperand(0))) + return (Shift <= 4) ? 2 : 1; + EVT VT = Op.getValueType(); + if ((VT == MVT::i32 && Shift <= 31) || (VT == MVT::i64 && Shift <= 63)) + return 1; + } + + return 0; +} + static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, SDValue &AArch64cc, SelectionDAG &DAG, const SDLoc &dl) { @@ -1821,6 +1921,27 @@ static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, } } } + + // Comparisons are canonicalized so that the RHS operand is simpler than the + // LHS one, the extreme case being when RHS is an immediate. However, AArch64 + // can fold some shift+extend operations on the RHS operand, so swap the + // operands if that can be done. + // + // For example: + // lsl w13, w11, #1 + // cmp w13, w12 + // can be turned into: + // cmp w12, w11, lsl #1 + if (!isa<ConstantSDNode>(RHS) || + !isLegalArithImmed(cast<ConstantSDNode>(RHS)->getZExtValue())) { + SDValue TheLHS = isCMN(LHS, CC) ? LHS.getOperand(1) : LHS; + + if (getCmpOperandFoldingProfit(TheLHS) > getCmpOperandFoldingProfit(RHS)) { + std::swap(LHS, RHS); + CC = ISD::getSetCCSwappedOperands(CC); + } + } + SDValue Cmp; AArch64CC::CondCode AArch64CC; if ((CC == ISD::SETEQ || CC == ISD::SETNE) && isa<ConstantSDNode>(RHS)) { @@ -1859,7 +1980,7 @@ static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, } if (!Cmp && (RHSC->isNullValue() || RHSC->isOne())) { - if ((Cmp = emitConjunctionDisjunctionTree(DAG, LHS, AArch64CC))) { + if ((Cmp = emitConjunction(DAG, LHS, AArch64CC))) { if ((CC == ISD::SETNE) ^ RHSC->isNullValue()) AArch64CC = AArch64CC::getInvertedCondCode(AArch64CC); } @@ -2587,66 +2708,6 @@ static SDValue LowerMUL(SDValue Op, SelectionDAG &DAG) { DAG.getNode(ISD::BITCAST, DL, Op1VT, N01), Op1)); } -// Lower vector multiply high (ISD::MULHS and ISD::MULHU). -static SDValue LowerMULH(SDValue Op, SelectionDAG &DAG) { - // Multiplications are only custom-lowered for 128-bit vectors so that - // {S,U}MULL{2} can be detected. Otherwise v2i64 multiplications are not - // legal. - EVT VT = Op.getValueType(); - assert(VT.is128BitVector() && VT.isInteger() && - "unexpected type for custom-lowering ISD::MULH{U,S}"); - - SDValue V0 = Op.getOperand(0); - SDValue V1 = Op.getOperand(1); - - SDLoc DL(Op); - - EVT ExtractVT = VT.getHalfNumVectorElementsVT(*DAG.getContext()); - - // We turn (V0 mulhs/mulhu V1) to: - // - // (uzp2 (smull (extract_subvector (ExtractVT V128:V0, (i64 0)), - // (extract_subvector (ExtractVT V128:V1, (i64 0))))), - // (smull (extract_subvector (ExtractVT V128:V0, (i64 VMull2Idx)), - // (extract_subvector (ExtractVT V128:V2, (i64 VMull2Idx)))))) - // - // Where ExtractVT is a subvector with half number of elements, and - // VMullIdx2 is the index of the middle element (the high part). - // - // The vector hight part extract and multiply will be matched against - // {S,U}MULL{v16i8_v8i16,v8i16_v4i32,v4i32_v2i64} which in turn will - // issue a {s}mull2 instruction. - // - // This basically multiply the lower subvector with '{s,u}mull', the high - // subvector with '{s,u}mull2', and shuffle both results high part in - // resulting vector. - unsigned Mull2VectorIdx = VT.getVectorNumElements () / 2; - SDValue VMullIdx = DAG.getConstant(0, DL, MVT::i64); - SDValue VMull2Idx = DAG.getConstant(Mull2VectorIdx, DL, MVT::i64); - - SDValue VMullV0 = - DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ExtractVT, V0, VMullIdx); - SDValue VMullV1 = - DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ExtractVT, V1, VMullIdx); - - SDValue VMull2V0 = - DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ExtractVT, V0, VMull2Idx); - SDValue VMull2V1 = - DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ExtractVT, V1, VMull2Idx); - - unsigned MullOpc = Op.getOpcode() == ISD::MULHS ? AArch64ISD::SMULL - : AArch64ISD::UMULL; - - EVT MullVT = ExtractVT.widenIntegerVectorElementType(*DAG.getContext()); - SDValue Mull = DAG.getNode(MullOpc, DL, MullVT, VMullV0, VMullV1); - SDValue Mull2 = DAG.getNode(MullOpc, DL, MullVT, VMull2V0, VMull2V1); - - Mull = DAG.getNode(ISD::BITCAST, DL, VT, Mull); - Mull2 = DAG.getNode(ISD::BITCAST, DL, VT, Mull2); - - return DAG.getNode(AArch64ISD::UZP2, DL, VT, Mull, Mull2); -} - SDValue AArch64TargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const { unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); @@ -2657,9 +2718,19 @@ SDValue AArch64TargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, EVT PtrVT = getPointerTy(DAG.getDataLayout()); return DAG.getNode(AArch64ISD::THREAD_POINTER, dl, PtrVT); } - case Intrinsic::aarch64_neon_abs: - return DAG.getNode(ISD::ABS, dl, Op.getValueType(), - Op.getOperand(1)); + case Intrinsic::aarch64_neon_abs: { + EVT Ty = Op.getValueType(); + if (Ty == MVT::i64) { + SDValue Result = DAG.getNode(ISD::BITCAST, dl, MVT::v1i64, + Op.getOperand(1)); + Result = DAG.getNode(ISD::ABS, dl, MVT::v1i64, Result); + return DAG.getNode(ISD::BITCAST, dl, MVT::i64, Result); + } else if (Ty.isVector() && Ty.isInteger() && isTypeLegal(Ty)) { + return DAG.getNode(ISD::ABS, dl, Ty, Op.getOperand(1)); + } else { + report_fatal_error("Unexpected type for AArch64 NEON intrinic"); + } + } case Intrinsic::aarch64_neon_smax: return DAG.getNode(ISD::SMAX, dl, Op.getValueType(), Op.getOperand(1), Op.getOperand(2)); @@ -2762,6 +2833,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerSELECT_CC(Op, DAG); case ISD::JumpTable: return LowerJumpTable(Op, DAG); + case ISD::BR_JT: + return LowerBR_JT(Op, DAG); case ISD::ConstantPool: return LowerConstantPool(Op, DAG); case ISD::BlockAddress: @@ -2798,8 +2871,12 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerFP_EXTEND(Op, DAG); case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG); + case ISD::SPONENTRY: + return LowerSPONENTRY(Op, DAG); case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG); + case ISD::ADDROFRETURNADDR: + return LowerADDROFRETURNADDR(Op, DAG); case ISD::INSERT_VECTOR_ELT: return LowerINSERT_VECTOR_ELT(Op, DAG); case ISD::EXTRACT_VECTOR_ELT: @@ -2843,9 +2920,6 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerFLT_ROUNDS_(Op, DAG); case ISD::MUL: return LowerMUL(Op, DAG); - case ISD::MULHS: - case ISD::MULHU: - return LowerMULH(Op, DAG); case ISD::INTRINSIC_WO_CHAIN: return LowerINTRINSIC_WO_CHAIN(Op, DAG); case ISD::STORE: @@ -2895,6 +2969,8 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, return IsVarArg ? CC_AArch64_DarwinPCS_VarArg : CC_AArch64_DarwinPCS; case CallingConv::Win64: return IsVarArg ? CC_AArch64_Win64_VarArg : CC_AArch64_AAPCS; + case CallingConv::AArch64_VectorCall: + return CC_AArch64_AAPCS; } } @@ -3081,6 +3157,17 @@ SDValue AArch64TargetLowering::LowerFormalArguments( // We currently pass all varargs at 8-byte alignment. StackOffset = ((StackOffset + 7) & ~7); FuncInfo->setVarArgsStackIndex(MFI.CreateFixedObject(4, StackOffset, true)); + + if (MFI.hasMustTailInVarArgFunc()) { + SmallVector<MVT, 2> RegParmTypes; + RegParmTypes.push_back(MVT::i64); + RegParmTypes.push_back(MVT::f128); + // Compute the set of forwarded registers. The rest are scratch. + SmallVectorImpl<ForwardedRegister> &Forwards = + FuncInfo->getForwardedMustTailRegParms(); + CCInfo.analyzeMustTailForwardedRegisters(Forwards, RegParmTypes, + CC_AArch64_AAPCS); + } } unsigned StackArgSize = CCInfo.getNextStackOffset(); @@ -3103,6 +3190,9 @@ SDValue AArch64TargetLowering::LowerFormalArguments( // much is there while considering tail calls (because we can reuse it). FuncInfo->setBytesInStackArgArea(StackArgSize); + if (Subtarget->hasCustomCallingConv()) + Subtarget->getRegisterInfo()->UpdateCustomCalleeSavedRegs(MF); + return Chain; } @@ -3333,6 +3423,10 @@ bool AArch64TargetLowering::isEligibleForTailCallOptimization( const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC); if (!CCMatch) { const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC); + if (Subtarget->hasCustomCallingConv()) { + TRI->UpdateCustomCallPreservedMask(MF, &CallerPreserved); + TRI->UpdateCustomCallPreservedMask(MF, &CalleePreserved); + } if (!TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved)) return false; } @@ -3534,6 +3628,14 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVector<SDValue, 8> MemOpChains; auto PtrVT = getPointerTy(DAG.getDataLayout()); + if (IsVarArg && CLI.CS && CLI.CS.isMustTailCall()) { + const auto &Forwards = FuncInfo->getForwardedMustTailRegParms(); + for (const auto &F : Forwards) { + SDValue Val = DAG.getCopyFromReg(Chain, DL, F.VReg, F.VT); + RegsToPass.push_back(std::make_pair(unsigned(F.PReg), Val)); + } + } + // Walk the register/memloc assignments, inserting copies/loads. for (unsigned i = 0, realArgIdx = 0, e = ArgLocs.size(); i != e; ++i, ++realArgIdx) { @@ -3726,6 +3828,12 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, } else Mask = TRI->getCallPreservedMask(MF, CallConv); + if (Subtarget->hasCustomCallingConv()) + TRI->UpdateCustomCallPreservedMask(MF, &Mask); + + if (TRI->isAnyArgRegReserved(MF)) + TRI->emitReservedArgRegCallError(MF); + assert(Mask && "Missing call preserved mask for calling convention"); Ops.push_back(DAG.getRegisterMask(Mask)); @@ -3912,13 +4020,21 @@ SDValue AArch64TargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG, return DAG.getNode(AArch64ISD::ADDlow, DL, Ty, ADRP, Lo); } +// (adr sym) +template <class NodeTy> +SDValue AArch64TargetLowering::getAddrTiny(NodeTy *N, SelectionDAG &DAG, + unsigned Flags) const { + LLVM_DEBUG(dbgs() << "AArch64TargetLowering::getAddrTiny\n"); + SDLoc DL(N); + EVT Ty = getPointerTy(DAG.getDataLayout()); + SDValue Sym = getTargetNode(N, Ty, DAG, Flags); + return DAG.getNode(AArch64ISD::ADR, DL, Ty, Sym); +} + SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { GlobalAddressSDNode *GN = cast<GlobalAddressSDNode>(Op); const GlobalValue *GV = GN->getGlobal(); - const AArch64II::TOF TargetFlags = - (GV->hasDLLImportStorageClass() ? AArch64II::MO_DLLIMPORT - : AArch64II::MO_NO_FLAG); unsigned char OpFlags = Subtarget->ClassifyGlobalReference(GV, getTargetMachine()); @@ -3926,20 +4042,23 @@ SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op, assert(cast<GlobalAddressSDNode>(Op)->getOffset() == 0 && "unexpected offset in global node"); - // This also catches the large code model case for Darwin. + // This also catches the large code model case for Darwin, and tiny code + // model with got relocations. if ((OpFlags & AArch64II::MO_GOT) != 0) { - return getGOT(GN, DAG, TargetFlags); + return getGOT(GN, DAG, OpFlags); } SDValue Result; if (getTargetMachine().getCodeModel() == CodeModel::Large) { - Result = getAddrLarge(GN, DAG, TargetFlags); + Result = getAddrLarge(GN, DAG, OpFlags); + } else if (getTargetMachine().getCodeModel() == CodeModel::Tiny) { + Result = getAddrTiny(GN, DAG, OpFlags); } else { - Result = getAddr(GN, DAG, TargetFlags); + Result = getAddr(GN, DAG, OpFlags); } EVT PtrVT = getPointerTy(DAG.getDataLayout()); SDLoc DL(GN); - if (GV->hasDLLImportStorageClass()) + if (OpFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB)) Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, MachinePointerInfo::getGOT(DAG.getMachineFunction())); return Result; @@ -4004,8 +4123,10 @@ AArch64TargetLowering::LowerDarwinGlobalTLSAddress(SDValue Op, // TLS calls preserve all registers except those that absolutely must be // trashed: X0 (it takes an argument), LR (it's a call) and NZCV (let's not be // silly). - const uint32_t *Mask = - Subtarget->getRegisterInfo()->getTLSCallPreservedMask(); + const AArch64RegisterInfo *TRI = Subtarget->getRegisterInfo(); + const uint32_t *Mask = TRI->getTLSCallPreservedMask(); + if (Subtarget->hasCustomCallingConv()) + TRI->UpdateCustomCallPreservedMask(DAG.getMachineFunction(), &Mask); // Finally, we can make the call. This is just a degenerate version of a // normal AArch64 call node: x0 takes the address of the descriptor, and @@ -4055,13 +4176,15 @@ SDValue AArch64TargetLowering::LowerELFGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { assert(Subtarget->isTargetELF() && "This function expects an ELF target"); - assert(Subtarget->useSmallAddressing() && - "ELF TLS only supported in small memory model"); + if (getTargetMachine().getCodeModel() == CodeModel::Large) + report_fatal_error("ELF TLS only supported in small memory model"); // Different choices can be made for the maximum size of the TLS area for a // module. For the small address model, the default TLS size is 16MiB and the // maximum TLS size is 4GiB. // FIXME: add -mtls-size command line option and make it control the 16MiB // vs. 4GiB code sequence generation. + // FIXME: add tiny codemodel support. We currently generate the same code as + // small, which may be larger than needed. const GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op); TLSModel::Model Model = getTargetMachine().getTLSModel(GA->getGlobal()); @@ -4232,6 +4355,13 @@ SDValue AArch64TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { SDValue Dest = Op.getOperand(4); SDLoc dl(Op); + MachineFunction &MF = DAG.getMachineFunction(); + // Speculation tracking/SLH assumes that optimized TB(N)Z/CB(N)Z instructions + // will not be produced, as they are conditional branch instructions that do + // not set flags. + bool ProduceNonFlagSettingCondBr = + !MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening); + // Handle f128 first, since lowering it will result in comparing the return // value of a libcall against zero, which is just what the rest of LowerBR_CC // is expecting to deal with. @@ -4274,7 +4404,7 @@ SDValue AArch64TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { // If the RHS of the comparison is zero, we can potentially fold this // to a specialized branch. const ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS); - if (RHSC && RHSC->getZExtValue() == 0) { + if (RHSC && RHSC->getZExtValue() == 0 && ProduceNonFlagSettingCondBr) { if (CC == ISD::SETEQ) { // See if we can use a TBZ to fold in an AND as well. // TBZ has a smaller branch displacement than CBZ. If the offset is @@ -4317,7 +4447,7 @@ SDValue AArch64TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { } } if (RHSC && RHSC->getSExtValue() == -1 && CC == ISD::SETGT && - LHS.getOpcode() != ISD::AND) { + LHS.getOpcode() != ISD::AND && ProduceNonFlagSettingCondBr) { // Don't combine AND since emitComparison converts the AND to an ANDS // (a.k.a. TST) and the test in the test bit and branch instruction // becomes redundant. This would also increase register pressure. @@ -4446,18 +4576,42 @@ SDValue AArch64TargetLowering::LowerCTPOP(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); EVT VT = Op.getValueType(); - if (VT == MVT::i32) - Val = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, Val); - Val = DAG.getNode(ISD::BITCAST, DL, MVT::v8i8, Val); + if (VT == MVT::i32 || VT == MVT::i64) { + if (VT == MVT::i32) + Val = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, Val); + Val = DAG.getNode(ISD::BITCAST, DL, MVT::v8i8, Val); - SDValue CtPop = DAG.getNode(ISD::CTPOP, DL, MVT::v8i8, Val); - SDValue UaddLV = DAG.getNode( - ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, - DAG.getConstant(Intrinsic::aarch64_neon_uaddlv, DL, MVT::i32), CtPop); + SDValue CtPop = DAG.getNode(ISD::CTPOP, DL, MVT::v8i8, Val); + SDValue UaddLV = DAG.getNode( + ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, + DAG.getConstant(Intrinsic::aarch64_neon_uaddlv, DL, MVT::i32), CtPop); - if (VT == MVT::i64) - UaddLV = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, UaddLV); - return UaddLV; + if (VT == MVT::i64) + UaddLV = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, UaddLV); + return UaddLV; + } + + assert((VT == MVT::v1i64 || VT == MVT::v2i64 || VT == MVT::v2i32 || + VT == MVT::v4i32 || VT == MVT::v4i16 || VT == MVT::v8i16) && + "Unexpected type for custom ctpop lowering"); + + EVT VT8Bit = VT.is64BitVector() ? MVT::v8i8 : MVT::v16i8; + Val = DAG.getBitcast(VT8Bit, Val); + Val = DAG.getNode(ISD::CTPOP, DL, VT8Bit, Val); + + // Widen v8i8/v16i8 CTPOP result to VT by repeatedly widening pairwise adds. + unsigned EltSize = 8; + unsigned NumElts = VT.is64BitVector() ? 8 : 16; + while (EltSize != VT.getScalarSizeInBits()) { + EltSize *= 2; + NumElts /= 2; + MVT WidenVT = MVT::getVectorVT(MVT::getIntegerVT(EltSize), NumElts); + Val = DAG.getNode( + ISD::INTRINSIC_WO_CHAIN, DL, WidenVT, + DAG.getConstant(Intrinsic::aarch64_neon_uaddlp, DL, MVT::i32), Val); + } + + return Val; } SDValue AArch64TargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { @@ -4779,10 +4933,28 @@ SDValue AArch64TargetLowering::LowerJumpTable(SDValue Op, if (getTargetMachine().getCodeModel() == CodeModel::Large && !Subtarget->isTargetMachO()) { return getAddrLarge(JT, DAG); + } else if (getTargetMachine().getCodeModel() == CodeModel::Tiny) { + return getAddrTiny(JT, DAG); } return getAddr(JT, DAG); } +SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, + SelectionDAG &DAG) const { + // Jump table entries as PC relative offsets. No additional tweaking + // is necessary here. Just get the address of the jump table. + SDLoc DL(Op); + SDValue JT = Op.getOperand(1); + SDValue Entry = Op.getOperand(2); + int JTI = cast<JumpTableSDNode>(JT.getNode())->getIndex(); + + SDNode *Dest = + DAG.getMachineNode(AArch64::JumpTableDest32, DL, MVT::i64, MVT::i64, JT, + Entry, DAG.getTargetJumpTable(JTI, MVT::i32)); + return DAG.getNode(ISD::BRIND, DL, MVT::Other, Op.getOperand(0), + SDValue(Dest, 0)); +} + SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op, SelectionDAG &DAG) const { ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op); @@ -4793,6 +4965,8 @@ SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op, return getGOT(CP, DAG); } return getAddrLarge(CP, DAG); + } else if (getTargetMachine().getCodeModel() == CodeModel::Tiny) { + return getAddrTiny(CP, DAG); } else { return getAddr(CP, DAG); } @@ -4804,9 +4978,10 @@ SDValue AArch64TargetLowering::LowerBlockAddress(SDValue Op, if (getTargetMachine().getCodeModel() == CodeModel::Large && !Subtarget->isTargetMachO()) { return getAddrLarge(BA, DAG); - } else { - return getAddr(BA, DAG); + } else if (getTargetMachine().getCodeModel() == CodeModel::Tiny) { + return getAddrTiny(BA, DAG); } + return getAddr(BA, DAG); } SDValue AArch64TargetLowering::LowerDarwin_VASTART(SDValue Op, @@ -5012,21 +5187,59 @@ SDValue AArch64TargetLowering::LowerFRAMEADDR(SDValue Op, return FrameAddr; } +SDValue AArch64TargetLowering::LowerSPONENTRY(SDValue Op, + SelectionDAG &DAG) const { + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + + EVT VT = getPointerTy(DAG.getDataLayout()); + SDLoc DL(Op); + int FI = MFI.CreateFixedObject(4, 0, false); + return DAG.getFrameIndex(FI, VT); +} + // FIXME? Maybe this could be a TableGen attribute on some registers and // this table could be generated automatically from RegInfo. unsigned AArch64TargetLowering::getRegisterByName(const char* RegName, EVT VT, SelectionDAG &DAG) const { unsigned Reg = StringSwitch<unsigned>(RegName) .Case("sp", AArch64::SP) + .Case("x1", AArch64::X1) + .Case("w1", AArch64::W1) + .Case("x2", AArch64::X2) + .Case("w2", AArch64::W2) + .Case("x3", AArch64::X3) + .Case("w3", AArch64::W3) + .Case("x4", AArch64::X4) + .Case("w4", AArch64::W4) + .Case("x5", AArch64::X5) + .Case("w5", AArch64::W5) + .Case("x6", AArch64::X6) + .Case("w6", AArch64::W6) + .Case("x7", AArch64::X7) + .Case("w7", AArch64::W7) .Case("x18", AArch64::X18) .Case("w18", AArch64::W18) .Case("x20", AArch64::X20) .Case("w20", AArch64::W20) .Default(0); - if (((Reg == AArch64::X18 || Reg == AArch64::W18) && - !Subtarget->isX18Reserved()) || + if (((Reg == AArch64::X1 || Reg == AArch64::W1) && + !Subtarget->isXRegisterReserved(1)) || + ((Reg == AArch64::X2 || Reg == AArch64::W2) && + !Subtarget->isXRegisterReserved(2)) || + ((Reg == AArch64::X3 || Reg == AArch64::W3) && + !Subtarget->isXRegisterReserved(3)) || + ((Reg == AArch64::X4 || Reg == AArch64::W4) && + !Subtarget->isXRegisterReserved(4)) || + ((Reg == AArch64::X5 || Reg == AArch64::W5) && + !Subtarget->isXRegisterReserved(5)) || + ((Reg == AArch64::X6 || Reg == AArch64::W6) && + !Subtarget->isXRegisterReserved(6)) || + ((Reg == AArch64::X7 || Reg == AArch64::W7) && + !Subtarget->isXRegisterReserved(7)) || + ((Reg == AArch64::X18 || Reg == AArch64::W18) && + !Subtarget->isXRegisterReserved(18)) || ((Reg == AArch64::X20 || Reg == AArch64::W20) && - !Subtarget->isX20Reserved())) + !Subtarget->isXRegisterReserved(20))) Reg = 0; if (Reg) return Reg; @@ -5034,6 +5247,20 @@ unsigned AArch64TargetLowering::getRegisterByName(const char* RegName, EVT VT, + StringRef(RegName) + "\".")); } +SDValue AArch64TargetLowering::LowerADDROFRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); + + EVT VT = Op.getValueType(); + SDLoc DL(Op); + + SDValue FrameAddr = + DAG.getCopyFromReg(DAG.getEntryNode(), DL, AArch64::FP, VT); + SDValue Offset = DAG.getConstant(8, DL, getPointerTy(DAG.getDataLayout())); + + return DAG.getNode(ISD::ADD, DL, VT, FrameAddr, Offset); +} + SDValue AArch64TargetLowering::LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); @@ -5176,40 +5403,29 @@ bool AArch64TargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT) const { // FIXME: We should be able to handle f128 as well with a clever lowering. if (Imm.isPosZero() && (VT == MVT::f64 || VT == MVT::f32 || (VT == MVT::f16 && Subtarget->hasFullFP16()))) { - LLVM_DEBUG( - dbgs() << "Legal fp imm: materialize 0 using the zero register\n"); + LLVM_DEBUG(dbgs() << "Legal " << VT.getEVTString() << " imm value: 0\n"); return true; } - StringRef FPType; bool IsLegal = false; SmallString<128> ImmStrVal; Imm.toString(ImmStrVal); - if (VT == MVT::f64) { - FPType = "f64"; + if (VT == MVT::f64) IsLegal = AArch64_AM::getFP64Imm(Imm) != -1; - } else if (VT == MVT::f32) { - FPType = "f32"; + else if (VT == MVT::f32) IsLegal = AArch64_AM::getFP32Imm(Imm) != -1; - } else if (VT == MVT::f16 && Subtarget->hasFullFP16()) { - FPType = "f16"; + else if (VT == MVT::f16 && Subtarget->hasFullFP16()) IsLegal = AArch64_AM::getFP16Imm(Imm) != -1; - } if (IsLegal) { - LLVM_DEBUG(dbgs() << "Legal " << FPType << " imm value: " << ImmStrVal - << "\n"); + LLVM_DEBUG(dbgs() << "Legal " << VT.getEVTString() + << " imm value: " << ImmStrVal << "\n"); return true; } - if (!FPType.empty()) - LLVM_DEBUG(dbgs() << "Illegal " << FPType << " imm value: " << ImmStrVal - << "\n"); - else - LLVM_DEBUG(dbgs() << "Illegal fp imm " << ImmStrVal - << ": unsupported fp type\n"); - + LLVM_DEBUG(dbgs() << "Illegal " << VT.getEVTString() + << " imm value: " << ImmStrVal << "\n"); return false; } @@ -5421,6 +5637,8 @@ AArch64TargetLowering::getRegForInlineAsmConstraint( return std::make_pair(0U, &AArch64::GPR64commonRegClass); return std::make_pair(0U, &AArch64::GPR32commonRegClass); case 'w': + if (!Subtarget->hasFPARMv8()) + break; if (VT.getSizeInBits() == 16) return std::make_pair(0U, &AArch64::FPR16RegClass); if (VT.getSizeInBits() == 32) @@ -5433,6 +5651,8 @@ AArch64TargetLowering::getRegForInlineAsmConstraint( // The instructions that this constraint is designed for can // only take 128-bit registers so just use that regclass. case 'x': + if (!Subtarget->hasFPARMv8()) + break; if (VT.getSizeInBits() == 128) return std::make_pair(0U, &AArch64::FPR128_loRegClass); break; @@ -5468,6 +5688,11 @@ AArch64TargetLowering::getRegForInlineAsmConstraint( } } + if (Res.second && !Subtarget->hasFPARMv8() && + !AArch64::GPR32allRegClass.hasSubClassEq(Res.second) && + !AArch64::GPR64allRegClass.hasSubClassEq(Res.second)) + return std::make_pair(0U, nullptr); + return Res; } @@ -6889,10 +7114,19 @@ static SDValue NormalizeBuildVector(SDValue Op, SmallVector<SDValue, 16> Ops; for (SDValue Lane : Op->ops()) { + // For integer vectors, type legalization would have promoted the + // operands already. Otherwise, if Op is a floating-point splat + // (with operands cast to integers), then the only possibilities + // are constants and UNDEFs. if (auto *CstLane = dyn_cast<ConstantSDNode>(Lane)) { APInt LowBits(EltTy.getSizeInBits(), CstLane->getZExtValue()); Lane = DAG.getConstant(LowBits.getZExtValue(), dl, MVT::i32); + } else if (Lane.getNode()->isUndef()) { + Lane = DAG.getUNDEF(MVT::i32); + } else { + assert(Lane.getValueType() == MVT::i32 && + "Unexpected BUILD_VECTOR operand type"); } Ops.push_back(Lane); } @@ -7018,7 +7252,10 @@ SDValue AArch64TargetLowering::LowerBUILD_VECTOR(SDValue Op, return DAG.getUNDEF(VT); } - if (isOnlyLowElement) { + // Convert BUILD_VECTOR where all elements but the lowest are undef into + // SCALAR_TO_VECTOR, except for when we have a single-element constant vector + // as SimplifyDemandedBits will just turn that back into BUILD_VECTOR. + if (isOnlyLowElement && !(NumElts == 1 && isa<ConstantSDNode>(Value))) { LLVM_DEBUG(dbgs() << "LowerBUILD_VECTOR: only low element used, creating 1 " "SCALAR_TO_VECTOR node\n"); return DAG.getNode(ISD::SCALAR_TO_VECTOR, dl, VT, Value); @@ -7600,7 +7837,7 @@ SDValue AArch64TargetLowering::LowerVSETCC(SDValue Op, Cmp = DAG.getSExtOrTrunc(Cmp, dl, Op.getValueType()); if (ShouldInvert) - return Cmp = DAG.getNOT(dl, Cmp, Cmp.getValueType()); + Cmp = DAG.getNOT(dl, Cmp, Cmp.getValueType()); return Cmp; } @@ -7686,8 +7923,10 @@ SDValue AArch64TargetLowering::LowerWindowsDYNAMIC_STACKALLOC( EVT PtrVT = getPointerTy(DAG.getDataLayout()); SDValue Callee = DAG.getTargetExternalSymbol("__chkstk", PtrVT, 0); - const uint32_t *Mask = - Subtarget->getRegisterInfo()->getWindowsStackProbePreservedMask(); + const AArch64RegisterInfo *TRI = Subtarget->getRegisterInfo(); + const uint32_t *Mask = TRI->getWindowsStackProbePreservedMask(); + if (Subtarget->hasCustomCallingConv()) + TRI->UpdateCustomCallPreservedMask(DAG.getMachineFunction(), &Mask); Size = DAG.getNode(ISD::SRL, dl, MVT::i64, Size, DAG.getConstant(4, dl, MVT::i64)); @@ -7795,7 +8034,7 @@ bool AArch64TargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, Info.opc = ISD::INTRINSIC_VOID; // Conservatively set memVT to the entire set of vectors stored. unsigned NumElts = 0; - for (unsigned ArgI = 1, ArgE = I.getNumArgOperands(); ArgI < ArgE; ++ArgI) { + for (unsigned ArgI = 0, ArgE = I.getNumArgOperands(); ArgI < ArgE; ++ArgI) { Type *ArgTy = I.getArgOperand(ArgI)->getType(); if (!ArgTy->isVectorTy()) break; @@ -7859,6 +8098,10 @@ bool AArch64TargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, bool AArch64TargetLowering::shouldReduceLoadWidth(SDNode *Load, ISD::LoadExtType ExtTy, EVT NewVT) const { + // TODO: This may be worth removing. Check regression tests for diffs. + if (!TargetLoweringBase::shouldReduceLoadWidth(Load, ExtTy, NewVT)) + return false; + // If we're reducing the load width in order to avoid having to use an extra // instruction to do extension then it's probably a good idea. if (ExtTy != ISD::NON_EXTLOAD) @@ -8316,27 +8559,30 @@ EVT AArch64TargetLowering::getOptimalMemOpType(uint64_t Size, unsigned DstAlign, bool ZeroMemset, bool MemcpyStrSrc, MachineFunction &MF) const { - // Don't use AdvSIMD to implement 16-byte memset. It would have taken one - // instruction to materialize the v2i64 zero and one store (with restrictive - // addressing mode). Just do two i64 store of zero-registers. - bool Fast; const Function &F = MF.getFunction(); - if (Subtarget->hasFPARMv8() && !IsMemset && Size >= 16 && - !F.hasFnAttribute(Attribute::NoImplicitFloat) && - (memOpAlign(SrcAlign, DstAlign, 16) || - (allowsMisalignedMemoryAccesses(MVT::f128, 0, 1, &Fast) && Fast))) - return MVT::f128; + bool CanImplicitFloat = !F.hasFnAttribute(Attribute::NoImplicitFloat); + bool CanUseNEON = Subtarget->hasNEON() && CanImplicitFloat; + bool CanUseFP = Subtarget->hasFPARMv8() && CanImplicitFloat; + // Only use AdvSIMD to implement memset of 32-byte and above. It would have + // taken one instruction to materialize the v2i64 zero and one store (with + // restrictive addressing mode). Just do i64 stores. + bool IsSmallMemset = IsMemset && Size < 32; + auto AlignmentIsAcceptable = [&](EVT VT, unsigned AlignCheck) { + if (memOpAlign(SrcAlign, DstAlign, AlignCheck)) + return true; + bool Fast; + return allowsMisalignedMemoryAccesses(VT, 0, 1, &Fast) && Fast; + }; - if (Size >= 8 && - (memOpAlign(SrcAlign, DstAlign, 8) || - (allowsMisalignedMemoryAccesses(MVT::i64, 0, 1, &Fast) && Fast))) + if (CanUseNEON && IsMemset && !IsSmallMemset && + AlignmentIsAcceptable(MVT::v2i64, 16)) + return MVT::v2i64; + if (CanUseFP && !IsSmallMemset && AlignmentIsAcceptable(MVT::f128, 16)) + return MVT::f128; + if (Size >= 8 && AlignmentIsAcceptable(MVT::i64, 8)) return MVT::i64; - - if (Size >= 4 && - (memOpAlign(SrcAlign, DstAlign, 4) || - (allowsMisalignedMemoryAccesses(MVT::i32, 0, 1, &Fast) && Fast))) + if (Size >= 4 && AlignmentIsAcceptable(MVT::i32, 4)) return MVT::i32; - return MVT::Other; } @@ -8464,7 +8710,9 @@ AArch64TargetLowering::getScratchRegisters(CallingConv::ID) const { } bool -AArch64TargetLowering::isDesirableToCommuteWithShift(const SDNode *N) const { +AArch64TargetLowering::isDesirableToCommuteWithShift(const SDNode *N, + CombineLevel Level) const { + N = N->getOperand(0).getNode(); EVT VT = N->getValueType(0); // If N is unsigned bit extraction: ((x >> C) & mask), then do not combine // it with shift to let it be lowered to UBFX. @@ -9685,10 +9933,10 @@ static SDValue performIntrinsicCombine(SDNode *N, case Intrinsic::aarch64_neon_umaxv: return combineAcrossLanesIntrinsic(AArch64ISD::UMAXV, N, DAG); case Intrinsic::aarch64_neon_fmax: - return DAG.getNode(ISD::FMAXNAN, SDLoc(N), N->getValueType(0), + return DAG.getNode(ISD::FMAXIMUM, SDLoc(N), N->getValueType(0), N->getOperand(1), N->getOperand(2)); case Intrinsic::aarch64_neon_fmin: - return DAG.getNode(ISD::FMINNAN, SDLoc(N), N->getValueType(0), + return DAG.getNode(ISD::FMINIMUM, SDLoc(N), N->getValueType(0), N->getOperand(1), N->getOperand(2)); case Intrinsic::aarch64_neon_fmaxnm: return DAG.getNode(ISD::FMAXNUM, SDLoc(N), N->getValueType(0), @@ -9817,6 +10065,7 @@ static SDValue performExtendCombine(SDNode *N, static SDValue splitStoreSplat(SelectionDAG &DAG, StoreSDNode &St, SDValue SplatVal, unsigned NumVecElts) { + assert(!St.isTruncatingStore() && "cannot split truncating vector store"); unsigned OrigAlignment = St.getAlignment(); unsigned EltOffset = SplatVal.getValueType().getSizeInBits() / 8; @@ -9891,6 +10140,11 @@ static SDValue replaceZeroVectorStore(SelectionDAG &DAG, StoreSDNode &St) { if (!StVal.hasOneUse()) return SDValue(); + // If the store is truncating then it's going down to i16 or smaller, which + // means it can be implemented in a single store anyway. + if (St.isTruncatingStore()) + return SDValue(); + // If the immediate offset of the address operand is too large for the stp // instruction, then bail out. if (DAG.isBaseWithConstantOffset(St.getBasePtr())) { @@ -9941,6 +10195,11 @@ static SDValue replaceSplatVectorStore(SelectionDAG &DAG, StoreSDNode &St) { if (NumVecElts != 4 && NumVecElts != 2) return SDValue(); + // If the store is truncating then it's going down to i16 or smaller, which + // means it can be implemented in a single store anyway. + if (St.isTruncatingStore()) + return SDValue(); + // Check that this is a splat. // Make sure that each of the relevant vector element locations are inserted // to, i.e. 0 and 1 for v2i64 and 0, 1, 2, 3 for v4i32. @@ -10097,15 +10356,6 @@ static SDValue performPostLD1Combine(SDNode *N, || UI.getUse().getResNo() != Addr.getResNo()) continue; - // Check that the add is independent of the load. Otherwise, folding it - // would create a cycle. - if (User->isPredecessorOf(LD) || LD->isPredecessorOf(User)) - continue; - // Also check that add is not used in the vector operand. This would also - // create a cycle. - if (User->isPredecessorOf(Vector.getNode())) - continue; - // If the increment is a constant, it must match the memory ref size. SDValue Inc = User->getOperand(User->getOperand(0) == Addr ? 1 : 0); if (ConstantSDNode *CInc = dyn_cast<ConstantSDNode>(Inc.getNode())) { @@ -10116,11 +10366,16 @@ static SDValue performPostLD1Combine(SDNode *N, Inc = DAG.getRegister(AArch64::XZR, MVT::i64); } - // Finally, check that the vector doesn't depend on the load. - // Again, this would create a cycle. - // The load depending on the vector is fine, as that's the case for the - // LD1*post we'll eventually generate anyway. - if (LoadSDN->isPredecessorOf(Vector.getNode())) + // To avoid cycle construction make sure that neither the load nor the add + // are predecessors to each other or the Vector. + SmallPtrSet<const SDNode *, 32> Visited; + SmallVector<const SDNode *, 16> Worklist; + Visited.insert(N); + Worklist.push_back(User); + Worklist.push_back(LD); + Worklist.push_back(Vector.getNode()); + if (SDNode::hasPredecessorHelper(LD, Visited, Worklist) || + SDNode::hasPredecessorHelper(User, Visited, Worklist)) continue; SmallVector<SDValue, 8> Ops; @@ -10206,7 +10461,13 @@ static SDValue performNEONPostLDSTCombine(SDNode *N, // Check that the add is independent of the load/store. Otherwise, folding // it would create a cycle. - if (User->isPredecessorOf(N) || N->isPredecessorOf(User)) + SmallPtrSet<const SDNode *, 32> Visited; + SmallVector<const SDNode *, 16> Worklist; + Visited.insert(Addr.getNode()); + Worklist.push_back(N); + Worklist.push_back(User); + if (SDNode::hasPredecessorHelper(N, Visited, Worklist) || + SDNode::hasPredecessorHelper(User, Visited, Worklist)) continue; // Find the new opcode for the updating load/store. @@ -10576,6 +10837,13 @@ SDValue performCONDCombine(SDNode *N, static SDValue performBRCONDCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI, SelectionDAG &DAG) { + MachineFunction &MF = DAG.getMachineFunction(); + // Speculation tracking/SLH assumes that optimized TB(N)Z/CB(N)Z instructions + // will not be produced, as they are conditional branch instructions that do + // not set flags. + if (MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening)) + return SDValue(); + if (SDValue NV = performCONDCombine(N, DCI, DAG, 2, 3)) N = NV.getNode(); SDValue Chain = N->getOperand(0); @@ -10833,9 +11101,9 @@ static SDValue performNVCASTCombine(SDNode *N) { static SDValue performGlobalAddressCombine(SDNode *N, SelectionDAG &DAG, const AArch64Subtarget *Subtarget, const TargetMachine &TM) { - auto *GN = dyn_cast<GlobalAddressSDNode>(N); - if (!GN || Subtarget->ClassifyGlobalReference(GN->getGlobal(), TM) != - AArch64II::MO_NO_FLAG) + auto *GN = cast<GlobalAddressSDNode>(N); + if (Subtarget->ClassifyGlobalReference(GN->getGlobal(), TM) != + AArch64II::MO_NO_FLAG) return SDValue(); uint64_t MinOffset = -1ull; @@ -10967,6 +11235,7 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N, default: break; } + break; case ISD::GlobalAddress: return performGlobalAddressCombine(N, DAG, Subtarget, getTargetMachine()); } @@ -11164,12 +11433,10 @@ static void ReplaceCMP_SWAP_128Results(SDNode *N, N->getOperand(0), // Chain in }; - MachineFunction &MF = DAG.getMachineFunction(); - MachineSDNode::mmo_iterator MemOp = MF.allocateMemRefsArray(1); - MemOp[0] = cast<MemSDNode>(N)->getMemOperand(); + MachineMemOperand *MemOp = cast<MemSDNode>(N)->getMemOperand(); unsigned Opcode; - switch (MemOp[0]->getOrdering()) { + switch (MemOp->getOrdering()) { case AtomicOrdering::Monotonic: Opcode = AArch64::CASPX; break; @@ -11189,7 +11456,7 @@ static void ReplaceCMP_SWAP_128Results(SDNode *N, MachineSDNode *CmpSwap = DAG.getMachineNode( Opcode, SDLoc(N), DAG.getVTList(MVT::Untyped, MVT::Other), Ops); - CmpSwap->setMemRefs(MemOp, MemOp + 1); + DAG.setNodeMemRefs(CmpSwap, {MemOp}); unsigned SubReg1 = AArch64::sube64, SubReg2 = AArch64::subo64; if (DAG.getDataLayout().isBigEndian()) @@ -11210,10 +11477,8 @@ static void ReplaceCMP_SWAP_128Results(SDNode *N, AArch64::CMP_SWAP_128, SDLoc(N), DAG.getVTList(MVT::i64, MVT::i64, MVT::i32, MVT::Other), Ops); - MachineFunction &MF = DAG.getMachineFunction(); - MachineSDNode::mmo_iterator MemOp = MF.allocateMemRefsArray(1); - MemOp[0] = cast<MemSDNode>(N)->getMemOperand(); - cast<MachineSDNode>(CmpSwap)->setMemRefs(MemOp, MemOp + 1); + MachineMemOperand *MemOp = cast<MemSDNode>(N)->getMemOperand(); + DAG.setNodeMemRefs(cast<MachineSDNode>(CmpSwap), {MemOp}); Results.push_back(SDValue(CmpSwap, 0)); Results.push_back(SDValue(CmpSwap, 1)); @@ -11278,12 +11543,11 @@ unsigned AArch64TargetLowering::combineRepeatedFPDivisors() const { } TargetLoweringBase::LegalizeTypeAction -AArch64TargetLowering::getPreferredVectorAction(EVT VT) const { - MVT SVT = VT.getSimpleVT(); +AArch64TargetLowering::getPreferredVectorAction(MVT VT) const { // During type legalization, we prefer to widen v1i8, v1i16, v1i32 to v8i8, // v4i16, v2i32 instead of to promote. - if (SVT == MVT::v1i8 || SVT == MVT::v1i16 || SVT == MVT::v1i32 - || SVT == MVT::v1f32) + if (VT == MVT::v1i8 || VT == MVT::v1i16 || VT == MVT::v1i32 || + VT == MVT::v1f32) return TypeWidenVector; return TargetLoweringBase::getPreferredVectorAction(VT); @@ -11317,16 +11581,20 @@ AArch64TargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { return (Subtarget->hasLSE() && Size < 128) ? AtomicExpansionKind::None : AtomicExpansionKind::LLSC; } -bool AArch64TargetLowering::shouldExpandAtomicCmpXchgInIR( +TargetLowering::AtomicExpansionKind +AArch64TargetLowering::shouldExpandAtomicCmpXchgInIR( AtomicCmpXchgInst *AI) const { // If subtarget has LSE, leave cmpxchg intact for codegen. - if (Subtarget->hasLSE()) return false; + if (Subtarget->hasLSE()) + return AtomicExpansionKind::None; // At -O0, fast-regalloc cannot cope with the live vregs necessary to // implement cmpxchg without spilling. If the address being exchanged 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 we need a late-expanded pseudo-inst instead. - return getTargetMachine().getOptLevel() != 0; + if (getTargetMachine().getOptLevel() == 0) + return AtomicExpansionKind::None; + return AtomicExpansionKind::LLSC; } Value *AArch64TargetLowering::emitLoadLinked(IRBuilder<> &Builder, Value *Addr, @@ -11436,6 +11704,39 @@ Value *AArch64TargetLowering::getIRStackGuard(IRBuilder<> &IRB) const { return TargetLowering::getIRStackGuard(IRB); } +void AArch64TargetLowering::insertSSPDeclarations(Module &M) const { + // MSVC CRT provides functionalities for stack protection. + if (Subtarget->getTargetTriple().isWindowsMSVCEnvironment()) { + // MSVC CRT has a global variable holding security cookie. + M.getOrInsertGlobal("__security_cookie", + Type::getInt8PtrTy(M.getContext())); + + // MSVC CRT has a function to validate security cookie. + auto *SecurityCheckCookie = cast<Function>( + M.getOrInsertFunction("__security_check_cookie", + Type::getVoidTy(M.getContext()), + Type::getInt8PtrTy(M.getContext()))); + SecurityCheckCookie->setCallingConv(CallingConv::Win64); + SecurityCheckCookie->addAttribute(1, Attribute::AttrKind::InReg); + return; + } + TargetLowering::insertSSPDeclarations(M); +} + +Value *AArch64TargetLowering::getSDagStackGuard(const Module &M) const { + // MSVC CRT has a global variable holding security cookie. + if (Subtarget->getTargetTriple().isWindowsMSVCEnvironment()) + return M.getGlobalVariable("__security_cookie"); + return TargetLowering::getSDagStackGuard(M); +} + +Value *AArch64TargetLowering::getSSPStackGuardCheck(const Module &M) const { + // MSVC CRT has a function to validate security cookie. + if (Subtarget->getTargetTriple().isWindowsMSVCEnvironment()) + return M.getFunction("__security_check_cookie"); + return TargetLowering::getSSPStackGuardCheck(M); +} + Value *AArch64TargetLowering::getSafeStackPointerLocation(IRBuilder<> &IRB) const { // Android provides a fixed TLS slot for the SafeStack pointer. See the // definition of TLS_SLOT_SAFESTACK in @@ -11540,3 +11841,8 @@ void AArch64TargetLowering::finalizeLowering(MachineFunction &MF) const { MF.getFrameInfo().computeMaxCallFrameSize(MF); TargetLoweringBase::finalizeLowering(MF); } + +// Unlike X86, we let frame lowering assign offsets to all catch objects. +bool AArch64TargetLowering::needsFixedCatchObjects() const { + return false; +} diff --git a/lib/Target/AArch64/AArch64ISelLowering.h b/lib/Target/AArch64/AArch64ISelLowering.h index d783c8a6048c..ffc4cc3ef534 100644 --- a/lib/Target/AArch64/AArch64ISelLowering.h +++ b/lib/Target/AArch64/AArch64ISelLowering.h @@ -35,6 +35,7 @@ enum NodeType : unsigned { // offset of a variable into X0, using the TLSDesc model. TLSDESC_CALLSEQ, ADRP, // Page address of a TargetGlobalAddress operand. + ADR, // ADR ADDlow, // Add the low 12 bits of a TargetGlobalAddress operand. LOADgot, // Load from automatically generated descriptor (e.g. Global // Offset Table, TLS record). @@ -301,6 +302,12 @@ public: MachineBasicBlock *EmitF128CSEL(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCatchRet(MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *EmitLoweredCatchPad(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; @@ -363,7 +370,8 @@ public: const MCPhysReg *getScratchRegisters(CallingConv::ID CC) const override; /// Returns false if N is a bit extraction pattern of (X >> C) & Mask. - bool isDesirableToCommuteWithShift(const SDNode *N) const override; + bool isDesirableToCommuteWithShift(const SDNode *N, + CombineLevel Level) const override; /// Returns true if it is beneficial to convert a load of a constant /// to just the constant itself. @@ -388,16 +396,21 @@ public: TargetLoweringBase::AtomicExpansionKind shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const override; - bool shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *AI) const override; + TargetLoweringBase::AtomicExpansionKind + shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *AI) const override; bool useLoadStackGuardNode() const override; TargetLoweringBase::LegalizeTypeAction - getPreferredVectorAction(EVT VT) const override; + getPreferredVectorAction(MVT VT) const override; /// If the target has a standard location for the stack protector cookie, /// returns the address of that location. Otherwise, returns nullptr. Value *getIRStackGuard(IRBuilder<> &IRB) const override; + void insertSSPDeclarations(Module &M) const override; + Value *getSDagStackGuard(const Module &M) const override; + Value *getSSPStackGuardCheck(const Module &M) const override; + /// If the target has a standard location for the unsafe stack pointer, /// returns the address of that location. Otherwise, returns nullptr. Value *getSafeStackPointerLocation(IRBuilder<> &IRB) const override; @@ -514,6 +527,8 @@ public: bool functionArgumentNeedsConsecutiveRegisters(Type *Ty, CallingConv::ID CallConv, bool isVarArg) const override; + /// Used for exception handling on Win64. + bool needsFixedCatchObjects() const override; private: /// Keep a pointer to the AArch64Subtarget around so that we can /// make the right decision when generating code for different targets. @@ -586,6 +601,8 @@ private: SDValue getAddrLarge(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; template <class NodeTy> SDValue getAddr(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; + template <class NodeTy> + SDValue getAddrTiny(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; SDValue LowerADDROFRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; @@ -602,6 +619,7 @@ private: SDValue TVal, SDValue FVal, const SDLoc &dl, SelectionDAG &DAG) const; SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerAAPCS_VASTART(SDValue Op, SelectionDAG &DAG) const; @@ -611,6 +629,7 @@ private: SDValue LowerVACOPY(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSPONENTRY(SDValue Op, SelectionDAG &DAG) const; SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFLT_ROUNDS_(SDValue Op, SelectionDAG &DAG) const; SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; diff --git a/lib/Target/AArch64/AArch64InstrFormats.td b/lib/Target/AArch64/AArch64InstrFormats.td index 15d61cd1ad26..9061ed4f9f54 100644 --- a/lib/Target/AArch64/AArch64InstrFormats.td +++ b/lib/Target/AArch64/AArch64InstrFormats.td @@ -263,6 +263,14 @@ class SImmOperand<int width> : AsmOperandClass { let PredicateMethod = "isSImm<" # width # ">"; } + +class AsmImmRange<int Low, int High> : AsmOperandClass { + let Name = "Imm" # Low # "_" # High; + let DiagnosticType = "InvalidImm" # Low # "_" # High; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isImmInRange<" # Low # "," # High # ">"; +} + // Authenticated loads for v8.3 can have scaled 10-bit immediate offsets. def SImm10s8Operand : SImmScaledMemoryIndexed<10, 8>; def simm10Scaled : Operand<i64> { @@ -271,6 +279,12 @@ def simm10Scaled : Operand<i64> { let PrintMethod = "printImmScale<8>"; } +def simm9s16 : Operand<i64> { + let ParserMatchClass = SImmScaledMemoryIndexed<9, 16>; + let DecoderMethod = "DecodeSImm<9>"; + let PrintMethod = "printImmScale<16>"; +} + // uimm6 predicate - True if the immediate is in the range [0, 63]. def UImm6Operand : AsmOperandClass { let Name = "UImm6"; @@ -281,6 +295,10 @@ def uimm6 : Operand<i64>, ImmLeaf<i64, [{ return Imm >= 0 && Imm < 64; }]> { let ParserMatchClass = UImm6Operand; } +def uimm16 : Operand<i16>, ImmLeaf<i16, [{return Imm >= 0 && Imm < 65536;}]>{ + let ParserMatchClass = AsmImmRange<0, 65535>; +} + def SImm9Operand : SImmOperand<9>; def simm9 : Operand<i64>, ImmLeaf<i64, [{ return Imm >= -256 && Imm < 256; }]> { let ParserMatchClass = SImm9Operand; @@ -366,6 +384,7 @@ def UImm6s1Operand : UImmScaledMemoryIndexed<6, 1>; def UImm6s2Operand : UImmScaledMemoryIndexed<6, 2>; def UImm6s4Operand : UImmScaledMemoryIndexed<6, 4>; def UImm6s8Operand : UImmScaledMemoryIndexed<6, 8>; +def UImm6s16Operand : UImmScaledMemoryIndexed<6, 16>; def uimm6s1 : Operand<i64>, ImmLeaf<i64, [{ return Imm >= 0 && Imm < 64; }]> { let ParserMatchClass = UImm6s1Operand; @@ -385,6 +404,11 @@ def uimm6s8 : Operand<i64>, ImmLeaf<i64, let PrintMethod = "printImmScale<8>"; let ParserMatchClass = UImm6s8Operand; } +def uimm6s16 : Operand<i64>, ImmLeaf<i64, +[{ return Imm >= 0 && Imm < (64*16) && ((Imm % 16) == 0); }]> { + let PrintMethod = "printImmScale<16>"; + let ParserMatchClass = UImm6s16Operand; +} // simm6sN predicate - True if the immediate is a multiple of N in the range // [-32 * N, 31 * N]. @@ -435,13 +459,6 @@ def simm4s16 : Operand<i64>, ImmLeaf<i64, let DecoderMethod = "DecodeSImm<4>"; } -class AsmImmRange<int Low, int High> : AsmOperandClass { - let Name = "Imm" # Low # "_" # High; - let DiagnosticType = "InvalidImm" # Low # "_" # High; - let RenderMethod = "addImmOperands"; - let PredicateMethod = "isImmInRange<" # Low # "," # High # ">"; -} - def Imm1_8Operand : AsmImmRange<1, 8>; def Imm1_16Operand : AsmImmRange<1, 16>; def Imm1_32Operand : AsmImmRange<1, 32>; @@ -696,11 +713,10 @@ def logical_imm64_not : Operand<i64> { } // imm0_65535 predicate - True if the immediate is in the range [0,65535]. -def Imm0_65535Operand : AsmImmRange<0, 65535>; def imm0_65535 : Operand<i32>, ImmLeaf<i32, [{ return ((uint32_t)Imm) < 65536; }]> { - let ParserMatchClass = Imm0_65535Operand; + let ParserMatchClass = AsmImmRange<0, 65535>; let PrintMethod = "printImmHex"; } @@ -1149,6 +1165,21 @@ def psbhint_op : Operand<i32> { }]; } +def BTIHintOperand : AsmOperandClass { + let Name = "BTIHint"; + let ParserMethod = "tryParseBTIHint"; +} +def btihint_op : Operand<i32> { + let ParserMatchClass = BTIHintOperand; + let PrintMethod = "printBTIHintOp"; + let MCOperandPredicate = [{ + // "bti" is an alias to "hint" only for certain values of CRm:Op2 fields. + if (!MCOp.isImm()) + return false; + return AArch64BTIHint::lookupBTIByEncoding((MCOp.getImm() ^ 32) >> 1) != nullptr; + }]; +} + class MRSI : RtSystemI<1, (outs GPR64:$Rt), (ins mrs_sysreg_op:$systemreg), "mrs", "\t$Rt, $systemreg"> { bits<16> systemreg; @@ -1173,16 +1204,23 @@ def pstatefield4_op : Operand<i32> { let PrintMethod = "printSystemPStateField"; } +// Instructions to modify PSTATE, no input reg let Defs = [NZCV] in +class PstateWriteSimple<dag iops, string asm, string operands> + : SimpleSystemI<0, iops, asm, operands> { + + let Inst{20-19} = 0b00; + let Inst{15-12} = 0b0100; +} + class MSRpstateImm0_15 - : SimpleSystemI<0, (ins pstatefield4_op:$pstatefield, imm0_15:$imm), - "msr", "\t$pstatefield, $imm">, + : PstateWriteSimple<(ins pstatefield4_op:$pstatefield, imm0_15:$imm), "msr", + "\t$pstatefield, $imm">, Sched<[WriteSys]> { + bits<6> pstatefield; bits<4> imm; - let Inst{20-19} = 0b00; let Inst{18-16} = pstatefield{5-3}; - let Inst{15-12} = 0b0100; let Inst{11-8} = imm; let Inst{7-5} = pstatefield{2-0}; @@ -1201,16 +1239,15 @@ def pstatefield1_op : Operand<i32> { let PrintMethod = "printSystemPStateField"; } -let Defs = [NZCV] in class MSRpstateImm0_1 - : SimpleSystemI<0, (ins pstatefield1_op:$pstatefield, imm0_1:$imm), - "msr", "\t$pstatefield, $imm">, + : PstateWriteSimple<(ins pstatefield1_op:$pstatefield, imm0_1:$imm), "msr", + "\t$pstatefield, $imm">, Sched<[WriteSys]> { + bits<6> pstatefield; bit imm; - let Inst{20-19} = 0b00; let Inst{18-16} = pstatefield{5-3}; - let Inst{15-9} = 0b0100000; + let Inst{11-9} = 0b000; let Inst{8} = imm; let Inst{7-5} = pstatefield{2-0}; @@ -1719,10 +1756,12 @@ multiclass AddSubCarry<bit isSub, string asm, string asm_setflags, } class BaseTwoOperand<bits<4> opc, RegisterClass regtype, string asm, - SDPatternOperator OpNode> - : I<(outs regtype:$Rd), (ins regtype:$Rn, regtype:$Rm), + SDPatternOperator OpNode, + RegisterClass in1regtype = regtype, + RegisterClass in2regtype = regtype> + : I<(outs regtype:$Rd), (ins in1regtype:$Rn, in2regtype:$Rm), asm, "\t$Rd, $Rn, $Rm", "", - [(set regtype:$Rd, (OpNode regtype:$Rn, regtype:$Rm))]> { + [(set regtype:$Rd, (OpNode in1regtype:$Rn, in2regtype:$Rm))]> { bits<5> Rd; bits<5> Rn; bits<5> Rm; @@ -1902,7 +1941,7 @@ class ADRI<bit page, string asm, Operand adr, list<dag> pattern> //--- def movimm32_imm : Operand<i32> { - let ParserMatchClass = Imm0_65535Operand; + let ParserMatchClass = AsmImmRange<0, 65535>; let EncoderMethod = "getMoveWideImmOpValue"; let PrintMethod = "printImm"; } @@ -1977,23 +2016,29 @@ multiclass InsertImmediate<bits<2> opc, string asm> { //--- class BaseAddSubImm<bit isSub, bit setFlags, RegisterClass dstRegtype, - RegisterClass srcRegtype, addsub_shifted_imm immtype, - string asm, SDPatternOperator OpNode> - : I<(outs dstRegtype:$Rd), (ins srcRegtype:$Rn, immtype:$imm), - asm, "\t$Rd, $Rn, $imm", "", - [(set dstRegtype:$Rd, (OpNode srcRegtype:$Rn, immtype:$imm))]>, - Sched<[WriteI, ReadI]> { + string asm_inst, string asm_ops, + dag inputs, dag pattern> + : I<(outs dstRegtype:$Rd), inputs, asm_inst, asm_ops, "", [pattern]>, + Sched<[WriteI, ReadI]> { bits<5> Rd; bits<5> Rn; - bits<14> imm; let Inst{30} = isSub; let Inst{29} = setFlags; let Inst{28-24} = 0b10001; - let Inst{23-22} = imm{13-12}; // '00' => lsl #0, '01' => lsl #12 - let Inst{21-10} = imm{11-0}; let Inst{9-5} = Rn; let Inst{4-0} = Rd; - let DecoderMethod = "DecodeBaseAddSubImm"; +} + +class AddSubImmShift<bit isSub, bit setFlags, RegisterClass dstRegtype, + RegisterClass srcRegtype, addsub_shifted_imm immtype, + string asm_inst, SDPatternOperator OpNode> + : BaseAddSubImm<isSub, setFlags, dstRegtype, asm_inst, "\t$Rd, $Rn, $imm", + (ins srcRegtype:$Rn, immtype:$imm), + (set dstRegtype:$Rd, (OpNode srcRegtype:$Rn, immtype:$imm))> { + bits<14> imm; + let Inst{23-22} = imm{13-12}; // '00' => lsl #0, '01' => lsl #12 + let Inst{21-10} = imm{11-0}; + let DecoderMethod = "DecodeAddSubImmShift"; } class BaseAddSubRegPseudo<RegisterClass regtype, @@ -2097,12 +2142,12 @@ multiclass AddSub<bit isSub, string mnemonic, string alias, // We used to match the register variant before the immediate when the // register argument could be implicitly zero-extended. let AddedComplexity = 6 in - def Wri : BaseAddSubImm<isSub, 0, GPR32sp, GPR32sp, addsub_shifted_imm32, + def Wri : AddSubImmShift<isSub, 0, GPR32sp, GPR32sp, addsub_shifted_imm32, mnemonic, OpNode> { let Inst{31} = 0; } let AddedComplexity = 6 in - def Xri : BaseAddSubImm<isSub, 0, GPR64sp, GPR64sp, addsub_shifted_imm64, + def Xri : AddSubImmShift<isSub, 0, GPR64sp, GPR64sp, addsub_shifted_imm64, mnemonic, OpNode> { let Inst{31} = 1; } @@ -2173,11 +2218,11 @@ multiclass AddSubS<bit isSub, string mnemonic, SDNode OpNode, string cmp, string alias, string cmpAlias> { let isCompare = 1, Defs = [NZCV] in { // Add/Subtract immediate - def Wri : BaseAddSubImm<isSub, 1, GPR32, GPR32sp, addsub_shifted_imm32, + def Wri : AddSubImmShift<isSub, 1, GPR32, GPR32sp, addsub_shifted_imm32, mnemonic, OpNode> { let Inst{31} = 0; } - def Xri : BaseAddSubImm<isSub, 1, GPR64, GPR64sp, addsub_shifted_imm64, + def Xri : AddSubImmShift<isSub, 1, GPR64, GPR64sp, addsub_shifted_imm64, mnemonic, OpNode> { let Inst{31} = 1; } @@ -2271,6 +2316,27 @@ multiclass AddSubS<bit isSub, string mnemonic, SDNode OpNode, string cmp, GPR64, GPR64sponly, GPR64, 24>; // UXTX #0 } +class AddSubG<bit isSub, string asm_inst, SDPatternOperator OpNode> + : BaseAddSubImm< + isSub, 0, GPR64sp, asm_inst, "\t$Rd, $Rn, $imm6, $imm4", + (ins GPR64sp:$Rn, uimm6s16:$imm6, imm0_15:$imm4), + (set GPR64sp:$Rd, (OpNode GPR64sp:$Rn, imm0_63:$imm6, imm0_15:$imm4))> { + bits<6> imm6; + bits<4> imm4; + let Inst{31} = 1; + let Inst{23-22} = 0b10; + let Inst{21-16} = imm6; + let Inst{15-14} = 0b00; + let Inst{13-10} = imm4; + let Unpredictable{15-14} = 0b11; +} + +class SUBP<bit setsFlags, string asm_instr, SDPatternOperator OpNode> + : BaseTwoOperand<0b0000, GPR64, asm_instr, null_frag, GPR64sp, GPR64sp> { + let Inst{31} = 1; + let Inst{29} = setsFlags; +} + //--- // Extract //--- @@ -2853,10 +2919,10 @@ def am_ldrlit : Operand<iPTR> { let OperandType = "OPERAND_PCREL"; } -let mayLoad = 1, mayStore = 0, hasSideEffects = 0 in -class LoadLiteral<bits<2> opc, bit V, RegisterOperand regtype, string asm> +let mayLoad = 1, mayStore = 0, hasSideEffects = 0, AddedComplexity = 20 in +class LoadLiteral<bits<2> opc, bit V, RegisterOperand regtype, string asm, list<dag> pat> : I<(outs regtype:$Rt), (ins am_ldrlit:$label), - asm, "\t$Rt, $label", "", []>, + asm, "\t$Rt, $label", "", pat>, Sched<[WriteLD]> { bits<5> Rt; bits<19> label; @@ -3932,6 +3998,78 @@ class StoreExclusivePair<bits<2> sz, bit o2, bit L, bit o1, bit o0, let Constraints = "@earlyclobber $Ws"; } +// Armv8.5-A Memory Tagging Extension +class BaseMemTag<bits<2> opc1, bits<2> opc2, string asm_insn, + string asm_opnds, string cstr, dag oops, dag iops> + : I<oops, iops, asm_insn, asm_opnds, cstr, []>, + Sched<[]> { + bits<5> Rn; + + let Inst{31-24} = 0b11011001; + let Inst{23-22} = opc1; + let Inst{21} = 1; + // Inst{20-12} defined by subclass + let Inst{11-10} = opc2; + let Inst{9-5} = Rn; + // Inst{4-0} defined by subclass +} + +class MemTagVector<bit Load, string asm_insn, string asm_opnds, + dag oops, dag iops> + : BaseMemTag<{0b1, Load}, 0b00, asm_insn, asm_opnds, + "$Rn = $wback,@earlyclobber $wback", oops, iops> { + bits<5> Rt; + + let Inst{20-12} = 0b000000000; + let Inst{4-0} = Rt; + + let mayLoad = Load; +} + +class MemTagLoad<string asm_insn, string asm_opnds> + : BaseMemTag<0b01, 0b00, asm_insn, asm_opnds, "", (outs GPR64:$Rt), + (ins GPR64sp:$Rn, simm9s16:$offset)> { + bits<5> Rt; + bits<9> offset; + + let Inst{20-12} = offset; + let Inst{4-0} = Rt; + + let mayLoad = 1; +} + +class BaseMemTagStore<bits<2> opc1, bits<2> opc2, string asm_insn, + string asm_opnds, string cstr, dag oops, dag iops> + : BaseMemTag<opc1, opc2, asm_insn, asm_opnds, cstr, oops, iops> { + bits<5> Rt; + bits<9> offset; + + let Inst{20-12} = offset; + let Inst{4-0} = 0b11111; + let Unpredictable{4-0} = 0b11111; + + let mayStore = 1; +} + +multiclass MemTagStore<bits<2> opc1, string insn> { + def Offset : + BaseMemTagStore<opc1, 0b10, insn, "\t[$Rn, $offset]", "", + (outs), (ins GPR64sp:$Rn, simm9s16:$offset)>; + def PreIndex : + BaseMemTagStore<opc1, 0b11, insn, "\t[$Rn, $offset]!", + "$Rn = $wback,@earlyclobber $wback", + (outs GPR64sp:$wback), + (ins GPR64sp:$Rn, simm9s16:$offset)>; + def PostIndex : + BaseMemTagStore<opc1, 0b01, insn, "\t[$Rn], $offset", + "$Rn = $wback,@earlyclobber $wback", + (outs GPR64sp:$wback), + (ins GPR64sp:$Rn, simm9s16:$offset)>; + + def : InstAlias<insn # "\t[$Rn]", + (!cast<Instruction>(NAME # "Offset") GPR64sp:$Rn, 0)>; +} + //--- // Exception generation //--- @@ -3948,6 +4086,19 @@ class ExceptionGeneration<bits<3> op1, bits<2> ll, string asm> let Inst{1-0} = ll; } +//--- +// UDF : Permanently UNDEFINED instructions. Format: Opc = 0x0000, 16 bit imm. +//-- +let hasSideEffects = 1, isTrap = 1, mayLoad = 0, mayStore = 0 in { +class UDFType<bits<16> opc, string asm> + : I<(outs), (ins uimm16:$imm), + asm, "\t$imm", "", []>, + Sched<[]> { + bits<16> imm; + let Inst{31-16} = opc; + let Inst{15-0} = imm; +} +} let Predicates = [HasFPARMv8] in { //--- @@ -4395,7 +4546,7 @@ multiclass FPConversion<string asm> { //--- let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in -class BaseSingleOperandFPData<bits<4> opcode, RegisterClass regtype, +class BaseSingleOperandFPData<bits<6> opcode, RegisterClass regtype, ValueType vt, string asm, SDPatternOperator node> : I<(outs regtype:$Rd), (ins regtype:$Rn), asm, "\t$Rd, $Rn", "", [(set (vt regtype:$Rd), (node (vt regtype:$Rn)))]>, @@ -4403,8 +4554,8 @@ class BaseSingleOperandFPData<bits<4> opcode, RegisterClass regtype, bits<5> Rd; bits<5> Rn; let Inst{31-24} = 0b00011110; - let Inst{21-19} = 0b100; - let Inst{18-15} = opcode; + let Inst{21} = 0b1; + let Inst{20-15} = opcode; let Inst{14-10} = 0b10000; let Inst{9-5} = Rn; let Inst{4-0} = Rd; @@ -4412,20 +4563,37 @@ class BaseSingleOperandFPData<bits<4> opcode, RegisterClass regtype, multiclass SingleOperandFPData<bits<4> opcode, string asm, SDPatternOperator node = null_frag> { - def Hr : BaseSingleOperandFPData<opcode, FPR16, f16, asm, node> { + + def Hr : BaseSingleOperandFPData<{0b00,opcode}, FPR16, f16, asm, node> { let Inst{23-22} = 0b11; // 16-bit size flag let Predicates = [HasFullFP16]; } - def Sr : BaseSingleOperandFPData<opcode, FPR32, f32, asm, node> { + def Sr : BaseSingleOperandFPData<{0b00,opcode}, FPR32, f32, asm, node> { let Inst{23-22} = 0b00; // 32-bit size flag } - def Dr : BaseSingleOperandFPData<opcode, FPR64, f64, asm, node> { + def Dr : BaseSingleOperandFPData<{0b00,opcode}, FPR64, f64, asm, node> { let Inst{23-22} = 0b01; // 64-bit size flag } } +multiclass SingleOperandFPNo16<bits<6> opcode, string asm, + SDPatternOperator node = null_frag>{ + + def Sr : BaseSingleOperandFPData<opcode, FPR32, f32, asm, node> { + let Inst{23-22} = 0b00; // 32-bit registers + } + + def Dr : BaseSingleOperandFPData<opcode, FPR64, f64, asm, node> { + let Inst{23-22} = 0b01; // 64-bit registers + } +} + +// FRInt[32|64][Z|N] instructions +multiclass FRIntNNT<bits<2> opcode, string asm, SDPatternOperator node = null_frag> : + SingleOperandFPNo16<{0b0100,opcode}, asm, node>; + //--- // Two operand floating point data processing //--- @@ -4639,7 +4807,9 @@ class BaseFPCondComparison<bit signalAllNans, RegisterClass regtype, multiclass FPCondComparison<bit signalAllNans, string mnemonic, SDPatternOperator OpNode = null_frag> { - def Hrr : BaseFPCondComparison<signalAllNans, FPR16, mnemonic, []> { + def Hrr : BaseFPCondComparison<signalAllNans, FPR16, mnemonic, + [(set NZCV, (OpNode (f16 FPR16:$Rn), (f16 FPR16:$Rm), (i32 imm:$nzcv), + (i32 imm:$cond), NZCV))]> { let Inst{23-22} = 0b11; let Predicates = [HasFullFP16]; } @@ -4788,25 +4958,6 @@ class BaseSIMDThreeSameVectorTied<bit Q, bit U, bits<3> size, bits<5> opcode, let Inst{4-0} = Rd; } -class BaseSIMDThreeSameVectorDot<bit Q, bit U, string asm, string kind1, - string kind2, RegisterOperand RegType, - ValueType AccumType, ValueType InputType, - SDPatternOperator OpNode> : - BaseSIMDThreeSameVectorTied<Q, U, 0b100, 0b10010, RegType, asm, kind1, - [(set (AccumType RegType:$dst), - (OpNode (AccumType RegType:$Rd), - (InputType RegType:$Rn), - (InputType RegType:$Rm)))]> { - let AsmString = !strconcat(asm, "{\t$Rd" # kind1 # ", $Rn" # kind2 # ", $Rm" # kind2 # "}"); -} - -multiclass SIMDThreeSameVectorDot<bit U, string asm, SDPatternOperator OpNode> { - def v8i8 : BaseSIMDThreeSameVectorDot<0, U, asm, ".2s", ".8b", V64, - v2i32, v8i8, OpNode>; - def v16i8 : BaseSIMDThreeSameVectorDot<1, U, asm, ".4s", ".16b", V128, - v4i32, v16i8, OpNode>; -} - // All operand sizes distinguished in the encoding. multiclass SIMDThreeSameVector<bit U, bits<5> opc, string asm, SDPatternOperator OpNode> { @@ -5047,6 +5198,51 @@ multiclass SIMDLogicalThreeVectorTied<bit U, bits<2> size, V128:$LHS, V128:$MHS, V128:$RHS)>; } +// ARMv8.2-A Dot Product Instructions (Vector): These instructions extract +// bytes from S-sized elements. +class BaseSIMDThreeSameVectorDot<bit Q, bit U, string asm, string kind1, + string kind2, RegisterOperand RegType, + ValueType AccumType, ValueType InputType, + SDPatternOperator OpNode> : + BaseSIMDThreeSameVectorTied<Q, U, 0b100, 0b10010, RegType, asm, kind1, + [(set (AccumType RegType:$dst), + (OpNode (AccumType RegType:$Rd), + (InputType RegType:$Rn), + (InputType RegType:$Rm)))]> { + let AsmString = !strconcat(asm, "{\t$Rd" # kind1 # ", $Rn" # kind2 # ", $Rm" # kind2 # "}"); +} + +multiclass SIMDThreeSameVectorDot<bit U, string asm, SDPatternOperator OpNode> { + def v8i8 : BaseSIMDThreeSameVectorDot<0, U, asm, ".2s", ".8b", V64, + v2i32, v8i8, OpNode>; + def v16i8 : BaseSIMDThreeSameVectorDot<1, U, asm, ".4s", ".16b", V128, + v4i32, v16i8, OpNode>; +} + +// ARMv8.2-A Fused Multiply Add-Long Instructions (Vector): These instructions +// select inputs from 4H vectors and accumulate outputs to a 2S vector (or from +// 8H to 4S, when Q=1). +class BaseSIMDThreeSameVectorFML<bit Q, bit U, bit b13, bits<3> size, string asm, string kind1, + string kind2, RegisterOperand RegType, + ValueType AccumType, ValueType InputType, + SDPatternOperator OpNode> : + BaseSIMDThreeSameVectorTied<Q, U, size, 0b11101, RegType, asm, kind1, + [(set (AccumType RegType:$dst), + (OpNode (AccumType RegType:$Rd), + (InputType RegType:$Rn), + (InputType RegType:$Rm)))]> { + let AsmString = !strconcat(asm, "{\t$Rd" # kind1 # ", $Rn" # kind2 # ", $Rm" # kind2 # "}"); + let Inst{13} = b13; +} + +multiclass SIMDThreeSameVectorFML<bit U, bit b13, bits<3> size, string asm, + SDPatternOperator OpNode> { + def v4f16 : BaseSIMDThreeSameVectorFML<0, U, b13, size, asm, ".2s", ".2h", V64, + v2f32, v4f16, OpNode>; + def v8f16 : BaseSIMDThreeSameVectorFML<1, U, b13, size, asm, ".4s", ".4h", V128, + v4f32, v8f16, OpNode>; +} + //---------------------------------------------------------------------------- // AdvSIMD two register vector instructions. @@ -5291,7 +5487,7 @@ multiclass SIMDTwoVectorBH<bit U, bits<5> opc, string asm, [(set (v8i16 V128:$Rd), (OpNode V128:$Rn))]>; } -// Supports only S and D element sizes, uses high bit of the size field +// Supports H, S and D element sizes, uses high bit of the size field // as an extra opcode bit. multiclass SIMDTwoVectorFP<bit U, bit S, bits<5> opc, string asm, SDPatternOperator OpNode> { @@ -5314,6 +5510,25 @@ multiclass SIMDTwoVectorFP<bit U, bit S, bits<5> opc, string asm, [(set (v2f64 V128:$Rd), (OpNode (v2f64 V128:$Rn)))]>; } +// Supports only S and D element sizes +multiclass SIMDTwoVectorSD<bit U, bits<5> opc, string asm, + SDPatternOperator OpNode = null_frag> { + + def v2f32 : BaseSIMDTwoSameVector<0, U, 00, opc, 0b00, V64, + asm, ".2s", ".2s", + [(set (v2f32 V64:$Rd), (OpNode (v2f32 V64:$Rn)))]>; + def v4f32 : BaseSIMDTwoSameVector<1, U, 00, opc, 0b00, V128, + asm, ".4s", ".4s", + [(set (v4f32 V128:$Rd), (OpNode (v4f32 V128:$Rn)))]>; + def v2f64 : BaseSIMDTwoSameVector<1, U, 01, opc, 0b00, V128, + asm, ".2d", ".2d", + [(set (v2f64 V128:$Rd), (OpNode (v2f64 V128:$Rn)))]>; +} + +multiclass FRIntNNTVector<bit U, bit op, string asm, + SDPatternOperator OpNode = null_frag> : + SIMDTwoVectorSD<U, {0b1111,op}, asm, OpNode>; + // Supports only S element size. multiclass SIMDTwoVectorS<bit U, bit S, bits<5> opc, string asm, SDPatternOperator OpNode> { @@ -7234,7 +7449,7 @@ class BaseSIMDIndexedTied<bit Q, bit U, bit Scalar, bits<2> size, bits<4> opc, let Inst{4-0} = Rd; } -// ARMv8.2 Index Dot product instructions +// ARMv8.2-A Dot Product Instructions (Indexed) class BaseSIMDThreeSameVectorDotIndex<bit Q, bit U, string asm, string dst_kind, string lhs_kind, string rhs_kind, RegisterOperand RegType, @@ -7255,10 +7470,38 @@ class BaseSIMDThreeSameVectorDotIndex<bit Q, bit U, string asm, string dst_kind, multiclass SIMDThreeSameVectorDotIndex<bit U, string asm, SDPatternOperator OpNode> { - def v8i8 : BaseSIMDThreeSameVectorDotIndex<0, U, asm, ".2s", ".8b", ".4b", V64, - v2i32, v8i8, OpNode>; - def v16i8 : BaseSIMDThreeSameVectorDotIndex<1, U, asm, ".4s", ".16b", ".4b", V128, - v4i32, v16i8, OpNode>; + def v8i8 : BaseSIMDThreeSameVectorDotIndex<0, U, asm, ".2s", ".8b", ".4b", + V64, v2i32, v8i8, OpNode>; + def v16i8 : BaseSIMDThreeSameVectorDotIndex<1, U, asm, ".4s", ".16b", ".4b", + V128, v4i32, v16i8, OpNode>; +} + +// ARMv8.2-A Fused Multiply Add-Long Instructions (Indexed) +class BaseSIMDThreeSameVectorFMLIndex<bit Q, bit U, bits<4> opc, string asm, + string dst_kind, string lhs_kind, + string rhs_kind, RegisterOperand RegType, + ValueType AccumType, ValueType InputType, + SDPatternOperator OpNode> : + BaseSIMDIndexedTied<Q, U, 0, 0b10, opc, RegType, RegType, V128, + VectorIndexH, asm, "", dst_kind, lhs_kind, rhs_kind, + [(set (AccumType RegType:$dst), + (AccumType (OpNode (AccumType RegType:$Rd), + (InputType RegType:$Rn), + (InputType (AArch64duplane16 (v8f16 V128:$Rm), + VectorIndexH:$idx)))))]> { + // idx = H:L:M + bits<3> idx; + let Inst{11} = idx{2}; // H + let Inst{21} = idx{1}; // L + let Inst{20} = idx{0}; // M +} + +multiclass SIMDThreeSameVectorFMLIndex<bit U, bits<4> opc, string asm, + SDPatternOperator OpNode> { + def v4f16 : BaseSIMDThreeSameVectorFMLIndex<0, U, opc, asm, ".2s", ".2h", ".h", + V64, v2f32, v4f16, OpNode>; + def v8f16 : BaseSIMDThreeSameVectorFMLIndex<1, U, opc, asm, ".4s", ".4h", ".h", + V128, v4f32, v8f16, OpNode>; } multiclass SIMDFPIndexed<bit U, bits<4> opc, string asm, @@ -9746,9 +9989,10 @@ class BaseSIMDThreeSameVectorComplex<bit Q, bit U, bits<2> size, bits<3> opcode, let Inst{4-0} = Rd; } +//8.3 CompNum - Floating-point complex number support multiclass SIMDThreeSameVectorComplexHSD<bit U, bits<3> opcode, Operand rottype, string asm, SDPatternOperator OpNode>{ - let Predicates = [HasV8_3a, HasNEON, HasFullFP16] in { + let Predicates = [HasComplxNum, HasNEON, HasFullFP16] in { def v4f16 : BaseSIMDThreeSameVectorComplex<0, U, 0b01, opcode, V64, rottype, asm, ".4h", [(set (v4f16 V64:$dst), (OpNode (v4f16 V64:$Rd), @@ -9764,7 +10008,7 @@ multiclass SIMDThreeSameVectorComplexHSD<bit U, bits<3> opcode, Operand rottype, (rottype i32:$rot)))]>; } - let Predicates = [HasV8_3a, HasNEON] in { + let Predicates = [HasComplxNum, HasNEON] in { def v2f32 : BaseSIMDThreeSameVectorComplex<0, U, 0b10, opcode, V64, rottype, asm, ".2s", [(set (v2f32 V64:$dst), (OpNode (v2f32 V64:$Rd), @@ -9820,7 +10064,7 @@ class BaseSIMDThreeSameVectorTiedComplex<bit Q, bit U, bits<2> size, multiclass SIMDThreeSameVectorTiedComplexHSD<bit U, bits<3> opcode, Operand rottype, string asm, SDPatternOperator OpNode> { - let Predicates = [HasV8_3a, HasNEON, HasFullFP16] in { + let Predicates = [HasComplxNum, HasNEON, HasFullFP16] in { def v4f16 : BaseSIMDThreeSameVectorTiedComplex<0, U, 0b01, opcode, V64, rottype, asm, ".4h", [(set (v4f16 V64:$dst), (OpNode (v4f16 V64:$Rd), @@ -9836,7 +10080,7 @@ multiclass SIMDThreeSameVectorTiedComplexHSD<bit U, bits<3> opcode, (rottype i32:$rot)))]>; } - let Predicates = [HasV8_3a, HasNEON] in { + let Predicates = [HasComplxNum, HasNEON] in { def v2f32 : BaseSIMDThreeSameVectorTiedComplex<0, U, 0b10, opcode, V64, rottype, asm, ".2s", [(set (v2f32 V64:$dst), (OpNode (v2f32 V64:$Rd), @@ -9902,7 +10146,7 @@ class BaseSIMDIndexedTiedComplex<bit Q, bit U, bit Scalar, bits<2> size, // classes. multiclass SIMDIndexedTiedComplexHSD<bit U, bit opc1, bit opc2, Operand rottype, string asm, SDPatternOperator OpNode> { - let Predicates = [HasV8_3a,HasNEON,HasFullFP16] in { + let Predicates = [HasComplxNum, HasNEON, HasFullFP16] in { def v4f16_indexed : BaseSIMDIndexedTiedComplex<0, 1, 0, 0b01, opc1, opc2, V64, V64, V128, VectorIndexD, rottype, asm, ".4h", ".4h", ".4h", ".h", []> { @@ -9918,9 +10162,9 @@ multiclass SIMDIndexedTiedComplexHSD<bit U, bit opc1, bit opc2, Operand rottype, let Inst{11} = idx{1}; let Inst{21} = idx{0}; } - } // Predicates = [HasV8_3a,HasNEON,HasFullFP16] + } // Predicates = HasComplxNum, HasNEON, HasFullFP16] - let Predicates = [HasV8_3a,HasNEON] in { + let Predicates = [HasComplxNum, HasNEON] in { def v4f32_indexed : BaseSIMDIndexedTiedComplex<1, 1, 0, 0b10, opc1, opc2, V128, V128, V128, VectorIndexD, rottype, asm, ".4s", ".4s", ".4s", ".s", []> { @@ -9928,7 +10172,7 @@ multiclass SIMDIndexedTiedComplexHSD<bit U, bit opc1, bit opc2, Operand rottype, let Inst{11} = idx{0}; let Inst{21} = 0; } - } // Predicates = [HasV8_3a,HasNEON] + } // Predicates = [HasComplxNum, HasNEON] } //---------------------------------------------------------------------------- diff --git a/lib/Target/AArch64/AArch64InstrInfo.cpp b/lib/Target/AArch64/AArch64InstrInfo.cpp index 032d53d19620..ada067888572 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -66,7 +66,8 @@ static cl::opt<unsigned> cl::desc("Restrict range of Bcc instructions (DEBUG)")); AArch64InstrInfo::AArch64InstrInfo(const AArch64Subtarget &STI) - : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP), + : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP, + AArch64::CATCHRET), RI(STI.getTargetTriple()), Subtarget(STI) {} /// GetInstSize - Return the number of bytes of code the specified @@ -108,6 +109,14 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { // This gets lowered to an instruction sequence which takes 16 bytes NumBytes = 16; break; + case AArch64::JumpTableDest32: + case AArch64::JumpTableDest16: + case AArch64::JumpTableDest8: + NumBytes = 12; + break; + case AArch64::SPACE: + NumBytes = MI.getOperand(1).getImm(); + break; } return NumBytes; @@ -675,14 +684,36 @@ bool AArch64InstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const { if (!Subtarget.hasCustomCheapAsMoveHandling()) return MI.isAsCheapAsAMove(); + const unsigned Opcode = MI.getOpcode(); + + // Firstly, check cases gated by features. + + if (Subtarget.hasZeroCycleZeroingFP()) { + if (Opcode == AArch64::FMOVH0 || + Opcode == AArch64::FMOVS0 || + Opcode == AArch64::FMOVD0) + return true; + } + + if (Subtarget.hasZeroCycleZeroingGP()) { + if (Opcode == TargetOpcode::COPY && + (MI.getOperand(1).getReg() == AArch64::WZR || + MI.getOperand(1).getReg() == AArch64::XZR)) + return true; + } + + // Secondly, check cases specific to sub-targets. + if (Subtarget.hasExynosCheapAsMoveHandling()) { - if (isExynosResetFast(MI) || isExynosShiftLeftFast(MI)) + if (isExynosCheapAsMove(MI)) return true; - else - return MI.isAsCheapAsAMove(); + + return MI.isAsCheapAsAMove(); } - switch (MI.getOpcode()) { + // Finally, check generic cases. + + switch (Opcode) { default: return false; @@ -723,217 +754,12 @@ bool AArch64InstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const { return canBeExpandedToORR(MI, 32); case AArch64::MOVi64imm: return canBeExpandedToORR(MI, 64); - - // It is cheap to zero out registers if the subtarget has ZeroCycleZeroing - // feature. - case AArch64::FMOVH0: - case AArch64::FMOVS0: - case AArch64::FMOVD0: - return Subtarget.hasZeroCycleZeroing(); - case TargetOpcode::COPY: - return (Subtarget.hasZeroCycleZeroing() && - (MI.getOperand(1).getReg() == AArch64::WZR || - MI.getOperand(1).getReg() == AArch64::XZR)); } llvm_unreachable("Unknown opcode to check as cheap as a move!"); } -bool AArch64InstrInfo::isExynosResetFast(const MachineInstr &MI) const { - unsigned Reg, Imm, Shift; - - switch (MI.getOpcode()) { - default: - return false; - - // MOV Rd, SP - case AArch64::ADDWri: - case AArch64::ADDXri: - if (!MI.getOperand(1).isReg() || !MI.getOperand(2).isImm()) - return false; - - Reg = MI.getOperand(1).getReg(); - Imm = MI.getOperand(2).getImm(); - return ((Reg == AArch64::WSP || Reg == AArch64::SP) && Imm == 0); - - // Literal - case AArch64::ADR: - case AArch64::ADRP: - return true; - - // MOVI Vd, #0 - case AArch64::MOVID: - case AArch64::MOVIv8b_ns: - case AArch64::MOVIv2d_ns: - case AArch64::MOVIv16b_ns: - Imm = MI.getOperand(1).getImm(); - return (Imm == 0); - - // MOVI Vd, #0 - case AArch64::MOVIv2i32: - case AArch64::MOVIv4i16: - case AArch64::MOVIv4i32: - case AArch64::MOVIv8i16: - Imm = MI.getOperand(1).getImm(); - Shift = MI.getOperand(2).getImm(); - return (Imm == 0 && Shift == 0); - - // MOV Rd, Imm - case AArch64::MOVNWi: - case AArch64::MOVNXi: - - // MOV Rd, Imm - case AArch64::MOVZWi: - case AArch64::MOVZXi: - return true; - - // MOV Rd, Imm - case AArch64::ORRWri: - case AArch64::ORRXri: - if (!MI.getOperand(1).isReg()) - return false; - - Reg = MI.getOperand(1).getReg(); - Imm = MI.getOperand(2).getImm(); - return ((Reg == AArch64::WZR || Reg == AArch64::XZR) && Imm == 0); - - // MOV Rd, Rm - case AArch64::ORRWrs: - case AArch64::ORRXrs: - if (!MI.getOperand(1).isReg()) - return false; - - Reg = MI.getOperand(1).getReg(); - Imm = MI.getOperand(3).getImm(); - Shift = AArch64_AM::getShiftValue(Imm); - return ((Reg == AArch64::WZR || Reg == AArch64::XZR) && Shift == 0); - } -} - -bool AArch64InstrInfo::isExynosShiftLeftFast(const MachineInstr &MI) const { - unsigned Imm, Shift; - AArch64_AM::ShiftExtendType Ext; - - switch (MI.getOpcode()) { - default: - return false; - - // WriteI - case AArch64::ADDSWri: - case AArch64::ADDSXri: - case AArch64::ADDWri: - case AArch64::ADDXri: - case AArch64::SUBSWri: - case AArch64::SUBSXri: - case AArch64::SUBWri: - case AArch64::SUBXri: - return true; - - // WriteISReg - case AArch64::ADDSWrs: - case AArch64::ADDSXrs: - case AArch64::ADDWrs: - case AArch64::ADDXrs: - case AArch64::ANDSWrs: - case AArch64::ANDSXrs: - case AArch64::ANDWrs: - case AArch64::ANDXrs: - case AArch64::BICSWrs: - case AArch64::BICSXrs: - case AArch64::BICWrs: - case AArch64::BICXrs: - case AArch64::EONWrs: - case AArch64::EONXrs: - case AArch64::EORWrs: - case AArch64::EORXrs: - case AArch64::ORNWrs: - case AArch64::ORNXrs: - case AArch64::ORRWrs: - case AArch64::ORRXrs: - case AArch64::SUBSWrs: - case AArch64::SUBSXrs: - case AArch64::SUBWrs: - case AArch64::SUBXrs: - Imm = MI.getOperand(3).getImm(); - Shift = AArch64_AM::getShiftValue(Imm); - Ext = AArch64_AM::getShiftType(Imm); - return (Shift == 0 || (Shift <= 3 && Ext == AArch64_AM::LSL)); - - // WriteIEReg - case AArch64::ADDSWrx: - case AArch64::ADDSXrx: - case AArch64::ADDSXrx64: - case AArch64::ADDWrx: - case AArch64::ADDXrx: - case AArch64::ADDXrx64: - case AArch64::SUBSWrx: - case AArch64::SUBSXrx: - case AArch64::SUBSXrx64: - case AArch64::SUBWrx: - case AArch64::SUBXrx: - case AArch64::SUBXrx64: - Imm = MI.getOperand(3).getImm(); - Shift = AArch64_AM::getArithShiftValue(Imm); - Ext = AArch64_AM::getArithExtendType(Imm); - return (Shift == 0 || (Shift <= 3 && Ext == AArch64_AM::UXTX)); - - case AArch64::PRFMroW: - case AArch64::PRFMroX: - - // WriteLDIdx - case AArch64::LDRBBroW: - case AArch64::LDRBBroX: - case AArch64::LDRHHroW: - case AArch64::LDRHHroX: - case AArch64::LDRSBWroW: - case AArch64::LDRSBWroX: - case AArch64::LDRSBXroW: - case AArch64::LDRSBXroX: - case AArch64::LDRSHWroW: - case AArch64::LDRSHWroX: - case AArch64::LDRSHXroW: - case AArch64::LDRSHXroX: - case AArch64::LDRSWroW: - case AArch64::LDRSWroX: - case AArch64::LDRWroW: - case AArch64::LDRWroX: - case AArch64::LDRXroW: - case AArch64::LDRXroX: - - case AArch64::LDRBroW: - case AArch64::LDRBroX: - case AArch64::LDRDroW: - case AArch64::LDRDroX: - case AArch64::LDRHroW: - case AArch64::LDRHroX: - case AArch64::LDRSroW: - case AArch64::LDRSroX: - - // WriteSTIdx - case AArch64::STRBBroW: - case AArch64::STRBBroX: - case AArch64::STRHHroW: - case AArch64::STRHHroX: - case AArch64::STRWroW: - case AArch64::STRWroX: - case AArch64::STRXroW: - case AArch64::STRXroX: - - case AArch64::STRBroW: - case AArch64::STRBroX: - case AArch64::STRDroW: - case AArch64::STRDroX: - case AArch64::STRHroW: - case AArch64::STRHroX: - case AArch64::STRSroW: - case AArch64::STRSroX: - Imm = MI.getOperand(3).getImm(); - Ext = AArch64_AM::getMemExtendType(Imm); - return (Ext == AArch64_AM::SXTX || Ext == AArch64_AM::UXTX); - } -} - -bool AArch64InstrInfo::isFalkorShiftExtFast(const MachineInstr &MI) const { +bool AArch64InstrInfo::isFalkorShiftExtFast(const MachineInstr &MI) { switch (MI.getOpcode()) { default: return false; @@ -1055,6 +881,32 @@ bool AArch64InstrInfo::isFalkorShiftExtFast(const MachineInstr &MI) const { } } +bool AArch64InstrInfo::isSEHInstruction(const MachineInstr &MI) { + unsigned Opc = MI.getOpcode(); + switch (Opc) { + default: + return false; + case AArch64::SEH_StackAlloc: + case AArch64::SEH_SaveFPLR: + case AArch64::SEH_SaveFPLR_X: + case AArch64::SEH_SaveReg: + case AArch64::SEH_SaveReg_X: + case AArch64::SEH_SaveRegP: + case AArch64::SEH_SaveRegP_X: + case AArch64::SEH_SaveFReg: + case AArch64::SEH_SaveFReg_X: + case AArch64::SEH_SaveFRegP: + case AArch64::SEH_SaveFRegP_X: + case AArch64::SEH_SetFP: + case AArch64::SEH_AddFP: + case AArch64::SEH_Nop: + case AArch64::SEH_PrologEnd: + case AArch64::SEH_EpilogStart: + case AArch64::SEH_EpilogEnd: + return true; + } +} + bool AArch64InstrInfo::isCoalescableExtInstr(const MachineInstr &MI, unsigned &SrcReg, unsigned &DstReg, unsigned &SubIdx) const { @@ -1078,7 +930,7 @@ bool AArch64InstrInfo::isCoalescableExtInstr(const MachineInstr &MI, bool AArch64InstrInfo::areMemAccessesTriviallyDisjoint( MachineInstr &MIa, MachineInstr &MIb, AliasAnalysis *AA) const { const TargetRegisterInfo *TRI = &getRegisterInfo(); - unsigned BaseRegA = 0, BaseRegB = 0; + MachineOperand *BaseOpA = nullptr, *BaseOpB = nullptr; int64_t OffsetA = 0, OffsetB = 0; unsigned WidthA = 0, WidthB = 0; @@ -1089,14 +941,14 @@ bool AArch64InstrInfo::areMemAccessesTriviallyDisjoint( MIa.hasOrderedMemoryRef() || MIb.hasOrderedMemoryRef()) return false; - // Retrieve the base register, offset from the base register and width. Width + // Retrieve the base, offset from the base and width. Width // is the size of memory that is being loaded/stored (e.g. 1, 2, 4, 8). If - // base registers are identical, and the offset of a lower memory access + + // base are identical, and the offset of a lower memory access + // the width doesn't overlap the offset of a higher memory access, // then the memory accesses are different. - if (getMemOpBaseRegImmOfsWidth(MIa, BaseRegA, OffsetA, WidthA, TRI) && - getMemOpBaseRegImmOfsWidth(MIb, BaseRegB, OffsetB, WidthB, TRI)) { - if (BaseRegA == BaseRegB) { + if (getMemOperandWithOffsetWidth(MIa, BaseOpA, OffsetA, WidthA, TRI) && + getMemOperandWithOffsetWidth(MIb, BaseOpB, OffsetB, WidthB, TRI)) { + if (BaseOpA->isIdenticalTo(*BaseOpB)) { int LowOffset = OffsetA < OffsetB ? OffsetA : OffsetB; int HighOffset = OffsetA < OffsetB ? OffsetB : OffsetA; int LowWidth = (LowOffset == OffsetA) ? WidthA : WidthB; @@ -1107,6 +959,26 @@ bool AArch64InstrInfo::areMemAccessesTriviallyDisjoint( return false; } +bool AArch64InstrInfo::isSchedulingBoundary(const MachineInstr &MI, + const MachineBasicBlock *MBB, + const MachineFunction &MF) const { + if (TargetInstrInfo::isSchedulingBoundary(MI, MBB, MF)) + return true; + switch (MI.getOpcode()) { + case AArch64::HINT: + // CSDB hints are scheduling barriers. + if (MI.getOperand(0).getImm() == 0x14) + return true; + break; + case AArch64::DSB: + case AArch64::ISB: + // DSB and ISB also are scheduling barriers. + return true; + default:; + } + return isSEHInstruction(MI); +} + /// analyzeCompare - For a comparison instruction, return the source registers /// in SrcReg and SrcReg2, and the value it compares against in CmpValue. /// Return true if the comparison instruction can be analyzed. @@ -1593,11 +1465,36 @@ bool AArch64InstrInfo::substituteCmpToZero( } bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { - if (MI.getOpcode() != TargetOpcode::LOAD_STACK_GUARD) + if (MI.getOpcode() != TargetOpcode::LOAD_STACK_GUARD && + MI.getOpcode() != AArch64::CATCHRET) return false; MachineBasicBlock &MBB = *MI.getParent(); DebugLoc DL = MI.getDebugLoc(); + + if (MI.getOpcode() == AArch64::CATCHRET) { + // Skip to the first instruction before the epilog. + const TargetInstrInfo *TII = + MBB.getParent()->getSubtarget().getInstrInfo(); + MachineBasicBlock *TargetMBB = MI.getOperand(0).getMBB(); + auto MBBI = MachineBasicBlock::iterator(MI); + MachineBasicBlock::iterator FirstEpilogSEH = std::prev(MBBI); + while (FirstEpilogSEH->getFlag(MachineInstr::FrameDestroy) && + FirstEpilogSEH != MBB.begin()) + FirstEpilogSEH = std::prev(FirstEpilogSEH); + if (FirstEpilogSEH != MBB.begin()) + FirstEpilogSEH = std::next(FirstEpilogSEH); + BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADRP)) + .addReg(AArch64::X0, RegState::Define) + .addMBB(TargetMBB); + BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADDXri)) + .addReg(AArch64::X0, RegState::Define) + .addReg(AArch64::X0) + .addMBB(TargetMBB) + .addImm(0); + return true; + } + unsigned Reg = MI.getOperand(0).getReg(); const GlobalValue *GV = cast<GlobalValue>((*MI.memoperands_begin())->getValue()); @@ -1607,7 +1504,7 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { if ((OpFlags & AArch64II::MO_GOT) != 0) { BuildMI(MBB, MI, DL, get(AArch64::LOADgot), Reg) - .addGlobalAddress(GV, 0, AArch64II::MO_GOT); + .addGlobalAddress(GV, 0, OpFlags); BuildMI(MBB, MI, DL, get(AArch64::LDRXui), Reg) .addReg(Reg, RegState::Kill) .addImm(0) @@ -1632,6 +1529,9 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { .addReg(Reg, RegState::Kill) .addImm(0) .addMemOperand(*MI.memoperands_begin()); + } else if (TM.getCodeModel() == CodeModel::Tiny) { + BuildMI(MBB, MI, DL, get(AArch64::ADR), Reg) + .addGlobalAddress(GV, 0, OpFlags); } else { BuildMI(MBB, MI, DL, get(AArch64::ADRP), Reg) .addGlobalAddress(GV, 0, OpFlags | AArch64II::MO_PAGE); @@ -1647,71 +1547,6 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { return true; } -/// Return true if this is this instruction has a non-zero immediate -bool AArch64InstrInfo::hasShiftedReg(const MachineInstr &MI) { - switch (MI.getOpcode()) { - default: - break; - case AArch64::ADDSWrs: - case AArch64::ADDSXrs: - case AArch64::ADDWrs: - case AArch64::ADDXrs: - case AArch64::ANDSWrs: - case AArch64::ANDSXrs: - case AArch64::ANDWrs: - case AArch64::ANDXrs: - case AArch64::BICSWrs: - case AArch64::BICSXrs: - case AArch64::BICWrs: - case AArch64::BICXrs: - case AArch64::EONWrs: - case AArch64::EONXrs: - case AArch64::EORWrs: - case AArch64::EORXrs: - case AArch64::ORNWrs: - case AArch64::ORNXrs: - case AArch64::ORRWrs: - case AArch64::ORRXrs: - case AArch64::SUBSWrs: - case AArch64::SUBSXrs: - case AArch64::SUBWrs: - case AArch64::SUBXrs: - if (MI.getOperand(3).isImm()) { - unsigned val = MI.getOperand(3).getImm(); - return (val != 0); - } - break; - } - return false; -} - -/// Return true if this is this instruction has a non-zero immediate -bool AArch64InstrInfo::hasExtendedReg(const MachineInstr &MI) { - switch (MI.getOpcode()) { - default: - break; - case AArch64::ADDSWrx: - case AArch64::ADDSXrx: - case AArch64::ADDSXrx64: - case AArch64::ADDWrx: - case AArch64::ADDXrx: - case AArch64::ADDXrx64: - case AArch64::SUBSWrx: - case AArch64::SUBSXrx: - case AArch64::SUBSXrx64: - case AArch64::SUBWrx: - case AArch64::SUBXrx: - case AArch64::SUBXrx64: - if (MI.getOperand(3).isImm()) { - unsigned val = MI.getOperand(3).getImm(); - return (val != 0); - } - break; - } - - return false; -} - // Return true if this instruction simply sets its single destination register // to zero. This is equivalent to a register rename of the zero-register. bool AArch64InstrInfo::isGPRZero(const MachineInstr &MI) { @@ -1834,67 +1669,6 @@ unsigned AArch64InstrInfo::isStoreToStackSlot(const MachineInstr &MI, return 0; } -/// Return true if this is load/store scales or extends its register offset. -/// This refers to scaling a dynamic index as opposed to scaled immediates. -/// MI should be a memory op that allows scaled addressing. -bool AArch64InstrInfo::isScaledAddr(const MachineInstr &MI) { - switch (MI.getOpcode()) { - default: - break; - case AArch64::LDRBBroW: - case AArch64::LDRBroW: - case AArch64::LDRDroW: - case AArch64::LDRHHroW: - case AArch64::LDRHroW: - case AArch64::LDRQroW: - case AArch64::LDRSBWroW: - case AArch64::LDRSBXroW: - case AArch64::LDRSHWroW: - case AArch64::LDRSHXroW: - case AArch64::LDRSWroW: - case AArch64::LDRSroW: - case AArch64::LDRWroW: - case AArch64::LDRXroW: - case AArch64::STRBBroW: - case AArch64::STRBroW: - case AArch64::STRDroW: - case AArch64::STRHHroW: - case AArch64::STRHroW: - case AArch64::STRQroW: - case AArch64::STRSroW: - case AArch64::STRWroW: - case AArch64::STRXroW: - case AArch64::LDRBBroX: - case AArch64::LDRBroX: - case AArch64::LDRDroX: - case AArch64::LDRHHroX: - case AArch64::LDRHroX: - case AArch64::LDRQroX: - case AArch64::LDRSBWroX: - case AArch64::LDRSBXroX: - case AArch64::LDRSHWroX: - case AArch64::LDRSHXroX: - case AArch64::LDRSWroX: - case AArch64::LDRSroX: - case AArch64::LDRWroX: - case AArch64::LDRXroX: - case AArch64::STRBBroX: - case AArch64::STRBroX: - case AArch64::STRDroX: - case AArch64::STRHHroX: - case AArch64::STRHroX: - case AArch64::STRQroX: - case AArch64::STRSroX: - case AArch64::STRWroX: - case AArch64::STRXroX: - - unsigned Val = MI.getOperand(3).getImm(); - AArch64_AM::ShiftExtendType ExtType = AArch64_AM::getMemExtendType(Val); - return (ExtType != AArch64_AM::UXTX) || AArch64_AM::getMemDoShift(Val); - } - return false; -} - /// Check all MachineMemOperands for a hint to suppress pairing. bool AArch64InstrInfo::isLdStPairSuppressed(const MachineInstr &MI) { return llvm::any_of(MI.memoperands(), [](MachineMemOperand *MMO) { @@ -2068,17 +1842,21 @@ bool AArch64InstrInfo::isCandidateToMergeOrPair(MachineInstr &MI) const { if (MI.hasOrderedMemoryRef()) return false; - // Make sure this is a reg+imm (as opposed to an address reloc). - assert(MI.getOperand(1).isReg() && "Expected a reg operand."); + // Make sure this is a reg/fi+imm (as opposed to an address reloc). + assert((MI.getOperand(1).isReg() || MI.getOperand(1).isFI()) && + "Expected a reg or frame index operand."); if (!MI.getOperand(2).isImm()) return false; // Can't merge/pair if the instruction modifies the base register. // e.g., ldr x0, [x0] - unsigned BaseReg = MI.getOperand(1).getReg(); - const TargetRegisterInfo *TRI = &getRegisterInfo(); - if (MI.modifiesRegister(BaseReg, TRI)) - return false; + // This case will never occur with an FI base. + if (MI.getOperand(1).isReg()) { + unsigned BaseReg = MI.getOperand(1).getReg(); + const TargetRegisterInfo *TRI = &getRegisterInfo(); + if (MI.modifiesRegister(BaseReg, TRI)) + return false; + } // Check if this load/store has a hint to avoid pair formation. // MachineMemOperands hints are set by the AArch64StorePairSuppress pass. @@ -2101,25 +1879,28 @@ bool AArch64InstrInfo::isCandidateToMergeOrPair(MachineInstr &MI) const { return true; } -bool AArch64InstrInfo::getMemOpBaseRegImmOfs( - MachineInstr &LdSt, unsigned &BaseReg, int64_t &Offset, - const TargetRegisterInfo *TRI) const { +bool AArch64InstrInfo::getMemOperandWithOffset(MachineInstr &LdSt, + MachineOperand *&BaseOp, + int64_t &Offset, + const TargetRegisterInfo *TRI) const { unsigned Width; - return getMemOpBaseRegImmOfsWidth(LdSt, BaseReg, Offset, Width, TRI); + return getMemOperandWithOffsetWidth(LdSt, BaseOp, Offset, Width, TRI); } -bool AArch64InstrInfo::getMemOpBaseRegImmOfsWidth( - MachineInstr &LdSt, unsigned &BaseReg, int64_t &Offset, unsigned &Width, - const TargetRegisterInfo *TRI) const { +bool AArch64InstrInfo::getMemOperandWithOffsetWidth( + MachineInstr &LdSt, MachineOperand *&BaseOp, int64_t &Offset, + unsigned &Width, const TargetRegisterInfo *TRI) const { assert(LdSt.mayLoadOrStore() && "Expected a memory operation."); // Handle only loads/stores with base register followed by immediate offset. if (LdSt.getNumExplicitOperands() == 3) { // Non-paired instruction (e.g., ldr x1, [x0, #8]). - if (!LdSt.getOperand(1).isReg() || !LdSt.getOperand(2).isImm()) + if ((!LdSt.getOperand(1).isReg() && !LdSt.getOperand(1).isFI()) || + !LdSt.getOperand(2).isImm()) return false; } else if (LdSt.getNumExplicitOperands() == 4) { // Paired instruction (e.g., ldp x1, x2, [x0, #8]). - if (!LdSt.getOperand(1).isReg() || !LdSt.getOperand(2).isReg() || + if (!LdSt.getOperand(1).isReg() || + (!LdSt.getOperand(2).isReg() && !LdSt.getOperand(2).isFI()) || !LdSt.getOperand(3).isImm()) return false; } else @@ -2138,13 +1919,18 @@ bool AArch64InstrInfo::getMemOpBaseRegImmOfsWidth( // multiplied by the scaling factor. Unscaled instructions have scaling factor // set to 1. if (LdSt.getNumExplicitOperands() == 3) { - BaseReg = LdSt.getOperand(1).getReg(); + BaseOp = &LdSt.getOperand(1); Offset = LdSt.getOperand(2).getImm() * Scale; } else { assert(LdSt.getNumExplicitOperands() == 4 && "invalid number of operands"); - BaseReg = LdSt.getOperand(2).getReg(); + BaseOp = &LdSt.getOperand(2); Offset = LdSt.getOperand(3).getImm() * Scale; } + + assert((BaseOp->isReg() || BaseOp->isFI()) && + "getMemOperandWithOffset only supports base " + "operands of type register or frame index."); + return true; } @@ -2299,31 +2085,33 @@ bool AArch64InstrInfo::getMemOpInfo(unsigned Opcode, unsigned &Scale, return true; } -// Scale the unscaled offsets. Returns false if the unscaled offset can't be -// scaled. -static bool scaleOffset(unsigned Opc, int64_t &Offset) { - unsigned OffsetStride = 1; +static unsigned getOffsetStride(unsigned Opc) { switch (Opc) { default: - return false; + return 0; case AArch64::LDURQi: case AArch64::STURQi: - OffsetStride = 16; - break; + return 16; case AArch64::LDURXi: case AArch64::LDURDi: case AArch64::STURXi: case AArch64::STURDi: - OffsetStride = 8; - break; + return 8; case AArch64::LDURWi: case AArch64::LDURSi: case AArch64::LDURSWi: case AArch64::STURWi: case AArch64::STURSi: - OffsetStride = 4; - break; + return 4; } +} + +// Scale the unscaled offsets. Returns false if the unscaled offset can't be +// scaled. +static bool scaleOffset(unsigned Opc, int64_t &Offset) { + unsigned OffsetStride = getOffsetStride(Opc); + if (OffsetStride == 0) + return false; // If the byte-offset isn't a multiple of the stride, we can't scale this // offset. if (Offset % OffsetStride != 0) @@ -2335,6 +2123,19 @@ static bool scaleOffset(unsigned Opc, int64_t &Offset) { return true; } +// Unscale the scaled offsets. Returns false if the scaled offset can't be +// unscaled. +static bool unscaleOffset(unsigned Opc, int64_t &Offset) { + unsigned OffsetStride = getOffsetStride(Opc); + if (OffsetStride == 0) + return false; + + // Convert the "element" offset used by scaled pair load/store instructions + // into the byte-offset used by unscaled. + Offset *= OffsetStride; + return true; +} + static bool canPairLdStOpc(unsigned FirstOpc, unsigned SecondOpc) { if (FirstOpc == SecondOpc) return true; @@ -2353,15 +2154,46 @@ static bool canPairLdStOpc(unsigned FirstOpc, unsigned SecondOpc) { return false; } +static bool shouldClusterFI(const MachineFrameInfo &MFI, int FI1, + int64_t Offset1, unsigned Opcode1, int FI2, + int64_t Offset2, unsigned Opcode2) { + // Accesses through fixed stack object frame indices may access a different + // fixed stack slot. Check that the object offsets + offsets match. + if (MFI.isFixedObjectIndex(FI1) && MFI.isFixedObjectIndex(FI2)) { + int64_t ObjectOffset1 = MFI.getObjectOffset(FI1); + int64_t ObjectOffset2 = MFI.getObjectOffset(FI2); + assert(ObjectOffset1 <= ObjectOffset2 && "Object offsets are not ordered."); + // Get the byte-offset from the object offset. + if (!unscaleOffset(Opcode1, Offset1) || !unscaleOffset(Opcode2, Offset2)) + return false; + ObjectOffset1 += Offset1; + ObjectOffset2 += Offset2; + // Get the "element" index in the object. + if (!scaleOffset(Opcode1, ObjectOffset1) || + !scaleOffset(Opcode2, ObjectOffset2)) + return false; + return ObjectOffset1 + 1 == ObjectOffset2; + } + + return FI1 == FI2; +} + /// Detect opportunities for ldp/stp formation. /// -/// Only called for LdSt for which getMemOpBaseRegImmOfs returns true. -bool AArch64InstrInfo::shouldClusterMemOps(MachineInstr &FirstLdSt, - unsigned BaseReg1, - MachineInstr &SecondLdSt, - unsigned BaseReg2, +/// Only called for LdSt for which getMemOperandWithOffset returns true. +bool AArch64InstrInfo::shouldClusterMemOps(MachineOperand &BaseOp1, + MachineOperand &BaseOp2, unsigned NumLoads) const { - if (BaseReg1 != BaseReg2) + MachineInstr &FirstLdSt = *BaseOp1.getParent(); + MachineInstr &SecondLdSt = *BaseOp2.getParent(); + if (BaseOp1.getType() != BaseOp2.getType()) + return false; + + assert((BaseOp1.isReg() || BaseOp1.isFI()) && + "Only base registers and frame indices are supported."); + + // Check for both base regs and base FI. + if (BaseOp1.isReg() && BaseOp1.getReg() != BaseOp2.getReg()) return false; // Only cluster up to a single pair. @@ -2397,7 +2229,20 @@ bool AArch64InstrInfo::shouldClusterMemOps(MachineInstr &FirstLdSt, return false; // The caller should already have ordered First/SecondLdSt by offset. - assert(Offset1 <= Offset2 && "Caller should have ordered offsets."); + // Note: except for non-equal frame index bases + if (BaseOp1.isFI()) { + assert((!BaseOp1.isIdenticalTo(BaseOp2) || Offset1 >= Offset2) && + "Caller should have ordered offsets."); + + const MachineFrameInfo &MFI = + FirstLdSt.getParent()->getParent()->getFrameInfo(); + return shouldClusterFI(MFI, BaseOp1.getIndex(), Offset1, FirstOpc, + BaseOp2.getIndex(), Offset2, SecondOpc); + } + + assert((!BaseOp1.isIdenticalTo(BaseOp2) || Offset1 <= Offset2) && + "Caller should have ordered offsets."); + return Offset1 + 1 == Offset2; } @@ -2478,7 +2323,7 @@ void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, .addImm(0) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)); } - } else if (SrcReg == AArch64::WZR && Subtarget.hasZeroCycleZeroing()) { + } else if (SrcReg == AArch64::WZR && Subtarget.hasZeroCycleZeroingGP()) { BuildMI(MBB, I, DL, get(AArch64::MOVZWi), DestReg) .addImm(0) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)); @@ -2515,7 +2360,7 @@ void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, .addReg(SrcReg, getKillRegState(KillSrc)) .addImm(0) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)); - } else if (SrcReg == AArch64::XZR && Subtarget.hasZeroCycleZeroing()) { + } else if (SrcReg == AArch64::XZR && Subtarget.hasZeroCycleZeroingGP()) { BuildMI(MBB, I, DL, get(AArch64::MOVZXi), DestReg) .addImm(0) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)); @@ -2730,13 +2575,33 @@ void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, llvm_unreachable("unimplemented reg-to-reg copy"); } +static void storeRegPairToStackSlot(const TargetRegisterInfo &TRI, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator InsertBefore, + const MCInstrDesc &MCID, + unsigned SrcReg, bool IsKill, + unsigned SubIdx0, unsigned SubIdx1, int FI, + MachineMemOperand *MMO) { + unsigned SrcReg0 = SrcReg; + unsigned SrcReg1 = SrcReg; + if (TargetRegisterInfo::isPhysicalRegister(SrcReg)) { + SrcReg0 = TRI.getSubReg(SrcReg, SubIdx0); + SubIdx0 = 0; + SrcReg1 = TRI.getSubReg(SrcReg, SubIdx1); + SubIdx1 = 0; + } + BuildMI(MBB, InsertBefore, DebugLoc(), MCID) + .addReg(SrcReg0, getKillRegState(IsKill), SubIdx0) + .addReg(SrcReg1, getKillRegState(IsKill), SubIdx1) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); +} + void AArch64InstrInfo::storeRegToStackSlot( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, unsigned SrcReg, bool isKill, int FI, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { - DebugLoc DL; - if (MBBI != MBB.end()) - DL = MBBI->getDebugLoc(); MachineFunction &MF = *MBB.getParent(); MachineFrameInfo &MFI = MF.getFrameInfo(); unsigned Align = MFI.getObjectAlignment(FI); @@ -2772,8 +2637,14 @@ void AArch64InstrInfo::storeRegToStackSlot( MF.getRegInfo().constrainRegClass(SrcReg, &AArch64::GPR64RegClass); else assert(SrcReg != AArch64::SP); - } else if (AArch64::FPR64RegClass.hasSubClassEq(RC)) + } else if (AArch64::FPR64RegClass.hasSubClassEq(RC)) { Opc = AArch64::STRDui; + } else if (AArch64::WSeqPairsClassRegClass.hasSubClassEq(RC)) { + storeRegPairToStackSlot(getRegisterInfo(), MBB, MBBI, + get(AArch64::STPWi), SrcReg, isKill, + AArch64::sube32, AArch64::subo32, FI, MMO); + return; + } break; case 16: if (AArch64::FPR128RegClass.hasSubClassEq(RC)) @@ -2783,14 +2654,9 @@ void AArch64InstrInfo::storeRegToStackSlot( Opc = AArch64::ST1Twov1d; Offset = false; } else if (AArch64::XSeqPairsClassRegClass.hasSubClassEq(RC)) { - BuildMI(MBB, MBBI, DL, get(AArch64::STPXi)) - .addReg(TRI->getSubReg(SrcReg, AArch64::sube64), - getKillRegState(isKill)) - .addReg(TRI->getSubReg(SrcReg, AArch64::subo64), - getKillRegState(isKill)) - .addFrameIndex(FI) - .addImm(0) - .addMemOperand(MMO); + storeRegPairToStackSlot(getRegisterInfo(), MBB, MBBI, + get(AArch64::STPXi), SrcReg, isKill, + AArch64::sube64, AArch64::subo64, FI, MMO); return; } break; @@ -2829,7 +2695,7 @@ void AArch64InstrInfo::storeRegToStackSlot( } assert(Opc && "Unknown register class"); - const MachineInstrBuilder MI = BuildMI(MBB, MBBI, DL, get(Opc)) + const MachineInstrBuilder MI = BuildMI(MBB, MBBI, DebugLoc(), get(Opc)) .addReg(SrcReg, getKillRegState(isKill)) .addFrameIndex(FI); @@ -2838,13 +2704,35 @@ void AArch64InstrInfo::storeRegToStackSlot( MI.addMemOperand(MMO); } +static void loadRegPairFromStackSlot(const TargetRegisterInfo &TRI, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator InsertBefore, + const MCInstrDesc &MCID, + unsigned DestReg, unsigned SubIdx0, + unsigned SubIdx1, int FI, + MachineMemOperand *MMO) { + unsigned DestReg0 = DestReg; + unsigned DestReg1 = DestReg; + bool IsUndef = true; + if (TargetRegisterInfo::isPhysicalRegister(DestReg)) { + DestReg0 = TRI.getSubReg(DestReg, SubIdx0); + SubIdx0 = 0; + DestReg1 = TRI.getSubReg(DestReg, SubIdx1); + SubIdx1 = 0; + IsUndef = false; + } + BuildMI(MBB, InsertBefore, DebugLoc(), MCID) + .addReg(DestReg0, RegState::Define | getUndefRegState(IsUndef), SubIdx0) + .addReg(DestReg1, RegState::Define | getUndefRegState(IsUndef), SubIdx1) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); +} + void AArch64InstrInfo::loadRegFromStackSlot( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, unsigned DestReg, int FI, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { - DebugLoc DL; - if (MBBI != MBB.end()) - DL = MBBI->getDebugLoc(); MachineFunction &MF = *MBB.getParent(); MachineFrameInfo &MFI = MF.getFrameInfo(); unsigned Align = MFI.getObjectAlignment(FI); @@ -2880,8 +2768,14 @@ void AArch64InstrInfo::loadRegFromStackSlot( MF.getRegInfo().constrainRegClass(DestReg, &AArch64::GPR64RegClass); else assert(DestReg != AArch64::SP); - } else if (AArch64::FPR64RegClass.hasSubClassEq(RC)) + } else if (AArch64::FPR64RegClass.hasSubClassEq(RC)) { Opc = AArch64::LDRDui; + } else if (AArch64::WSeqPairsClassRegClass.hasSubClassEq(RC)) { + loadRegPairFromStackSlot(getRegisterInfo(), MBB, MBBI, + get(AArch64::LDPWi), DestReg, AArch64::sube32, + AArch64::subo32, FI, MMO); + return; + } break; case 16: if (AArch64::FPR128RegClass.hasSubClassEq(RC)) @@ -2891,14 +2785,9 @@ void AArch64InstrInfo::loadRegFromStackSlot( Opc = AArch64::LD1Twov1d; Offset = false; } else if (AArch64::XSeqPairsClassRegClass.hasSubClassEq(RC)) { - BuildMI(MBB, MBBI, DL, get(AArch64::LDPXi)) - .addReg(TRI->getSubReg(DestReg, AArch64::sube64), - getDefRegState(true)) - .addReg(TRI->getSubReg(DestReg, AArch64::subo64), - getDefRegState(true)) - .addFrameIndex(FI) - .addImm(0) - .addMemOperand(MMO); + loadRegPairFromStackSlot(getRegisterInfo(), MBB, MBBI, + get(AArch64::LDPXi), DestReg, AArch64::sube64, + AArch64::subo64, FI, MMO); return; } break; @@ -2937,7 +2826,7 @@ void AArch64InstrInfo::loadRegFromStackSlot( } assert(Opc && "Unknown register class"); - const MachineInstrBuilder MI = BuildMI(MBB, MBBI, DL, get(Opc)) + const MachineInstrBuilder MI = BuildMI(MBB, MBBI, DebugLoc(), get(Opc)) .addReg(DestReg, getDefRegState(true)) .addFrameIndex(FI); if (Offset) @@ -2949,7 +2838,8 @@ void llvm::emitFrameOffset(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int Offset, const TargetInstrInfo *TII, - MachineInstr::MIFlag Flag, bool SetNZCV) { + MachineInstr::MIFlag Flag, bool SetNZCV, + bool NeedsWinCFI) { if (DestReg == SrcReg && Offset == 0) return; @@ -2994,6 +2884,11 @@ void llvm::emitFrameOffset(MachineBasicBlock &MBB, .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, ShiftSize)) .setMIFlag(Flag); + if (NeedsWinCFI && SrcReg == AArch64::SP && DestReg == AArch64::SP) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(ThisVal) + .setMIFlag(Flag); + SrcReg = DestReg; Offset -= ThisVal; if (Offset == 0) @@ -3004,6 +2899,21 @@ void llvm::emitFrameOffset(MachineBasicBlock &MBB, .addImm(Offset) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)) .setMIFlag(Flag); + + if (NeedsWinCFI) { + if ((DestReg == AArch64::FP && SrcReg == AArch64::SP) || + (SrcReg == AArch64::FP && DestReg == AArch64::SP)) { + if (Offset == 0) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_SetFP)). + setMIFlag(Flag); + else + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_AddFP)). + addImm(Offset).setMIFlag(Flag); + } else if (DestReg == AArch64::SP) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)). + addImm(Offset).setMIFlag(Flag); + } + } } MachineInstr *AArch64InstrInfo::foldMemoryOperandImpl( @@ -4839,7 +4749,10 @@ AArch64InstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const { using namespace AArch64II; static const std::pair<unsigned, const char *> TargetFlags[] = { - {MO_GOT, "aarch64-got"}, {MO_NC, "aarch64-nc"}, {MO_TLS, "aarch64-tls"}}; + {MO_COFFSTUB, "aarch64-coffstub"}, + {MO_GOT, "aarch64-got"}, {MO_NC, "aarch64-nc"}, + {MO_S, "aarch64-s"}, {MO_TLS, "aarch64-tls"}, + {MO_DLLIMPORT, "aarch64-dllimport"}}; return makeArrayRef(TargetFlags); } @@ -4941,11 +4854,13 @@ enum MachineOutlinerClass { enum MachineOutlinerMBBFlags { LRUnavailableSomewhere = 0x2, - HasCalls = 0x4 + HasCalls = 0x4, + UnsafeRegsDead = 0x8 }; unsigned AArch64InstrInfo::findRegisterToSaveLRTo(const outliner::Candidate &C) const { + assert(C.LRUWasSet && "LRU wasn't set?"); MachineFunction *MF = C.getMF(); const AArch64RegisterInfo *ARI = static_cast<const AArch64RegisterInfo *>( MF->getSubtarget().getRegisterInfo()); @@ -4968,17 +4883,22 @@ AArch64InstrInfo::findRegisterToSaveLRTo(const outliner::Candidate &C) const { outliner::OutlinedFunction AArch64InstrInfo::getOutliningCandidateInfo( std::vector<outliner::Candidate> &RepeatedSequenceLocs) const { - unsigned SequenceSize = std::accumulate( - RepeatedSequenceLocs[0].front(), - std::next(RepeatedSequenceLocs[0].back()), - 0, [this](unsigned Sum, const MachineInstr &MI) { - return Sum + getInstSizeInBytes(MI); - }); + outliner::Candidate &FirstCand = RepeatedSequenceLocs[0]; + unsigned SequenceSize = + std::accumulate(FirstCand.front(), std::next(FirstCand.back()), 0, + [this](unsigned Sum, const MachineInstr &MI) { + return Sum + getInstSizeInBytes(MI); + }); - // Compute liveness information for each candidate. + // Properties about candidate MBBs that hold for all of them. + unsigned FlagsSetInAll = 0xF; + + // Compute liveness information for each candidate, and set FlagsSetInAll. const TargetRegisterInfo &TRI = getRegisterInfo(); std::for_each(RepeatedSequenceLocs.begin(), RepeatedSequenceLocs.end(), - [&TRI](outliner::Candidate &C) { C.initLRU(TRI); }); + [&FlagsSetInAll](outliner::Candidate &C) { + FlagsSetInAll &= C.Flags; + }); // According to the AArch64 Procedure Call Standard, the following are // undefined on entry/exit from a function call: @@ -4991,23 +4911,31 @@ AArch64InstrInfo::getOutliningCandidateInfo( // of these registers is live into/across it. Thus, we need to delete // those // candidates. - auto CantGuaranteeValueAcrossCall = [](outliner::Candidate &C) { + auto CantGuaranteeValueAcrossCall = [&TRI](outliner::Candidate &C) { + // If the unsafe registers in this block are all dead, then we don't need + // to compute liveness here. + if (C.Flags & UnsafeRegsDead) + return false; + C.initLRU(TRI); LiveRegUnits LRU = C.LRU; return (!LRU.available(AArch64::W16) || !LRU.available(AArch64::W17) || !LRU.available(AArch64::NZCV)); }; - // Erase every candidate that violates the restrictions above. (It could be - // true that we have viable candidates, so it's not worth bailing out in - // the case that, say, 1 out of 20 candidates violate the restructions.) - RepeatedSequenceLocs.erase(std::remove_if(RepeatedSequenceLocs.begin(), - RepeatedSequenceLocs.end(), - CantGuaranteeValueAcrossCall), - RepeatedSequenceLocs.end()); + // Are there any candidates where those registers are live? + if (!(FlagsSetInAll & UnsafeRegsDead)) { + // Erase every candidate that violates the restrictions above. (It could be + // true that we have viable candidates, so it's not worth bailing out in + // the case that, say, 1 out of 20 candidates violate the restructions.) + RepeatedSequenceLocs.erase(std::remove_if(RepeatedSequenceLocs.begin(), + RepeatedSequenceLocs.end(), + CantGuaranteeValueAcrossCall), + RepeatedSequenceLocs.end()); - // If the sequence is empty, we're done. - if (RepeatedSequenceLocs.empty()) - return outliner::OutlinedFunction(); + // If the sequence doesn't have enough candidates left, then we're done. + if (RepeatedSequenceLocs.size() < 2) + return outliner::OutlinedFunction(); + } // At this point, we have only "safe" candidates to outline. Figure out // frame + call instruction information. @@ -5024,6 +4952,64 @@ AArch64InstrInfo::getOutliningCandidateInfo( unsigned FrameID = MachineOutlinerDefault; unsigned NumBytesToCreateFrame = 4; + bool HasBTI = any_of(RepeatedSequenceLocs, [](outliner::Candidate &C) { + return C.getMF()->getFunction().hasFnAttribute("branch-target-enforcement"); + }); + + // Returns true if an instructions is safe to fix up, false otherwise. + auto IsSafeToFixup = [this, &TRI](MachineInstr &MI) { + if (MI.isCall()) + return true; + + if (!MI.modifiesRegister(AArch64::SP, &TRI) && + !MI.readsRegister(AArch64::SP, &TRI)) + return true; + + // Any modification of SP will break our code to save/restore LR. + // FIXME: We could handle some instructions which add a constant + // offset to SP, with a bit more work. + if (MI.modifiesRegister(AArch64::SP, &TRI)) + return false; + + // At this point, we have a stack instruction that we might need to + // fix up. We'll handle it if it's a load or store. + if (MI.mayLoadOrStore()) { + MachineOperand *Base; // Filled with the base operand of MI. + int64_t Offset; // Filled with the offset of MI. + + // Does it allow us to offset the base operand and is the base the + // register SP? + if (!getMemOperandWithOffset(MI, Base, Offset, &TRI) || !Base->isReg() || + Base->getReg() != AArch64::SP) + return false; + + // Find the minimum/maximum offset for this instruction and check + // if fixing it up would be in range. + int64_t MinOffset, + MaxOffset; // Unscaled offsets for the instruction. + unsigned Scale; // The scale to multiply the offsets by. + unsigned DummyWidth; + getMemOpInfo(MI.getOpcode(), Scale, DummyWidth, MinOffset, MaxOffset); + + Offset += 16; // Update the offset to what it would be if we outlined. + if (Offset < MinOffset * Scale || Offset > MaxOffset * Scale) + return false; + + // It's in range, so we can outline it. + return true; + } + + // FIXME: Add handling for instructions like "add x0, sp, #8". + + // We can't fix it up, so don't outline it. + return false; + }; + + // True if it's possible to fix up each stack instruction in this sequence. + // Important for frames/call variants that modify the stack. + bool AllStackInstrsSafe = std::all_of( + FirstCand.front(), std::next(FirstCand.back()), IsSafeToFixup); + // If the last instruction in any candidate is a terminator, then we should // tail call all of the candidates. if (RepeatedSequenceLocs[0].back()->isTerminator()) { @@ -5032,65 +5018,102 @@ AArch64InstrInfo::getOutliningCandidateInfo( SetCandidateCallInfo(MachineOutlinerTailCall, 4); } - else if (LastInstrOpcode == AArch64::BL || LastInstrOpcode == AArch64::BLR) { + else if (LastInstrOpcode == AArch64::BL || + (LastInstrOpcode == AArch64::BLR && !HasBTI)) { // FIXME: Do we need to check if the code after this uses the value of LR? FrameID = MachineOutlinerThunk; NumBytesToCreateFrame = 0; SetCandidateCallInfo(MachineOutlinerThunk, 4); } - // Make sure that LR isn't live on entry to this candidate. The only - // instructions that use LR that could possibly appear in a repeated sequence - // are calls. Therefore, we only have to check and see if LR is dead on entry - // to (or exit from) some candidate. - else if (std::all_of(RepeatedSequenceLocs.begin(), - RepeatedSequenceLocs.end(), - [](outliner::Candidate &C) { - return C.LRU.available(AArch64::LR); - })) { - FrameID = MachineOutlinerNoLRSave; - NumBytesToCreateFrame = 4; - SetCandidateCallInfo(MachineOutlinerNoLRSave, 4); - } - - // LR is live, so we need to save it. Decide whether it should be saved to - // the stack, or if it can be saved to a register. else { - if (std::all_of(RepeatedSequenceLocs.begin(), RepeatedSequenceLocs.end(), - [this](outliner::Candidate &C) { - return findRegisterToSaveLRTo(C); - })) { - // Every candidate has an available callee-saved register for the save. - // We can save LR to a register. - FrameID = MachineOutlinerRegSave; - NumBytesToCreateFrame = 4; - SetCandidateCallInfo(MachineOutlinerRegSave, 12); + // We need to decide how to emit calls + frames. We can always emit the same + // frame if we don't need to save to the stack. If we have to save to the + // stack, then we need a different frame. + unsigned NumBytesNoStackCalls = 0; + std::vector<outliner::Candidate> CandidatesWithoutStackFixups; + + for (outliner::Candidate &C : RepeatedSequenceLocs) { + C.initLRU(TRI); + + // Is LR available? If so, we don't need a save. + if (C.LRU.available(AArch64::LR)) { + NumBytesNoStackCalls += 4; + C.setCallInfo(MachineOutlinerNoLRSave, 4); + CandidatesWithoutStackFixups.push_back(C); + } + + // Is an unused register available? If so, we won't modify the stack, so + // we can outline with the same frame type as those that don't save LR. + else if (findRegisterToSaveLRTo(C)) { + NumBytesNoStackCalls += 12; + C.setCallInfo(MachineOutlinerRegSave, 12); + CandidatesWithoutStackFixups.push_back(C); + } + + // Is SP used in the sequence at all? If not, we don't have to modify + // the stack, so we are guaranteed to get the same frame. + else if (C.UsedInSequence.available(AArch64::SP)) { + NumBytesNoStackCalls += 12; + C.setCallInfo(MachineOutlinerDefault, 12); + CandidatesWithoutStackFixups.push_back(C); + } + + // If we outline this, we need to modify the stack. Pretend we don't + // outline this by saving all of its bytes. + else { + NumBytesNoStackCalls += SequenceSize; + } } - else { - // At least one candidate does not have an available callee-saved - // register. We must save LR to the stack. - FrameID = MachineOutlinerDefault; - NumBytesToCreateFrame = 4; + // If there are no places where we have to save LR, then note that we + // don't have to update the stack. Otherwise, give every candidate the + // default call type, as long as it's safe to do so. + if (!AllStackInstrsSafe || + NumBytesNoStackCalls <= RepeatedSequenceLocs.size() * 12) { + RepeatedSequenceLocs = CandidatesWithoutStackFixups; + FrameID = MachineOutlinerNoLRSave; + } else { SetCandidateCallInfo(MachineOutlinerDefault, 12); } + + // If we dropped all of the candidates, bail out here. + if (RepeatedSequenceLocs.size() < 2) { + RepeatedSequenceLocs.clear(); + return outliner::OutlinedFunction(); + } } - // Check if the range contains a call. These require a save + restore of the - // link register. - if (std::any_of(RepeatedSequenceLocs[0].front(), - RepeatedSequenceLocs[0].back(), - [](const MachineInstr &MI) { return MI.isCall(); })) - NumBytesToCreateFrame += 8; // Save + restore the link register. + // Does every candidate's MBB contain a call? If so, then we might have a call + // in the range. + if (FlagsSetInAll & MachineOutlinerMBBFlags::HasCalls) { + // Check if the range contains a call. These require a save + restore of the + // link register. + bool ModStackToSaveLR = false; + if (std::any_of(FirstCand.front(), FirstCand.back(), + [](const MachineInstr &MI) { return MI.isCall(); })) + ModStackToSaveLR = true; + + // Handle the last instruction separately. If this is a tail call, then the + // last instruction is a call. We don't want to save + restore in this case. + // However, it could be possible that the last instruction is a call without + // it being valid to tail call this sequence. We should consider this as + // well. + else if (FrameID != MachineOutlinerThunk && + FrameID != MachineOutlinerTailCall && FirstCand.back()->isCall()) + ModStackToSaveLR = true; - // Handle the last instruction separately. If this is a tail call, then the - // last instruction is a call. We don't want to save + restore in this case. - // However, it could be possible that the last instruction is a call without - // it being valid to tail call this sequence. We should consider this as well. - else if (FrameID != MachineOutlinerThunk && - FrameID != MachineOutlinerTailCall && - RepeatedSequenceLocs[0].back()->isCall()) - NumBytesToCreateFrame += 8; + if (ModStackToSaveLR) { + // We can't fix up the stack. Bail out. + if (!AllStackInstrsSafe) { + RepeatedSequenceLocs.clear(); + return outliner::OutlinedFunction(); + } + + // Save + restore LR. + NumBytesToCreateFrame += 8; + } + } return outliner::OutlinedFunction(RepeatedSequenceLocs, SequenceSize, NumBytesToCreateFrame, FrameID); @@ -5122,30 +5145,70 @@ bool AArch64InstrInfo::isFunctionSafeToOutlineFrom( return true; } -unsigned -AArch64InstrInfo::getMachineOutlinerMBBFlags(MachineBasicBlock &MBB) const { - unsigned Flags = 0x0; - // Check if there's a call inside this MachineBasicBlock. If there is, then - // set a flag. - if (std::any_of(MBB.begin(), MBB.end(), - [](MachineInstr &MI) { return MI.isCall(); })) - Flags |= MachineOutlinerMBBFlags::HasCalls; - +bool AArch64InstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, + unsigned &Flags) const { // Check if LR is available through all of the MBB. If it's not, then set // a flag. assert(MBB.getParent()->getRegInfo().tracksLiveness() && "Suitable Machine Function for outlining must track liveness"); LiveRegUnits LRU(getRegisterInfo()); - LRU.addLiveOuts(MBB); - std::for_each(MBB.rbegin(), - MBB.rend(), + std::for_each(MBB.rbegin(), MBB.rend(), [&LRU](MachineInstr &MI) { LRU.accumulate(MI); }); - if (!LRU.available(AArch64::LR)) - Flags |= MachineOutlinerMBBFlags::LRUnavailableSomewhere; + // Check if each of the unsafe registers are available... + bool W16AvailableInBlock = LRU.available(AArch64::W16); + bool W17AvailableInBlock = LRU.available(AArch64::W17); + bool NZCVAvailableInBlock = LRU.available(AArch64::NZCV); + + // If all of these are dead (and not live out), we know we don't have to check + // them later. + if (W16AvailableInBlock && W17AvailableInBlock && NZCVAvailableInBlock) + Flags |= MachineOutlinerMBBFlags::UnsafeRegsDead; + + // Now, add the live outs to the set. + LRU.addLiveOuts(MBB); - return Flags; + // If any of these registers is available in the MBB, but also a live out of + // the block, then we know outlining is unsafe. + if (W16AvailableInBlock && !LRU.available(AArch64::W16)) + return false; + if (W17AvailableInBlock && !LRU.available(AArch64::W17)) + return false; + if (NZCVAvailableInBlock && !LRU.available(AArch64::NZCV)) + return false; + + // Check if there's a call inside this MachineBasicBlock. If there is, then + // set a flag. + if (any_of(MBB, [](MachineInstr &MI) { return MI.isCall(); })) + Flags |= MachineOutlinerMBBFlags::HasCalls; + + MachineFunction *MF = MBB.getParent(); + + // In the event that we outline, we may have to save LR. If there is an + // available register in the MBB, then we'll always save LR there. Check if + // this is true. + bool CanSaveLR = false; + const AArch64RegisterInfo *ARI = static_cast<const AArch64RegisterInfo *>( + MF->getSubtarget().getRegisterInfo()); + + // Check if there is an available register across the sequence that we can + // use. + for (unsigned Reg : AArch64::GPR64RegClass) { + if (!ARI->isReservedReg(*MF, Reg) && Reg != AArch64::LR && + Reg != AArch64::X16 && Reg != AArch64::X17 && LRU.available(Reg)) { + CanSaveLR = true; + break; + } + } + + // Check if we have a register we can save LR to, and if LR was used + // somewhere. If both of those things are true, then we need to evaluate the + // safety of outlining stack instructions later. + if (!CanSaveLR && !LRU.available(AArch64::LR)) + Flags |= MachineOutlinerMBBFlags::LRUnavailableSomewhere; + + return true; } outliner::InstrType @@ -5268,108 +5331,19 @@ AArch64InstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT, MI.modifiesRegister(AArch64::W30, &getRegisterInfo())) return outliner::InstrType::Illegal; - // Does this use the stack? - if (MI.modifiesRegister(AArch64::SP, &RI) || - MI.readsRegister(AArch64::SP, &RI)) { - // True if there is no chance that any outlined candidate from this range - // could require stack fixups. That is, both - // * LR is available in the range (No save/restore around call) - // * The range doesn't include calls (No save/restore in outlined frame) - // are true. - // FIXME: This is very restrictive; the flags check the whole block, - // not just the bit we will try to outline. - bool MightNeedStackFixUp = - (Flags & (MachineOutlinerMBBFlags::LRUnavailableSomewhere | - MachineOutlinerMBBFlags::HasCalls)); - - // If this instruction is in a range where it *never* needs to be fixed - // up, then we can *always* outline it. This is true even if it's not - // possible to fix that instruction up. - // - // Why? Consider two equivalent instructions I1, I2 where both I1 and I2 - // use SP. Suppose that I1 sits within a range that definitely doesn't - // need stack fixups, while I2 sits in a range that does. - // - // First, I1 can be outlined as long as we *never* fix up the stack in - // any sequence containing it. I1 is already a safe instruction in the - // original program, so as long as we don't modify it we're good to go. - // So this leaves us with showing that outlining I2 won't break our - // program. - // - // Suppose I1 and I2 belong to equivalent candidate sequences. When we - // look at I2, we need to see if it can be fixed up. Suppose I2, (and - // thus I1) cannot be fixed up. Then I2 will be assigned an unique - // integer label; thus, I2 cannot belong to any candidate sequence (a - // contradiction). Suppose I2 can be fixed up. Then I1 can be fixed up - // as well, so we're good. Thus, I1 is always safe to outline. - // - // This gives us two things: first off, it buys us some more instructions - // for our search space by deeming stack instructions illegal only when - // they can't be fixed up AND we might have to fix them up. Second off, - // This allows us to catch tricky instructions like, say, - // %xi = ADDXri %sp, n, 0. We can't safely outline these since they might - // be paired with later SUBXris, which might *not* end up being outlined. - // If we mess with the stack to save something, then an ADDXri messes with - // it *after*, then we aren't going to restore the right something from - // the stack if we don't outline the corresponding SUBXri first. ADDXris and - // SUBXris are extremely common in prologue/epilogue code, so supporting - // them in the outliner can be a pretty big win! - if (!MightNeedStackFixUp) - return outliner::InstrType::Legal; - - // Any modification of SP will break our code to save/restore LR. - // FIXME: We could handle some instructions which add a constant offset to - // SP, with a bit more work. - if (MI.modifiesRegister(AArch64::SP, &RI)) - return outliner::InstrType::Illegal; - - // At this point, we have a stack instruction that we might need to fix - // up. We'll handle it if it's a load or store. - if (MI.mayLoadOrStore()) { - unsigned Base; // Filled with the base regiser of MI. - int64_t Offset; // Filled with the offset of MI. - unsigned DummyWidth; - - // Does it allow us to offset the base register and is the base SP? - if (!getMemOpBaseRegImmOfsWidth(MI, Base, Offset, DummyWidth, &RI) || - Base != AArch64::SP) - return outliner::InstrType::Illegal; - - // Find the minimum/maximum offset for this instruction and check if - // fixing it up would be in range. - int64_t MinOffset, MaxOffset; // Unscaled offsets for the instruction. - unsigned Scale; // The scale to multiply the offsets by. - getMemOpInfo(MI.getOpcode(), Scale, DummyWidth, MinOffset, MaxOffset); - - // TODO: We should really test what happens if an instruction overflows. - // This is tricky to test with IR tests, but when the outliner is moved - // to a MIR test, it really ought to be checked. - Offset += 16; // Update the offset to what it would be if we outlined. - if (Offset < MinOffset * Scale || Offset > MaxOffset * Scale) - return outliner::InstrType::Illegal; - - // It's in range, so we can outline it. - return outliner::InstrType::Legal; - } - - // FIXME: Add handling for instructions like "add x0, sp, #8". - - // We can't fix it up, so don't outline it. - return outliner::InstrType::Illegal; - } - return outliner::InstrType::Legal; } void AArch64InstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const { for (MachineInstr &MI : MBB) { - unsigned Base, Width; + MachineOperand *Base; + unsigned Width; int64_t Offset; // Is this a load or store with an immediate offset with SP as the base? if (!MI.mayLoadOrStore() || - !getMemOpBaseRegImmOfsWidth(MI, Base, Offset, Width, &RI) || - Base != AArch64::SP) + !getMemOperandWithOffsetWidth(MI, Base, Offset, Width, &RI) || + (Base->isReg() && Base->getReg() != AArch64::SP)) continue; // It is, so we have to fix it up. @@ -5401,7 +5375,7 @@ void AArch64InstrInfo::buildOutlinedFrame( TailOpcode = AArch64::TCRETURNdi; } else { assert(Call->getOpcode() == AArch64::BLR); - TailOpcode = AArch64::TCRETURNri; + TailOpcode = AArch64::TCRETURNriALL; } MachineInstr *TC = BuildMI(MF, DebugLoc(), get(TailOpcode)) .add(Call->getOperand(0)) @@ -5562,3 +5536,6 @@ bool AArch64InstrInfo::shouldOutlineFromFunctionByDefault( MachineFunction &MF) const { return MF.getFunction().optForMinSize(); } + +#define GET_INSTRINFO_HELPERS +#include "AArch64GenInstrInfo.inc" diff --git a/lib/Target/AArch64/AArch64InstrInfo.h b/lib/Target/AArch64/AArch64InstrInfo.h index 11882e238b70..9954669d5675 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.h +++ b/lib/Target/AArch64/AArch64InstrInfo.h @@ -62,14 +62,6 @@ public: unsigned isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex) const override; - /// Returns true if there is a shiftable register and that the shift value - /// is non-zero. - static bool hasShiftedReg(const MachineInstr &MI); - - /// Returns true if there is an extendable register and that the extending - /// value is non-zero. - static bool hasExtendedReg(const MachineInstr &MI); - /// Does this instruction set its full destination register to zero? static bool isGPRZero(const MachineInstr &MI); @@ -79,11 +71,6 @@ public: /// Does this instruction rename an FPR without modifying bits? static bool isFPRCopy(const MachineInstr &MI); - /// Return true if this is load/store scales or extends its register offset. - /// This refers to scaling a dynamic index as opposed to scaled immediates. - /// MI should be a memory op that allows scaled addressing. - static bool isScaledAddr(const MachineInstr &MI); - /// Return true if pairing the given load or store is hinted to be /// unprofitable. static bool isLdStPairSuppressed(const MachineInstr &MI); @@ -110,13 +97,13 @@ public: /// Hint that pairing the given load or store is unprofitable. static void suppressLdStPair(MachineInstr &MI); - bool getMemOpBaseRegImmOfs(MachineInstr &LdSt, unsigned &BaseReg, - int64_t &Offset, - const TargetRegisterInfo *TRI) const override; + bool getMemOperandWithOffset(MachineInstr &MI, MachineOperand *&BaseOp, + int64_t &Offset, + const TargetRegisterInfo *TRI) const override; - bool getMemOpBaseRegImmOfsWidth(MachineInstr &LdSt, unsigned &BaseReg, - int64_t &Offset, unsigned &Width, - const TargetRegisterInfo *TRI) const; + bool getMemOperandWithOffsetWidth(MachineInstr &MI, MachineOperand *&BaseOp, + int64_t &Offset, unsigned &Width, + const TargetRegisterInfo *TRI) const; /// Return the immediate offset of the base register in a load/store \p LdSt. MachineOperand &getMemOpBaseRegImmOfsOffsetOperand(MachineInstr &LdSt) const; @@ -128,8 +115,7 @@ public: bool getMemOpInfo(unsigned Opcode, unsigned &Scale, unsigned &Width, int64_t &MinOffset, int64_t &MaxOffset) const; - bool shouldClusterMemOps(MachineInstr &FirstLdSt, unsigned BaseReg1, - MachineInstr &SecondLdSt, unsigned BaseReg2, + bool shouldClusterMemOps(MachineOperand &BaseOp1, MachineOperand &BaseOp2, unsigned NumLoads) const override; void copyPhysRegTuple(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, @@ -189,6 +175,10 @@ public: unsigned FalseReg) const override; void getNoop(MCInst &NopInst) const override; + bool isSchedulingBoundary(const MachineInstr &MI, + const MachineBasicBlock *MBB, + const MachineFunction &MF) const override; + /// analyzeCompare - For a comparison instruction, return the source registers /// in SrcReg and SrcReg2, and the value it compares against in CmpValue. /// Return true if the comparison instruction can be analyzed. @@ -242,7 +232,8 @@ public: std::vector<outliner::Candidate> &RepeatedSequenceLocs) const override; outliner::InstrType getOutliningType(MachineBasicBlock::iterator &MIT, unsigned Flags) const override; - unsigned getMachineOutlinerMBBFlags(MachineBasicBlock &MBB) const override; + bool isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, + unsigned &Flags) const override; void buildOutlinedFrame(MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const override; MachineBasicBlock::iterator @@ -250,15 +241,15 @@ public: MachineBasicBlock::iterator &It, MachineFunction &MF, const outliner::Candidate &C) const override; bool shouldOutlineFromFunctionByDefault(MachineFunction &MF) const override; - /// Returns true if the instruction sets to an immediate value that can be - /// executed more efficiently. - bool isExynosResetFast(const MachineInstr &MI) const; - /// Returns true if the instruction has a shift left that can be executed - /// more efficiently. - bool isExynosShiftLeftFast(const MachineInstr &MI) const; /// Returns true if the instruction has a shift by immediate that can be /// executed in one cycle less. - bool isFalkorShiftExtFast(const MachineInstr &MI) const; + static bool isFalkorShiftExtFast(const MachineInstr &MI); + /// Return true if the instructions is a SEH instruciton used for unwinding + /// on Windows. + static bool isSEHInstruction(const MachineInstr &MI); + +#define GET_INSTRINFO_HELPER_DECLS +#include "AArch64GenInstrInfo.inc" private: /// Sets the offsets on outlined instructions in \p MBB which use SP @@ -286,7 +277,7 @@ void emitFrameOffset(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int Offset, const TargetInstrInfo *TII, MachineInstr::MIFlag = MachineInstr::NoFlags, - bool SetNZCV = false); + bool SetNZCV = false, bool NeedsWinCFI = false); /// rewriteAArch64FrameIndex - Rewrite MI to access 'Offset' bytes from the /// FP. Return false if the offset could not be handled directly in MI, and diff --git a/lib/Target/AArch64/AArch64InstrInfo.td b/lib/Target/AArch64/AArch64InstrInfo.td index d6b8bb5d89c7..c24b8b36441b 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.td +++ b/lib/Target/AArch64/AArch64InstrInfo.td @@ -22,6 +22,56 @@ def HasV8_3a : Predicate<"Subtarget->hasV8_3aOps()">, AssemblerPredicate<"HasV8_3aOps", "armv8.3a">; def HasV8_4a : Predicate<"Subtarget->hasV8_4aOps()">, AssemblerPredicate<"HasV8_4aOps", "armv8.4a">; +def HasV8_5a : Predicate<"Subtarget->hasV8_5aOps()">, + AssemblerPredicate<"HasV8_5aOps", "armv8.5a">; +def HasVH : Predicate<"Subtarget->hasVH()">, + AssemblerPredicate<"FeatureVH", "vh">; + +def HasLOR : Predicate<"Subtarget->hasLOR()">, + AssemblerPredicate<"FeatureLOR", "lor">; + +def HasPA : Predicate<"Subtarget->hasPA()">, + AssemblerPredicate<"FeaturePA", "pa">; + +def HasJS : Predicate<"Subtarget->hasJS()">, + AssemblerPredicate<"FeatureJS", "jsconv">; + +def HasCCIDX : Predicate<"Subtarget->hasCCIDX()">, + AssemblerPredicate<"FeatureCCIDX", "ccidx">; + +def HasComplxNum : Predicate<"Subtarget->hasComplxNum()">, + AssemblerPredicate<"FeatureComplxNum", "complxnum">; + +def HasNV : Predicate<"Subtarget->hasNV()">, + AssemblerPredicate<"FeatureNV", "nv">; + +def HasRASv8_4 : Predicate<"Subtarget->hasRASv8_4()">, + AssemblerPredicate<"FeatureRASv8_4", "rasv8_4">; + +def HasMPAM : Predicate<"Subtarget->hasMPAM()">, + AssemblerPredicate<"FeatureMPAM", "mpam">; + +def HasDIT : Predicate<"Subtarget->hasDIT()">, + AssemblerPredicate<"FeatureDIT", "dit">; + +def HasTRACEV8_4 : Predicate<"Subtarget->hasTRACEV8_4()">, + AssemblerPredicate<"FeatureTRACEV8_4", "tracev8.4">; + +def HasAM : Predicate<"Subtarget->hasAM()">, + AssemblerPredicate<"FeatureAM", "am">; + +def HasSEL2 : Predicate<"Subtarget->hasSEL2()">, + AssemblerPredicate<"FeatureSEL2", "sel2">; + +def HasTLB_RMI : Predicate<"Subtarget->hasTLB_RMI()">, + AssemblerPredicate<"FeatureTLB_RMI", "tlb-rmi">; + +def HasFMI : Predicate<"Subtarget->hasFMI()">, + AssemblerPredicate<"FeatureFMI", "fmi">; + +def HasRCPC_IMMO : Predicate<"Subtarget->hasRCPCImm()">, + AssemblerPredicate<"FeatureRCPC_IMMO", "rcpc-immo">; + def HasFPARMv8 : Predicate<"Subtarget->hasFPARMv8()">, AssemblerPredicate<"FeatureFPARMv8", "fp-armv8">; def HasNEON : Predicate<"Subtarget->hasNEON()">, @@ -49,6 +99,8 @@ def HasRDM : Predicate<"Subtarget->hasRDM()">, def HasPerfMon : Predicate<"Subtarget->hasPerfMon()">; def HasFullFP16 : Predicate<"Subtarget->hasFullFP16()">, AssemblerPredicate<"FeatureFullFP16", "fullfp16">; +def HasFP16FML : Predicate<"Subtarget->hasFP16FML()">, + AssemblerPredicate<"FeatureFP16FML", "fp16fml">; def HasSPE : Predicate<"Subtarget->hasSPE()">, AssemblerPredicate<"FeatureSPE", "spe">; def HasFuseAES : Predicate<"Subtarget->hasFuseAES()">, @@ -58,7 +110,20 @@ def HasSVE : Predicate<"Subtarget->hasSVE()">, AssemblerPredicate<"FeatureSVE", "sve">; def HasRCPC : Predicate<"Subtarget->hasRCPC()">, AssemblerPredicate<"FeatureRCPC", "rcpc">; - +def HasAltNZCV : Predicate<"Subtarget->hasAlternativeNZCV()">, + AssemblerPredicate<"FeatureAltFPCmp", "altnzcv">; +def HasFRInt3264 : Predicate<"Subtarget->hasFRInt3264()">, + AssemblerPredicate<"FeatureFRInt3264", "frint3264">; +def HasSB : Predicate<"Subtarget->hasSB()">, + AssemblerPredicate<"FeatureSB", "sb">; +def HasPredRes : Predicate<"Subtarget->hasPredRes()">, + AssemblerPredicate<"FeaturePredRes", "predres">; +def HasCCDP : Predicate<"Subtarget->hasCCDP()">, + AssemblerPredicate<"FeatureCacheDeepPersist", "ccdp">; +def HasBTI : Predicate<"Subtarget->hasBTI()">, + AssemblerPredicate<"FeatureBranchTargetId", "bti">; +def HasMTE : Predicate<"Subtarget->hasMTE()">, + AssemblerPredicate<"FeatureMTE", "mte">; def IsLE : Predicate<"Subtarget->isLittleEndian()">; def IsBE : Predicate<"!Subtarget->isLittleEndian()">; def UseAlternateSExtLoadCVTF32 @@ -174,6 +239,7 @@ def SDT_AArch64WrapperLarge : SDTypeProfile<1, 4, // Node definitions. def AArch64adrp : SDNode<"AArch64ISD::ADRP", SDTIntUnaryOp, []>; +def AArch64adr : SDNode<"AArch64ISD::ADR", SDTIntUnaryOp, []>; def AArch64addlow : SDNode<"AArch64ISD::ADDlow", SDTIntBinOp, []>; def AArch64LOADgot : SDNode<"AArch64ISD::LOADgot", SDTIntUnaryOp>; def AArch64callseq_start : SDNode<"ISD::CALLSEQ_START", @@ -342,6 +408,9 @@ let RecomputePerFunction = 1 in { def NotForCodeSize : Predicate<"!MF->getFunction().optForSize()">; // Avoid generating STRQro if it is slow, unless we're optimizing for code size. def UseSTRQro : Predicate<"!Subtarget->isSTRQroSlow() || MF->getFunction().optForSize()">; + + def UseBTI : Predicate<[{ MF->getFunction().hasFnAttribute("branch-target-enforcement") }]>; + def NotUseBTI : Predicate<[{ !MF->getFunction().hasFnAttribute("branch-target-enforcement") }]>; } include "AArch64InstrFormats.td" @@ -427,6 +496,38 @@ def : Pat<(AArch64LOADgot texternalsym:$addr), def : Pat<(AArch64LOADgot tconstpool:$addr), (LOADgot tconstpool:$addr)>; +// 32-bit jump table destination is actually only 2 instructions since we can +// use the table itself as a PC-relative base. But optimization occurs after +// branch relaxation so be pessimistic. +let Size = 12, Constraints = "@earlyclobber $dst,@earlyclobber $scratch" in { +def JumpTableDest32 : Pseudo<(outs GPR64:$dst, GPR64sp:$scratch), + (ins GPR64:$table, GPR64:$entry, i32imm:$jti), []>, + Sched<[]>; +def JumpTableDest16 : Pseudo<(outs GPR64:$dst, GPR64sp:$scratch), + (ins GPR64:$table, GPR64:$entry, i32imm:$jti), []>, + Sched<[]>; +def JumpTableDest8 : Pseudo<(outs GPR64:$dst, GPR64sp:$scratch), + (ins GPR64:$table, GPR64:$entry, i32imm:$jti), []>, + Sched<[]>; +} + +// Space-consuming pseudo to aid testing of placement and reachability +// algorithms. Immediate operand is the number of bytes this "instruction" +// occupies; register operands can be used to enforce dependency and constrain +// the scheduler. +let hasSideEffects = 1, mayLoad = 1, mayStore = 1 in +def SPACE : Pseudo<(outs GPR64:$Rd), (ins i32imm:$size, GPR64:$Rn), + [(set GPR64:$Rd, (int_aarch64_space imm:$size, GPR64:$Rn))]>, + Sched<[]>; + +let hasSideEffects = 1, isCodeGenOnly = 1 in { + def SpeculationSafeValueX + : Pseudo<(outs GPR64:$dst), (ins GPR64:$src), []>, Sched<[]>; + def SpeculationSafeValueW + : Pseudo<(outs GPR32:$dst), (ins GPR32:$src), []>, Sched<[]>; +} + + //===----------------------------------------------------------------------===// // System instructions. //===----------------------------------------------------------------------===// @@ -440,6 +541,8 @@ def : InstAlias<"sev", (HINT 0b100)>; def : InstAlias<"sevl", (HINT 0b101)>; def : InstAlias<"esb", (HINT 0b10000)>, Requires<[HasRAS]>; def : InstAlias<"csdb", (HINT 20)>; +def : InstAlias<"bti", (HINT 32)>, Requires<[HasBTI]>; +def : InstAlias<"bti $op", (HINT btihint_op:$op)>, Requires<[HasBTI]>; // v8.2a Statistical Profiling extension def : InstAlias<"psb $op", (HINT psbhint_op:$op)>, Requires<[HasSPE]>; @@ -463,11 +566,11 @@ def ISB : CRmSystemI<barrier_op, 0b110, "isb", def TSB : CRmSystemI<barrier_op, 0b010, "tsb", []> { let CRm = 0b0010; let Inst{12} = 0; - let Predicates = [HasV8_4a]; + let Predicates = [HasTRACEV8_4]; } } -// ARMv8.2 Dot Product +// ARMv8.2-A Dot Product let Predicates = [HasDotProd] in { defm SDOT : SIMDThreeSameVectorDot<0, "sdot", int_aarch64_neon_sdot>; defm UDOT : SIMDThreeSameVectorDot<1, "udot", int_aarch64_neon_udot>; @@ -475,6 +578,18 @@ defm SDOTlane : SIMDThreeSameVectorDotIndex<0, "sdot", int_aarch64_neon_sdot>; defm UDOTlane : SIMDThreeSameVectorDotIndex<1, "udot", int_aarch64_neon_udot>; } +// ARMv8.2-A FP16 Fused Multiply-Add Long +let Predicates = [HasNEON, HasFP16FML] in { +defm FMLAL : SIMDThreeSameVectorFML<0, 1, 0b001, "fmlal", int_aarch64_neon_fmlal>; +defm FMLSL : SIMDThreeSameVectorFML<0, 1, 0b101, "fmlsl", int_aarch64_neon_fmlsl>; +defm FMLAL2 : SIMDThreeSameVectorFML<1, 0, 0b001, "fmlal2", int_aarch64_neon_fmlal2>; +defm FMLSL2 : SIMDThreeSameVectorFML<1, 0, 0b101, "fmlsl2", int_aarch64_neon_fmlsl2>; +defm FMLALlane : SIMDThreeSameVectorFMLIndex<0, 0b0000, "fmlal", int_aarch64_neon_fmlal>; +defm FMLSLlane : SIMDThreeSameVectorFMLIndex<0, 0b0100, "fmlsl", int_aarch64_neon_fmlsl>; +defm FMLAL2lane : SIMDThreeSameVectorFMLIndex<1, 0b1000, "fmlal2", int_aarch64_neon_fmlal2>; +defm FMLSL2lane : SIMDThreeSameVectorFMLIndex<1, 0b1100, "fmlsl2", int_aarch64_neon_fmlsl2>; +} + // Armv8.2-A Crypto extensions let Predicates = [HasSHA3] in { def SHA512H : CryptoRRRTied<0b0, 0b00, "sha512h">; @@ -543,7 +658,7 @@ let Uses = [LR], Defs = [LR], CRm = 0b0000 in { } // These pointer authentication isntructions require armv8.3a -let Predicates = [HasV8_3a] in { +let Predicates = [HasPA] in { multiclass SignAuth<bits<3> prefix, bits<3> prefix_z, string asm> { def IA : SignAuthOneData<prefix, 0b00, !strconcat(asm, "ia")>; def IB : SignAuthOneData<prefix, 0b01, !strconcat(asm, "ib")>; @@ -583,17 +698,17 @@ let Predicates = [HasV8_3a] in { defm LDRAA : AuthLoad<0, "ldraa", simm10Scaled>; defm LDRAB : AuthLoad<1, "ldrab", simm10Scaled>; - // v8.3a floating point conversion for javascript - let Predicates = [HasV8_3a, HasFPARMv8] in - def FJCVTZS : BaseFPToIntegerUnscaled<0b01, 0b11, 0b110, FPR64, GPR32, - "fjcvtzs", []> { - let Inst{31} = 0; - } +} -} // HasV8_3a +// v8.3a floating point conversion for javascript +let Predicates = [HasJS, HasFPARMv8] in +def FJCVTZS : BaseFPToIntegerUnscaled<0b01, 0b11, 0b110, FPR64, GPR32, + "fjcvtzs", []> { + let Inst{31} = 0; +} // HasJS, HasFPARMv8 // v8.4 Flag manipulation instructions -let Predicates = [HasV8_4a] in { +let Predicates = [HasFMI] in { def CFINV : SimpleSystemI<0, (ins), "cfinv", "">, Sched<[WriteSys]> { let Inst{20-5} = 0b0000001000000000; } @@ -601,10 +716,39 @@ def SETF8 : BaseFlagManipulation<0, 0, (ins GPR32:$Rn), "setf8", "{\t$Rn}">; def SETF16 : BaseFlagManipulation<0, 1, (ins GPR32:$Rn), "setf16", "{\t$Rn}">; def RMIF : FlagRotate<(ins GPR64:$Rn, uimm6:$imm, imm0_15:$mask), "rmif", "{\t$Rn, $imm, $mask}">; -} // HasV8_4a +} // HasFMI + +// v8.5 flag manipulation instructions +let Predicates = [HasAltNZCV], Uses = [NZCV], Defs = [NZCV] in { + +def XAFLAG : PstateWriteSimple<(ins), "xaflag", "">, Sched<[WriteSys]> { + let Inst{18-16} = 0b000; + let Inst{11-8} = 0b0000; + let Unpredictable{11-8} = 0b1111; + let Inst{7-5} = 0b001; +} + +def AXFLAG : PstateWriteSimple<(ins), "axflag", "">, Sched<[WriteSys]> { + let Inst{18-16} = 0b000; + let Inst{11-8} = 0b0000; + let Unpredictable{11-8} = 0b1111; + let Inst{7-5} = 0b010; +} +} // HasAltNZCV + + +// Armv8.5-A speculation barrier +def SB : SimpleSystemI<0, (ins), "sb", "">, Sched<[]> { + let Inst{20-5} = 0b0001100110000111; + let Unpredictable{11-8} = 0b1111; + let Predicates = [HasSB]; + let hasSideEffects = 1; +} def : InstAlias<"clrex", (CLREX 0xf)>; def : InstAlias<"isb", (ISB 0xf)>; +def : InstAlias<"ssbb", (DSB 0)>; +def : InstAlias<"pssbb", (DSB 4)>; def MRS : MRSI; def MSR : MSRI; @@ -1076,6 +1220,50 @@ defm : STOPregister<"stsmin","LDSMIN">;// STSMINx defm : STOPregister<"stumax","LDUMAX">;// STUMAXx defm : STOPregister<"stumin","LDUMIN">;// STUMINx +// v8.5 Memory Tagging Extension +let Predicates = [HasMTE] in { + +def IRG : BaseTwoOperand<0b0100, GPR64sp, "irg", null_frag, GPR64sp, GPR64>, + Sched<[]>{ + let Inst{31} = 1; +} +def GMI : BaseTwoOperand<0b0101, GPR64, "gmi", null_frag, GPR64sp>, Sched<[]>{ + let Inst{31} = 1; + let isNotDuplicable = 1; +} +def ADDG : AddSubG<0, "addg", null_frag>; +def SUBG : AddSubG<1, "subg", null_frag>; + +def : InstAlias<"irg $dst, $src", (IRG GPR64sp:$dst, GPR64sp:$src, XZR), 1>; + +def SUBP : SUBP<0, "subp", null_frag>, Sched<[]>; +def SUBPS : SUBP<1, "subps", null_frag>, Sched<[]>{ + let Defs = [NZCV]; +} + +def : InstAlias<"cmpp $lhs, $rhs", (SUBPS XZR, GPR64sp:$lhs, GPR64sp:$rhs), 0>; + +def LDG : MemTagLoad<"ldg", "\t$Rt, [$Rn, $offset]">; +def : InstAlias<"ldg $Rt, [$Rn]", (LDG GPR64:$Rt, GPR64sp:$Rn, 0), 1>; + +def LDGV : MemTagVector<1, "ldgv", "\t$Rt, [$Rn]!", + (outs GPR64sp:$wback, GPR64:$Rt), (ins GPR64sp:$Rn)> { + let DecoderMethod = "DecodeLoadAllocTagArrayInstruction"; +} +def STGV : MemTagVector<0, "stgv", "\t$Rt, [$Rn]!", + (outs GPR64sp:$wback), (ins GPR64:$Rt, GPR64sp:$Rn)>; + +defm STG : MemTagStore<0b00, "stg">; +defm STZG : MemTagStore<0b01, "stzg">; +defm ST2G : MemTagStore<0b10, "st2g">; +defm STZ2G : MemTagStore<0b11, "stz2g">; + +defm STGP : StorePairOffset <0b01, 0, GPR64z, simm7s16, "stgp">; +def STGPpre : StorePairPreIdx <0b01, 0, GPR64z, simm7s16, "stgp">; +def STGPpost : StorePairPostIdx<0b01, 0, GPR64z, simm7s16, "stgp">; + +} // Predicates = [HasMTE] + //===----------------------------------------------------------------------===// // Logical instructions. //===----------------------------------------------------------------------===// @@ -1383,7 +1571,8 @@ def : InstAlias<"cneg $dst, $src, $cc", //===----------------------------------------------------------------------===// let isReMaterializable = 1 in { let hasSideEffects = 0, mayStore = 0, mayLoad = 0 in { -def ADR : ADRI<0, "adr", adrlabel, []>; +def ADR : ADRI<0, "adr", adrlabel, + [(set GPR64:$Xd, (AArch64adr tglobaladdr:$label))]>; } // hasSideEffects = 0 def ADRP : ADRI<1, "adrp", adrplabel, @@ -1391,6 +1580,10 @@ def ADRP : ADRI<1, "adrp", adrplabel, } // isReMaterializable = 1 // page address of a constant pool entry, block address +def : Pat<(AArch64adr tconstpool:$cp), (ADR tconstpool:$cp)>; +def : Pat<(AArch64adr tblockaddress:$cp), (ADR tblockaddress:$cp)>; +def : Pat<(AArch64adr texternalsym:$sym), (ADR texternalsym:$sym)>; +def : Pat<(AArch64adr tjumptable:$sym), (ADR tjumptable:$sym)>; def : Pat<(AArch64adrp tconstpool:$cp), (ADRP tconstpool:$cp)>; def : Pat<(AArch64adrp tblockaddress:$cp), (ADRP tblockaddress:$cp)>; def : Pat<(AArch64adrp texternalsym:$sym), (ADRP texternalsym:$sym)>; @@ -1434,6 +1627,10 @@ def TLSDESCCALL : Pseudo<(outs), (ins i64imm:$sym), []>, Sched<[]> { let AsmString = ".tlsdesccall $sym"; } +// Pseudo instruction to tell the streamer to emit a 'B' character into the +// augmentation string. +def EMITBKEY : Pseudo<(outs), (ins), []>, Sched<[]> {} + // FIXME: maybe the scratch register used shouldn't be fixed to X1? // FIXME: can "hasSideEffects be dropped? let isCall = 1, Defs = [LR, X0, X1], hasSideEffects = 1, @@ -1493,6 +1690,8 @@ def : InstAlias<"dcps1", (DCPS1 0)>; def : InstAlias<"dcps2", (DCPS2 0)>; def : InstAlias<"dcps3", (DCPS3 0)>; +def UDF : UDFType<0, "udf">; + //===----------------------------------------------------------------------===// // Load instructions. //===----------------------------------------------------------------------===// @@ -1883,14 +2082,37 @@ def : InstAlias<"prfm $Rt, [$Rn]", (PRFMui prfop:$Rt, GPR64sp:$Rn, 0)>; //--- // (literal) -def LDRWl : LoadLiteral<0b00, 0, GPR32z, "ldr">; -def LDRXl : LoadLiteral<0b01, 0, GPR64z, "ldr">; -def LDRSl : LoadLiteral<0b00, 1, FPR32Op, "ldr">; -def LDRDl : LoadLiteral<0b01, 1, FPR64Op, "ldr">; -def LDRQl : LoadLiteral<0b10, 1, FPR128Op, "ldr">; + +def alignedglobal : PatLeaf<(iPTR iPTR:$label), [{ + if (auto *G = dyn_cast<GlobalAddressSDNode>(N)) { + const DataLayout &DL = MF->getDataLayout(); + unsigned Align = G->getGlobal()->getPointerAlignment(DL); + return Align >= 4 && G->getOffset() % 4 == 0; + } + if (auto *C = dyn_cast<ConstantPoolSDNode>(N)) + return C->getAlignment() >= 4 && C->getOffset() % 4 == 0; + return false; +}]>; + +def LDRWl : LoadLiteral<0b00, 0, GPR32z, "ldr", + [(set GPR32z:$Rt, (load (AArch64adr alignedglobal:$label)))]>; +def LDRXl : LoadLiteral<0b01, 0, GPR64z, "ldr", + [(set GPR64z:$Rt, (load (AArch64adr alignedglobal:$label)))]>; +def LDRSl : LoadLiteral<0b00, 1, FPR32Op, "ldr", + [(set (f32 FPR32Op:$Rt), (load (AArch64adr alignedglobal:$label)))]>; +def LDRDl : LoadLiteral<0b01, 1, FPR64Op, "ldr", + [(set (f64 FPR64Op:$Rt), (load (AArch64adr alignedglobal:$label)))]>; +def LDRQl : LoadLiteral<0b10, 1, FPR128Op, "ldr", + [(set (f128 FPR128Op:$Rt), (load (AArch64adr alignedglobal:$label)))]>; // load sign-extended word -def LDRSWl : LoadLiteral<0b10, 0, GPR64z, "ldrsw">; +def LDRSWl : LoadLiteral<0b10, 0, GPR64z, "ldrsw", + [(set GPR64z:$Rt, (sextloadi32 (AArch64adr alignedglobal:$label)))]>; + +let AddedComplexity = 20 in { +def : Pat<(i64 (zextloadi32 (AArch64adr alignedglobal:$label))), + (SUBREG_TO_REG (i64 0), (LDRWl $label), sub_32)>; +} // prefetch def PRFMl : PrefetchLiteral<0b11, 0, "prfm", []>; @@ -2467,8 +2689,9 @@ defm STURBB : StoreUnscaled<0b00, 0, 0b00, GPR32z, "sturb", [(truncstorei8 GPR32z:$Rt, (am_unscaled8 GPR64sp:$Rn, simm9:$offset))]>; -// Armv8.4 LDAPR & STLR with Immediate Offset instruction -let Predicates = [HasV8_4a] in { +// Armv8.4 Weaker Release Consistency enhancements +// LDAPR & STLR with Immediate Offset instructions +let Predicates = [HasRCPC_IMMO] in { defm STLURB : BaseStoreUnscaleV84<"stlurb", 0b00, 0b00, GPR32>; defm STLURH : BaseStoreUnscaleV84<"stlurh", 0b01, 0b00, GPR32>; defm STLURW : BaseStoreUnscaleV84<"stlur", 0b10, 0b00, GPR32>; @@ -2753,7 +2976,7 @@ def STLXPX : StoreExclusivePair<0b11, 0, 0, 1, 1, GPR64, "stlxp">; def STXPW : StoreExclusivePair<0b10, 0, 0, 1, 0, GPR32, "stxp">; def STXPX : StoreExclusivePair<0b11, 0, 0, 1, 0, GPR64, "stxp">; -let Predicates = [HasV8_1a] in { +let Predicates = [HasLOR] in { // v8.1a "Limited Order Region" extension load-acquire instructions def LDLARW : LoadAcquire <0b10, 1, 1, 0, 0, GPR32, "ldlar">; def LDLARX : LoadAcquire <0b11, 1, 1, 0, 0, GPR64, "ldlar">; @@ -2886,6 +3109,13 @@ let SchedRW = [WriteFDiv] in { defm FSQRT : SingleOperandFPData<0b0011, "fsqrt", fsqrt>; } +let Predicates = [HasFRInt3264] in { + defm FRINT32Z : FRIntNNT<0b00, "frint32z">; + defm FRINT64Z : FRIntNNT<0b10, "frint64z">; + defm FRINT32X : FRIntNNT<0b01, "frint32x">; + defm FRINT64X : FRIntNNT<0b11, "frint64x">; +} // HasFRInt3264 + //===----------------------------------------------------------------------===// // Floating point two operand instructions. //===----------------------------------------------------------------------===// @@ -2895,18 +3125,18 @@ let SchedRW = [WriteFDiv] in { defm FDIV : TwoOperandFPData<0b0001, "fdiv", fdiv>; } defm FMAXNM : TwoOperandFPData<0b0110, "fmaxnm", fmaxnum>; -defm FMAX : TwoOperandFPData<0b0100, "fmax", fmaxnan>; +defm FMAX : TwoOperandFPData<0b0100, "fmax", fmaximum>; defm FMINNM : TwoOperandFPData<0b0111, "fminnm", fminnum>; -defm FMIN : TwoOperandFPData<0b0101, "fmin", fminnan>; +defm FMIN : TwoOperandFPData<0b0101, "fmin", fminimum>; let SchedRW = [WriteFMul] in { defm FMUL : TwoOperandFPData<0b0000, "fmul", fmul>; defm FNMUL : TwoOperandFPDataNeg<0b1000, "fnmul", fmul>; } defm FSUB : TwoOperandFPData<0b0011, "fsub", fsub>; -def : Pat<(v1f64 (fmaxnan (v1f64 FPR64:$Rn), (v1f64 FPR64:$Rm))), +def : Pat<(v1f64 (fmaximum (v1f64 FPR64:$Rn), (v1f64 FPR64:$Rm))), (FMAXDrr FPR64:$Rn, FPR64:$Rm)>; -def : Pat<(v1f64 (fminnan (v1f64 FPR64:$Rn), (v1f64 FPR64:$Rm))), +def : Pat<(v1f64 (fminimum (v1f64 FPR64:$Rn), (v1f64 FPR64:$Rm))), (FMINDrr FPR64:$Rn, FPR64:$Rm)>; def : Pat<(v1f64 (fmaxnum (v1f64 FPR64:$Rn), (v1f64 FPR64:$Rm))), (FMAXNMDrr FPR64:$Rn, FPR64:$Rm)>; @@ -2983,6 +3213,42 @@ def F128CSEL : Pseudo<(outs FPR128:$Rd), let hasNoSchedulingInfo = 1; } +//===----------------------------------------------------------------------===// +// Instructions used for emitting unwind opcodes on ARM64 Windows. +//===----------------------------------------------------------------------===// +let isPseudo = 1 in { + def SEH_StackAlloc : Pseudo<(outs), (ins i32imm:$size), []>, Sched<[]>; + def SEH_SaveFPLR : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFPLR_X : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_SaveReg : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveReg_X : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveRegP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveRegP_X : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFReg : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFReg_X : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFRegP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFRegP_X : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SetFP : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_AddFP : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_Nop : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_PrologEnd : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_EpilogStart : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_EpilogEnd : Pseudo<(outs), (ins), []>, Sched<[]>; +} + +// Pseudo instructions for Windows EH +//===----------------------------------------------------------------------===// +let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, + isCodeGenOnly = 1, isReturn = 1, isEHScopeReturn = 1, isPseudo = 1 in { + def CLEANUPRET : Pseudo<(outs), (ins), [(cleanupret)]>, Sched<[]>; + let usesCustomInserter = 1 in + def CATCHRET : Pseudo<(outs), (ins am_brcond:$dst, am_brcond:$src), [(catchret bb:$dst, bb:$src)]>, + Sched<[]>; +} + +let hasSideEffects = 1, hasCtrlDep = 1, isCodeGenOnly = 1, + usesCustomInserter = 1 in +def CATCHPAD : Pseudo<(outs), (ins), [(catchpad)]>, Sched<[]>; //===----------------------------------------------------------------------===// // Floating point immediate move. @@ -3104,6 +3370,14 @@ defm FRINTN : SIMDTwoVectorFP<0, 0, 0b11000, "frintn", int_aarch64_neon_frintn>; defm FRINTP : SIMDTwoVectorFP<0, 1, 0b11000, "frintp", fceil>; defm FRINTX : SIMDTwoVectorFP<1, 0, 0b11001, "frintx", frint>; defm FRINTZ : SIMDTwoVectorFP<0, 1, 0b11001, "frintz", ftrunc>; + +let Predicates = [HasFRInt3264] in { + defm FRINT32Z : FRIntNNTVector<0, 0, "frint32z">; + defm FRINT64Z : FRIntNNTVector<0, 1, "frint64z">; + defm FRINT32X : FRIntNNTVector<1, 0, "frint32x">; + defm FRINT64X : FRIntNNTVector<1, 1, "frint64x">; +} // HasFRInt3264 + defm FRSQRTE: SIMDTwoVectorFP<1, 1, 0b11101, "frsqrte", int_aarch64_neon_frsqrte>; defm FSQRT : SIMDTwoVectorFP<1, 1, 0b11111, "fsqrt", fsqrt>; defm NEG : SIMDTwoVectorBHSD<1, 0b01011, "neg", @@ -3224,11 +3498,11 @@ defm FDIV : SIMDThreeSameVectorFP<1,0,0b111,"fdiv", fdiv>; defm FMAXNMP : SIMDThreeSameVectorFP<1,0,0b000,"fmaxnmp", int_aarch64_neon_fmaxnmp>; defm FMAXNM : SIMDThreeSameVectorFP<0,0,0b000,"fmaxnm", fmaxnum>; defm FMAXP : SIMDThreeSameVectorFP<1,0,0b110,"fmaxp", int_aarch64_neon_fmaxp>; -defm FMAX : SIMDThreeSameVectorFP<0,0,0b110,"fmax", fmaxnan>; +defm FMAX : SIMDThreeSameVectorFP<0,0,0b110,"fmax", fmaximum>; defm FMINNMP : SIMDThreeSameVectorFP<1,1,0b000,"fminnmp", int_aarch64_neon_fminnmp>; defm FMINNM : SIMDThreeSameVectorFP<0,1,0b000,"fminnm", fminnum>; defm FMINP : SIMDThreeSameVectorFP<1,1,0b110,"fminp", int_aarch64_neon_fminp>; -defm FMIN : SIMDThreeSameVectorFP<0,1,0b110,"fmin", fminnan>; +defm FMIN : SIMDThreeSameVectorFP<0,1,0b110,"fmin", fminimum>; // NOTE: The operands of the PatFrag are reordered on FMLA/FMLS because the // instruction expects the addend first, while the fma intrinsic puts it last. @@ -3895,25 +4169,6 @@ defm : Neon_mul_widen_patterns<AArch64smull, SMULLv8i8_v8i16, defm : Neon_mul_widen_patterns<AArch64umull, UMULLv8i8_v8i16, UMULLv4i16_v4i32, UMULLv2i32_v2i64>; -// Patterns for smull2/umull2. -multiclass Neon_mul_high_patterns<SDPatternOperator opnode, - Instruction INST8B, Instruction INST4H, Instruction INST2S> { - def : Pat<(v8i16 (opnode (extract_high_v16i8 V128:$Rn), - (extract_high_v16i8 V128:$Rm))), - (INST8B V128:$Rn, V128:$Rm)>; - def : Pat<(v4i32 (opnode (extract_high_v8i16 V128:$Rn), - (extract_high_v8i16 V128:$Rm))), - (INST4H V128:$Rn, V128:$Rm)>; - def : Pat<(v2i64 (opnode (extract_high_v4i32 V128:$Rn), - (extract_high_v4i32 V128:$Rm))), - (INST2S V128:$Rn, V128:$Rm)>; -} - -defm : Neon_mul_high_patterns<AArch64smull, SMULLv16i8_v8i16, - SMULLv8i16_v4i32, SMULLv4i32_v2i64>; -defm : Neon_mul_high_patterns<AArch64umull, UMULLv16i8_v8i16, - UMULLv8i16_v4i32, UMULLv4i32_v2i64>; - // Additional patterns for SMLAL/SMLSL and UMLAL/UMLSL multiclass Neon_mulacc_widen_patterns<SDPatternOperator opnode, Instruction INST8B, Instruction INST4H, Instruction INST2S> { @@ -4004,44 +4259,43 @@ def : Pat<(concat_vectors (v2i32 V64:$Rd), defm EXT : SIMDBitwiseExtract<"ext">; -def : Pat<(v4i16 (AArch64ext V64:$Rn, V64:$Rm, (i32 imm:$imm))), - (EXTv8i8 V64:$Rn, V64:$Rm, imm:$imm)>; -def : Pat<(v8i16 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; -def : Pat<(v2i32 (AArch64ext V64:$Rn, V64:$Rm, (i32 imm:$imm))), - (EXTv8i8 V64:$Rn, V64:$Rm, imm:$imm)>; -def : Pat<(v2f32 (AArch64ext V64:$Rn, V64:$Rm, (i32 imm:$imm))), - (EXTv8i8 V64:$Rn, V64:$Rm, imm:$imm)>; -def : Pat<(v4i32 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; -def : Pat<(v4f32 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; -def : Pat<(v2i64 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; -def : Pat<(v2f64 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; -def : Pat<(v4f16 (AArch64ext V64:$Rn, V64:$Rm, (i32 imm:$imm))), - (EXTv8i8 V64:$Rn, V64:$Rm, imm:$imm)>; -def : Pat<(v8f16 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), - (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; - -// We use EXT to handle extract_subvector to copy the upper 64-bits of a -// 128-bit vector. -def : Pat<(v8i8 (extract_subvector V128:$Rn, (i64 8))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v4i16 (extract_subvector V128:$Rn, (i64 4))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v2i32 (extract_subvector V128:$Rn, (i64 2))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v1i64 (extract_subvector V128:$Rn, (i64 1))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v4f16 (extract_subvector V128:$Rn, (i64 4))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v2f32 (extract_subvector V128:$Rn, (i64 2))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; -def : Pat<(v1f64 (extract_subvector V128:$Rn, (i64 1))), - (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; +def AdjustExtImm : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(8 + N->getZExtValue(), SDLoc(N), MVT::i32); +}]>; +multiclass ExtPat<ValueType VT64, ValueType VT128, int N> { + def : Pat<(VT64 (AArch64ext V64:$Rn, V64:$Rm, (i32 imm:$imm))), + (EXTv8i8 V64:$Rn, V64:$Rm, imm:$imm)>; + def : Pat<(VT128 (AArch64ext V128:$Rn, V128:$Rm, (i32 imm:$imm))), + (EXTv16i8 V128:$Rn, V128:$Rm, imm:$imm)>; + // We use EXT to handle extract_subvector to copy the upper 64-bits of a + // 128-bit vector. + def : Pat<(VT64 (extract_subvector V128:$Rn, (i64 N))), + (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, 8), dsub)>; + // A 64-bit EXT of two halves of the same 128-bit register can be done as a + // single 128-bit EXT. + def : Pat<(VT64 (AArch64ext (extract_subvector V128:$Rn, (i64 0)), + (extract_subvector V128:$Rn, (i64 N)), + (i32 imm:$imm))), + (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, V128:$Rn, imm:$imm), dsub)>; + // A 64-bit EXT of the high half of a 128-bit register can be done using a + // 128-bit EXT of the whole register with an adjustment to the immediate. The + // top half of the other operand will be unset, but that doesn't matter as it + // will not be used. + def : Pat<(VT64 (AArch64ext (extract_subvector V128:$Rn, (i64 N)), + V64:$Rm, + (i32 imm:$imm))), + (EXTRACT_SUBREG (EXTv16i8 V128:$Rn, + (SUBREG_TO_REG (i32 0), V64:$Rm, dsub), + (AdjustExtImm imm:$imm)), dsub)>; +} +defm : ExtPat<v8i8, v16i8, 8>; +defm : ExtPat<v4i16, v8i16, 4>; +defm : ExtPat<v4f16, v8f16, 4>; +defm : ExtPat<v2i32, v4i32, 2>; +defm : ExtPat<v2f32, v4f32, 2>; +defm : ExtPat<v1i64, v2i64, 1>; +defm : ExtPat<v1f64, v2f64, 1>; //---------------------------------------------------------------------------- // AdvSIMD zip vector @@ -4137,6 +4391,12 @@ def DUPv8i16lane : SIMDDup16FromElement<1, ".8h", v8i16, V128>; def DUPv8i8lane : SIMDDup8FromElement <0, ".8b", v8i8, V64>; def DUPv16i8lane : SIMDDup8FromElement <1, ".16b", v16i8, V128>; +// DUP from a 64-bit register to a 64-bit register is just a copy +def : Pat<(v1i64 (AArch64dup (i64 GPR64:$Rn))), + (COPY_TO_REGCLASS GPR64:$Rn, FPR64)>; +def : Pat<(v1f64 (AArch64dup (f64 FPR64:$Rn))), + (COPY_TO_REGCLASS FPR64:$Rn, FPR64)>; + def : Pat<(v2f32 (AArch64dup (f32 FPR32:$Rn))), (v2f32 (DUPv2i32lane (INSERT_SUBREG (v4i32 (IMPLICIT_DEF)), FPR32:$Rn, ssub), @@ -4739,16 +4999,6 @@ def MOVID : SIMDModifiedImmScalarNoShift<0, 1, 0b1110, "movi", def : Pat<(f64 (AArch64movi_edit imm0_255:$shift)), (MOVID imm0_255:$shift)>; -def : Pat<(v1i64 immAllZerosV), (MOVID (i32 0))>; -def : Pat<(v2i32 immAllZerosV), (MOVID (i32 0))>; -def : Pat<(v4i16 immAllZerosV), (MOVID (i32 0))>; -def : Pat<(v8i8 immAllZerosV), (MOVID (i32 0))>; - -def : Pat<(v1i64 immAllOnesV), (MOVID (i32 255))>; -def : Pat<(v2i32 immAllOnesV), (MOVID (i32 255))>; -def : Pat<(v4i16 immAllOnesV), (MOVID (i32 255))>; -def : Pat<(v8i8 immAllOnesV), (MOVID (i32 255))>; - // EDIT byte mask: 2d // The movi_edit node has the immediate value already encoded, so we use @@ -4769,6 +5019,18 @@ def : Pat<(v4i32 immAllOnesV), (MOVIv2d_ns (i32 255))>; def : Pat<(v8i16 immAllOnesV), (MOVIv2d_ns (i32 255))>; def : Pat<(v16i8 immAllOnesV), (MOVIv2d_ns (i32 255))>; +// Set 64-bit vectors to all 0/1 by extracting from a 128-bit register as the +// extract is free and this gives better MachineCSE results. +def : Pat<(v1i64 immAllZerosV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 0)), dsub)>; +def : Pat<(v2i32 immAllZerosV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 0)), dsub)>; +def : Pat<(v4i16 immAllZerosV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 0)), dsub)>; +def : Pat<(v8i8 immAllZerosV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 0)), dsub)>; + +def : Pat<(v1i64 immAllOnesV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 255)), dsub)>; +def : Pat<(v2i32 immAllOnesV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 255)), dsub)>; +def : Pat<(v4i16 immAllOnesV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 255)), dsub)>; +def : Pat<(v8i8 immAllOnesV), (EXTRACT_SUBREG (MOVIv2d_ns (i32 255)), dsub)>; + // EDIT per word & halfword: 2s, 4h, 4s, & 8h let isReMaterializable = 1, isAsCheapAsAMove = 1 in defm MOVI : SIMDModifiedImmVectorShift<0, 0b10, 0b00, "movi">; @@ -5770,6 +6032,41 @@ def : Pat<(i32 (trunc GPR64sp:$src)), // __builtin_trap() uses the BRK instruction on AArch64. def : Pat<(trap), (BRK 1)>; +// Multiply high patterns which multiply the lower subvector using smull/umull +// and the upper subvector with smull2/umull2. Then shuffle the high the high +// part of both results together. +def : Pat<(v16i8 (mulhs V128:$Rn, V128:$Rm)), + (UZP2v16i8 + (SMULLv8i8_v8i16 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (SMULLv16i8_v8i16 V128:$Rn, V128:$Rm))>; +def : Pat<(v8i16 (mulhs V128:$Rn, V128:$Rm)), + (UZP2v8i16 + (SMULLv4i16_v4i32 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (SMULLv8i16_v4i32 V128:$Rn, V128:$Rm))>; +def : Pat<(v4i32 (mulhs V128:$Rn, V128:$Rm)), + (UZP2v4i32 + (SMULLv2i32_v2i64 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (SMULLv4i32_v2i64 V128:$Rn, V128:$Rm))>; + +def : Pat<(v16i8 (mulhu V128:$Rn, V128:$Rm)), + (UZP2v16i8 + (UMULLv8i8_v8i16 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (UMULLv16i8_v8i16 V128:$Rn, V128:$Rm))>; +def : Pat<(v8i16 (mulhu V128:$Rn, V128:$Rm)), + (UZP2v8i16 + (UMULLv4i16_v4i32 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (UMULLv8i16_v4i32 V128:$Rn, V128:$Rm))>; +def : Pat<(v4i32 (mulhu V128:$Rn, V128:$Rm)), + (UZP2v4i32 + (UMULLv2i32_v2i64 (EXTRACT_SUBREG V128:$Rn, dsub), + (EXTRACT_SUBREG V128:$Rm, dsub)), + (UMULLv4i32_v2i64 V128:$Rn, V128:$Rm))>; + // Conversions within AdvSIMD types in the same register size are free. // But because we need a consistent lane ordering, in big endian many // conversions require one or more REV instructions. @@ -6481,10 +6778,24 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [SP] in { Sched<[WriteBrReg]>; def TCRETURNri : Pseudo<(outs), (ins tcGPR64:$dst, i32imm:$FPDiff), []>, Sched<[WriteBrReg]>; + // Indirect tail-call with any register allowed, used by MachineOutliner when + // this is proven safe. + // FIXME: If we have to add any more hacks like this, we should instead relax + // some verifier checks for outlined functions. + def TCRETURNriALL : Pseudo<(outs), (ins GPR64:$dst, i32imm:$FPDiff), []>, + Sched<[WriteBrReg]>; + // Indirect tail-call limited to only use registers (x16 and x17) which are + // allowed to tail-call a "BTI c" instruction. + def TCRETURNriBTI : Pseudo<(outs), (ins rtcGPR64:$dst, i32imm:$FPDiff), []>, + Sched<[WriteBrReg]>; } def : Pat<(AArch64tcret tcGPR64:$dst, (i32 timm:$FPDiff)), - (TCRETURNri tcGPR64:$dst, imm:$FPDiff)>; + (TCRETURNri tcGPR64:$dst, imm:$FPDiff)>, + Requires<[NotUseBTI]>; +def : Pat<(AArch64tcret rtcGPR64:$dst, (i32 timm:$FPDiff)), + (TCRETURNriBTI rtcGPR64:$dst, imm:$FPDiff)>, + Requires<[UseBTI]>; def : Pat<(AArch64tcret tglobaladdr:$dst, (i32 timm:$FPDiff)), (TCRETURNdi texternalsym:$dst, imm:$FPDiff)>; def : Pat<(AArch64tcret texternalsym:$dst, (i32 timm:$FPDiff)), diff --git a/lib/Target/AArch64/AArch64InstructionSelector.cpp b/lib/Target/AArch64/AArch64InstructionSelector.cpp index b2b500320b5c..5eb589bf66d5 100644 --- a/lib/Target/AArch64/AArch64InstructionSelector.cpp +++ b/lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -65,6 +65,16 @@ private: bool selectCompareBranch(MachineInstr &I, MachineFunction &MF, MachineRegisterInfo &MRI) const; + // Helper to generate an equivalent of scalar_to_vector into a new register, + // returned via 'Dst'. + bool emitScalarToVector(unsigned &Dst, const LLT DstTy, + const TargetRegisterClass *DstRC, unsigned Scalar, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineRegisterInfo &MRI) const; + bool selectBuildVector(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectMergeValues(MachineInstr &I, MachineRegisterInfo &MRI) const; + ComplexRendererFns selectArithImmed(MachineOperand &Root) const; ComplexRendererFns selectAddrModeUnscaled(MachineOperand &Root, @@ -667,7 +677,7 @@ void AArch64InstructionSelector::materializeLargeCMVal( MachineRegisterInfo &MRI = MF.getRegInfo(); MachineIRBuilder MIB(I); - auto MovZ = MIB.buildInstr(AArch64::MOVZXi, &AArch64::GPR64RegClass); + auto MovZ = MIB.buildInstr(AArch64::MOVZXi, {&AArch64::GPR64RegClass}, {}); MovZ->addOperand(MF, I.getOperand(1)); MovZ->getOperand(1).setTargetFlags(OpFlags | AArch64II::MO_G0 | AArch64II::MO_NC); @@ -779,16 +789,36 @@ bool AArch64InstructionSelector::select(MachineInstr &I, const unsigned CondReg = I.getOperand(0).getReg(); MachineBasicBlock *DestMBB = I.getOperand(1).getMBB(); - if (selectCompareBranch(I, MF, MRI)) + // Speculation tracking/SLH assumes that optimized TB(N)Z/CB(N)Z + // instructions will not be produced, as they are conditional branch + // instructions that do not set flags. + bool ProduceNonFlagSettingCondBr = + !MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening); + if (ProduceNonFlagSettingCondBr && selectCompareBranch(I, MF, MRI)) return true; - auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(AArch64::TBNZW)) - .addUse(CondReg) - .addImm(/*bit offset=*/0) - .addMBB(DestMBB); + if (ProduceNonFlagSettingCondBr) { + auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(AArch64::TBNZW)) + .addUse(CondReg) + .addImm(/*bit offset=*/0) + .addMBB(DestMBB); - I.eraseFromParent(); - return constrainSelectedInstRegOperands(*MIB.getInstr(), TII, TRI, RBI); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*MIB.getInstr(), TII, TRI, RBI); + } else { + auto CMP = BuildMI(MBB, I, I.getDebugLoc(), TII.get(AArch64::ANDSWri)) + .addDef(AArch64::WZR) + .addUse(CondReg) + .addImm(1); + constrainSelectedInstRegOperands(*CMP.getInstr(), TII, TRI, RBI); + auto Bcc = + BuildMI(MBB, I, I.getDebugLoc(), TII.get(AArch64::Bcc)) + .addImm(AArch64CC::EQ) + .addMBB(DestMBB); + + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*Bcc.getInstr(), TII, TRI, RBI); + } } case TargetOpcode::G_BRINDIRECT: { @@ -983,6 +1013,9 @@ bool AArch64InstructionSelector::select(MachineInstr &I, materializeLargeCMVal(I, GV, OpFlags); I.eraseFromParent(); return true; + } else if (TM.getCodeModel() == CodeModel::Tiny) { + I.setDesc(TII.get(AArch64::ADR)); + I.getOperand(1).setTargetFlags(OpFlags); } else { I.setDesc(TII.get(AArch64::MOVaddr)); I.getOperand(1).setTargetFlags(OpFlags | AArch64II::MO_PAGE); @@ -1010,12 +1043,6 @@ bool AArch64InstructionSelector::select(MachineInstr &I, } unsigned MemSizeInBits = MemOp.getSize() * 8; - // FIXME: PR36018: Volatile loads in some cases are incorrectly selected by - // folding with an extend. Until we have a G_SEXTLOAD solution bail out if - // we hit one. - if (Opcode == TargetOpcode::G_LOAD && MemOp.isVolatile()) - return false; - const unsigned PtrReg = I.getOperand(1).getReg(); #ifndef NDEBUG const RegisterBank &PtrRB = *RBI.getRegBank(PtrReg, MRI, TRI); @@ -1525,11 +1552,178 @@ bool AArch64InstructionSelector::select(MachineInstr &I, return constrainSelectedInstRegOperands(*MovMI, TII, TRI, RBI); } } + case TargetOpcode::G_BUILD_VECTOR: + return selectBuildVector(I, MRI); + case TargetOpcode::G_MERGE_VALUES: + return selectMergeValues(I, MRI); } return false; } +bool AArch64InstructionSelector::emitScalarToVector( + unsigned &Dst, const LLT DstTy, const TargetRegisterClass *DstRC, + unsigned Scalar, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, MachineRegisterInfo &MRI) const { + Dst = MRI.createVirtualRegister(DstRC); + + unsigned UndefVec = MRI.createVirtualRegister(DstRC); + MachineInstr &UndefMI = *BuildMI(MBB, MBBI, MBBI->getDebugLoc(), + TII.get(TargetOpcode::IMPLICIT_DEF)) + .addDef(UndefVec); + + auto BuildFn = [&](unsigned SubregIndex) { + MachineInstr &InsMI = *BuildMI(MBB, MBBI, MBBI->getDebugLoc(), + TII.get(TargetOpcode::INSERT_SUBREG)) + .addDef(Dst) + .addUse(UndefVec) + .addUse(Scalar) + .addImm(SubregIndex); + constrainSelectedInstRegOperands(UndefMI, TII, TRI, RBI); + return constrainSelectedInstRegOperands(InsMI, TII, TRI, RBI); + }; + + switch (DstTy.getElementType().getSizeInBits()) { + case 32: + return BuildFn(AArch64::ssub); + case 64: + return BuildFn(AArch64::dsub); + default: + return false; + } +} + +bool AArch64InstructionSelector::selectMergeValues( + MachineInstr &I, MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_MERGE_VALUES && "unexpected opcode"); + const LLT DstTy = MRI.getType(I.getOperand(0).getReg()); + const LLT SrcTy = MRI.getType(I.getOperand(1).getReg()); + assert(!DstTy.isVector() && !SrcTy.isVector() && "invalid merge operation"); + + // At the moment we only support merging two s32s into an s64. + if (I.getNumOperands() != 3) + return false; + if (DstTy.getSizeInBits() != 64 || SrcTy.getSizeInBits() != 32) + return false; + const RegisterBank &RB = *RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI); + if (RB.getID() != AArch64::GPRRegBankID) + return false; + + auto *DstRC = &AArch64::GPR64RegClass; + unsigned SubToRegDef = MRI.createVirtualRegister(DstRC); + MachineInstr &SubRegMI = *BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(TargetOpcode::SUBREG_TO_REG)) + .addDef(SubToRegDef) + .addImm(0) + .addUse(I.getOperand(1).getReg()) + .addImm(AArch64::sub_32); + unsigned SubToRegDef2 = MRI.createVirtualRegister(DstRC); + // Need to anyext the second scalar before we can use bfm + MachineInstr &SubRegMI2 = *BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(TargetOpcode::SUBREG_TO_REG)) + .addDef(SubToRegDef2) + .addImm(0) + .addUse(I.getOperand(2).getReg()) + .addImm(AArch64::sub_32); + MachineInstr &BFM = + *BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(AArch64::BFMXri)) + .addDef(I.getOperand(0).getReg()) + .addUse(SubToRegDef) + .addUse(SubToRegDef2) + .addImm(32) + .addImm(31); + constrainSelectedInstRegOperands(SubRegMI, TII, TRI, RBI); + constrainSelectedInstRegOperands(SubRegMI2, TII, TRI, RBI); + constrainSelectedInstRegOperands(BFM, TII, TRI, RBI); + I.eraseFromParent(); + return true; +} + +bool AArch64InstructionSelector::selectBuildVector( + MachineInstr &I, MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_BUILD_VECTOR); + // Until we port more of the optimized selections, for now just use a vector + // insert sequence. + const LLT DstTy = MRI.getType(I.getOperand(0).getReg()); + const LLT EltTy = MRI.getType(I.getOperand(1).getReg()); + unsigned EltSize = EltTy.getSizeInBits(); + if (EltSize < 32 || EltSize > 64) + return false; // Don't support all element types yet. + const RegisterBank &RB = *RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI); + unsigned Opc; + unsigned SubregIdx; + if (RB.getID() == AArch64::GPRRegBankID) { + if (EltSize == 32) { + Opc = AArch64::INSvi32gpr; + SubregIdx = AArch64::ssub; + } else { + Opc = AArch64::INSvi64gpr; + SubregIdx = AArch64::dsub; + } + } else { + if (EltSize == 32) { + Opc = AArch64::INSvi32lane; + SubregIdx = AArch64::ssub; + } else { + Opc = AArch64::INSvi64lane; + SubregIdx = AArch64::dsub; + } + } + + if (EltSize * DstTy.getNumElements() != 128) + return false; // Don't handle unpacked vectors yet. + + unsigned DstVec = 0; + const TargetRegisterClass *DstRC = getRegClassForTypeOnBank( + DstTy, RBI.getRegBank(AArch64::FPRRegBankID), RBI); + emitScalarToVector(DstVec, DstTy, DstRC, I.getOperand(1).getReg(), + *I.getParent(), I.getIterator(), MRI); + for (unsigned i = 2, e = DstTy.getSizeInBits() / EltSize + 1; i < e; ++i) { + unsigned InsDef; + // For the last insert re-use the dst reg of the G_BUILD_VECTOR. + if (i + 1 < e) + InsDef = MRI.createVirtualRegister(DstRC); + else + InsDef = I.getOperand(0).getReg(); + unsigned LaneIdx = i - 1; + if (RB.getID() == AArch64::FPRRegBankID) { + unsigned ImpDef = MRI.createVirtualRegister(DstRC); + MachineInstr &ImpDefMI = *BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(TargetOpcode::IMPLICIT_DEF)) + .addDef(ImpDef); + unsigned InsSubDef = MRI.createVirtualRegister(DstRC); + MachineInstr &InsSubMI = *BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(TargetOpcode::INSERT_SUBREG)) + .addDef(InsSubDef) + .addUse(ImpDef) + .addUse(I.getOperand(i).getReg()) + .addImm(SubregIdx); + MachineInstr &InsEltMI = + *BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opc)) + .addDef(InsDef) + .addUse(DstVec) + .addImm(LaneIdx) + .addUse(InsSubDef) + .addImm(0); + constrainSelectedInstRegOperands(ImpDefMI, TII, TRI, RBI); + constrainSelectedInstRegOperands(InsSubMI, TII, TRI, RBI); + constrainSelectedInstRegOperands(InsEltMI, TII, TRI, RBI); + DstVec = InsDef; + } else { + MachineInstr &InsMI = + *BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opc)) + .addDef(InsDef) + .addUse(DstVec) + .addImm(LaneIdx) + .addUse(I.getOperand(i).getReg()); + constrainSelectedInstRegOperands(InsMI, TII, TRI, RBI); + DstVec = InsDef; + } + } + I.eraseFromParent(); + return true; +} + /// SelectArithImmed - Select an immediate value that can be represented as /// a 12-bit value shifted left by either 0 or 12. If so, return true with /// Val set to the 12-bit value and Shift set to the shifter operand. diff --git a/lib/Target/AArch64/AArch64LegalizerInfo.cpp b/lib/Target/AArch64/AArch64LegalizerInfo.cpp index 327c758a7f8e..6f7fb7a8bc21 100644 --- a/lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ b/lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -48,9 +48,21 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { const LLT v2s64 = LLT::vector(2, 64); getActionDefinitionsBuilder(G_IMPLICIT_DEF) - .legalFor({p0, s1, s8, s16, s32, s64}) - .clampScalar(0, s1, s64) - .widenScalarToNextPow2(0, 8); + .legalFor({p0, s1, s8, s16, s32, s64, v2s64}) + .clampScalar(0, s1, s64) + .widenScalarToNextPow2(0, 8) + .fewerElementsIf( + [=](const LegalityQuery &Query) { + return Query.Types[0].isVector() && + (Query.Types[0].getElementType() != s64 || + Query.Types[0].getNumElements() != 2); + }, + [=](const LegalityQuery &Query) { + LLT EltTy = Query.Types[0].getElementType(); + if (EltTy == s64) + return std::make_pair(0, LLT::vector(2, 64)); + return std::make_pair(0, EltTy); + }); getActionDefinitionsBuilder(G_PHI) .legalFor({p0, s16, s32, s64}) @@ -97,6 +109,16 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { getActionDefinitionsBuilder({G_FREM, G_FPOW}).libcallFor({s32, s64}); + getActionDefinitionsBuilder(G_FCEIL) + // If we don't have full FP16 support, then widen s16 to s32 if we + // encounter it. + .widenScalarIf( + [=, &ST](const LegalityQuery &Query) { + return Query.Types[0] == s16 && !ST.hasFullFP16(); + }, + [=](const LegalityQuery &Query) { return std::make_pair(0, s32); }) + .legalFor({s16, s32, s64, v2s32, v4s32, v2s64}); + getActionDefinitionsBuilder(G_INSERT) .unsupportedIf([=](const LegalityQuery &Query) { return Query.Types[0].getSizeInBits() <= Query.Types[1].getSizeInBits(); @@ -167,9 +189,10 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { .unsupportedIfMemSizeNotPow2() // Lower any any-extending loads left into G_ANYEXT and G_LOAD .lowerIf([=](const LegalityQuery &Query) { - return Query.Types[0].getSizeInBits() != Query.MMODescrs[0].Size * 8; + return Query.Types[0].getSizeInBits() != Query.MMODescrs[0].SizeInBits; }) - .clampNumElements(0, v2s32, v2s32); + .clampNumElements(0, v2s32, v2s32) + .clampMaxNumElements(0, s64, 1); getActionDefinitionsBuilder(G_STORE) .legalForTypesWithMemSize({{s8, p0, 8}, @@ -185,9 +208,10 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { .unsupportedIfMemSizeNotPow2() .lowerIf([=](const LegalityQuery &Query) { return Query.Types[0].isScalar() && - Query.Types[0].getSizeInBits() != Query.MMODescrs[0].Size * 8; + Query.Types[0].getSizeInBits() != Query.MMODescrs[0].SizeInBits; }) - .clampNumElements(0, v2s32, v2s32); + .clampNumElements(0, v2s32, v2s32) + .clampMaxNumElements(0, s64, 1); // Constants getActionDefinitionsBuilder(G_CONSTANT) @@ -385,13 +409,37 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { }); } + getActionDefinitionsBuilder(G_EXTRACT_VECTOR_ELT) + .unsupportedIf([=](const LegalityQuery &Query) { + const LLT &EltTy = Query.Types[1].getElementType(); + return Query.Types[0] != EltTy; + }) + .minScalar(2, s64) + .legalIf([=](const LegalityQuery &Query) { + const LLT &VecTy = Query.Types[1]; + return VecTy == v4s32 || VecTy == v2s64; + }); + + getActionDefinitionsBuilder(G_BUILD_VECTOR) + .legalFor({{v4s32, s32}, {v2s64, s64}}) + .clampNumElements(0, v4s32, v4s32) + .clampNumElements(0, v2s64, v2s64) + + // Deal with larger scalar types, which will be implicitly truncated. + .legalIf([=](const LegalityQuery &Query) { + return Query.Types[0].getScalarSizeInBits() < + Query.Types[1].getSizeInBits(); + }) + .minScalarSameAs(1, 0); + computeTables(); verify(*ST.getInstrInfo()); } bool AArch64LegalizerInfo::legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI, - MachineIRBuilder &MIRBuilder) const { + MachineIRBuilder &MIRBuilder, + GISelChangeObserver &Observer) const { switch (MI.getOpcode()) { default: // No idea what to do. diff --git a/lib/Target/AArch64/AArch64LegalizerInfo.h b/lib/Target/AArch64/AArch64LegalizerInfo.h index a745b0edbc6d..77e8bdc7623c 100644 --- a/lib/Target/AArch64/AArch64LegalizerInfo.h +++ b/lib/Target/AArch64/AArch64LegalizerInfo.h @@ -15,6 +15,7 @@ #ifndef LLVM_LIB_TARGET_AARCH64_AARCH64MACHINELEGALIZER_H #define LLVM_LIB_TARGET_AARCH64_AARCH64MACHINELEGALIZER_H +#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h" #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" namespace llvm { @@ -28,7 +29,8 @@ public: AArch64LegalizerInfo(const AArch64Subtarget &ST); bool legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI, - MachineIRBuilder &MIRBuilder) const override; + MachineIRBuilder &MIRBuilder, + GISelChangeObserver &Observer) const override; private: bool legalizeVaArg(MachineInstr &MI, MachineRegisterInfo &MRI, diff --git a/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp b/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp index 4a19ecd69103..aa732a99469c 100644 --- a/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp +++ b/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp @@ -702,7 +702,7 @@ AArch64LoadStoreOpt::mergeNarrowZeroStores(MachineBasicBlock::iterator I, .addReg(isNarrowStore(Opc) ? AArch64::WZR : AArch64::XZR) .add(BaseRegOp) .addImm(OffsetImm) - .setMemRefs(I->mergeMemRefsWith(*MergeMI)) + .cloneMergedMemRefs({&*I, &*MergeMI}) .setMIFlags(I->mergeFlagsWith(*MergeMI)); (void)MIB; @@ -819,7 +819,7 @@ AArch64LoadStoreOpt::mergePairedInsns(MachineBasicBlock::iterator I, .add(RegOp1) .add(BaseRegOp) .addImm(OffsetImm) - .setMemRefs(I->mergeMemRefsWith(*Paired)) + .cloneMergedMemRefs({&*I, &*Paired}) .setMIFlags(I->mergeFlagsWith(*Paired)); (void)MIB; @@ -1338,7 +1338,7 @@ AArch64LoadStoreOpt::mergeUpdateInsn(MachineBasicBlock::iterator I, .add(getLdStRegOp(*I)) .add(getLdStBaseOp(*I)) .addImm(Value) - .setMemRefs(I->memoperands_begin(), I->memoperands_end()) + .setMemRefs(I->memoperands()) .setMIFlags(I->mergeFlagsWith(*Update)); } else { // Paired instruction. @@ -1349,7 +1349,7 @@ AArch64LoadStoreOpt::mergeUpdateInsn(MachineBasicBlock::iterator I, .add(getLdStRegOp(*I, 1)) .add(getLdStBaseOp(*I)) .addImm(Value / Scale) - .setMemRefs(I->memoperands_begin(), I->memoperands_end()) + .setMemRefs(I->memoperands()) .setMIFlags(I->mergeFlagsWith(*Update)); } (void)MIB; diff --git a/lib/Target/AArch64/AArch64MCInstLower.cpp b/lib/Target/AArch64/AArch64MCInstLower.cpp index 6c0263585933..d71359223b1b 100644 --- a/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -18,6 +18,7 @@ #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/IR/Mangler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" @@ -44,16 +45,31 @@ AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { assert(TheTriple.isOSWindows() && "Windows is the only supported COFF target"); - bool IsIndirect = (TargetFlags & AArch64II::MO_DLLIMPORT); + bool IsIndirect = (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB)); if (!IsIndirect) return Printer.getSymbol(GV); SmallString<128> Name; - Name = "__imp_"; + if (TargetFlags & AArch64II::MO_DLLIMPORT) + Name = "__imp_"; + else if (TargetFlags & AArch64II::MO_COFFSTUB) + Name = ".refptr."; Printer.TM.getNameWithPrefix(Name, GV, Printer.getObjFileLowering().getMangler()); - return Ctx.getOrCreateSymbol(Name); + MCSymbol *MCSym = Ctx.getOrCreateSymbol(Name); + + if (TargetFlags & AArch64II::MO_COFFSTUB) { + MachineModuleInfoCOFF &MMICOFF = + Printer.MMI->getObjFileInfo<MachineModuleInfoCOFF>(); + MachineModuleInfoImpl::StubValueTy &StubSym = + MMICOFF.getGVStubEntry(MCSym); + + if (!StubSym.getPointer()) + StubSym = MachineModuleInfoImpl::StubValueTy(Printer.getSymbol(GV), true); + } + + return MCSym; } MCSymbol * @@ -173,20 +189,51 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO, MCOperand AArch64MCInstLower::lowerSymbolOperandCOFF(const MachineOperand &MO, MCSymbol *Sym) const { - AArch64MCExpr::VariantKind RefKind = AArch64MCExpr::VK_NONE; + uint32_t RefFlags = 0; + if (MO.getTargetFlags() & AArch64II::MO_TLS) { if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGEOFF) - RefKind = AArch64MCExpr::VK_SECREL_LO12; + RefFlags |= AArch64MCExpr::VK_SECREL_LO12; else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_HI12) - RefKind = AArch64MCExpr::VK_SECREL_HI12; + RefFlags |= AArch64MCExpr::VK_SECREL_HI12; + + } else if (MO.getTargetFlags() & AArch64II::MO_S) { + RefFlags |= AArch64MCExpr::VK_SABS; + } else { + RefFlags |= AArch64MCExpr::VK_ABS; + } + + if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G3) + RefFlags |= AArch64MCExpr::VK_G3; + else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G2) + RefFlags |= AArch64MCExpr::VK_G2; + else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G1) + RefFlags |= AArch64MCExpr::VK_G1; + else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G0) + RefFlags |= AArch64MCExpr::VK_G0; + + // FIXME: Currently we only set VK_NC for MO_G3/MO_G2/MO_G1/MO_G0. This is + // because setting VK_NC for others would mean setting their respective + // RefFlags correctly. We should do this in a separate patch. + if (MO.getTargetFlags() & AArch64II::MO_NC) { + auto MOFrag = (MO.getTargetFlags() & AArch64II::MO_FRAGMENT); + if (MOFrag == AArch64II::MO_G3 || MOFrag == AArch64II::MO_G2 || + MOFrag == AArch64II::MO_G1 || MOFrag == AArch64II::MO_G0) + RefFlags |= AArch64MCExpr::VK_NC; } + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); if (!MO.isJTI() && MO.getOffset()) Expr = MCBinaryExpr::createAdd( Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + + auto RefKind = static_cast<AArch64MCExpr::VariantKind>(RefFlags); + assert(RefKind != AArch64MCExpr::VK_INVALID && + "Invalid relocation requested"); Expr = AArch64MCExpr::create(Expr, RefKind, Ctx); + return MCOperand::createExpr(Expr); } @@ -253,4 +300,17 @@ void AArch64MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { if (lowerOperand(MO, MCOp)) OutMI.addOperand(MCOp); } + + switch (OutMI.getOpcode()) { + case AArch64::CATCHRET: + OutMI = MCInst(); + OutMI.setOpcode(AArch64::RET); + OutMI.addOperand(MCOperand::createReg(AArch64::LR)); + break; + case AArch64::CLEANUPRET: + OutMI = MCInst(); + OutMI.setOpcode(AArch64::RET); + OutMI.addOperand(MCOperand::createReg(AArch64::LR)); + break; + } } diff --git a/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/lib/Target/AArch64/AArch64MachineFunctionInfo.h index e42214d15699..5183e7d3c0d0 100644 --- a/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ b/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -18,6 +18,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/MC/MCLinkerOptimizationHint.h" #include <cassert> @@ -97,6 +98,9 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { /// attribute, in which case it is set to false at construction. Optional<bool> HasRedZone; + /// ForwardedMustTailRegParms - A list of virtual and physical registers + /// that must be forwarded to every musttail call. + SmallVector<ForwardedRegister, 1> ForwardedMustTailRegParms; public: AArch64FunctionInfo() = default; @@ -162,6 +166,19 @@ public: unsigned getVarArgsFPRSize() const { return VarArgsFPRSize; } void setVarArgsFPRSize(unsigned Size) { VarArgsFPRSize = Size; } + unsigned getJumpTableEntrySize(int Idx) const { + auto It = JumpTableEntryInfo.find(Idx); + if (It != JumpTableEntryInfo.end()) + return It->second.first; + return 4; + } + MCSymbol *getJumpTableEntryPCRelSymbol(int Idx) const { + return JumpTableEntryInfo.find(Idx)->second.second; + } + void setJumpTableEntryInfo(int Idx, unsigned Size, MCSymbol *PCRelSym) { + JumpTableEntryInfo[Idx] = std::make_pair(Size, PCRelSym); + } + using SetOfInstructions = SmallPtrSet<const MachineInstr *, 16>; const SetOfInstructions &getLOHRelated() const { return LOHRelated; } @@ -196,10 +213,16 @@ public: LOHRelated.insert(Args.begin(), Args.end()); } + SmallVectorImpl<ForwardedRegister> &getForwardedMustTailRegParms() { + return ForwardedMustTailRegParms; + } + private: // Hold the lists of LOHs. MILOHContainer LOHContainerSet; SetOfInstructions LOHRelated; + + DenseMap<int, std::pair<unsigned, MCSymbol *>> JumpTableEntryInfo; }; } // end namespace llvm diff --git a/lib/Target/AArch64/AArch64MacroFusion.cpp b/lib/Target/AArch64/AArch64MacroFusion.cpp index bc0168e783be..bc596dd38b6e 100644 --- a/lib/Target/AArch64/AArch64MacroFusion.cpp +++ b/lib/Target/AArch64/AArch64MacroFusion.cpp @@ -20,161 +20,175 @@ using namespace llvm; namespace { -// Fuse CMN, CMP, TST followed by Bcc. +/// CMN, CMP, TST followed by Bcc static bool isArithmeticBccPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { - if (SecondMI.getOpcode() == AArch64::Bcc) { - // Assume the 1st instr to be a wildcard if it is unspecified. - if (!FirstMI) - return true; + if (SecondMI.getOpcode() != AArch64::Bcc) + return false; - switch (FirstMI->getOpcode()) { - case AArch64::ADDSWri: - case AArch64::ADDSWrr: - case AArch64::ADDSXri: - case AArch64::ADDSXrr: - case AArch64::ANDSWri: - case AArch64::ANDSWrr: - case AArch64::ANDSXri: - case AArch64::ANDSXrr: - case AArch64::SUBSWri: - case AArch64::SUBSWrr: - case AArch64::SUBSXri: - case AArch64::SUBSXrr: - case AArch64::BICSWrr: - case AArch64::BICSXrr: - return true; - case AArch64::ADDSWrs: - case AArch64::ADDSXrs: - case AArch64::ANDSWrs: - case AArch64::ANDSXrs: - case AArch64::SUBSWrs: - case AArch64::SUBSXrs: - case AArch64::BICSWrs: - case AArch64::BICSXrs: - // Shift value can be 0 making these behave like the "rr" variant... - return (!AArch64InstrInfo::hasShiftedReg(*FirstMI)); - } + // Assume the 1st instr to be a wildcard if it is unspecified. + if (FirstMI == nullptr) + return true; + + switch (FirstMI->getOpcode()) { + case AArch64::ADDSWri: + case AArch64::ADDSWrr: + case AArch64::ADDSXri: + case AArch64::ADDSXrr: + case AArch64::ANDSWri: + case AArch64::ANDSWrr: + case AArch64::ANDSXri: + case AArch64::ANDSXrr: + case AArch64::SUBSWri: + case AArch64::SUBSWrr: + case AArch64::SUBSXri: + case AArch64::SUBSXrr: + case AArch64::BICSWrr: + case AArch64::BICSXrr: + return true; + case AArch64::ADDSWrs: + case AArch64::ADDSXrs: + case AArch64::ANDSWrs: + case AArch64::ANDSXrs: + case AArch64::SUBSWrs: + case AArch64::SUBSXrs: + case AArch64::BICSWrs: + case AArch64::BICSXrs: + // Shift value can be 0 making these behave like the "rr" variant... + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); } + return false; } -// Fuse ALU operations followed by CBZ/CBNZ. +/// ALU operations followed by CBZ/CBNZ. static bool isArithmeticCbzPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { - unsigned SecondOpcode = SecondMI.getOpcode(); + if (SecondMI.getOpcode() != AArch64::CBZW && + SecondMI.getOpcode() != AArch64::CBZX && + SecondMI.getOpcode() != AArch64::CBNZW && + SecondMI.getOpcode() != AArch64::CBNZX) + return false; - if (SecondOpcode == AArch64::CBNZW || SecondOpcode == AArch64::CBNZX || - SecondOpcode == AArch64::CBZW || SecondOpcode == AArch64::CBZX) { - // Assume the 1st instr to be a wildcard if it is unspecified. - if (!FirstMI) - return true; + // Assume the 1st instr to be a wildcard if it is unspecified. + if (FirstMI == nullptr) + return true; - switch (FirstMI->getOpcode()) { - case AArch64::ADDWri: - case AArch64::ADDWrr: - case AArch64::ADDXri: - case AArch64::ADDXrr: - case AArch64::ANDWri: - case AArch64::ANDWrr: - case AArch64::ANDXri: - case AArch64::ANDXrr: - case AArch64::EORWri: - case AArch64::EORWrr: - case AArch64::EORXri: - case AArch64::EORXrr: - case AArch64::ORRWri: - case AArch64::ORRWrr: - case AArch64::ORRXri: - case AArch64::ORRXrr: - case AArch64::SUBWri: - case AArch64::SUBWrr: - case AArch64::SUBXri: - case AArch64::SUBXrr: - return true; - case AArch64::ADDWrs: - case AArch64::ADDXrs: - case AArch64::ANDWrs: - case AArch64::ANDXrs: - case AArch64::SUBWrs: - case AArch64::SUBXrs: - case AArch64::BICWrs: - case AArch64::BICXrs: - // Shift value can be 0 making these behave like the "rr" variant... - return (!AArch64InstrInfo::hasShiftedReg(*FirstMI)); - } + switch (FirstMI->getOpcode()) { + case AArch64::ADDWri: + case AArch64::ADDWrr: + case AArch64::ADDXri: + case AArch64::ADDXrr: + case AArch64::ANDWri: + case AArch64::ANDWrr: + case AArch64::ANDXri: + case AArch64::ANDXrr: + case AArch64::EORWri: + case AArch64::EORWrr: + case AArch64::EORXri: + case AArch64::EORXrr: + case AArch64::ORRWri: + case AArch64::ORRWrr: + case AArch64::ORRXri: + case AArch64::ORRXrr: + case AArch64::SUBWri: + case AArch64::SUBWrr: + case AArch64::SUBXri: + case AArch64::SUBXrr: + return true; + case AArch64::ADDWrs: + case AArch64::ADDXrs: + case AArch64::ANDWrs: + case AArch64::ANDXrs: + case AArch64::SUBWrs: + case AArch64::SUBXrs: + case AArch64::BICWrs: + case AArch64::BICXrs: + // Shift value can be 0 making these behave like the "rr" variant... + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); } + return false; } -// Fuse AES crypto encoding or decoding. +/// AES crypto encoding or decoding. static bool isAESPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { // Assume the 1st instr to be a wildcard if it is unspecified. - unsigned FirstOpcode = - FirstMI ? FirstMI->getOpcode() - : static_cast<unsigned>(AArch64::INSTRUCTION_LIST_END); - unsigned SecondOpcode = SecondMI.getOpcode(); - + switch (SecondMI.getOpcode()) { // AES encode. - if ((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - FirstOpcode == AArch64::AESErr) && - (SecondOpcode == AArch64::AESMCrr || - SecondOpcode == AArch64::AESMCrrTied)) - return true; + case AArch64::AESMCrr: + case AArch64::AESMCrrTied: + return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESErr; // AES decode. - else if ((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - FirstOpcode == AArch64::AESDrr) && - (SecondOpcode == AArch64::AESIMCrr || - SecondOpcode == AArch64::AESIMCrrTied)) + case AArch64::AESIMCrr: + case AArch64::AESIMCrrTied: + return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESDrr; + } + + return false; +} + +/// AESE/AESD/PMULL + EOR. +static bool isCryptoEORPair(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + if (SecondMI.getOpcode() != AArch64::EORv16i8) + return false; + + // Assume the 1st instr to be a wildcard if it is unspecified. + if (FirstMI == nullptr) + return true; + + switch (FirstMI->getOpcode()) { + case AArch64::AESErr: + case AArch64::AESDrr: + case AArch64::PMULLv16i8: + case AArch64::PMULLv8i8: + case AArch64::PMULLv1i64: + case AArch64::PMULLv2i64: return true; + } return false; } -// Fuse literal generation. +/// Literal generation. static bool isLiteralsPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { // Assume the 1st instr to be a wildcard if it is unspecified. - unsigned FirstOpcode = - FirstMI ? FirstMI->getOpcode() - : static_cast<unsigned>(AArch64::INSTRUCTION_LIST_END); - unsigned SecondOpcode = SecondMI.getOpcode(); // PC relative address. - if ((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - FirstOpcode == AArch64::ADRP) && - SecondOpcode == AArch64::ADDXri) + if ((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::ADRP) && + SecondMI.getOpcode() == AArch64::ADDXri) return true; + // 32 bit immediate. - else if ((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - FirstOpcode == AArch64::MOVZWi) && - (SecondOpcode == AArch64::MOVKWi && - SecondMI.getOperand(3).getImm() == 16)) + if ((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZWi) && + (SecondMI.getOpcode() == AArch64::MOVKWi && + SecondMI.getOperand(3).getImm() == 16)) return true; + // Lower half of 64 bit immediate. - else if((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - FirstOpcode == AArch64::MOVZXi) && - (SecondOpcode == AArch64::MOVKXi && - SecondMI.getOperand(3).getImm() == 16)) + if((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZXi) && + (SecondMI.getOpcode() == AArch64::MOVKXi && + SecondMI.getOperand(3).getImm() == 16)) return true; + // Upper half of 64 bit immediate. - else if ((FirstOpcode == AArch64::INSTRUCTION_LIST_END || - (FirstOpcode == AArch64::MOVKXi && - FirstMI->getOperand(3).getImm() == 32)) && - (SecondOpcode == AArch64::MOVKXi && - SecondMI.getOperand(3).getImm() == 48)) + if ((FirstMI == nullptr || + (FirstMI->getOpcode() == AArch64::MOVKXi && + FirstMI->getOperand(3).getImm() == 32)) && + (SecondMI.getOpcode() == AArch64::MOVKXi && + SecondMI.getOperand(3).getImm() == 48)) return true; return false; } -// Fuse address generation and loads or stores. +/// Fuse address generation and loads or stores. static bool isAddressLdStPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { - unsigned SecondOpcode = SecondMI.getOpcode(); - - switch (SecondOpcode) { + switch (SecondMI.getOpcode()) { case AArch64::STRBBui: case AArch64::STRBui: case AArch64::STRDui: @@ -199,63 +213,164 @@ static bool isAddressLdStPair(const MachineInstr *FirstMI, case AArch64::LDRSHXui: case AArch64::LDRSWui: // Assume the 1st instr to be a wildcard if it is unspecified. - if (!FirstMI) + if (FirstMI == nullptr) return true; - switch (FirstMI->getOpcode()) { + switch (FirstMI->getOpcode()) { case AArch64::ADR: - return (SecondMI.getOperand(2).getImm() == 0); + return SecondMI.getOperand(2).getImm() == 0; case AArch64::ADRP: return true; } } + return false; } -// Fuse compare and conditional select. +/// Compare and conditional select. static bool isCCSelectPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI) { - unsigned SecondOpcode = SecondMI.getOpcode(); - // 32 bits - if (SecondOpcode == AArch64::CSELWr) { + if (SecondMI.getOpcode() == AArch64::CSELWr) { // Assume the 1st instr to be a wildcard if it is unspecified. - if (!FirstMI) + if (FirstMI == nullptr) return true; if (FirstMI->definesRegister(AArch64::WZR)) switch (FirstMI->getOpcode()) { case AArch64::SUBSWrs: - return (!AArch64InstrInfo::hasShiftedReg(*FirstMI)); + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); case AArch64::SUBSWrx: - return (!AArch64InstrInfo::hasExtendedReg(*FirstMI)); + return !AArch64InstrInfo::hasExtendedReg(*FirstMI); case AArch64::SUBSWrr: case AArch64::SUBSWri: return true; } } + // 64 bits - else if (SecondOpcode == AArch64::CSELXr) { + if (SecondMI.getOpcode() == AArch64::CSELXr) { // Assume the 1st instr to be a wildcard if it is unspecified. - if (!FirstMI) + if (FirstMI == nullptr) return true; if (FirstMI->definesRegister(AArch64::XZR)) switch (FirstMI->getOpcode()) { case AArch64::SUBSXrs: - return (!AArch64InstrInfo::hasShiftedReg(*FirstMI)); + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); case AArch64::SUBSXrx: case AArch64::SUBSXrx64: - return (!AArch64InstrInfo::hasExtendedReg(*FirstMI)); + return !AArch64InstrInfo::hasExtendedReg(*FirstMI); case AArch64::SUBSXrr: case AArch64::SUBSXri: return true; } } + + return false; +} + +// Arithmetic and logic. +static bool isArithmeticLogicPair(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + if (AArch64InstrInfo::hasShiftedReg(SecondMI)) + return false; + + switch (SecondMI.getOpcode()) { + // Arithmetic + case AArch64::ADDWrr: + case AArch64::ADDXrr: + case AArch64::SUBWrr: + case AArch64::SUBXrr: + case AArch64::ADDWrs: + case AArch64::ADDXrs: + case AArch64::SUBWrs: + case AArch64::SUBXrs: + // Logic + case AArch64::ANDWrr: + case AArch64::ANDXrr: + case AArch64::BICWrr: + case AArch64::BICXrr: + case AArch64::EONWrr: + case AArch64::EONXrr: + case AArch64::EORWrr: + case AArch64::EORXrr: + case AArch64::ORNWrr: + case AArch64::ORNXrr: + case AArch64::ORRWrr: + case AArch64::ORRXrr: + case AArch64::ANDWrs: + case AArch64::ANDXrs: + case AArch64::BICWrs: + case AArch64::BICXrs: + case AArch64::EONWrs: + case AArch64::EONXrs: + case AArch64::EORWrs: + case AArch64::EORXrs: + case AArch64::ORNWrs: + case AArch64::ORNXrs: + case AArch64::ORRWrs: + case AArch64::ORRXrs: + // Assume the 1st instr to be a wildcard if it is unspecified. + if (FirstMI == nullptr) + return true; + + // Arithmetic + switch (FirstMI->getOpcode()) { + case AArch64::ADDWrr: + case AArch64::ADDXrr: + case AArch64::ADDSWrr: + case AArch64::ADDSXrr: + case AArch64::SUBWrr: + case AArch64::SUBXrr: + case AArch64::SUBSWrr: + case AArch64::SUBSXrr: + return true; + case AArch64::ADDWrs: + case AArch64::ADDXrs: + case AArch64::ADDSWrs: + case AArch64::ADDSXrs: + case AArch64::SUBWrs: + case AArch64::SUBXrs: + case AArch64::SUBSWrs: + case AArch64::SUBSXrs: + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); + } + break; + + // Arithmetic, setting flags. + case AArch64::ADDSWrr: + case AArch64::ADDSXrr: + case AArch64::SUBSWrr: + case AArch64::SUBSXrr: + case AArch64::ADDSWrs: + case AArch64::ADDSXrs: + case AArch64::SUBSWrs: + case AArch64::SUBSXrs: + // Assume the 1st instr to be a wildcard if it is unspecified. + if (FirstMI == nullptr) + return true; + + // Arithmetic, not setting flags. + switch (FirstMI->getOpcode()) { + case AArch64::ADDWrr: + case AArch64::ADDXrr: + case AArch64::SUBWrr: + case AArch64::SUBXrr: + return true; + case AArch64::ADDWrs: + case AArch64::ADDXrs: + case AArch64::SUBWrs: + case AArch64::SUBXrs: + return !AArch64InstrInfo::hasShiftedReg(*FirstMI); + } + break; + } + return false; } -/// Check if the instr pair, FirstMI and SecondMI, should be fused +/// \brief Check if the instr pair, FirstMI and SecondMI, should be fused /// together. Given SecondMI, when FirstMI is unspecified, then check if /// SecondMI may be part of a fused pair at all. static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, @@ -264,18 +379,24 @@ static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, const MachineInstr &SecondMI) { const AArch64Subtarget &ST = static_cast<const AArch64Subtarget&>(TSI); + // All checking functions assume that the 1st instr is a wildcard if it is + // unspecified. if (ST.hasArithmeticBccFusion() && isArithmeticBccPair(FirstMI, SecondMI)) return true; if (ST.hasArithmeticCbzFusion() && isArithmeticCbzPair(FirstMI, SecondMI)) return true; if (ST.hasFuseAES() && isAESPair(FirstMI, SecondMI)) return true; + if (ST.hasFuseCryptoEOR() && isCryptoEORPair(FirstMI, SecondMI)) + return true; if (ST.hasFuseLiterals() && isLiteralsPair(FirstMI, SecondMI)) return true; if (ST.hasFuseAddress() && isAddressLdStPair(FirstMI, SecondMI)) return true; if (ST.hasFuseCCSelect() && isCCSelectPair(FirstMI, SecondMI)) return true; + if (ST.hasFuseArithmeticLogic() && isArithmeticLogicPair(FirstMI, SecondMI)) + return true; return false; } diff --git a/lib/Target/AArch64/AArch64PfmCounters.td b/lib/Target/AArch64/AArch64PfmCounters.td new file mode 100644 index 000000000000..16ba3e4282a0 --- /dev/null +++ b/lib/Target/AArch64/AArch64PfmCounters.td @@ -0,0 +1,19 @@ +//===-- AArch64PfmCounters.td - AArch64 Hardware Counters --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This describes the available hardware counters for AArch64. +// +//===----------------------------------------------------------------------===// + +def CpuCyclesPfmCounter : PfmCounter<"CPU_CYCLES">; + +def DefaultPfmCounters : ProcPfmCounters { + let CycleCounter = CpuCyclesPfmCounter; +} +def : PfmCountersDefaultBinding<DefaultPfmCounters>; diff --git a/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp b/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp new file mode 100644 index 000000000000..3da9306e6460 --- /dev/null +++ b/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp @@ -0,0 +1,108 @@ +//=== lib/CodeGen/GlobalISel/AArch64PreLegalizerCombiner.cpp --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "AArch64TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/Combiner.h" +#include "llvm/CodeGen/GlobalISel/CombinerHelper.h" +#include "llvm/CodeGen/GlobalISel/CombinerInfo.h" +#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "aarch64-prelegalizer-combiner" + +using namespace llvm; +using namespace MIPatternMatch; + +namespace { +class AArch64PreLegalizerCombinerInfo : public CombinerInfo { +public: + AArch64PreLegalizerCombinerInfo() + : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false, + /*LegalizerInfo*/ nullptr) {} + virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI, + MachineIRBuilder &B) const override; +}; + +bool AArch64PreLegalizerCombinerInfo::combine(GISelChangeObserver &Observer, + MachineInstr &MI, + MachineIRBuilder &B) const { + CombinerHelper Helper(Observer, B); + + switch (MI.getOpcode()) { + default: + return false; + case TargetOpcode::G_LOAD: + case TargetOpcode::G_SEXTLOAD: + case TargetOpcode::G_ZEXTLOAD: + return Helper.tryCombineExtendingLoads(MI); + } + + return false; +} + +// Pass boilerplate +// ================ + +class AArch64PreLegalizerCombiner : public MachineFunctionPass { +public: + static char ID; + + AArch64PreLegalizerCombiner(); + + StringRef getPassName() const override { return "AArch64PreLegalizerCombiner"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; +} + +void AArch64PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<TargetPassConfig>(); + AU.setPreservesCFG(); + getSelectionDAGFallbackAnalysisUsage(AU); + MachineFunctionPass::getAnalysisUsage(AU); +} + +AArch64PreLegalizerCombiner::AArch64PreLegalizerCombiner() : MachineFunctionPass(ID) { + initializeAArch64PreLegalizerCombinerPass(*PassRegistry::getPassRegistry()); +} + +bool AArch64PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) { + if (MF.getProperties().hasProperty( + MachineFunctionProperties::Property::FailedISel)) + return false; + auto *TPC = &getAnalysis<TargetPassConfig>(); + AArch64PreLegalizerCombinerInfo PCInfo; + Combiner C(PCInfo, TPC); + return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr); +} + +char AArch64PreLegalizerCombiner::ID = 0; +INITIALIZE_PASS_BEGIN(AArch64PreLegalizerCombiner, DEBUG_TYPE, + "Combine AArch64 machine instrs before legalization", + false, false) +INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) +INITIALIZE_PASS_END(AArch64PreLegalizerCombiner, DEBUG_TYPE, + "Combine AArch64 machine instrs before legalization", false, + false) + + +namespace llvm { +FunctionPass *createAArch64PreLegalizeCombiner() { + return new AArch64PreLegalizerCombiner(); +} +} // end namespace llvm diff --git a/lib/Target/AArch64/AArch64RegisterBankInfo.cpp b/lib/Target/AArch64/AArch64RegisterBankInfo.cpp index c497669f937f..68c48a5ec216 100644 --- a/lib/Target/AArch64/AArch64RegisterBankInfo.cpp +++ b/lib/Target/AArch64/AArch64RegisterBankInfo.cpp @@ -389,6 +389,7 @@ static bool isPreISelGenericFloatingPointOpcode(unsigned Opc) { case TargetOpcode::G_FCONSTANT: case TargetOpcode::G_FPEXT: case TargetOpcode::G_FPTRUNC: + case TargetOpcode::G_FCEIL: return true; } return false; diff --git a/lib/Target/AArch64/AArch64RegisterInfo.cpp b/lib/Target/AArch64/AArch64RegisterInfo.cpp index a7c2c1b8125b..96ae45ae3d0d 100644 --- a/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -25,6 +25,7 @@ #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RegisterScavenging.h" #include "llvm/IR/Function.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/Support/raw_ostream.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/Target/TargetOptions.h" @@ -42,12 +43,16 @@ AArch64RegisterInfo::AArch64RegisterInfo(const Triple &TT) const MCPhysReg * AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); + if (MF->getSubtarget<AArch64Subtarget>().isTargetWindows()) + return CSR_Win_AArch64_AAPCS_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::GHC) // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around return CSR_AArch64_NoRegs_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::AnyReg) return CSR_AArch64_AllRegs_SaveList; + if (MF->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall) + return CSR_AArch64_AAVPCS_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::CXX_FAST_TLS) return MF->getInfo<AArch64FunctionInfo>()->isSplitCSR() ? CSR_AArch64_CXX_TLS_Darwin_PE_SaveList : @@ -72,6 +77,23 @@ const MCPhysReg *AArch64RegisterInfo::getCalleeSavedRegsViaCopy( return nullptr; } +void AArch64RegisterInfo::UpdateCustomCalleeSavedRegs( + MachineFunction &MF) const { + const MCPhysReg *CSRs = getCalleeSavedRegs(&MF); + SmallVector<MCPhysReg, 32> UpdatedCSRs; + for (const MCPhysReg *I = CSRs; *I; ++I) + UpdatedCSRs.push_back(*I); + + for (size_t i = 0; i < AArch64::GPR64commonRegClass.getNumRegs(); ++i) { + if (MF.getSubtarget<AArch64Subtarget>().isXRegCustomCalleeSaved(i)) { + UpdatedCSRs.push_back(AArch64::GPR64commonRegClass.getRegister(i)); + } + } + // Register lists are zero-terminated. + UpdatedCSRs.push_back(0); + MF.getRegInfo().setCalleeSavedRegs(UpdatedCSRs); +} + const TargetRegisterClass * AArch64RegisterInfo::getSubClassWithSubReg(const TargetRegisterClass *RC, unsigned Idx) const { @@ -97,6 +119,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (CC == CallingConv::CXX_FAST_TLS) return SCS ? CSR_AArch64_CXX_TLS_Darwin_SCS_RegMask : CSR_AArch64_CXX_TLS_Darwin_RegMask; + if (CC == CallingConv::AArch64_VectorCall) + return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask; if (MF.getSubtarget<AArch64Subtarget>().getTargetLowering() ->supportSwiftError() && MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError)) @@ -117,6 +141,30 @@ const uint32_t *AArch64RegisterInfo::getTLSCallPreservedMask() const { return CSR_AArch64_TLS_ELF_RegMask; } +void AArch64RegisterInfo::UpdateCustomCallPreservedMask(MachineFunction &MF, + const uint32_t **Mask) const { + uint32_t *UpdatedMask = MF.allocateRegMask(); + unsigned RegMaskSize = MachineOperand::getRegMaskSize(getNumRegs()); + memcpy(UpdatedMask, *Mask, sizeof(UpdatedMask[0]) * RegMaskSize); + + for (size_t i = 0; i < AArch64::GPR64commonRegClass.getNumRegs(); ++i) { + if (MF.getSubtarget<AArch64Subtarget>().isXRegCustomCalleeSaved(i)) { + for (MCSubRegIterator SubReg(AArch64::GPR64commonRegClass.getRegister(i), + this, true); + SubReg.isValid(); ++SubReg) { + // See TargetRegisterInfo::getCallPreservedMask for how to interpret the + // register mask. + UpdatedMask[*SubReg / 32] |= 1u << (*SubReg % 32); + } + } + } + *Mask = UpdatedMask; +} + +const uint32_t *AArch64RegisterInfo::getNoPreservedMask() const { + return CSR_AArch64_NoRegs_RegMask; +} + const uint32_t * AArch64RegisterInfo::getThisReturnPreservedMask(const MachineFunction &MF, CallingConv::ID CC) const { @@ -147,46 +195,46 @@ AArch64RegisterInfo::getReservedRegs(const MachineFunction &MF) const { if (TFI->hasFP(MF) || TT.isOSDarwin()) markSuperRegs(Reserved, AArch64::W29); - if (MF.getSubtarget<AArch64Subtarget>().isX18Reserved()) - markSuperRegs(Reserved, AArch64::W18); // Platform register - - if (MF.getSubtarget<AArch64Subtarget>().isX20Reserved()) - markSuperRegs(Reserved, AArch64::W20); // Platform register + for (size_t i = 0; i < AArch64::GPR32commonRegClass.getNumRegs(); ++i) { + if (MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(i)) + markSuperRegs(Reserved, AArch64::GPR32commonRegClass.getRegister(i)); + } if (hasBasePointer(MF)) markSuperRegs(Reserved, AArch64::W19); + // SLH uses register W16/X16 as the taint register. + if (MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening)) + markSuperRegs(Reserved, AArch64::W16); + assert(checkAllSuperRegsMarked(Reserved)); return Reserved; } bool AArch64RegisterInfo::isReservedReg(const MachineFunction &MF, unsigned Reg) const { - const AArch64FrameLowering *TFI = getFrameLowering(MF); + return getReservedRegs(MF)[Reg]; +} - switch (Reg) { - default: - break; - case AArch64::SP: - case AArch64::XZR: - case AArch64::WSP: - case AArch64::WZR: - return true; - case AArch64::X18: - case AArch64::W18: - return MF.getSubtarget<AArch64Subtarget>().isX18Reserved(); - case AArch64::X19: - case AArch64::W19: - return hasBasePointer(MF); - case AArch64::X20: - case AArch64::W20: - return MF.getSubtarget<AArch64Subtarget>().isX20Reserved(); - case AArch64::FP: - case AArch64::W29: - return TFI->hasFP(MF) || TT.isOSDarwin(); - } +bool AArch64RegisterInfo::isAnyArgRegReserved(const MachineFunction &MF) const { + // FIXME: Get the list of argument registers from TableGen. + static const MCPhysReg GPRArgRegs[] = { AArch64::X0, AArch64::X1, AArch64::X2, + AArch64::X3, AArch64::X4, AArch64::X5, + AArch64::X6, AArch64::X7 }; + return std::any_of(std::begin(GPRArgRegs), std::end(GPRArgRegs), + [this, &MF](MCPhysReg r){return isReservedReg(MF, r);}); +} - return false; +void AArch64RegisterInfo::emitReservedArgRegCallError( + const MachineFunction &MF) const { + const Function &F = MF.getFunction(); + F.getContext().diagnose(DiagnosticInfoUnsupported{F, "AArch64 doesn't support" + " function calls if any of the argument registers is reserved."}); +} + +bool AArch64RegisterInfo::isAsmClobberable(const MachineFunction &MF, + unsigned PhysReg) const { + return !isReservedReg(MF, PhysReg); } bool AArch64RegisterInfo::isConstantPhysReg(unsigned PhysReg) const { @@ -211,14 +259,15 @@ unsigned AArch64RegisterInfo::getBaseRegister() const { return AArch64::X19; } bool AArch64RegisterInfo::hasBasePointer(const MachineFunction &MF) const { const MachineFrameInfo &MFI = MF.getFrameInfo(); - // In the presence of variable sized objects, if the fixed stack size is - // large enough that referencing from the FP won't result in things being - // in range relatively often, we can use a base pointer to allow access + // In the presence of variable sized objects or funclets, if the fixed stack + // size is large enough that referencing from the FP won't result in things + // being in range relatively often, we can use a base pointer to allow access // from the other direction like the SP normally works. + // // Furthermore, if both variable sized objects are present, and the // stack needs to be dynamically re-aligned, the base pointer is the only // reliable way to reference the locals. - if (MFI.hasVarSizedObjects()) { + if (MFI.hasVarSizedObjects() || MF.hasEHFunclets()) { if (needsStackRealignment(MF)) return true; // Conservatively estimate whether the negative offset from the frame @@ -449,10 +498,7 @@ unsigned AArch64RegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC, case AArch64::GPR64commonRegClassID: return 32 - 1 // XZR/SP - (TFI->hasFP(MF) || TT.isOSDarwin()) // FP - - MF.getSubtarget<AArch64Subtarget>() - .isX18Reserved() // X18 reserved as platform register - - MF.getSubtarget<AArch64Subtarget>() - .isX20Reserved() // X20 reserved as platform register + - MF.getSubtarget<AArch64Subtarget>().getNumXRegisterReserved() - hasBasePointer(MF); // X19 case AArch64::FPR8RegClassID: case AArch64::FPR16RegClassID: diff --git a/lib/Target/AArch64/AArch64RegisterInfo.h b/lib/Target/AArch64/AArch64RegisterInfo.h index 57000d37090d..c4153228a7c0 100644 --- a/lib/Target/AArch64/AArch64RegisterInfo.h +++ b/lib/Target/AArch64/AArch64RegisterInfo.h @@ -30,7 +30,18 @@ class AArch64RegisterInfo final : public AArch64GenRegisterInfo { public: AArch64RegisterInfo(const Triple &TT); + // FIXME: This should be tablegen'd like getDwarfRegNum is + int getSEHRegNum(unsigned i) const { + return getEncodingValue(i); + } + bool isReservedReg(const MachineFunction &MF, unsigned Reg) const; + bool isAnyArgRegReserved(const MachineFunction &MF) const; + void emitReservedArgRegCallError(const MachineFunction &MF) const; + + void UpdateCustomCalleeSavedRegs(MachineFunction &MF) const; + void UpdateCustomCallPreservedMask(MachineFunction &MF, + const uint32_t **Mask) const; /// Code Generation virtual methods... const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; @@ -54,6 +65,9 @@ public: // normal calls, so they need a different mask to represent this. const uint32_t *getTLSCallPreservedMask() const; + // Funclets on ARM64 Windows don't preserve any registers. + const uint32_t *getNoPreservedMask() const override; + /// getThisReturnPreservedMask - Returns a call preserved mask specific to the /// case that 'returned' is on an i64 first argument if the calling convention /// is one that can (partially) model this attribute with a preserved mask @@ -69,6 +83,8 @@ public: const uint32_t *getWindowsStackProbePreservedMask() const; BitVector getReservedRegs(const MachineFunction &MF) const override; + bool isAsmClobberable(const MachineFunction &MF, + unsigned PhysReg) const override; bool isConstantPhysReg(unsigned PhysReg) const override; const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, @@ -76,8 +92,6 @@ public: const TargetRegisterClass * getCrossCopyRegClass(const TargetRegisterClass *RC) const override; - bool enableMultipleCopyHints() const override { return true; } - bool requiresRegisterScavenging(const MachineFunction &MF) const override; bool useFPForScavengingIndex(const MachineFunction &MF) const override; bool requiresFrameIndexScavenging(const MachineFunction &MF) const override; diff --git a/lib/Target/AArch64/AArch64RegisterInfo.td b/lib/Target/AArch64/AArch64RegisterInfo.td index bbf401b474ca..d3710cea0687 100644 --- a/lib/Target/AArch64/AArch64RegisterInfo.td +++ b/lib/Target/AArch64/AArch64RegisterInfo.td @@ -200,6 +200,12 @@ def tcGPR64 : RegisterClass<"AArch64", [i64], 64, (sub GPR64common, X19, X20, X2 X22, X23, X24, X25, X26, X27, X28, FP, LR)>; +// Restricted set of tail call registers, for use when branch target +// enforcement is enabled. These are the only registers which can be used to +// indirectly branch (not call) to the "BTI c" instruction at the start of a +// BTI-protected function. +def rtcGPR64 : RegisterClass<"AArch64", [i64], 64, (add X16, X17)>; + // GPR register classes for post increment amount of vector load/store that // has alternate printing when Rm=31 and prints a constant immediate value // equal to the total number of bytes transferred. diff --git a/lib/Target/AArch64/AArch64SchedExynosM1.td b/lib/Target/AArch64/AArch64SchedExynosM1.td index ecc68aed1550..f757d53b6c1c 100644 --- a/lib/Target/AArch64/AArch64SchedExynosM1.td +++ b/lib/Target/AArch64/AArch64SchedExynosM1.td @@ -62,13 +62,6 @@ def M1UnitNALU : ProcResGroup<[M1UnitNAL0, M1UnitNAL1]>; // All simple vector //===----------------------------------------------------------------------===// -// Predicates. - -def M1BranchLinkFastPred : SchedPredicate<[{MI->getOpcode() == AArch64::BLR && - MI->getOperand(0).getReg() != AArch64::LR}]>; -def M1ShiftLeftFastPred : SchedPredicate<[{TII->isExynosShiftLeftFast(*MI)}]>; - -//===----------------------------------------------------------------------===// // Coarse scheduling model. def M1WriteA1 : SchedWriteRes<[M1UnitALU]> { let Latency = 1; } @@ -85,14 +78,15 @@ def M1WriteAC : SchedWriteRes<[M1UnitALU, def M1WriteAD : SchedWriteRes<[M1UnitALU, M1UnitC]> { let Latency = 2; let NumMicroOps = 2; } -def M1WriteAX : SchedWriteVariant<[SchedVar<M1ShiftLeftFastPred, [M1WriteA1]>, - SchedVar<NoSchedPred, [M1WriteAA]>]>; +def M1WriteAX : SchedWriteVariant<[SchedVar<ExynosArithPred, [M1WriteA1]>, + SchedVar<ExynosLogicPred, [M1WriteA1]>, + SchedVar<NoSchedPred, [M1WriteAA]>]>; def M1WriteC1 : SchedWriteRes<[M1UnitC]> { let Latency = 1; } def M1WriteC2 : SchedWriteRes<[M1UnitC]> { let Latency = 2; } def M1WriteB1 : SchedWriteRes<[M1UnitB]> { let Latency = 1; } -def M1WriteBX : SchedWriteVariant<[SchedVar<M1BranchLinkFastPred, [M1WriteAB]>, - SchedVar<NoSchedPred, [M1WriteAC]>]>; +def M1WriteBX : SchedWriteVariant<[SchedVar<ExynosBranchLinkLRPred, [M1WriteAC]>, + SchedVar<NoSchedPred, [M1WriteAB]>]>; def M1WriteL5 : SchedWriteRes<[M1UnitL]> { let Latency = 5; } def M1WriteL6 : SchedWriteRes<[M1UnitL]> { let Latency = 6; } @@ -110,40 +104,27 @@ def M1WriteLD : SchedWriteRes<[M1UnitL, let ResourceCycles = [2, 1]; } def M1WriteLH : SchedWriteRes<[]> { let Latency = 5; let NumMicroOps = 0; } -def M1WriteLX : SchedWriteVariant<[SchedVar<M1ShiftLeftFastPred, [M1WriteL5]>, - SchedVar<NoSchedPred, [M1WriteLC]>]>; -def M1WriteLY : SchedWriteVariant<[SchedVar<M1ShiftLeftFastPred, [M1WriteL5]>, - SchedVar<NoSchedPred, [M1WriteLD]>]>; +def M1WriteLX : SchedWriteVariant<[SchedVar<ScaledIdxPred, [M1WriteLC]>, + SchedVar<NoSchedPred, [M1WriteL5]>]>; def M1WriteS1 : SchedWriteRes<[M1UnitS]> { let Latency = 1; } def M1WriteS3 : SchedWriteRes<[M1UnitS]> { let Latency = 3; } def M1WriteS4 : SchedWriteRes<[M1UnitS]> { let Latency = 4; } def M1WriteSA : SchedWriteRes<[M1UnitS, M1UnitFST, - M1UnitS, - M1UnitFST]> { let Latency = 1; - let NumMicroOps = 2; } -def M1WriteSB : SchedWriteRes<[M1UnitS, - M1UnitFST, M1UnitA]> { let Latency = 3; let NumMicroOps = 2; } -def M1WriteSC : SchedWriteRes<[M1UnitS, +def M1WriteSB : SchedWriteRes<[M1UnitS, M1UnitFST, M1UnitS, M1UnitFST, M1UnitA]> { let Latency = 3; let NumMicroOps = 3; } -def M1WriteSD : SchedWriteRes<[M1UnitS, - M1UnitFST, - M1UnitA]> { let Latency = 1; - let NumMicroOps = 2; } -def M1WriteSE : SchedWriteRes<[M1UnitS, +def M1WriteSC : SchedWriteRes<[M1UnitS, M1UnitA]> { let Latency = 2; let NumMicroOps = 2; } -def M1WriteSX : SchedWriteVariant<[SchedVar<M1ShiftLeftFastPred, [M1WriteS1]>, - SchedVar<NoSchedPred, [M1WriteSE]>]>; -def M1WriteSY : SchedWriteVariant<[SchedVar<M1ShiftLeftFastPred, [M1WriteS1]>, - SchedVar<NoSchedPred, [M1WriteSB]>]>; +def M1WriteSX : SchedWriteVariant<[SchedVar<ScaledIdxPred, [M1WriteSC]>, + SchedVar<NoSchedPred, [M1WriteS1]>]>; def M1ReadAdrBase : SchedReadVariant<[SchedVar<ScaledIdxPred, [ReadDefault]>, SchedVar<NoSchedPred, [ReadDefault]>]>; @@ -414,9 +395,9 @@ def M1WriteVSTH : SchedWriteRes<[M1UnitNALU, M1UnitS, M1UnitFST, M1UnitFST, - M1UnitFST]> { let Latency = 14; - let NumMicroOps = 4; - let ResourceCycles = [1, 7, 1, 7, 1]; } + M1UnitFST]> { let Latency = 14; + let NumMicroOps = 4; + let ResourceCycles = [1, 7, 1, 7, 1]; } def M1WriteVSTI : SchedWriteRes<[M1UnitNALU, M1UnitS, M1UnitFST, @@ -427,9 +408,17 @@ def M1WriteVSTI : SchedWriteRes<[M1UnitNALU, M1UnitS, M1UnitFST, M1UnitFST, - M1UnitFST]> { let Latency = 17; - let NumMicroOps = 7; - let ResourceCycles = [1, 7, 1, 7, 1, 1, 1, 1, 1, 1, 1]; } + M1UnitFST]> { let Latency = 17; + let NumMicroOps = 7; + let ResourceCycles = [1, 7, 1, 7, 1, 1, 1, 1, 1, 1, 1]; } + +// Special cases. +def M1WriteAES : SchedWriteRes<[M1UnitNCRYPT]> { let Latency = 1; } +def M1WriteCOPY : SchedWriteVariant<[SchedVar<ExynosFPPred, [M1WriteNALU1]>, + SchedVar<NoSchedPred, [M1WriteA1]>]>; + +// Fast forwarding. +def M1ReadAES : SchedReadAdvance<1, [M1WriteAES]>; // Branch instructions def : InstRW<[M1WriteB1], (instrs Bcc)>; @@ -439,8 +428,11 @@ def : InstRW<[M1WriteC1], (instregex "^CBN?Z[WX]")>; def : InstRW<[M1WriteAD], (instregex "^TBN?Z[WX]")>; // Arithmetic and logical integer instructions. -def : InstRW<[M1WriteA1], (instrs COPY)>; -def : InstRW<[M1WriteAX], (instregex ".+r[sx](64)?$")>; +def : InstRW<[M1WriteAX], (instregex ".+rx(64)?$")>; +def : InstRW<[M1WriteAX], (instregex ".+rs$")>; + +// Move instructions. +def : InstRW<[M1WriteCOPY], (instrs COPY)>; // Divide and multiply instructions. @@ -450,10 +442,20 @@ def : InstRW<[M1WriteAX], (instregex ".+r[sx](64)?$")>; def : InstRW<[M1WriteLB, WriteLDHi, WriteAdr], (instregex "^LDP(SW|W|X)(post|pre)")>; -def : InstRW<[M1WriteLX, - ReadAdrBase], (instregex "^PRFMro[WX]")>; +def : InstRW<[M1WriteLC, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roW")>; +def : InstRW<[M1WriteL5, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roX")>; +def : InstRW<[M1WriteLC, + ReadAdrBase], (instrs PRFMroW)>; +def : InstRW<[M1WriteL5, + ReadAdrBase], (instrs PRFMroX)>; // Store instructions. +def : InstRW<[M1WriteSC, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roW")>; +def : InstRW<[WriteST, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roX")>; // FP data instructions. def : InstRW<[M1WriteNALU1], (instregex "^F(ABS|NEG)[DS]r")>; @@ -487,8 +489,10 @@ def : InstRW<[WriteVLD], (instregex "^LDUR[BDHSQ]i")>; def : InstRW<[WriteVLD, WriteAdr], (instregex "^LDR[BDHSQ](post|pre)")>; def : InstRW<[WriteVLD], (instregex "^LDR[BDHSQ]ui")>; -def : InstRW<[M1WriteLY, - ReadAdrBase], (instregex "^LDR[BDHS]ro[WX]")>; +def : InstRW<[M1WriteLD, + ReadAdrBase], (instregex "^LDR[BDHS]roW")>; +def : InstRW<[WriteVLD, + ReadAdrBase], (instregex "^LDR[BDHS]roX")>; def : InstRW<[M1WriteLD, ReadAdrBase], (instregex "^LDRQro[WX]")>; def : InstRW<[WriteVLD, @@ -507,14 +511,16 @@ def : InstRW<[WriteVST], (instregex "^STUR[BDHSQ]i")>; def : InstRW<[WriteVST, WriteAdr], (instregex "^STR[BDHSQ](post|pre)")>; def : InstRW<[WriteVST], (instregex "^STR[BDHSQ]ui")>; -def : InstRW<[M1WriteSY, - ReadAdrBase], (instregex "^STR[BDHS]ro[WX]")>; -def : InstRW<[M1WriteSB, +def : InstRW<[M1WriteSA, + ReadAdrBase], (instregex "^STR[BDHS]roW")>; +def : InstRW<[WriteVST, + ReadAdrBase], (instregex "^STR[BDHS]roX")>; +def : InstRW<[M1WriteSA, ReadAdrBase], (instregex "^STRQro[WX]")>; def : InstRW<[WriteVST], (instregex "^STN?P[DSQ]i")>; def : InstRW<[WriteVST, WriteAdr], (instregex "^STP[DS](post|pre)")>; -def : InstRW<[M1WriteSC, +def : InstRW<[M1WriteSB, WriteAdr], (instregex "^STPQ(post|pre)")>; // ASIMD instructions. @@ -608,21 +614,21 @@ def : InstRW<[M1WriteVLDE], (instregex "LD1i(64)$")>; def : InstRW<[M1WriteVLDE, WriteAdr], (instregex "LD1i(64)_POST$")>; -def : InstRW<[M1WriteL5], (instregex "LD1Rv(8b|4h|2s)$")>; -def : InstRW<[M1WriteL5, +def : InstRW<[WriteVLD], (instregex "LD1Rv(8b|4h|2s)$")>; +def : InstRW<[WriteVLD, WriteAdr], (instregex "LD1Rv(8b|4h|2s)_POST$")>; -def : InstRW<[M1WriteL5], (instregex "LD1Rv(1d)$")>; -def : InstRW<[M1WriteL5, +def : InstRW<[WriteVLD], (instregex "LD1Rv(1d)$")>; +def : InstRW<[WriteVLD, WriteAdr], (instregex "LD1Rv(1d)_POST$")>; -def : InstRW<[M1WriteL5], (instregex "LD1Rv(16b|8h|4s|2d)$")>; -def : InstRW<[M1WriteL5, +def : InstRW<[WriteVLD], (instregex "LD1Rv(16b|8h|4s|2d)$")>; +def : InstRW<[WriteVLD, WriteAdr], (instregex "LD1Rv(16b|8h|4s|2d)_POST$")>; -def : InstRW<[M1WriteL5], (instregex "LD1Onev(8b|4h|2s|1d)$")>; -def : InstRW<[M1WriteL5, +def : InstRW<[WriteVLD], (instregex "LD1Onev(8b|4h|2s|1d)$")>; +def : InstRW<[WriteVLD, WriteAdr], (instregex "LD1Onev(8b|4h|2s|1d)_POST$")>; -def : InstRW<[M1WriteL5], (instregex "LD1Onev(16b|8h|4s|2d)$")>; -def : InstRW<[M1WriteL5, +def : InstRW<[WriteVLD], (instregex "LD1Onev(16b|8h|4s|2d)$")>; +def : InstRW<[WriteVLD, WriteAdr], (instregex "LD1Onev(16b|8h|4s|2d)_POST$")>; def : InstRW<[M1WriteVLDA], (instregex "LD1Twov(8b|4h|2s|1d)$")>; def : InstRW<[M1WriteVLDA, @@ -830,8 +836,6 @@ def : InstRW<[M1WriteVSTI, WriteAdr], (instregex "ST4Fourv(2d)_POST$")>; // Cryptography instructions. -def M1WriteAES : SchedWriteRes<[M1UnitNCRYPT]> { let Latency = 1; } -def M1ReadAES : SchedReadAdvance<1, [M1WriteAES]>; def : InstRW<[M1WriteAES], (instregex "^AES[DE]")>; def : InstRW<[M1WriteAES, M1ReadAES], (instregex "^AESI?MC")>; diff --git a/lib/Target/AArch64/AArch64SchedExynosM3.td b/lib/Target/AArch64/AArch64SchedExynosM3.td index 5e5369a5a7fe..15935088a17e 100644 --- a/lib/Target/AArch64/AArch64SchedExynosM3.td +++ b/lib/Target/AArch64/AArch64SchedExynosM3.td @@ -26,9 +26,6 @@ def ExynosM3Model : SchedMachineModel { let CompleteModel = 1; // Use the default model otherwise. list<Predicate> UnsupportedFeatures = [HasSVE]; - - // FIXME: Remove when all errors have been fixed. - let FullInstRWOverlapCheck = 0; } //===----------------------------------------------------------------------===// @@ -107,23 +104,12 @@ def M3UnitNSHF : ProcResGroup<[M3UnitNSHF0, M3UnitNSHF2]>; //===----------------------------------------------------------------------===// -// Predicates. - -def M3BranchLinkFastPred : SchedPredicate<[{MI->getOpcode() == AArch64::BLR && - MI->getOperand(0).isReg() && - MI->getOperand(0).getReg() != AArch64::LR}]>; -def M3ResetFastPred : SchedPredicate<[{TII->isExynosResetFast(*MI)}]>; -def M3RotateRightFastPred : SchedPredicate<[{(MI->getOpcode() == AArch64::EXTRWrri || - MI->getOpcode() == AArch64::EXTRXrri) && - MI->getOperand(1).isReg() && MI->getOperand(2).isReg() && - MI->getOperand(1).getReg() == MI->getOperand(2).getReg()}]>; -def M3ShiftLeftFastPred : SchedPredicate<[{TII->isExynosShiftLeftFast(*MI)}]>; - -//===----------------------------------------------------------------------===// // Coarse scheduling model. def M3WriteZ0 : SchedWriteRes<[]> { let Latency = 0; let NumMicroOps = 1; } +def M3WriteZ1 : SchedWriteRes<[]> { let Latency = 1; + let NumMicroOps = 0; } def M3WriteA1 : SchedWriteRes<[M3UnitALU]> { let Latency = 1; } def M3WriteAA : SchedWriteRes<[M3UnitALU]> { let Latency = 2; @@ -140,15 +126,23 @@ def M3WriteAD : SchedWriteRes<[M3UnitALU, let NumMicroOps = 2; } def M3WriteC1 : SchedWriteRes<[M3UnitC]> { let Latency = 1; } def M3WriteC2 : SchedWriteRes<[M3UnitC]> { let Latency = 2; } -def M3WriteAX : SchedWriteVariant<[SchedVar<M3ResetFastPred, [M3WriteZ0]>, - SchedVar<M3ShiftLeftFastPred, [M3WriteA1]>, - SchedVar<NoSchedPred, [M3WriteAA]>]>; -def M3WriteAY : SchedWriteVariant<[SchedVar<M3RotateRightFastPred, [M3WriteA1]>, - SchedVar<NoSchedPred, [M3WriteAA]>]>; +def M3WriteAU : SchedWriteVariant<[SchedVar<IsCopyIdiomPred, [M3WriteZ0]>, + SchedVar<ExynosArithPred, [M3WriteA1]>, + SchedVar<ExynosLogicPred, [M3WriteA1]>, + SchedVar<NoSchedPred, [M3WriteAA]>]>; +def M3WriteAV : SchedWriteVariant<[SchedVar<IsCopyIdiomPred, [M3WriteZ0]>, + SchedVar<NoSchedPred, [M3WriteAA]>]>; +def M3WriteAW : SchedWriteVariant<[SchedVar<IsZeroIdiomPred, [M3WriteZ0]>, + SchedVar<NoSchedPred, [M3WriteAA]>]>; +def M3WriteAX : SchedWriteVariant<[SchedVar<ExynosArithPred, [M3WriteA1]>, + SchedVar<ExynosLogicPred, [M3WriteA1]>, + SchedVar<NoSchedPred, [M3WriteAA]>]>; +def M3WriteAY : SchedWriteVariant<[SchedVar<ExynosRotateRightImmPred, [M3WriteA1]>, + SchedVar<NoSchedPred, [M3WriteAA]>]>; def M3WriteB1 : SchedWriteRes<[M3UnitB]> { let Latency = 1; } -def M3WriteBX : SchedWriteVariant<[SchedVar<M3BranchLinkFastPred, [M3WriteAB]>, - SchedVar<NoSchedPred, [M3WriteAC]>]>; +def M3WriteBX : SchedWriteVariant<[SchedVar<ExynosBranchLinkLRPred, [M3WriteAC]>, + SchedVar<NoSchedPred, [M3WriteAB]>]>; def M3WriteL4 : SchedWriteRes<[M3UnitL]> { let Latency = 4; } def M3WriteL5 : SchedWriteRes<[M3UnitL]> { let Latency = 5; } @@ -165,44 +159,38 @@ def M3WriteLC : SchedWriteRes<[M3UnitA, def M3WriteLD : SchedWriteRes<[M3UnitA, M3UnitL]> { let Latency = 4; let NumMicroOps = 2; } +def M3WriteLE : SchedWriteRes<[M3UnitA, + M3UnitL]> { let Latency = 6; + let NumMicroOps = 2; } def M3WriteLH : SchedWriteRes<[]> { let Latency = 5; let NumMicroOps = 0; } - -def M3WriteLX : SchedWriteVariant<[SchedVar<M3ShiftLeftFastPred, [M3WriteL5]>, - SchedVar<NoSchedPred, [M3WriteLB]>]>; +def M3WriteLX : SchedWriteVariant<[SchedVar<ExynosScaledIdxPred, [M3WriteL5]>, + SchedVar<NoSchedPred, [M3WriteL4]>]>; def M3WriteS1 : SchedWriteRes<[M3UnitS]> { let Latency = 1; } def M3WriteSA : SchedWriteRes<[M3UnitA, M3UnitS, - M3UnitFST]> { let Latency = 2; + M3UnitFST]> { let Latency = 3; let NumMicroOps = 2; } def M3WriteSB : SchedWriteRes<[M3UnitA, - M3UnitS]> { let Latency = 1; - let NumMicroOps = 2; } -def M3WriteSC : SchedWriteRes<[M3UnitA, M3UnitS]> { let Latency = 2; let NumMicroOps = 2; } -def M3WriteSX : SchedWriteVariant<[SchedVar<M3ShiftLeftFastPred, [M3WriteS1]>, - SchedVar<NoSchedPred, [M3WriteSB]>]>; -def M3WriteSY : SchedWriteVariant<[SchedVar<M3ShiftLeftFastPred, [M3WriteS1]>, - SchedVar<NoSchedPred, [M3WriteSC]>]>; - -def M3ReadAdrBase : SchedReadVariant<[SchedVar<ScaledIdxPred, [ReadDefault]>, - SchedVar<NoSchedPred, [ReadDefault]>]>; +def M3ReadAdrBase : SchedReadVariant<[SchedVar<ExynosScaledIdxPred, [ReadDefault]>, + SchedVar<NoSchedPred, [ReadDefault]>]>; // Branch instructions. def : SchedAlias<WriteBr, M3WriteZ0>; -def : WriteRes<WriteBrReg, [M3UnitC]> { let Latency = 1; } +def : SchedAlias<WriteBrReg, M3WriteC1>; // Arithmetic and logical integer instructions. -def : WriteRes<WriteI, [M3UnitALU]> { let Latency = 1; } -def : WriteRes<WriteISReg, [M3UnitALU]> { let Latency = 1; } -def : WriteRes<WriteIEReg, [M3UnitALU]> { let Latency = 1; } -def : WriteRes<WriteIS, [M3UnitALU]> { let Latency = 1; } +def : SchedAlias<WriteI, M3WriteA1>; +def : SchedAlias<WriteISReg, M3WriteA1>; +def : SchedAlias<WriteIEReg, M3WriteA1>; +def : SchedAlias<WriteIS, M3WriteA1>; // Move instructions. -def : WriteRes<WriteImm, [M3UnitALU]> { let Latency = 1; } +def : SchedAlias<WriteImm, M3WriteA1>; // Divide and multiply instructions. def : WriteRes<WriteID32, [M3UnitC, @@ -216,26 +204,23 @@ def : WriteRes<WriteIM64, [M3UnitC]> { let Latency = 4; let ResourceCycles = [2]; } // Miscellaneous instructions. -def : WriteRes<WriteExtr, [M3UnitALU, - M3UnitALU]> { let Latency = 1; - let NumMicroOps = 2; } +def : SchedAlias<WriteExtr, M3WriteAY>; // Addressing modes. -def : WriteRes<WriteAdr, []> { let Latency = 1; - let NumMicroOps = 0; } +def : SchedAlias<WriteAdr, M3WriteZ1>; def : SchedAlias<ReadAdrBase, M3ReadAdrBase>; // Load instructions. def : SchedAlias<WriteLD, M3WriteL4>; def : WriteRes<WriteLDHi, []> { let Latency = 4; let NumMicroOps = 0; } -def : SchedAlias<WriteLDIdx, M3WriteLX>; +def : SchedAlias<WriteLDIdx, M3WriteLB>; // Store instructions. def : SchedAlias<WriteST, M3WriteS1>; def : SchedAlias<WriteSTP, M3WriteS1>; def : SchedAlias<WriteSTX, M3WriteS1>; -def : SchedAlias<WriteSTIdx, M3WriteSX>; +def : SchedAlias<WriteSTIdx, M3WriteSB>; // FP data instructions. def : WriteRes<WriteF, [M3UnitFADD]> { let Latency = 2; } @@ -245,7 +230,6 @@ def : WriteRes<WriteFDiv, [M3UnitFDIV]> { let Latency = 12; def : WriteRes<WriteFMul, [M3UnitFMAC]> { let Latency = 4; } // FP miscellaneous instructions. -// TODO: Conversion between register files is much different. def : WriteRes<WriteFCvt, [M3UnitFCVT]> { let Latency = 3; } def : WriteRes<WriteFImm, [M3UnitNALU]> { let Latency = 1; } def : WriteRes<WriteFCopy, [M3UnitNALU]> { let Latency = 1; } @@ -481,11 +465,15 @@ def M3WriteVSTI : SchedWriteRes<[M3UnitNALU, // Special cases. def M3WriteAES : SchedWriteRes<[M3UnitNCRY]> { let Latency = 1; } +def M3WriteCOPY : SchedWriteVariant<[SchedVar<ExynosFPPred, [M3WriteNALU1]>, + SchedVar<NoSchedPred, [M3WriteZ0]>]>; +def M3WriteMOVI : SchedWriteVariant<[SchedVar<IsZeroFPIdiomPred, [M3WriteZ0]>, + SchedVar<NoSchedPred, [M3WriteNALU1]>]>; + +// Fast forwarding. def M3ReadAES : SchedReadAdvance<1, [M3WriteAES]>; def M3ReadFMAC : SchedReadAdvance<1, [M3WriteFMAC4, M3WriteFMAC5]>; -def M3WriteMOVI : SchedWriteVariant<[SchedVar<M3ResetFastPred, [M3WriteZ0]>, - SchedVar<NoSchedPred, [M3WriteNALU1]>]>; def M3ReadNMUL : SchedReadAdvance<1, [M3WriteNMUL3]>; // Branch instructions @@ -496,29 +484,40 @@ def : InstRW<[M3WriteC1], (instregex "^CBN?Z[WX]")>; def : InstRW<[M3WriteAD], (instregex "^TBN?Z[WX]")>; // Arithmetic and logical integer instructions. -def : InstRW<[M3WriteA1], (instrs COPY)>; -def : InstRW<[M3WriteAX], (instregex "^(ADD|SUB)S?Xrx64")>; -def : InstRW<[M3WriteAX], (instregex "^(ADD|AND|BIC|EON|EOR|ORN|ORR|SUB)[WX]r[sx]$")>; -def : InstRW<[M3WriteAX], (instregex "^(ADD|BIC|SUB)S[WX]r[sx]$")>; -def : InstRW<[M3WriteAX], (instregex "^(ADD|AND|EOR|ORR|SUB)[WX]ri")>; +def : InstRW<[M3WriteAX], (instregex "^(ADD|AND|BIC|EON|EOR|ORN|SUB)[WX]rs$")>; +def : InstRW<[M3WriteAU], (instrs ORRWrs, ORRXrs)>; +def : InstRW<[M3WriteAX], (instregex "^(ADD|SUB)S?[WX]rx(64)?$")>; +def : InstRW<[M3WriteAX], (instregex "^(ADD|AND|BIC|SUB)S[WX]rs$")>; +def : InstRW<[M3WriteAV], (instrs ADDWri, ADDXri)>; +def : InstRW<[M3WriteAW], (instrs ORRWri, ORRXri)>; // Move instructions. -def : InstRW<[M3WriteZ0], (instrs ADR, ADRP)>; -def : InstRW<[M3WriteZ0], (instregex "^MOV[NZ][WX]i")>; +def : InstRW<[M3WriteCOPY], (instrs COPY)>; +def : InstRW<[M3WriteZ0], (instrs ADR, ADRP)>; +def : InstRW<[M3WriteZ0], (instregex "^MOV[NZ][WX]i")>; // Divide and multiply instructions. // Miscellaneous instructions. -def : InstRW<[M3WriteAY], (instrs EXTRWrri, EXTRXrri)>; // Load instructions. def : InstRW<[M3WriteLD, WriteLDHi, WriteAdr], (instregex "^LDP(SW|W|X)(post|pre)")>; +def : InstRW<[M3WriteLB, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roW")>; +def : InstRW<[M3WriteLX, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roX")>; +def : InstRW<[M3WriteLB, + ReadAdrBase], (instrs PRFMroW)>; def : InstRW<[M3WriteLX, - ReadAdrBase], (instregex "^PRFMro[WX]")>; + ReadAdrBase], (instrs PRFMroX)>; // Store instructions. +def : InstRW<[M3WriteSB, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roW")>; +def : InstRW<[WriteST, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roX")>; // FP data instructions. def : InstRW<[M3WriteNSHF1], (instregex "^FABS[DS]r")>; @@ -555,9 +554,11 @@ def : InstRW<[WriteVLD], (instregex "^LDUR[BDHSQ]i")>; def : InstRW<[WriteVLD, WriteAdr], (instregex "^LDR[BDHSQ](post|pre)")>; def : InstRW<[WriteVLD], (instregex "^LDR[BDHSQ]ui")>; -def : InstRW<[M3WriteLX, - ReadAdrBase], (instregex "^LDR[BDHS]ro[WX]")>; -def : InstRW<[M3WriteLB, +def : InstRW<[M3WriteLE, + ReadAdrBase], (instregex "^LDR[BDHS]roW")>; +def : InstRW<[WriteVLD, + ReadAdrBase], (instregex "^LDR[BDHS]roX")>; +def : InstRW<[M3WriteLE, ReadAdrBase], (instregex "^LDRQro[WX]")>; def : InstRW<[WriteVLD, M3WriteLH], (instregex "^LDN?P[DS]i")>; @@ -575,8 +576,10 @@ def : InstRW<[WriteVST], (instregex "^STUR[BDHSQ]i")>; def : InstRW<[WriteVST, WriteAdr], (instregex "^STR[BDHSQ](post|pre)")>; def : InstRW<[WriteVST], (instregex "^STR[BDHSQ]ui")>; -def : InstRW<[M3WriteSY, - ReadAdrBase], (instregex "^STR[BDHS]ro[WX]")>; +def : InstRW<[M3WriteSA, + ReadAdrBase], (instregex "^STR[BDHS]roW")>; +def : InstRW<[WriteVST, + ReadAdrBase], (instregex "^STR[BDHS]roX")>; def : InstRW<[M3WriteSA, ReadAdrBase], (instregex "^STRQro[WX]")>; def : InstRW<[WriteVST], (instregex "^STN?P[DSQ]i")>; @@ -588,7 +591,7 @@ def : InstRW<[M3WriteSA, // ASIMD instructions. def : InstRW<[M3WriteNMSC3], (instregex "^[SU]ABAL?v")>; def : InstRW<[M3WriteNMSC1], (instregex "^[SU]ABDL?v")>; -def : InstRW<[M3WriteNMSC1], (instregex "^(SQ)?(ABS|NEG)v")>; +def : InstRW<[M3WriteNMSC1], (instregex "^((SQ)?ABS|SQNEG)v")>; def : InstRW<[M3WriteNALU1], (instregex "^(ADD|NEG|SUB)v")>; def : InstRW<[M3WriteNMSC3], (instregex "^[SU]?ADDL?Pv")>; def : InstRW<[M3WriteNMSC3], (instregex "^[SU]H(ADD|SUB)v")>; @@ -597,7 +600,6 @@ def : InstRW<[M3WriteNMSC3], (instregex "^R?(ADD|SUB)HN2?v")>; def : InstRW<[M3WriteNMSC3], (instregex "^[SU]Q(ADD|SUB)v")>; def : InstRW<[M3WriteNMSC3], (instregex "^(SU|US)QADDv")>; def : InstRW<[M3WriteNMSC3], (instregex "^[SU]RHADDv")>; -def : InstRW<[M3WriteNMSC3], (instregex "^[SU]?ADDL?Vv")>; def : InstRW<[M3WriteNMSC1], (instregex "^CM(EQ|GE|GT|HI|HS|LE|LT)v")>; def : InstRW<[M3WriteNALU1], (instregex "^CMTSTv")>; def : InstRW<[M3WriteNALU1], (instregex "^(AND|BIC|EOR|MVNI|NOT|ORN|ORR)v")>; @@ -668,108 +670,108 @@ def : InstRW<[M3WriteNSHF1], (instregex "^(TRN|UZP|ZIP)[12]v")>; // ASIMD load instructions. def : InstRW<[M3WriteL5], (instregex "LD1Onev(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteL5, - WriteAdr], (instregex "LD1Onev(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD1Onev(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteL5], (instregex "LD1Onev(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteL5, - WriteAdr], (instregex "LD1Onev(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD1Onev(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDA], (instregex "LD1Twov(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDA, - WriteAdr], (instregex "LD1Twov(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD1Twov(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDA], (instregex "LD1Twov(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDA, - WriteAdr], (instregex "LD1Twov(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD1Twov(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDB], (instregex "LD1Threev(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDB, - WriteAdr], (instregex "LD1Threev(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD1Threev(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDB], (instregex "LD1Threev(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDB, - WriteAdr], (instregex "LD1Threev(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD1Threev(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDC], (instregex "LD1Fourv(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDC, - WriteAdr], (instregex "LD1Fourv(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD1Fourv(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDC], (instregex "LD1Fourv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDC, - WriteAdr], (instregex "LD1Fourv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD1Fourv(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDD], (instregex "LD1i(8|16|32)$")>; def : InstRW<[M3WriteVLDD, - WriteAdr], (instregex "LD1i(8|16|32)_POST")>; + M3WriteA1], (instregex "LD1i(8|16|32)_POST")>; def : InstRW<[M3WriteVLDE], (instregex "LD1i(64)$")>; def : InstRW<[M3WriteVLDE, - WriteAdr], (instregex "LD1i(64)_POST")>; + M3WriteA1], (instregex "LD1i(64)_POST")>; def : InstRW<[M3WriteL5], (instregex "LD1Rv(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteL5, - WriteAdr], (instregex "LD1Rv(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD1Rv(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteL5], (instregex "LD1Rv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteL5, - WriteAdr], (instregex "LD1Rv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD1Rv(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDF], (instregex "LD2Twov(8b|4h|2s)$")>; def : InstRW<[M3WriteVLDF, - WriteAdr], (instregex "LD2Twov(8b|4h|2s)_POST")>; + M3WriteA1], (instregex "LD2Twov(8b|4h|2s)_POST")>; def : InstRW<[M3WriteVLDF], (instregex "LD2Twov(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDF, - WriteAdr], (instregex "LD2Twov(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD2Twov(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDG], (instregex "LD2i(8|16|32)$")>; def : InstRW<[M3WriteVLDG, - WriteAdr], (instregex "LD2i(8|16|32)_POST")>; + M3WriteA1], (instregex "LD2i(8|16|32)_POST")>; def : InstRW<[M3WriteVLDH], (instregex "LD2i(64)$")>; def : InstRW<[M3WriteVLDH, - WriteAdr], (instregex "LD2i(64)_POST")>; + M3WriteA1], (instregex "LD2i(64)_POST")>; def : InstRW<[M3WriteVLDA], (instregex "LD2Rv(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDA, - WriteAdr], (instregex "LD2Rv(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD2Rv(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDA], (instregex "LD2Rv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDA, - WriteAdr], (instregex "LD2Rv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD2Rv(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDI], (instregex "LD3Threev(8b|4h|2s)$")>; def : InstRW<[M3WriteVLDI, - WriteAdr], (instregex "LD3Threev(8b|4h|2s)_POST")>; + M3WriteA1], (instregex "LD3Threev(8b|4h|2s)_POST")>; def : InstRW<[M3WriteVLDI], (instregex "LD3Threev(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDI, - WriteAdr], (instregex "LD3Threev(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD3Threev(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDJ], (instregex "LD3i(8|16|32)$")>; def : InstRW<[M3WriteVLDJ, - WriteAdr], (instregex "LD3i(8|16|32)_POST")>; + M3WriteA1], (instregex "LD3i(8|16|32)_POST")>; def : InstRW<[M3WriteVLDL], (instregex "LD3i(64)$")>; def : InstRW<[M3WriteVLDL, - WriteAdr], (instregex "LD3i(64)_POST")>; + M3WriteA1], (instregex "LD3i(64)_POST")>; def : InstRW<[M3WriteVLDB], (instregex "LD3Rv(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDB, - WriteAdr], (instregex "LD3Rv(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD3Rv(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDB], (instregex "LD3Rv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDB, - WriteAdr], (instregex "LD3Rv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD3Rv(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDN], (instregex "LD4Fourv(8b|4h|2s)$")>; def : InstRW<[M3WriteVLDN, - WriteAdr], (instregex "LD4Fourv(8b|4h|2s)_POST")>; + M3WriteA1], (instregex "LD4Fourv(8b|4h|2s)_POST")>; def : InstRW<[M3WriteVLDN], (instregex "LD4Fourv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDN, - WriteAdr], (instregex "LD4Fourv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD4Fourv(16b|8h|4s|2d)_POST")>; def : InstRW<[M3WriteVLDK], (instregex "LD4i(8|16|32)$")>; def : InstRW<[M3WriteVLDK, - WriteAdr], (instregex "LD4i(8|16|32)_POST")>; + M3WriteA1], (instregex "LD4i(8|16|32)_POST")>; def : InstRW<[M3WriteVLDM], (instregex "LD4i(64)$")>; def : InstRW<[M3WriteVLDM, - WriteAdr], (instregex "LD4i(64)_POST")>; + M3WriteA1], (instregex "LD4i(64)_POST")>; def : InstRW<[M3WriteVLDC], (instregex "LD4Rv(8b|4h|2s|1d)$")>; def : InstRW<[M3WriteVLDC, - WriteAdr], (instregex "LD4Rv(8b|4h|2s|1d)_POST")>; + M3WriteA1], (instregex "LD4Rv(8b|4h|2s|1d)_POST")>; def : InstRW<[M3WriteVLDC], (instregex "LD4Rv(16b|8h|4s|2d)$")>; def : InstRW<[M3WriteVLDC, - WriteAdr], (instregex "LD4Rv(16b|8h|4s|2d)_POST")>; + M3WriteA1], (instregex "LD4Rv(16b|8h|4s|2d)_POST")>; // ASIMD store instructions. def : InstRW<[WriteVST], (instregex "ST1Onev(8b|4h|2s|1d)$")>; diff --git a/lib/Target/AArch64/AArch64SchedExynosM4.td b/lib/Target/AArch64/AArch64SchedExynosM4.td new file mode 100644 index 000000000000..4d892465b3f2 --- /dev/null +++ b/lib/Target/AArch64/AArch64SchedExynosM4.td @@ -0,0 +1,1004 @@ +//=- AArch64SchedExynosM4.td - Samsung Exynos M4 Sched Defs --*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the machine model for the Samsung Exynos M4 to support +// instruction scheduling and other instruction cost heuristics. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// The Exynos-M4 is an advanced superscalar microprocessor with a 6-wide +// in-order stage for decode and dispatch and a wider issue stage. +// The execution units and loads and stores are out-of-order. + +def ExynosM4Model : SchedMachineModel { + let IssueWidth = 6; // Up to 6 uops per cycle. + let MicroOpBufferSize = 228; // ROB size. + let LoopMicroOpBufferSize = 48; // Based on the instruction queue size. + let LoadLatency = 4; // Optimistic load cases. + let MispredictPenalty = 16; // Minimum branch misprediction penalty. + let CompleteModel = 1; // Use the default model otherwise. + + list<Predicate> UnsupportedFeatures = [HasSVE]; +} + +//===----------------------------------------------------------------------===// +// Define each kind of processor resource and number available on the Exynos-M4. + +let SchedModel = ExynosM4Model in { + +def M4UnitA : ProcResource<2>; // Simple integer +def M4UnitC : ProcResource<2>; // Simple and complex integer +let Super = M4UnitC, BufferSize = 1 in +def M4UnitD : ProcResource<1>; // Integer division (inside C0, serialized) +let Super = M4UnitC in +def M4UnitE : ProcResource<1>; // CRC (inside C0) +def M4UnitB : ProcResource<2>; // Branch +def M4UnitL0 : ProcResource<1>; // Load +def M4UnitS0 : ProcResource<1>; // Store +def M4PipeLS : ProcResource<1>; // Load/Store +let Super = M4PipeLS in { + def M4UnitL1 : ProcResource<1>; + def M4UnitS1 : ProcResource<1>; +} +def M4PipeF0 : ProcResource<1>; // FP #0 +let Super = M4PipeF0 in { + def M4UnitFMAC0 : ProcResource<1>; // FP multiplication + def M4UnitFADD0 : ProcResource<1>; // Simple FP + def M4UnitFCVT0 : ProcResource<1>; // FP conversion + def M4UnitNALU0 : ProcResource<1>; // Simple vector + def M4UnitNHAD : ProcResource<1>; // Horizontal vector + def M4UnitNMSC : ProcResource<1>; // FP and vector miscellanea + def M4UnitNMUL0 : ProcResource<1>; // Vector multiplication + def M4UnitNSHT0 : ProcResource<1>; // Vector shifting + def M4UnitNSHF0 : ProcResource<1>; // Vector shuffling + def M4UnitNCRY0 : ProcResource<1>; // Cryptographic +} +def M4PipeF1 : ProcResource<1>; // FP #1 +let Super = M4PipeF1 in { + def M4UnitFMAC1 : ProcResource<1>; // FP multiplication + def M4UnitFADD1 : ProcResource<1>; // Simple FP + def M4UnitFDIV0 : ProcResource<2>; // FP division (serialized) + def M4UnitFSQR0 : ProcResource<2>; // FP square root (serialized) + def M4UnitFST0 : ProcResource<1>; // FP store + def M4UnitNALU1 : ProcResource<1>; // Simple vector + def M4UnitNSHT1 : ProcResource<1>; // Vector shifting + def M4UnitNSHF1 : ProcResource<1>; // Vector shuffling +} +def M4PipeF2 : ProcResource<1>; // FP #2 +let Super = M4PipeF2 in { + def M4UnitFMAC2 : ProcResource<1>; // FP multiplication + def M4UnitFADD2 : ProcResource<1>; // Simple FP + def M4UnitFCVT1 : ProcResource<1>; // FP conversion + def M4UnitFDIV1 : ProcResource<2>; // FP division (serialized) + def M4UnitFSQR1 : ProcResource<2>; // FP square root (serialized) + def M4UnitFST1 : ProcResource<1>; // FP store + def M4UnitNALU2 : ProcResource<1>; // Simple vector + def M4UnitNMUL1 : ProcResource<1>; // Vector multiplication + def M4UnitNSHT2 : ProcResource<1>; // Vector shifting + def M4UnitNCRY1 : ProcResource<1>; // Cryptographic +} + +def M4UnitALU : ProcResGroup<[M4UnitA, + M4UnitC]>; +def M4UnitL : ProcResGroup<[M4UnitL0, + M4UnitL1]>; +def M4UnitS : ProcResGroup<[M4UnitS0, + M4UnitS1]>; +def M4UnitFMAC : ProcResGroup<[M4UnitFMAC0, + M4UnitFMAC1, + M4UnitFMAC2]>; +def M4UnitFMACH : ProcResGroup<[M4UnitFMAC0, + M4UnitFMAC1]>; +def M4UnitFADD : ProcResGroup<[M4UnitFADD0, + M4UnitFADD1, + M4UnitFADD2]>; +def M4UnitFADDH : ProcResGroup<[M4UnitFADD0, + M4UnitFADD1]>; +def M4UnitFCVT : ProcResGroup<[M4UnitFCVT0, + M4UnitFCVT1]>; +def M4UnitFCVTH : ProcResGroup<[M4UnitFCVT0]>; +def M4UnitFDIV : ProcResGroup<[M4UnitFDIV0, + M4UnitFDIV1]>; +def M4UnitFDIVH : ProcResGroup<[M4UnitFDIV0]>; +def M4UnitFSQR : ProcResGroup<[M4UnitFSQR0, + M4UnitFSQR1]>; +def M4UnitFSQRH : ProcResGroup<[M4UnitFSQR0]>; +def M4UnitFST : ProcResGroup<[M4UnitFST0, + M4UnitFST1]>; +def M4UnitNALU : ProcResGroup<[M4UnitNALU0, + M4UnitNALU1, + M4UnitNALU2]>; +def M4UnitNALUH : ProcResGroup<[M4UnitNALU0, + M4UnitNALU1]>; +def M4UnitNMUL : ProcResGroup<[M4UnitNMUL0, + M4UnitNMUL1]>; +def M4UnitNSHT : ProcResGroup<[M4UnitNSHT0, + M4UnitNSHT1, + M4UnitNSHT2]>; +def M4UnitNSHF : ProcResGroup<[M4UnitNSHF0, + M4UnitNSHF1]>; +def M4UnitNSHFH : ProcResGroup<[M4UnitNSHF0]>; +def M4UnitNCRY : ProcResGroup<[M4UnitNCRY0, + M4UnitNCRY1]>; + +//===----------------------------------------------------------------------===// +// Resources details. + +def M4WriteZ0 : SchedWriteRes<[]> { let Latency = 0; } +def M4WriteZ1 : SchedWriteRes<[]> { let Latency = 1; + let NumMicroOps = 0; } +def M4WriteZ4 : SchedWriteRes<[]> { let Latency = 4; + let NumMicroOps = 0; } + +def M4WriteA1 : SchedWriteRes<[M4UnitALU]> { let Latency = 1; } +def M4WriteA2 : SchedWriteRes<[M4UnitALU]> { let Latency = 2; } +def M4WriteAA : SchedWriteRes<[M4UnitALU]> { let Latency = 2; + let ResourceCycles = [2]; } +def M4WriteAB : SchedWriteRes<[M4UnitALU, + M4UnitC]> { let Latency = 2; + let NumMicroOps = 2; } +def M4WriteAC : SchedWriteRes<[M4UnitALU, + M4UnitALU, + M4UnitC]> { let Latency = 3; + let NumMicroOps = 3; } +def M4WriteAD : SchedWriteRes<[M4UnitALU, + M4UnitC]> { let Latency = 2; + let NumMicroOps = 2; } +def M4WriteAF : SchedWriteRes<[M4UnitALU]> { let Latency = 2; + let NumMicroOps = 2; } +def M4WriteAU : SchedWriteVariant<[SchedVar<IsCopyIdiomPred, [M4WriteZ0]>, + SchedVar<ExynosArithPred, [M4WriteA1]>, + SchedVar<ExynosLogicExPred, [M4WriteA1]>, + SchedVar<NoSchedPred, [M4WriteAA]>]>; +def M4WriteAV : SchedWriteVariant<[SchedVar<ExynosResetPred, [M4WriteZ0]>, + SchedVar<NoSchedPred, [M4WriteAA]>]>; +def M4WriteAX : SchedWriteVariant<[SchedVar<ExynosArithPred, [M4WriteA1]>, + SchedVar<ExynosLogicExPred, [M4WriteA1]>, + SchedVar<NoSchedPred, [M4WriteAA]>]>; +def M4WriteAY : SchedWriteVariant<[SchedVar<ExynosRotateRightImmPred, [M4WriteA1]>, + SchedVar<NoSchedPred, [M4WriteAF]>]>; + +def M4WriteB1 : SchedWriteRes<[M4UnitB]> { let Latency = 1; } +def M4WriteBX : SchedWriteVariant<[SchedVar<ExynosBranchLinkLRPred, [M4WriteAC]>, + SchedVar<NoSchedPred, [M4WriteAB]>]>; + +def M4WriteC1 : SchedWriteRes<[M4UnitC]> { let Latency = 1; } +def M4WriteC3 : SchedWriteRes<[M4UnitC]> { let Latency = 3; } +def M4WriteCA : SchedWriteRes<[M4UnitC]> { let Latency = 4; + let ResourceCycles = [2]; } + +def M4WriteD12 : SchedWriteRes<[M4UnitD]> { let Latency = 12; } +def M4WriteD21 : SchedWriteRes<[M4UnitD]> { let Latency = 21; } + +def M4WriteE2 : SchedWriteRes<[M4UnitE]> { let Latency = 2; } + +def M4WriteL4 : SchedWriteRes<[M4UnitL]> { let Latency = 4; } +def M4WriteL5 : SchedWriteRes<[M4UnitL]> { let Latency = 5; } +def M4WriteLA : SchedWriteRes<[M4UnitL, + M4UnitL]> { let Latency = 5; + let NumMicroOps = 1; } +def M4WriteLB : SchedWriteRes<[M4UnitA, + M4UnitL]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteLC : SchedWriteRes<[M4UnitA, + M4UnitL, + M4UnitL]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteLD : SchedWriteRes<[M4UnitA, + M4UnitL]> { let Latency = 4; + let NumMicroOps = 2; } +def M4WriteLE : SchedWriteRes<[M4UnitA, + M4UnitL]> { let Latency = 6; + let NumMicroOps = 2; } +def M4WriteLH : SchedWriteRes<[]> { let Latency = 5; + let NumMicroOps = 0; } +def M4WriteLX : SchedWriteVariant<[SchedVar<ScaledIdxPred, [M4WriteL5]>, + SchedVar<NoSchedPred, [M4WriteL4]>]>; + +def M4WriteS1 : SchedWriteRes<[M4UnitS]> { let Latency = 1; } +def M4WriteSA : SchedWriteRes<[M4UnitS0]> { let Latency = 3; } +def M4WriteSB : SchedWriteRes<[M4UnitA, + M4UnitS]> { let Latency = 2; + let NumMicroOps = 1; } +def M4WriteSX : SchedWriteVariant<[SchedVar<ExynosScaledIdxPred, [M4WriteSB]>, + SchedVar<NoSchedPred, [M4WriteS1]>]>; + +def M4ReadAdrBase : SchedReadVariant<[SchedVar< + MCSchedPredicate< + CheckAny< + [ScaledIdxFn, + ExynosScaledIdxFn]>>, [ReadDefault]>, + SchedVar<NoSchedPred, [ReadDefault]>]>; + +def M4WriteNEONA : SchedWriteRes<[M4UnitNSHF, + M4UnitFADD]> { let Latency = 3; + let NumMicroOps = 2; } +def M4WriteNEONB : SchedWriteRes<[M4UnitNALU, + M4UnitS0]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteNEOND : SchedWriteRes<[M4UnitNSHF, + M4UnitFST]> { let Latency = 6; + let NumMicroOps = 2; } +def M4WriteNEONH : SchedWriteRes<[M4UnitNALU, + M4UnitS0]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteNEONI : SchedWriteRes<[M4UnitNSHF, + M4UnitS0]> { let Latency = 2; + let NumMicroOps = 2; } +def M4WriteNEONJ : SchedWriteRes<[M4UnitNMSC, + M4UnitS0]> { let Latency = 4; } +def M4WriteNEONK : SchedWriteRes<[M4UnitNSHF, + M4UnitNMSC, + M4UnitS0]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteNEONL : SchedWriteRes<[M4UnitNMUL]> { let Latency = 3; } +def M4WriteNEONM : SchedWriteRes<[M4UnitNMUL]> { let Latency = 3; } +def M4WriteNEONN : SchedWriteRes<[M4UnitNMSC, + M4UnitNMSC]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteNEONO : SchedWriteRes<[M4UnitNMSC, + M4UnitNMSC, + M4UnitNMSC]> { let Latency = 8; + let NumMicroOps = 3; } +def M4WriteNEONP : SchedWriteRes<[M4UnitNSHF, + M4UnitNMSC]> { let Latency = 4; + let NumMicroOps = 2; } +def M4WriteNEONQ : SchedWriteRes<[M4UnitNMSC, + M4UnitC]> { let Latency = 3; + let NumMicroOps = 1; } +def M4WriteNEONR : SchedWriteRes<[M4UnitFCVT0, + M4UnitS0]> { let Latency = 4; + let NumMicroOps = 1; } +def M4WriteNEONV : SchedWriteRes<[M4UnitFDIV, + M4UnitFDIV]> { let Latency = 7; + let ResourceCycles = [6, 6]; } +def M4WriteNEONVH : SchedWriteRes<[M4UnitFDIVH, + M4UnitFDIVH]> { let Latency = 7; + let ResourceCycles = [6, 6]; } +def M4WriteNEONW : SchedWriteRes<[M4UnitFDIV, + M4UnitFDIV]> { let Latency = 12; + let ResourceCycles = [9, 9]; } +def M4WriteNEONX : SchedWriteRes<[M4UnitFSQR, + M4UnitFSQR]> { let Latency = 8; + let ResourceCycles = [7, 7]; } +def M4WriteNEONXH : SchedWriteRes<[M4UnitFSQRH, + M4UnitFSQRH]> { let Latency = 7; + let ResourceCycles = [6, 6]; } +def M4WriteNEONY : SchedWriteRes<[M4UnitFSQR, + M4UnitFSQR]> { let Latency = 12; + let ResourceCycles = [9, 9]; } +def M4WriteNEONZ : SchedWriteVariant<[SchedVar<ExynosQFormPred, [M4WriteNEONO]>, + SchedVar<NoSchedPred, [M4WriteNEONN]>]>; + +def M4WriteFADD2 : SchedWriteRes<[M4UnitFADD]> { let Latency = 2; } +def M4WriteFADD2H : SchedWriteRes<[M4UnitFADDH]> { let Latency = 2; } + +def M4WriteFCVT2 : SchedWriteRes<[M4UnitFCVT]> { let Latency = 2; } +def M4WriteFCVT2A : SchedWriteRes<[M4UnitFCVT0]> { let Latency = 2; } +def M4WriteFCVT2H : SchedWriteRes<[M4UnitFCVTH]> { let Latency = 2; } +def M4WriteFCVT3 : SchedWriteRes<[M4UnitFCVT]> { let Latency = 3; } +def M4WriteFCVT3A : SchedWriteRes<[M4UnitFCVT0]> { let Latency = 3; } +def M4WriteFCVT3H : SchedWriteRes<[M4UnitFCVTH]> { let Latency = 3; } +def M4WriteFCVT4 : SchedWriteRes<[M4UnitFCVT]> { let Latency = 4; } +def M4WriteFCVT4A : SchedWriteRes<[M4UnitFCVT0]> { let Latency = 4; } +def M4WriteFCVT6A : SchedWriteRes<[M4UnitFCVT0]> { let Latency = 6; } + +def M4WriteFDIV7 : SchedWriteRes<[M4UnitFDIV]> { let Latency = 7; + let ResourceCycles = [6]; } +def M4WriteFDIV7H : SchedWriteRes<[M4UnitFDIVH]> { let Latency = 7; + let ResourceCycles = [6]; } +def M4WriteFDIV12 : SchedWriteRes<[M4UnitFDIV]> { let Latency = 12; + let ResourceCycles = [9]; } + +def M4WriteFMAC2H : SchedWriteRes<[M4UnitFMACH]> { let Latency = 2; } +def M4WriteFMAC3H : SchedWriteRes<[M4UnitFMACH]> { let Latency = 3; } +def M4WriteFMAC3 : SchedWriteRes<[M4UnitFMAC]> { let Latency = 3; } +def M4WriteFMAC4 : SchedWriteRes<[M4UnitFMAC]> { let Latency = 4; } +def M4WriteFMAC4H : SchedWriteRes<[M4UnitFMACH]> { let Latency = 4; } +def M4WriteFMAC5 : SchedWriteRes<[M4UnitFMAC]> { let Latency = 5; } + +def M4WriteFSQR7H : SchedWriteRes<[M4UnitFSQRH]> { let Latency = 7; + let ResourceCycles = [6]; } +def M4WriteFSQR8 : SchedWriteRes<[M4UnitFSQR]> { let Latency = 8; + let ResourceCycles = [7]; } +def M4WriteFSQR12 : SchedWriteRes<[M4UnitFSQR]> { let Latency = 12; + let ResourceCycles = [9]; } + +def M4WriteNALU1 : SchedWriteRes<[M4UnitNALU]> { let Latency = 1; } +def M4WriteNALU1H : SchedWriteRes<[M4UnitNALUH]> { let Latency = 1; } + +def M4WriteNCRY1 : SchedWriteRes<[M4UnitNCRY]> { let Latency = 1; } +def M4WriteNCRY1A : SchedWriteRes<[M4UnitNCRY0]> { let Latency = 1; } +def M4WriteNCRY3A : SchedWriteRes<[M4UnitNCRY0]> { let Latency = 3; } +def M4WriteNCRY5A : SchedWriteRes<[M4UnitNCRY]> { let Latency = 5; } + +def M4WriteNHAD1 : SchedWriteRes<[M4UnitNHAD]> { let Latency = 1; } +def M4WriteNHAD3 : SchedWriteRes<[M4UnitNHAD]> { let Latency = 3; } + +def M4WriteNMSC1 : SchedWriteRes<[M4UnitNMSC]> { let Latency = 1; } +def M4WriteNMSC2 : SchedWriteRes<[M4UnitNMSC]> { let Latency = 2; } +def M4WriteNMSC3 : SchedWriteRes<[M4UnitNMSC]> { let Latency = 3; } + +def M4WriteNMUL3 : SchedWriteRes<[M4UnitNMUL]> { let Latency = 3; } + +def M4WriteNSHF1 : SchedWriteRes<[M4UnitNSHF]> { let Latency = 1; } +def M4WriteNSHF1H : SchedWriteRes<[M4UnitNSHFH]> { let Latency = 1; } +def M4WriteNSHF3 : SchedWriteRes<[M4UnitNSHF]> { let Latency = 3; } +def M4WriteNSHFA : SchedWriteRes<[M4UnitNSHF]> { let Latency = 1; + let ResourceCycles = [2]; } +def M4WriteNSHFB : SchedWriteRes<[M4UnitNSHF]> { let Latency = 2; + let NumMicroOps = 2; + let ResourceCycles = [2]; } +def M4WriteNSHFC : SchedWriteRes<[M4UnitNSHF]> { let Latency = 3; + let NumMicroOps = 3; + let ResourceCycles = [4]; } +def M4WriteNSHFD : SchedWriteRes<[M4UnitNSHF]> { let Latency = 4; + let NumMicroOps = 4; + let ResourceCycles = [4]; } + +def M4WriteNSHT1 : SchedWriteRes<[M4UnitNSHT]> { let Latency = 1; } +def M4WriteNSHT2 : SchedWriteRes<[M4UnitNSHT]> { let Latency = 2; } +def M4WriteNSHT3 : SchedWriteRes<[M4UnitNSHT]> { let Latency = 3; } +def M4WriteNSHT4A : SchedWriteRes<[M4UnitNSHT1]> { let Latency = 4; } + +def M4WriteVLDA : SchedWriteRes<[M4UnitL, + M4UnitL]> { let Latency = 5; + let NumMicroOps = 2; } +def M4WriteVLDB : SchedWriteRes<[M4UnitL, + M4UnitL, + M4UnitL]> { let Latency = 6; + let NumMicroOps = 3; } +def M4WriteVLDC : SchedWriteRes<[M4UnitL, + M4UnitL, + M4UnitL, + M4UnitL]> { let Latency = 6; + let NumMicroOps = 4; } +def M4WriteVLDD : SchedWriteRes<[M4UnitL, + M4UnitNSHF]> { let Latency = 6; + let NumMicroOps = 2; + let ResourceCycles = [2, 1]; } +def M4WriteVLDF : SchedWriteRes<[M4UnitL, + M4UnitL]> { let Latency = 10; + let NumMicroOps = 2; + let ResourceCycles = [3, 3]; } +def M4WriteVLDG : SchedWriteRes<[M4UnitL, + M4UnitNSHF, + M4UnitNSHF]> { let Latency = 6; + let NumMicroOps = 3; + let ResourceCycles = [2, 1, 1]; } +def M4WriteVLDI : SchedWriteRes<[M4UnitL, + M4UnitL, + M4UnitL]> { let Latency = 12; + let NumMicroOps = 3; + let ResourceCycles = [3, 3, 3]; } +def M4WriteVLDJ : SchedWriteRes<[M4UnitL, + M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF]> { let Latency = 7; + let NumMicroOps = 4; + let ResourceCycles = [3, 1, 1, 1]; } +def M4WriteVLDK : SchedWriteRes<[M4UnitL, + M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF]> { let Latency = 7; + let NumMicroOps = 5; + let ResourceCycles = [3, 1, 1, 1, 1]; } +def M4WriteVLDL : SchedWriteRes<[M4UnitL, + M4UnitNSHF, + M4UnitNSHF, + M4UnitL, + M4UnitNSHF]> { let Latency = 7; + let NumMicroOps = 5; + let ResourceCycles = [3, 1, 1, 6, 1]; } +def M4WriteVLDM : SchedWriteRes<[M4UnitL, + M4UnitNSHF, + M4UnitNSHF, + M4UnitL, + M4UnitNSHF, + M4UnitNSHF]> { let Latency = 7; + let NumMicroOps = 6; + let ResourceCycles = [3, 1, 1, 3, 1, 1]; } +def M4WriteVLDN : SchedWriteRes<[M4UnitL, + M4UnitL, + M4UnitL, + M4UnitL]> { let Latency = 14; + let NumMicroOps = 4; + let ResourceCycles = [3, 3, 3, 3]; } + +def M4WriteVST1 : SchedWriteRes<[M4UnitS, + M4UnitFST]> { let Latency = 1; + let NumMicroOps = 1; } +def M4WriteVSTA : WriteSequence<[WriteVST], 2>; +def M4WriteVSTB : WriteSequence<[WriteVST], 3>; +def M4WriteVSTC : WriteSequence<[WriteVST], 4>; +def M4WriteVSTD : SchedWriteRes<[M4UnitS, + M4UnitFST]> { let Latency = 2; } +def M4WriteVSTE : SchedWriteRes<[M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST]> { let Latency = 2; + let NumMicroOps = 2; } +def M4WriteVSTF : SchedWriteRes<[M4UnitNSHF, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST]> { let Latency = 4; + let NumMicroOps = 4; + let ResourceCycles = [1, 2, 1, 2, 1]; } +def M4WriteVSTG : SchedWriteRes<[M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST]> { let Latency = 5; + let NumMicroOps = 6; + let ResourceCycles = [1, 1, 1, 2, 1, 2, 1, 2, 1]; } +def M4WriteVSTI : SchedWriteRes<[M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF, + M4UnitNSHF, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST]> { let Latency = 8; + let NumMicroOps = 5; + let ResourceCycles = [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1]; } +def M4WriteVSTJ : SchedWriteRes<[M4UnitA, + M4UnitS, + M4UnitFST]> { let Latency = 1; + let NumMicroOps = 2; } +def M4WriteVSTK : SchedWriteRes<[M4UnitA, + M4UnitS, + M4UnitFST]> { let Latency = 3; + let NumMicroOps = 2; } +def M4WriteVSTL : SchedWriteRes<[M4UnitNSHF, + M4UnitNSHF, + M4UnitS, + M4UnitFST, + M4UnitS, + M4UnitFST]> { let Latency = 4; + let NumMicroOps = 4; + let ResourceCycles = [1, 1, 2, 1, 2, 1]; } + +// Special cases. +def M4WriteCOPY : SchedWriteVariant<[SchedVar<ExynosFPPred, [M4WriteNALU1]>, + SchedVar<NoSchedPred, [M4WriteZ0]>]>; +def M4WriteMOVI : SchedWriteVariant<[SchedVar<IsZeroFPIdiomPred, [M4WriteZ0]>, + SchedVar<NoSchedPred, [M4WriteNALU1]>]>; +def M4WriteMULL : SchedWriteVariant<[SchedVar<ExynosLongVectorUpperPred, [M4WriteNEONM]>, + SchedVar<NoSchedPred, [M4WriteNMUL3]>]>; + +// Fast forwarding. +def M4ReadAESM1 : SchedReadAdvance<+1, [M4WriteNCRY1]>; +def M4ReadFMACM1 : SchedReadAdvance<+1, [M4WriteFMAC4, + M4WriteFMAC4H, + M4WriteFMAC5]>; +def M4ReadNMULM1 : SchedReadAdvance<+1, [M4WriteNMUL3]>; +def M4ReadMULLP2 : SchedReadAdvance<-2, [M4WriteNEONM]>; + +//===----------------------------------------------------------------------===// +// Coarse scheduling model. + +// Branch instructions. +def : SchedAlias<WriteBr, M4WriteZ0>; +def : SchedAlias<WriteBrReg, M4WriteC1>; + +// Arithmetic and logical integer instructions. +def : SchedAlias<WriteI, M4WriteA1>; +def : SchedAlias<WriteIEReg, M4WriteAA>; // FIXME: M4WriteAX crashes TableGen. +def : SchedAlias<WriteISReg, M4WriteAA>; // FIXME: M4WriteAX crashes TableGen. +def : SchedAlias<WriteIS, M4WriteA1>; + +// Move instructions. +def : SchedAlias<WriteImm, M4WriteA1>; + +// Divide and multiply instructions. +def : SchedAlias<WriteID32, M4WriteD12>; +def : SchedAlias<WriteID64, M4WriteD21>; +def : SchedAlias<WriteIM32, M4WriteC3>; +def : SchedAlias<WriteIM64, M4WriteCA>; + +// Miscellaneous instructions. +def : SchedAlias<WriteExtr, M4WriteAY>; + +// Addressing modes. +def : SchedAlias<WriteAdr, M4WriteZ1>; +def : SchedAlias<ReadAdrBase, M4ReadAdrBase>; + +// Load instructions. +def : SchedAlias<WriteLD, M4WriteL4>; +def : SchedAlias<WriteLDHi, M4WriteZ4>; +def : SchedAlias<WriteLDIdx, M4WriteLX>; + +// Store instructions. +def : SchedAlias<WriteST, M4WriteS1>; +def : SchedAlias<WriteSTP, M4WriteS1>; +def : SchedAlias<WriteSTX, M4WriteS1>; +def : SchedAlias<WriteSTIdx, M4WriteSX>; + +// FP data instructions. +def : SchedAlias<WriteF, M4WriteFADD2>; +def : SchedAlias<WriteFCmp, M4WriteNMSC2>; +def : SchedAlias<WriteFDiv, M4WriteFDIV12>; +def : SchedAlias<WriteFMul, M4WriteFMAC3>; + +// FP miscellaneous instructions. +def : SchedAlias<WriteFCvt, M4WriteFCVT2>; +def : SchedAlias<WriteFImm, M4WriteNALU1>; +def : SchedAlias<WriteFCopy, M4WriteCOPY>; + +// FP load instructions. +def : SchedAlias<WriteVLD, M4WriteL5>; + +// FP store instructions. +def : SchedAlias<WriteVST, M4WriteVST1>; + +// ASIMD FP instructions. +def : SchedAlias<WriteV, M4WriteNALU1>; + +// Other miscellaneous instructions. +def : WriteRes<WriteAtomic, []> { let Unsupported = 1; } +def : WriteRes<WriteBarrier, []> { let Latency = 1; } +def : WriteRes<WriteHint, []> { let Latency = 1; } +def : WriteRes<WriteSys, []> { let Latency = 1; } + +//===----------------------------------------------------------------------===// +// Generic fast forwarding. + +// TODO: Add FP register forwarding rules. + +def : ReadAdvance<ReadI, 0>; +def : ReadAdvance<ReadISReg, 0>; +def : ReadAdvance<ReadIEReg, 0>; +def : ReadAdvance<ReadIM, 0>; +// TODO: The forwarding for 32 bits actually saves 2 cycles. +def : ReadAdvance<ReadIMA, 3, [WriteIM32, WriteIM64]>; +def : ReadAdvance<ReadID, 0>; +def : ReadAdvance<ReadExtrHi, 0>; +def : ReadAdvance<ReadAdrBase, 0>; +def : ReadAdvance<ReadVLD, 0>; + +//===----------------------------------------------------------------------===// +// Finer scheduling model. + +// Branch instructions +def : InstRW<[M4WriteB1], (instrs Bcc)>; +def : InstRW<[M4WriteAF], (instrs BL)>; +def : InstRW<[M4WriteBX], (instrs BLR)>; +def : InstRW<[M4WriteC1], (instregex "^CBN?Z[WX]")>; +def : InstRW<[M4WriteAD], (instregex "^TBN?Z[WX]")>; + +// Arithmetic and logical integer instructions. +def : InstRW<[M4WriteAX], (instregex "^(ADD|AND|BIC|EON|EOR|ORN|SUB)[WX]rs$")>; +def : InstRW<[M4WriteAU], (instrs ORRWrs, ORRXrs)>; +def : InstRW<[M4WriteAX], (instregex "^(ADD|AND|BIC|SUB)S[WX]rs$")>; +def : InstRW<[M4WriteAX], (instregex "^(ADD|SUB)S?[WX]rx(64)?$")>; +def : InstRW<[M4WriteAV], (instrs ADDWri, ADDXri, ORRWri, ORRXri)>; + +// Move instructions. +def : InstRW<[M4WriteCOPY], (instrs COPY)>; +def : InstRW<[M4WriteZ0], (instrs ADR, ADRP)>; +def : InstRW<[M4WriteZ0], (instregex "^MOV[NZ][WX]i")>; + +// Divide and multiply instructions. + +// Miscellaneous instructions. + +// Load instructions. +def : InstRW<[M4WriteLD, + WriteLDHi, + WriteAdr], (instregex "^LDP(SW|W|X)(post|pre)")>; +def : InstRW<[M4WriteL5, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roW")>; +def : InstRW<[WriteLDIdx, + ReadAdrBase], (instregex "^LDR(BB|SBW|SBX|HH|SHW|SHX|SW|W|X)roX")>; +def : InstRW<[M4WriteL5, + ReadAdrBase], (instrs PRFMroW)>; +def : InstRW<[WriteLDIdx, + ReadAdrBase], (instrs PRFMroX)>; + +// Store instructions. +def : InstRW<[M4WriteSB, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roW")>; +def : InstRW<[WriteST, + ReadAdrBase], (instregex "^STR(BB|HH|W|X)roX")>; + +// FP data instructions. +def : InstRW<[M4WriteNSHF1H], (instrs FABSHr)>; +def : InstRW<[M4WriteNSHF1], (instregex "^FABS[SD]r")>; +def : InstRW<[M4WriteFADD2H], (instregex "^F(ADD|SUB)Hrr")>; +def : InstRW<[M4WriteFADD2], (instregex "^F(ADD|SUB)[SD]rr")>; +def : InstRW<[M4WriteFADD2H], (instregex "^FADDPv.i16")>; +def : InstRW<[M4WriteFADD2], (instregex "^FADDPv.i(32|64)")>; +def : InstRW<[M4WriteNEONQ], (instregex "^FCCMPE?[HSD]rr")>; +def : InstRW<[M4WriteNMSC2], (instregex "^FCMPE?[HSD]r[ir]")>; +def : InstRW<[M4WriteNMSC1], (instregex "^F(AC|CM)(EQ|GE|GT|LE|LT)(16|32|64|v1)")>; +def : InstRW<[M4WriteFDIV7H], (instrs FDIVHrr)>; +def : InstRW<[M4WriteFDIV7], (instrs FDIVSrr)>; +def : InstRW<[M4WriteFDIV12], (instrs FDIVDrr)>; +def : InstRW<[M4WriteNMSC1], (instregex "^F(MAX|MIN)(NM)?[HSD]rr")>; +def : InstRW<[M4WriteFMAC3H], (instregex "^FN?MULHrr")>; +def : InstRW<[M4WriteFMAC3], (instregex "^FN?MUL[SD]rr")>; +def : InstRW<[M4WriteFMAC3H], (instrs FMULX16)>; +def : InstRW<[M4WriteFMAC3], (instregex "^FMULX(32|64)")>; +def : InstRW<[M4WriteFMAC4H, + M4ReadFMACM1], (instregex "^FN?M(ADD|SUB)Hrrr")>; +def : InstRW<[M4WriteFMAC4, + M4ReadFMACM1], (instregex "^FN?M(ADD|SUB)[SD]rrr")>; +def : InstRW<[M4WriteNALU1H], (instrs FNEGHr)>; +def : InstRW<[M4WriteNALU1], (instregex "^FNEG[SD]r")>; +def : InstRW<[M4WriteFCVT3A], (instregex "^FRINT.+r")>; +def : InstRW<[M4WriteNEONH], (instregex "^FCSEL[HSD]rrr")>; +def : InstRW<[M4WriteFSQR7H], (instrs FSQRTHr)>; +def : InstRW<[M4WriteFSQR8], (instrs FSQRTSr)>; +def : InstRW<[M4WriteFSQR12], (instrs FSQRTDr)>; + +// FP miscellaneous instructions. +def : InstRW<[M4WriteFCVT2H], (instregex "^FCVTH[SD]r")>; +def : InstRW<[M4WriteFCVT2H], (instregex "^FCVT[SD]Hr")>; +def : InstRW<[M4WriteFCVT2], (instregex "^FCVT[SD][SD]r")>; +def : InstRW<[M4WriteFCVT6A], (instregex "^[SU]CVTF[SU][XW][HSD]ri")>; +def : InstRW<[M4WriteNEONR], (instregex "^FCVT[AMNPZ][SU][SU][XW][HSD]r")>; +def : InstRW<[M4WriteNALU1], (instregex "^FMOV[HSD][ir]")>; +def : InstRW<[M4WriteSA], (instregex "^FMOV[WX][HSD]r")>; +def : InstRW<[M4WriteNEONJ], (instregex "^FMOV[HSD][WX]r")>; +def : InstRW<[M4WriteNEONI], (instregex "^FMOVXDHighr")>; +def : InstRW<[M4WriteNEONK], (instregex "^FMOVDXHighr")>; +def : InstRW<[M4WriteFCVT3H], (instregex "^F(RECP|RSQRT)Ev1f16")>; +def : InstRW<[M4WriteFCVT3], (instregex "^F(RECP|RSQRT)Ev1i(32|64)")>; +def : InstRW<[M4WriteNMSC1], (instregex "^FRECPXv1")>; +def : InstRW<[M4WriteFMAC4H, + M4ReadFMACM1], (instregex "^F(RECP|RSQRT)S16")>; +def : InstRW<[M4WriteFMAC4, + M4ReadFMACM1], (instregex "^F(RECP|RSQRT)S(32|64)")>; + +// FP load instructions. +def : InstRW<[WriteVLD], (instregex "^LDR[SDQ]l")>; +def : InstRW<[WriteVLD], (instregex "^LDUR[BHSDQ]i")>; +def : InstRW<[WriteVLD, + WriteAdr], (instregex "^LDR[BHSDQ](post|pre)")>; +def : InstRW<[WriteVLD], (instregex "^LDR[BHSDQ]ui")>; +def : InstRW<[M4WriteLE, + ReadAdrBase], (instregex "^LDR[BHSDQ]roW")>; +def : InstRW<[WriteVLD, + ReadAdrBase], (instregex "^LDR[BHSD]roX")>; +def : InstRW<[M4WriteLE, + ReadAdrBase], (instrs LDRQroX)>; +def : InstRW<[WriteVLD, + M4WriteLH], (instregex "^LDN?P[SD]i")>; +def : InstRW<[M4WriteLA, + M4WriteLH], (instregex "^LDN?PQi")>; +def : InstRW<[M4WriteL5, + M4WriteLH, + WriteAdr], (instregex "^LDP[SD]post")>; +def : InstRW<[M4WriteLB, + M4WriteLH, + WriteAdr], (instrs LDPQpost)>; +def : InstRW<[M4WriteLB, + M4WriteLH, + WriteAdr], (instregex "^LDP[SD]pre")>; +def : InstRW<[M4WriteLC, + M4WriteLH, + WriteAdr], (instrs LDPQpre)>; + +// FP store instructions. +def : InstRW<[WriteVST], (instregex "^STUR[BHSDQ]i")>; +def : InstRW<[WriteVST, + WriteAdr], (instregex "^STR[BHSDQ](post|pre)")>; +def : InstRW<[WriteVST], (instregex "^STR[BHSDQ]ui")>; +def : InstRW<[M4WriteVSTJ, + ReadAdrBase], (instregex "^STR[BHSD]roW")>; +def : InstRW<[M4WriteVSTK, + ReadAdrBase], (instrs STRQroW)>; +def : InstRW<[WriteVST, + ReadAdrBase], (instregex "^STR[BHSD]roX")>; +def : InstRW<[M4WriteVSTK, + ReadAdrBase], (instrs STRQroX)>; +def : InstRW<[WriteVST], (instregex "^STN?P[SD]i")>; +def : InstRW<[M4WriteVSTA], (instregex "^STN?PQi")>; +def : InstRW<[WriteVST, + WriteAdr], (instregex "^STP[SD](post|pre)")>; +def : InstRW<[M4WriteVSTJ, + WriteAdr], (instregex "^STPQ(post|pre)")>; + +// ASIMD instructions. +def : InstRW<[M4WriteNHAD1], (instregex "^[SU]ABDL?v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]ABAL?v")>; +def : InstRW<[M4WriteNMSC1], (instregex "^ABSv")>; +def : InstRW<[M4WriteNALU1], (instregex "^(ADD|NEG|SUB)v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]?ADDL?Pv")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]H(ADD|SUB)v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU](ADD|SUB)[LW]v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^R?(ADD|SUB)HN2?v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]Q(ADD|SUB)v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^(SU|US)QADDv")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]RHADDv")>; +def : InstRW<[M4WriteNMSC1], (instregex "^SQ(ABS|NEG)v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]?ADDL?Vv")>; +def : InstRW<[M4WriteNMSC1], (instregex "^CM(EQ|GE|GT|HI|HS|LE|LT)v")>; +def : InstRW<[M4WriteNALU1], (instregex "^CMTSTv")>; +def : InstRW<[M4WriteNALU1], (instregex "^(AND|BIC|EOR|NOT|ORN|ORR)v")>; +def : InstRW<[M4WriteNMSC1], (instregex "^[SU](MIN|MAX)v")>; +def : InstRW<[M4WriteNMSC2], (instregex "^[SU](MIN|MAX)Pv")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU](MIN|MAX)Vv")>; +def : InstRW<[M4WriteNMUL3], (instregex "^(SQR?D)?MULH?v")>; +def : InstRW<[M4WriteNMUL3, + M4ReadNMULM1], (instregex "^ML[AS]v")>; +def : InstRW<[M4WriteNMUL3], (instregex "^SQRDML[AS]H")>; +def : InstRW<[M4WriteMULL, + M4ReadMULLP2], (instregex "^(S|U|SQD)ML[AS]Lv")>; +def : InstRW<[M4WriteMULL, + M4ReadMULLP2], (instregex "^(S|U|SQD)MULLv")>; +def : InstRW<[M4WriteNMUL3], (instregex "^[SU]DOT(lane)?v")>; +def : InstRW<[M4WriteNHAD3], (instregex "^[SU]ADALPv")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^[SU]R?SRA[dv]")>; +def : InstRW<[M4WriteNSHT1], (instregex "^SHL[dv]")>; +def : InstRW<[M4WriteNSHT1], (instregex "^S[LR]I[dv]")>; +def : InstRW<[M4WriteNSHT1], (instregex "^[SU]SH[LR][dv]")>; +def : InstRW<[M4WriteNSHT2], (instregex "^[SU]?SHLLv")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^[SU]?Q?R?SHRU?N[bhsv]")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^[SU]RSH[LR][dv]")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^[SU]QR?SHLU?[bhsdv]")>; + +// ASIMD FP instructions. +def : InstRW<[M4WriteNSHF1H], (instregex "^FABSv.f16")>; +def : InstRW<[M4WriteNSHF1], (instregex "^FABSv.f(32|64)")>; +def : InstRW<[M4WriteFADD2H], (instregex "^F(ABD|ADD|SUB)v.f16")>; +def : InstRW<[M4WriteFADD2], (instregex "^F(ABD|ADD|SUB)v.f(32|64)")>; +def : InstRW<[M4WriteFADD2H], (instregex "^FADDPv.f16")>; +def : InstRW<[M4WriteFADD2], (instregex "^FADDPv.f(32|64)")>; +def : InstRW<[M4WriteNMSC1], (instregex "^F(AC|CM)(EQ|GE|GT|LE|LT)v[^1]")>; +def : InstRW<[M4WriteFCVT2], (instregex "^FCVT(L|N|XN)v")>; +def : InstRW<[M4WriteFCVT2A], (instregex "^FCVT[AMNPZ][SU]v")>; +def : InstRW<[M4WriteFCVT2H], (instregex "^[SU]CVTFv.[fi]16")>; +def : InstRW<[M4WriteFCVT2], (instregex "^[SU]CVTFv.[fi](32|64)")>; +def : InstRW<[M4WriteFDIV7H], (instrs FDIVv4f16)>; +def : InstRW<[M4WriteNEONVH], (instrs FDIVv8f16)>; +def : InstRW<[M4WriteFDIV7], (instrs FDIVv2f32)>; +def : InstRW<[M4WriteNEONV], (instrs FDIVv4f32)>; +def : InstRW<[M4WriteNEONW], (instrs FDIVv2f64)>; +def : InstRW<[M4WriteNMSC1], (instregex "^F(MAX|MIN)(NM)?v")>; +def : InstRW<[M4WriteNMSC2], (instregex "^F(MAX|MIN)(NM)?Pv")>; +def : InstRW<[M4WriteNEONZ], (instregex "^F(MAX|MIN)(NM)?Vv")>; +def : InstRW<[M4WriteFMAC2H], (instregex "^FMULX?v.[fi]16")>; +def : InstRW<[M4WriteFMAC3], (instregex "^FMULX?v.[fi](32|64)")>; +def : InstRW<[M4WriteFMAC4H, + M4ReadFMACM1], (instregex "^FML[AS]v.[fi]16")>; +def : InstRW<[M4WriteFMAC4, + M4ReadFMACM1], (instregex "^FML[AS]v.[fi](32|64)")>; +def : InstRW<[M4WriteNALU1H], (instregex "^FNEGv.f16")>; +def : InstRW<[M4WriteNALU1], (instregex "^FNEGv.f(32|64)")>; +def : InstRW<[M4WriteFCVT3A], (instregex "^FRINT[AIMNPXZ]v")>; +def : InstRW<[M4WriteFSQR7H], (instrs FSQRTv4f16)>; +def : InstRW<[M4WriteNEONXH], (instrs FSQRTv8f16)>; +def : InstRW<[M4WriteFSQR8], (instrs FSQRTv2f32)>; +def : InstRW<[M4WriteNEONX], (instrs FSQRTv4f32)>; +def : InstRW<[M4WriteNEONY], (instrs FSQRTv2f64)>; + +// ASIMD miscellaneous instructions. +def : InstRW<[M4WriteNALU1], (instregex "^RBITv")>; +def : InstRW<[M4WriteNALU1], (instregex "^(BIF|BIT|BSL)v")>; +def : InstRW<[M4WriteNALU1], (instregex "^CL[STZ]v")>; +def : InstRW<[M4WriteNEONB], (instregex "^DUPv.+gpr")>; +def : InstRW<[M4WriteNSHF1], (instregex "^CPY")>; +def : InstRW<[M4WriteNSHF1], (instregex "^DUPv.+lane")>; +def : InstRW<[M4WriteNSHF1], (instregex "^EXTv")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^XTNv")>; +def : InstRW<[M4WriteNSHT4A], (instregex "^[SU]?QXTU?Nv")>; +def : InstRW<[M4WriteNEONB], (instregex "^INSv.+gpr")>; +def : InstRW<[M4WriteNSHF1], (instregex "^INSv.+lane")>; +def : InstRW<[M4WriteMOVI], (instregex "^(MOV|MVN)I")>; +def : InstRW<[M4WriteNALU1H], (instregex "^FMOVv.f16")>; +def : InstRW<[M4WriteNALU1], (instregex "^FMOVv.f(32|64)")>; +def : InstRW<[M4WriteFCVT3H], (instregex "^F(RECP|RSQRT)Ev[248]f16")>; +def : InstRW<[M4WriteFCVT3], (instregex "^F(RECP|RSQRT)Ev[248]f(32|64)")>; +def : InstRW<[M4WriteFCVT3], (instregex "^U(RECP|RSQRT)Ev[24]i32")>; +def : InstRW<[M4WriteFMAC4H, + M4ReadFMACM1], (instregex "^F(RECP|RSQRT)Sv.f16")>; +def : InstRW<[M4WriteFMAC4, + M4ReadFMACM1], (instregex "^F(RECP|RSQRT)Sv.f(32|64)")>; +def : InstRW<[M4WriteNSHF1], (instregex "^REV(16|32|64)v")>; +def : InstRW<[M4WriteNSHFA], (instregex "^TB[LX]v(8|16)i8One")>; +def : InstRW<[M4WriteNSHFB], (instregex "^TB[LX]v(8|16)i8Two")>; +def : InstRW<[M4WriteNSHFC], (instregex "^TB[LX]v(8|16)i8Three")>; +def : InstRW<[M4WriteNSHFD], (instregex "^TB[LX]v(8|16)i8Four")>; +def : InstRW<[M4WriteNEONP], (instregex "^[SU]MOVv")>; +def : InstRW<[M4WriteNSHF1], (instregex "^(TRN|UZP|ZIP)[12]v")>; + +// ASIMD load instructions. +def : InstRW<[WriteVLD], (instregex "LD1Onev(8b|4h|2s|1d)$")>; +def : InstRW<[WriteVLD, + M4WriteA1], (instregex "LD1Onev(8b|4h|2s|1d)_POST$")>; +def : InstRW<[WriteVLD], (instregex "LD1Onev(16b|8h|4s|2d)$")>; +def : InstRW<[WriteVLD, + M4WriteA1], (instregex "LD1Onev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDA], (instregex "LD1Twov(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDA, + M4WriteA1], (instregex "LD1Twov(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDA], (instregex "LD1Twov(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDA, + M4WriteA1], (instregex "LD1Twov(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDB], (instregex "LD1Threev(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDB, + M4WriteA1], (instregex "LD1Threev(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDB], (instregex "LD1Threev(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDB, + M4WriteA1], (instregex "LD1Threev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDC], (instregex "LD1Fourv(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDC, + M4WriteA1], (instregex "LD1Fourv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDC], (instregex "LD1Fourv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDC, + M4WriteA1], (instregex "LD1Fourv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDD], (instregex "LD1i(8|16|32|64)$")>; +def : InstRW<[M4WriteVLDD, + M4WriteA1], (instregex "LD1i(8|16|32|64)_POST$")>; + +def : InstRW<[WriteVLD], (instregex "LD1Rv(8b|4h|2s|1d)$")>; +def : InstRW<[WriteVLD, + M4WriteA1], (instregex "LD1Rv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[WriteVLD], (instregex "LD1Rv(16b|8h|4s|2d)$")>; +def : InstRW<[WriteVLD, + M4WriteA1], (instregex "LD1Rv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDF], (instregex "LD2Twov(8b|4h|2s)$")>; +def : InstRW<[M4WriteVLDF, + M4WriteA1], (instregex "LD2Twov(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVLDF], (instregex "LD2Twov(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDF, + M4WriteA1], (instregex "LD2Twov(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDG], (instregex "LD2i(8|16|32|64)$")>; +def : InstRW<[M4WriteVLDG, + M4WriteA1], (instregex "LD2i(8|16|32|64)_POST$")>; + +def : InstRW<[M4WriteVLDA], (instregex "LD2Rv(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDA, + M4WriteA1], (instregex "LD2Rv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDA], (instregex "LD2Rv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDA, + M4WriteA1], (instregex "LD2Rv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDI], (instregex "LD3Threev(8b|4h|2s)$")>; +def : InstRW<[M4WriteVLDI, + M4WriteA1], (instregex "LD3Threev(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVLDI], (instregex "LD3Threev(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDI, + M4WriteA1], (instregex "LD3Threev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDJ], (instregex "LD3i(8|16|32)$")>; +def : InstRW<[M4WriteVLDJ, + M4WriteA1], (instregex "LD3i(8|16|32)_POST$")>; +def : InstRW<[M4WriteVLDL], (instregex "LD3i64$")>; +def : InstRW<[M4WriteVLDL, + M4WriteA1], (instregex "LD3i64_POST$")>; + +def : InstRW<[M4WriteVLDB], (instregex "LD3Rv(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDB, + M4WriteA1], (instregex "LD3Rv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDB], (instregex "LD3Rv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDB, + M4WriteA1], (instregex "LD3Rv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDN], (instregex "LD4Fourv(8b|4h|2s)$")>; +def : InstRW<[M4WriteVLDN, + M4WriteA1], (instregex "LD4Fourv(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVLDN], (instregex "LD4Fourv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDN, + M4WriteA1], (instregex "LD4Fourv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVLDK], (instregex "LD4i(8|16|32)$")>; +def : InstRW<[M4WriteVLDK, + M4WriteA1], (instregex "LD4i(8|16|32)_POST$")>; +def : InstRW<[M4WriteVLDM], (instregex "LD4i64$")>; +def : InstRW<[M4WriteVLDM, + M4WriteA1], (instregex "LD4i64_POST$")>; + +def : InstRW<[M4WriteVLDC], (instregex "LD4Rv(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVLDC, + M4WriteA1], (instregex "LD4Rv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVLDC], (instregex "LD4Rv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVLDC, + M4WriteA1], (instregex "LD4Rv(16b|8h|4s|2d)_POST$")>; + +// ASIMD store instructions. +def : InstRW<[WriteVST], (instregex "ST1Onev(8b|4h|2s|1d)$")>; +def : InstRW<[WriteVST, + M4WriteA1], (instregex "ST1Onev(8b|4h|2s|1d)_POST$")>; +def : InstRW<[WriteVST], (instregex "ST1Onev(16b|8h|4s|2d)$")>; +def : InstRW<[WriteVST, + M4WriteA1], (instregex "ST1Onev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTA], (instregex "ST1Twov(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVSTA, + M4WriteA1], (instregex "ST1Twov(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVSTA], (instregex "ST1Twov(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTA, + M4WriteA1], (instregex "ST1Twov(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTB], (instregex "ST1Threev(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVSTB, + M4WriteA1], (instregex "ST1Threev(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVSTB], (instregex "ST1Threev(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTB, + M4WriteA1], (instregex "ST1Threev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTC], (instregex "ST1Fourv(8b|4h|2s|1d)$")>; +def : InstRW<[M4WriteVSTC, + M4WriteA1], (instregex "ST1Fourv(8b|4h|2s|1d)_POST$")>; +def : InstRW<[M4WriteVSTC], (instregex "ST1Fourv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTC, + M4WriteA1], (instregex "ST1Fourv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[WriteVST], (instregex "ST1i(8|16|32|64)$")>; +def : InstRW<[WriteVST, + M4WriteA1], (instregex "ST1i(8|16|32|64)_POST$")>; + +def : InstRW<[M4WriteVSTD], (instregex "ST2Twov(8b|4h|2s)$")>; +def : InstRW<[M4WriteVSTD, + M4WriteA1], (instregex "ST2Twov(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVSTE], (instregex "ST2Twov(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTE, + M4WriteA1], (instregex "ST2Twov(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTD], (instregex "ST2i(8|16|32|64)$")>; +def : InstRW<[M4WriteVSTD, + M4WriteA1], (instregex "ST2i(8|16|32|64)_POST$")>; + +def : InstRW<[M4WriteVSTF], (instregex "ST3Threev(8b|4h|2s)$")>; +def : InstRW<[M4WriteVSTF, + M4WriteA1], (instregex "ST3Threev(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVSTG], (instregex "ST3Threev(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTG, + M4WriteA1], (instregex "ST3Threev(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTE], (instregex "ST3i(8|16|32|64)$")>; +def : InstRW<[M4WriteVSTE, + M4WriteA1], (instregex "ST3i(8|16|32|64)_POST$")>; + +def : InstRW<[M4WriteVSTL], (instregex "ST4Fourv(8b|4h|2s)$")>; +def : InstRW<[M4WriteVSTL, + M4WriteA1], (instregex "ST4Fourv(8b|4h|2s)_POST$")>; +def : InstRW<[M4WriteVSTI], (instregex "ST4Fourv(16b|8h|4s|2d)$")>; +def : InstRW<[M4WriteVSTI, + M4WriteA1], (instregex "ST4Fourv(16b|8h|4s|2d)_POST$")>; + +def : InstRW<[M4WriteVSTE], (instregex "ST4i(8|16|32|64)$")>; +def : InstRW<[M4WriteVSTE, + M4WriteA1], (instregex "ST4i(8|16|32|64)_POST$")>; + +// Cryptography instructions. +def : InstRW<[M4WriteNCRY1], (instregex "^AES[DE]")>; +def : InstRW<[M4WriteNCRY1, + M4ReadAESM1], (instregex "^AESI?MC")>; +def : InstRW<[M4WriteNCRY1A], (instregex "^PMULv")>; +def : InstRW<[M4WriteNCRY1A], (instregex "^PMULLv(1|8)i")>; +def : InstRW<[M4WriteNCRY3A], (instregex "^PMULLv(2|16)i")>; +def : InstRW<[M4WriteNCRY1A], (instregex "^SHA1([CHMP]|SU[01])")>; +def : InstRW<[M4WriteNCRY1A], (instrs SHA256SU0rr)>; +def : InstRW<[M4WriteNCRY5A], (instrs SHA256SU1rrr)>; +def : InstRW<[M4WriteNCRY5A], (instrs SHA256H2rrr)>; + +// CRC instructions. +def : InstRW<[M4WriteE2], (instregex "^CRC32C?[BHWX]rr$")>; + +} // SchedModel = ExynosM4Model diff --git a/lib/Target/AArch64/AArch64SchedPredExynos.td b/lib/Target/AArch64/AArch64SchedPredExynos.td new file mode 100644 index 000000000000..48c54230e9d8 --- /dev/null +++ b/lib/Target/AArch64/AArch64SchedPredExynos.td @@ -0,0 +1,157 @@ +//===- AArch64SchedPredExynos.td - AArch64 Sched Preds -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines scheduling predicate definitions that are used by the +// AArch64 Exynos processors. +// +//===----------------------------------------------------------------------===// + +// Auxiliary predicates. + +// Check the shift in arithmetic and logic instructions. +def ExynosCheckShift : CheckAny<[CheckShiftBy0, + CheckAll< + [CheckShiftLSL, + CheckAny< + [CheckShiftBy1, + CheckShiftBy2, + CheckShiftBy3]>]>]>; + +// Exynos predicates. + +// Identify BLR specifying the LR register as the indirect target register. +def ExynosBranchLinkLRPred : MCSchedPredicate< + CheckAll<[CheckOpcode<[BLR]>, + CheckRegOperand<0, LR>]>>; + +// Identify arithmetic instructions without or with limited extension or shift. +def ExynosArithFn : TIIPredicate< + "isExynosArithFast", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsArithExtOp.ValidOpcodes, + MCReturnStatement< + CheckAny<[CheckExtBy0, + CheckAll< + [CheckAny< + [CheckExtUXTW, + CheckExtUXTX]>, + CheckAny< + [CheckExtBy1, + CheckExtBy2, + CheckExtBy3]>]>]>>>, + MCOpcodeSwitchCase< + IsArithShiftOp.ValidOpcodes, + MCReturnStatement<ExynosCheckShift>>, + MCOpcodeSwitchCase< + IsArithUnshiftOp.ValidOpcodes, + MCReturnStatement<TruePred>>], + MCReturnStatement<FalsePred>>>; +def ExynosArithPred : MCSchedPredicate<ExynosArithFn>; + +// Identify logic instructions with limited shift. +def ExynosLogicFn : TIIPredicate< + "isExynosLogicFast", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsLogicShiftOp.ValidOpcodes, + MCReturnStatement<ExynosCheckShift>>, + MCOpcodeSwitchCase< + IsLogicUnshiftOp.ValidOpcodes, + MCReturnStatement<TruePred>>], + MCReturnStatement<FalsePred>>>; +def ExynosLogicPred : MCSchedPredicate<ExynosLogicFn>; + +// Identify more logic instructions with limited shift. +def ExynosLogicExFn : TIIPredicate< + "isExynosLogicExFast", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsLogicShiftOp.ValidOpcodes, + MCReturnStatement< + CheckAny< + [ExynosCheckShift, + CheckAll< + [CheckShiftLSL, + CheckShiftBy8]>]>>>, + MCOpcodeSwitchCase< + IsLogicUnshiftOp.ValidOpcodes, + MCReturnStatement<TruePred>>], + MCReturnStatement<FalsePred>>>; +def ExynosLogicExPred : MCSchedPredicate<ExynosLogicExFn>; + +// Identify a load or store using the register offset addressing mode +// with a scaled non-extended register. +def ExynosScaledIdxFn : TIIPredicate<"isExynosScaledAddr", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsLoadStoreRegOffsetOp.ValidOpcodes, + MCReturnStatement< + CheckAny< + [CheckMemExtSXTW, + CheckMemExtUXTW, + CheckMemScaled]>>>], + MCReturnStatement<FalsePred>>>; +def ExynosScaledIdxPred : MCSchedPredicate<ExynosScaledIdxFn>; + +// Identify FP instructions. +def ExynosFPPred : MCSchedPredicate<CheckAny<[CheckDForm, CheckQForm]>>; + +// Identify whether an instruction whose result is a long vector +// operates on the upper half of the input registers. +def ExynosLongVectorUpperFn : TIIPredicate< + "isExynosLongVectorUpper", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsLongVectorUpperOp.ValidOpcodes, + MCReturnStatement<TruePred>>], + MCReturnStatement<FalsePred>>>; +def ExynosLongVectorUpperPred : MCSchedPredicate<ExynosLongVectorUpperFn>; + +// Identify 128-bit NEON instructions. +def ExynosQFormPred : MCSchedPredicate<CheckQForm>; + +// Identify instructions that reset a register efficiently. +def ExynosResetFn : TIIPredicate< + "isExynosResetFast", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + [ADR, ADRP, + MOVNWi, MOVNXi, + MOVZWi, MOVZXi], + MCReturnStatement<TruePred>>, + MCOpcodeSwitchCase< + [ORRWri, ORRXri], + MCReturnStatement< + CheckAll< + [CheckIsRegOperand<1>, + CheckAny< + [CheckRegOperand<1, WZR>, + CheckRegOperand<1, XZR>]>]>>>], + MCReturnStatement< + CheckAny< + [IsCopyIdiomFn, + IsZeroFPIdiomFn]>>>>; +def ExynosResetPred : MCSchedPredicate<ExynosResetFn>; + +// Identify EXTR as the alias for ROR (immediate). +def ExynosRotateRightImmPred : MCSchedPredicate< + CheckAll<[CheckOpcode<[EXTRWrri, EXTRXrri]>, + CheckSameRegOperand<1, 2>]>>; + +// Identify cheap arithmetic and logic immediate instructions. +def ExynosCheapFn : TIIPredicate< + "isExynosCheapAsMove", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsArithLogicImmOp.ValidOpcodes, + MCReturnStatement<TruePred>>], + MCReturnStatement< + CheckAny< + [ExynosArithFn, ExynosResetFn, ExynosLogicFn]>>>>; diff --git a/lib/Target/AArch64/AArch64SchedPredicates.td b/lib/Target/AArch64/AArch64SchedPredicates.td new file mode 100644 index 000000000000..dbaf11fc95dd --- /dev/null +++ b/lib/Target/AArch64/AArch64SchedPredicates.td @@ -0,0 +1,423 @@ +//===- AArch64SchedPredicates.td - AArch64 Sched Preds -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines scheduling predicate definitions that are used by the +// AArch64 subtargets. +// +//===----------------------------------------------------------------------===// + +// Function mappers. + +// Check the extension type in arithmetic instructions. +let FunctionMapper = "AArch64_AM::getArithExtendType" in { + def CheckExtUXTB : CheckImmOperand_s<3, "AArch64_AM::UXTB">; + def CheckExtUXTH : CheckImmOperand_s<3, "AArch64_AM::UXTH">; + def CheckExtUXTW : CheckImmOperand_s<3, "AArch64_AM::UXTW">; + def CheckExtUXTX : CheckImmOperand_s<3, "AArch64_AM::UXTX">; + def CheckExtSXTB : CheckImmOperand_s<3, "AArch64_AM::SXTB">; + def CheckExtSXTH : CheckImmOperand_s<3, "AArch64_AM::SXTH">; + def CheckExtSXTW : CheckImmOperand_s<3, "AArch64_AM::SXTW">; + def CheckExtSXTX : CheckImmOperand_s<3, "AArch64_AM::SXTX">; +} + +// Check for shifting in extended arithmetic instructions. +foreach I = {0-3} in { + let FunctionMapper = "AArch64_AM::getArithShiftValue" in + def CheckExtBy#I : CheckImmOperand<3, I>; +} + +// Check the extension type in the register offset addressing mode. +let FunctionMapper = "AArch64_AM::getMemExtendType" in { + def CheckMemExtUXTW : CheckImmOperand_s<3, "AArch64_AM::UXTW">; + def CheckMemExtLSL : CheckImmOperand_s<3, "AArch64_AM::UXTX">; + def CheckMemExtSXTW : CheckImmOperand_s<3, "AArch64_AM::SXTW">; + def CheckMemExtSXTX : CheckImmOperand_s<3, "AArch64_AM::SXTX">; +} + +// Check for scaling in the register offset addressing mode. +let FunctionMapper = "AArch64_AM::getMemDoShift" in +def CheckMemScaled : CheckImmOperandSimple<3>; + +// Check the shifting type in arithmetic and logic instructions. +let FunctionMapper = "AArch64_AM::getShiftType" in { + def CheckShiftLSL : CheckImmOperand_s<3, "AArch64_AM::LSL">; + def CheckShiftLSR : CheckImmOperand_s<3, "AArch64_AM::LSR">; + def CheckShiftASR : CheckImmOperand_s<3, "AArch64_AM::ASR">; + def CheckShiftROR : CheckImmOperand_s<3, "AArch64_AM::ROR">; + def CheckShiftMSL : CheckImmOperand_s<3, "AArch64_AM::MSL">; +} + +// Check for shifting in arithmetic and logic instructions. +foreach I = {0-3, 8} in { + let FunctionMapper = "AArch64_AM::getShiftValue" in + def CheckShiftBy#I : CheckImmOperand<3, I>; +} + +// Generic predicates. + +// Identify whether an instruction is the 64-bit NEON form based on its result. +def CheckDForm : CheckAll<[CheckIsRegOperand<0>, + CheckAny<[CheckRegOperand<0, D0>, + CheckRegOperand<0, D1>, + CheckRegOperand<0, D2>, + CheckRegOperand<0, D3>, + CheckRegOperand<0, D4>, + CheckRegOperand<0, D5>, + CheckRegOperand<0, D6>, + CheckRegOperand<0, D7>, + CheckRegOperand<0, D8>, + CheckRegOperand<0, D9>, + CheckRegOperand<0, D10>, + CheckRegOperand<0, D11>, + CheckRegOperand<0, D12>, + CheckRegOperand<0, D13>, + CheckRegOperand<0, D14>, + CheckRegOperand<0, D15>, + CheckRegOperand<0, D16>, + CheckRegOperand<0, D17>, + CheckRegOperand<0, D18>, + CheckRegOperand<0, D19>, + CheckRegOperand<0, D20>, + CheckRegOperand<0, D21>, + CheckRegOperand<0, D22>, + CheckRegOperand<0, D23>, + CheckRegOperand<0, D24>, + CheckRegOperand<0, D25>, + CheckRegOperand<0, D26>, + CheckRegOperand<0, D27>, + CheckRegOperand<0, D28>, + CheckRegOperand<0, D29>, + CheckRegOperand<0, D30>, + CheckRegOperand<0, D31>]>]>; + +// Identify whether an instruction is the 128-bit NEON form based on its result. +def CheckQForm : CheckAll<[CheckIsRegOperand<0>, + CheckAny<[CheckRegOperand<0, Q0>, + CheckRegOperand<0, Q1>, + CheckRegOperand<0, Q2>, + CheckRegOperand<0, Q3>, + CheckRegOperand<0, Q4>, + CheckRegOperand<0, Q5>, + CheckRegOperand<0, Q6>, + CheckRegOperand<0, Q7>, + CheckRegOperand<0, Q8>, + CheckRegOperand<0, Q9>, + CheckRegOperand<0, Q10>, + CheckRegOperand<0, Q11>, + CheckRegOperand<0, Q12>, + CheckRegOperand<0, Q13>, + CheckRegOperand<0, Q14>, + CheckRegOperand<0, Q15>, + CheckRegOperand<0, Q16>, + CheckRegOperand<0, Q17>, + CheckRegOperand<0, Q18>, + CheckRegOperand<0, Q19>, + CheckRegOperand<0, Q20>, + CheckRegOperand<0, Q21>, + CheckRegOperand<0, Q22>, + CheckRegOperand<0, Q23>, + CheckRegOperand<0, Q24>, + CheckRegOperand<0, Q25>, + CheckRegOperand<0, Q26>, + CheckRegOperand<0, Q27>, + CheckRegOperand<0, Q28>, + CheckRegOperand<0, Q29>, + CheckRegOperand<0, Q30>, + CheckRegOperand<0, Q31>]>]>; + +// Identify arithmetic instructions with extend. +def IsArithExtOp : CheckOpcode<[ADDWrx, ADDXrx, ADDSWrx, ADDSXrx, + SUBWrx, SUBXrx, SUBSWrx, SUBSXrx, + ADDXrx64, ADDSXrx64, + SUBXrx64, SUBSXrx64]>; + +// Identify arithmetic immediate instructions. +def IsArithImmOp : CheckOpcode<[ADDWri, ADDXri, ADDSWri, ADDSXri, + SUBWri, SUBXri, SUBSWri, SUBSXri]>; + +// Identify arithmetic instructions with shift. +def IsArithShiftOp : CheckOpcode<[ADDWrs, ADDXrs, ADDSWrs, ADDSXrs, + SUBWrs, SUBXrs, SUBSWrs, SUBSXrs]>; + +// Identify arithmetic instructions without shift. +def IsArithUnshiftOp : CheckOpcode<[ADDWrr, ADDXrr, ADDSWrr, ADDSXrr, + SUBWrr, SUBXrr, SUBSWrr, SUBSXrr]>; + +// Identify logic immediate instructions. +def IsLogicImmOp : CheckOpcode<[ANDWri, ANDXri, + EORWri, EORXri, + ORRWri, ORRXri]>; + +// Identify logic instructions with shift. +def IsLogicShiftOp : CheckOpcode<[ANDWrs, ANDXrs, ANDSWrs, ANDSXrs, + BICWrs, BICXrs, BICSWrs, BICSXrs, + EONWrs, EONXrs, + EORWrs, EORXrs, + ORNWrs, ORNXrs, + ORRWrs, ORRXrs]>; + +// Identify logic instructions without shift. +def IsLogicUnshiftOp : CheckOpcode<[ANDWrr, ANDXrr, ANDSWrr, ANDSXrr, + BICWrr, BICXrr, BICSWrr, BICSXrr, + EONWrr, EONXrr, + EORWrr, EORXrr, + ORNWrr, ORNXrr, + ORRWrr, ORRXrr]>; + +// Identify arithmetic and logic immediate instructions. +def IsArithLogicImmOp : CheckOpcode<!listconcat(IsArithImmOp.ValidOpcodes, + IsLogicImmOp.ValidOpcodes)>; + +// Identify arithmetic and logic instructions with shift. +def IsArithLogicShiftOp : CheckOpcode<!listconcat(IsArithShiftOp.ValidOpcodes, + IsLogicShiftOp.ValidOpcodes)>; + +// Identify arithmetic and logic instructions without shift. +def IsArithLogicUnshiftOp : CheckOpcode<!listconcat(IsArithUnshiftOp.ValidOpcodes, + IsLogicUnshiftOp.ValidOpcodes)>; + +// Identify whether an instruction is an ASIMD +// load using the post index addressing mode. +def IsLoadASIMDPostOp : CheckOpcode<[LD1Onev8b_POST, LD1Onev4h_POST, LD1Onev2s_POST, LD1Onev1d_POST, + LD1Onev16b_POST, LD1Onev8h_POST, LD1Onev4s_POST, LD1Onev2d_POST, + LD1Twov8b_POST, LD1Twov4h_POST, LD1Twov2s_POST, LD1Twov1d_POST, + LD1Twov16b_POST, LD1Twov8h_POST, LD1Twov4s_POST, LD1Twov2d_POST, + LD1Threev8b_POST, LD1Threev4h_POST, LD1Threev2s_POST, LD1Threev1d_POST, + LD1Threev16b_POST, LD1Threev8h_POST, LD1Threev4s_POST, LD1Threev2d_POST, + LD1Fourv8b_POST, LD1Fourv4h_POST, LD1Fourv2s_POST, LD1Fourv1d_POST, + LD1Fourv16b_POST, LD1Fourv8h_POST, LD1Fourv4s_POST, LD1Fourv2d_POST, + LD1i8_POST, LD1i16_POST, LD1i32_POST, LD1i64_POST, + LD1Rv8b_POST, LD1Rv4h_POST, LD1Rv2s_POST, LD1Rv1d_POST, + LD1Rv16b_POST, LD1Rv8h_POST, LD1Rv4s_POST, LD1Rv2d_POST, + LD2Twov8b_POST, LD2Twov4h_POST, LD2Twov2s_POST, + LD2Twov16b_POST, LD2Twov8h_POST, LD2Twov4s_POST, LD2Twov2d_POST, + LD2i8_POST, LD2i16_POST, LD2i32_POST, LD2i64_POST, + LD2Rv8b_POST, LD2Rv4h_POST, LD2Rv2s_POST, LD2Rv1d_POST, + LD2Rv16b_POST, LD2Rv8h_POST, LD2Rv4s_POST, LD2Rv2d_POST, + LD3Threev8b_POST, LD3Threev4h_POST, LD3Threev2s_POST, + LD3Threev16b_POST, LD3Threev8h_POST, LD3Threev4s_POST, LD3Threev2d_POST, + LD3i8_POST, LD3i16_POST, LD3i32_POST, LD3i64_POST, + LD3Rv8b_POST, LD3Rv4h_POST, LD3Rv2s_POST, LD3Rv1d_POST, + LD3Rv16b_POST, LD3Rv8h_POST, LD3Rv4s_POST, LD3Rv2d_POST, + LD4Fourv8b_POST, LD4Fourv4h_POST, LD4Fourv2s_POST, + LD4Fourv16b_POST, LD4Fourv8h_POST, LD4Fourv4s_POST, LD4Fourv2d_POST, + LD4i8_POST, LD4i16_POST, LD4i32_POST, LD4i64_POST, + LD4Rv8b_POST, LD4Rv4h_POST, LD4Rv2s_POST, LD4Rv1d_POST, + LD4Rv16b_POST, LD4Rv8h_POST, LD4Rv4s_POST, LD4Rv2d_POST]>; + +// Identify whether an instruction is an ASIMD +// store using the post index addressing mode. +def IsStoreASIMDPostOp : CheckOpcode<[ST1Onev8b_POST, ST1Onev4h_POST, ST1Onev2s_POST, ST1Onev1d_POST, + ST1Onev16b_POST, ST1Onev8h_POST, ST1Onev4s_POST, ST1Onev2d_POST, + ST1Twov8b_POST, ST1Twov4h_POST, ST1Twov2s_POST, ST1Twov1d_POST, + ST1Twov16b_POST, ST1Twov8h_POST, ST1Twov4s_POST, ST1Twov2d_POST, + ST1Threev8b_POST, ST1Threev4h_POST, ST1Threev2s_POST, ST1Threev1d_POST, + ST1Threev16b_POST, ST1Threev8h_POST, ST1Threev4s_POST, ST1Threev2d_POST, + ST1Fourv8b_POST, ST1Fourv4h_POST, ST1Fourv2s_POST, ST1Fourv1d_POST, + ST1Fourv16b_POST, ST1Fourv8h_POST, ST1Fourv4s_POST, ST1Fourv2d_POST, + ST1i8_POST, ST1i16_POST, ST1i32_POST, ST1i64_POST, + ST2Twov8b_POST, ST2Twov4h_POST, ST2Twov2s_POST, + ST2Twov16b_POST, ST2Twov8h_POST, ST2Twov4s_POST, ST2Twov2d_POST, + ST2i8_POST, ST2i16_POST, ST2i32_POST, ST2i64_POST, + ST3Threev8b_POST, ST3Threev4h_POST, ST3Threev2s_POST, + ST3Threev16b_POST, ST3Threev8h_POST, ST3Threev4s_POST, ST3Threev2d_POST, + ST3i8_POST, ST3i16_POST, ST3i32_POST, ST3i64_POST, + ST4Fourv8b_POST, ST4Fourv4h_POST, ST4Fourv2s_POST, + ST4Fourv16b_POST, ST4Fourv8h_POST, ST4Fourv4s_POST, ST4Fourv2d_POST, + ST4i8_POST, ST4i16_POST, ST4i32_POST, ST4i64_POST]>; + +// Identify whether an instruction is an ASIMD load +// or store using the post index addressing mode. +def IsLoadStoreASIMDPostOp : CheckOpcode<!listconcat(IsLoadASIMDPostOp.ValidOpcodes, + IsStoreASIMDPostOp.ValidOpcodes)>; + +// Identify whether an instruction is a load +// using the register offset addressing mode. +def IsLoadRegOffsetOp : CheckOpcode<[PRFMroW, PRFMroX, + LDRBBroW, LDRBBroX, + LDRSBWroW, LDRSBWroX, LDRSBXroW, LDRSBXroX, + LDRHHroW, LDRHHroX, + LDRSHWroW, LDRSHWroX, LDRSHXroW, LDRSHXroX, + LDRWroW, LDRWroX, + LDRSWroW, LDRSWroX, + LDRXroW, LDRXroX, + LDRBroW, LDRBroX, + LDRHroW, LDRHroX, + LDRSroW, LDRSroX, + LDRDroW, LDRDroX]>; + +// Identify whether an instruction is a load +// using the register offset addressing mode. +def IsStoreRegOffsetOp : CheckOpcode<[STRBBroW, STRBBroX, + STRHHroW, STRHHroX, + STRWroW, STRWroX, + STRXroW, STRXroX, + STRBroW, STRBroX, + STRHroW, STRHroX, + STRSroW, STRSroX, + STRDroW, STRDroX]>; + +// Identify whether an instruction is a load or +// store using the register offset addressing mode. +def IsLoadStoreRegOffsetOp : CheckOpcode<!listconcat(IsLoadRegOffsetOp.ValidOpcodes, + IsStoreRegOffsetOp.ValidOpcodes)>; + +// Identify whether an instruction whose result is a long vector +// operates on the upper half of the input registers. +def IsLongVectorUpperOp : CheckOpcode<[FCVTLv8i16, FCVTLv4i32, + FCVTNv8i16, FCVTNv4i32, + FCVTXNv4f32, + PMULLv16i8, PMULLv2i64, + RADDHNv8i16_v16i8, RADDHNv4i32_v8i16, RADDHNv2i64_v4i32, + RSHRNv16i8_shift, RSHRNv8i16_shift, RSHRNv4i32_shift, + RSUBHNv8i16_v16i8, RSUBHNv4i32_v8i16, RSUBHNv2i64_v4i32, + SABALv16i8_v8i16, SABALv8i16_v4i32, SABALv4i32_v2i64, + SABDLv16i8_v8i16, SABDLv8i16_v4i32, SABDLv4i32_v2i64, + SADDLv16i8_v8i16, SADDLv8i16_v4i32, SADDLv4i32_v2i64, + SADDWv16i8_v8i16, SADDWv8i16_v4i32, SADDWv4i32_v2i64, + SHLLv16i8, SHLLv8i16, SHLLv4i32, + SHRNv16i8_shift, SHRNv8i16_shift, SHRNv4i32_shift, + SMLALv16i8_v8i16, SMLALv8i16_v4i32, SMLALv4i32_v2i64, + SMLALv8i16_indexed, SMLALv4i32_indexed, + SMLSLv16i8_v8i16, SMLSLv8i16_v4i32, SMLSLv4i32_v2i64, + SMLSLv8i16_indexed, SMLSLv4i32_indexed, + SMULLv16i8_v8i16, SMULLv8i16_v4i32, SMULLv4i32_v2i64, + SMULLv8i16_indexed, SMULLv4i32_indexed, + SQDMLALv8i16_v4i32, SQDMLALv4i32_v2i64, + SQDMLALv8i16_indexed, SQDMLALv4i32_indexed, + SQDMLSLv8i16_v4i32, SQDMLSLv4i32_v2i64, + SQDMLSLv8i16_indexed, SQDMLSLv4i32_indexed, + SQDMULLv8i16_v4i32, SQDMULLv4i32_v2i64, + SQDMULLv8i16_indexed, SQDMULLv4i32_indexed, + SQRSHRNv16i8_shift, SQRSHRNv8i16_shift, SQRSHRNv4i32_shift, + SQRSHRUNv16i8_shift, SQRSHRUNv8i16_shift, SQRSHRUNv4i32_shift, + SQSHRNv16i8_shift, SQSHRNv8i16_shift, SQSHRNv4i32_shift, + SQSHRUNv16i8_shift, SQSHRUNv8i16_shift, SQSHRUNv4i32_shift, + SQXTNv16i8, SQXTNv8i16, SQXTNv4i32, + SQXTUNv16i8, SQXTUNv8i16, SQXTUNv4i32, + SSHLLv16i8_shift, SSHLLv8i16_shift, SSHLLv4i32_shift, + SSUBLv16i8_v8i16, SSUBLv8i16_v4i32, SSUBLv4i32_v2i64, + SSUBWv16i8_v8i16, SSUBWv8i16_v4i32, SSUBWv4i32_v2i64, + UABALv16i8_v8i16, UABALv8i16_v4i32, UABALv4i32_v2i64, + UABDLv16i8_v8i16, UABDLv8i16_v4i32, UABDLv4i32_v2i64, + UADDLv16i8_v8i16, UADDLv8i16_v4i32, UADDLv4i32_v2i64, + UADDWv16i8_v8i16, UADDWv8i16_v4i32, UADDWv4i32_v2i64, + UMLALv16i8_v8i16, UMLALv8i16_v4i32, UMLALv4i32_v2i64, + UMLALv8i16_indexed, UMLALv4i32_indexed, + UMLSLv16i8_v8i16, UMLSLv8i16_v4i32, UMLSLv4i32_v2i64, + UMLSLv8i16_indexed, UMLSLv4i32_indexed, + UMULLv16i8_v8i16, UMULLv8i16_v4i32, UMULLv4i32_v2i64, + UMULLv8i16_indexed, UMULLv4i32_indexed, + UQSHRNv16i8_shift, UQSHRNv8i16_shift, UQSHRNv4i32_shift, + UQXTNv16i8, UQXTNv8i16, UQXTNv4i32, + USHLLv16i8_shift, USHLLv8i16_shift, USHLLv4i32_shift, + USUBLv16i8_v8i16, USUBLv8i16_v4i32, USUBLv4i32_v2i64, + USUBWv16i8_v8i16, USUBWv8i16_v4i32, USUBWv4i32_v2i64, + XTNv16i8, XTNv8i16, XTNv4i32]>; + +// Target predicates. + +// Identify an instruction that effectively transfers a register to another. +def IsCopyIdiomFn : TIIPredicate<"isCopyIdiom", + MCOpcodeSwitchStatement< + [// MOV {Rd, SP}, {SP, Rn} => + // ADD {Rd, SP}, {SP, Rn}, #0 + MCOpcodeSwitchCase< + [ADDWri, ADDXri], + MCReturnStatement< + CheckAll< + [CheckIsRegOperand<0>, + CheckIsRegOperand<1>, + CheckAny< + [CheckRegOperand<0, WSP>, + CheckRegOperand<0, SP>, + CheckRegOperand<1, WSP>, + CheckRegOperand<1, SP>]>, + CheckZeroOperand<2>]>>>, + // MOV Rd, Rm => + // ORR Rd, ZR, Rm, LSL #0 + MCOpcodeSwitchCase< + [ORRWrs, ORRXrs], + MCReturnStatement< + CheckAll< + [CheckIsRegOperand<1>, + CheckIsRegOperand<2>, + CheckAny< + [CheckRegOperand<1, WZR>, + CheckRegOperand<1, XZR>]>, + CheckShiftBy0]>>>], + MCReturnStatement<FalsePred>>>; +def IsCopyIdiomPred : MCSchedPredicate<IsCopyIdiomFn>; + +// Identify arithmetic instructions with an extended register. +def RegExtendedFn : TIIPredicate<"hasExtendedReg", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsArithExtOp.ValidOpcodes, + MCReturnStatement< + CheckNot<CheckZeroOperand<3>>>>], + MCReturnStatement<FalsePred>>>; +def RegExtendedPred : MCSchedPredicate<RegExtendedFn>; + +// Identify arithmetic and logic instructions with a shifted register. +def RegShiftedFn : TIIPredicate<"hasShiftedReg", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsArithLogicShiftOp.ValidOpcodes, + MCReturnStatement< + CheckNot<CheckZeroOperand<3>>>>], + MCReturnStatement<FalsePred>>>; +def RegShiftedPred : MCSchedPredicate<RegShiftedFn>; + +// Identify a load or store using the register offset addressing mode +// with an extended or scaled register. +def ScaledIdxFn : TIIPredicate<"isScaledAddr", + MCOpcodeSwitchStatement< + [MCOpcodeSwitchCase< + IsLoadStoreRegOffsetOp.ValidOpcodes, + MCReturnStatement< + CheckAny<[CheckNot<CheckMemExtLSL>, + CheckMemScaled]>>>], + MCReturnStatement<FalsePred>>>; +def ScaledIdxPred : MCSchedPredicate<ScaledIdxFn>; + +// Identify an instruction that effectively resets a FP register to zero. +def IsZeroFPIdiomFn : TIIPredicate<"isZeroFPIdiom", + MCOpcodeSwitchStatement< + [// MOVI Vd, #0 + MCOpcodeSwitchCase< + [MOVIv8b_ns, MOVIv16b_ns, + MOVID, MOVIv2d_ns], + MCReturnStatement<CheckZeroOperand<1>>>, + // MOVI Vd, #0, LSL #0 + MCOpcodeSwitchCase< + [MOVIv4i16, MOVIv8i16, + MOVIv2i32, MOVIv4i32], + MCReturnStatement< + CheckAll< + [CheckZeroOperand<1>, + CheckZeroOperand<2>]>>>], + MCReturnStatement<FalsePred>>>; +def IsZeroFPIdiomPred : MCSchedPredicate<IsZeroFPIdiomFn>; + +// Identify an instruction that effectively resets a GP register to zero. +def IsZeroIdiomFn : TIIPredicate<"isZeroIdiom", + MCOpcodeSwitchStatement< + [// ORR Rd, ZR, #0 + MCOpcodeSwitchCase< + [ORRWri, ORRXri], + MCReturnStatement< + CheckAll< + [CheckIsRegOperand<1>, + CheckAny< + [CheckRegOperand<1, WZR>, + CheckRegOperand<1, XZR>]>, + CheckZeroOperand<2>]>>>], + MCReturnStatement<FalsePred>>>; +def IsZeroIdiomPred : MCSchedPredicate<IsZeroIdiomFn>; diff --git a/lib/Target/AArch64/AArch64Schedule.td b/lib/Target/AArch64/AArch64Schedule.td index ce81f48acf71..f55ba4d42fce 100644 --- a/lib/Target/AArch64/AArch64Schedule.td +++ b/lib/Target/AArch64/AArch64Schedule.td @@ -50,17 +50,6 @@ def WriteLDIdx : SchedWrite; // Load from a register index (maybe scaled). def WriteSTIdx : SchedWrite; // Store to a register index (maybe scaled). def ReadAdrBase : SchedRead; // Read the base resister of a reg-offset LD/ST. -// Predicate for determining when a shiftable register is shifted. -def RegShiftedPred : SchedPredicate<[{TII->hasShiftedReg(*MI)}]>; - -// Predicate for determining when a extendedable register is extended. -def RegExtendedPred : SchedPredicate<[{TII->hasExtendedReg(*MI)}]>; - -// ScaledIdxPred is true if a WriteLDIdx operand will be -// scaled. Subtargets can use this to dynamically select resources and -// latency for WriteLDIdx and ReadAdrBase. -def ScaledIdxPred : SchedPredicate<[{TII->isScaledAddr(*MI)}]>; - // Serialized two-level address load. // EXAMPLE: LOADGot def WriteLDAdr : WriteSequence<[WriteAdr, WriteLD]>; diff --git a/lib/Target/AArch64/AArch64SpeculationHardening.cpp b/lib/Target/AArch64/AArch64SpeculationHardening.cpp new file mode 100644 index 000000000000..e9699b0367d3 --- /dev/null +++ b/lib/Target/AArch64/AArch64SpeculationHardening.cpp @@ -0,0 +1,641 @@ +//===- AArch64SpeculationHardening.cpp - Harden Against Missspeculation --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass to insert code to mitigate against side channel +// vulnerabilities that may happen under control flow miss-speculation. +// +// The pass implements tracking of control flow miss-speculation into a "taint" +// register. That taint register can then be used to mask off registers with +// sensitive data when executing under miss-speculation, a.k.a. "transient +// execution". +// This pass is aimed at mitigating against SpectreV1-style vulnarabilities. +// +// It also implements speculative load hardening, i.e. using the taint register +// to automatically mask off loaded data. +// +// As a possible follow-on improvement, also an intrinsics-based approach as +// explained at https://lwn.net/Articles/759423/ could be implemented on top of +// the current design. +// +// For AArch64, the following implementation choices are made to implement the +// tracking of control flow miss-speculation into a taint register: +// Some of these are different than the implementation choices made in +// the similar pass implemented in X86SpeculativeLoadHardening.cpp, as +// the instruction set characteristics result in different trade-offs. +// - The speculation hardening is done after register allocation. With a +// relative abundance of registers, one register is reserved (X16) to be +// the taint register. X16 is expected to not clash with other register +// reservation mechanisms with very high probability because: +// . The AArch64 ABI doesn't guarantee X16 to be retained across any call. +// . The only way to request X16 to be used as a programmer is through +// inline assembly. In the rare case a function explicitly demands to +// use X16/W16, this pass falls back to hardening against speculation +// by inserting a DSB SYS/ISB barrier pair which will prevent control +// flow speculation. +// - It is easy to insert mask operations at this late stage as we have +// mask operations available that don't set flags. +// - The taint variable contains all-ones when no miss-speculation is detected, +// and contains all-zeros when miss-speculation is detected. Therefore, when +// masking, an AND instruction (which only changes the register to be masked, +// no other side effects) can easily be inserted anywhere that's needed. +// - The tracking of miss-speculation is done by using a data-flow conditional +// select instruction (CSEL) to evaluate the flags that were also used to +// make conditional branch direction decisions. Speculation of the CSEL +// instruction can be limited with a CSDB instruction - so the combination of +// CSEL + a later CSDB gives the guarantee that the flags as used in the CSEL +// aren't speculated. When conditional branch direction gets miss-speculated, +// the semantics of the inserted CSEL instruction is such that the taint +// register will contain all zero bits. +// One key requirement for this to work is that the conditional branch is +// followed by an execution of the CSEL instruction, where the CSEL +// instruction needs to use the same flags status as the conditional branch. +// This means that the conditional branches must not be implemented as one +// of the AArch64 conditional branches that do not use the flags as input +// (CB(N)Z and TB(N)Z). This is implemented by ensuring in the instruction +// selectors to not produce these instructions when speculation hardening +// is enabled. This pass will assert if it does encounter such an instruction. +// - On function call boundaries, the miss-speculation state is transferred from +// the taint register X16 to be encoded in the SP register as value 0. +// +// For the aspect of automatically hardening loads, using the taint register, +// (a.k.a. speculative load hardening, see +// https://llvm.org/docs/SpeculativeLoadHardening.html), the following +// implementation choices are made for AArch64: +// - Many of the optimizations described at +// https://llvm.org/docs/SpeculativeLoadHardening.html to harden fewer +// loads haven't been implemented yet - but for some of them there are +// FIXMEs in the code. +// - loads that load into general purpose (X or W) registers get hardened by +// masking the loaded data. For loads that load into other registers, the +// address loaded from gets hardened. It is expected that hardening the +// loaded data may be more efficient; but masking data in registers other +// than X or W is not easy and may result in being slower than just +// hardening the X address register loaded from. +// - On AArch64, CSDB instructions are inserted between the masking of the +// register and its first use, to ensure there's no non-control-flow +// speculation that might undermine the hardening mechanism. +// +// Future extensions/improvements could be: +// - Implement this functionality using full speculation barriers, akin to the +// x86-slh-lfence option. This may be more useful for the intrinsics-based +// approach than for the SLH approach to masking. +// Note that this pass already inserts the full speculation barriers if the +// function for some niche reason makes use of X16/W16. +// - no indirect branch misprediction gets protected/instrumented; but this +// could be done for some indirect branches, such as switch jump tables. +//===----------------------------------------------------------------------===// + +#include "AArch64InstrInfo.h" +#include "AArch64Subtarget.h" +#include "Utils/AArch64BaseInfo.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/Pass.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Target/TargetMachine.h" +#include <cassert> + +using namespace llvm; + +#define DEBUG_TYPE "aarch64-speculation-hardening" + +#define AARCH64_SPECULATION_HARDENING_NAME "AArch64 speculation hardening pass" + +cl::opt<bool> HardenLoads("aarch64-slh-loads", cl::Hidden, + cl::desc("Sanitize loads from memory."), + cl::init(true)); + +namespace { + +class AArch64SpeculationHardening : public MachineFunctionPass { +public: + const TargetInstrInfo *TII; + const TargetRegisterInfo *TRI; + + static char ID; + + AArch64SpeculationHardening() : MachineFunctionPass(ID) { + initializeAArch64SpeculationHardeningPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &Fn) override; + + StringRef getPassName() const override { + return AARCH64_SPECULATION_HARDENING_NAME; + } + +private: + unsigned MisspeculatingTaintReg; + unsigned MisspeculatingTaintReg32Bit; + bool UseControlFlowSpeculationBarrier; + BitVector RegsNeedingCSDBBeforeUse; + BitVector RegsAlreadyMasked; + + bool functionUsesHardeningRegister(MachineFunction &MF) const; + bool instrumentControlFlow(MachineBasicBlock &MBB); + bool endsWithCondControlFlow(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + AArch64CC::CondCode &CondCode) const; + void insertTrackingCode(MachineBasicBlock &SplitEdgeBB, + AArch64CC::CondCode &CondCode, DebugLoc DL) const; + void insertSPToRegTaintPropagation(MachineBasicBlock *MBB, + MachineBasicBlock::iterator MBBI) const; + void insertRegToSPTaintPropagation(MachineBasicBlock *MBB, + MachineBasicBlock::iterator MBBI, + unsigned TmpReg) const; + + bool slhLoads(MachineBasicBlock &MBB); + bool makeGPRSpeculationSafe(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineInstr &MI, unsigned Reg); + bool lowerSpeculationSafeValuePseudos(MachineBasicBlock &MBB); + bool expandSpeculationSafeValue(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI); + bool insertCSDB(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + DebugLoc DL); +}; + +} // end anonymous namespace + +char AArch64SpeculationHardening::ID = 0; + +INITIALIZE_PASS(AArch64SpeculationHardening, "aarch64-speculation-hardening", + AARCH64_SPECULATION_HARDENING_NAME, false, false) + +bool AArch64SpeculationHardening::endsWithCondControlFlow( + MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, + AArch64CC::CondCode &CondCode) const { + SmallVector<MachineOperand, 1> analyzeBranchCondCode; + if (TII->analyzeBranch(MBB, TBB, FBB, analyzeBranchCondCode, false)) + return false; + + // Ignore if the BB ends in an unconditional branch/fall-through. + if (analyzeBranchCondCode.empty()) + return false; + + // If the BB ends with a single conditional branch, FBB will be set to + // nullptr (see API docs for TII->analyzeBranch). For the rest of the + // analysis we want the FBB block to be set always. + assert(TBB != nullptr); + if (FBB == nullptr) + FBB = MBB.getFallThrough(); + + // If both the true and the false condition jump to the same basic block, + // there isn't need for any protection - whether the branch is speculated + // correctly or not, we end up executing the architecturally correct code. + if (TBB == FBB) + return false; + + assert(MBB.succ_size() == 2); + // translate analyzeBranchCondCode to CondCode. + assert(analyzeBranchCondCode.size() == 1 && "unknown Cond array format"); + CondCode = AArch64CC::CondCode(analyzeBranchCondCode[0].getImm()); + return true; +} + +void AArch64SpeculationHardening::insertTrackingCode( + MachineBasicBlock &SplitEdgeBB, AArch64CC::CondCode &CondCode, + DebugLoc DL) const { + if (UseControlFlowSpeculationBarrier) { + // insert full control flow speculation barrier (DSB SYS + ISB) + BuildMI(SplitEdgeBB, SplitEdgeBB.begin(), DL, TII->get(AArch64::ISB)) + .addImm(0xf); + BuildMI(SplitEdgeBB, SplitEdgeBB.begin(), DL, TII->get(AArch64::DSB)) + .addImm(0xf); + } else { + BuildMI(SplitEdgeBB, SplitEdgeBB.begin(), DL, TII->get(AArch64::CSELXr)) + .addDef(MisspeculatingTaintReg) + .addUse(MisspeculatingTaintReg) + .addUse(AArch64::XZR) + .addImm(CondCode); + SplitEdgeBB.addLiveIn(AArch64::NZCV); + } +} + +bool AArch64SpeculationHardening::instrumentControlFlow( + MachineBasicBlock &MBB) { + LLVM_DEBUG(dbgs() << "Instrument control flow tracking on MBB: " << MBB); + + bool Modified = false; + MachineBasicBlock *TBB = nullptr; + MachineBasicBlock *FBB = nullptr; + AArch64CC::CondCode CondCode; + + if (!endsWithCondControlFlow(MBB, TBB, FBB, CondCode)) { + LLVM_DEBUG(dbgs() << "... doesn't end with CondControlFlow\n"); + } else { + // Now insert: + // "CSEL MisSpeculatingR, MisSpeculatingR, XZR, cond" on the True edge and + // "CSEL MisSpeculatingR, MisSpeculatingR, XZR, Invertcond" on the False + // edge. + AArch64CC::CondCode InvCondCode = AArch64CC::getInvertedCondCode(CondCode); + + MachineBasicBlock *SplitEdgeTBB = MBB.SplitCriticalEdge(TBB, *this); + MachineBasicBlock *SplitEdgeFBB = MBB.SplitCriticalEdge(FBB, *this); + + assert(SplitEdgeTBB != nullptr); + assert(SplitEdgeFBB != nullptr); + + DebugLoc DL; + if (MBB.instr_end() != MBB.instr_begin()) + DL = (--MBB.instr_end())->getDebugLoc(); + + insertTrackingCode(*SplitEdgeTBB, CondCode, DL); + insertTrackingCode(*SplitEdgeFBB, InvCondCode, DL); + + LLVM_DEBUG(dbgs() << "SplitEdgeTBB: " << *SplitEdgeTBB << "\n"); + LLVM_DEBUG(dbgs() << "SplitEdgeFBB: " << *SplitEdgeFBB << "\n"); + Modified = true; + } + + // Perform correct code generation around function calls and before returns. + { + SmallVector<MachineInstr *, 4> ReturnInstructions; + SmallVector<MachineInstr *, 4> CallInstructions; + + for (MachineInstr &MI : MBB) { + if (MI.isReturn()) + ReturnInstructions.push_back(&MI); + else if (MI.isCall()) + CallInstructions.push_back(&MI); + } + + Modified |= + (ReturnInstructions.size() > 0) || (CallInstructions.size() > 0); + + for (MachineInstr *Return : ReturnInstructions) + insertRegToSPTaintPropagation(Return->getParent(), Return, AArch64::X17); + for (MachineInstr *Call : CallInstructions) { + // Just after the call: + MachineBasicBlock::iterator i = Call; + i++; + insertSPToRegTaintPropagation(Call->getParent(), i); + // Just before the call: + insertRegToSPTaintPropagation(Call->getParent(), Call, AArch64::X17); + } + } + + return Modified; +} + +void AArch64SpeculationHardening::insertSPToRegTaintPropagation( + MachineBasicBlock *MBB, MachineBasicBlock::iterator MBBI) const { + // If full control flow speculation barriers are used, emit a control flow + // barrier to block potential miss-speculation in flight coming in to this + // function. + if (UseControlFlowSpeculationBarrier) { + // insert full control flow speculation barrier (DSB SYS + ISB) + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::DSB)).addImm(0xf); + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::ISB)).addImm(0xf); + return; + } + + // CMP SP, #0 === SUBS xzr, SP, #0 + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::SUBSXri)) + .addDef(AArch64::XZR) + .addUse(AArch64::SP) + .addImm(0) + .addImm(0); // no shift + // CSETM x16, NE === CSINV x16, xzr, xzr, EQ + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::CSINVXr)) + .addDef(MisspeculatingTaintReg) + .addUse(AArch64::XZR) + .addUse(AArch64::XZR) + .addImm(AArch64CC::EQ); +} + +void AArch64SpeculationHardening::insertRegToSPTaintPropagation( + MachineBasicBlock *MBB, MachineBasicBlock::iterator MBBI, + unsigned TmpReg) const { + // If full control flow speculation barriers are used, there will not be + // miss-speculation when returning from this function, and therefore, also + // no need to encode potential miss-speculation into the stack pointer. + if (UseControlFlowSpeculationBarrier) + return; + + // mov Xtmp, SP === ADD Xtmp, SP, #0 + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::ADDXri)) + .addDef(TmpReg) + .addUse(AArch64::SP) + .addImm(0) + .addImm(0); // no shift + // and Xtmp, Xtmp, TaintReg === AND Xtmp, Xtmp, TaintReg, #0 + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::ANDXrs)) + .addDef(TmpReg, RegState::Renamable) + .addUse(TmpReg, RegState::Kill | RegState::Renamable) + .addUse(MisspeculatingTaintReg, RegState::Kill) + .addImm(0); + // mov SP, Xtmp === ADD SP, Xtmp, #0 + BuildMI(*MBB, MBBI, DebugLoc(), TII->get(AArch64::ADDXri)) + .addDef(AArch64::SP) + .addUse(TmpReg, RegState::Kill) + .addImm(0) + .addImm(0); // no shift +} + +bool AArch64SpeculationHardening::functionUsesHardeningRegister( + MachineFunction &MF) const { + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + // treat function calls specially, as the hardening register does not + // need to remain live across function calls. + if (MI.isCall()) + continue; + if (MI.readsRegister(MisspeculatingTaintReg, TRI) || + MI.modifiesRegister(MisspeculatingTaintReg, TRI)) + return true; + } + } + return false; +} + +// Make GPR register Reg speculation-safe by putting it through the +// SpeculationSafeValue pseudo instruction, if we can't prove that +// the value in the register has already been hardened. +bool AArch64SpeculationHardening::makeGPRSpeculationSafe( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineInstr &MI, + unsigned Reg) { + assert(AArch64::GPR32allRegClass.contains(Reg) || + AArch64::GPR64allRegClass.contains(Reg)); + + // Loads cannot directly load a value into the SP (nor WSP). + // Therefore, if Reg is SP or WSP, it is because the instruction loads from + // the stack through the stack pointer. + // + // Since the stack pointer is never dynamically controllable, don't harden it. + if (Reg == AArch64::SP || Reg == AArch64::WSP) + return false; + + // Do not harden the register again if already hardened before. + if (RegsAlreadyMasked[Reg]) + return false; + + const bool Is64Bit = AArch64::GPR64allRegClass.contains(Reg); + LLVM_DEBUG(dbgs() << "About to harden register : " << Reg << "\n"); + BuildMI(MBB, MBBI, MI.getDebugLoc(), + TII->get(Is64Bit ? AArch64::SpeculationSafeValueX + : AArch64::SpeculationSafeValueW)) + .addDef(Reg) + .addUse(Reg); + RegsAlreadyMasked.set(Reg); + return true; +} + +bool AArch64SpeculationHardening::slhLoads(MachineBasicBlock &MBB) { + bool Modified = false; + + LLVM_DEBUG(dbgs() << "slhLoads running on MBB: " << MBB); + + RegsAlreadyMasked.reset(); + + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + MachineBasicBlock::iterator NextMBBI; + for (; MBBI != E; MBBI = NextMBBI) { + MachineInstr &MI = *MBBI; + NextMBBI = std::next(MBBI); + // Only harden loaded values or addresses used in loads. + if (!MI.mayLoad()) + continue; + + LLVM_DEBUG(dbgs() << "About to harden: " << MI); + + // For general purpose register loads, harden the registers loaded into. + // For other loads, harden the address loaded from. + // Masking the loaded value is expected to result in less performance + // overhead, as the load can still execute speculatively in comparison to + // when the address loaded from gets masked. However, masking is only + // easy to do efficiently on GPR registers, so for loads into non-GPR + // registers (e.g. floating point loads), mask the address loaded from. + bool AllDefsAreGPR = llvm::all_of(MI.defs(), [&](MachineOperand &Op) { + return Op.isReg() && (AArch64::GPR32allRegClass.contains(Op.getReg()) || + AArch64::GPR64allRegClass.contains(Op.getReg())); + }); + // FIXME: it might be a worthwhile optimization to not mask loaded + // values if all the registers involved in address calculation are already + // hardened, leading to this load not able to execute on a miss-speculated + // path. + bool HardenLoadedData = AllDefsAreGPR; + bool HardenAddressLoadedFrom = !HardenLoadedData; + + // First remove registers from AlreadyMaskedRegisters if their value is + // updated by this instruction - it makes them contain a new value that is + // not guaranteed to already have been masked. + for (MachineOperand Op : MI.defs()) + for (MCRegAliasIterator AI(Op.getReg(), TRI, true); AI.isValid(); ++AI) + RegsAlreadyMasked.reset(*AI); + + // FIXME: loads from the stack with an immediate offset from the stack + // pointer probably shouldn't be hardened, which could result in a + // significant optimization. See section "Don’t check loads from + // compile-time constant stack offsets", in + // https://llvm.org/docs/SpeculativeLoadHardening.html + + if (HardenLoadedData) + for (auto Def : MI.defs()) { + if (Def.isDead()) + // Do not mask a register that is not used further. + continue; + // FIXME: For pre/post-increment addressing modes, the base register + // used in address calculation is also defined by this instruction. + // It might be a worthwhile optimization to not harden that + // base register increment/decrement when the increment/decrement is + // an immediate. + Modified |= makeGPRSpeculationSafe(MBB, NextMBBI, MI, Def.getReg()); + } + + if (HardenAddressLoadedFrom) + for (auto Use : MI.uses()) { + if (!Use.isReg()) + continue; + unsigned Reg = Use.getReg(); + // Some loads of floating point data have implicit defs/uses on a + // super register of that floating point data. Some examples: + // $s0 = LDRSui $sp, 22, implicit-def $q0 + // $q0 = LD1i64 $q0, 1, renamable $x0 + // We need to filter out these uses for non-GPR register which occur + // because the load partially fills a non-GPR register with the loaded + // data. Just skipping all non-GPR registers is safe (for now) as all + // AArch64 load instructions only use GPR registers to perform the + // address calculation. FIXME: However that might change once we can + // produce SVE gather instructions. + if (!(AArch64::GPR32allRegClass.contains(Reg) || + AArch64::GPR64allRegClass.contains(Reg))) + continue; + Modified |= makeGPRSpeculationSafe(MBB, MBBI, MI, Reg); + } + } + return Modified; +} + +/// \brief If MBBI references a pseudo instruction that should be expanded +/// here, do the expansion and return true. Otherwise return false. +bool AArch64SpeculationHardening::expandSpeculationSafeValue( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) { + MachineInstr &MI = *MBBI; + unsigned Opcode = MI.getOpcode(); + bool Is64Bit = true; + + switch (Opcode) { + default: + break; + case AArch64::SpeculationSafeValueW: + Is64Bit = false; + LLVM_FALLTHROUGH; + case AArch64::SpeculationSafeValueX: + // Just remove the SpeculationSafe pseudo's if control flow + // miss-speculation isn't happening because we're already inserting barriers + // to guarantee that. + if (!UseControlFlowSpeculationBarrier) { + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + // Mark this register and all its aliasing registers as needing to be + // value speculation hardened before its next use, by using a CSDB + // barrier instruction. + for (MachineOperand Op : MI.defs()) + for (MCRegAliasIterator AI(Op.getReg(), TRI, true); AI.isValid(); ++AI) + RegsNeedingCSDBBeforeUse.set(*AI); + + // Mask off with taint state. + BuildMI(MBB, MBBI, MI.getDebugLoc(), + Is64Bit ? TII->get(AArch64::ANDXrs) : TII->get(AArch64::ANDWrs)) + .addDef(DstReg) + .addUse(SrcReg, RegState::Kill) + .addUse(Is64Bit ? MisspeculatingTaintReg + : MisspeculatingTaintReg32Bit) + .addImm(0); + } + MI.eraseFromParent(); + return true; + } + return false; +} + +bool AArch64SpeculationHardening::insertCSDB(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + DebugLoc DL) { + assert(!UseControlFlowSpeculationBarrier && "No need to insert CSDBs when " + "control flow miss-speculation " + "is already blocked"); + // insert data value speculation barrier (CSDB) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::HINT)).addImm(0x14); + RegsNeedingCSDBBeforeUse.reset(); + return true; +} + +bool AArch64SpeculationHardening::lowerSpeculationSafeValuePseudos( + MachineBasicBlock &MBB) { + bool Modified = false; + + RegsNeedingCSDBBeforeUse.reset(); + + // The following loop iterates over all instructions in the basic block, + // and performs 2 operations: + // 1. Insert a CSDB at this location if needed. + // 2. Expand the SpeculationSafeValuePseudo if the current instruction is + // one. + // + // The insertion of the CSDB is done as late as possible (i.e. just before + // the use of a masked register), in the hope that that will reduce the + // total number of CSDBs in a block when there are multiple masked registers + // in the block. + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + DebugLoc DL; + while (MBBI != E) { + MachineInstr &MI = *MBBI; + DL = MI.getDebugLoc(); + MachineBasicBlock::iterator NMBBI = std::next(MBBI); + + // First check if a CSDB needs to be inserted due to earlier registers + // that were masked and that are used by the next instruction. + // Also emit the barrier on any potential control flow changes. + bool NeedToEmitBarrier = false; + if (RegsNeedingCSDBBeforeUse.any() && (MI.isCall() || MI.isTerminator())) + NeedToEmitBarrier = true; + if (!NeedToEmitBarrier) + for (MachineOperand Op : MI.uses()) + if (Op.isReg() && RegsNeedingCSDBBeforeUse[Op.getReg()]) { + NeedToEmitBarrier = true; + break; + } + + if (NeedToEmitBarrier) + Modified |= insertCSDB(MBB, MBBI, DL); + + Modified |= expandSpeculationSafeValue(MBB, MBBI); + + MBBI = NMBBI; + } + + if (RegsNeedingCSDBBeforeUse.any()) + Modified |= insertCSDB(MBB, MBBI, DL); + + return Modified; +} + +bool AArch64SpeculationHardening::runOnMachineFunction(MachineFunction &MF) { + if (!MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening)) + return false; + + MisspeculatingTaintReg = AArch64::X16; + MisspeculatingTaintReg32Bit = AArch64::W16; + TII = MF.getSubtarget().getInstrInfo(); + TRI = MF.getSubtarget().getRegisterInfo(); + RegsNeedingCSDBBeforeUse.resize(TRI->getNumRegs()); + RegsAlreadyMasked.resize(TRI->getNumRegs()); + UseControlFlowSpeculationBarrier = functionUsesHardeningRegister(MF); + + bool Modified = false; + + // Step 1: Enable automatic insertion of SpeculationSafeValue. + if (HardenLoads) { + LLVM_DEBUG( + dbgs() << "***** AArch64SpeculationHardening - automatic insertion of " + "SpeculationSafeValue intrinsics *****\n"); + for (auto &MBB : MF) + Modified |= slhLoads(MBB); + } + + // 2.a Add instrumentation code to function entry and exits. + LLVM_DEBUG( + dbgs() + << "***** AArch64SpeculationHardening - track control flow *****\n"); + + SmallVector<MachineBasicBlock *, 2> EntryBlocks; + EntryBlocks.push_back(&MF.front()); + for (const LandingPadInfo &LPI : MF.getLandingPads()) + EntryBlocks.push_back(LPI.LandingPadBlock); + for (auto Entry : EntryBlocks) + insertSPToRegTaintPropagation( + Entry, Entry->SkipPHIsLabelsAndDebug(Entry->begin())); + + // 2.b Add instrumentation code to every basic block. + for (auto &MBB : MF) + Modified |= instrumentControlFlow(MBB); + + LLVM_DEBUG(dbgs() << "***** AArch64SpeculationHardening - Lowering " + "SpeculationSafeValue Pseudos *****\n"); + // Step 3: Lower SpeculationSafeValue pseudo instructions. + for (auto &MBB : MF) + Modified |= lowerSpeculationSafeValuePseudos(MBB); + + return Modified; +} + +/// \brief Returns an instance of the pseudo instruction expansion pass. +FunctionPass *llvm::createAArch64SpeculationHardeningPass() { + return new AArch64SpeculationHardening(); +} diff --git a/lib/Target/AArch64/AArch64StorePairSuppress.cpp b/lib/Target/AArch64/AArch64StorePairSuppress.cpp index fc7b5984fe3e..d5643d384283 100644 --- a/lib/Target/AArch64/AArch64StorePairSuppress.cpp +++ b/lib/Target/AArch64/AArch64StorePairSuppress.cpp @@ -148,9 +148,11 @@ bool AArch64StorePairSuppress::runOnMachineFunction(MachineFunction &MF) { for (auto &MI : MBB) { if (!isNarrowFPStore(MI)) continue; - unsigned BaseReg; + MachineOperand *BaseOp; int64_t Offset; - if (TII->getMemOpBaseRegImmOfs(MI, BaseReg, Offset, TRI)) { + if (TII->getMemOperandWithOffset(MI, BaseOp, Offset, TRI) && + BaseOp->isReg()) { + unsigned BaseReg = BaseOp->getReg(); if (PrevBaseReg == BaseReg) { // If this block can take STPs, skip ahead to the next block. if (!SuppressSTP && shouldAddSTPToBlock(MI.getParent())) diff --git a/lib/Target/AArch64/AArch64Subtarget.cpp b/lib/Target/AArch64/AArch64Subtarget.cpp index 04bb90d30d6d..dd30d25b2b50 100644 --- a/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/lib/Target/AArch64/AArch64Subtarget.cpp @@ -14,13 +14,13 @@ #include "AArch64Subtarget.h" #include "AArch64.h" -#include "AArch64InstrInfo.h" -#include "AArch64PBQPRegAlloc.h" -#include "AArch64TargetMachine.h" - #include "AArch64CallLowering.h" +#include "AArch64InstrInfo.h" #include "AArch64LegalizerInfo.h" +#include "AArch64PBQPRegAlloc.h" #include "AArch64RegisterBankInfo.h" +#include "AArch64TargetMachine.h" +#include "MCTargetDesc/AArch64AddressingModes.h" #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/CodeGen/MachineScheduler.h" #include "llvm/IR/GlobalValue.h" @@ -67,16 +67,30 @@ void AArch64Subtarget::initializeProperties() { // this in the future so we can specify it together with the subtarget // features. switch (ARMProcFamily) { + case Others: + break; + case CortexA35: + break; + case CortexA53: + PrefFunctionAlignment = 3; + break; + case CortexA55: + break; + case CortexA57: + MaxInterleaveFactor = 4; + PrefFunctionAlignment = 4; + break; + case CortexA72: + case CortexA73: + case CortexA75: + PrefFunctionAlignment = 4; + break; case Cyclone: CacheLineSize = 64; PrefetchDistance = 280; MinPrefetchStride = 2048; MaxPrefetchIterationsAhead = 3; break; - case CortexA57: - MaxInterleaveFactor = 4; - PrefFunctionAlignment = 4; - break; case ExynosM1: MaxInterleaveFactor = 4; MaxJumpTableSize = 8; @@ -98,11 +112,6 @@ void AArch64Subtarget::initializeProperties() { MinPrefetchStride = 2048; MaxPrefetchIterationsAhead = 8; break; - case Saphira: - MaxInterleaveFactor = 4; - // FIXME: remove this to enable 64-bit SLP if performance looks good. - MinVectorRegisterBitWidth = 128; - break; case Kryo: MaxInterleaveFactor = 4; VectorInsertExtractBaseCost = 2; @@ -113,6 +122,11 @@ void AArch64Subtarget::initializeProperties() { // FIXME: remove this to enable 64-bit SLP if performance looks good. MinVectorRegisterBitWidth = 128; break; + case Saphira: + MaxInterleaveFactor = 4; + // FIXME: remove this to enable 64-bit SLP if performance looks good. + MinVectorRegisterBitWidth = 128; + break; case ThunderX2T99: CacheLineSize = 64; PrefFunctionAlignment = 3; @@ -134,17 +148,11 @@ void AArch64Subtarget::initializeProperties() { // FIXME: remove this to enable 64-bit SLP if performance looks good. MinVectorRegisterBitWidth = 128; break; - case CortexA35: break; - case CortexA53: - PrefFunctionAlignment = 3; - break; - case CortexA55: break; - case CortexA72: - case CortexA73: - case CortexA75: + case TSV110: + CacheLineSize = 64; PrefFunctionAlignment = 4; + PrefLoopAlignment = 2; break; - case Others: break; } } @@ -152,10 +160,15 @@ AArch64Subtarget::AArch64Subtarget(const Triple &TT, const std::string &CPU, const std::string &FS, const TargetMachine &TM, bool LittleEndian) : AArch64GenSubtargetInfo(TT, CPU, FS), - ReserveX18(AArch64::isX18ReservedByDefault(TT)), IsLittle(LittleEndian), + ReserveXRegister(AArch64::GPR64commonRegClass.getNumRegs()), + CustomCallSavedXRegs(AArch64::GPR64commonRegClass.getNumRegs()), + IsLittle(LittleEndian), TargetTriple(TT), FrameLowering(), InstrInfo(initializeSubtargetDependencies(FS, CPU)), TSInfo(), TLInfo(TM, *this) { + if (AArch64::isX18ReservedByDefault(TT)) + ReserveXRegister.set(18); + CallLoweringInfo.reset(new AArch64CallLowering(*getTargetLowering())); Legalizer.reset(new AArch64LegalizerInfo(*this)); @@ -196,18 +209,22 @@ AArch64Subtarget::ClassifyGlobalReference(const GlobalValue *GV, if (TM.getCodeModel() == CodeModel::Large && isTargetMachO()) return AArch64II::MO_GOT; - unsigned Flags = GV->hasDLLImportStorageClass() ? AArch64II::MO_DLLIMPORT - : AArch64II::MO_NO_FLAG; - - if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV)) - return AArch64II::MO_GOT | Flags; + if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV)) { + if (GV->hasDLLImportStorageClass()) + return AArch64II::MO_GOT | AArch64II::MO_DLLIMPORT; + if (getTargetTriple().isOSWindows()) + return AArch64II::MO_GOT | AArch64II::MO_COFFSTUB; + return AArch64II::MO_GOT; + } // The small code model's direct accesses use ADRP, which cannot // necessarily produce the value 0 (if the code is above 4GB). - if (useSmallAddressing() && GV->hasExternalWeakLinkage()) - return AArch64II::MO_GOT | Flags; + // Same for the tiny code model, where we have a pc relative LDR. + if ((useSmallAddressing() || TM.getCodeModel() == CodeModel::Tiny) && + GV->hasExternalWeakLinkage()) + return AArch64II::MO_GOT; - return Flags; + return AArch64II::MO_NO_FLAG; } unsigned char AArch64Subtarget::classifyGlobalFunctionReference( @@ -265,7 +282,7 @@ void AArch64Subtarget::mirFileLoaded(MachineFunction &MF) const { // We usually compute max call frame size after ISel. Do the computation now // if the .mir file didn't specify it. Note that this will probably give you // bogus values after PEI has eliminated the callframe setup/destroy pseudo - // instructions, specify explicitely if you need it to be correct. + // instructions, specify explicitly if you need it to be correct. MachineFrameInfo &MFI = MF.getFrameInfo(); if (!MFI.isMaxCallFrameSizeComputed()) MFI.computeMaxCallFrameSize(MF); diff --git a/lib/Target/AArch64/AArch64Subtarget.h b/lib/Target/AArch64/AArch64Subtarget.h index 5af4c0dd9c19..82f7bb755951 100644 --- a/lib/Target/AArch64/AArch64Subtarget.h +++ b/lib/Target/AArch64/AArch64Subtarget.h @@ -56,7 +56,8 @@ public: ThunderX, ThunderXT81, ThunderXT83, - ThunderXT88 + ThunderXT88, + TSV110 }; protected: @@ -67,6 +68,7 @@ protected: bool HasV8_2aOps = false; bool HasV8_3aOps = false; bool HasV8_4aOps = false; + bool HasV8_5aOps = false; bool HasFPARMv8 = false; bool HasNEON = false; @@ -78,8 +80,36 @@ protected: bool HasRDM = false; bool HasPerfMon = false; bool HasFullFP16 = false; + bool HasFP16FML = false; bool HasSPE = false; + // ARMv8.1 extensions + bool HasVH = false; + bool HasPAN = false; + bool HasLOR = false; + + // ARMv8.2 extensions + bool HasPsUAO = false; + bool HasPAN_RWV = false; + bool HasCCPP = false; + + // ARMv8.3 extensions + bool HasPA = false; + bool HasJS = false; + bool HasCCIDX = false; + bool HasComplxNum = false; + + // ARMv8.4 extensions + bool HasNV = false; + bool HasRASv8_4 = false; + bool HasMPAM = false; + bool HasDIT = false; + bool HasTRACEV8_4 = false; + bool HasAM = false; + bool HasSEL2 = false; + bool HasTLB_RMI = false; + bool HasFMI = false; + bool HasRCPC_IMMO = false; // ARMv8.4 Crypto extensions bool HasSM4 = true; bool HasSHA3 = true; @@ -92,11 +122,25 @@ protected: bool HasRCPC = false; bool HasAggressiveFMA = false; + // Armv8.5-A Extensions + bool HasAlternativeNZCV = false; + bool HasFRInt3264 = false; + bool HasSpecRestrict = false; + bool HasSSBS = false; + bool HasSB = false; + bool HasPredRes = false; + bool HasCCDP = false; + bool HasBTI = false; + bool HasRandGen = false; + bool HasMTE = false; + // HasZeroCycleRegMove - Has zero-cycle register mov instructions. bool HasZeroCycleRegMove = false; // HasZeroCycleZeroing - Has zero-cycle zeroing instructions. bool HasZeroCycleZeroing = false; + bool HasZeroCycleZeroingGP = false; + bool HasZeroCycleZeroingFP = false; bool HasZeroCycleZeroingFPWorkaround = false; // StrictAlign - Disallow unaligned memory accesses. @@ -122,10 +166,13 @@ protected: bool HasArithmeticCbzFusion = false; bool HasFuseAddress = false; bool HasFuseAES = false; + bool HasFuseArithmeticLogic = false; bool HasFuseCCSelect = false; + bool HasFuseCryptoEOR = false; bool HasFuseLiterals = false; bool DisableLatencySchedHeuristic = false; bool UseRSqrt = false; + bool Force32BitJumpTables = false; uint8_t MaxInterleaveFactor = 2; uint8_t VectorInsertExtractBaseCost = 3; uint16_t CacheLineSize = 0; @@ -137,11 +184,11 @@ protected: unsigned MaxJumpTableSize = 0; unsigned WideningBaseCost = 0; - // ReserveX18 - X18 is not available as a general purpose register. - bool ReserveX18; + // ReserveXRegister[i] - X#i is not available as a general purpose register. + BitVector ReserveXRegister; - // ReserveX20 - X20 is not available as a general purpose register. - bool ReserveX20 = false; + // CustomCallUsedXRegister[i] - X#i call saved. + BitVector CustomCallSavedXRegs; bool IsLittle; @@ -211,10 +258,13 @@ public: bool hasV8_2aOps() const { return HasV8_2aOps; } bool hasV8_3aOps() const { return HasV8_3aOps; } bool hasV8_4aOps() const { return HasV8_4aOps; } + bool hasV8_5aOps() const { return HasV8_5aOps; } bool hasZeroCycleRegMove() const { return HasZeroCycleRegMove; } - bool hasZeroCycleZeroing() const { return HasZeroCycleZeroing; } + bool hasZeroCycleZeroingGP() const { return HasZeroCycleZeroingGP; } + + bool hasZeroCycleZeroingFP() const { return HasZeroCycleZeroingFP; } bool hasZeroCycleZeroingFPWorkaround() const { return HasZeroCycleZeroingFPWorkaround; @@ -228,8 +278,12 @@ public: return MinVectorRegisterBitWidth; } - bool isX18Reserved() const { return ReserveX18; } - bool isX20Reserved() const { return ReserveX20; } + bool isXRegisterReserved(size_t i) const { return ReserveXRegister[i]; } + unsigned getNumXRegisterReserved() const { return ReserveXRegister.count(); } + bool isXRegCustomCalleeSaved(size_t i) const { + return CustomCallSavedXRegs[i]; + } + bool hasCustomCallingConv() const { return CustomCallSavedXRegs.any(); } bool hasFPARMv8() const { return HasFPARMv8; } bool hasNEON() const { return HasNEON; } bool hasCrypto() const { return HasCrypto; } @@ -258,16 +312,20 @@ public: bool hasArithmeticCbzFusion() const { return HasArithmeticCbzFusion; } bool hasFuseAddress() const { return HasFuseAddress; } bool hasFuseAES() const { return HasFuseAES; } + bool hasFuseArithmeticLogic() const { return HasFuseArithmeticLogic; } bool hasFuseCCSelect() const { return HasFuseCCSelect; } + bool hasFuseCryptoEOR() const { return HasFuseCryptoEOR; } bool hasFuseLiterals() const { return HasFuseLiterals; } /// Return true if the CPU supports any kind of instruction fusion. bool hasFusion() const { return hasArithmeticBccFusion() || hasArithmeticCbzFusion() || - hasFuseAES() || hasFuseCCSelect() || hasFuseLiterals(); + hasFuseAES() || hasFuseArithmeticLogic() || + hasFuseCCSelect() || hasFuseLiterals(); } bool useRSqrt() const { return UseRSqrt; } + bool force32BitJumpTables() const { return Force32BitJumpTables; } unsigned getMaxInterleaveFactor() const { return MaxInterleaveFactor; } unsigned getVectorInsertExtractBaseCost() const { return VectorInsertExtractBaseCost; @@ -291,11 +349,22 @@ public: bool hasPerfMon() const { return HasPerfMon; } bool hasFullFP16() const { return HasFullFP16; } + bool hasFP16FML() const { return HasFP16FML; } bool hasSPE() const { return HasSPE; } bool hasLSLFast() const { return HasLSLFast; } bool hasSVE() const { return HasSVE; } bool hasRCPC() const { return HasRCPC; } bool hasAggressiveFMA() const { return HasAggressiveFMA; } + bool hasAlternativeNZCV() const { return HasAlternativeNZCV; } + bool hasFRInt3264() const { return HasFRInt3264; } + bool hasSpecRestrict() const { return HasSpecRestrict; } + bool hasSSBS() const { return HasSSBS; } + bool hasSB() const { return HasSB; } + bool hasPredRes() const { return HasPredRes; } + bool hasCCDP() const { return HasCCDP; } + bool hasBTI() const { return HasBTI; } + bool hasRandGen() const { return HasRandGen; } + bool hasMTE() const { return HasMTE; } bool isLittleEndian() const { return IsLittle; } @@ -312,6 +381,30 @@ public: bool useAA() const override { return UseAA; } + bool hasVH() const { return HasVH; } + bool hasPAN() const { return HasPAN; } + bool hasLOR() const { return HasLOR; } + + bool hasPsUAO() const { return HasPsUAO; } + bool hasPAN_RWV() const { return HasPAN_RWV; } + bool hasCCPP() const { return HasCCPP; } + + bool hasPA() const { return HasPA; } + bool hasJS() const { return HasJS; } + bool hasCCIDX() const { return HasCCIDX; } + bool hasComplxNum() const { return HasComplxNum; } + + bool hasNV() const { return HasNV; } + bool hasRASv8_4() const { return HasRASv8_4; } + bool hasMPAM() const { return HasMPAM; } + bool hasDIT() const { return HasDIT; } + bool hasTRACEV8_4() const { return HasTRACEV8_4; } + bool hasAM() const { return HasAM; } + bool hasSEL2() const { return HasSEL2; } + bool hasTLB_RMI() const { return HasTLB_RMI; } + bool hasFMI() const { return HasFMI; } + bool hasRCPC_IMMO() const { return HasRCPC_IMMO; } + bool useSmallAddressing() const { switch (TLInfo.getTargetMachine().getCodeModel()) { case CodeModel::Kernel: @@ -346,6 +439,8 @@ public: bool isCallingConvWin64(CallingConv::ID CC) const { switch (CC) { case CallingConv::C: + case CallingConv::Fast: + case CallingConv::Swift: return isTargetWindows(); case CallingConv::Win64: return true; diff --git a/lib/Target/AArch64/AArch64SystemOperands.td b/lib/Target/AArch64/AArch64SystemOperands.td index 8acd32533eea..a804fb11175b 100644 --- a/lib/Target/AArch64/AArch64SystemOperands.td +++ b/lib/Target/AArch64/AArch64SystemOperands.td @@ -15,6 +15,25 @@ include "llvm/TableGen/SearchableTable.td" //===----------------------------------------------------------------------===// +// Features that, for the compiler, only enable system operands and PStates +//===----------------------------------------------------------------------===// + +def HasCCPP : Predicate<"Subtarget->hasCCPP()">, + AssemblerPredicate<"FeatureCCPP", "ccpp">; + +def HasPAN : Predicate<"Subtarget->hasPAN()">, + AssemblerPredicate<"FeaturePAN", + "ARM v8.1 Privileged Access-Never extension">; + +def HasPsUAO : Predicate<"Subtarget->hasPsUAO()">, + AssemblerPredicate<"FeaturePsUAO", + "ARM v8.2 UAO PState extension (psuao)">; + +def HasPAN_RWV : Predicate<"Subtarget->hasPAN_RWV()">, + AssemblerPredicate<"FeaturePAN_RWV", + "ARM v8.2 PAN AT S1E1R and AT S1E1W Variation">; + +//===----------------------------------------------------------------------===// // AT (address translate) instruction options. //===----------------------------------------------------------------------===// @@ -45,7 +64,7 @@ def : AT<"S12E1W", 0b100, 0b0111, 0b1000, 0b101>; def : AT<"S12E0R", 0b100, 0b0111, 0b1000, 0b110>; def : AT<"S12E0W", 0b100, 0b0111, 0b1000, 0b111>; -let Requires = [{ {AArch64::HasV8_2aOps} }] in { +let Requires = [{ {AArch64::FeaturePAN_RWV} }] in { def : AT<"S1E1RP", 0b000, 0b0111, 0b1001, 0b000>; def : AT<"S1E1WP", 0b000, 0b0111, 0b1001, 0b001>; } @@ -102,9 +121,33 @@ def : DC<"CVAU", 0b011, 0b0111, 0b1011, 0b001>; def : DC<"CIVAC", 0b011, 0b0111, 0b1110, 0b001>; def : DC<"CISW", 0b000, 0b0111, 0b1110, 0b010>; -let Requires = [{ {AArch64::HasV8_2aOps} }] in +let Requires = [{ {AArch64::FeatureCCPP} }] in def : DC<"CVAP", 0b011, 0b0111, 0b1100, 0b001>; +let Requires = [{ {AArch64::FeatureCacheDeepPersist} }] in +def : DC<"CVADP", 0b011, 0b0111, 0b1101, 0b001>; + +let Requires = [{ {AArch64::FeatureMTE} }] in { +def : DC<"IGVAC", 0b000, 0b0111, 0b0110, 0b011>; +def : DC<"IGSW", 0b000, 0b0111, 0b0110, 0b100>; +def : DC<"CGSW", 0b000, 0b0111, 0b1010, 0b100>; +def : DC<"CIGSW", 0b000, 0b0111, 0b1110, 0b100>; +def : DC<"CGVAC", 0b011, 0b0111, 0b1010, 0b011>; +def : DC<"CGVAP", 0b011, 0b0111, 0b1100, 0b011>; +def : DC<"CGVADP", 0b011, 0b0111, 0b1101, 0b011>; +def : DC<"CIGVAC", 0b011, 0b0111, 0b1110, 0b011>; +def : DC<"GVA", 0b011, 0b0111, 0b0100, 0b011>; +def : DC<"IGDVAC", 0b000, 0b0111, 0b0110, 0b101>; +def : DC<"IGDSW", 0b000, 0b0111, 0b0110, 0b110>; +def : DC<"CGDSW", 0b000, 0b0111, 0b1010, 0b110>; +def : DC<"CIGDSW", 0b000, 0b0111, 0b1110, 0b110>; +def : DC<"CGDVAC", 0b011, 0b0111, 0b1010, 0b101>; +def : DC<"CGDVAP", 0b011, 0b0111, 0b1100, 0b101>; +def : DC<"CGDVADP", 0b011, 0b0111, 0b1101, 0b101>; +def : DC<"CIGDVAC", 0b011, 0b0111, 0b1110, 0b101>; +def : DC<"GZVA", 0b011, 0b0111, 0b0100, 0b100>; +} + //===----------------------------------------------------------------------===// // IC (instruction cache maintenance) instruction options. //===----------------------------------------------------------------------===// @@ -154,7 +197,7 @@ class TSB<string name, bits<4> encoding> : SearchableTable{ bits<4> Encoding; let Encoding = encoding; - code Requires = [{ {AArch64::HasV8_4aOps} }]; + code Requires = [{ {AArch64::FeatureTRACEV8_4} }]; } def : TSB<"csync", 0>; @@ -290,14 +333,21 @@ def : PState<"SPSel", 0b00101>; def : PState<"DAIFSet", 0b11110>; def : PState<"DAIFClr", 0b11111>; // v8.1a "Privileged Access Never" extension-specific PStates -let Requires = [{ {AArch64::HasV8_1aOps} }] in +let Requires = [{ {AArch64::FeaturePAN} }] in def : PState<"PAN", 0b00100>; + // v8.2a "User Access Override" extension-specific PStates -let Requires = [{ {AArch64::HasV8_2aOps} }] in +let Requires = [{ {AArch64::FeaturePsUAO} }] in def : PState<"UAO", 0b00011>; // v8.4a timining insensitivity of data processing instructions -let Requires = [{ {AArch64::HasV8_4aOps} }] in +let Requires = [{ {AArch64::FeatureDIT} }] in def : PState<"DIT", 0b11010>; +// v8.5a Spectre Mitigation +let Requires = [{ {AArch64::FeatureSSBS} }] in +def : PState<"SSBS", 0b11001>; +// v8.5a Memory Tagging Extension +let Requires = [{ {AArch64::FeatureMTE} }] in +def : PState<"TCO", 0b11100>; //===----------------------------------------------------------------------===// // PSB instruction options. @@ -315,6 +365,23 @@ class PSB<string name, bits<5> encoding> : SearchableTable { def : PSB<"csync", 0x11>; //===----------------------------------------------------------------------===// +// BTI instruction options. +//===----------------------------------------------------------------------===// + +class BTI<string name, bits<2> encoding> : SearchableTable { + let SearchableFields = ["Name", "Encoding"]; + let EnumValueField = "Encoding"; + + string Name = name; + bits<2> Encoding; + let Encoding = encoding; +} + +def : BTI<"c", 0b01>; +def : BTI<"j", 0b10>; +def : BTI<"jc", 0b11>; + +//===----------------------------------------------------------------------===// // TLBI (translation lookaside buffer invalidate) instruction options. //===----------------------------------------------------------------------===// @@ -366,8 +433,9 @@ def : TLBI<"VALE3", 0b110, 0b1000, 0b0111, 0b101>; def : TLBI<"VMALLS12E1", 0b100, 0b1000, 0b0111, 0b110, 0>; def : TLBI<"VAALE1", 0b000, 0b1000, 0b0111, 0b111>; +// Armv8.4-A Translation Lookaside Buffer Instructions (TLBI) +let Requires = [{ {AArch64::FeatureTLB_RMI} }] in { // Armv8.4-A Outer Sharable TLB Maintenance instructions: -let Requires = [{ {AArch64::HasV8_4aOps} }] in { // op1 CRn CRm op2 def : TLBI<"VMALLE1OS", 0b000, 0b1000, 0b0001, 0b000, 0>; def : TLBI<"VAE1OS", 0b000, 0b1000, 0b0001, 0b001>; @@ -418,6 +486,23 @@ def : TLBI<"RVAE3IS", 0b110, 0b1000, 0b0010, 0b001>; def : TLBI<"RVALE3IS", 0b110, 0b1000, 0b0010, 0b101>; def : TLBI<"RVAE3OS", 0b110, 0b1000, 0b0101, 0b001>; def : TLBI<"RVALE3OS", 0b110, 0b1000, 0b0101, 0b101>; +} //FeatureTLB_RMI + +// Armv8.5-A Prediction Restriction by Context instruction options: +class PRCTX<string name, bits<4> crm> : SearchableTable { + let SearchableFields = ["Name", "Encoding"]; + let EnumValueField = "Encoding"; + + string Name = name; + bits<11> Encoding; + let Encoding{10-4} = 0b0110111; + let Encoding{3-0} = crm; + bit NeedsReg = 1; + code Requires = [{ {} }]; +} + +let Requires = [{ {AArch64::FeaturePredRes} }] in { +def : PRCTX<"RCTX", 0b0011>; } //===----------------------------------------------------------------------===// @@ -476,8 +561,10 @@ def : ROSysReg<"PMCEID0_EL0", 0b11, 0b011, 0b1001, 0b1100, 0b110>; def : ROSysReg<"PMCEID1_EL0", 0b11, 0b011, 0b1001, 0b1100, 0b111>; def : ROSysReg<"MIDR_EL1", 0b11, 0b000, 0b0000, 0b0000, 0b000>; def : ROSysReg<"CCSIDR_EL1", 0b11, 0b001, 0b0000, 0b0000, 0b000>; + +//v8.3 CCIDX - extending the CCsIDr number of sets def : ROSysReg<"CCSIDR2_EL1", 0b11, 0b001, 0b0000, 0b0000, 0b010> { - let Requires = [{ {AArch64::HasV8_3aOps} }]; + let Requires = [{ {AArch64::FeatureCCIDX} }]; } def : ROSysReg<"CLIDR_EL1", 0b11, 0b001, 0b0000, 0b0000, 0b001>; def : ROSysReg<"CTR_EL0", 0b11, 0b011, 0b0000, 0b0000, 0b001>; @@ -487,6 +574,9 @@ def : ROSysReg<"AIDR_EL1", 0b11, 0b001, 0b0000, 0b0000, 0b111>; def : ROSysReg<"DCZID_EL0", 0b11, 0b011, 0b0000, 0b0000, 0b111>; def : ROSysReg<"ID_PFR0_EL1", 0b11, 0b000, 0b0000, 0b0001, 0b000>; def : ROSysReg<"ID_PFR1_EL1", 0b11, 0b000, 0b0000, 0b0001, 0b001>; +def : ROSysReg<"ID_PFR2_EL1", 0b11, 0b000, 0b0000, 0b0011, 0b100> { + let Requires = [{ {AArch64::FeatureSpecRestrict} }]; +} def : ROSysReg<"ID_DFR0_EL1", 0b11, 0b000, 0b0000, 0b0001, 0b010>; def : ROSysReg<"ID_AFR0_EL1", 0b11, 0b000, 0b0000, 0b0001, 0b011>; def : ROSysReg<"ID_MMFR0_EL1", 0b11, 0b000, 0b0000, 0b0001, 0b100>; @@ -512,9 +602,7 @@ def : ROSysReg<"ID_AA64ISAR0_EL1", 0b11, 0b000, 0b0000, 0b0110, 0b000>; def : ROSysReg<"ID_AA64ISAR1_EL1", 0b11, 0b000, 0b0000, 0b0110, 0b001>; def : ROSysReg<"ID_AA64MMFR0_EL1", 0b11, 0b000, 0b0000, 0b0111, 0b000>; def : ROSysReg<"ID_AA64MMFR1_EL1", 0b11, 0b000, 0b0000, 0b0111, 0b001>; -def : ROSysReg<"ID_AA64MMFR2_EL1", 0b11, 0b000, 0b0000, 0b0111, 0b010> { - let Requires = [{ {AArch64::HasV8_2aOps} }]; -} +def : ROSysReg<"ID_AA64MMFR2_EL1", 0b11, 0b000, 0b0000, 0b0111, 0b010>; def : ROSysReg<"MVFR0_EL1", 0b11, 0b000, 0b0000, 0b0011, 0b000>; def : ROSysReg<"MVFR1_EL1", 0b11, 0b000, 0b0000, 0b0011, 0b001>; def : ROSysReg<"MVFR2_EL1", 0b11, 0b000, 0b0000, 0b0011, 0b010>; @@ -576,9 +664,15 @@ def : ROSysReg<"ICH_VTR_EL2", 0b11, 0b100, 0b1100, 0b1011, 0b001>; def : ROSysReg<"ICH_EISR_EL2", 0b11, 0b100, 0b1100, 0b1011, 0b011>; def : ROSysReg<"ICH_ELRSR_EL2", 0b11, 0b100, 0b1100, 0b1011, 0b101>; +// SVE control registers +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureSVE} }] in { +def : ROSysReg<"ID_AA64ZFR0_EL1", 0b11, 0b000, 0b0000, 0b0100, 0b100>; +} + // v8.1a "Limited Ordering Regions" extension-specific system register // Op0 Op1 CRn CRm Op2 -let Requires = [{ {AArch64::HasV8_1aOps} }] in +let Requires = [{ {AArch64::FeatureLOR} }] in def : ROSysReg<"LORID_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b111>; // v8.2a "RAS extension" registers @@ -588,6 +682,22 @@ def : ROSysReg<"ERRIDR_EL1", 0b11, 0b000, 0b0101, 0b0011, 0b000>; def : ROSysReg<"ERXFR_EL1", 0b11, 0b000, 0b0101, 0b0100, 0b000>; } +// v8.5a "random number" registers +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureRandGen} }] in { +def : ROSysReg<"RNDR", 0b11, 0b011, 0b0010, 0b0100, 0b000>; +def : ROSysReg<"RNDRRS", 0b11, 0b011, 0b0010, 0b0100, 0b001>; +} + +// v8.5a Software Context Number registers +let Requires = [{ {AArch64::FeatureSpecRestrict} }] in { +def : RWSysReg<"SCXTNUM_EL0", 0b11, 0b011, 0b1101, 0b0000, 0b111>; +def : RWSysReg<"SCXTNUM_EL1", 0b11, 0b000, 0b1101, 0b0000, 0b111>; +def : RWSysReg<"SCXTNUM_EL2", 0b11, 0b100, 0b1101, 0b0000, 0b111>; +def : RWSysReg<"SCXTNUM_EL3", 0b11, 0b110, 0b1101, 0b0000, 0b111>; +def : RWSysReg<"SCXTNUM_EL12", 0b11, 0b101, 0b1101, 0b0000, 0b111>; +} + //===---------------------- // Write-only regs //===---------------------- @@ -1096,21 +1206,21 @@ def : RWSysReg<"ICH_LR14_EL2", 0b11, 0b100, 0b1100, 0b1101, 0b110>; def : RWSysReg<"ICH_LR15_EL2", 0b11, 0b100, 0b1100, 0b1101, 0b111>; // v8.1a "Privileged Access Never" extension-specific system registers -let Requires = [{ {AArch64::HasV8_1aOps} }] in +let Requires = [{ {AArch64::FeaturePAN} }] in def : RWSysReg<"PAN", 0b11, 0b000, 0b0100, 0b0010, 0b011>; // v8.1a "Limited Ordering Regions" extension-specific system registers // Op0 Op1 CRn CRm Op2 -let Requires = [{ {AArch64::HasV8_1aOps} }] in { +let Requires = [{ {AArch64::FeatureLOR} }] in { def : RWSysReg<"LORSA_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b000>; def : RWSysReg<"LOREA_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b001>; def : RWSysReg<"LORN_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b010>; def : RWSysReg<"LORC_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b011>; } -// v8.1a "Virtualization hos extensions" system registers +// v8.1a "Virtualization Host extensions" system registers // Op0 Op1 CRn CRm Op2 -let Requires = [{ {AArch64::HasV8_1aOps} }] in { +let Requires = [{ {AArch64::FeatureVH} }] in { def : RWSysReg<"TTBR1_EL2", 0b11, 0b100, 0b0010, 0b0000, 0b001>; def : RWSysReg<"CONTEXTIDR_EL2", 0b11, 0b100, 0b1101, 0b0000, 0b001>; def : RWSysReg<"CNTHV_TVAL_EL2", 0b11, 0b100, 0b1110, 0b0011, 0b000>; @@ -1141,7 +1251,7 @@ def : RWSysReg<"ELR_EL12", 0b11, 0b101, 0b0100, 0b0000, 0b001>; } // v8.2a registers // Op0 Op1 CRn CRm Op2 -let Requires = [{ {AArch64::HasV8_2aOps} }] in +let Requires = [{ {AArch64::FeaturePsUAO} }] in def : RWSysReg<"UAO", 0b11, 0b000, 0b0100, 0b0010, 0b100>; // v8.2a "Statistical Profiling extension" registers @@ -1178,7 +1288,7 @@ def : RWSysReg<"VSESR_EL2", 0b11, 0b100, 0b0101, 0b0010, 0b011>; // v8.3a "Pointer authentication extension" registers // Op0 Op1 CRn CRm Op2 -let Requires = [{ {AArch64::HasV8_3aOps} }] in { +let Requires = [{ {AArch64::FeaturePA} }] in { def : RWSysReg<"APIAKeyLo_EL1", 0b11, 0b000, 0b0010, 0b0001, 0b000>; def : RWSysReg<"APIAKeyHi_EL1", 0b11, 0b000, 0b0010, 0b0001, 0b001>; def : RWSysReg<"APIBKeyLo_EL1", 0b11, 0b000, 0b0010, 0b0001, 0b010>; @@ -1191,8 +1301,8 @@ def : RWSysReg<"APGAKeyLo_EL1", 0b11, 0b000, 0b0010, 0b0011, 0b000>; def : RWSysReg<"APGAKeyHi_EL1", 0b11, 0b000, 0b0010, 0b0011, 0b001>; } -let Requires = [{ {AArch64::HasV8_4aOps} }] in { - +// v8.4 "Secure Exception Level 2 extension" +let Requires = [{ {AArch64::FeatureSEL2} }] in { // v8.4a "Virtualization secure second stage translation" registers // Op0 Op1 CRn CRm Op2 def : RWSysReg<"VSTCR_EL2" , 0b11, 0b100, 0b0010, 0b0110, 0b010>; @@ -1210,18 +1320,22 @@ def : RWSysReg<"CNTHPS_CTL_EL2", 0b11, 0b100, 0b1110, 0b0101, 0b001>; // v8.4a "Virtualization debug state" registers // Op0 Op1 CRn CRm Op2 def : RWSysReg<"SDER32_EL2", 0b11, 0b100, 0b0001, 0b0011, 0b001>; +} // FeatureSEL2 // v8.4a RAS registers -// Op0 Op1 CRn CRm Op2 +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureRASv8_4} }] in { def : RWSysReg<"ERXPFGCTL_EL1", 0b11, 0b000, 0b0101, 0b0100, 0b101>; def : RWSysReg<"ERXPFGCDN_EL1", 0b11, 0b000, 0b0101, 0b0100, 0b110>; def : RWSysReg<"ERXTS_EL1", 0b11, 0b000, 0b0101, 0b0101, 0b111>; def : RWSysReg<"ERXMISC2_EL1", 0b11, 0b000, 0b0101, 0b0101, 0b010>; def : RWSysReg<"ERXMISC3_EL1", 0b11, 0b000, 0b0101, 0b0101, 0b011>; def : ROSysReg<"ERXPFGF_EL1", 0b11, 0b000, 0b0101, 0b0100, 0b100>; +} // FeatureRASv8_4 // v8.4a MPAM registers // Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureMPAM} }] in { def : RWSysReg<"MPAM0_EL1", 0b11, 0b000, 0b1010, 0b0101, 0b001>; def : RWSysReg<"MPAM1_EL1", 0b11, 0b000, 0b1010, 0b0101, 0b000>; def : RWSysReg<"MPAM2_EL2", 0b11, 0b100, 0b1010, 0b0101, 0b000>; @@ -1238,9 +1352,11 @@ def : RWSysReg<"MPAMVPM5_EL2", 0b11, 0b100, 0b1010, 0b0110, 0b101>; def : RWSysReg<"MPAMVPM6_EL2", 0b11, 0b100, 0b1010, 0b0110, 0b110>; def : RWSysReg<"MPAMVPM7_EL2", 0b11, 0b100, 0b1010, 0b0110, 0b111>; def : ROSysReg<"MPAMIDR_EL1", 0b11, 0b000, 0b1010, 0b0100, 0b100>; +} //FeatureMPAM -// v8.4a Activitiy monitor registers +// v8.4a Activitiy Monitor registers // Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureAM} }] in { def : RWSysReg<"AMCR_EL0", 0b11, 0b011, 0b1101, 0b0010, 0b000>; def : ROSysReg<"AMCFGR_EL0", 0b11, 0b011, 0b1101, 0b0010, 0b001>; def : ROSysReg<"AMCGCR_EL0", 0b11, 0b011, 0b1101, 0b0010, 0b010>; @@ -1289,6 +1405,7 @@ def : RWSysReg<"AMEVTYPER112_EL0", 0b11, 0b011, 0b1101, 0b1111, 0b100>; def : RWSysReg<"AMEVTYPER113_EL0", 0b11, 0b011, 0b1101, 0b1111, 0b101>; def : RWSysReg<"AMEVTYPER114_EL0", 0b11, 0b011, 0b1101, 0b1111, 0b110>; def : RWSysReg<"AMEVTYPER115_EL0", 0b11, 0b011, 0b1101, 0b1111, 0b111>; +} //FeatureAM // v8.4a Trace Extension registers // @@ -1297,19 +1414,51 @@ def : RWSysReg<"AMEVTYPER115_EL0", 0b11, 0b011, 0b1101, 0b1111, 0b111>; // but they are already defined above. // // Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureTRACEV8_4} }] in { def : RWSysReg<"TRFCR_EL1", 0b11, 0b000, 0b0001, 0b0010, 0b001>; def : RWSysReg<"TRFCR_EL2", 0b11, 0b100, 0b0001, 0b0010, 0b001>; def : RWSysReg<"TRFCR_EL12", 0b11, 0b101, 0b0001, 0b0010, 0b001>; +} //FeatureTRACEV8_4 // v8.4a Timining insensitivity of data processing instructions +// DIT: Data Independent Timing instructions // Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureDIT} }] in { def : RWSysReg<"DIT", 0b11, 0b011, 0b0100, 0b0010, 0b101>; +} //FeatureDIT // v8.4a Enhanced Support for Nested Virtualization // Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureNV} }] in { def : RWSysReg<"VNCR_EL2", 0b11, 0b100, 0b0010, 0b0010, 0b000>; +} //FeatureNV -} // HasV8_4aOps +// SVE control registers +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureSVE} }] in { +def : RWSysReg<"ZCR_EL1", 0b11, 0b000, 0b0001, 0b0010, 0b000>; +def : RWSysReg<"ZCR_EL2", 0b11, 0b100, 0b0001, 0b0010, 0b000>; +def : RWSysReg<"ZCR_EL3", 0b11, 0b110, 0b0001, 0b0010, 0b000>; +def : RWSysReg<"ZCR_EL12", 0b11, 0b101, 0b0001, 0b0010, 0b000>; +} + +// V8.5a Spectre mitigation SSBS register +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureSSBS} }] in +def : RWSysReg<"SSBS", 0b11, 0b011, 0b0100, 0b0010, 0b110>; + +// v8.5a Memory Tagging Extension +// Op0 Op1 CRn CRm Op2 +let Requires = [{ {AArch64::FeatureMTE} }] in { +def : RWSysReg<"TCO", 0b11, 0b011, 0b0100, 0b0010, 0b111>; +def : RWSysReg<"GCR_EL1", 0b11, 0b000, 0b0001, 0b0000, 0b110>; +def : RWSysReg<"RGSR_EL1", 0b11, 0b000, 0b0001, 0b0000, 0b101>; +def : RWSysReg<"TFSR_EL1", 0b11, 0b000, 0b0110, 0b0101, 0b000>; +def : RWSysReg<"TFSR_EL2", 0b11, 0b100, 0b0110, 0b0101, 0b000>; +def : RWSysReg<"TFSR_EL3", 0b11, 0b110, 0b0110, 0b0110, 0b000>; +def : RWSysReg<"TFSR_EL12", 0b11, 0b101, 0b0110, 0b0110, 0b000>; +def : RWSysReg<"TFSRE0_EL1", 0b11, 0b000, 0b0110, 0b0110, 0b001>; +} // HasMTE // Cyclone specific system registers // Op0 Op1 CRn CRm Op2 diff --git a/lib/Target/AArch64/AArch64TargetMachine.cpp b/lib/Target/AArch64/AArch64TargetMachine.cpp index 120d71381c67..4e016525f7e4 100644 --- a/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -30,6 +30,7 @@ #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Pass.h" #include "llvm/Support/CodeGen.h" @@ -123,6 +124,10 @@ static cl::opt<bool> BranchRelaxation("aarch64-enable-branch-relax", cl::Hidden, cl::init(true), cl::desc("Relax out of range conditional branches")); +static cl::opt<bool> EnableCompressJumpTables( + "aarch64-enable-compress-jump-tables", cl::Hidden, cl::init(true), + cl::desc("Use smallest entry possible for jump tables")); + // FIXME: Unify control over GlobalMerge. static cl::opt<cl::boolOrDefault> EnableGlobalMerge("aarch64-enable-global-merge", cl::Hidden, @@ -141,6 +146,11 @@ static cl::opt<int> EnableGlobalISelAtO( static cl::opt<bool> EnableFalkorHWPFFix("aarch64-enable-falkor-hwpf-fix", cl::init(true), cl::Hidden); +static cl::opt<bool> + EnableBranchTargets("aarch64-enable-branch-targets", cl::Hidden, + cl::desc("Enable the AAcrh64 branch target pass"), + cl::init(true)); + extern "C" void LLVMInitializeAArch64Target() { // Register the target. RegisterTargetMachine<AArch64leTargetMachine> X(getTheAArch64leTarget()); @@ -151,19 +161,23 @@ extern "C" void LLVMInitializeAArch64Target() { initializeAArch64A53Fix835769Pass(*PR); initializeAArch64A57FPLoadBalancingPass(*PR); initializeAArch64AdvSIMDScalarPass(*PR); + initializeAArch64BranchTargetsPass(*PR); initializeAArch64CollectLOHPass(*PR); + initializeAArch64CompressJumpTablesPass(*PR); initializeAArch64ConditionalComparesPass(*PR); initializeAArch64ConditionOptimizerPass(*PR); initializeAArch64DeadRegisterDefinitionsPass(*PR); initializeAArch64ExpandPseudoPass(*PR); initializeAArch64LoadStoreOptPass(*PR); initializeAArch64SIMDInstrOptPass(*PR); + initializeAArch64PreLegalizerCombinerPass(*PR); initializeAArch64PromoteConstantPass(*PR); initializeAArch64RedundantCopyEliminationPass(*PR); initializeAArch64StorePairSuppressPass(*PR); initializeFalkorHWPFFixPass(*PR); initializeFalkorMarkStridedAccessesLegacyPass(*PR); initializeLDTLSCleanupPass(*PR); + initializeAArch64SpeculationHardeningPass(*PR); } //===----------------------------------------------------------------------===// @@ -206,18 +220,20 @@ static Reloc::Model getEffectiveRelocModel(const Triple &TT, return *RM; } -static CodeModel::Model getEffectiveCodeModel(const Triple &TT, - Optional<CodeModel::Model> CM, - bool JIT) { +static CodeModel::Model +getEffectiveAArch64CodeModel(const Triple &TT, Optional<CodeModel::Model> CM, + bool JIT) { if (CM) { - if (*CM != CodeModel::Small && *CM != CodeModel::Large) { + if (*CM != CodeModel::Small && *CM != CodeModel::Tiny && + *CM != CodeModel::Large) { if (!TT.isOSFuchsia()) report_fatal_error( - "Only small and large code models are allowed on AArch64"); - else if (CM != CodeModel::Kernel) - report_fatal_error( - "Only small, kernel, and large code models are allowed on AArch64"); - } + "Only small, tiny and large code models are allowed on AArch64"); + else if (*CM != CodeModel::Kernel) + report_fatal_error("Only small, tiny, kernel, and large code models " + "are allowed on AArch64"); + } else if (*CM == CodeModel::Tiny && !TT.isOSBinFormatELF()) + report_fatal_error("tiny code model is only supported on ELF"); return *CM; } // The default MCJIT memory managers make no guarantees about where they can @@ -240,7 +256,7 @@ AArch64TargetMachine::AArch64TargetMachine(const Target &T, const Triple &TT, : LLVMTargetMachine(T, computeDataLayout(TT, Options.MCOptions, LittleEndian), TT, CPU, FS, Options, getEffectiveRelocModel(TT, RM), - getEffectiveCodeModel(TT, CM, JIT), OL), + getEffectiveAArch64CodeModel(TT, CM, JIT), OL), TLOF(createTLOF(getTargetTriple())), isLittle(LittleEndian) { initAsmInfo(); @@ -249,9 +265,21 @@ AArch64TargetMachine::AArch64TargetMachine(const Target &T, const Triple &TT, this->Options.NoTrapAfterNoreturn = true; } + if (getMCAsmInfo()->usesWindowsCFI()) { + // Unwinding can get confused if the last instruction in an + // exception-handling region (function, funclet, try block, etc.) + // is a call. + // + // FIXME: We could elide the trap if the next instruction would be in + // the same region anyway. + this->Options.TrapUnreachable = true; + } + // Enable GlobalISel at or below EnableGlobalISelAt0. - if (getOptLevel() <= EnableGlobalISelAtO) + if (getOptLevel() <= EnableGlobalISelAtO) { setGlobalISel(true); + setGlobalISelAbort(GlobalISelAbortMode::Disable); + } // AArch64 supports the MachineOutliner. setMachineOutliner(true); @@ -346,6 +374,7 @@ public: bool addPreISel() override; bool addInstSelector() override; bool addIRTranslator() override; + void addPreLegalizeMachineIR() override; bool addLegalizeMachineIR() override; bool addRegBankSelect() override; void addPreGlobalInstructionSelect() override; @@ -393,8 +422,10 @@ void AArch64PassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); // Match interleaved memory accesses to ldN/stN intrinsics. - if (TM->getOptLevel() != CodeGenOpt::None) + if (TM->getOptLevel() != CodeGenOpt::None) { + addPass(createInterleavedLoadCombinePass()); addPass(createInterleavedAccessPass()); + } if (TM->getOptLevel() == CodeGenOpt::Aggressive && EnableGEPOpt) { // Call SeparateConstOffsetFromGEP pass to extract constants within indices @@ -447,6 +478,10 @@ bool AArch64PassConfig::addIRTranslator() { return false; } +void AArch64PassConfig::addPreLegalizeMachineIR() { + addPass(createAArch64PreLegalizeCombiner()); +} + bool AArch64PassConfig::addLegalizeMachineIR() { addPass(new Legalizer()); return false; @@ -516,12 +551,28 @@ void AArch64PassConfig::addPreSched2() { if (TM->getOptLevel() != CodeGenOpt::None) { if (EnableLoadStoreOpt) addPass(createAArch64LoadStoreOptimizationPass()); + } + + // The AArch64SpeculationHardeningPass destroys dominator tree and natural + // loop info, which is needed for the FalkorHWPFFixPass and also later on. + // Therefore, run the AArch64SpeculationHardeningPass before the + // FalkorHWPFFixPass to avoid recomputing dominator tree and natural loop + // info. + addPass(createAArch64SpeculationHardeningPass()); + + if (TM->getOptLevel() != CodeGenOpt::None) { if (EnableFalkorHWPFFix) addPass(createFalkorHWPFFixPass()); } } void AArch64PassConfig::addPreEmitPass() { + // Machine Block Placement might have created new opportunities when run + // at O3, where the Tail Duplication Threshold is set to 4 instructions. + // Run the load/store optimizer once more. + if (TM->getOptLevel() >= CodeGenOpt::Aggressive && EnableLoadStoreOpt) + addPass(createAArch64LoadStoreOptimizationPass()); + if (EnableA53Fix835769) addPass(createAArch64A53Fix835769()); // Relax conditional branch instructions if they're otherwise out of @@ -529,6 +580,12 @@ void AArch64PassConfig::addPreEmitPass() { if (BranchRelaxation) addPass(&BranchRelaxationPassID); + if (EnableBranchTargets) + addPass(createAArch64BranchTargetsPass()); + + if (TM->getOptLevel() != CodeGenOpt::None && EnableCompressJumpTables) + addPass(createAArch64CompressJumpTablesPass()); + if (TM->getOptLevel() != CodeGenOpt::None && EnableCollectLOH && TM->getTargetTriple().isOSBinFormatMachO()) addPass(createAArch64CollectLOHPass()); diff --git a/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/lib/Target/AArch64/AArch64TargetObjectFile.cpp index 4bc2c060a068..8ae72a7ddb57 100644 --- a/lib/Target/AArch64/AArch64TargetObjectFile.cpp +++ b/lib/Target/AArch64/AArch64TargetObjectFile.cpp @@ -22,6 +22,9 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFileELF::Initialize(Ctx, TM); InitializeELF(TM.Options.UseInitArray); + // AARCH64 ELF ABI does not define static relocation type for TLS offset + // within a module. Do not generate AT_location for TLS variables. + SupportDebugThreadLocalLocation = false; } AArch64_MachoTargetObjectFile::AArch64_MachoTargetObjectFile() diff --git a/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/lib/Target/AArch64/AArch64TargetTransformInfo.cpp index 96e751e86971..a256cb7c9215 100644 --- a/lib/Target/AArch64/AArch64TargetTransformInfo.cpp +++ b/lib/Target/AArch64/AArch64TargetTransformInfo.cpp @@ -659,11 +659,14 @@ int AArch64TTIImpl::getInterleavedMemoryOpCost(unsigned Opcode, Type *VecTy, unsigned Factor, ArrayRef<unsigned> Indices, unsigned Alignment, - unsigned AddressSpace) { + unsigned AddressSpace, + bool UseMaskForCond, + bool UseMaskForGaps) { assert(Factor >= 2 && "Invalid interleave factor"); assert(isa<VectorType>(VecTy) && "Expect a vector type"); - if (Factor <= TLI->getMaxSupportedInterleaveFactor()) { + if (!UseMaskForCond && !UseMaskForGaps && + Factor <= TLI->getMaxSupportedInterleaveFactor()) { unsigned NumElts = VecTy->getVectorNumElements(); auto *SubVecTy = VectorType::get(VecTy->getScalarType(), NumElts / Factor); @@ -676,7 +679,8 @@ int AArch64TTIImpl::getInterleavedMemoryOpCost(unsigned Opcode, Type *VecTy, } return BaseT::getInterleavedMemoryOpCost(Opcode, VecTy, Factor, Indices, - Alignment, AddressSpace); + Alignment, AddressSpace, + UseMaskForCond, UseMaskForGaps); } int AArch64TTIImpl::getCostOfKeepingLiveOverCall(ArrayRef<Type *> Tys) { @@ -945,9 +949,20 @@ int AArch64TTIImpl::getArithmeticReductionCost(unsigned Opcode, Type *ValTy, int AArch64TTIImpl::getShuffleCost(TTI::ShuffleKind Kind, Type *Tp, int Index, Type *SubTp) { - if (Kind == TTI::SK_Transpose || Kind == TTI::SK_Select || - Kind == TTI::SK_PermuteSingleSrc) { + if (Kind == TTI::SK_Broadcast || Kind == TTI::SK_Transpose || + Kind == TTI::SK_Select || Kind == TTI::SK_PermuteSingleSrc) { static const CostTblEntry ShuffleTbl[] = { + // Broadcast shuffle kinds can be performed with 'dup'. + { TTI::SK_Broadcast, MVT::v8i8, 1 }, + { TTI::SK_Broadcast, MVT::v16i8, 1 }, + { TTI::SK_Broadcast, MVT::v4i16, 1 }, + { TTI::SK_Broadcast, MVT::v8i16, 1 }, + { TTI::SK_Broadcast, MVT::v2i32, 1 }, + { TTI::SK_Broadcast, MVT::v4i32, 1 }, + { TTI::SK_Broadcast, MVT::v2i64, 1 }, + { TTI::SK_Broadcast, MVT::v2f32, 1 }, + { TTI::SK_Broadcast, MVT::v4f32, 1 }, + { TTI::SK_Broadcast, MVT::v2f64, 1 }, // Transpose shuffle kinds can be performed with 'trn1/trn2' and // 'zip1/zip2' instructions. { TTI::SK_Transpose, MVT::v8i8, 1 }, diff --git a/lib/Target/AArch64/AArch64TargetTransformInfo.h b/lib/Target/AArch64/AArch64TargetTransformInfo.h index c056a7d2428b..08c1a8924220 100644 --- a/lib/Target/AArch64/AArch64TargetTransformInfo.h +++ b/lib/Target/AArch64/AArch64TargetTransformInfo.h @@ -146,7 +146,9 @@ public: int getInterleavedMemoryOpCost(unsigned Opcode, Type *VecTy, unsigned Factor, ArrayRef<unsigned> Indices, unsigned Alignment, - unsigned AddressSpace); + unsigned AddressSpace, + bool UseMaskForCond = false, + bool UseMaskForGaps = false); bool shouldConsiderAddressTypePromotion(const Instruction &I, diff --git a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 30a9a08f2346..6cc9b67e4d27 100644 --- a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -39,6 +39,7 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/MC/MCValue.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" @@ -164,6 +165,7 @@ private: OperandVector &Operands); bool parseDirectiveArch(SMLoc L); + bool parseDirectiveArchExtension(SMLoc L); bool parseDirectiveCPU(SMLoc L); bool parseDirectiveInst(SMLoc L); @@ -174,6 +176,8 @@ private: bool parseDirectiveReq(StringRef Name, SMLoc L); bool parseDirectiveUnreq(SMLoc L); + bool parseDirectiveCFINegateRAState(); + bool parseDirectiveCFIBKeyFrame(); bool validateInstruction(MCInst &Inst, SMLoc &IDLoc, SmallVectorImpl<SMLoc> &Loc); @@ -200,6 +204,7 @@ private: template <bool IsSVEPrefetch = false> OperandMatchResultTy tryParsePrefetch(OperandVector &Operands); OperandMatchResultTy tryParsePSBHint(OperandVector &Operands); + OperandMatchResultTy tryParseBTIHint(OperandVector &Operands); OperandMatchResultTy tryParseAdrpLabel(OperandVector &Operands); OperandMatchResultTy tryParseAdrLabel(OperandVector &Operands); template<bool AddFPZeroAsLiteral> @@ -282,6 +287,7 @@ private: k_FPImm, k_Barrier, k_PSBHint, + k_BTIHint, } Kind; SMLoc StartLoc, EndLoc; @@ -385,6 +391,12 @@ private: unsigned Val; }; + struct BTIHintOp { + const char *Data; + unsigned Length; + unsigned Val; + }; + struct ExtendOp { unsigned Val; }; @@ -403,6 +415,7 @@ private: struct SysCRImmOp SysCRImm; struct PrefetchOp Prefetch; struct PSBHintOp PSBHint; + struct BTIHintOp BTIHint; struct ShiftExtendOp ShiftExtend; }; @@ -457,6 +470,9 @@ public: case k_PSBHint: PSBHint = o.PSBHint; break; + case k_BTIHint: + BTIHint = o.BTIHint; + break; case k_ShiftExtend: ShiftExtend = o.ShiftExtend; break; @@ -568,6 +584,16 @@ public: return StringRef(PSBHint.Data, PSBHint.Length); } + unsigned getBTIHint() const { + assert(Kind == k_BTIHint && "Invalid access!"); + return BTIHint.Val; + } + + StringRef getBTIHintName() const { + assert(Kind == k_BTIHint && "Invalid access!"); + return StringRef(BTIHint.Data, BTIHint.Length); + } + StringRef getPrefetchName() const { assert(Kind == k_Prefetch && "Invalid access!"); return StringRef(Prefetch.Data, Prefetch.Length); @@ -658,7 +684,7 @@ public: return DiagnosticPredicateTy::NearMatch; } - bool isSymbolicUImm12Offset(const MCExpr *Expr, unsigned Scale) const { + bool isSymbolicUImm12Offset(const MCExpr *Expr) const { AArch64MCExpr::VariantKind ELFRefKind; MCSymbolRefExpr::VariantKind DarwinRefKind; int64_t Addend; @@ -683,7 +709,7 @@ public: // Note that we don't range-check the addend. It's adjusted modulo page // size when converted, so there is no "out of range" condition when using // @pageoff. - return Addend >= 0 && (Addend % Scale) == 0; + return true; } else if (DarwinRefKind == MCSymbolRefExpr::VK_GOTPAGEOFF || DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF) { // @gotpageoff/@tlvppageoff can only be used directly, not with an addend. @@ -699,7 +725,7 @@ public: const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(getImm()); if (!MCE) - return isSymbolicUImm12Offset(getImm(), Scale); + return isSymbolicUImm12Offset(getImm()); int64_t Val = MCE->getValue(); return (Val % Scale) == 0 && Val >= 0 && (Val / Scale) < 0x1000; @@ -901,7 +927,7 @@ public: for (unsigned i = 0; i != AllowedModifiers.size(); ++i) { if (ELFRefKind == AllowedModifiers[i]) - return Addend == 0; + return true; } return false; @@ -996,7 +1022,8 @@ public: if (!isSysReg()) return false; return (SysReg.PStateField == AArch64PState::PAN || SysReg.PStateField == AArch64PState::DIT || - SysReg.PStateField == AArch64PState::UAO); + SysReg.PStateField == AArch64PState::UAO || + SysReg.PStateField == AArch64PState::SSBS); } bool isSystemPStateFieldWithImm0_15() const { @@ -1185,6 +1212,7 @@ public: bool isSysCR() const { return Kind == k_SysCR; } bool isPrefetch() const { return Kind == k_Prefetch; } bool isPSBHint() const { return Kind == k_PSBHint; } + bool isBTIHint() const { return Kind == k_BTIHint; } bool isShiftExtend() const { return Kind == k_ShiftExtend; } bool isShifter() const { if (!isShiftExtend()) @@ -1702,6 +1730,11 @@ public: Inst.addOperand(MCOperand::createImm(getPSBHint())); } + void addBTIHintOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(getBTIHint())); + } + void addShifterOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); unsigned Imm = @@ -1950,6 +1983,19 @@ public: return Op; } + static std::unique_ptr<AArch64Operand> CreateBTIHint(unsigned Val, + StringRef Str, + SMLoc S, + MCContext &Ctx) { + auto Op = make_unique<AArch64Operand>(k_BTIHint, Ctx); + Op->BTIHint.Val = Val << 1 | 32; + Op->BTIHint.Data = Str.data(); + Op->BTIHint.Length = Str.size(); + Op->StartLoc = S; + Op->EndLoc = S; + return Op; + } + static std::unique_ptr<AArch64Operand> CreateShiftExtend(AArch64_AM::ShiftExtendType ShOp, unsigned Val, bool HasExplicitAmount, SMLoc S, SMLoc E, MCContext &Ctx) { @@ -2030,6 +2076,9 @@ void AArch64Operand::print(raw_ostream &OS) const { if (!getShiftExtendAmount() && !hasShiftExtendAmount()) break; LLVM_FALLTHROUGH; + case k_BTIHint: + OS << getBTIHintName(); + break; case k_ShiftExtend: OS << "<" << AArch64_AM::getShiftExtendName(getShiftExtendType()) << " #" << getShiftExtendAmount(); @@ -2395,6 +2444,29 @@ AArch64AsmParser::tryParsePSBHint(OperandVector &Operands) { return MatchOperand_Success; } +/// tryParseBTIHint - Try to parse a BTI operand, mapped to Hint command +OperandMatchResultTy +AArch64AsmParser::tryParseBTIHint(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + SMLoc S = getLoc(); + const AsmToken &Tok = Parser.getTok(); + if (Tok.isNot(AsmToken::Identifier)) { + TokError("invalid operand for instruction"); + return MatchOperand_ParseFail; + } + + auto BTI = AArch64BTIHint::lookupBTIByName(Tok.getString()); + if (!BTI) { + TokError("invalid operand for instruction"); + return MatchOperand_ParseFail; + } + + Parser.Lex(); // Eat identifier token. + Operands.push_back(AArch64Operand::CreateBTIHint( + BTI->Encoding, Tok.getString(), S, getContext())); + return MatchOperand_Success; +} + /// tryParseAdrpLabel - Parse and validate a source label for the ADRP /// instruction. OperandMatchResultTy @@ -2453,17 +2525,34 @@ AArch64AsmParser::tryParseAdrLabel(OperandVector &Operands) { SMLoc S = getLoc(); const MCExpr *Expr; - const AsmToken &Tok = getParser().getTok(); - if (parseOptionalToken(AsmToken::Hash) || Tok.is(AsmToken::Integer)) { - if (getParser().parseExpression(Expr)) - return MatchOperand_ParseFail; + // Leave anything with a bracket to the default for SVE + if (getParser().getTok().is(AsmToken::LBrac)) + return MatchOperand_NoMatch; - SMLoc E = SMLoc::getFromPointer(getLoc().getPointer() - 1); - Operands.push_back(AArch64Operand::CreateImm(Expr, S, E, getContext())); + if (getParser().getTok().is(AsmToken::Hash)) + getParser().Lex(); // Eat hash token. - return MatchOperand_Success; + if (parseSymbolicImmVal(Expr)) + return MatchOperand_ParseFail; + + AArch64MCExpr::VariantKind ELFRefKind; + MCSymbolRefExpr::VariantKind DarwinRefKind; + int64_t Addend; + if (classifySymbolRef(Expr, ELFRefKind, DarwinRefKind, Addend)) { + if (DarwinRefKind == MCSymbolRefExpr::VK_None && + ELFRefKind == AArch64MCExpr::VK_INVALID) { + // No modifier was specified at all; this is the syntax for an ELF basic + // ADR relocation (unfortunately). + Expr = AArch64MCExpr::create(Expr, AArch64MCExpr::VK_ABS, getContext()); + } else { + Error(S, "unexpected adr label"); + return MatchOperand_ParseFail; + } } - return MatchOperand_NoMatch; + + SMLoc E = SMLoc::getFromPointer(getLoc().getPointer() - 1); + Operands.push_back(AArch64Operand::CreateImm(Expr, S, E, getContext())); + return MatchOperand_Success; } /// tryParseFPImm - A floating point immediate expression operand. @@ -2723,6 +2812,34 @@ AArch64AsmParser::tryParseOptionalShiftExtend(OperandVector &Operands) { return MatchOperand_Success; } +static const struct Extension { + const char *Name; + const FeatureBitset Features; +} ExtensionMap[] = { + {"crc", {AArch64::FeatureCRC}}, + {"sm4", {AArch64::FeatureSM4}}, + {"sha3", {AArch64::FeatureSHA3}}, + {"sha2", {AArch64::FeatureSHA2}}, + {"aes", {AArch64::FeatureAES}}, + {"crypto", {AArch64::FeatureCrypto}}, + {"fp", {AArch64::FeatureFPARMv8}}, + {"simd", {AArch64::FeatureNEON}}, + {"ras", {AArch64::FeatureRAS}}, + {"lse", {AArch64::FeatureLSE}}, + {"predres", {AArch64::FeaturePredRes}}, + {"ccdp", {AArch64::FeatureCacheDeepPersist}}, + {"mte", {AArch64::FeatureMTE}}, + {"tlb-rmi", {AArch64::FeatureTLB_RMI}}, + {"pan-rwv", {AArch64::FeaturePAN_RWV}}, + {"ccpp", {AArch64::FeatureCCPP}}, + {"sve", {AArch64::FeatureSVE}}, + // FIXME: Unsupported extensions + {"pan", {}}, + {"lor", {}}, + {"rdma", {}}, + {"profile", {}}, +}; + static void setRequiredFeatureString(FeatureBitset FBS, std::string &Str) { if (FBS[AArch64::HasV8_1aOps]) Str += "ARMv8.1a"; @@ -2732,8 +2849,18 @@ static void setRequiredFeatureString(FeatureBitset FBS, std::string &Str) { Str += "ARMv8.3a"; else if (FBS[AArch64::HasV8_4aOps]) Str += "ARMv8.4a"; - else - Str += "(unknown)"; + else if (FBS[AArch64::HasV8_5aOps]) + Str += "ARMv8.5a"; + else { + auto ext = std::find_if(std::begin(ExtensionMap), + std::end(ExtensionMap), + [&](const Extension& e) + // Use & in case multiple features are enabled + { return (FBS & e.Features) != FeatureBitset(); } + ); + + Str += ext != std::end(ExtensionMap) ? ext->Name : "(unknown)"; + } } void AArch64AsmParser::createSysAlias(uint16_t Encoding, OperandVector &Operands, @@ -2812,6 +2939,23 @@ bool AArch64AsmParser::parseSysAlias(StringRef Name, SMLoc NameLoc, return TokError(Str.c_str()); } createSysAlias(TLBI->Encoding, Operands, S); + } else if (Mnemonic == "cfp" || Mnemonic == "dvp" || Mnemonic == "cpp") { + const AArch64PRCTX::PRCTX *PRCTX = AArch64PRCTX::lookupPRCTXByName(Op); + if (!PRCTX) + return TokError("invalid operand for prediction restriction instruction"); + else if (!PRCTX->haveFeatures(getSTI().getFeatureBits())) { + std::string Str( + Mnemonic.upper() + std::string(PRCTX->Name) + " requires "); + setRequiredFeatureString(PRCTX->getRequiredFeatures(), Str); + return TokError(Str.c_str()); + } + uint16_t PRCTX_Op2 = + Mnemonic == "cfp" ? 4 : + Mnemonic == "dvp" ? 5 : + Mnemonic == "cpp" ? 7 : + 0; + assert(PRCTX_Op2 && "Invalid mnemonic for prediction restriction instruction"); + createSysAlias(PRCTX->Encoding << 3 | PRCTX_Op2 , Operands, S); } Parser.Lex(); // Eat operand. @@ -3630,8 +3774,10 @@ bool AArch64AsmParser::ParseInstruction(ParseInstructionInfo &Info, size_t Start = 0, Next = Name.find('.'); StringRef Head = Name.slice(Start, Next); - // IC, DC, AT, and TLBI instructions are aliases for the SYS instruction. - if (Head == "ic" || Head == "dc" || Head == "at" || Head == "tlbi") + // IC, DC, AT, TLBI and Prediction invalidation instructions are aliases for + // the SYS instruction. + if (Head == "ic" || Head == "dc" || Head == "at" || Head == "tlbi" || + Head == "cfp" || Head == "dvp" || Head == "cpp") return parseSysAlias(Head, NameLoc, Operands); Operands.push_back( @@ -3685,13 +3831,9 @@ bool AArch64AsmParser::ParseInstruction(ParseInstructionInfo &Info, // Read the remaining operands. if (getLexer().isNot(AsmToken::EndOfStatement)) { - // Read the first operand. - if (parseOperand(Operands, false, false)) { - return true; - } - unsigned N = 2; - while (parseOptionalToken(AsmToken::Comma)) { + unsigned N = 1; + do { // Parse and remember the operand. if (parseOperand(Operands, (N == 4 && condCodeFourthOperand) || (N == 3 && condCodeThirdOperand) || @@ -3719,7 +3861,7 @@ bool AArch64AsmParser::ParseInstruction(ParseInstructionInfo &Info, AArch64Operand::CreateToken("!", false, ELoc, getContext())); ++N; - } + } while (parseOptionalToken(AsmToken::Comma)); } if (parseToken(AsmToken::EndOfStatement, "unexpected token in argument list")) @@ -3956,6 +4098,15 @@ bool AArch64AsmParser::validateInstruction(MCInst &Inst, SMLoc &IDLoc, "unpredictable STXP instruction, status is also a source"); break; } + case AArch64::LDGV: { + unsigned Rt = Inst.getOperand(0).getReg(); + unsigned Rn = Inst.getOperand(1).getReg(); + if (RI->isSubRegisterEq(Rt, Rn)) { + return Error(Loc[0], + "unpredictable LDGV instruction, writeback register is also " + "the target register"); + } + } } @@ -4090,6 +4241,8 @@ bool AArch64AsmParser::showMatchError(SMLoc Loc, unsigned ErrCode, return Error(Loc, "index must be an integer in range [-128, 127]."); case Match_InvalidMemoryIndexedSImm9: return Error(Loc, "index must be an integer in range [-256, 255]."); + case Match_InvalidMemoryIndexed16SImm9: + return Error(Loc, "index must be a multiple of 16 in range [-4096, 4080]."); case Match_InvalidMemoryIndexed8SImm10: return Error(Loc, "index must be a multiple of 8 in range [-4096, 4088]."); case Match_InvalidMemoryIndexed4SImm7: @@ -4106,6 +4259,8 @@ bool AArch64AsmParser::showMatchError(SMLoc Loc, unsigned ErrCode, return Error(Loc, "index must be a multiple of 2 in range [0, 62]."); case Match_InvalidMemoryIndexed8UImm6: return Error(Loc, "index must be a multiple of 8 in range [0, 504]."); + case Match_InvalidMemoryIndexed16UImm6: + return Error(Loc, "index must be a multiple of 16 in range [0, 1008]."); case Match_InvalidMemoryIndexed4UImm6: return Error(Loc, "index must be a multiple of 4 in range [0, 252]."); case Match_InvalidMemoryIndexed2UImm6: @@ -4742,10 +4897,12 @@ bool AArch64AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, case Match_InvalidMemoryIndexed2UImm6: case Match_InvalidMemoryIndexed4UImm6: case Match_InvalidMemoryIndexed8UImm6: + case Match_InvalidMemoryIndexed16UImm6: case Match_InvalidMemoryIndexedSImm6: case Match_InvalidMemoryIndexedSImm5: case Match_InvalidMemoryIndexedSImm8: case Match_InvalidMemoryIndexedSImm9: + case Match_InvalidMemoryIndexed16SImm9: case Match_InvalidMemoryIndexed8SImm10: case Match_InvalidImm0_1: case Match_InvalidImm0_7: @@ -4874,6 +5031,12 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) { parseDirectiveUnreq(Loc); else if (IDVal == ".inst") parseDirectiveInst(Loc); + else if (IDVal == ".cfi_negate_ra_state") + parseDirectiveCFINegateRAState(); + else if (IDVal == ".cfi_b_key_frame") + parseDirectiveCFIBKeyFrame(); + else if (IDVal == ".arch_extension") + parseDirectiveArchExtension(Loc); else if (IsMachO) { if (IDVal == MCLOHDirectiveName()) parseDirectiveLOH(IDVal, Loc); @@ -4884,28 +5047,6 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) { return false; } -static const struct { - const char *Name; - const FeatureBitset Features; -} ExtensionMap[] = { - { "crc", {AArch64::FeatureCRC} }, - { "sm4", {AArch64::FeatureSM4} }, - { "sha3", {AArch64::FeatureSHA3} }, - { "sha2", {AArch64::FeatureSHA2} }, - { "aes", {AArch64::FeatureAES} }, - { "crypto", {AArch64::FeatureCrypto} }, - { "fp", {AArch64::FeatureFPARMv8} }, - { "simd", {AArch64::FeatureNEON} }, - { "ras", {AArch64::FeatureRAS} }, - { "lse", {AArch64::FeatureLSE} }, - - // FIXME: Unsupported extensions - { "pan", {} }, - { "lor", {} }, - { "rdma", {} }, - { "profile", {} }, -}; - static void ExpandCryptoAEK(AArch64::ArchKind ArchKind, SmallVector<StringRef, 4> &RequestedExtensions) { const bool NoCrypto = @@ -4927,6 +5068,7 @@ static void ExpandCryptoAEK(AArch64::ArchKind ArchKind, RequestedExtensions.push_back("aes"); break; case AArch64::ArchKind::ARMV8_4A: + case AArch64::ArchKind::ARMV8_5A: RequestedExtensions.push_back("sm4"); RequestedExtensions.push_back("sha3"); RequestedExtensions.push_back("sha2"); @@ -4945,6 +5087,7 @@ static void ExpandCryptoAEK(AArch64::ArchKind ArchKind, RequestedExtensions.push_back("noaes"); break; case AArch64::ArchKind::ARMV8_4A: + case AArch64::ArchKind::ARMV8_5A: RequestedExtensions.push_back("nosm4"); RequestedExtensions.push_back("nosha3"); RequestedExtensions.push_back("nosha2"); @@ -5014,6 +5157,50 @@ bool AArch64AsmParser::parseDirectiveArch(SMLoc L) { return false; } +/// parseDirectiveArchExtension +/// ::= .arch_extension [no]feature +bool AArch64AsmParser::parseDirectiveArchExtension(SMLoc L) { + MCAsmParser &Parser = getParser(); + + if (getLexer().isNot(AsmToken::Identifier)) + return Error(getLexer().getLoc(), "expected architecture extension name"); + + const AsmToken &Tok = Parser.getTok(); + StringRef Name = Tok.getString(); + SMLoc ExtLoc = Tok.getLoc(); + Lex(); + + if (parseToken(AsmToken::EndOfStatement, + "unexpected token in '.arch_extension' directive")) + return true; + + bool EnableFeature = true; + if (Name.startswith_lower("no")) { + EnableFeature = false; + Name = Name.substr(2); + } + + MCSubtargetInfo &STI = copySTI(); + FeatureBitset Features = STI.getFeatureBits(); + for (const auto &Extension : ExtensionMap) { + if (Extension.Name != Name) + continue; + + if (Extension.Features.none()) + return Error(ExtLoc, "unsupported architectural extension: " + Name); + + FeatureBitset ToggleFeatures = EnableFeature + ? (~Features & Extension.Features) + : (Features & Extension.Features); + uint64_t Features = + ComputeAvailableFeatures(STI.ToggleFeature(ToggleFeatures)); + setAvailableFeatures(Features); + return false; + } + + return Error(ExtLoc, "unknown architectural extension: " + Name); +} + static SMLoc incrementLoc(SMLoc L, int Offset) { return SMLoc::getFromPointer(L.getPointer() + Offset); } @@ -5267,6 +5454,23 @@ bool AArch64AsmParser::parseDirectiveUnreq(SMLoc L) { return false; } +bool AArch64AsmParser::parseDirectiveCFINegateRAState() { + if (parseToken(AsmToken::EndOfStatement, "unexpected token in directive")) + return true; + getStreamer().EmitCFINegateRAState(); + return false; +} + +/// parseDirectiveCFIBKeyFrame +/// ::= .cfi_b_key +bool AArch64AsmParser::parseDirectiveCFIBKeyFrame() { + if (parseToken(AsmToken::EndOfStatement, + "unexpected token in '.cfi_b_key_frame'")) + return true; + getStreamer().EmitCFIBKeyFrame(); + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, @@ -5288,28 +5492,20 @@ AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, return true; } - const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr); - if (!BE) - return false; - - SE = dyn_cast<MCSymbolRefExpr>(BE->getLHS()); - if (!SE) - return false; - DarwinRefKind = SE->getKind(); - - if (BE->getOpcode() != MCBinaryExpr::Add && - BE->getOpcode() != MCBinaryExpr::Sub) + // Check that it looks like a symbol + an addend + MCValue Res; + bool Relocatable = Expr->evaluateAsRelocatable(Res, nullptr, nullptr); + if (!Relocatable || Res.getSymB()) return false; - // See if the addend is a constant, otherwise there's more going - // on here than we can deal with. - auto AddendExpr = dyn_cast<MCConstantExpr>(BE->getRHS()); - if (!AddendExpr) + // Treat expressions with an ELFRefKind (like ":abs_g1:3", or + // ":abs_g1:x" where x is constant) as symbolic even if there is no symbol. + if (!Res.getSymA() && ELFRefKind == AArch64MCExpr::VK_INVALID) return false; - Addend = AddendExpr->getValue(); - if (BE->getOpcode() == MCBinaryExpr::Sub) - Addend = -Addend; + if (Res.getSymA()) + DarwinRefKind = Res.getSymA()->getKind(); + Addend = Res.getConstant(); // It's some symbol reference + a constant addend, but really // shouldn't use both Darwin and ELF syntax. diff --git a/lib/Target/AArch64/CMakeLists.txt b/lib/Target/AArch64/CMakeLists.txt index d9a00512f71d..7778882d4915 100644 --- a/lib/Target/AArch64/CMakeLists.txt +++ b/lib/Target/AArch64/CMakeLists.txt @@ -15,6 +15,7 @@ tablegen(LLVM AArch64GenRegisterBank.inc -gen-register-bank) tablegen(LLVM AArch64GenRegisterInfo.inc -gen-register-info) tablegen(LLVM AArch64GenSubtargetInfo.inc -gen-subtarget) tablegen(LLVM AArch64GenSystemOperands.inc -gen-searchable-tables) +tablegen(LLVM AArch64GenExegesis.inc -gen-exegesis) add_public_tablegen_target(AArch64CommonTableGen) @@ -22,6 +23,7 @@ add_llvm_target(AArch64CodeGen AArch64A57FPLoadBalancing.cpp AArch64AdvSIMDScalarPass.cpp AArch64AsmPrinter.cpp + AArch64BranchTargets.cpp AArch64CallLowering.cpp AArch64CleanupLocalDynamicTLSPass.cpp AArch64CollectLOH.cpp @@ -33,6 +35,7 @@ add_llvm_target(AArch64CodeGen AArch64FastISel.cpp AArch64A53Fix835769.cpp AArch64FrameLowering.cpp + AArch64CompressJumpTables.cpp AArch64ConditionOptimizer.cpp AArch64RedundantCopyElimination.cpp AArch64ISelDAGToDAG.cpp @@ -43,11 +46,13 @@ add_llvm_target(AArch64CodeGen AArch64LoadStoreOptimizer.cpp AArch64MacroFusion.cpp AArch64MCInstLower.cpp + AArch64PreLegalizerCombiner.cpp AArch64PromoteConstant.cpp AArch64PBQPRegAlloc.cpp AArch64RegisterBankInfo.cpp AArch64RegisterInfo.cpp AArch64SelectionDAGInfo.cpp + AArch64SpeculationHardening.cpp AArch64StorePairSuppress.cpp AArch64Subtarget.cpp AArch64TargetMachine.cpp diff --git a/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp index cef0ff346448..4102f1eb5cc1 100644 --- a/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp +++ b/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp @@ -12,7 +12,6 @@ #include "AArch64Disassembler.h" #include "AArch64ExternalSymbolizer.h" -#include "AArch64Subtarget.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" #include "Utils/AArch64BaseInfo.h" @@ -20,6 +19,8 @@ #include "llvm/MC/MCDisassembler/MCRelocationInfo.h" #include "llvm/MC/MCFixedLenDisassembler.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -159,8 +160,8 @@ static DecodeStatus DecodeModImmTiedInstruction(MCInst &Inst, uint32_t insn, const void *Decoder); static DecodeStatus DecodeAdrInstruction(MCInst &Inst, uint32_t insn, uint64_t Address, const void *Decoder); -static DecodeStatus DecodeBaseAddSubImm(MCInst &Inst, uint32_t insn, - uint64_t Address, const void *Decoder); +static DecodeStatus DecodeAddSubImmShift(MCInst &Inst, uint32_t insn, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeUnconditionalBranch(MCInst &Inst, uint32_t insn, uint64_t Address, const void *Decoder); @@ -219,6 +220,11 @@ static DecodeStatus DecodeImm8OptLsl(MCInst &Inst, unsigned Imm, static DecodeStatus DecodeSVEIncDecImm(MCInst &Inst, unsigned Imm, uint64_t Addr, const void *Decoder); +static DecodeStatus DecodeLoadAllocTagArrayInstruction(MCInst &Inst, + uint32_t insn, + uint64_t address, + const void* Decoder); + static bool Check(DecodeStatus &Out, DecodeStatus In) { switch (In) { case MCDisassembler::Success: @@ -1402,6 +1408,8 @@ static DecodeStatus DecodePairLdStInstruction(MCInst &Inst, uint32_t insn, case AArch64::STPSpost: case AArch64::LDPSpre: case AArch64::STPSpre: + case AArch64::STGPpre: + case AArch64::STGPpost: DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder); break; } @@ -1415,6 +1423,8 @@ static DecodeStatus DecodePairLdStInstruction(MCInst &Inst, uint32_t insn, case AArch64::LDPXpre: case AArch64::STPXpre: case AArch64::LDPSWpre: + case AArch64::STGPpre: + case AArch64::STGPpost: NeedsDisjointWritebackTransfer = true; LLVM_FALLTHROUGH; case AArch64::LDNPXi: @@ -1422,6 +1432,7 @@ static DecodeStatus DecodePairLdStInstruction(MCInst &Inst, uint32_t insn, case AArch64::LDPXi: case AArch64::STPXi: case AArch64::LDPSWi: + case AArch64::STGPi: DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder); DecodeGPR64RegisterClass(Inst, Rt2, Addr, Decoder); break; @@ -1652,8 +1663,8 @@ static DecodeStatus DecodeAdrInstruction(MCInst &Inst, uint32_t insn, return Success; } -static DecodeStatus DecodeBaseAddSubImm(MCInst &Inst, uint32_t insn, - uint64_t Addr, const void *Decoder) { +static DecodeStatus DecodeAddSubImmShift(MCInst &Inst, uint32_t insn, + uint64_t Addr, const void *Decoder) { unsigned Rd = fieldFromInstruction(insn, 0, 5); unsigned Rn = fieldFromInstruction(insn, 5, 5); unsigned Imm = fieldFromInstruction(insn, 10, 14); @@ -1711,11 +1722,17 @@ static DecodeStatus DecodeSystemPStateInstruction(MCInst &Inst, uint32_t insn, uint64_t op1 = fieldFromInstruction(insn, 16, 3); uint64_t op2 = fieldFromInstruction(insn, 5, 3); uint64_t crm = fieldFromInstruction(insn, 8, 4); - uint64_t pstate_field = (op1 << 3) | op2; + switch (pstate_field) { + case 0x01: // XAFlag + case 0x02: // AXFlag + return Fail; + } + if ((pstate_field == AArch64PState::PAN || - pstate_field == AArch64PState::UAO) && crm > 1) + pstate_field == AArch64PState::UAO || + pstate_field == AArch64PState::SSBS) && crm > 1) return Fail; Inst.addOperand(MCOperand::createImm(pstate_field)); @@ -1835,3 +1852,25 @@ static DecodeStatus DecodeSVEIncDecImm(MCInst &Inst, unsigned Imm, Inst.addOperand(MCOperand::createImm(Imm + 1)); return Success; } + +static DecodeStatus DecodeLoadAllocTagArrayInstruction(MCInst &Inst, + uint32_t insn, + uint64_t address, + const void* Decoder) { + unsigned Rn = fieldFromInstruction(insn, 5, 5); + unsigned Rt = fieldFromInstruction(insn, 0, 5); + + // Outputs + DecodeGPR64spRegisterClass(Inst, Rn, address, Decoder); + DecodeGPR64RegisterClass(Inst, Rt, address, Decoder); + + // Input (Rn again) + Inst.addOperand(Inst.getOperand(0)); + + //Do this post decode since the raw number for xzr and sp is the same + if (Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg()) { + return SoftFail; + } else { + return Success; + } +} diff --git a/lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp b/lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp index 6e64fc9347b9..342655a29b1d 100644 --- a/lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp +++ b/lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp @@ -8,12 +8,12 @@ //===----------------------------------------------------------------------===// #include "AArch64ExternalSymbolizer.h" -#include "AArch64Subtarget.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" @@ -60,6 +60,8 @@ getVariant(uint64_t LLVMDisassembler_VariantKind) { bool AArch64ExternalSymbolizer::tryAddingSymbolicOperand( MCInst &MI, raw_ostream &CommentStream, int64_t Value, uint64_t Address, bool IsBranch, uint64_t Offset, uint64_t InstSize) { + if (!SymbolLookUp) + return false; // FIXME: This method shares a lot of code with // MCExternalSymbolizer::tryAddingSymbolicOperand. It may be possible // refactor the MCExternalSymbolizer interface to allow more of this diff --git a/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp b/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp index 26e41215afc6..dcf2dd251149 100644 --- a/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp +++ b/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp @@ -775,8 +775,33 @@ bool AArch64InstPrinter::printSysAlias(const MCInst *MI, if (CnVal == 7) { switch (CmVal) { default: return false; + // Maybe IC, maybe Prediction Restriction + case 1: + switch (Op1Val) { + default: return false; + case 0: goto Search_IC; + case 3: goto Search_PRCTX; + } + // Prediction Restriction aliases + case 3: { + Search_PRCTX: + const AArch64PRCTX::PRCTX *PRCTX = AArch64PRCTX::lookupPRCTXByEncoding(Encoding >> 3); + if (!PRCTX || !PRCTX->haveFeatures(STI.getFeatureBits())) + return false; + + NeedsReg = PRCTX->NeedsReg; + switch (Op2Val) { + default: return false; + case 4: Ins = "cfp\t"; break; + case 5: Ins = "dvp\t"; break; + case 7: Ins = "cpp\t"; break; + } + Name = std::string(PRCTX->Name); + } + break; // IC aliases - case 1: case 5: { + case 5: { + Search_IC: const AArch64IC::IC *IC = AArch64IC::lookupICByEncoding(Encoding); if (!IC || !IC->haveFeatures(STI.getFeatureBits())) return false; @@ -787,7 +812,7 @@ bool AArch64InstPrinter::printSysAlias(const MCInst *MI, } break; // DC aliases - case 4: case 6: case 10: case 11: case 12: case 14: + case 4: case 6: case 10: case 11: case 12: case 13: case 14: { const AArch64DC::DC *DC = AArch64DC::lookupDCByEncoding(Encoding); if (!DC || !DC->haveFeatures(STI.getFeatureBits())) @@ -1097,6 +1122,17 @@ void AArch64InstPrinter::printPSBHintOp(const MCInst *MI, unsigned OpNum, O << '#' << formatImm(psbhintop); } +void AArch64InstPrinter::printBTIHintOp(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + unsigned btihintop = (MI->getOperand(OpNum).getImm() ^ 32) >> 1; + auto BTI = AArch64BTIHint::lookupBTIByEncoding(btihintop); + if (BTI) + O << BTI->Name; + else + O << '#' << formatImm(btihintop); +} + void AArch64InstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O) { diff --git a/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h b/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h index 8dc9264f94a1..4e9982f5b7be 100644 --- a/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h +++ b/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h @@ -131,6 +131,9 @@ protected: void printPSBHintOp(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); + void printBTIHintOp(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); + void printFPImmOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h b/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h index 62644ab2f457..688ca755d0b5 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h +++ b/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h @@ -16,6 +16,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/bit.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include <cassert> @@ -342,27 +343,23 @@ static inline bool isValidDecodeLogicalImmediate(uint64_t val, // static inline float getFPImmFloat(unsigned Imm) { // We expect an 8-bit binary encoding of a floating-point number here. - union { - uint32_t I; - float F; - } FPUnion; uint8_t Sign = (Imm >> 7) & 0x1; uint8_t Exp = (Imm >> 4) & 0x7; uint8_t Mantissa = Imm & 0xf; - // 8-bit FP iEEEE Float Encoding + // 8-bit FP IEEE Float Encoding // abcd efgh aBbbbbbc defgh000 00000000 00000000 // // where B = NOT(b); - FPUnion.I = 0; - FPUnion.I |= Sign << 31; - FPUnion.I |= ((Exp & 0x4) != 0 ? 0 : 1) << 30; - FPUnion.I |= ((Exp & 0x4) != 0 ? 0x1f : 0) << 25; - FPUnion.I |= (Exp & 0x3) << 23; - FPUnion.I |= Mantissa << 19; - return FPUnion.F; + uint32_t I = 0; + I |= Sign << 31; + I |= ((Exp & 0x4) != 0 ? 0 : 1) << 30; + I |= ((Exp & 0x4) != 0 ? 0x1f : 0) << 25; + I |= (Exp & 0x3) << 23; + I |= Mantissa << 19; + return bit_cast<float>(I); } /// getFP16Imm - Return an 8-bit floating-point version of the 16-bit @@ -757,12 +754,8 @@ static inline uint64_t decodeAdvSIMDModImmType12(uint8_t Imm) { /// Returns true if Imm is the concatenation of a repeating pattern of type T. template <typename T> static inline bool isSVEMaskOfIdenticalElements(int64_t Imm) { - union { - int64_t Whole; - T Parts[sizeof(int64_t)/sizeof(T)]; - } Vec { Imm }; - - return all_of(Vec.Parts, [Vec](T Elem) { return Elem == Vec.Parts[0]; }); + auto Parts = bit_cast<std::array<T, sizeof(int64_t) / sizeof(T)>>(Imm); + return all_of(Parts, [&](T Elem) { return Elem == Parts[0]; }); } /// Returns true if Imm is valid for CPY/DUP. @@ -790,29 +783,20 @@ static inline bool isSVEAddSubImm(int64_t Imm) { /// Return true if Imm is valid for DUPM and has no single CPY/DUP equivalent. static inline bool isSVEMoveMaskPreferredLogicalImmediate(int64_t Imm) { - union { - int64_t D; - int32_t S[2]; - int16_t H[4]; - int8_t B[8]; - } Vec = { Imm }; - - if (isSVECpyImm<int64_t>(Vec.D)) + if (isSVECpyImm<int64_t>(Imm)) return false; - if (isSVEMaskOfIdenticalElements<int32_t>(Imm) && - isSVECpyImm<int32_t>(Vec.S[0])) - return false; + auto S = bit_cast<std::array<int32_t, 2>>(Imm); + auto H = bit_cast<std::array<int16_t, 4>>(Imm); + auto B = bit_cast<std::array<int8_t, 8>>(Imm); - if (isSVEMaskOfIdenticalElements<int16_t>(Imm) && - isSVECpyImm<int16_t>(Vec.H[0])) + if (isSVEMaskOfIdenticalElements<int32_t>(Imm) && isSVECpyImm<int32_t>(S[0])) return false; - - if (isSVEMaskOfIdenticalElements<int8_t>(Imm) && - isSVECpyImm<int8_t>(Vec.B[0])) + if (isSVEMaskOfIdenticalElements<int16_t>(Imm) && isSVECpyImm<int16_t>(H[0])) return false; - - return isLogicalImmediate(Vec.D, 64); + if (isSVEMaskOfIdenticalElements<int8_t>(Imm) && isSVECpyImm<int8_t>(B[0])) + return false; + return isLogicalImmediate(Imm, 64); } inline static bool isAnyMOVZMovAlias(uint64_t Value, int RegWidth) { diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp index 856946555198..ed89d991d9fb 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// #include "AArch64.h" -#include "AArch64RegisterInfo.h" #include "MCTargetDesc/AArch64FixupKinds.h" +#include "MCTargetDesc/AArch64MCExpr.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/MCAsmBackend.h" @@ -19,6 +19,7 @@ #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCValue.h" @@ -109,11 +110,11 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { case FK_Data_1: return 1; - case AArch64::fixup_aarch64_movw: case FK_Data_2: case FK_SecRel_2: return 2; + case AArch64::fixup_aarch64_movw: case AArch64::fixup_aarch64_pcrel_branch14: case AArch64::fixup_aarch64_add_imm12: case AArch64::fixup_aarch64_ldst_imm12_scale1: @@ -144,9 +145,9 @@ static unsigned AdrImmBits(unsigned Value) { return (hi19 << 5) | (lo2 << 29); } -static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, - MCContext &Ctx, const Triple &TheTriple, - bool IsResolved) { +static uint64_t adjustFixupValue(const MCFixup &Fixup, const MCValue &Target, + uint64_t Value, MCContext &Ctx, + const Triple &TheTriple, bool IsResolved) { unsigned Kind = Fixup.getKind(); int64_t SignedValue = static_cast<int64_t>(Value); switch (Kind) { @@ -214,10 +215,79 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, if (Value & 0xf) Ctx.reportError(Fixup.getLoc(), "fixup must be 16-byte aligned"); return Value >> 4; - case AArch64::fixup_aarch64_movw: - Ctx.reportError(Fixup.getLoc(), - "no resolvable MOVZ/MOVK fixups supported yet"); + case AArch64::fixup_aarch64_movw: { + AArch64MCExpr::VariantKind RefKind = + static_cast<AArch64MCExpr::VariantKind>(Target.getRefKind()); + if (AArch64MCExpr::getSymbolLoc(RefKind) != AArch64MCExpr::VK_ABS && + AArch64MCExpr::getSymbolLoc(RefKind) != AArch64MCExpr::VK_SABS) { + // VK_GOTTPREL, VK_TPREL, VK_DTPREL are movw fixups, but they can't + // ever be resolved in the assembler. + Ctx.reportError(Fixup.getLoc(), + "relocation for a thread-local variable points to an " + "absolute symbol"); + return Value; + } + + if (!IsResolved) { + // FIXME: Figure out when this can actually happen, and verify our + // behavior. + Ctx.reportError(Fixup.getLoc(), "unresolved movw fixup not yet " + "implemented"); + return Value; + } + + if (AArch64MCExpr::getSymbolLoc(RefKind) == AArch64MCExpr::VK_SABS) { + switch (AArch64MCExpr::getAddressFrag(RefKind)) { + case AArch64MCExpr::VK_G0: + break; + case AArch64MCExpr::VK_G1: + SignedValue = SignedValue >> 16; + break; + case AArch64MCExpr::VK_G2: + SignedValue = SignedValue >> 32; + break; + case AArch64MCExpr::VK_G3: + SignedValue = SignedValue >> 48; + break; + default: + llvm_unreachable("Variant kind doesn't correspond to fixup"); + } + + } else { + switch (AArch64MCExpr::getAddressFrag(RefKind)) { + case AArch64MCExpr::VK_G0: + break; + case AArch64MCExpr::VK_G1: + Value = Value >> 16; + break; + case AArch64MCExpr::VK_G2: + Value = Value >> 32; + break; + case AArch64MCExpr::VK_G3: + Value = Value >> 48; + break; + default: + llvm_unreachable("Variant kind doesn't correspond to fixup"); + } + } + + if (RefKind & AArch64MCExpr::VK_NC) { + Value &= 0xFFFF; + } + else if (RefKind & AArch64MCExpr::VK_SABS) { + if (SignedValue > 0xFFFF || SignedValue < -0xFFFF) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + + // Invert the negative immediate because it will feed into a MOVN. + if (SignedValue < 0) + SignedValue = ~SignedValue; + Value = static_cast<uint64_t>(SignedValue); + } + else if (Value > 0xFFFF) { + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + } return Value; + } case AArch64::fixup_aarch64_pcrel_branch14: // Signed 16-bit immediate if (SignedValue > 32767 || SignedValue < -32768) @@ -294,8 +364,9 @@ void AArch64AsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, return; // Doesn't change encoding. MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); MCContext &Ctx = Asm.getContext(); + int64_t SignedValue = static_cast<int64_t>(Value); // Apply any target-specific value adjustments. - Value = adjustFixupValue(Fixup, Value, Ctx, TheTriple, IsResolved); + Value = adjustFixupValue(Fixup, Target, Value, Ctx, TheTriple, IsResolved); // Shift the value into position. Value <<= Info.TargetOffset; @@ -322,6 +393,19 @@ void AArch64AsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, Data[Offset + Idx] |= uint8_t((Value >> (i * 8)) & 0xff); } } + + // FIXME: getFixupKindInfo() and getFixupKindNumBytes() could be fixed to + // handle this more cleanly. This may affect the output of -show-mc-encoding. + AArch64MCExpr::VariantKind RefKind = + static_cast<AArch64MCExpr::VariantKind>(Target.getRefKind()); + if (RefKind & AArch64MCExpr::VK_SABS) { + // If the immediate is negative, generate MOVN else MOVZ. + // (Bit 30 = 0) ==> MOVN, (Bit 30 = 1) ==> MOVZ. + if (SignedValue < 0) + Data[Offset + 3] &= ~(1 << 6); + else + Data[Offset + 3] |= (1 << 6); + } } bool AArch64AsmBackend::mayNeedRelaxation(const MCInst &Inst, @@ -376,6 +460,14 @@ bool AArch64AsmBackend::shouldForceRelocation(const MCAssembler &Asm, // to the linker -- a relocation! if ((uint32_t)Fixup.getKind() == AArch64::fixup_aarch64_pcrel_adrp_imm21) return true; + + AArch64MCExpr::VariantKind RefKind = + static_cast<AArch64MCExpr::VariantKind>(Target.getRefKind()); + AArch64MCExpr::VariantKind SymLoc = AArch64MCExpr::getSymbolLoc(RefKind); + // LDR GOT relocations need a relocation + if ((uint32_t)Fixup.getKind() == AArch64::fixup_aarch64_ldr_pcrel_imm19 && + SymLoc == AArch64MCExpr::VK_GOT) + return true; return false; } diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp index a11e396217af..2ccd7cef8bef 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp @@ -138,7 +138,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx, } else return ELF::R_AARCH64_PREL64; case AArch64::fixup_aarch64_pcrel_adr_imm21: - assert(SymLoc == AArch64MCExpr::VK_NONE && "unexpected ADR relocation"); + if (SymLoc != AArch64MCExpr::VK_ABS) + Ctx.reportError(Fixup.getLoc(), + "invalid symbol kind for ADR relocation"); return R_CLS(ADR_PREL_LO21); case AArch64::fixup_aarch64_pcrel_adrp_imm21: if (SymLoc == AArch64MCExpr::VK_ABS && !IsNC) @@ -169,6 +171,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx, case AArch64::fixup_aarch64_ldr_pcrel_imm19: if (SymLoc == AArch64MCExpr::VK_GOTTPREL) return R_CLS(TLSIE_LD_GOTTPREL_PREL19); + if (SymLoc == AArch64MCExpr::VK_GOT) + return R_CLS(GOT_LD_PREL19); return R_CLS(LD_PREL_LO19); case AArch64::fixup_aarch64_pcrel_branch14: return R_CLS(TSTBR14); diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp index c0ef8b670286..9a7e34b0aeb1 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -60,16 +60,6 @@ void AArch64TargetAsmStreamer::emitInst(uint32_t Inst) { OS << "\t.inst\t0x" << Twine::utohexstr(Inst) << "\n"; } -class AArch64TargetELFStreamer : public AArch64TargetStreamer { -private: - AArch64ELFStreamer &getStreamer(); - - void emitInst(uint32_t Inst) override; - -public: - AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} -}; - /// Extend the generic ELFStreamer class so that it can emit mapping symbols at /// the appropriate points in the object files. These symbols are defined in the /// AArch64 ELF ABI: @@ -85,8 +75,6 @@ public: /// by MachO. Beware! class AArch64ELFStreamer : public MCELFStreamer { public: - friend class AArch64TargetELFStreamer; - AArch64ELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> TAB, std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter) @@ -154,6 +142,11 @@ public: MCELFStreamer::EmitValueImpl(Value, Size, Loc); } + void emitFill(const MCExpr &NumBytes, uint64_t FillValue, + SMLoc Loc) override { + EmitDataMappingSymbol(); + MCObjectStreamer::emitFill(NumBytes, FillValue, Loc); + } private: enum ElfMappingSymbol { EMS_None, @@ -192,6 +185,8 @@ private: } // end anonymous namespace +namespace llvm { + AArch64ELFStreamer &AArch64TargetELFStreamer::getStreamer() { return static_cast<AArch64ELFStreamer &>(Streamer); } @@ -200,8 +195,6 @@ void AArch64TargetELFStreamer::emitInst(uint32_t Inst) { getStreamer().emitInst(Inst); } -namespace llvm { - MCTargetStreamer *createAArch64AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, @@ -221,14 +214,4 @@ MCELFStreamer *createAArch64ELFStreamer(MCContext &Context, return S; } -MCTargetStreamer * -createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { - const Triple &TT = STI.getTargetTriple(); - if (TT.isOSBinFormatELF()) - return new AArch64TargetELFStreamer(S); - if (TT.isOSBinFormatCOFF()) - return new AArch64TargetWinCOFFStreamer(S); - return nullptr; -} - } // end namespace llvm diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp index ebb49121c1bf..58e4a9c9a9e9 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp @@ -115,6 +115,7 @@ AArch64MCAsmInfoMicrosoftCOFF::AArch64MCAsmInfoMicrosoftCOFF() { CommentString = ";"; ExceptionsType = ExceptionHandling::WinEH; + WinEHEncodingType = WinEH::EncodingType::Itanium; } AArch64MCAsmInfoGNUCOFF::AArch64MCAsmInfoGNUCOFF() { @@ -131,4 +132,7 @@ AArch64MCAsmInfoGNUCOFF::AArch64MCAsmInfoGNUCOFF() { CommentString = "//"; ExceptionsType = ExceptionHandling::DwarfCFI; + // The default is dwarf, but WinEH can be enabled optionally, which requires + // WinEHEncodingType to be set. + WinEHEncodingType = WinEH::EncodingType::Itanium; } diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp index cd937935ddbf..729486b1020c 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp @@ -13,11 +13,11 @@ //===----------------------------------------------------------------------===// #include "AArch64MCExpr.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCValue.h" -#include "llvm/Object/ELF.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; @@ -62,8 +62,10 @@ StringRef AArch64MCExpr::getVariantKindName() const { case VK_TLSDESC_LO12: return ":tlsdesc_lo12:"; case VK_ABS_PAGE: return ""; case VK_ABS_PAGE_NC: return ":pg_hi21_nc:"; + case VK_GOT: return ":got:"; case VK_GOT_PAGE: return ":got:"; case VK_GOT_LO12: return ":got_lo12:"; + case VK_GOTTPREL: return ":gottprel:"; case VK_GOTTPREL_PAGE: return ":gottprel:"; case VK_GOTTPREL_LO12_NC: return ":gottprel_lo12:"; case VK_GOTTPREL_G1: return ":gottprel_g1:"; diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp index 4ceda7e122f4..0f8198ba4e9b 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp @@ -16,6 +16,7 @@ #include "AArch64MCAsmInfo.h" #include "AArch64WinCOFFStreamer.h" #include "InstPrinter/AArch64InstPrinter.h" +#include "MCTargetDesc/AArch64AddressingModes.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCInstrAnalysis.h" @@ -24,12 +25,14 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; #define GET_INSTRINFO_MC_DESC +#define GET_INSTRINFO_MC_HELPERS #include "AArch64GenInstrInfo.inc" #define GET_SUBTARGETINFO_MC_DESC @@ -153,6 +156,31 @@ public: } return false; } + + std::vector<std::pair<uint64_t, uint64_t>> + findPltEntries(uint64_t PltSectionVA, ArrayRef<uint8_t> PltContents, + uint64_t GotPltSectionVA, + const Triple &TargetTriple) const override { + // Do a lightweight parsing of PLT entries. + std::vector<std::pair<uint64_t, uint64_t>> Result; + for (uint64_t Byte = 0, End = PltContents.size(); Byte + 7 < End; + Byte += 4) { + uint32_t Insn = support::endian::read32le(PltContents.data() + Byte); + // Check for adrp. + if ((Insn & 0x9f000000) != 0x90000000) + continue; + uint64_t Imm = (((PltSectionVA + Byte) >> 12) << 12) + + (((Insn >> 29) & 3) << 12) + (((Insn >> 5) & 0x3ffff) << 14); + uint32_t Insn2 = support::endian::read32le(PltContents.data() + Byte + 4); + // Check for: ldr Xt, [Xn, #pimm]. + if (Insn2 >> 22 == 0x3e5) { + Imm += ((Insn2 >> 10) & 0xfff) << 3; + Result.push_back(std::make_pair(PltSectionVA + Byte, Imm)); + Byte += 4; + } + } + return Result; + } }; } // end anonymous namespace diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.h b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.h index 63f50778ccdb..0f22f69bd5b0 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.h +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.h @@ -84,6 +84,7 @@ void initLLVMToCVRegMapping(MCRegisterInfo *MRI); // Defines symbolic names for the AArch64 instructions. // #define GET_INSTRINFO_ENUM +#define GET_INSTRINFO_MC_HELPER_DECLS #include "AArch64GenInstrInfo.inc" #define GET_SUBTARGETINFO_ENUM diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp index dee964df2635..a6b8d963bef9 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -13,6 +13,7 @@ #include "AArch64TargetStreamer.h" #include "llvm/MC/ConstantPools.h" +#include "llvm/MC/MCSubtargetInfo.h" using namespace llvm; @@ -52,3 +53,17 @@ void AArch64TargetStreamer::emitInst(uint32_t Inst) { getStreamer().EmitBytes(StringRef(Buffer, 4)); } + +namespace llvm { + +MCTargetStreamer * +createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + const Triple &TT = STI.getTargetTriple(); + if (TT.isOSBinFormatELF()) + return new AArch64TargetELFStreamer(S); + if (TT.isOSBinFormatCOFF()) + return new AArch64TargetWinCOFFStreamer(S); + return nullptr; +} + +} // end namespace llvm diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h index 51432830f795..73fb9baea3e3 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -12,6 +12,10 @@ #include "llvm/MC/MCStreamer.h" +namespace { +class AArch64ELFStreamer; +} + namespace llvm { class AArch64TargetStreamer : public MCTargetStreamer { @@ -33,10 +37,75 @@ public: /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); + virtual void EmitARM64WinCFIAllocStack(unsigned Size) {} + virtual void EmitARM64WinCFISaveFPLR(int Offset) {} + virtual void EmitARM64WinCFISaveFPLRX(int Offset) {} + virtual void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISetFP() {} + virtual void EmitARM64WinCFIAddFP(unsigned Size) {} + virtual void EmitARM64WinCFINop() {} + virtual void EmitARM64WinCFIPrologEnd() {} + virtual void EmitARM64WinCFIEpilogStart() {} + virtual void EmitARM64WinCFIEpilogEnd() {} + private: std::unique_ptr<AssemblerConstantPools> ConstantPools; }; +class AArch64TargetELFStreamer : public AArch64TargetStreamer { +private: + AArch64ELFStreamer &getStreamer(); + + void emitInst(uint32_t Inst) override; + +public: + AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} +}; + +class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer { +private: + // True if we are processing SEH directives in an epilogue. + bool InEpilogCFI = false; + + // Symbol of the current epilog for which we are processing SEH directives. + MCSymbol *CurrentEpilog = nullptr; +public: + AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S) + : AArch64TargetStreamer(S) {} + + // The unwind codes on ARM64 Windows are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + void EmitARM64WinCFIAllocStack(unsigned Size) override; + void EmitARM64WinCFISaveFPLR(int Offset) override; + void EmitARM64WinCFISaveFPLRX(int Offset) override; + void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISetFP() override; + void EmitARM64WinCFIAddFP(unsigned Size) override; + void EmitARM64WinCFINop() override; + void EmitARM64WinCFIPrologEnd() override; + void EmitARM64WinCFIEpilogStart() override; + void EmitARM64WinCFIEpilogEnd() override; +private: + void EmitARM64WinUnwindCode(unsigned UnwindCode, int Reg, int Offset); +}; + +MCTargetStreamer * +createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + } // end namespace llvm #endif diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp index 9871dc553bed..b828ab832e9d 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp @@ -11,31 +11,184 @@ #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCWin64EH.h" +#include "llvm/MC/MCWinCOFFStreamer.h" using namespace llvm; namespace { class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { -public: - friend class AArch64TargetWinCOFFStreamer; + Win64EH::ARM64UnwindEmitter EHStreamer; +public: AArch64WinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, std::unique_ptr<MCCodeEmitter> CE, std::unique_ptr<MCObjectWriter> OW) : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} + void EmitWinEHHandlerData(SMLoc Loc) override; + void EmitWindowsUnwindTables() override; void FinishImpl() override; }; +void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { + MCStreamer::EmitWinEHHandlerData(Loc); + + // We have to emit the unwind info now, because this directive + // actually switches to the .xdata section! + EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo()); +} + +void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { + if (!getNumWinFrameInfos()) + return; + EHStreamer.Emit(*this); +} + void AArch64WinCOFFStreamer::FinishImpl() { EmitFrames(nullptr); + EmitWindowsUnwindTables(); MCWinCOFFStreamer::FinishImpl(); } } // end anonymous namespace namespace llvm { + +// Helper function to common out unwind code setup for those codes that can +// belong to both prolog and epilog. +// There are three types of Windows ARM64 SEH codes. They can +// 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd +// 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X +// 3) take a register and an offset/size: all others +void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode, + int Reg, + int Offset) { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + MCSymbol *Label = S.EmitCFILabel(); + auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); + if (InEpilogCFI) + CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); + else + CurFrame->Instructions.push_back(Inst); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { + unsigned Op = Win64EH::UOP_AllocSmall; + if (Size >= 16384) + Op = Win64EH::UOP_AllocLarge; + else if (Size >= 512) + Op = Win64EH::UOP_AllocMedium; + EmitARM64WinUnwindCode(Op, -1, Size); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, + int Offset) { + assert(Offset >= 0 && Offset <= 504 && + "Offset for save reg should be >= 0 && <= 504"); + EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, + int Offset) { + assert(Offset >= 0 && Offset <= 504 && + "Offset for save reg should be >= 0 && <= 504"); + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { + EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { + assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); + EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { + EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); +} + +// The functions below handle opcodes that can end up in either a prolog or +// an epilog, but not both. +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + MCSymbol *Label = S.EmitCFILabel(); + CurFrame->PrologEnd = Label; + WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); + auto it = CurFrame->Instructions.begin(); + CurFrame->Instructions.insert(it, Inst); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + InEpilogCFI = true; + CurrentEpilog = S.EmitCFILabel(); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + InEpilogCFI = false; + MCSymbol *Label = S.EmitCFILabel(); + WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); + CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); + CurrentEpilog = nullptr; +} + MCWinCOFFStreamer *createAArch64WinCOFFStreamer( MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h b/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h index c05422163584..ed265a876ab3 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h +++ b/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h @@ -17,20 +17,6 @@ #include "AArch64TargetStreamer.h" #include "llvm/MC/MCWinCOFFStreamer.h" -namespace { -class AArch64WinCOFFStreamer; - -class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer { -private: - AArch64WinCOFFStreamer &getStreamer(); - -public: - AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S) - : AArch64TargetStreamer(S) {} -}; - -} // end anonymous namespace - namespace llvm { MCWinCOFFStreamer *createAArch64WinCOFFStreamer( diff --git a/lib/Target/AArch64/SVEInstrFormats.td b/lib/Target/AArch64/SVEInstrFormats.td index 7a8dd8bc5aee..23a65b345bad 100644 --- a/lib/Target/AArch64/SVEInstrFormats.td +++ b/lib/Target/AArch64/SVEInstrFormats.td @@ -1535,7 +1535,7 @@ multiclass sve_fp_2op_p_zd_HSD<bits<5> opc, string asm> { def _S : sve_fp_2op_p_zd<{ 0b10, opc }, asm, ZPR32, ZPR32, ElementSizeS>; def _D : sve_fp_2op_p_zd<{ 0b11, opc }, asm, ZPR64, ZPR64, ElementSizeD>; } - + //===----------------------------------------------------------------------===// // SVE Floating Point Unary Operations - Unpredicated Group //===----------------------------------------------------------------------===// diff --git a/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp b/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp index 23cc21ce2e7c..c88155db7037 100644 --- a/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp +++ b/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp @@ -62,6 +62,13 @@ namespace llvm { } namespace llvm { + namespace AArch64PRCTX { +#define GET_PRCTX_IMPL +#include "AArch64GenSystemOperands.inc" + } +} + +namespace llvm { namespace AArch64PRFM { #define GET_PRFM_IMPL #include "AArch64GenSystemOperands.inc" @@ -104,6 +111,13 @@ namespace llvm { } namespace llvm { + namespace AArch64BTIHint { +#define GET_BTI_IMPL +#include "AArch64GenSystemOperands.inc" + } +} + +namespace llvm { namespace AArch64SysReg { #define GET_SYSREG_IMPL #include "AArch64GenSystemOperands.inc" diff --git a/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/lib/Target/AArch64/Utils/AArch64BaseInfo.h index 2874c4ab42ea..44c6a6b44895 100644 --- a/lib/Target/AArch64/Utils/AArch64BaseInfo.h +++ b/lib/Target/AArch64/Utils/AArch64BaseInfo.h @@ -388,6 +388,14 @@ namespace AArch64PSBHint { #include "AArch64GenSystemOperands.inc" } +namespace AArch64BTIHint { + struct BTI : SysAlias { + using SysAlias::SysAlias; + }; + #define GET_BTI_DECL + #include "AArch64GenSystemOperands.inc" +} + namespace AArch64SE { enum ShiftExtSpecifiers { Invalid = -1, @@ -499,6 +507,14 @@ namespace AArch64TLBI { #include "AArch64GenSystemOperands.inc" } +namespace AArch64PRCTX { + struct PRCTX : SysAliasReg { + using SysAliasReg::SysAliasReg; + }; + #define GET_PRCTX_DECL + #include "AArch64GenSystemOperands.inc" +} + namespace AArch64II { /// Target Operand Flag enum. enum TOF { @@ -507,7 +523,7 @@ namespace AArch64II { MO_NO_FLAG, - MO_FRAGMENT = 0xf, + MO_FRAGMENT = 0x7, /// MO_PAGE - A symbol operand with this flag represents the pc-relative /// offset of the 4K page containing the symbol. This is used with the @@ -540,6 +556,11 @@ namespace AArch64II { /// by-12-bits instruction. MO_HI12 = 7, + /// MO_COFFSTUB - On a symbol operand "FOO", this indicates that the + /// reference is actually to the ".refptrp.FOO" symbol. This is used for + /// stub symbols on windows. + MO_COFFSTUB = 0x8, + /// MO_GOT - This flag indicates that a symbol operand represents the /// address of the GOT entry for the symbol, rather than the address of /// the symbol itself. @@ -560,6 +581,10 @@ namespace AArch64II { /// to the symbol is for an import stub. This is used for DLL import /// storage class indication on Windows. MO_DLLIMPORT = 0x80, + + /// MO_S - Indicates that the bits of the symbol operand represented by + /// MO_G0 etc are signed. + MO_S = 0x100, }; } // end namespace AArch64II |
