aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/AArch64
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Target/AArch64
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'lib/Target/AArch64')
-rw-r--r--lib/Target/AArch64/AArch64.h8
-rw-r--r--lib/Target/AArch64/AArch64.td242
-rw-r--r--lib/Target/AArch64/AArch64A57FPLoadBalancing.cpp29
-rw-r--r--lib/Target/AArch64/AArch64AsmPrinter.cpp282
-rw-r--r--lib/Target/AArch64/AArch64BranchTargets.cpp130
-rw-r--r--lib/Target/AArch64/AArch64CallLowering.cpp61
-rw-r--r--lib/Target/AArch64/AArch64CallLowering.h4
-rw-r--r--lib/Target/AArch64/AArch64CallingConvention.td18
-rw-r--r--lib/Target/AArch64/AArch64CompressJumpTables.cpp162
-rw-r--r--lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp67
-rw-r--r--lib/Target/AArch64/AArch64FastISel.cpp43
-rw-r--r--lib/Target/AArch64/AArch64FrameLowering.cpp902
-rw-r--r--lib/Target/AArch64/AArch64FrameLowering.h11
-rw-r--r--lib/Target/AArch64/AArch64ISelDAGToDAG.cpp48
-rw-r--r--lib/Target/AArch64/AArch64ISelLowering.cpp956
-rw-r--r--lib/Target/AArch64/AArch64ISelLowering.h25
-rw-r--r--lib/Target/AArch64/AArch64InstrFormats.td402
-rw-r--r--lib/Target/AArch64/AArch64InstrInfo.cpp1145
-rw-r--r--lib/Target/AArch64/AArch64InstrInfo.h51
-rw-r--r--lib/Target/AArch64/AArch64InstrInfo.td503
-rw-r--r--lib/Target/AArch64/AArch64InstructionSelector.cpp222
-rw-r--r--lib/Target/AArch64/AArch64LegalizerInfo.cpp64
-rw-r--r--lib/Target/AArch64/AArch64LegalizerInfo.h4
-rw-r--r--lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp8
-rw-r--r--lib/Target/AArch64/AArch64MCInstLower.cpp72
-rw-r--r--lib/Target/AArch64/AArch64MachineFunctionInfo.h23
-rw-r--r--lib/Target/AArch64/AArch64MacroFusion.cpp375
-rw-r--r--lib/Target/AArch64/AArch64PfmCounters.td19
-rw-r--r--lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp108
-rw-r--r--lib/Target/AArch64/AArch64RegisterBankInfo.cpp1
-rw-r--r--lib/Target/AArch64/AArch64RegisterInfo.cpp118
-rw-r--r--lib/Target/AArch64/AArch64RegisterInfo.h18
-rw-r--r--lib/Target/AArch64/AArch64RegisterInfo.td6
-rw-r--r--lib/Target/AArch64/AArch64SchedExynosM1.td120
-rw-r--r--lib/Target/AArch64/AArch64SchedExynosM3.td200
-rw-r--r--lib/Target/AArch64/AArch64SchedExynosM4.td1004
-rw-r--r--lib/Target/AArch64/AArch64SchedPredExynos.td157
-rw-r--r--lib/Target/AArch64/AArch64SchedPredicates.td423
-rw-r--r--lib/Target/AArch64/AArch64Schedule.td11
-rw-r--r--lib/Target/AArch64/AArch64SpeculationHardening.cpp641
-rw-r--r--lib/Target/AArch64/AArch64StorePairSuppress.cpp6
-rw-r--r--lib/Target/AArch64/AArch64Subtarget.cpp81
-rw-r--r--lib/Target/AArch64/AArch64Subtarget.h113
-rw-r--r--lib/Target/AArch64/AArch64SystemOperands.td195
-rw-r--r--lib/Target/AArch64/AArch64TargetMachine.cpp81
-rw-r--r--lib/Target/AArch64/AArch64TargetObjectFile.cpp3
-rw-r--r--lib/Target/AArch64/AArch64TargetTransformInfo.cpp25
-rw-r--r--lib/Target/AArch64/AArch64TargetTransformInfo.h4
-rw-r--r--lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp324
-rw-r--r--lib/Target/AArch64/CMakeLists.txt5
-rw-r--r--lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp53
-rw-r--r--lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp4
-rw-r--r--lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp40
-rw-r--r--lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h3
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h56
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp110
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp6
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp31
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp4
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp4
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp28
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.h1
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp15
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h69
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp157
-rw-r--r--lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h14
-rw-r--r--lib/Target/AArch64/SVEInstrFormats.td2
-rw-r--r--lib/Target/AArch64/Utils/AArch64BaseInfo.cpp14
-rw-r--r--lib/Target/AArch64/Utils/AArch64BaseInfo.h27
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