diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
| commit | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (patch) | |
| tree | 599ab169a01f1c86eda9adc774edaedde2f2db5b /lib/Target/ARM | |
| parent | 1a56a5ead7a2e84bee8240f5f6b033b5f1707154 (diff) | |
Notes
Diffstat (limited to 'lib/Target/ARM')
125 files changed, 15631 insertions, 4009 deletions
diff --git a/lib/Target/ARM/A15SDOptimizer.cpp b/lib/Target/ARM/A15SDOptimizer.cpp index be88fe4ddb14..fb238bfc9cbc 100644 --- a/lib/Target/ARM/A15SDOptimizer.cpp +++ b/lib/Target/ARM/A15SDOptimizer.cpp @@ -1,9 +1,8 @@ //=== A15SDOptimizerPass.cpp - Optimize DPR and SPR register accesses on A15==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARM.h b/lib/Target/ARM/ARM.h index b5cc45c5cc94..bf8ed6562fe7 100644 --- a/lib/Target/ARM/ARM.h +++ b/lib/Target/ARM/ARM.h @@ -1,9 +1,8 @@ //===-- ARM.h - Top-level interface for ARM representation ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -36,7 +35,7 @@ class MachineInstr; class MCInst; class PassRegistry; - +FunctionPass *createARMLowOverheadLoopsPass(); Pass *createARMParallelDSPPass(); FunctionPass *createARMISelDag(ARMBaseTargetMachine &TM, CodeGenOpt::Level OptLevel); @@ -47,6 +46,7 @@ FunctionPass *createARMCodeGenPreparePass(); FunctionPass *createARMConstantIslandPass(); FunctionPass *createMLxExpansionPass(); FunctionPass *createThumb2ITBlockPass(); +FunctionPass *createMVEVPTBlockPass(); FunctionPass *createARMOptimizeBarriersPass(); FunctionPass *createThumb2SizeReductionPass( std::function<bool(const Function &)> Ftor = nullptr); @@ -57,11 +57,6 @@ createARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget void LowerARMMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, ARMAsmPrinter &AP); -void computeBlockSize(MachineFunction *MF, MachineBasicBlock *MBB, - BasicBlockInfo &BBI); -std::vector<BasicBlockInfo> computeAllBlockSizes(MachineFunction *MF); - - void initializeARMParallelDSPPass(PassRegistry &); void initializeARMLoadStoreOptPass(PassRegistry &); void initializeARMPreAllocLoadStoreOptPass(PassRegistry &); @@ -69,6 +64,9 @@ void initializeARMCodeGenPreparePass(PassRegistry &); void initializeARMConstantIslandsPass(PassRegistry &); void initializeARMExpandPseudoPass(PassRegistry &); void initializeThumb2SizeReducePass(PassRegistry &); +void initializeThumb2ITBlockPass(PassRegistry &); +void initializeMVEVPTBlockPass(PassRegistry &); +void initializeARMLowOverheadLoopsPass(PassRegistry &); } // end namespace llvm diff --git a/lib/Target/ARM/ARM.td b/lib/Target/ARM/ARM.td index 3db60f1c16d6..b687db12eaf5 100644 --- a/lib/Target/ARM/ARM.td +++ b/lib/Target/ARM/ARM.td @@ -1,9 +1,8 @@ //===-- ARM.td - Describe the ARM Target Machine -----------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -33,12 +32,59 @@ def ModeSoftFloat : SubtargetFeature<"soft-float","UseSoftFloat", // // Floating Point, HW Division and Neon Support -def FeatureVFP2 : SubtargetFeature<"vfp2", "HasVFPv2", "true", - "Enable VFP2 instructions">; -def FeatureVFP3 : SubtargetFeature<"vfp3", "HasVFPv3", "true", - "Enable VFP3 instructions", - [FeatureVFP2]>; +// FP loads/stores/moves, shared between VFP and MVE (even in the integer-only +// version). +def FeatureFPRegs : SubtargetFeature<"fpregs", "HasFPRegs", "true", + "Enable FP registers">; + +// 16-bit FP loads/stores/moves, shared between VFP (with the v8.2A FP16 +// extension) and MVE (even in the integer-only version). +def FeatureFPRegs16 : SubtargetFeature<"fpregs16", "HasFPRegs16", "true", + "Enable 16-bit FP registers", + [FeatureFPRegs]>; + +def FeatureFPRegs64 : SubtargetFeature<"fpregs64", "HasFPRegs64", "true", + "Enable 64-bit FP registers", + [FeatureFPRegs]>; + +def FeatureFP64 : SubtargetFeature<"fp64", "HasFP64", "true", + "Floating point unit supports " + "double precision", + [FeatureFPRegs64]>; + +def FeatureD32 : SubtargetFeature<"d32", "HasD32", "true", + "Extend FP to 32 double registers">; + +multiclass VFPver<string name, string query, string description, + list<SubtargetFeature> prev = [], + list<SubtargetFeature> otherimplies = []> { + def _D16_SP: SubtargetFeature< + name#"d16sp", query#"D16SP", "true", + description#" with only 16 d-registers and no double precision", + !foreach(v, prev, !cast<SubtargetFeature>(v # "_D16_SP")) # otherimplies>; + def _SP: SubtargetFeature< + name#"sp", query#"SP", "true", + description#" with no double precision", + !foreach(v, prev, !cast<SubtargetFeature>(v # "_SP")) # + otherimplies # [FeatureD32, !cast<SubtargetFeature>(NAME # "_D16_SP")]>; + def _D16: SubtargetFeature< + name#"d16", query#"D16", "true", + description#" with only 16 d-registers", + !foreach(v, prev, !cast<SubtargetFeature>(v # "_D16")) # + otherimplies # [FeatureFP64, !cast<SubtargetFeature>(NAME # "_D16_SP")]>; + def "": SubtargetFeature< + name, query, "true", description, + prev # otherimplies # [ + !cast<SubtargetFeature>(NAME # "_D16"), + !cast<SubtargetFeature>(NAME # "_SP")]>; +} + +defm FeatureVFP2: VFPver<"vfp2", "HasVFPv2", "Enable VFP2 instructions", + [], [FeatureFPRegs]>; + +defm FeatureVFP3: VFPver<"vfp3", "HasVFPv3", "Enable VFP3 instructions", + [FeatureVFP2]>; def FeatureNEON : SubtargetFeature<"neon", "HasNEON", "true", "Enable NEON instructions", @@ -48,31 +94,22 @@ def FeatureFP16 : SubtargetFeature<"fp16", "HasFP16", "true", "Enable half-precision " "floating point">; -def FeatureVFP4 : SubtargetFeature<"vfp4", "HasVFPv4", "true", - "Enable VFP4 instructions", - [FeatureVFP3, FeatureFP16]>; +defm FeatureVFP4: VFPver<"vfp4", "HasVFPv4", "Enable VFP4 instructions", + [FeatureVFP3], [FeatureFP16]>; -def FeatureFPARMv8 : SubtargetFeature<"fp-armv8", "HasFPARMv8", - "true", "Enable ARMv8 FP", - [FeatureVFP4]>; +defm FeatureFPARMv8: VFPver<"fp-armv8", "HasFPARMv8", "Enable ARMv8 FP", + [FeatureVFP4]>; def FeatureFullFP16 : SubtargetFeature<"fullfp16", "HasFullFP16", "true", "Enable full half-precision " "floating point", - [FeatureFPARMv8]>; + [FeatureFPARMv8_D16_SP, FeatureFPRegs16]>; def FeatureFP16FML : SubtargetFeature<"fp16fml", "HasFP16FML", "true", "Enable full half-precision " "floating point fml instructions", [FeatureFullFP16]>; -def FeatureVFPOnlySP : SubtargetFeature<"fp-only-sp", "FPOnlySP", "true", - "Floating point unit supports " - "single precision only">; - -def FeatureD16 : SubtargetFeature<"d16", "HasD16", "true", - "Restrict FP to 16 double registers">; - def FeatureHWDivThumb : SubtargetFeature<"hwdiv", "HasHardwareDivideInThumb", "true", "Enable divide instructions in Thumb">; @@ -368,6 +405,12 @@ def FeatureUseAA : SubtargetFeature<"use-aa", "UseAA", "true", def FeatureSB : SubtargetFeature<"sb", "HasSB", "true", "Enable v8.5a Speculation Barrier" >; +// Armv8.1-M extensions + +def FeatureLOB : SubtargetFeature<"lob", "HasLOB", "true", + "Enable Low Overhead Branch " + "extensions">; + //===----------------------------------------------------------------------===// // ARM architecture class // @@ -461,6 +504,19 @@ def HasV8_5aOps : SubtargetFeature<"v8.5a", "HasV8_5aOps", "true", "Support ARM v8.5a instructions", [HasV8_4aOps, FeatureSB]>; +def HasV8_1MMainlineOps : SubtargetFeature< + "v8.1m.main", "HasV8_1MMainlineOps", "true", + "Support ARM v8-1M Mainline instructions", + [HasV8MMainlineOps]>; +def HasMVEIntegerOps : SubtargetFeature< + "mve", "HasMVEIntegerOps", "true", + "Support M-Class Vector Extension with integer ops", + [HasV8_1MMainlineOps, FeatureDSP, FeatureFPRegs16, FeatureFPRegs64]>; +def HasMVEFloatOps : SubtargetFeature< + "mve.fp", "HasMVEFloatOps", "true", + "Support M-Class Vector Extension with integer and floating ops", + [HasMVEIntegerOps, FeatureFPARMv8_D16_SP, FeatureFullFP16]>; + //===----------------------------------------------------------------------===// // ARM Processor subtarget features. // @@ -495,6 +551,8 @@ def ProcA73 : SubtargetFeature<"a73", "ARMProcFamily", "CortexA73", "Cortex-A73 ARM processors", []>; def ProcA75 : SubtargetFeature<"a75", "ARMProcFamily", "CortexA75", "Cortex-A75 ARM processors", []>; +def ProcA76 : SubtargetFeature<"a76", "ARMProcFamily", "CortexA76", + "Cortex-A76 ARM processors", []>; def ProcKrait : SubtargetFeature<"krait", "ARMProcFamily", "Krait", "Qualcomm Krait processors", []>; @@ -744,6 +802,18 @@ def ARMv8mMainline : Architecture<"armv8-m.main", "ARMv8mMainline", FeatureAcquireRelease, FeatureMClass]>; +def ARMv81mMainline : Architecture<"armv8.1-m.main", "ARMv81mMainline", + [HasV8_1MMainlineOps, + FeatureNoARM, + ModeThumb, + FeatureDB, + FeatureHWDivThumb, + Feature8MSecExt, + FeatureAcquireRelease, + FeatureMClass, + FeatureRAS, + FeatureLOB]>; + // Aliases def IWMMXT : Architecture<"iwmmxt", "ARMv5te", [ARMv5te]>; def IWMMXT2 : Architecture<"iwmmxt2", "ARMv5te", [ARMv5te]>; @@ -757,6 +827,7 @@ def ARMv7s : Architecture<"armv7s", "ARMv7a", [ARMv7a]>; // ARM schedules. //===----------------------------------------------------------------------===// // +include "ARMPredicates.td" include "ARMSchedule.td" //===----------------------------------------------------------------------===// @@ -942,14 +1013,12 @@ def : ProcessorModel<"cortex-r4f", CortexA8Model, [ARMv7r, ProcR4, FeatureHasRetAddrStack, FeatureSlowFPBrcc, FeatureHasSlowFPVMLx, - FeatureVFP3, - FeatureD16, + FeatureVFP3_D16, FeatureAvoidPartialCPSR]>; def : ProcessorModel<"cortex-r5", CortexA8Model, [ARMv7r, ProcR5, FeatureHasRetAddrStack, - FeatureVFP3, - FeatureD16, + FeatureVFP3_D16, FeatureSlowFPBrcc, FeatureHWDivARM, FeatureHasSlowFPVMLx, @@ -957,8 +1026,7 @@ def : ProcessorModel<"cortex-r5", CortexA8Model, [ARMv7r, ProcR5, def : ProcessorModel<"cortex-r7", CortexA8Model, [ARMv7r, ProcR7, FeatureHasRetAddrStack, - FeatureVFP3, - FeatureD16, + FeatureVFP3_D16, FeatureFP16, FeatureMP, FeatureSlowFPBrcc, @@ -968,8 +1036,7 @@ def : ProcessorModel<"cortex-r7", CortexA8Model, [ARMv7r, ProcR7, def : ProcessorModel<"cortex-r8", CortexA8Model, [ARMv7r, FeatureHasRetAddrStack, - FeatureVFP3, - FeatureD16, + FeatureVFP3_D16, FeatureFP16, FeatureMP, FeatureSlowFPBrcc, @@ -977,39 +1044,52 @@ def : ProcessorModel<"cortex-r8", CortexA8Model, [ARMv7r, FeatureHasSlowFPVMLx, FeatureAvoidPartialCPSR]>; -def : ProcessorModel<"cortex-m3", CortexM3Model, [ARMv7m, +def : ProcessorModel<"cortex-m3", CortexM4Model, [ARMv7m, ProcM3, FeaturePrefLoopAlign32, + FeatureUseMISched, + FeatureUseAA, FeatureHasNoBranchPredictor]>; -def : ProcessorModel<"sc300", CortexM3Model, [ARMv7m, +def : ProcessorModel<"sc300", CortexM4Model, [ARMv7m, ProcM3, + FeatureUseMISched, + FeatureUseAA, FeatureHasNoBranchPredictor]>; -def : ProcessorModel<"cortex-m4", CortexM3Model, [ARMv7em, - FeatureVFP4, - FeatureVFPOnlySP, - FeatureD16, +def : ProcessorModel<"cortex-m4", CortexM4Model, [ARMv7em, + FeatureVFP4_D16_SP, FeaturePrefLoopAlign32, FeatureHasSlowFPVMLx, + FeatureUseMISched, + FeatureUseAA, FeatureHasNoBranchPredictor]>; def : ProcNoItin<"cortex-m7", [ARMv7em, - FeatureFPARMv8, - FeatureD16]>; + FeatureFPARMv8_D16]>; def : ProcNoItin<"cortex-m23", [ARMv8mBaseline, FeatureNoMovt]>; -def : ProcessorModel<"cortex-m33", CortexM3Model, [ARMv8mMainline, +def : ProcessorModel<"cortex-m33", CortexM4Model, [ARMv8mMainline, FeatureDSP, - FeatureFPARMv8, - FeatureD16, - FeatureVFPOnlySP, + FeatureFPARMv8_D16_SP, FeaturePrefLoopAlign32, FeatureHasSlowFPVMLx, + FeatureUseMISched, + FeatureUseAA, FeatureHasNoBranchPredictor]>; +def : ProcessorModel<"cortex-m35p", CortexM4Model, [ARMv8mMainline, + FeatureDSP, + FeatureFPARMv8_D16_SP, + FeaturePrefLoopAlign32, + FeatureHasSlowFPVMLx, + FeatureUseMISched, + FeatureUseAA, + FeatureHasNoBranchPredictor]>; + + def : ProcNoItin<"cortex-a32", [ARMv8a, FeatureHWDivThumb, FeatureHWDivARM, @@ -1060,6 +1140,22 @@ def : ProcNoItin<"cortex-a75", [ARMv82a, ProcA75, FeatureHWDivARM, FeatureDotProd]>; +def : ProcNoItin<"cortex-a76", [ARMv82a, ProcA76, + FeatureHWDivThumb, + FeatureHWDivARM, + FeatureCrypto, + FeatureCRC, + FeatureFullFP16, + FeatureDotProd]>; + +def : ProcNoItin<"cortex-a76ae", [ARMv82a, ProcA76, + FeatureHWDivThumb, + FeatureHWDivARM, + FeatureCrypto, + FeatureCRC, + FeatureFullFP16, + FeatureDotProd]>; + def : ProcessorModel<"cyclone", SwiftModel, [ARMv8a, ProcSwift, FeatureHasRetAddrStack, FeatureNEONForFP, @@ -1081,6 +1177,9 @@ def : ProcNoItin<"exynos-m3", [ARMv8a, ProcExynos]>; def : ProcNoItin<"exynos-m4", [ARMv82a, ProcExynos, FeatureFullFP16, FeatureDotProd]>; +def : ProcNoItin<"exynos-m5", [ARMv82a, ProcExynos, + FeatureFullFP16, + FeatureDotProd]>; def : ProcNoItin<"kryo", [ARMv8a, ProcKryo, FeatureHWDivThumb, diff --git a/lib/Target/ARM/ARMAsmPrinter.cpp b/lib/Target/ARM/ARMAsmPrinter.cpp index b7cd3a0c2dae..e29077266fcd 100644 --- a/lib/Target/ARM/ARMAsmPrinter.cpp +++ b/lib/Target/ARM/ARMAsmPrinter.cpp @@ -1,9 +1,8 @@ //===-- ARMAsmPrinter.cpp - Print machine code to an ARM .s file ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -18,9 +17,10 @@ #include "ARMMachineFunctionInfo.h" #include "ARMTargetMachine.h" #include "ARMTargetObjectFile.h" -#include "InstPrinter/ARMInstPrinter.h" #include "MCTargetDesc/ARMAddressingModes.h" +#include "MCTargetDesc/ARMInstPrinter.h" #include "MCTargetDesc/ARMMCExpr.h" +#include "TargetInfo/ARMTargetInfo.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/BinaryFormat/COFF.h" @@ -120,13 +120,13 @@ bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { // Calculate this function's optimization goal. unsigned OptimizationGoal; - if (F.hasFnAttribute(Attribute::OptimizeNone)) + if (F.hasOptNone()) // For best debugging illusion, speed and small size sacrificed OptimizationGoal = 6; - else if (F.optForMinSize()) + else if (F.hasMinSize()) // Aggressively for small size, speed and debug illusion sacrificed OptimizationGoal = 4; - else if (F.optForSize()) + else if (F.hasOptSize()) // For small size, but speed and debugging illusion preserved OptimizationGoal = 3; else if (TM.getOptLevel() == CodeGenOpt::Aggressive) @@ -184,10 +184,21 @@ bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { return false; } +void ARMAsmPrinter::PrintSymbolOperand(const MachineOperand &MO, + raw_ostream &O) { + assert(MO.isGlobal() && "caller should check MO.isGlobal"); + unsigned TF = MO.getTargetFlags(); + if (TF & ARMII::MO_LO16) + O << ":lower16:"; + else if (TF & ARMII::MO_HI16) + O << ":upper16:"; + GetARMGVSymbol(MO.getGlobal(), TF)->print(O, MAI); + printOffset(MO.getOffset(), O); +} + void ARMAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O) { const MachineOperand &MO = MI->getOperand(OpNum); - unsigned TF = MO.getTargetFlags(); switch (MO.getType()) { default: llvm_unreachable("<unknown operand type>"); @@ -204,27 +215,20 @@ void ARMAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, break; } case MachineOperand::MO_Immediate: { - int64_t Imm = MO.getImm(); O << '#'; + unsigned TF = MO.getTargetFlags(); if (TF == ARMII::MO_LO16) O << ":lower16:"; else if (TF == ARMII::MO_HI16) O << ":upper16:"; - O << Imm; + O << MO.getImm(); break; } case MachineOperand::MO_MachineBasicBlock: MO.getMBB()->getSymbol()->print(O, MAI); return; case MachineOperand::MO_GlobalAddress: { - const GlobalValue *GV = MO.getGlobal(); - if (TF & ARMII::MO_LO16) - O << ":lower16:"; - else if (TF & ARMII::MO_HI16) - O << ":upper16:"; - GetARMGVSymbol(GV, TF)->print(O, MAI); - - printOffset(MO.getOffset(), O); + PrintSymbolOperand(MO, O); break; } case MachineOperand::MO_ConstantPoolIndex: @@ -256,8 +260,7 @@ GetARMJTIPICJumpTableLabel(unsigned uid) const { } bool ARMAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, - unsigned AsmVariant, const char *ExtraCode, - raw_ostream &O) { + const char *ExtraCode, raw_ostream &O) { // Does this asm operand have a single letter operand modifier? if (ExtraCode && ExtraCode[0]) { if (ExtraCode[1] != 0) return true; // Unknown modifier. @@ -265,20 +268,7 @@ bool ARMAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, switch (ExtraCode[0]) { default: // See if this is a generic print operand - return AsmPrinter::PrintAsmOperand(MI, OpNum, AsmVariant, ExtraCode, O); - case 'a': // Print as a memory address. - if (MI->getOperand(OpNum).isReg()) { - O << "[" - << ARMInstPrinter::getRegisterName(MI->getOperand(OpNum).getReg()) - << "]"; - return false; - } - LLVM_FALLTHROUGH; - case 'c': // Don't print "#" before an immediate operand. - if (!MI->getOperand(OpNum).isImm()) - return true; - O << MI->getOperand(OpNum).getImm(); - return false; + return AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O); case 'P': // Print a VFP double precision register. case 'q': // Print a NEON quad precision register. printOperand(MI, OpNum, O); @@ -444,8 +434,7 @@ bool ARMAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, } bool ARMAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, - unsigned OpNum, unsigned AsmVariant, - const char *ExtraCode, + unsigned OpNum, const char *ExtraCode, raw_ostream &O) { // Does this asm operand have a single letter operand modifier? if (ExtraCode && ExtraCode[0]) { @@ -668,7 +657,7 @@ void ARMAsmPrinter::emitAttributes() { ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal, ARMBuildAttrs::IEEEDenormals); else { - if (!STI.hasVFP2()) { + if (!STI.hasVFP2Base()) { // When the target doesn't have an FPU (by design or // intention), the assumptions made on the software support // mirror that of the equivalent hardware support *if it @@ -678,7 +667,7 @@ void ARMAsmPrinter::emitAttributes() { if (STI.hasV7Ops()) ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal, ARMBuildAttrs::PreserveFPSign); - } else if (STI.hasVFP3()) { + } else if (STI.hasVFP3Base()) { // In VFPv4, VFPv4U, VFPv3, or VFPv3U, it is preserved. That is, // the sign bit of the zero matches the sign bit of the input or // result that is being flushed to zero. @@ -773,6 +762,14 @@ void ARMAsmPrinter::emitAttributes() { //===----------------------------------------------------------------------===// +static MCSymbol *getBFLabel(StringRef Prefix, unsigned FunctionNumber, + unsigned LabelId, MCContext &Ctx) { + + MCSymbol *Label = Ctx.getOrCreateSymbol(Twine(Prefix) + + "BF" + Twine(FunctionNumber) + "_" + Twine(LabelId)); + return Label; +} + static MCSymbol *getPICLabel(StringRef Prefix, unsigned FunctionNumber, unsigned LabelId, MCContext &Ctx) { @@ -1074,7 +1071,6 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { const TargetRegisterInfo *TargetRegInfo = MF.getSubtarget().getRegisterInfo(); const MachineRegisterInfo &MachineRegInfo = MF.getRegInfo(); - const ARMFunctionInfo &AFI = *MF.getInfo<ARMFunctionInfo>(); unsigned FramePtr = TargetRegInfo->getFrameRegister(MF); unsigned Opc = MI->getOpcode(); @@ -1138,7 +1134,12 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { Pad += Width; continue; } - RegList.push_back(MO.getReg()); + // Check for registers that are remapped (for a Thumb1 prologue that + // saves high registers). + unsigned Reg = MO.getReg(); + if (unsigned RemappedReg = AFI->EHPrologueRemappedRegs.lookup(Reg)) + Reg = RemappedReg; + RegList.push_back(Reg); } break; case ARM::STR_PRE_IMM: @@ -1188,7 +1189,7 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { unsigned CPI = MI->getOperand(1).getIndex(); const MachineConstantPool *MCP = MF.getConstantPool(); if (CPI >= MCP->getConstants().size()) - CPI = AFI.getOriginalCPIdx(CPI); + CPI = AFI->getOriginalCPIdx(CPI); assert(CPI != -1U && "Invalid constpool index"); // Derive the actual offset. @@ -1218,8 +1219,12 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { } else if (DstReg == ARM::SP) { MI->print(errs()); llvm_unreachable("Unsupported opcode for unwinding information"); - } - else { + } else if (Opc == ARM::tMOVr) { + // If a Thumb1 function spills r8-r11, we copy the values to low + // registers before pushing them. Record the copy so we can emit the + // correct ".save" later. + AFI->EHPrologueRemappedRegs[DstReg] = SrcReg; + } else { MI->print(errs()); llvm_unreachable("Unsupported opcode for unwinding information"); } @@ -1447,6 +1452,66 @@ void ARMAsmPrinter::EmitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); return; } + case ARM::t2BFi: + case ARM::t2BFic: + case ARM::t2BFLi: + case ARM::t2BFr: + case ARM::t2BFLr: { + // This is a Branch Future instruction. + + const MCExpr *BranchLabel = MCSymbolRefExpr::create( + getBFLabel(DL.getPrivateGlobalPrefix(), getFunctionNumber(), + MI->getOperand(0).getIndex(), OutContext), + OutContext); + + auto MCInst = MCInstBuilder(Opc).addExpr(BranchLabel); + if (MI->getOperand(1).isReg()) { + // For BFr/BFLr + MCInst.addReg(MI->getOperand(1).getReg()); + } else { + // For BFi/BFLi/BFic + const MCExpr *BranchTarget; + if (MI->getOperand(1).isMBB()) + BranchTarget = MCSymbolRefExpr::create( + MI->getOperand(1).getMBB()->getSymbol(), OutContext); + else if (MI->getOperand(1).isGlobal()) { + const GlobalValue *GV = MI->getOperand(1).getGlobal(); + BranchTarget = MCSymbolRefExpr::create( + GetARMGVSymbol(GV, MI->getOperand(1).getTargetFlags()), OutContext); + } else if (MI->getOperand(1).isSymbol()) { + BranchTarget = MCSymbolRefExpr::create( + GetExternalSymbolSymbol(MI->getOperand(1).getSymbolName()), + OutContext); + } else + llvm_unreachable("Unhandled operand kind in Branch Future instruction"); + + MCInst.addExpr(BranchTarget); + } + + if (Opc == ARM::t2BFic) { + const MCExpr *ElseLabel = MCSymbolRefExpr::create( + getBFLabel(DL.getPrivateGlobalPrefix(), getFunctionNumber(), + MI->getOperand(2).getIndex(), OutContext), + OutContext); + MCInst.addExpr(ElseLabel); + MCInst.addImm(MI->getOperand(3).getImm()); + } else { + MCInst.addImm(MI->getOperand(2).getImm()) + .addReg(MI->getOperand(3).getReg()); + } + + EmitToStreamer(*OutStreamer, MCInst); + return; + } + case ARM::t2BF_LabelPseudo: { + // This is a pseudo op for a label used by a branch future instruction + + // Emit the label. + OutStreamer->EmitLabel(getBFLabel(DL.getPrivateGlobalPrefix(), + getFunctionNumber(), + MI->getOperand(0).getIndex(), OutContext)); + return; + } case ARM::tPICADD: { // This is a pseudo op for a label + instruction sequence, which looks like: // LPC0: diff --git a/lib/Target/ARM/ARMAsmPrinter.h b/lib/Target/ARM/ARMAsmPrinter.h index 0ba4bc05d6f7..a4b37fa2331f 100644 --- a/lib/Target/ARM/ARMAsmPrinter.h +++ b/lib/Target/ARM/ARMAsmPrinter.h @@ -1,9 +1,8 @@ //===-- ARMAsmPrinter.h - ARM implementation of AsmPrinter ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -76,12 +75,11 @@ public: void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O); + void PrintSymbolOperand(const MachineOperand &MO, raw_ostream &O) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, - unsigned AsmVariant, const char *ExtraCode, - raw_ostream &O) override; + const char *ExtraCode, raw_ostream &O) override; bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, - unsigned AsmVariant, const char *ExtraCode, - raw_ostream &O) override; + const char *ExtraCode, raw_ostream &O) override; void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, const MCSubtargetInfo *EndInfo) const override; diff --git a/lib/Target/ARM/ARMBaseInstrInfo.cpp b/lib/Target/ARM/ARMBaseInstrInfo.cpp index bbebed59c851..222aa85856a2 100644 --- a/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMBaseInstrInfo.cpp - ARM Instruction Information ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -134,7 +133,7 @@ ARMBaseInstrInfo::CreateTargetHazardRecognizer(const TargetSubtargetInfo *STI, ScheduleHazardRecognizer *ARMBaseInstrInfo:: CreateTargetPostRAHazardRecognizer(const InstrItineraryData *II, const ScheduleDAG *DAG) const { - if (Subtarget.isThumb2() || Subtarget.hasVFP2()) + if (Subtarget.isThumb2() || Subtarget.hasVFP2Base()) return (ScheduleHazardRecognizer *)new ARMHazardRecognizer(II, DAG); return TargetInstrInfo::CreateTargetPostRAHazardRecognizer(II, DAG); } @@ -707,15 +706,7 @@ unsigned ARMBaseInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { if (MCID.getSize()) return MCID.getSize(); - // If this machine instr is an inline asm, measure it. - if (MI.getOpcode() == ARM::INLINEASM) { - unsigned Size = getInlineAsmLength(MI.getOperand(0).getSymbolName(), *MAI); - if (!MF->getInfo<ARMFunctionInfo>()->isThumbFunction()) - Size = alignTo(Size, 4); - return Size; - } - unsigned Opc = MI.getOpcode(); - switch (Opc) { + switch (MI.getOpcode()) { default: // pseudo-instruction sizes are zero. return 0; @@ -752,6 +743,14 @@ unsigned ARMBaseInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { return 12; case ARM::SPACE: return MI.getOperand(1).getImm(); + case ARM::INLINEASM: + case ARM::INLINEASM_BR: { + // If this machine instr is an inline asm, measure it. + unsigned Size = getInlineAsmLength(MI.getOperand(0).getSymbolName(), *MAI); + if (!MF->getInfo<ARMFunctionInfo>()->isThumbFunction()) + Size = alignTo(Size, 4); + return Size; + } } } @@ -806,6 +805,28 @@ void ARMBaseInstrInfo::copyToCPSR(MachineBasicBlock &MBB, .addReg(ARM::CPSR, RegState::Implicit | RegState::Define); } +void llvm::addUnpredicatedMveVpredNOp(MachineInstrBuilder &MIB) { + MIB.addImm(ARMVCC::None); + MIB.addReg(0); +} + +void llvm::addUnpredicatedMveVpredROp(MachineInstrBuilder &MIB, + unsigned DestReg) { + addUnpredicatedMveVpredNOp(MIB); + MIB.addReg(DestReg, RegState::Undef); +} + +void llvm::addPredicatedMveVpredNOp(MachineInstrBuilder &MIB, unsigned Cond) { + MIB.addImm(Cond); + MIB.addReg(ARM::VPR, RegState::Implicit); +} + +void llvm::addPredicatedMveVpredROp(MachineInstrBuilder &MIB, + unsigned Cond, unsigned Inactive) { + addPredicatedMveVpredNOp(MIB, Cond); + MIB.addReg(Inactive); +} + void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, unsigned DestReg, @@ -831,17 +852,20 @@ void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, Opc = ARM::VMOVRS; else if (SPRDest && GPRSrc) Opc = ARM::VMOVSR; - else if (ARM::DPRRegClass.contains(DestReg, SrcReg) && !Subtarget.isFPOnlySP()) + else if (ARM::DPRRegClass.contains(DestReg, SrcReg) && Subtarget.hasFP64()) Opc = ARM::VMOVD; else if (ARM::QPRRegClass.contains(DestReg, SrcReg)) - Opc = ARM::VORRq; + Opc = Subtarget.hasNEON() ? ARM::VORRq : ARM::MVE_VORR; if (Opc) { MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(Opc), DestReg); MIB.addReg(SrcReg, getKillRegState(KillSrc)); - if (Opc == ARM::VORRq) + if (Opc == ARM::VORRq || Opc == ARM::MVE_VORR) MIB.addReg(SrcReg, getKillRegState(KillSrc)); - MIB.add(predOps(ARMCC::AL)); + if (Opc == ARM::MVE_VORR) + addUnpredicatedMveVpredROp(MIB, DestReg); + else + MIB.add(predOps(ARMCC::AL)); return; } @@ -852,11 +876,11 @@ void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, // Use VORRq when possible. if (ARM::QQPRRegClass.contains(DestReg, SrcReg)) { - Opc = ARM::VORRq; + Opc = Subtarget.hasNEON() ? ARM::VORRq : ARM::MVE_VORR; BeginIdx = ARM::qsub_0; SubRegs = 2; } else if (ARM::QQQQPRRegClass.contains(DestReg, SrcReg)) { - Opc = ARM::VORRq; + Opc = Subtarget.hasNEON() ? ARM::VORRq : ARM::MVE_VORR; BeginIdx = ARM::qsub_0; SubRegs = 4; // Fall back to VMOVD. @@ -891,7 +915,8 @@ void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, BeginIdx = ARM::dsub_0; SubRegs = 4; Spacing = 2; - } else if (ARM::DPRRegClass.contains(DestReg, SrcReg) && Subtarget.isFPOnlySP()) { + } else if (ARM::DPRRegClass.contains(DestReg, SrcReg) && + !Subtarget.hasFP64()) { Opc = ARM::VMOVS; BeginIdx = ARM::ssub_0; SubRegs = 2; @@ -901,6 +926,30 @@ void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, } else if (DestReg == ARM::CPSR) { copyToCPSR(MBB, I, SrcReg, KillSrc, Subtarget); return; + } else if (DestReg == ARM::VPR) { + assert(ARM::GPRRegClass.contains(SrcReg)); + BuildMI(MBB, I, I->getDebugLoc(), get(ARM::VMSR_P0), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .add(predOps(ARMCC::AL)); + return; + } else if (SrcReg == ARM::VPR) { + assert(ARM::GPRRegClass.contains(DestReg)); + BuildMI(MBB, I, I->getDebugLoc(), get(ARM::VMRS_P0), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .add(predOps(ARMCC::AL)); + return; + } else if (DestReg == ARM::FPSCR_NZCV) { + assert(ARM::GPRRegClass.contains(SrcReg)); + BuildMI(MBB, I, I->getDebugLoc(), get(ARM::VMSR_FPSCR_NZCVQC), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .add(predOps(ARMCC::AL)); + return; + } else if (SrcReg == ARM::FPSCR_NZCV) { + assert(ARM::GPRRegClass.contains(DestReg)); + BuildMI(MBB, I, I->getDebugLoc(), get(ARM::VMRS_FPSCR_NZCVQC), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .add(predOps(ARMCC::AL)); + return; } assert(Opc && "Impossible reg-to-reg copy"); @@ -925,10 +974,15 @@ void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, DstRegs.insert(Dst); #endif Mov = BuildMI(MBB, I, I->getDebugLoc(), get(Opc), Dst).addReg(Src); - // VORR takes two source operands. - if (Opc == ARM::VORRq) + // VORR (NEON or MVE) takes two source operands. + if (Opc == ARM::VORRq || Opc == ARM::MVE_VORR) { Mov.addReg(Src); - Mov = Mov.add(predOps(ARMCC::AL)); + } + // MVE VORR takes predicate operands in place of an ordinary condition. + if (Opc == ARM::MVE_VORR) + addUnpredicatedMveVpredROp(Mov, Dst); + else + Mov = Mov.add(predOps(ARMCC::AL)); // MOVr can set CC. if (Opc == ARM::MOVr) Mov = Mov.add(condCodeOp()); @@ -1010,6 +1064,13 @@ storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, .addImm(0) .addMemOperand(MMO) .add(predOps(ARMCC::AL)); + } else if (ARM::VCCRRegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DebugLoc(), get(ARM::VSTR_P0_off)) + .addReg(SrcReg, getKillRegState(isKill)) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO) + .add(predOps(ARMCC::AL)); } else llvm_unreachable("Unknown reg class!"); break; @@ -1042,7 +1103,7 @@ storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, llvm_unreachable("Unknown reg class!"); break; case 16: - if (ARM::DPairRegClass.hasSubClassEq(RC)) { + if (ARM::DPairRegClass.hasSubClassEq(RC) && Subtarget.hasNEON()) { // Use aligned spills if the stack can be realigned. if (Align >= 16 && getRegisterInfo().canRealignStack(MF)) { BuildMI(MBB, I, DebugLoc(), get(ARM::VST1q64)) @@ -1058,6 +1119,14 @@ storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, .addMemOperand(MMO) .add(predOps(ARMCC::AL)); } + } else if (ARM::QPRRegClass.hasSubClassEq(RC) && + Subtarget.hasMVEIntegerOps()) { + auto MIB = BuildMI(MBB, I, DebugLoc(), get(ARM::MVE_VSTRWU32)); + MIB.addReg(SrcReg, getKillRegState(isKill)) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); + addUnpredicatedMveVpredNOp(MIB); } else llvm_unreachable("Unknown reg class!"); break; @@ -1155,6 +1224,13 @@ unsigned ARMBaseInstrInfo::isStoreToStackSlot(const MachineInstr &MI, return MI.getOperand(0).getReg(); } break; + case ARM::VSTR_P0_off: + if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() && + MI.getOperand(1).getImm() == 0) { + FrameIndex = MI.getOperand(0).getIndex(); + return ARM::P0; + } + break; case ARM::VST1q64: case ARM::VST1d64TPseudo: case ARM::VST1d64QPseudo: @@ -1177,7 +1253,8 @@ unsigned ARMBaseInstrInfo::isStoreToStackSlot(const MachineInstr &MI, unsigned ARMBaseInstrInfo::isStoreToStackSlotPostFE(const MachineInstr &MI, int &FrameIndex) const { SmallVector<const MachineMemOperand *, 1> Accesses; - if (MI.mayStore() && hasStoreToStackSlot(MI, Accesses)) { + if (MI.mayStore() && hasStoreToStackSlot(MI, Accesses) && + Accesses.size() == 1) { FrameIndex = cast<FixedStackPseudoSourceValue>(Accesses.front()->getPseudoValue()) ->getFrameIndex(); @@ -1224,6 +1301,12 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, .addImm(0) .addMemOperand(MMO) .add(predOps(ARMCC::AL)); + } else if (ARM::VCCRRegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(ARM::VLDR_P0_off), DestReg) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO) + .add(predOps(ARMCC::AL)); } else llvm_unreachable("Unknown reg class!"); break; @@ -1260,7 +1343,7 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, llvm_unreachable("Unknown reg class!"); break; case 16: - if (ARM::DPairRegClass.hasSubClassEq(RC)) { + if (ARM::DPairRegClass.hasSubClassEq(RC) && Subtarget.hasNEON()) { if (Align >= 16 && getRegisterInfo().canRealignStack(MF)) { BuildMI(MBB, I, DL, get(ARM::VLD1q64), DestReg) .addFrameIndex(FI) @@ -1273,6 +1356,13 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, .addMemOperand(MMO) .add(predOps(ARMCC::AL)); } + } else if (ARM::QPRRegClass.hasSubClassEq(RC) && + Subtarget.hasMVEIntegerOps()) { + auto MIB = BuildMI(MBB, I, DL, get(ARM::MVE_VLDRWU32), DestReg); + MIB.addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); + addUnpredicatedMveVpredNOp(MIB); } else llvm_unreachable("Unknown reg class!"); break; @@ -1369,6 +1459,13 @@ unsigned ARMBaseInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, return MI.getOperand(0).getReg(); } break; + case ARM::VLDR_P0_off: + if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() && + MI.getOperand(1).getImm() == 0) { + FrameIndex = MI.getOperand(0).getIndex(); + return ARM::P0; + } + break; case ARM::VLD1q64: case ARM::VLD1d8TPseudo: case ARM::VLD1d16TPseudo: @@ -1397,7 +1494,8 @@ unsigned ARMBaseInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, unsigned ARMBaseInstrInfo::isLoadFromStackSlotPostFE(const MachineInstr &MI, int &FrameIndex) const { SmallVector<const MachineMemOperand *, 1> Accesses; - if (MI.mayLoad() && hasLoadFromStackSlot(MI, Accesses)) { + if (MI.mayLoad() && hasLoadFromStackSlot(MI, Accesses) && + Accesses.size() == 1) { FrameIndex = cast<FixedStackPseudoSourceValue>(Accesses.front()->getPseudoValue()) ->getFrameIndex(); @@ -1480,7 +1578,7 @@ bool ARMBaseInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { // copyPhysReg() calls. Look for VMOVS instructions that can legally be // widened to VMOVD. We prefer the VMOVD when possible because it may be // changed into a VORR that can go down the NEON pipeline. - if (!MI.isCopy() || Subtarget.dontWidenVMOVS() || Subtarget.isFPOnlySP()) + if (!MI.isCopy() || Subtarget.dontWidenVMOVS() || !Subtarget.hasFP64()) return false; // Look for a copy between even S-registers. That is where we keep floats @@ -1898,24 +1996,15 @@ isProfitableToIfCvt(MachineBasicBlock &MBB, // If we are optimizing for size, see if the branch in the predecessor can be // lowered to cbn?z by the constant island lowering pass, and return false if // so. This results in a shorter instruction sequence. - if (MBB.getParent()->getFunction().optForSize()) { + if (MBB.getParent()->getFunction().hasOptSize()) { MachineBasicBlock *Pred = *MBB.pred_begin(); if (!Pred->empty()) { MachineInstr *LastMI = &*Pred->rbegin(); if (LastMI->getOpcode() == ARM::t2Bcc) { - MachineBasicBlock::iterator CmpMI = LastMI; - if (CmpMI != Pred->begin()) { - --CmpMI; - if (CmpMI->getOpcode() == ARM::tCMPi8 || - CmpMI->getOpcode() == ARM::t2CMPri) { - unsigned Reg = CmpMI->getOperand(0).getReg(); - unsigned PredReg = 0; - ARMCC::CondCodes P = getInstrPredicate(*CmpMI, PredReg); - if (P == ARMCC::AL && CmpMI->getOperand(1).getImm() == 0 && - isARMLowRegister(Reg)) - return false; - } - } + const TargetRegisterInfo *TRI = &getRegisterInfo(); + MachineInstr *CmpMI = findCMPToFoldIntoCBZ(LastMI, TRI); + if (CmpMI) + return false; } } } @@ -1932,6 +2021,15 @@ isProfitableToIfCvt(MachineBasicBlock &TBB, if (!TCycles) return false; + // In thumb code we often end up trading one branch for a IT block, and + // if we are cloning the instruction can increase code size. Prevent + // blocks with multiple predecesors from being ifcvted to prevent this + // cloning. + if (Subtarget.isThumb2() && TBB.getParent()->getFunction().hasMinSize()) { + if (TBB.pred_size() != 1 || FBB.pred_size() != 1) + return false; + } + // Attempt to estimate the relative costs of predication versus branching. // Here we scale up each component of UnpredCost to avoid precision issue when // scaling TCycles/FCycles by Probability. @@ -2040,9 +2138,9 @@ MachineInstr *ARMBaseInstrInfo::commuteInstructionImpl(MachineInstr &MI, /// Identify instructions that can be folded into a MOVCC instruction, and /// return the defining instruction. -static MachineInstr *canFoldIntoMOVCC(unsigned Reg, - const MachineRegisterInfo &MRI, - const TargetInstrInfo *TII) { +MachineInstr * +ARMBaseInstrInfo::canFoldIntoMOVCC(unsigned Reg, const MachineRegisterInfo &MRI, + const TargetInstrInfo *TII) const { if (!TargetRegisterInfo::isVirtualRegister(Reg)) return nullptr; if (!MRI.hasOneNonDBGUse(Reg)) @@ -2050,8 +2148,8 @@ static MachineInstr *canFoldIntoMOVCC(unsigned Reg, MachineInstr *MI = MRI.getVRegDef(Reg); if (!MI) return nullptr; - // MI is folded into the MOVCC by predicating it. - if (!MI->isPredicable()) + // Check if MI can be predicated and folded into the MOVCC. + if (!isPredicable(*MI)) return nullptr; // Check if MI has any non-dead defs or physreg uses. This also detects // predicated instructions which will be reading CPSR. @@ -2266,7 +2364,7 @@ bool llvm::tryFoldSPUpdateIntoPushPop(const ARMSubtarget &Subtarget, unsigned NumBytes) { // This optimisation potentially adds lots of load and store // micro-operations, it's only really a great benefit to code-size. - if (!MF.getFunction().optForMinSize()) + if (!Subtarget.hasMinSize()) return false; // If only one register is pushed/popped, LLVM can use an LDR/STR @@ -2332,6 +2430,8 @@ bool llvm::tryFoldSPUpdateIntoPushPop(const ARMSubtarget &Subtarget, for (int CurRegEnc = FirstRegEnc - 1; CurRegEnc >= 0 && RegsNeeded; --CurRegEnc) { unsigned CurReg = RegClass->getRegister(CurRegEnc); + if (IsT1PushPop && CurReg > ARM::R7) + continue; if (!IsPop) { // Pushing any register is completely harmless, mark the register involved // as undef since we don't care about its value and must not restore it @@ -2389,7 +2489,7 @@ bool llvm::rewriteARMFrameIndex(MachineInstr &MI, unsigned FrameRegIdx, bool isSub = false; // Memory operands in inline assembly always use AddrMode2. - if (Opcode == ARM::INLINEASM) + if (Opcode == ARM::INLINEASM || Opcode == ARM::INLINEASM_BR) AddrMode = ARMII::AddrMode2; if (Opcode == ARM::ADDri) { @@ -2473,6 +2573,15 @@ bool llvm::rewriteARMFrameIndex(MachineInstr &MI, unsigned FrameRegIdx, NumBits = 8; Scale = 2; break; + case ARMII::AddrModeT2_i7: + case ARMII::AddrModeT2_i7s2: + case ARMII::AddrModeT2_i7s4: + ImmIdx = FrameRegIdx+1; + InstrOffs = MI.getOperand(ImmIdx).getImm(); + NumBits = 7; + Scale = (AddrMode == ARMII::AddrModeT2_i7s2 ? 2 : + AddrMode == ARMII::AddrModeT2_i7s4 ? 4 : 1); + break; default: llvm_unreachable("Unsupported addressing mode!"); } @@ -2543,6 +2652,7 @@ bool ARMBaseInstrInfo::analyzeCompare(const MachineInstr &MI, unsigned &SrcReg, return true; case ARM::CMPrr: case ARM::t2CMPrr: + case ARM::tCMPr: SrcReg = MI.getOperand(0).getReg(); SrcReg2 = MI.getOperand(1).getReg(); CmpMask = ~0; @@ -2619,32 +2729,62 @@ inline static ARMCC::CondCodes getCmpToAddCondition(ARMCC::CondCodes CC) { /// This function can be extended later on. inline static bool isRedundantFlagInstr(const MachineInstr *CmpI, unsigned SrcReg, unsigned SrcReg2, - int ImmValue, const MachineInstr *OI) { - if ((CmpI->getOpcode() == ARM::CMPrr || - CmpI->getOpcode() == ARM::t2CMPrr) && - (OI->getOpcode() == ARM::SUBrr || - OI->getOpcode() == ARM::t2SUBrr) && + int ImmValue, const MachineInstr *OI, + bool &IsThumb1) { + if ((CmpI->getOpcode() == ARM::CMPrr || CmpI->getOpcode() == ARM::t2CMPrr) && + (OI->getOpcode() == ARM::SUBrr || OI->getOpcode() == ARM::t2SUBrr) && ((OI->getOperand(1).getReg() == SrcReg && OI->getOperand(2).getReg() == SrcReg2) || (OI->getOperand(1).getReg() == SrcReg2 && - OI->getOperand(2).getReg() == SrcReg))) + OI->getOperand(2).getReg() == SrcReg))) { + IsThumb1 = false; return true; + } - if ((CmpI->getOpcode() == ARM::CMPri || - CmpI->getOpcode() == ARM::t2CMPri) && - (OI->getOpcode() == ARM::SUBri || - OI->getOpcode() == ARM::t2SUBri) && + if (CmpI->getOpcode() == ARM::tCMPr && OI->getOpcode() == ARM::tSUBrr && + ((OI->getOperand(2).getReg() == SrcReg && + OI->getOperand(3).getReg() == SrcReg2) || + (OI->getOperand(2).getReg() == SrcReg2 && + OI->getOperand(3).getReg() == SrcReg))) { + IsThumb1 = true; + return true; + } + + if ((CmpI->getOpcode() == ARM::CMPri || CmpI->getOpcode() == ARM::t2CMPri) && + (OI->getOpcode() == ARM::SUBri || OI->getOpcode() == ARM::t2SUBri) && OI->getOperand(1).getReg() == SrcReg && - OI->getOperand(2).getImm() == ImmValue) + OI->getOperand(2).getImm() == ImmValue) { + IsThumb1 = false; + return true; + } + + if (CmpI->getOpcode() == ARM::tCMPi8 && + (OI->getOpcode() == ARM::tSUBi8 || OI->getOpcode() == ARM::tSUBi3) && + OI->getOperand(2).getReg() == SrcReg && + OI->getOperand(3).getImm() == ImmValue) { + IsThumb1 = true; return true; + } if ((CmpI->getOpcode() == ARM::CMPrr || CmpI->getOpcode() == ARM::t2CMPrr) && (OI->getOpcode() == ARM::ADDrr || OI->getOpcode() == ARM::t2ADDrr || OI->getOpcode() == ARM::ADDri || OI->getOpcode() == ARM::t2ADDri) && OI->getOperand(0).isReg() && OI->getOperand(1).isReg() && OI->getOperand(0).getReg() == SrcReg && - OI->getOperand(1).getReg() == SrcReg2) + OI->getOperand(1).getReg() == SrcReg2) { + IsThumb1 = false; + return true; + } + + if (CmpI->getOpcode() == ARM::tCMPr && + (OI->getOpcode() == ARM::tADDi3 || OI->getOpcode() == ARM::tADDi8 || + OI->getOpcode() == ARM::tADDrr) && + OI->getOperand(0).getReg() == SrcReg && + OI->getOperand(2).getReg() == SrcReg2) { + IsThumb1 = true; return true; + } + return false; } @@ -2662,6 +2802,17 @@ static bool isOptimizeCompareCandidate(MachineInstr *MI, bool &IsThumb1) { case ARM::tSUBi3: case ARM::tSUBi8: case ARM::tMUL: + case ARM::tADC: + case ARM::tSBC: + case ARM::tRSB: + case ARM::tAND: + case ARM::tORR: + case ARM::tEOR: + case ARM::tBIC: + case ARM::tMVN: + case ARM::tASRri: + case ARM::tASRrr: + case ARM::tROR: IsThumb1 = true; LLVM_FALLTHROUGH; case ARM::RSBrr: @@ -2761,7 +2912,8 @@ bool ARMBaseInstrInfo::optimizeCompareInstr( // For CMPri w/ CmpValue != 0, a SubAdd may still be a candidate. // Thus we cannot return here. if (CmpInstr.getOpcode() == ARM::CMPri || - CmpInstr.getOpcode() == ARM::t2CMPri) + CmpInstr.getOpcode() == ARM::t2CMPri || + CmpInstr.getOpcode() == ARM::tCMPi8) MI = nullptr; else return false; @@ -2783,20 +2935,22 @@ bool ARMBaseInstrInfo::optimizeCompareInstr( // CMP. This peephole works on the vregs, so is still in SSA form. As a // consequence, the movs won't redefine/kill the MUL operands which would // make this reordering illegal. + const TargetRegisterInfo *TRI = &getRegisterInfo(); if (MI && IsThumb1) { --I; - bool CanReorder = true; - const bool HasStmts = I != E; - for (; I != E; --I) { - if (I->getOpcode() != ARM::tMOVi8) { - CanReorder = false; - break; + if (I != E && !MI->readsRegister(ARM::CPSR, TRI)) { + bool CanReorder = true; + for (; I != E; --I) { + if (I->getOpcode() != ARM::tMOVi8) { + CanReorder = false; + break; + } + } + if (CanReorder) { + MI = MI->removeFromParent(); + E = CmpInstr; + CmpInstr.getParent()->insert(E, MI); } - } - if (HasStmts && CanReorder) { - MI = MI->removeFromParent(); - E = CmpInstr; - CmpInstr.getParent()->insert(E, MI); } I = CmpInstr; E = MI; @@ -2804,12 +2958,13 @@ bool ARMBaseInstrInfo::optimizeCompareInstr( // Check that CPSR isn't set between the comparison instruction and the one we // want to change. At the same time, search for SubAdd. - const TargetRegisterInfo *TRI = &getRegisterInfo(); + bool SubAddIsThumb1 = false; do { const MachineInstr &Instr = *--I; // Check whether CmpInstr can be made redundant by the current instruction. - if (isRedundantFlagInstr(&CmpInstr, SrcReg, SrcReg2, CmpValue, &Instr)) { + if (isRedundantFlagInstr(&CmpInstr, SrcReg, SrcReg2, CmpValue, &Instr, + SubAddIsThumb1)) { SubAdd = &*I; break; } @@ -2824,14 +2979,25 @@ bool ARMBaseInstrInfo::optimizeCompareInstr( // change. We can't do this transformation. return false; - } while (I != B); + if (I == B) { + // In some cases, we scan the use-list of an instruction for an AND; + // that AND is in the same BB, but may not be scheduled before the + // corresponding TST. In that case, bail out. + // + // FIXME: We could try to reschedule the AND. + return false; + } + } while (true); // Return false if no candidates exist. if (!MI && !SubAdd) return false; - // The single candidate is called MI. - if (!MI) MI = SubAdd; + // If we found a SubAdd, use it as it will be closer to the CMP + if (SubAdd) { + MI = SubAdd; + IsThumb1 = SubAddIsThumb1; + } // We can't use a predicated instruction - it doesn't always write the flags. if (isPredicated(*MI)) @@ -2899,9 +3065,13 @@ bool ARMBaseInstrInfo::optimizeCompareInstr( // operands will be modified. unsigned Opc = SubAdd->getOpcode(); bool IsSub = Opc == ARM::SUBrr || Opc == ARM::t2SUBrr || - Opc == ARM::SUBri || Opc == ARM::t2SUBri; - if (!IsSub || (SrcReg2 != 0 && SubAdd->getOperand(1).getReg() == SrcReg2 && - SubAdd->getOperand(2).getReg() == SrcReg)) { + Opc == ARM::SUBri || Opc == ARM::t2SUBri || + Opc == ARM::tSUBrr || Opc == ARM::tSUBi3 || + Opc == ARM::tSUBi8; + unsigned OpI = Opc != ARM::tSUBrr ? 1 : 2; + if (!IsSub || + (SrcReg2 != 0 && SubAdd->getOperand(OpI).getReg() == SrcReg2 && + SubAdd->getOperand(OpI + 1).getReg() == SrcReg)) { // VSel doesn't support condition code update. if (IsInstrVSel) return false; @@ -2979,9 +3149,10 @@ bool ARMBaseInstrInfo::shouldSink(const MachineInstr &MI) const { ++Next; unsigned SrcReg, SrcReg2; int CmpMask, CmpValue; + bool IsThumb1; if (Next != MI.getParent()->end() && analyzeCompare(*Next, SrcReg, SrcReg2, CmpMask, CmpValue) && - isRedundantFlagInstr(&*Next, SrcReg, SrcReg2, CmpValue, &MI)) + isRedundantFlagInstr(&*Next, SrcReg, SrcReg2, CmpValue, &MI, IsThumb1)) return false; return true; } @@ -3372,7 +3543,12 @@ unsigned ARMBaseInstrInfo::getNumLDMAddresses(const MachineInstr &MI) const { I != E; ++I) { Size += (*I)->getSize(); } - return Size / 4; + // FIXME: The scheduler currently can't handle values larger than 16. But + // the values can actually go up to 32 for floating-point load/store + // multiple (VLDMIA etc.). Also, the way this code is reasoning about memory + // operations isn't right; we could end up with "extra" memory operands for + // various reasons, like tail merge merging two memory operations. + return std::min(Size / 4, 16U); } static unsigned getNumMicroOpsSingleIssuePlusExtras(unsigned Opc, @@ -4093,7 +4269,7 @@ int ARMBaseInstrInfo::getOperandLatencyImpl( // instructions). if (Latency > 0 && Subtarget.isThumb2()) { const MachineFunction *MF = DefMI.getParent()->getParent(); - // FIXME: Use Function::optForSize(). + // FIXME: Use Function::hasOptSize(). if (MF->getFunction().hasFnAttribute(Attribute::OptimizeForSize)) --Latency; } @@ -4517,6 +4693,31 @@ bool ARMBaseInstrInfo::verifyInstruction(const MachineInstr &MI, ErrInfo = "Pseudo flag setting opcodes only exist in Selection DAG"; return false; } + if (MI.getOpcode() == ARM::tMOVr && !Subtarget.hasV6Ops()) { + // Make sure we don't generate a lo-lo mov that isn't supported. + if (!ARM::hGPRRegClass.contains(MI.getOperand(0).getReg()) && + !ARM::hGPRRegClass.contains(MI.getOperand(1).getReg())) { + ErrInfo = "Non-flag-setting Thumb1 mov is v6-only"; + return false; + } + } + if (MI.getOpcode() == ARM::tPUSH || + MI.getOpcode() == ARM::tPOP || + MI.getOpcode() == ARM::tPOP_RET) { + for (int i = 2, e = MI.getNumOperands(); i < e; ++i) { + if (MI.getOperand(i).isImplicit() || + !MI.getOperand(i).isReg()) + continue; + unsigned Reg = MI.getOperand(i).getReg(); + if (Reg < ARM::R0 || Reg > ARM::R7) { + if (!(MI.getOpcode() == ARM::tPUSH && Reg == ARM::LR) && + !(MI.getOpcode() == ARM::tPOP_RET && Reg == ARM::PC)) { + ErrInfo = "Unsupported register in Thumb1 push/pop"; + return false; + } + } + } + } return true; } @@ -5107,3 +5308,44 @@ ARMBaseInstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const { {MO_NONLAZY, "arm-nonlazy"}}; return makeArrayRef(TargetFlags); } + +bool llvm::registerDefinedBetween(unsigned Reg, + MachineBasicBlock::iterator From, + MachineBasicBlock::iterator To, + const TargetRegisterInfo *TRI) { + for (auto I = From; I != To; ++I) + if (I->modifiesRegister(Reg, TRI)) + return true; + return false; +} + +MachineInstr *llvm::findCMPToFoldIntoCBZ(MachineInstr *Br, + const TargetRegisterInfo *TRI) { + // Search backwards to the instruction that defines CSPR. This may or not + // be a CMP, we check that after this loop. If we find another instruction + // that reads cpsr, we return nullptr. + MachineBasicBlock::iterator CmpMI = Br; + while (CmpMI != Br->getParent()->begin()) { + --CmpMI; + if (CmpMI->modifiesRegister(ARM::CPSR, TRI)) + break; + if (CmpMI->readsRegister(ARM::CPSR, TRI)) + break; + } + + // Check that this inst is a CMP r[0-7], #0 and that the register + // is not redefined between the cmp and the br. + if (CmpMI->getOpcode() != ARM::tCMPi8 && CmpMI->getOpcode() != ARM::t2CMPri) + return nullptr; + unsigned Reg = CmpMI->getOperand(0).getReg(); + unsigned PredReg = 0; + ARMCC::CondCodes Pred = getInstrPredicate(*CmpMI, PredReg); + if (Pred != ARMCC::AL || CmpMI->getOperand(1).getImm() != 0) + return nullptr; + if (!isARMLowRegister(Reg)) + return nullptr; + if (registerDefinedBetween(Reg, CmpMI->getNextNode(), Br, TRI)) + return nullptr; + + return &*CmpMI; +} diff --git a/lib/Target/ARM/ARMBaseInstrInfo.h b/lib/Target/ARM/ARMBaseInstrInfo.h index de1f307083ba..c28983fcc15c 100644 --- a/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/lib/Target/ARM/ARMBaseInstrInfo.h @@ -1,9 +1,8 @@ //===-- ARMBaseInstrInfo.h - ARM Base Instruction Information ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -399,6 +398,11 @@ private: void expandMEMCPY(MachineBasicBlock::iterator) const; + /// Identify instructions that can be folded into a MOVCC instruction, and + /// return the defining instruction. + MachineInstr *canFoldIntoMOVCC(unsigned Reg, const MachineRegisterInfo &MRI, + const TargetInstrInfo *TII) const; + private: /// Modeling special VFP / NEON fp MLA / MLS hazards. @@ -478,6 +482,21 @@ bool isUncondBranchOpcode(int Opc) { return Opc == ARM::B || Opc == ARM::tB || Opc == ARM::t2B; } +static inline bool isVPTOpcode(int Opc) { + return Opc == ARM::MVE_VPTv16i8 || Opc == ARM::MVE_VPTv16u8 || + Opc == ARM::MVE_VPTv16s8 || Opc == ARM::MVE_VPTv8i16 || + Opc == ARM::MVE_VPTv8u16 || Opc == ARM::MVE_VPTv8s16 || + Opc == ARM::MVE_VPTv4i32 || Opc == ARM::MVE_VPTv4u32 || + Opc == ARM::MVE_VPTv4s32 || Opc == ARM::MVE_VPTv4f32 || + Opc == ARM::MVE_VPTv8f16 || Opc == ARM::MVE_VPTv16i8r || + Opc == ARM::MVE_VPTv16u8r || Opc == ARM::MVE_VPTv16s8r || + Opc == ARM::MVE_VPTv8i16r || Opc == ARM::MVE_VPTv8u16r || + Opc == ARM::MVE_VPTv8s16r || Opc == ARM::MVE_VPTv4i32r || + Opc == ARM::MVE_VPTv4u32r || Opc == ARM::MVE_VPTv4s32r || + Opc == ARM::MVE_VPTv4f32r || Opc == ARM::MVE_VPTv8f16r || + Opc == ARM::MVE_VPST; +} + static inline bool isCondBranchOpcode(int Opc) { return Opc == ARM::Bcc || Opc == ARM::tBcc || Opc == ARM::t2Bcc; @@ -505,6 +524,28 @@ static inline bool isPushOpcode(int Opc) { Opc == ARM::STMDB_UPD || Opc == ARM::VSTMDDB_UPD; } +/// isValidCoprocessorNumber - decide whether an explicit coprocessor +/// number is legal in generic instructions like CDP. The answer can +/// vary with the subtarget. +static inline bool isValidCoprocessorNumber(unsigned Num, + const FeatureBitset& featureBits) { + // Armv8-A disallows everything *other* than 111x (CP14 and CP15). + if (featureBits[ARM::HasV8Ops] && (Num & 0xE) != 0xE) + return false; + + // Armv7 disallows 101x (CP10 and CP11), which clash with VFP/NEON. + if (featureBits[ARM::HasV7Ops] && (Num & 0xE) == 0xA) + return false; + + // Armv8.1-M also disallows 100x (CP8,CP9) and 111x (CP14,CP15) + // which clash with MVE. + if (featureBits[ARM::HasV8_1MMainlineOps] && + ((Num & 0xE) == 0x8 || (Num & 0xE) == 0xE)) + return false; + + return true; +} + /// getInstrPredicate - If instruction is predicated, returns its predicate /// condition, otherwise returns AL. It also returns the condition code /// register by reference. @@ -512,12 +553,6 @@ ARMCC::CondCodes getInstrPredicate(const MachineInstr &MI, unsigned &PredReg); unsigned getMatchingCondBranchOpcode(unsigned Opc); -/// Determine if MI can be folded into an ARM MOVCC instruction, and return the -/// opcode of the SSA instruction representing the conditional MI. -unsigned canFoldARMInstrIntoMOVCC(unsigned Reg, - MachineInstr *&MI, - const MachineRegisterInfo &MRI); - /// Map pseudo instructions that imply an 'S' bit onto real opcodes. Whether /// the instruction is encoded with an 'S' bit is determined by the optional /// CPSR def operand. @@ -568,6 +603,23 @@ bool rewriteT2FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, unsigned FrameReg, int &Offset, const ARMBaseInstrInfo &TII); +/// Return true if Reg is defd between From and To +bool registerDefinedBetween(unsigned Reg, MachineBasicBlock::iterator From, + MachineBasicBlock::iterator To, + const TargetRegisterInfo *TRI); + +/// Search backwards from a tBcc to find a tCMPi8 against 0, meaning +/// we can convert them to a tCBZ or tCBNZ. Return nullptr if not found. +MachineInstr *findCMPToFoldIntoCBZ(MachineInstr *Br, + const TargetRegisterInfo *TRI); + +void addUnpredicatedMveVpredNOp(MachineInstrBuilder &MIB); +void addUnpredicatedMveVpredROp(MachineInstrBuilder &MIB, unsigned DestReg); + +void addPredicatedMveVpredNOp(MachineInstrBuilder &MIB, unsigned Cond); +void addPredicatedMveVpredROp(MachineInstrBuilder &MIB, unsigned Cond, + unsigned Inactive); + } // end namespace llvm #endif // LLVM_LIB_TARGET_ARM_ARMBASEINSTRINFO_H diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/lib/Target/ARM/ARMBaseRegisterInfo.cpp index 02b3daf3c6fd..dc99b37742da 100644 --- a/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMBaseRegisterInfo.cpp - ARM Register Information ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -150,7 +149,7 @@ ARMBaseRegisterInfo::getTLSCallPreservedMask(const MachineFunction &MF) const { const uint32_t * ARMBaseRegisterInfo::getSjLjDispatchPreservedMask(const MachineFunction &MF) const { const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>(); - if (!STI.useSoftFloat() && STI.hasVFP2() && !STI.isThumb1Only()) + if (!STI.useSoftFloat() && STI.hasVFP2Base() && !STI.isThumb1Only()) return CSR_NoRegs_RegMask; else return CSR_FPRegs_RegMask; @@ -194,7 +193,7 @@ getReservedRegs(const MachineFunction &MF) const { if (STI.isR9Reserved()) markSuperRegs(Reserved, ARM::R9); // Reserve D16-D31 if the subtarget doesn't support them. - if (!STI.hasVFP3() || STI.hasD16()) { + if (!STI.hasD32()) { static_assert(ARM::D31 == ARM::D16 + 15, "Register list not consecutive!"); for (unsigned R = 0; R < 16; ++R) markSuperRegs(Reserved, ARM::D16 + R); @@ -204,6 +203,8 @@ getReservedRegs(const MachineFunction &MF) const { for (MCSubRegIterator SI(Reg, this); SI.isValid(); ++SI) if (Reserved.test(*SI)) markSuperRegs(Reserved, Reg); + // For v8.1m architecture + markSuperRegs(Reserved, ARM::ZR); assert(checkAllSuperRegsMarked(Reserved)); return Reserved; @@ -369,29 +370,35 @@ bool ARMBaseRegisterInfo::hasBasePointer(const MachineFunction &MF) const { const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); const ARMFrameLowering *TFI = getFrameLowering(MF); - // When outgoing call frames are so large that we adjust the stack pointer - // around the call, we can no longer use the stack pointer to reach the - // emergency spill slot. + // If we have stack realignment and VLAs, we have no pointer to use to + // access the stack. If we have stack realignment, and a large call frame, + // we have no place to allocate the emergency spill slot. if (needsStackRealignment(MF) && !TFI->hasReservedCallFrame(MF)) return true; // Thumb has trouble with negative offsets from the FP. Thumb2 has a limited // negative range for ldr/str (255), and thumb1 is positive offsets only. + // // It's going to be better to use the SP or Base Pointer instead. When there // are variable sized objects, we can't reference off of the SP, so we // reserve a Base Pointer. - if (AFI->isThumbFunction() && MFI.hasVarSizedObjects()) { - // Conservatively estimate whether the negative offset from the frame - // pointer will be sufficient to reach. If a function has a smallish - // frame, it's less likely to have lots of spills and callee saved - // space, so it's all more likely to be within range of the frame pointer. - // If it's wrong, the scavenger will still enable access to work, it just - // won't be optimal. - if (AFI->isThumb2Function() && MFI.getLocalFrameSize() < 128) - return false; + // + // For Thumb2, estimate whether a negative offset from the frame pointer + // will be sufficient to reach the whole stack frame. If a function has a + // smallish frame, it's less likely to have lots of spills and callee saved + // space, so it's all more likely to be within range of the frame pointer. + // If it's wrong, the scavenger will still enable access to work, it just + // won't be optimal. (We should always be able to reach the emergency + // spill slot from the frame pointer.) + if (AFI->isThumb2Function() && MFI.hasVarSizedObjects() && + MFI.getLocalFrameSize() >= 128) + return true; + // For Thumb1, if sp moves, nothing is in range, so force a base pointer. + // This is necessary for correctness in cases where we need an emergency + // spill slot. (In Thumb1, we can't use a negative offset from the frame + // pointer.) + if (AFI->isThumb1OnlyFunction() && !TFI->hasReservedCallFrame(MF)) return true; - } - return false; } @@ -425,7 +432,7 @@ cannotEliminateFrame(const MachineFunction &MF) const { || needsStackRealignment(MF); } -unsigned +Register ARMBaseRegisterInfo::getFrameRegister(const MachineFunction &MF) const { const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>(); const ARMFrameLowering *TFI = getFrameLowering(MF); @@ -785,7 +792,7 @@ ARMBaseRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int PIdx = MI.findFirstPredOperandIdx(); ARMCC::CondCodes Pred = (PIdx == -1) ? ARMCC::AL : (ARMCC::CondCodes)MI.getOperand(PIdx).getImm(); - unsigned PredReg = (PIdx == -1) ? 0 : MI.getOperand(PIdx+1).getReg(); + Register PredReg = (PIdx == -1) ? Register() : MI.getOperand(PIdx+1).getReg(); if (Offset == 0) // Must be addrmode4/6. MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false, false, false); diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.h b/lib/Target/ARM/ARMBaseRegisterInfo.h index 45d29ebc0bd3..7e2c72b4d712 100644 --- a/lib/Target/ARM/ARMBaseRegisterInfo.h +++ b/lib/Target/ARM/ARMBaseRegisterInfo.h @@ -1,9 +1,8 @@ //===-- ARMBaseRegisterInfo.h - ARM Register Information Impl ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -174,7 +173,7 @@ public: bool cannotEliminateFrame(const MachineFunction &MF) const; // Debug information queries. - unsigned getFrameRegister(const MachineFunction &MF) const override; + Register getFrameRegister(const MachineFunction &MF) const override; unsigned getBaseRegister() const { return BasePtr; } bool isLowRegister(unsigned Reg) const; diff --git a/lib/Target/ARM/ARMBasicBlockInfo.cpp b/lib/Target/ARM/ARMBasicBlockInfo.cpp new file mode 100644 index 000000000000..2de90e816b33 --- /dev/null +++ b/lib/Target/ARM/ARMBasicBlockInfo.cpp @@ -0,0 +1,146 @@ +//===--- ARMBasicBlockInfo.cpp - Utilities for block sizes ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMBaseInstrInfo.h" +#include "ARMBasicBlockInfo.h" +#include "ARMMachineFunctionInfo.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include <vector> + +#define DEBUG_TYPE "arm-bb-utils" + +using namespace llvm; + +namespace llvm { + +// mayOptimizeThumb2Instruction - Returns true if optimizeThumb2Instructions +// below may shrink MI. +static bool +mayOptimizeThumb2Instruction(const MachineInstr *MI) { + switch(MI->getOpcode()) { + // optimizeThumb2Instructions. + case ARM::t2LEApcrel: + case ARM::t2LDRpci: + // optimizeThumb2Branches. + case ARM::t2B: + case ARM::t2Bcc: + case ARM::tBcc: + // optimizeThumb2JumpTables. + case ARM::t2BR_JT: + case ARM::tBR_JTr: + return true; + } + return false; +} + +void ARMBasicBlockUtils::computeBlockSize(MachineBasicBlock *MBB) { + LLVM_DEBUG(dbgs() << "computeBlockSize: " << MBB->getName() << "\n"); + BasicBlockInfo &BBI = BBInfo[MBB->getNumber()]; + BBI.Size = 0; + BBI.Unalign = 0; + BBI.PostAlign = 0; + + for (MachineInstr &I : *MBB) { + BBI.Size += TII->getInstSizeInBytes(I); + // For inline asm, getInstSizeInBytes returns a conservative estimate. + // The actual size may be smaller, but still a multiple of the instr size. + if (I.isInlineAsm()) + BBI.Unalign = isThumb ? 1 : 2; + // Also consider instructions that may be shrunk later. + else if (isThumb && mayOptimizeThumb2Instruction(&I)) + BBI.Unalign = 1; + } + + // tBR_JTr contains a .align 2 directive. + if (!MBB->empty() && MBB->back().getOpcode() == ARM::tBR_JTr) { + BBI.PostAlign = 2; + MBB->getParent()->ensureAlignment(2); + } +} + +/// getOffsetOf - Return the current offset of the specified machine instruction +/// from the start of the function. This offset changes as stuff is moved +/// around inside the function. +unsigned ARMBasicBlockUtils::getOffsetOf(MachineInstr *MI) const { + const MachineBasicBlock *MBB = MI->getParent(); + + // The offset is composed of two things: the sum of the sizes of all MBB's + // before this instruction's block, and the offset from the start of the block + // it is in. + unsigned Offset = BBInfo[MBB->getNumber()].Offset; + + // Sum instructions before MI in MBB. + for (MachineBasicBlock::const_iterator I = MBB->begin(); &*I != MI; ++I) { + assert(I != MBB->end() && "Didn't find MI in its own basic block?"); + Offset += TII->getInstSizeInBytes(*I); + } + return Offset; +} + +/// isBBInRange - Returns true if the distance between specific MI and +/// specific BB can fit in MI's displacement field. +bool ARMBasicBlockUtils::isBBInRange(MachineInstr *MI, + MachineBasicBlock *DestBB, + unsigned MaxDisp) const { + unsigned PCAdj = isThumb ? 4 : 8; + unsigned BrOffset = getOffsetOf(MI) + PCAdj; + unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset; + + LLVM_DEBUG(dbgs() << "Branch of destination " << printMBBReference(*DestBB) + << " from " << printMBBReference(*MI->getParent()) + << " max delta=" << MaxDisp << " from " << getOffsetOf(MI) + << " to " << DestOffset << " offset " + << int(DestOffset - BrOffset) << "\t" << *MI); + + if (BrOffset <= DestOffset) { + // Branch before the Dest. + if (DestOffset-BrOffset <= MaxDisp) + return true; + } else { + if (BrOffset-DestOffset <= MaxDisp) + return true; + } + return false; +} + +void ARMBasicBlockUtils::adjustBBOffsetsAfter(MachineBasicBlock *BB) { + assert(BB->getParent() == &MF && + "Basic block is not a child of the current function.\n"); + + unsigned BBNum = BB->getNumber(); + LLVM_DEBUG(dbgs() << "Adjust block:\n" + << " - name: " << BB->getName() << "\n" + << " - number: " << BB->getNumber() << "\n" + << " - function: " << MF.getName() << "\n" + << " - blocks: " << MF.getNumBlockIDs() << "\n"); + + for(unsigned i = BBNum + 1, e = MF.getNumBlockIDs(); i < e; ++i) { + // Get the offset and known bits at the end of the layout predecessor. + // Include the alignment of the current block. + unsigned LogAlign = MF.getBlockNumbered(i)->getAlignment(); + unsigned Offset = BBInfo[i - 1].postOffset(LogAlign); + unsigned KnownBits = BBInfo[i - 1].postKnownBits(LogAlign); + + // This is where block i begins. Stop if the offset is already correct, + // and we have updated 2 blocks. This is the maximum number of blocks + // changed before calling this function. + if (i > BBNum + 2 && + BBInfo[i].Offset == Offset && + BBInfo[i].KnownBits == KnownBits) + break; + + BBInfo[i].Offset = Offset; + BBInfo[i].KnownBits = KnownBits; + } +} + +} // end namespace llvm diff --git a/lib/Target/ARM/ARMBasicBlockInfo.h b/lib/Target/ARM/ARMBasicBlockInfo.h index e0cb0aa676a6..400bba351cec 100644 --- a/lib/Target/ARM/ARMBasicBlockInfo.h +++ b/lib/Target/ARM/ARMBasicBlockInfo.h @@ -1,9 +1,8 @@ //===-- ARMBasicBlockInfo.h - Basic Block Information -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,12 +13,16 @@ #ifndef LLVM_LIB_TARGET_ARM_ARMBASICBLOCKINFO_H #define LLVM_LIB_TARGET_ARM_ARMBASICBLOCKINFO_H +#include "ARMBaseInstrInfo.h" +#include "ARMMachineFunctionInfo.h" #include "llvm/Support/MathExtras.h" #include <algorithm> #include <cstdint> namespace llvm { +using BBInfoVector = SmallVectorImpl<BasicBlockInfo>; + /// UnknownPadding - Return the worst case padding that could result from /// unknown offset bits. This does not include alignment padding caused by /// known offset bits. @@ -104,6 +107,54 @@ struct BasicBlockInfo { } }; +class ARMBasicBlockUtils { + +private: + MachineFunction &MF; + bool isThumb = false; + const ARMBaseInstrInfo *TII = nullptr; + SmallVector<BasicBlockInfo, 8> BBInfo; + +public: + ARMBasicBlockUtils(MachineFunction &MF) : MF(MF) { + TII = + static_cast<const ARMBaseInstrInfo*>(MF.getSubtarget().getInstrInfo()); + isThumb = MF.getInfo<ARMFunctionInfo>()->isThumbFunction(); + } + + void computeAllBlockSizes() { + BBInfo.resize(MF.getNumBlockIDs()); + for (MachineBasicBlock &MBB : MF) + computeBlockSize(&MBB); + } + + void computeBlockSize(MachineBasicBlock *MBB); + + unsigned getOffsetOf(MachineInstr *MI) const; + + unsigned getOffsetOf(MachineBasicBlock *MBB) const { + return BBInfo[MBB->getNumber()].Offset; + } + + void adjustBBOffsetsAfter(MachineBasicBlock *MBB); + + void adjustBBSize(MachineBasicBlock *MBB, int Size) { + BBInfo[MBB->getNumber()].Size += Size; + } + + bool isBBInRange(MachineInstr *MI, MachineBasicBlock *DestBB, + unsigned MaxDisp) const; + + void insert(unsigned BBNum, BasicBlockInfo BBI) { + BBInfo.insert(BBInfo.begin() + BBNum, BBI); + } + + void clear() { BBInfo.clear(); } + + BBInfoVector &getBBInfo() { return BBInfo; } + +}; + } // end namespace llvm #endif // LLVM_LIB_TARGET_ARM_ARMBASICBLOCKINFO_H diff --git a/lib/Target/ARM/ARMCallLowering.cpp b/lib/Target/ARM/ARMCallLowering.cpp index 8e80c32bcf89..0cbe6e1871e4 100644 --- a/lib/Target/ARM/ARMCallLowering.cpp +++ b/lib/Target/ARM/ARMCallLowering.cpp @@ -1,9 +1,8 @@ //===- llvm/lib/Target/ARM/ARMCallLowering.cpp - Call lowering ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -56,7 +55,7 @@ ARMCallLowering::ARMCallLowering(const ARMTargetLowering &TLI) static bool isSupportedType(const DataLayout &DL, const ARMTargetLowering &TLI, Type *T) { if (T->isArrayTy()) - return true; + return isSupportedType(DL, TLI, T->getArrayElementType()); if (T->isStructTy()) { // For now we only allow homogeneous structs that we can manipulate with @@ -65,7 +64,7 @@ static bool isSupportedType(const DataLayout &DL, const ARMTargetLowering &TLI, for (unsigned i = 1, e = StructT->getNumElements(); i != e; ++i) if (StructT->getElementType(i) != StructT->getElementType(0)) return false; - return true; + return isSupportedType(DL, TLI, StructT->getElementType(0)); } EVT VT = TLI.getValueType(DL, T, true); @@ -91,27 +90,27 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { MachineInstrBuilder &MIB, CCAssignFn *AssignFn) : ValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB) {} - unsigned getStackAddress(uint64_t Size, int64_t Offset, + Register getStackAddress(uint64_t Size, int64_t Offset, MachinePointerInfo &MPO) override { assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && "Unsupported size"); LLT p0 = LLT::pointer(0, 32); LLT s32 = LLT::scalar(32); - unsigned SPReg = MRI.createGenericVirtualRegister(p0); - MIRBuilder.buildCopy(SPReg, ARM::SP); + Register SPReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildCopy(SPReg, Register(ARM::SP)); - unsigned OffsetReg = MRI.createGenericVirtualRegister(s32); + Register OffsetReg = MRI.createGenericVirtualRegister(s32); MIRBuilder.buildConstant(OffsetReg, Offset); - unsigned AddrReg = MRI.createGenericVirtualRegister(p0); + Register AddrReg = MRI.createGenericVirtualRegister(p0); MIRBuilder.buildGEP(AddrReg, SPReg, OffsetReg); MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Offset); return AddrReg; } - void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + void assignValueToReg(Register ValVReg, Register PhysReg, CCValAssign &VA) override { assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); @@ -119,25 +118,27 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { assert(VA.getValVT().getSizeInBits() <= 64 && "Unsupported value size"); assert(VA.getLocVT().getSizeInBits() <= 64 && "Unsupported location size"); - unsigned ExtReg = extendRegister(ValVReg, VA); + Register ExtReg = extendRegister(ValVReg, VA); MIRBuilder.buildCopy(PhysReg, ExtReg); MIB.addUse(PhysReg, RegState::Implicit); } - void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, MachinePointerInfo &MPO, CCValAssign &VA) override { assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && "Unsupported size"); - unsigned ExtReg = extendRegister(ValVReg, VA); + Register ExtReg = extendRegister(ValVReg, VA); auto MMO = MIRBuilder.getMF().getMachineMemOperand( MPO, MachineMemOperand::MOStore, VA.getLocVT().getStoreSize(), - /* Alignment */ 0); + /* Alignment */ 1); MIRBuilder.buildStore(ExtReg, Addr, *MMO); } unsigned assignCustomValue(const CallLowering::ArgInfo &Arg, ArrayRef<CCValAssign> VAs) override { + assert(Arg.Regs.size() == 1 && "Can't handle multple regs yet"); + CCValAssign VA = VAs[0]; assert(VA.needsCustom() && "Value doesn't need custom handling"); assert(VA.getValVT() == MVT::f64 && "Unsupported type"); @@ -152,9 +153,9 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { assert(VA.isRegLoc() && "Value should be in reg"); assert(NextVA.isRegLoc() && "Value should be in reg"); - unsigned NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), + Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), MRI.createGenericVirtualRegister(LLT::scalar(32))}; - MIRBuilder.buildUnmerge(NewRegs, Arg.Reg); + MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]); bool IsLittle = MIRBuilder.getMF().getSubtarget<ARMSubtarget>().isLittle(); if (!IsLittle) @@ -183,18 +184,17 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { } // end anonymous namespace -void ARMCallLowering::splitToValueTypes( - const ArgInfo &OrigArg, SmallVectorImpl<ArgInfo> &SplitArgs, - MachineFunction &MF, const SplitArgTy &PerformArgSplit) const { +void ARMCallLowering::splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl<ArgInfo> &SplitArgs, + MachineFunction &MF) const { const ARMTargetLowering &TLI = *getTLI<ARMTargetLowering>(); LLVMContext &Ctx = OrigArg.Ty->getContext(); const DataLayout &DL = MF.getDataLayout(); - MachineRegisterInfo &MRI = MF.getRegInfo(); const Function &F = MF.getFunction(); SmallVector<EVT, 4> SplitVTs; - SmallVector<uint64_t, 4> Offsets; - ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0); + ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, nullptr, nullptr, 0); + assert(OrigArg.Regs.size() == SplitVTs.size() && "Regs / types mismatch"); if (SplitVTs.size() == 1) { // Even if there is no splitting to do, we still want to replace the @@ -202,12 +202,12 @@ void ARMCallLowering::splitToValueTypes( auto Flags = OrigArg.Flags; unsigned OriginalAlignment = DL.getABITypeAlignment(OrigArg.Ty); Flags.setOrigAlign(OriginalAlignment); - SplitArgs.emplace_back(OrigArg.Reg, SplitVTs[0].getTypeForEVT(Ctx), Flags, - OrigArg.IsFixed); + SplitArgs.emplace_back(OrigArg.Regs[0], SplitVTs[0].getTypeForEVT(Ctx), + Flags, OrigArg.IsFixed); return; } - unsigned FirstRegIdx = SplitArgs.size(); + // Create one ArgInfo for each virtual register. for (unsigned i = 0, e = SplitVTs.size(); i != e; ++i) { EVT SplitVT = SplitVTs[i]; Type *SplitTy = SplitVT.getTypeForEVT(Ctx); @@ -225,19 +225,16 @@ void ARMCallLowering::splitToValueTypes( Flags.setInConsecutiveRegsLast(); } - SplitArgs.push_back( - ArgInfo{MRI.createGenericVirtualRegister(getLLTForType(*SplitTy, DL)), - SplitTy, Flags, OrigArg.IsFixed}); + // FIXME: We also want to split SplitTy further. + Register PartReg = OrigArg.Regs[i]; + SplitArgs.emplace_back(PartReg, SplitTy, Flags, OrigArg.IsFixed); } - - for (unsigned i = 0; i < Offsets.size(); ++i) - PerformArgSplit(SplitArgs[FirstRegIdx + i].Reg, Offsets[i] * 8); } /// Lower the return value for the already existing \p Ret. This assumes that /// \p MIRBuilder's insertion point is correct. bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder, - const Value *Val, ArrayRef<unsigned> VRegs, + const Value *Val, ArrayRef<Register> VRegs, MachineInstrBuilder &Ret) const { if (!Val) // Nothing to do here. @@ -251,35 +248,22 @@ bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder, if (!isSupportedType(DL, TLI, Val->getType())) return false; - 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, 4> SplitVTs; - LLVMContext &Ctx = Val->getType()->getContext(); - for (unsigned i = 0; i < SplitEVTs.size(); ++i) { - ArgInfo CurArgInfo(VRegs[i], SplitEVTs[i].getTypeForEVT(Ctx)); - setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F); + ArgInfo OrigRetInfo(VRegs, Val->getType()); + setArgFlags(OrigRetInfo, AttributeList::ReturnIndex, DL, F); - SmallVector<unsigned, 4> Regs; - splitToValueTypes( - CurArgInfo, SplitVTs, MF, - [&](unsigned Reg, uint64_t Offset) { Regs.push_back(Reg); }); - if (Regs.size() > 1) - MIRBuilder.buildUnmerge(Regs, VRegs[i]); - } + SmallVector<ArgInfo, 4> SplitRetInfos; + splitToValueTypes(OrigRetInfo, SplitRetInfos, MF); CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg()); OutgoingValueHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret, AssignFn); - return handleAssignments(MIRBuilder, SplitVTs, RetHandler); + return handleAssignments(MIRBuilder, SplitRetInfos, RetHandler); } bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, - ArrayRef<unsigned> VRegs) const { + ArrayRef<Register> VRegs) const { assert(!Val == VRegs.empty() && "Return value without a vreg"); auto const &ST = MIRBuilder.getMF().getSubtarget<ARMSubtarget>(); @@ -302,7 +286,9 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { CCAssignFn AssignFn) : ValueHandler(MIRBuilder, MRI, AssignFn) {} - unsigned getStackAddress(uint64_t Size, int64_t Offset, + bool isArgumentHandler() const override { return true; } + + Register getStackAddress(uint64_t Size, int64_t Offset, MachinePointerInfo &MPO) override { assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && "Unsupported size"); @@ -319,7 +305,7 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { return AddrReg; } - void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, MachinePointerInfo &MPO, CCValAssign &VA) override { assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && "Unsupported size"); @@ -332,22 +318,22 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { assert(MRI.getType(ValVReg).isScalar() && "Only scalars supported atm"); auto LoadVReg = MRI.createGenericVirtualRegister(LLT::scalar(32)); - buildLoad(LoadVReg, Addr, Size, /* Alignment */ 0, MPO); + buildLoad(LoadVReg, Addr, Size, /* Alignment */ 1, MPO); MIRBuilder.buildTrunc(ValVReg, LoadVReg); } else { // If the value is not extended, a simple load will suffice. - buildLoad(ValVReg, Addr, Size, /* Alignment */ 0, MPO); + buildLoad(ValVReg, Addr, Size, /* Alignment */ 1, MPO); } } - void buildLoad(unsigned Val, unsigned Addr, uint64_t Size, unsigned Alignment, + void buildLoad(Register Val, Register Addr, uint64_t Size, unsigned Alignment, MachinePointerInfo &MPO) { auto MMO = MIRBuilder.getMF().getMachineMemOperand( MPO, MachineMemOperand::MOLoad, Size, Alignment); MIRBuilder.buildLoad(Val, Addr, *MMO); } - void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + void assignValueToReg(Register ValVReg, Register PhysReg, CCValAssign &VA) override { assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); @@ -376,6 +362,8 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { unsigned assignCustomValue(const ARMCallLowering::ArgInfo &Arg, ArrayRef<CCValAssign> VAs) override { + assert(Arg.Regs.size() == 1 && "Can't handle multple regs yet"); + CCValAssign VA = VAs[0]; assert(VA.needsCustom() && "Value doesn't need custom handling"); assert(VA.getValVT() == MVT::f64 && "Unsupported type"); @@ -390,7 +378,7 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { assert(VA.isRegLoc() && "Value should be in reg"); assert(NextVA.isRegLoc() && "Value should be in reg"); - unsigned NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), + Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), MRI.createGenericVirtualRegister(LLT::scalar(32))}; assignValueToReg(NewRegs[0], VA.getLocReg(), VA); @@ -400,7 +388,7 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { if (!IsLittle) std::swap(NewRegs[0], NewRegs[1]); - MIRBuilder.buildMerge(Arg.Reg, NewRegs); + MIRBuilder.buildMerge(Arg.Regs[0], NewRegs); return 1; } @@ -423,9 +411,9 @@ struct FormalArgHandler : public IncomingValueHandler { } // end anonymous namespace -bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, - const Function &F, - ArrayRef<unsigned> VRegs) const { +bool ARMCallLowering::lowerFormalArguments( + MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef<ArrayRef<Register>> VRegs) const { auto &TLI = *getTLI<ARMTargetLowering>(); auto Subtarget = TLI.getSubtarget(); @@ -456,21 +444,13 @@ bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, FormalArgHandler ArgHandler(MIRBuilder, MIRBuilder.getMF().getRegInfo(), AssignFn); - SmallVector<ArgInfo, 8> ArgInfos; - SmallVector<unsigned, 4> SplitRegs; + SmallVector<ArgInfo, 8> SplitArgInfos; unsigned Idx = 0; for (auto &Arg : F.args()) { - ArgInfo AInfo(VRegs[Idx], Arg.getType()); - setArgFlags(AInfo, Idx + AttributeList::FirstArgIndex, DL, F); - - SplitRegs.clear(); - - splitToValueTypes(AInfo, ArgInfos, MF, [&](unsigned Reg, uint64_t Offset) { - SplitRegs.push_back(Reg); - }); + ArgInfo OrigArgInfo(VRegs[Idx], Arg.getType()); - if (!SplitRegs.empty()) - MIRBuilder.buildMerge(VRegs[Idx], SplitRegs); + setArgFlags(OrigArgInfo, Idx + AttributeList::FirstArgIndex, DL, F); + splitToValueTypes(OrigArgInfo, SplitArgInfos, MF); Idx++; } @@ -478,7 +458,7 @@ bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, if (!MBB.empty()) MIRBuilder.setInstr(*MBB.begin()); - if (!handleAssignments(MIRBuilder, ArgInfos, ArgHandler)) + if (!handleAssignments(MIRBuilder, SplitArgInfos, ArgHandler)) return false; // Move back to the end of the basic block. @@ -540,19 +520,19 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, // Create the call instruction so we can add the implicit uses of arg // registers, but don't insert it yet. - bool isDirect = !Callee.isReg(); - auto CallOpcode = getCallOpcode(STI, isDirect); + bool IsDirect = !Callee.isReg(); + auto CallOpcode = getCallOpcode(STI, IsDirect); auto MIB = MIRBuilder.buildInstrNoInsert(CallOpcode); - bool isThumb = STI.isThumb(); - if (isThumb) + bool IsThumb = STI.isThumb(); + if (IsThumb) MIB.add(predOps(ARMCC::AL)); MIB.add(Callee); - if (!isDirect) { + if (!IsDirect) { auto CalleeReg = Callee.getReg(); if (CalleeReg && !TRI->isPhysicalRegister(CalleeReg)) { - unsigned CalleeIdx = isThumb ? 2 : 0; + unsigned CalleeIdx = IsThumb ? 2 : 0; MIB->getOperand(CalleeIdx).setReg(constrainOperandRegClass( MF, *TRI, MRI, *STI.getInstrInfo(), *STI.getRegBankInfo(), *MIB.getInstr(), MIB->getDesc(), Callee, CalleeIdx)); @@ -561,27 +541,22 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, MIB.addRegMask(TRI->getCallPreservedMask(MF, CallConv)); + bool IsVarArg = false; SmallVector<ArgInfo, 8> ArgInfos; for (auto Arg : OrigArgs) { if (!isSupportedType(DL, TLI, Arg.Ty)) return false; if (!Arg.IsFixed) - return false; + IsVarArg = true; if (Arg.Flags.isByVal()) return false; - SmallVector<unsigned, 8> Regs; - splitToValueTypes(Arg, ArgInfos, MF, [&](unsigned Reg, uint64_t Offset) { - Regs.push_back(Reg); - }); - - if (Regs.size() > 1) - MIRBuilder.buildUnmerge(Regs, Arg.Reg); + splitToValueTypes(Arg, ArgInfos, MF); } - auto ArgAssignFn = TLI.CCAssignFnForCall(CallConv, /*IsVarArg=*/false); + auto ArgAssignFn = TLI.CCAssignFnForCall(CallConv, IsVarArg); OutgoingValueHandler ArgHandler(MIRBuilder, MRI, MIB, ArgAssignFn); if (!handleAssignments(MIRBuilder, ArgInfos, ArgHandler)) return false; @@ -594,22 +569,11 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, return false; ArgInfos.clear(); - SmallVector<unsigned, 8> SplitRegs; - splitToValueTypes(OrigRet, ArgInfos, MF, - [&](unsigned Reg, uint64_t Offset) { - SplitRegs.push_back(Reg); - }); - - auto RetAssignFn = TLI.CCAssignFnForReturn(CallConv, /*IsVarArg=*/false); + splitToValueTypes(OrigRet, ArgInfos, MF); + auto RetAssignFn = TLI.CCAssignFnForReturn(CallConv, IsVarArg); CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn); if (!handleAssignments(MIRBuilder, ArgInfos, RetHandler)) return false; - - if (!SplitRegs.empty()) { - // We have split the value and allocated each individual piece, now build - // it up again. - MIRBuilder.buildMerge(OrigRet.Reg, SplitRegs); - } } // We now know the size of the stack - update the ADJCALLSTACKDOWN diff --git a/lib/Target/ARM/ARMCallLowering.h b/lib/Target/ARM/ARMCallLowering.h index 45a988a2f00e..794127b5ebc7 100644 --- a/lib/Target/ARM/ARMCallLowering.h +++ b/lib/Target/ARM/ARMCallLowering.h @@ -1,9 +1,8 @@ //===- llvm/lib/Target/ARM/ARMCallLowering.h - Call lowering ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -34,10 +33,10 @@ public: ARMCallLowering(const ARMTargetLowering &TLI); bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, - ArrayRef<unsigned> VRegs) const override; + ArrayRef<Register> VRegs) const override; bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, - ArrayRef<unsigned> VRegs) const override; + ArrayRef<ArrayRef<Register>> VRegs) const override; bool lowerCall(MachineIRBuilder &MIRBuilder, CallingConv::ID CallConv, const MachineOperand &Callee, const ArgInfo &OrigRet, @@ -45,17 +44,14 @@ public: private: bool lowerReturnVal(MachineIRBuilder &MIRBuilder, const Value *Val, - ArrayRef<unsigned> VRegs, + ArrayRef<Register> VRegs, MachineInstrBuilder &Ret) const; - using SplitArgTy = std::function<void(unsigned Reg, uint64_t Offset)>; - /// Split an argument into one or more arguments that the CC lowering can cope - /// with (e.g. replace pointers with integers). + /// with. void splitToValueTypes(const ArgInfo &OrigArg, SmallVectorImpl<ArgInfo> &SplitArgs, - MachineFunction &MF, - const SplitArgTy &PerformArgSplit) const; + MachineFunction &MF) const; }; } // end namespace llvm diff --git a/lib/Target/ARM/ARMCallingConv.cpp b/lib/Target/ARM/ARMCallingConv.cpp new file mode 100644 index 000000000000..5ede7c67f7c2 --- /dev/null +++ b/lib/Target/ARM/ARMCallingConv.cpp @@ -0,0 +1,284 @@ +//=== ARMCallingConv.cpp - ARM Custom CC Routines ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the custom routines for the ARM Calling Convention that +// aren't done by tablegen, and includes the table generated implementations. +// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMCallingConv.h" +#include "ARMSubtarget.h" +#include "ARMRegisterInfo.h" +using namespace llvm; + +// APCS f64 is in register pairs, possibly split to stack +static bool f64AssignAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + CCState &State, bool CanFail) { + static const MCPhysReg RegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; + + // Try to get the first register. + if (unsigned Reg = State.AllocateReg(RegList)) + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + else { + // For the 2nd half of a v2f64, do not fail. + if (CanFail) + return false; + + // Put the whole thing on the stack. + State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, + State.AllocateStack(8, 4), + LocVT, LocInfo)); + return true; + } + + // Try to get the second register. + if (unsigned Reg = State.AllocateReg(RegList)) + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + else + State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, + State.AllocateStack(4, 4), + LocVT, LocInfo)); + return true; +} + +static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + if (!f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, true)) + return false; + if (LocVT == MVT::v2f64 && + !f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, false)) + return false; + return true; // we handled it +} + +// AAPCS f64 is in aligned register pairs +static bool f64AssignAAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + CCState &State, bool CanFail) { + static const MCPhysReg HiRegList[] = { ARM::R0, ARM::R2 }; + static const MCPhysReg LoRegList[] = { ARM::R1, ARM::R3 }; + static const MCPhysReg ShadowRegList[] = { ARM::R0, ARM::R1 }; + static const MCPhysReg GPRArgRegs[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; + + unsigned Reg = State.AllocateReg(HiRegList, ShadowRegList); + if (Reg == 0) { + + // If we had R3 unallocated only, now we still must to waste it. + Reg = State.AllocateReg(GPRArgRegs); + assert((!Reg || Reg == ARM::R3) && "Wrong GPRs usage for f64"); + + // For the 2nd half of a v2f64, do not just fail. + if (CanFail) + return false; + + // Put the whole thing on the stack. + State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, + State.AllocateStack(8, 8), + LocVT, LocInfo)); + return true; + } + + unsigned i; + for (i = 0; i < 2; ++i) + if (HiRegList[i] == Reg) + break; + + unsigned T = State.AllocateReg(LoRegList[i]); + (void)T; + assert(T == LoRegList[i] && "Could not allocate register"); + + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i], + LocVT, LocInfo)); + return true; +} + +static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + if (!f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, true)) + return false; + if (LocVT == MVT::v2f64 && + !f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, false)) + return false; + return true; // we handled it +} + +static bool f64RetAssign(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, CCState &State) { + static const MCPhysReg HiRegList[] = { ARM::R0, ARM::R2 }; + static const MCPhysReg LoRegList[] = { ARM::R1, ARM::R3 }; + + unsigned Reg = State.AllocateReg(HiRegList, LoRegList); + if (Reg == 0) + return false; // we didn't handle it + + unsigned i; + for (i = 0; i < 2; ++i) + if (HiRegList[i] == Reg) + break; + + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i], + LocVT, LocInfo)); + return true; +} + +static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + if (!f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State)) + return false; + if (LocVT == MVT::v2f64 && !f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State)) + return false; + return true; // we handled it +} + +static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + return RetCC_ARM_APCS_Custom_f64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, + State); +} + +static const MCPhysReg RRegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; + +static const MCPhysReg SRegList[] = { ARM::S0, ARM::S1, ARM::S2, ARM::S3, + ARM::S4, ARM::S5, ARM::S6, ARM::S7, + ARM::S8, ARM::S9, ARM::S10, ARM::S11, + ARM::S12, ARM::S13, ARM::S14, ARM::S15 }; +static const MCPhysReg DRegList[] = { ARM::D0, ARM::D1, ARM::D2, ARM::D3, + ARM::D4, ARM::D5, ARM::D6, ARM::D7 }; +static const MCPhysReg QRegList[] = { ARM::Q0, ARM::Q1, ARM::Q2, ARM::Q3 }; + + +// Allocate part of an AAPCS HFA or HVA. We assume that each member of the HA +// has InConsecutiveRegs set, and that the last member also has +// InConsecutiveRegsLast set. We must process all members of the HA before +// we can allocate it, as we need to know the total number of registers that +// will be needed in order to (attempt to) allocate a contiguous block. +static bool CC_ARM_AAPCS_Custom_Aggregate(unsigned &ValNo, MVT &ValVT, + MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + SmallVectorImpl<CCValAssign> &PendingMembers = State.getPendingLocs(); + + // AAPCS HFAs must have 1-4 elements, all of the same type + if (PendingMembers.size() > 0) + assert(PendingMembers[0].getLocVT() == LocVT); + + // Add the argument to the list to be allocated once we know the size of the + // aggregate. Store the type's required alignmnent as extra info for later: in + // the [N x i64] case all trace has been removed by the time we actually get + // to do allocation. + PendingMembers.push_back(CCValAssign::getPending(ValNo, ValVT, LocVT, LocInfo, + ArgFlags.getOrigAlign())); + + if (!ArgFlags.isInConsecutiveRegsLast()) + return true; + + // Try to allocate a contiguous block of registers, each of the correct + // size to hold one member. + auto &DL = State.getMachineFunction().getDataLayout(); + unsigned StackAlign = DL.getStackAlignment(); + unsigned Align = std::min(PendingMembers[0].getExtraInfo(), StackAlign); + + ArrayRef<MCPhysReg> RegList; + switch (LocVT.SimpleTy) { + case MVT::i32: { + RegList = RRegList; + unsigned RegIdx = State.getFirstUnallocated(RegList); + + // First consume all registers that would give an unaligned object. Whether + // we go on stack or in regs, no-one will be using them in future. + unsigned RegAlign = alignTo(Align, 4) / 4; + while (RegIdx % RegAlign != 0 && RegIdx < RegList.size()) + State.AllocateReg(RegList[RegIdx++]); + + break; + } + case MVT::f16: + case MVT::f32: + RegList = SRegList; + break; + case MVT::v4f16: + case MVT::f64: + RegList = DRegList; + break; + case MVT::v8f16: + case MVT::v2f64: + RegList = QRegList; + break; + default: + llvm_unreachable("Unexpected member type for block aggregate"); + break; + } + + unsigned RegResult = State.AllocateRegBlock(RegList, PendingMembers.size()); + if (RegResult) { + for (SmallVectorImpl<CCValAssign>::iterator It = PendingMembers.begin(); + It != PendingMembers.end(); ++It) { + It->convertToReg(RegResult); + State.addLoc(*It); + ++RegResult; + } + PendingMembers.clear(); + return true; + } + + // Register allocation failed, we'll be needing the stack + unsigned Size = LocVT.getSizeInBits() / 8; + if (LocVT == MVT::i32 && State.getNextStackOffset() == 0) { + // If nothing else has used the stack until this point, a non-HFA aggregate + // can be split between regs and stack. + unsigned RegIdx = State.getFirstUnallocated(RegList); + for (auto &It : PendingMembers) { + if (RegIdx >= RegList.size()) + It.convertToMem(State.AllocateStack(Size, Size)); + else + It.convertToReg(State.AllocateReg(RegList[RegIdx++])); + + State.addLoc(It); + } + PendingMembers.clear(); + return true; + } else if (LocVT != MVT::i32) + RegList = SRegList; + + // Mark all regs as unavailable (AAPCS rule C.2.vfp for VFP, C.6 for core) + for (auto Reg : RegList) + State.AllocateReg(Reg); + + // After the first item has been allocated, the rest are packed as tightly as + // possible. (E.g. an incoming i64 would have starting Align of 8, but we'll + // be allocating a bunch of i32 slots). + unsigned RestAlign = std::min(Align, Size); + + for (auto &It : PendingMembers) { + It.convertToMem(State.AllocateStack(Size, Align)); + State.addLoc(It); + Align = RestAlign; + } + + // All pending members have now been allocated + PendingMembers.clear(); + + // This will be allocated by the last member of the aggregate + return true; +} + +// Include the table generated calling convention implementations. +#include "ARMGenCallingConv.inc" diff --git a/lib/Target/ARM/ARMCallingConv.h b/lib/Target/ARM/ARMCallingConv.h index 543165de38d0..615634551d90 100644 --- a/lib/Target/ARM/ARMCallingConv.h +++ b/lib/Target/ARM/ARMCallingConv.h @@ -1,292 +1,50 @@ //=== ARMCallingConv.h - ARM Custom Calling Convention Routines -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // -// This file contains the custom routines for the ARM Calling Convention that -// aren't done by tablegen. +// This file declares the entry points for ARM calling convention analysis. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_ARM_ARMCALLINGCONV_H #define LLVM_LIB_TARGET_ARM_ARMCALLINGCONV_H -#include "ARM.h" -#include "ARMBaseInstrInfo.h" -#include "ARMSubtarget.h" #include "llvm/CodeGen/CallingConvLower.h" -#include "llvm/CodeGen/TargetInstrInfo.h" -#include "llvm/IR/CallingConv.h" namespace llvm { -// APCS f64 is in register pairs, possibly split to stack -static bool f64AssignAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - CCState &State, bool CanFail) { - static const MCPhysReg RegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; - - // Try to get the first register. - if (unsigned Reg = State.AllocateReg(RegList)) - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); - else { - // For the 2nd half of a v2f64, do not fail. - if (CanFail) - return false; - - // Put the whole thing on the stack. - State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, - State.AllocateStack(8, 4), - LocVT, LocInfo)); - return true; - } - - // Try to get the second register. - if (unsigned Reg = State.AllocateReg(RegList)) - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); - else - State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, - State.AllocateStack(4, 4), - LocVT, LocInfo)); - return true; -} - -static bool CC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - ISD::ArgFlagsTy &ArgFlags, - CCState &State) { - if (!f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, true)) - return false; - if (LocVT == MVT::v2f64 && - !f64AssignAPCS(ValNo, ValVT, LocVT, LocInfo, State, false)) - return false; - return true; // we handled it -} - -// AAPCS f64 is in aligned register pairs -static bool f64AssignAAPCS(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - CCState &State, bool CanFail) { - static const MCPhysReg HiRegList[] = { ARM::R0, ARM::R2 }; - static const MCPhysReg LoRegList[] = { ARM::R1, ARM::R3 }; - static const MCPhysReg ShadowRegList[] = { ARM::R0, ARM::R1 }; - static const MCPhysReg GPRArgRegs[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; - - unsigned Reg = State.AllocateReg(HiRegList, ShadowRegList); - if (Reg == 0) { - - // If we had R3 unallocated only, now we still must to waste it. - Reg = State.AllocateReg(GPRArgRegs); - assert((!Reg || Reg == ARM::R3) && "Wrong GPRs usage for f64"); - - // For the 2nd half of a v2f64, do not just fail. - if (CanFail) - return false; - - // Put the whole thing on the stack. - State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, - State.AllocateStack(8, 8), - LocVT, LocInfo)); - return true; - } - - unsigned i; - for (i = 0; i < 2; ++i) - if (HiRegList[i] == Reg) - break; - - unsigned T = State.AllocateReg(LoRegList[i]); - (void)T; - assert(T == LoRegList[i] && "Could not allocate register"); - - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i], - LocVT, LocInfo)); - return true; -} - -static bool CC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - ISD::ArgFlagsTy &ArgFlags, - CCState &State) { - if (!f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, true)) - return false; - if (LocVT == MVT::v2f64 && - !f64AssignAAPCS(ValNo, ValVT, LocVT, LocInfo, State, false)) - return false; - return true; // we handled it -} - -static bool f64RetAssign(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, CCState &State) { - static const MCPhysReg HiRegList[] = { ARM::R0, ARM::R2 }; - static const MCPhysReg LoRegList[] = { ARM::R1, ARM::R3 }; - - unsigned Reg = State.AllocateReg(HiRegList, LoRegList); - if (Reg == 0) - return false; // we didn't handle it - - unsigned i; - for (i = 0; i < 2; ++i) - if (HiRegList[i] == Reg) - break; - - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); - State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, LoRegList[i], - LocVT, LocInfo)); - return true; -} - -static bool RetCC_ARM_APCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - ISD::ArgFlagsTy &ArgFlags, - CCState &State) { - if (!f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State)) - return false; - if (LocVT == MVT::v2f64 && !f64RetAssign(ValNo, ValVT, LocVT, LocInfo, State)) - return false; - return true; // we handled it -} - -static bool RetCC_ARM_AAPCS_Custom_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - ISD::ArgFlagsTy &ArgFlags, - CCState &State) { - return RetCC_ARM_APCS_Custom_f64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, - State); -} - -static const MCPhysReg RRegList[] = { ARM::R0, ARM::R1, ARM::R2, ARM::R3 }; - -static const MCPhysReg SRegList[] = { ARM::S0, ARM::S1, ARM::S2, ARM::S3, - ARM::S4, ARM::S5, ARM::S6, ARM::S7, - ARM::S8, ARM::S9, ARM::S10, ARM::S11, - ARM::S12, ARM::S13, ARM::S14, ARM::S15 }; -static const MCPhysReg DRegList[] = { ARM::D0, ARM::D1, ARM::D2, ARM::D3, - ARM::D4, ARM::D5, ARM::D6, ARM::D7 }; -static const MCPhysReg QRegList[] = { ARM::Q0, ARM::Q1, ARM::Q2, ARM::Q3 }; - - -// Allocate part of an AAPCS HFA or HVA. We assume that each member of the HA -// has InConsecutiveRegs set, and that the last member also has -// InConsecutiveRegsLast set. We must process all members of the HA before -// we can allocate it, as we need to know the total number of registers that -// will be needed in order to (attempt to) allocate a contiguous block. -static bool CC_ARM_AAPCS_Custom_Aggregate(unsigned &ValNo, MVT &ValVT, - MVT &LocVT, - CCValAssign::LocInfo &LocInfo, - ISD::ArgFlagsTy &ArgFlags, - CCState &State) { - SmallVectorImpl<CCValAssign> &PendingMembers = State.getPendingLocs(); - - // AAPCS HFAs must have 1-4 elements, all of the same type - if (PendingMembers.size() > 0) - assert(PendingMembers[0].getLocVT() == LocVT); - - // Add the argument to the list to be allocated once we know the size of the - // aggregate. Store the type's required alignmnent as extra info for later: in - // the [N x i64] case all trace has been removed by the time we actually get - // to do allocation. - PendingMembers.push_back(CCValAssign::getPending(ValNo, ValVT, LocVT, LocInfo, - ArgFlags.getOrigAlign())); - - if (!ArgFlags.isInConsecutiveRegsLast()) - return true; - - // Try to allocate a contiguous block of registers, each of the correct - // size to hold one member. - auto &DL = State.getMachineFunction().getDataLayout(); - unsigned StackAlign = DL.getStackAlignment(); - unsigned Align = std::min(PendingMembers[0].getExtraInfo(), StackAlign); - - ArrayRef<MCPhysReg> RegList; - switch (LocVT.SimpleTy) { - case MVT::i32: { - RegList = RRegList; - unsigned RegIdx = State.getFirstUnallocated(RegList); - - // First consume all registers that would give an unaligned object. Whether - // we go on stack or in regs, no-one will be using them in future. - unsigned RegAlign = alignTo(Align, 4) / 4; - while (RegIdx % RegAlign != 0 && RegIdx < RegList.size()) - State.AllocateReg(RegList[RegIdx++]); - - break; - } - case MVT::f16: - case MVT::f32: - RegList = SRegList; - break; - case MVT::v4f16: - case MVT::f64: - RegList = DRegList; - break; - case MVT::v8f16: - case MVT::v2f64: - RegList = QRegList; - break; - default: - llvm_unreachable("Unexpected member type for block aggregate"); - break; - } - - unsigned RegResult = State.AllocateRegBlock(RegList, PendingMembers.size()); - if (RegResult) { - for (SmallVectorImpl<CCValAssign>::iterator It = PendingMembers.begin(); - It != PendingMembers.end(); ++It) { - It->convertToReg(RegResult); - State.addLoc(*It); - ++RegResult; - } - PendingMembers.clear(); - return true; - } - - // Register allocation failed, we'll be needing the stack - unsigned Size = LocVT.getSizeInBits() / 8; - if (LocVT == MVT::i32 && State.getNextStackOffset() == 0) { - // If nothing else has used the stack until this point, a non-HFA aggregate - // can be split between regs and stack. - unsigned RegIdx = State.getFirstUnallocated(RegList); - for (auto &It : PendingMembers) { - if (RegIdx >= RegList.size()) - It.convertToMem(State.AllocateStack(Size, Size)); - else - It.convertToReg(State.AllocateReg(RegList[RegIdx++])); - - State.addLoc(It); - } - PendingMembers.clear(); - return true; - } else if (LocVT != MVT::i32) - RegList = SRegList; - - // Mark all regs as unavailable (AAPCS rule C.2.vfp for VFP, C.6 for core) - for (auto Reg : RegList) - State.AllocateReg(Reg); - - // After the first item has been allocated, the rest are packed as tightly as - // possible. (E.g. an incoming i64 would have starting Align of 8, but we'll - // be allocating a bunch of i32 slots). - unsigned RestAlign = std::min(Align, Size); - - for (auto &It : PendingMembers) { - It.convertToMem(State.AllocateStack(Size, Align)); - State.addLoc(It); - Align = RestAlign; - } - - // All pending members have now been allocated - PendingMembers.clear(); - - // This will be allocated by the last member of the aggregate - return true; -} +bool CC_ARM_AAPCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool CC_ARM_AAPCS_VFP(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool CC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool CC_ARM_APCS_GHC(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool FastCC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool RetCC_ARM_AAPCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool RetCC_ARM_AAPCS_VFP(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool RetCC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); +bool RetFastCC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); -} // End llvm namespace +} // namespace llvm #endif diff --git a/lib/Target/ARM/ARMCallingConv.td b/lib/Target/ARM/ARMCallingConv.td index f173e423f3e4..61d2d83ddc40 100644 --- a/lib/Target/ARM/ARMCallingConv.td +++ b/lib/Target/ARM/ARMCallingConv.td @@ -1,9 +1,8 @@ //===-- ARMCallingConv.td - Calling Conventions for ARM ----*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This describes the calling conventions for ARM architecture. @@ -16,6 +15,7 @@ class CCIfAlign<string Align, CCAction A>: //===----------------------------------------------------------------------===// // ARM APCS Calling Convention //===----------------------------------------------------------------------===// +let Entry = 1 in def CC_ARM_APCS : CallingConv<[ // Handles byval parameters. @@ -30,8 +30,8 @@ def CC_ARM_APCS : CallingConv<[ CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R8]>>>, // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, // f64 and v2f64 are passed in adjacent GPRs, possibly split onto the stack CCIfType<[f64, v2f64], CCCustom<"CC_ARM_APCS_Custom_f64">>, @@ -44,6 +44,7 @@ def CC_ARM_APCS : CallingConv<[ CCIfType<[v2f64], CCAssignToStack<16, 4>> ]>; +let Entry = 1 in def RetCC_ARM_APCS : CallingConv<[ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>, CCIfType<[f32], CCBitConvertToType<i32>>, @@ -55,8 +56,8 @@ def RetCC_ARM_APCS : CallingConv<[ CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R8]>>>, // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, CCIfType<[f64, v2f64], CCCustom<"RetCC_ARM_APCS_Custom_f64">>, @@ -67,10 +68,11 @@ def RetCC_ARM_APCS : CallingConv<[ //===----------------------------------------------------------------------===// // ARM APCS Calling Convention for FastCC (when VFP2 or later is available) //===----------------------------------------------------------------------===// +let Entry = 1 in def FastCC_ARM_APCS : CallingConv<[ // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, CCIfType<[v2f64], CCAssignToReg<[Q0, Q1, Q2, Q3]>>, CCIfType<[f64], CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>>, @@ -86,10 +88,11 @@ def FastCC_ARM_APCS : CallingConv<[ CCDelegateTo<CC_ARM_APCS> ]>; +let Entry = 1 in def RetFastCC_ARM_APCS : CallingConv<[ // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, CCIfType<[v2f64], CCAssignToReg<[Q0, Q1, Q2, Q3]>>, CCIfType<[f64], CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>>, @@ -102,10 +105,11 @@ def RetFastCC_ARM_APCS : CallingConv<[ // ARM APCS Calling Convention for GHC //===----------------------------------------------------------------------===// +let Entry = 1 in def CC_ARM_APCS_GHC : CallingConv<[ // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, CCIfType<[v2f64], CCAssignToReg<[Q4, Q5]>>, CCIfType<[f64], CCAssignToReg<[D8, D9, D10, D11]>>, @@ -152,6 +156,7 @@ def RetCC_ARM_AAPCS_Common : CallingConv<[ // ARM AAPCS (EABI) Calling Convention //===----------------------------------------------------------------------===// +let Entry = 1 in def CC_ARM_AAPCS : CallingConv<[ // Handles byval parameters. CCIfByVal<CCPassByVal<4, 4>>, @@ -160,8 +165,8 @@ def CC_ARM_AAPCS : CallingConv<[ CCIfNest<CCAssignToReg<[R12]>>, // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, // Pass SwiftSelf in a callee saved register. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R10]>>>, @@ -174,10 +179,11 @@ def CC_ARM_AAPCS : CallingConv<[ CCDelegateTo<CC_ARM_AAPCS_Common> ]>; +let Entry = 1 in def RetCC_ARM_AAPCS : CallingConv<[ // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v8f16,v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v8f16,v16i8, v4f32], CCBitConvertToType<v2f64>>, // Pass SwiftSelf in a callee saved register. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R10]>>>, @@ -196,13 +202,14 @@ def RetCC_ARM_AAPCS : CallingConv<[ // Also used for FastCC (when VFP2 or later is available) //===----------------------------------------------------------------------===// +let Entry = 1 in def CC_ARM_AAPCS_VFP : CallingConv<[ // Handles byval parameters. CCIfByVal<CCPassByVal<4, 4>>, // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, // Pass SwiftSelf in a callee saved register. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R10]>>>, @@ -220,10 +227,11 @@ def CC_ARM_AAPCS_VFP : CallingConv<[ CCDelegateTo<CC_ARM_AAPCS_Common> ]>; +let Entry = 1 in def RetCC_ARM_AAPCS_VFP : CallingConv<[ // Handle all vector types as either f64 or v2f64. - CCIfType<[v1i64, v2i32, v4i16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, - CCIfType<[v2i64, v4i32, v8i16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + CCIfType<[v1i64, v2i32, v4i16, v4f16, v4f16, v8i8, v2f32], CCBitConvertToType<f64>>, + CCIfType<[v2i64, v4i32, v8i16, v8f16, v8f16, v16i8, v4f32], CCBitConvertToType<v2f64>>, // Pass SwiftSelf in a callee saved register. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R10]>>>, diff --git a/lib/Target/ARM/ARMCodeGenPrepare.cpp b/lib/Target/ARM/ARMCodeGenPrepare.cpp index b631c2bc687b..2fc5f4aaab50 100644 --- a/lib/Target/ARM/ARMCodeGenPrepare.cpp +++ b/lib/Target/ARM/ARMCodeGenPrepare.cpp @@ -1,9 +1,8 @@ //===----- ARMCodeGenPrepare.cpp ------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -114,15 +113,20 @@ class IRPromoter { SmallPtrSet<Value*, 8> Promoted; Module *M = nullptr; LLVMContext &Ctx; + // The type we promote to: always i32 IntegerType *ExtTy = nullptr; + // The type of the value that the search began from, either i8 or i16. + // This defines the max range of the values that we allow in the promoted + // tree. IntegerType *OrigTy = nullptr; - SmallPtrSetImpl<Value*> *Visited; + SetVector<Value*> *Visited; SmallPtrSetImpl<Value*> *Sources; SmallPtrSetImpl<Instruction*> *Sinks; SmallPtrSetImpl<Instruction*> *SafeToPromote; + SmallPtrSetImpl<Instruction*> *SafeWrap; void ReplaceAllUsersOfWith(Value *From, Value *To); - void PrepareConstants(void); + void PrepareWrappingAdds(void); void ExtendSources(void); void ConvertTruncs(void); void PromoteTree(void); @@ -135,10 +139,11 @@ public: void Mutate(Type *OrigTy, - SmallPtrSetImpl<Value*> &Visited, + SetVector<Value*> &Visited, SmallPtrSetImpl<Value*> &Sources, SmallPtrSetImpl<Instruction*> &Sinks, - SmallPtrSetImpl<Instruction*> &SafeToPromote); + SmallPtrSetImpl<Instruction*> &SafeToPromote, + SmallPtrSetImpl<Instruction*> &SafeWrap); }; class ARMCodeGenPrepare : public FunctionPass { @@ -146,8 +151,9 @@ class ARMCodeGenPrepare : public FunctionPass { IRPromoter *Promoter = nullptr; std::set<Value*> AllVisited; SmallPtrSet<Instruction*, 8> SafeToPromote; + SmallPtrSet<Instruction*, 4> SafeWrap; - bool isSafeOverflow(Instruction *I); + bool isSafeWrap(Instruction *I); bool isSupportedValue(Value *V); bool isLegalToPromote(Value *V); bool TryToPromote(Value *V); @@ -172,13 +178,17 @@ public: } -static bool generateSignBits(Value *V) { +static bool GenerateSignBits(Value *V) { + if (auto *Arg = dyn_cast<Argument>(V)) + return Arg->hasSExtAttr(); + if (!isa<Instruction>(V)) return false; unsigned Opc = cast<Instruction>(V)->getOpcode(); return Opc == Instruction::AShr || Opc == Instruction::SDiv || - Opc == Instruction::SRem; + Opc == Instruction::SRem || Opc == Instruction::SExt || + Opc == Instruction::SIToFP; } static bool EqualTypeSize(Value *V) { @@ -271,19 +281,14 @@ static bool isSink(Value *V) { return isa<CallInst>(V); } -/// Return whether the instruction can be promoted within any modifications to -/// its operands or result. -bool ARMCodeGenPrepare::isSafeOverflow(Instruction *I) { - // FIXME Do we need NSW too? - if (isa<OverflowingBinaryOperator>(I) && I->hasNoUnsignedWrap()) - return true; - - // We can support a, potentially, overflowing instruction (I) if: +/// Return whether this instruction can safely wrap. +bool ARMCodeGenPrepare::isSafeWrap(Instruction *I) { + // We can support a, potentially, wrapping instruction (I) if: // - It is only used by an unsigned icmp. // - The icmp uses a constant. - // - The overflowing value (I) is decreasing, i.e would underflow - wrapping + // - The wrapping value (I) is decreasing, i.e would underflow - wrapping // around zero to become a larger number than before. - // - The underflowing instruction (I) also uses a constant. + // - The wrapping instruction (I) also uses a constant. // // We can then use the two constants to calculate whether the result would // wrap in respect to itself in the original bitwidth. If it doesn't wrap, @@ -327,7 +332,7 @@ bool ARMCodeGenPrepare::isSafeOverflow(Instruction *I) { // - (255 >= 254) == (0xFFFFFFFF >= 254) == true // // To demonstrate why we can't handle increasing values: - // + // // %add = add i8 %a, 2 // %cmp = icmp ult i8 %add, 127 // @@ -385,6 +390,7 @@ bool ARMCodeGenPrepare::isSafeOverflow(Instruction *I) { return false; LLVM_DEBUG(dbgs() << "ARM CGP: Allowing safe overflow for " << *I << "\n"); + SafeWrap.insert(I); return true; } @@ -408,13 +414,16 @@ static bool shouldPromote(Value *V) { /// Return whether we can safely mutate V's type to ExtTy without having to be /// concerned with zero extending or truncation. static bool isPromotedResultSafe(Value *V) { + if (GenerateSignBits(V)) + return false; + if (!isa<Instruction>(V)) return true; - if (generateSignBits(V)) - return false; + if (!isa<OverflowingBinaryOperator>(V)) + return true; - return !isa<OverflowingBinaryOperator>(V); + return cast<Instruction>(V)->hasNoUnsignedWrap(); } /// Return the intrinsic for the instruction that can perform the same @@ -462,61 +471,34 @@ void IRPromoter::ReplaceAllUsersOfWith(Value *From, Value *To) { InstsToRemove.insert(I); } -void IRPromoter::PrepareConstants() { +void IRPromoter::PrepareWrappingAdds() { + LLVM_DEBUG(dbgs() << "ARM CGP: Prepare underflowing adds.\n"); IRBuilder<> Builder{Ctx}; - // First step is to prepare the instructions for mutation. Most constants - // just need to be zero extended into their new type, but complications arise - // because: - // - For nuw binary operators, negative immediates would need sign extending; - // however, instead we'll change them to positive and zext them. We can do - // this because: - // > The operators that can wrap are: add, sub, mul and shl. - // > shl interprets its second operand as unsigned and if the first operand - // is an immediate, it will need zext to be nuw. - // > I'm assuming mul has to interpret immediates as unsigned for nuw. - // > Which leaves the nuw add and sub to be handled; as with shl, if an - // immediate is used as operand 0, it will need zext to be nuw. - // - We also allow add and sub to safely overflow in certain circumstances - // and only when the value (operand 0) is being decreased. - // - // For adds and subs, that are either nuw or safely wrap and use a negative - // immediate as operand 1, we create an equivalent instruction using a - // positive immediate. That positive immediate can then be zext along with - // all the other immediates later. - for (auto *V : *Visited) { - if (!isa<Instruction>(V)) - continue; - - auto *I = cast<Instruction>(V); - if (SafeToPromote->count(I)) { - - if (!isa<OverflowingBinaryOperator>(I)) - continue; - - if (auto *Const = dyn_cast<ConstantInt>(I->getOperand(1))) { - if (!Const->isNegative()) - break; - unsigned Opc = I->getOpcode(); - if (Opc != Instruction::Add && Opc != Instruction::Sub) - continue; + // For adds that safely wrap and use a negative immediate as operand 1, we + // create an equivalent instruction using a positive immediate. + // That positive immediate can then be zext along with all the other + // immediates later. + for (auto *I : *SafeWrap) { + if (I->getOpcode() != Instruction::Add) + continue; - LLVM_DEBUG(dbgs() << "ARM CGP: Adjusting " << *I << "\n"); - auto *NewConst = ConstantInt::get(Ctx, Const->getValue().abs()); - Builder.SetInsertPoint(I); - Value *NewVal = Opc == Instruction::Sub ? - Builder.CreateAdd(I->getOperand(0), NewConst) : - Builder.CreateSub(I->getOperand(0), NewConst); - LLVM_DEBUG(dbgs() << "ARM CGP: New equivalent: " << *NewVal << "\n"); + LLVM_DEBUG(dbgs() << "ARM CGP: Adjusting " << *I << "\n"); + assert((isa<ConstantInt>(I->getOperand(1)) && + cast<ConstantInt>(I->getOperand(1))->isNegative()) && + "Wrapping should have a negative immediate as the second operand"); - if (auto *NewInst = dyn_cast<Instruction>(NewVal)) { - NewInst->copyIRFlags(I); - NewInsts.insert(NewInst); - } - InstsToRemove.insert(I); - I->replaceAllUsesWith(NewVal); - } + auto Const = cast<ConstantInt>(I->getOperand(1)); + auto *NewConst = ConstantInt::get(Ctx, Const->getValue().abs()); + Builder.SetInsertPoint(I); + Value *NewVal = Builder.CreateSub(I->getOperand(0), NewConst); + if (auto *NewInst = dyn_cast<Instruction>(NewVal)) { + NewInst->copyIRFlags(I); + NewInsts.insert(NewInst); } + InstsToRemove.insert(I); + I->replaceAllUsesWith(NewVal); + LLVM_DEBUG(dbgs() << "ARM CGP: New equivalent: " << *NewVal << "\n"); } for (auto *I : NewInsts) Visited->insert(I); @@ -605,7 +587,7 @@ void IRPromoter::PromoteTree() { if (!shouldPromote(I) || SafeToPromote->count(I) || NewInsts.count(I)) continue; - + assert(EnableDSP && "DSP intrinisc insertion not enabled!"); // Replace unsafe instructions with appropriate intrinsic calls. @@ -683,13 +665,14 @@ void IRPromoter::TruncateSinks() { } void IRPromoter::Cleanup() { + LLVM_DEBUG(dbgs() << "ARM CGP: Cleanup..\n"); // Some zexts will now have become redundant, along with their trunc // operands, so remove them for (auto V : *Visited) { - if (!isa<CastInst>(V)) + if (!isa<ZExtInst>(V)) continue; - auto ZExt = cast<CastInst>(V); + auto ZExt = cast<ZExtInst>(V); if (ZExt->getDestTy() != ExtTy) continue; @@ -701,9 +684,11 @@ void IRPromoter::Cleanup() { continue; } - // For any truncs that we insert to handle zexts, we can replace the - // result of the zext with the input to the trunc. - if (NewInsts.count(Src) && isa<ZExtInst>(V) && isa<TruncInst>(Src)) { + // Unless they produce a value that is narrower than ExtTy, we can + // replace the result of the zext with the input of a newly inserted + // trunc. + if (NewInsts.count(Src) && isa<TruncInst>(Src) && + Src->getType() == OrigTy) { auto *Trunc = cast<TruncInst>(Src); assert(Trunc->getOperand(0)->getType() == ExtTy && "expected inserted trunc to be operating on i32"); @@ -721,9 +706,12 @@ void IRPromoter::Cleanup() { NewInsts.clear(); TruncTysMap.clear(); Promoted.clear(); + SafeToPromote->clear(); + SafeWrap->clear(); } void IRPromoter::ConvertTruncs() { + LLVM_DEBUG(dbgs() << "ARM CGP: Converting truncs..\n"); IRBuilder<> Builder{Ctx}; for (auto *V : *Visited) { @@ -731,12 +719,13 @@ void IRPromoter::ConvertTruncs() { continue; auto *Trunc = cast<TruncInst>(V); - assert(LessThanTypeSize(Trunc) && "expected narrow trunc"); - Builder.SetInsertPoint(Trunc); - unsigned NumBits = - cast<IntegerType>(Trunc->getType())->getScalarSizeInBits(); - ConstantInt *Mask = ConstantInt::get(Ctx, APInt::getMaxValue(NumBits)); + IntegerType *SrcTy = cast<IntegerType>(Trunc->getOperand(0)->getType()); + IntegerType *DestTy = cast<IntegerType>(TruncTysMap[Trunc][0]); + + unsigned NumBits = DestTy->getScalarSizeInBits(); + ConstantInt *Mask = + ConstantInt::get(SrcTy, APInt::getMaxValue(NumBits).getZExtValue()); Value *Masked = Builder.CreateAnd(Trunc->getOperand(0), Mask); if (auto *I = dyn_cast<Instruction>(Masked)) @@ -747,10 +736,11 @@ void IRPromoter::ConvertTruncs() { } void IRPromoter::Mutate(Type *OrigTy, - SmallPtrSetImpl<Value*> &Visited, + SetVector<Value*> &Visited, SmallPtrSetImpl<Value*> &Sources, SmallPtrSetImpl<Instruction*> &Sinks, - SmallPtrSetImpl<Instruction*> &SafeToPromote) { + SmallPtrSetImpl<Instruction*> &SafeToPromote, + SmallPtrSetImpl<Instruction*> &SafeWrap) { LLVM_DEBUG(dbgs() << "ARM CGP: Promoting use-def chains to from " << ARMCodeGenPrepare::TypeSize << " to 32-bits\n"); @@ -763,6 +753,7 @@ void IRPromoter::Mutate(Type *OrigTy, this->Sources = &Sources; this->Sinks = &Sinks; this->SafeToPromote = &SafeToPromote; + this->SafeWrap = &SafeWrap; // Cache original types of the values that will likely need truncating for (auto *I : Sinks) { @@ -778,22 +769,28 @@ void IRPromoter::Mutate(Type *OrigTy, TruncTysMap[I].push_back(I->getOperand(i)->getType()); } } + for (auto *V : Visited) { + if (!isa<TruncInst>(V) || Sources.count(V)) + continue; + auto *Trunc = cast<TruncInst>(V); + TruncTysMap[Trunc].push_back(Trunc->getDestTy()); + } - // Convert adds and subs using negative immediates to equivalent instructions - // that use positive constants. - PrepareConstants(); + // Convert adds using negative immediates to equivalent instructions that use + // positive constants. + PrepareWrappingAdds(); // Insert zext instructions between sources and their users. ExtendSources(); - // Convert any truncs, that aren't sources, into AND masks. - ConvertTruncs(); - // Promote visited instructions, mutating their types in place. Also insert // DSP intrinsics, if enabled, for adds and subs which would be unsafe to // promote. PromoteTree(); + // Convert any truncs, that aren't sources, into AND masks. + ConvertTruncs(); + // Insert trunc instructions for use by calls, stores etc... TruncateSinks(); @@ -819,6 +816,11 @@ bool ARMCodeGenPrepare::isSupportedValue(Value *V) { return EqualTypeSize(I->getOperand(0)); } + if (GenerateSignBits(V)) { + LLVM_DEBUG(dbgs() << "ARM CGP: No, instruction can generate sign bits.\n"); + return false; + } + // Memory instructions if (isa<StoreInst>(V) || isa<GetElementPtrInst>(V)) return true; @@ -835,9 +837,6 @@ bool ARMCodeGenPrepare::isSupportedValue(Value *V) { isa<LoadInst>(V)) return isSupportedType(V); - if (isa<SExtInst>(V)) - return false; - if (auto *Cast = dyn_cast<CastInst>(V)) return isSupportedType(Cast) || isSupportedType(Cast->getOperand(0)); @@ -854,10 +853,6 @@ bool ARMCodeGenPrepare::isSupportedValue(Value *V) { if (!isSupportedType(V)) return false; - if (generateSignBits(V)) { - LLVM_DEBUG(dbgs() << "ARM CGP: No, instruction can generate sign bits.\n"); - return false; - } return true; } @@ -873,7 +868,7 @@ bool ARMCodeGenPrepare::isLegalToPromote(Value *V) { if (SafeToPromote.count(I)) return true; - if (isPromotedResultSafe(V) || isSafeOverflow(I)) { + if (isPromotedResultSafe(V) || isSafeWrap(I)) { SafeToPromote.insert(I); return true; } @@ -911,6 +906,7 @@ bool ARMCodeGenPrepare::TryToPromote(Value *V) { return false; SafeToPromote.clear(); + SafeWrap.clear(); if (!isSupportedValue(V) || !shouldPromote(V) || !isLegalToPromote(V)) return false; @@ -921,7 +917,7 @@ bool ARMCodeGenPrepare::TryToPromote(Value *V) { SetVector<Value*> WorkList; SmallPtrSet<Value*, 8> Sources; SmallPtrSet<Instruction*, 4> Sinks; - SmallPtrSet<Value*, 16> CurrentVisited; + SetVector<Value*> CurrentVisited; WorkList.insert(V); // Return true if V was added to the worklist as a supported instruction, @@ -1009,7 +1005,8 @@ bool ARMCodeGenPrepare::TryToPromote(Value *V) { if (ToPromote < 2) return false; - Promoter->Mutate(OrigTy, CurrentVisited, Sources, Sinks, SafeToPromote); + Promoter->Mutate(OrigTy, CurrentVisited, Sources, Sinks, SafeToPromote, + SafeWrap); return true; } diff --git a/lib/Target/ARM/ARMComputeBlockSize.cpp b/lib/Target/ARM/ARMComputeBlockSize.cpp deleted file mode 100644 index b263e9d86c42..000000000000 --- a/lib/Target/ARM/ARMComputeBlockSize.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//===--- ARMComputeBlockSize.cpp - Compute machine block sizes ------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ARM.h" -#include "ARMBaseInstrInfo.h" -#include "ARMBasicBlockInfo.h" -#include "ARMMachineFunctionInfo.h" -#include "llvm/CodeGen/MachineBasicBlock.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineInstr.h" -#include "llvm/CodeGen/TargetSubtargetInfo.h" -#include <vector> - -using namespace llvm; - -namespace llvm { - -// mayOptimizeThumb2Instruction - Returns true if optimizeThumb2Instructions -// below may shrink MI. -static bool -mayOptimizeThumb2Instruction(const MachineInstr *MI) { - switch(MI->getOpcode()) { - // optimizeThumb2Instructions. - case ARM::t2LEApcrel: - case ARM::t2LDRpci: - // optimizeThumb2Branches. - case ARM::t2B: - case ARM::t2Bcc: - case ARM::tBcc: - // optimizeThumb2JumpTables. - case ARM::t2BR_JT: - case ARM::tBR_JTr: - return true; - } - return false; -} - -void computeBlockSize(MachineFunction *MF, MachineBasicBlock *MBB, - BasicBlockInfo &BBI) { - const ARMBaseInstrInfo *TII = - static_cast<const ARMBaseInstrInfo *>(MF->getSubtarget().getInstrInfo()); - bool isThumb = MF->getInfo<ARMFunctionInfo>()->isThumbFunction(); - BBI.Size = 0; - BBI.Unalign = 0; - BBI.PostAlign = 0; - - for (MachineInstr &I : *MBB) { - BBI.Size += TII->getInstSizeInBytes(I); - // For inline asm, getInstSizeInBytes returns a conservative estimate. - // The actual size may be smaller, but still a multiple of the instr size. - if (I.isInlineAsm()) - BBI.Unalign = isThumb ? 1 : 2; - // Also consider instructions that may be shrunk later. - else if (isThumb && mayOptimizeThumb2Instruction(&I)) - BBI.Unalign = 1; - } - - // tBR_JTr contains a .align 2 directive. - if (!MBB->empty() && MBB->back().getOpcode() == ARM::tBR_JTr) { - BBI.PostAlign = 2; - MBB->getParent()->ensureAlignment(2); - } -} - -std::vector<BasicBlockInfo> computeAllBlockSizes(MachineFunction *MF) { - std::vector<BasicBlockInfo> BBInfo; - BBInfo.resize(MF->getNumBlockIDs()); - - for (MachineBasicBlock &MBB : *MF) - computeBlockSize(MF, &MBB, BBInfo[MBB.getNumber()]); - - return BBInfo; -} - -} // end namespace llvm diff --git a/lib/Target/ARM/ARMConstantIslandPass.cpp b/lib/Target/ARM/ARMConstantIslandPass.cpp index 5e97c4cb35e3..60e5d7bf6098 100644 --- a/lib/Target/ARM/ARMConstantIslandPass.cpp +++ b/lib/Target/ARM/ARMConstantIslandPass.cpp @@ -1,9 +1,8 @@ //===- ARMConstantIslandPass.cpp - ARM constant islands -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -98,7 +97,7 @@ namespace { /// CPE - A constant pool entry that has been placed somewhere, which /// tracks a list of users. class ARMConstantIslands : public MachineFunctionPass { - std::vector<BasicBlockInfo> BBInfo; + std::unique_ptr<ARMBasicBlockUtils> BBUtils = nullptr; /// WaterList - A sorted list of basic blocks where islands could be placed /// (i.e. blocks that don't fall through to the following block, due @@ -244,7 +243,6 @@ namespace { void initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs); MachineBasicBlock *splitBlockBeforeInstr(MachineInstr *MI); void updateForInsertedWaterBlock(MachineBasicBlock *NewBB); - void adjustBBOffsetsAfter(MachineBasicBlock *BB); bool decrementCPEReferenceCount(unsigned CPI, MachineInstr* CPEMI); unsigned getCombinedIndex(const MachineInstr *CPEMI); int findInRangeCPEntry(CPUser& U, unsigned UserOffset); @@ -260,7 +258,6 @@ namespace { bool DoDump = false); bool isWaterInRange(unsigned UserOffset, MachineBasicBlock *Water, CPUser &U, unsigned &Growth); - bool isBBInRange(MachineInstr *MI, MachineBasicBlock *BB, unsigned Disp); bool fixupImmediateBr(ImmBranch &Br); bool fixupConditionalBr(ImmBranch &Br); bool fixupUnconditionalBr(ImmBranch &Br); @@ -275,7 +272,6 @@ namespace { MachineBasicBlock *adjustJTTargetBlockForward(MachineBasicBlock *BB, MachineBasicBlock *JTBB); - unsigned getOffsetOf(MachineInstr *MI) const; unsigned getUserOffset(CPUser&) const; void dumpBBs(); void verify(); @@ -296,9 +292,10 @@ char ARMConstantIslands::ID = 0; /// verify - check BBOffsets, BBSizes, alignment of islands void ARMConstantIslands::verify() { #ifndef NDEBUG + BBInfoVector &BBInfo = BBUtils->getBBInfo(); assert(std::is_sorted(MF->begin(), MF->end(), - [this](const MachineBasicBlock &LHS, - const MachineBasicBlock &RHS) { + [&BBInfo](const MachineBasicBlock &LHS, + const MachineBasicBlock &RHS) { return BBInfo[LHS.getNumber()].postOffset() < BBInfo[RHS.getNumber()].postOffset(); })); @@ -324,6 +321,7 @@ void ARMConstantIslands::verify() { #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// print block size and offset information - debugging LLVM_DUMP_METHOD void ARMConstantIslands::dumpBBs() { + BBInfoVector &BBInfo = BBUtils->getBBInfo(); LLVM_DEBUG({ for (unsigned J = 0, E = BBInfo.size(); J !=E; ++J) { const BasicBlockInfo &BBI = BBInfo[J]; @@ -340,6 +338,7 @@ LLVM_DUMP_METHOD void ARMConstantIslands::dumpBBs() { bool ARMConstantIslands::runOnMachineFunction(MachineFunction &mf) { MF = &mf; MCP = mf.getConstantPool(); + BBUtils = std::unique_ptr<ARMBasicBlockUtils>(new ARMBasicBlockUtils(mf)); LLVM_DEBUG(dbgs() << "***** ARMConstantIslands: " << MCP->getConstants().size() << " CP entries, aligned to " @@ -467,7 +466,7 @@ bool ARMConstantIslands::runOnMachineFunction(MachineFunction &mf) { LLVM_DEBUG(dbgs() << '\n'; dumpBBs()); - BBInfo.clear(); + BBUtils->clear(); WaterList.clear(); CPUsers.clear(); CPEntries.clear(); @@ -684,14 +683,14 @@ void ARMConstantIslands::scanFunctionJumpTables() { void ARMConstantIslands:: initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs) { - BBInfo = computeAllBlockSizes(MF); - + BBUtils->computeAllBlockSizes(); + BBInfoVector &BBInfo = BBUtils->getBBInfo(); // The known bits of the entry block offset are determined by the function // alignment. BBInfo.front().KnownBits = MF->getAlignment(); // Compute block offsets and known bits. - adjustBBOffsetsAfter(&MF->front()); + BBUtils->adjustBBOffsetsAfter(&MF->front()); // Now go back through the instructions and build up our data structures. for (MachineBasicBlock &MBB : *MF) { @@ -856,25 +855,6 @@ initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs) { } } -/// getOffsetOf - Return the current offset of the specified machine instruction -/// from the start of the function. This offset changes as stuff is moved -/// around inside the function. -unsigned ARMConstantIslands::getOffsetOf(MachineInstr *MI) const { - MachineBasicBlock *MBB = MI->getParent(); - - // The offset is composed of two things: the sum of the sizes of all MBB's - // before this instruction's block, and the offset from the start of the block - // it is in. - unsigned Offset = BBInfo[MBB->getNumber()].Offset; - - // Sum instructions before MI in MBB. - for (MachineBasicBlock::iterator I = MBB->begin(); &*I != MI; ++I) { - assert(I != MBB->end() && "Didn't find MI in its own basic block?"); - Offset += TII->getInstSizeInBytes(*I); - } - return Offset; -} - /// CompareMBBNumbers - Little predicate function to sort the WaterList by MBB /// ID. static bool CompareMBBNumbers(const MachineBasicBlock *LHS, @@ -891,13 +871,11 @@ void ARMConstantIslands::updateForInsertedWaterBlock(MachineBasicBlock *NewBB) { // Insert an entry into BBInfo to align it properly with the (newly // renumbered) block numbers. - BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + BBUtils->insert(NewBB->getNumber(), BasicBlockInfo()); // Next, update WaterList. Specifically, we need to add NewMBB as having // available water after it. - water_iterator IP = - std::lower_bound(WaterList.begin(), WaterList.end(), NewBB, - CompareMBBNumbers); + water_iterator IP = llvm::lower_bound(WaterList, NewBB, CompareMBBNumbers); WaterList.insert(IP, NewBB); } @@ -942,15 +920,13 @@ MachineBasicBlock *ARMConstantIslands::splitBlockBeforeInstr(MachineInstr *MI) { // Insert an entry into BBInfo to align it properly with the (newly // renumbered) block numbers. - BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + BBUtils->insert(NewBB->getNumber(), BasicBlockInfo()); // Next, update WaterList. Specifically, we need to add OrigMBB as having // available water after it (but not if it's already there, which happens // when splitting before a conditional branch that is followed by an // unconditional branch - in that case we want to insert NewBB). - water_iterator IP = - std::lower_bound(WaterList.begin(), WaterList.end(), OrigBB, - CompareMBBNumbers); + water_iterator IP = llvm::lower_bound(WaterList, OrigBB, CompareMBBNumbers); MachineBasicBlock* WaterBB = *IP; if (WaterBB == OrigBB) WaterList.insert(std::next(IP), NewBB); @@ -963,14 +939,14 @@ MachineBasicBlock *ARMConstantIslands::splitBlockBeforeInstr(MachineInstr *MI) { // the new jump we added. (It should be possible to do this without // recounting everything, but it's very confusing, and this is rarely // executed.) - computeBlockSize(MF, OrigBB, BBInfo[OrigBB->getNumber()]); + BBUtils->computeBlockSize(OrigBB); // Figure out how large the NewMBB is. As the second half of the original // block, it may contain a tablejump. - computeBlockSize(MF, NewBB, BBInfo[NewBB->getNumber()]); + BBUtils->computeBlockSize(NewBB); // All BBOffsets following these blocks must be modified. - adjustBBOffsetsAfter(OrigBB); + BBUtils->adjustBBOffsetsAfter(OrigBB); return NewBB; } @@ -979,7 +955,9 @@ MachineBasicBlock *ARMConstantIslands::splitBlockBeforeInstr(MachineInstr *MI) { /// displacement computation. Update U.KnownAlignment to match its current /// basic block location. unsigned ARMConstantIslands::getUserOffset(CPUser &U) const { - unsigned UserOffset = getOffsetOf(U.MI); + unsigned UserOffset = BBUtils->getOffsetOf(U.MI); + + SmallVectorImpl<BasicBlockInfo> &BBInfo = BBUtils->getBBInfo(); const BasicBlockInfo &BBI = BBInfo[U.MI->getParent()->getNumber()]; unsigned KnownBits = BBI.internalKnownBits(); @@ -1028,6 +1006,7 @@ bool ARMConstantIslands::isOffsetInRange(unsigned UserOffset, bool ARMConstantIslands::isWaterInRange(unsigned UserOffset, MachineBasicBlock* Water, CPUser &U, unsigned &Growth) { + BBInfoVector &BBInfo = BBUtils->getBBInfo(); unsigned CPELogAlign = getCPELogAlign(U.CPEMI); unsigned CPEOffset = BBInfo[Water->getNumber()].postOffset(CPELogAlign); unsigned NextBlockOffset, NextBlockAlignment; @@ -1068,10 +1047,11 @@ bool ARMConstantIslands::isWaterInRange(unsigned UserOffset, bool ARMConstantIslands::isCPEntryInRange(MachineInstr *MI, unsigned UserOffset, MachineInstr *CPEMI, unsigned MaxDisp, bool NegOk, bool DoDump) { - unsigned CPEOffset = getOffsetOf(CPEMI); + unsigned CPEOffset = BBUtils->getOffsetOf(CPEMI); if (DoDump) { LLVM_DEBUG({ + BBInfoVector &BBInfo = BBUtils->getBBInfo(); unsigned Block = MI->getParent()->getNumber(); const BasicBlockInfo &BBI = BBInfo[Block]; dbgs() << "User of CPE#" << CPEMI->getOperand(0).getImm() @@ -1104,28 +1084,6 @@ static bool BBIsJumpedOver(MachineBasicBlock *MBB) { } #endif // NDEBUG -void ARMConstantIslands::adjustBBOffsetsAfter(MachineBasicBlock *BB) { - unsigned BBNum = BB->getNumber(); - for(unsigned i = BBNum + 1, e = MF->getNumBlockIDs(); i < e; ++i) { - // Get the offset and known bits at the end of the layout predecessor. - // Include the alignment of the current block. - unsigned LogAlign = MF->getBlockNumbered(i)->getAlignment(); - unsigned Offset = BBInfo[i - 1].postOffset(LogAlign); - unsigned KnownBits = BBInfo[i - 1].postKnownBits(LogAlign); - - // This is where block i begins. Stop if the offset is already correct, - // and we have updated 2 blocks. This is the maximum number of blocks - // changed before calling this function. - if (i > BBNum + 2 && - BBInfo[i].Offset == Offset && - BBInfo[i].KnownBits == KnownBits) - break; - - BBInfo[i].Offset = Offset; - BBInfo[i].KnownBits = KnownBits; - } -} - /// decrementCPEReferenceCount - find the constant pool entry with index CPI /// and instruction CPEMI, and decrement its refcount. If the refcount /// becomes 0 remove the entry and instruction. Returns true if we removed @@ -1241,6 +1199,7 @@ bool ARMConstantIslands::findAvailableWater(CPUser &U, unsigned UserOffset, // When a CP access is out of range, BB0 may be used as water. However, // inserting islands between BB0 and BB1 makes other accesses out of range. MachineBasicBlock *UserBB = U.MI->getParent(); + BBInfoVector &BBInfo = BBUtils->getBBInfo(); unsigned MinNoSplitDisp = BBInfo[UserBB->getNumber()].postOffset(getCPELogAlign(U.CPEMI)); if (CloserWater && MinNoSplitDisp > U.getMaxDisp() / 2) @@ -1297,6 +1256,7 @@ void ARMConstantIslands::createNewWater(unsigned CPUserIndex, MachineInstr *CPEMI = U.CPEMI; unsigned CPELogAlign = getCPELogAlign(CPEMI); MachineBasicBlock *UserMBB = UserMI->getParent(); + BBInfoVector &BBInfo = BBUtils->getBBInfo(); const BasicBlockInfo &UserBBI = BBInfo[UserMBB->getNumber()]; // If the block does not end in an unconditional branch already, and if the @@ -1328,8 +1288,8 @@ void ARMConstantIslands::createNewWater(unsigned CPUserIndex, unsigned MaxDisp = getUnconditionalBrDisp(UncondBr); ImmBranches.push_back(ImmBranch(&UserMBB->back(), MaxDisp, false, UncondBr)); - computeBlockSize(MF, UserMBB, BBInfo[UserMBB->getNumber()]); - adjustBBOffsetsAfter(UserMBB); + BBUtils->computeBlockSize(UserMBB); + BBUtils->adjustBBOffsetsAfter(UserMBB); return; } } @@ -1538,8 +1498,8 @@ bool ARMConstantIslands::handleConstantPoolUser(unsigned CPUserIndex, NewIsland->setAlignment(getCPELogAlign(U.CPEMI)); // Increase the size of the island block to account for the new entry. - BBInfo[NewIsland->getNumber()].Size += Size; - adjustBBOffsetsAfter(&*--NewIsland->getIterator()); + BBUtils->adjustBBSize(NewIsland, Size); + BBUtils->adjustBBOffsetsAfter(&*--NewIsland->getIterator()); // Finally, change the CPI in the instruction operand to be ID. for (unsigned i = 0, e = UserMI->getNumOperands(); i != e; ++i) @@ -1550,7 +1510,8 @@ bool ARMConstantIslands::handleConstantPoolUser(unsigned CPUserIndex, LLVM_DEBUG( dbgs() << " Moved CPE to #" << ID << " CPI=" << CPI - << format(" offset=%#x\n", BBInfo[NewIsland->getNumber()].Offset)); + << format(" offset=%#x\n", + BBUtils->getBBInfo()[NewIsland->getNumber()].Offset)); return true; } @@ -1561,7 +1522,8 @@ void ARMConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) { MachineBasicBlock *CPEBB = CPEMI->getParent(); unsigned Size = CPEMI->getOperand(2).getImm(); CPEMI->eraseFromParent(); - BBInfo[CPEBB->getNumber()].Size -= Size; + BBInfoVector &BBInfo = BBUtils->getBBInfo(); + BBUtils->adjustBBSize(CPEBB, -Size); // All succeeding offsets have the current size value added in, fix this. if (CPEBB->empty()) { BBInfo[CPEBB->getNumber()].Size = 0; @@ -1572,7 +1534,7 @@ void ARMConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) { // Entries are sorted by descending alignment, so realign from the front. CPEBB->setAlignment(getCPELogAlign(&*CPEBB->begin())); - adjustBBOffsetsAfter(CPEBB); + BBUtils->adjustBBOffsetsAfter(CPEBB); // An island has only one predecessor BB and one successor BB. Check if // this BB's predecessor jumps directly to this BB's successor. This // shouldn't happen currently. @@ -1597,30 +1559,6 @@ bool ARMConstantIslands::removeUnusedCPEntries() { return MadeChange; } -/// isBBInRange - Returns true if the distance between specific MI and -/// specific BB can fit in MI's displacement field. -bool ARMConstantIslands::isBBInRange(MachineInstr *MI,MachineBasicBlock *DestBB, - unsigned MaxDisp) { - unsigned PCAdj = isThumb ? 4 : 8; - unsigned BrOffset = getOffsetOf(MI) + PCAdj; - unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset; - - LLVM_DEBUG(dbgs() << "Branch of destination " << printMBBReference(*DestBB) - << " from " << printMBBReference(*MI->getParent()) - << " max delta=" << MaxDisp << " from " << getOffsetOf(MI) - << " to " << DestOffset << " offset " - << int(DestOffset - BrOffset) << "\t" << *MI); - - if (BrOffset <= DestOffset) { - // Branch before the Dest. - if (DestOffset-BrOffset <= MaxDisp) - return true; - } else { - if (BrOffset-DestOffset <= MaxDisp) - return true; - } - return false; -} /// fixupImmediateBr - Fix up an immediate branch whose destination is too far /// away to fit in its displacement field. @@ -1629,7 +1567,7 @@ bool ARMConstantIslands::fixupImmediateBr(ImmBranch &Br) { MachineBasicBlock *DestBB = MI->getOperand(0).getMBB(); // Check to see if the DestBB is already in-range. - if (isBBInRange(MI, DestBB, Br.MaxDisp)) + if (BBUtils->isBBInRange(MI, DestBB, Br.MaxDisp)) return false; if (!Br.isCond) @@ -1648,11 +1586,15 @@ ARMConstantIslands::fixupUnconditionalBr(ImmBranch &Br) { if (!isThumb1) llvm_unreachable("fixupUnconditionalBr is Thumb1 only!"); + if (!AFI->isLRSpilled()) + report_fatal_error("underestimated function size"); + // Use BL to implement far jump. Br.MaxDisp = (1 << 21) * 2; MI->setDesc(TII->get(ARM::tBfar)); + BBInfoVector &BBInfo = BBUtils->getBBInfo(); BBInfo[MBB->getNumber()].Size += 2; - adjustBBOffsetsAfter(MBB); + BBUtils->adjustBBOffsetsAfter(MBB); HasFarJump = true; ++NumUBrFixed; @@ -1699,7 +1641,7 @@ ARMConstantIslands::fixupConditionalBr(ImmBranch &Br) { // bne L2 // b L1 MachineBasicBlock *NewDest = BMI->getOperand(0).getMBB(); - if (isBBInRange(MI, NewDest, Br.MaxDisp)) { + if (BBUtils->isBBInRange(MI, NewDest, Br.MaxDisp)) { LLVM_DEBUG( dbgs() << " Invert Bcc condition and swap its destination with " << *BMI); @@ -1716,7 +1658,7 @@ ARMConstantIslands::fixupConditionalBr(ImmBranch &Br) { // No need for the branch to the next block. We're adding an unconditional // branch to the destination. int delta = TII->getInstSizeInBytes(MBB->back()); - BBInfo[MBB->getNumber()].Size -= delta; + BBUtils->adjustBBSize(MBB, -delta); MBB->back().eraseFromParent(); // The conditional successor will be swapped between the BBs after this, so @@ -1737,21 +1679,21 @@ ARMConstantIslands::fixupConditionalBr(ImmBranch &Br) { BuildMI(MBB, DebugLoc(), TII->get(MI->getOpcode())) .addMBB(NextBB).addImm(CC).addReg(CCReg); Br.MI = &MBB->back(); - BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + BBUtils->adjustBBSize(MBB, TII->getInstSizeInBytes(MBB->back())); if (isThumb) BuildMI(MBB, DebugLoc(), TII->get(Br.UncondBr)) .addMBB(DestBB) .add(predOps(ARMCC::AL)); else BuildMI(MBB, DebugLoc(), TII->get(Br.UncondBr)).addMBB(DestBB); - BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + BBUtils->adjustBBSize(MBB, TII->getInstSizeInBytes(MBB->back())); unsigned MaxDisp = getUnconditionalBrDisp(Br.UncondBr); ImmBranches.push_back(ImmBranch(&MBB->back(), MaxDisp, false, Br.UncondBr)); // Remove the old conditional branch. It may or may not still be in MBB. - BBInfo[MI->getParent()->getNumber()].Size -= TII->getInstSizeInBytes(*MI); + BBUtils->adjustBBSize(MI->getParent(), -TII->getInstSizeInBytes(*MI)); MI->eraseFromParent(); - adjustBBOffsetsAfter(MBB); + BBUtils->adjustBBOffsetsAfter(MBB); return true; } @@ -1826,8 +1768,8 @@ bool ARMConstantIslands::optimizeThumb2Instructions() { LLVM_DEBUG(dbgs() << "Shrink: " << *U.MI); U.MI->setDesc(TII->get(NewOpc)); MachineBasicBlock *MBB = U.MI->getParent(); - BBInfo[MBB->getNumber()].Size -= 2; - adjustBBOffsetsAfter(MBB); + BBUtils->adjustBBSize(MBB, -2); + BBUtils->adjustBBOffsetsAfter(MBB); ++NumT2CPShrunk; MadeChange = true; } @@ -1866,12 +1808,12 @@ bool ARMConstantIslands::optimizeThumb2Branches() { if (NewOpc) { unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale; MachineBasicBlock *DestBB = Br.MI->getOperand(0).getMBB(); - if (isBBInRange(Br.MI, DestBB, MaxOffs)) { + if (BBUtils->isBBInRange(Br.MI, DestBB, MaxOffs)) { LLVM_DEBUG(dbgs() << "Shrink branch: " << *Br.MI); Br.MI->setDesc(TII->get(NewOpc)); MachineBasicBlock *MBB = Br.MI->getParent(); - BBInfo[MBB->getNumber()].Size -= 2; - adjustBBOffsetsAfter(MBB); + BBUtils->adjustBBSize(MBB, -2); + BBUtils->adjustBBOffsetsAfter(MBB); ++NumT2BrShrunk; MadeChange = true; } @@ -1898,34 +1840,47 @@ bool ARMConstantIslands::optimizeThumb2Branches() { MachineBasicBlock *DestBB = Br.MI->getOperand(0).getMBB(); // Check if the distance is within 126. Subtract starting offset by 2 // because the cmp will be eliminated. - unsigned BrOffset = getOffsetOf(Br.MI) + 4 - 2; + unsigned BrOffset = BBUtils->getOffsetOf(Br.MI) + 4 - 2; + BBInfoVector &BBInfo = BBUtils->getBBInfo(); unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset; - if (BrOffset < DestOffset && (DestOffset - BrOffset) <= 126) { - MachineBasicBlock::iterator CmpMI = Br.MI; - if (CmpMI != Br.MI->getParent()->begin()) { - --CmpMI; - if (CmpMI->getOpcode() == ARM::tCMPi8) { - unsigned Reg = CmpMI->getOperand(0).getReg(); - Pred = getInstrPredicate(*CmpMI, PredReg); - if (Pred == ARMCC::AL && - CmpMI->getOperand(1).getImm() == 0 && - isARMLowRegister(Reg)) { - MachineBasicBlock *MBB = Br.MI->getParent(); - LLVM_DEBUG(dbgs() << "Fold: " << *CmpMI << " and: " << *Br.MI); - MachineInstr *NewBR = - BuildMI(*MBB, CmpMI, Br.MI->getDebugLoc(), TII->get(NewOpc)) - .addReg(Reg).addMBB(DestBB,Br.MI->getOperand(0).getTargetFlags()); - CmpMI->eraseFromParent(); - Br.MI->eraseFromParent(); - Br.MI = NewBR; - BBInfo[MBB->getNumber()].Size -= 2; - adjustBBOffsetsAfter(MBB); - ++NumCBZ; - MadeChange = true; - } - } + if (BrOffset >= DestOffset || (DestOffset - BrOffset) > 126) + continue; + + // Search backwards to find a tCMPi8 + auto *TRI = STI->getRegisterInfo(); + MachineInstr *CmpMI = findCMPToFoldIntoCBZ(Br.MI, TRI); + if (!CmpMI || CmpMI->getOpcode() != ARM::tCMPi8) + continue; + + unsigned Reg = CmpMI->getOperand(0).getReg(); + + // Check for Kill flags on Reg. If they are present remove them and set kill + // on the new CBZ. + MachineBasicBlock::iterator KillMI = Br.MI; + bool RegKilled = false; + do { + --KillMI; + if (KillMI->killsRegister(Reg, TRI)) { + KillMI->clearRegisterKills(Reg, TRI); + RegKilled = true; + break; } - } + } while (KillMI != CmpMI); + + // Create the new CBZ/CBNZ + MachineBasicBlock *MBB = Br.MI->getParent(); + LLVM_DEBUG(dbgs() << "Fold: " << *CmpMI << " and: " << *Br.MI); + MachineInstr *NewBR = + BuildMI(*MBB, Br.MI, Br.MI->getDebugLoc(), TII->get(NewOpc)) + .addReg(Reg, getKillRegState(RegKilled)) + .addMBB(DestBB, Br.MI->getOperand(0).getTargetFlags()); + CmpMI->eraseFromParent(); + Br.MI->eraseFromParent(); + Br.MI = NewBR; + BBInfo[MBB->getNumber()].Size -= 2; + BBUtils->adjustBBOffsetsAfter(MBB); + ++NumCBZ; + MadeChange = true; } return MadeChange; @@ -2085,16 +2040,6 @@ static void RemoveDeadAddBetweenLEAAndJT(MachineInstr *LEAMI, DeadSize += 4; } -static bool registerDefinedBetween(unsigned Reg, - MachineBasicBlock::iterator From, - MachineBasicBlock::iterator To, - const TargetRegisterInfo *TRI) { - for (auto I = From; I != To; ++I) - if (I->modifiesRegister(Reg, TRI)) - return true; - return false; -} - /// optimizeThumb2JumpTables - Use tbb / tbh instructions to generate smaller /// jumptables when it's possible. bool ARMConstantIslands::optimizeThumb2JumpTables() { @@ -2117,8 +2062,9 @@ bool ARMConstantIslands::optimizeThumb2JumpTables() { bool ByteOk = true; bool HalfWordOk = true; - unsigned JTOffset = getOffsetOf(MI) + 4; + unsigned JTOffset = BBUtils->getOffsetOf(MI) + 4; const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs; + BBInfoVector &BBInfo = BBUtils->getBBInfo(); for (unsigned j = 0, ee = JTBBs.size(); j != ee; ++j) { MachineBasicBlock *MBB = JTBBs[j]; unsigned DstOffset = BBInfo[MBB->getNumber()].Offset; @@ -2281,7 +2227,7 @@ bool ARMConstantIslands::optimizeThumb2JumpTables() { int Delta = OrigSize - NewSize + DeadSize; BBInfo[MBB->getNumber()].Size -= Delta; - adjustBBOffsetsAfter(MBB); + BBUtils->adjustBBOffsetsAfter(MBB); ++NumTBs; MadeChange = true; diff --git a/lib/Target/ARM/ARMConstantPoolValue.cpp b/lib/Target/ARM/ARMConstantPoolValue.cpp index 236c4fab2a5c..3bdb0e1ef62d 100644 --- a/lib/Target/ARM/ARMConstantPoolValue.cpp +++ b/lib/Target/ARM/ARMConstantPoolValue.cpp @@ -1,9 +1,8 @@ //===- ARMConstantPoolValue.cpp - ARM constantpool value ------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMConstantPoolValue.h b/lib/Target/ARM/ARMConstantPoolValue.h index 55194ed94532..660b7fc88d82 100644 --- a/lib/Target/ARM/ARMConstantPoolValue.h +++ b/lib/Target/ARM/ARMConstantPoolValue.h @@ -1,9 +1,8 @@ //===- ARMConstantPoolValue.h - ARM constantpool value ----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMExpandPseudoInsts.cpp b/lib/Target/ARM/ARMExpandPseudoInsts.cpp index eecd0a10dc7d..b32ba3eeea18 100644 --- a/lib/Target/ARM/ARMExpandPseudoInsts.cpp +++ b/lib/Target/ARM/ARMExpandPseudoInsts.cpp @@ -1,9 +1,8 @@ //===-- ARMExpandPseudoInsts.cpp - Expand pseudo instructions -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -24,6 +23,7 @@ #include "llvm/CodeGen/LivePhysRegs.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Support/Debug.h" using namespace llvm; @@ -423,8 +423,7 @@ static const NEONLdStTableEntry *LookupNEONLdSt(unsigned Opcode) { } #endif - auto I = std::lower_bound(std::begin(NEONLdStTable), - std::end(NEONLdStTable), Opcode); + auto I = llvm::lower_bound(NEONLdStTable, Opcode); if (I != std::end(NEONLdStTable) && I->PseudoOpc == Opcode) return I; return nullptr; @@ -470,6 +469,7 @@ static void GetDSubRegs(unsigned Reg, NEONRegSpacing RegSpc, void ARMExpandPseudo::ExpandVLD(MachineBasicBlock::iterator &MBBI) { MachineInstr &MI = *MBBI; MachineBasicBlock &MBB = *MI.getParent(); + LLVM_DEBUG(dbgs() << "Expanding: "; MI.dump()); const NEONLdStTableEntry *TableEntry = LookupNEONLdSt(MI.getOpcode()); assert(TableEntry && TableEntry->IsLoad && "NEONLdStTable lookup failed"); @@ -571,8 +571,8 @@ void ARMExpandPseudo::ExpandVLD(MachineBasicBlock::iterator &MBBI) { // Transfer memoperands. MIB.cloneMemRefs(MI); - MI.eraseFromParent(); + LLVM_DEBUG(dbgs() << "To: "; MIB.getInstr()->dump();); } /// ExpandVST - Translate VST pseudo instructions with Q, QQ or QQQQ register @@ -580,6 +580,7 @@ void ARMExpandPseudo::ExpandVLD(MachineBasicBlock::iterator &MBBI) { void ARMExpandPseudo::ExpandVST(MachineBasicBlock::iterator &MBBI) { MachineInstr &MI = *MBBI; MachineBasicBlock &MBB = *MI.getParent(); + LLVM_DEBUG(dbgs() << "Expanding: "; MI.dump()); const NEONLdStTableEntry *TableEntry = LookupNEONLdSt(MI.getOpcode()); assert(TableEntry && !TableEntry->IsLoad && "NEONLdStTable lookup failed"); @@ -646,8 +647,8 @@ void ARMExpandPseudo::ExpandVST(MachineBasicBlock::iterator &MBBI) { // Transfer memoperands. MIB.cloneMemRefs(MI); - MI.eraseFromParent(); + LLVM_DEBUG(dbgs() << "To: "; MIB.getInstr()->dump();); } /// ExpandLaneOp - Translate VLD*LN and VST*LN instructions with Q, QQ or QQQQ @@ -655,6 +656,7 @@ void ARMExpandPseudo::ExpandVST(MachineBasicBlock::iterator &MBBI) { void ARMExpandPseudo::ExpandLaneOp(MachineBasicBlock::iterator &MBBI) { MachineInstr &MI = *MBBI; MachineBasicBlock &MBB = *MI.getParent(); + LLVM_DEBUG(dbgs() << "Expanding: "; MI.dump()); const NEONLdStTableEntry *TableEntry = LookupNEONLdSt(MI.getOpcode()); assert(TableEntry && "NEONLdStTable lookup failed"); @@ -745,6 +747,7 @@ void ARMExpandPseudo::ExpandVTBL(MachineBasicBlock::iterator &MBBI, unsigned Opc, bool IsExt) { MachineInstr &MI = *MBBI; MachineBasicBlock &MBB = *MI.getParent(); + LLVM_DEBUG(dbgs() << "Expanding: "; MI.dump()); MachineInstrBuilder MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(Opc)); unsigned OpIdx = 0; @@ -774,6 +777,7 @@ void ARMExpandPseudo::ExpandVTBL(MachineBasicBlock::iterator &MBBI, MIB.addReg(SrcReg, RegState::Implicit | getKillRegState(SrcIsKill)); TransferImpOps(MI, MIB, MIB); MI.eraseFromParent(); + LLVM_DEBUG(dbgs() << "To: "; MIB.getInstr()->dump();); } static bool IsAnAddressOperand(const MachineOperand &MO) { @@ -830,6 +834,7 @@ void ARMExpandPseudo::ExpandMOV32BitImm(MachineBasicBlock &MBB, const MachineOperand &MO = MI.getOperand(isCC ? 2 : 1); bool RequiresBundling = STI->isTargetWindows() && IsAnAddressOperand(MO); MachineInstrBuilder LO16, HI16; + LLVM_DEBUG(dbgs() << "Expanding: "; MI.dump()); if (!STI->hasV6T2Ops() && (Opcode == ARM::MOVi32imm || Opcode == ARM::MOVCCi32imm)) { @@ -911,6 +916,8 @@ void ARMExpandPseudo::ExpandMOV32BitImm(MachineBasicBlock &MBB, LO16.add(makeImplicit(MI.getOperand(1))); TransferImpOps(MI, LO16, HI16); MI.eraseFromParent(); + LLVM_DEBUG(dbgs() << "To: "; LO16.getInstr()->dump();); + LLVM_DEBUG(dbgs() << "And: "; HI16.getInstr()->dump();); } /// Expand a CMP_SWAP pseudo-inst to an ldrex/strex loop as simply as @@ -1930,11 +1937,16 @@ bool ARMExpandPseudo::runOnMachineFunction(MachineFunction &MF) { TRI = STI->getRegisterInfo(); AFI = MF.getInfo<ARMFunctionInfo>(); + LLVM_DEBUG(dbgs() << "********** ARM EXPAND PSEUDO INSTRUCTIONS **********\n" + << "********** Function: " << MF.getName() << '\n'); + bool Modified = false; for (MachineBasicBlock &MBB : MF) Modified |= ExpandMBB(MBB); if (VerifyARMPseudo) MF.verify(this, "After expanding ARM pseudo instructions."); + + LLVM_DEBUG(dbgs() << "***************************************************\n"); return Modified; } diff --git a/lib/Target/ARM/ARMFastISel.cpp b/lib/Target/ARM/ARMFastISel.cpp index a50abfdbee44..6e274d269bf2 100644 --- a/lib/Target/ARM/ARMFastISel.cpp +++ b/lib/Target/ARM/ARMFastISel.cpp @@ -1,9 +1,8 @@ //===- ARMFastISel.cpp - ARM FastISel implementation ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -245,8 +244,6 @@ class ARMFastISel final : public FastISel { } // end anonymous namespace -#include "ARMGenCallingConv.inc" - // DefinesOptionalPredicate - This is different from DefinesPredicate in that // we don't care about implicit defs here, just places we'll need to add a // default CCReg argument. Sets CPSR if we're setting CPSR instead of CCR. @@ -444,7 +441,7 @@ unsigned ARMFastISel::ARMMaterializeFP(const ConstantFP *CFP, MVT VT) { } // Require VFP2 for loading fp constants. - if (!Subtarget->hasVFP2()) return false; + if (!Subtarget->hasVFP2Base()) return false; // MachineConstantPool wants an explicit alignment. unsigned Align = DL.getPrefTypeAlignment(CFP->getType()); @@ -500,7 +497,7 @@ unsigned ARMFastISel::ARMMaterializeInt(const Constant *C, MVT VT) { } unsigned ResultReg = 0; - if (Subtarget->useMovt(*FuncInfo.MF)) + if (Subtarget->useMovt()) ResultReg = fastEmit_i(VT, VT, ISD::Constant, CI->getZExtValue()); if (ResultReg) @@ -558,7 +555,7 @@ unsigned ARMFastISel::ARMMaterializeGV(const GlobalValue *GV, MVT VT) { bool IsPositionIndependent = isPositionIndependent(); // Use movw+movt when possible, it avoids constant pool entries. // Non-darwin targets only support static movt relocations in FastISel. - if (Subtarget->useMovt(*FuncInfo.MF) && + if (Subtarget->useMovt() && (Subtarget->isTargetMachO() || !IsPositionIndependent)) { unsigned Opc; unsigned char TF = 0; @@ -972,7 +969,7 @@ bool ARMFastISel::ARMEmitLoad(MVT VT, unsigned &ResultReg, Address &Addr, RC = isThumb2 ? &ARM::rGPRRegClass : &ARM::GPRnopcRegClass; break; case MVT::f32: - if (!Subtarget->hasVFP2()) return false; + if (!Subtarget->hasVFP2Base()) return false; // Unaligned loads need special handling. Floats require word-alignment. if (Alignment && Alignment < 4) { needVMOV = true; @@ -985,7 +982,8 @@ bool ARMFastISel::ARMEmitLoad(MVT VT, unsigned &ResultReg, Address &Addr, } break; case MVT::f64: - if (!Subtarget->hasVFP2()) return false; + // Can load and store double precision even without FeatureFP64 + if (!Subtarget->hasVFP2Base()) return false; // FIXME: Unaligned loads need special handling. Doublewords require // word-alignment. if (Alignment && Alignment < 4) @@ -1110,7 +1108,7 @@ bool ARMFastISel::ARMEmitStore(MVT VT, unsigned SrcReg, Address &Addr, } break; case MVT::f32: - if (!Subtarget->hasVFP2()) return false; + if (!Subtarget->hasVFP2Base()) return false; // Unaligned stores need special handling. Floats require word-alignment. if (Alignment && Alignment < 4) { unsigned MoveReg = createResultReg(TLI.getRegClassFor(MVT::i32)); @@ -1125,7 +1123,8 @@ bool ARMFastISel::ARMEmitStore(MVT VT, unsigned SrcReg, Address &Addr, } break; case MVT::f64: - if (!Subtarget->hasVFP2()) return false; + // Can load and store double precision even without FeatureFP64 + if (!Subtarget->hasVFP2Base()) return false; // FIXME: Unaligned stores need special handling. Doublewords require // word-alignment. if (Alignment && Alignment < 4) @@ -1356,10 +1355,10 @@ bool ARMFastISel::ARMEmitCmp(const Value *Src1Value, const Value *Src2Value, if (!SrcEVT.isSimple()) return false; MVT SrcVT = SrcEVT.getSimpleVT(); - if (Ty->isFloatTy() && !Subtarget->hasVFP2()) + if (Ty->isFloatTy() && !Subtarget->hasVFP2Base()) return false; - if (Ty->isDoubleTy() && (!Subtarget->hasVFP2() || Subtarget->isFPOnlySP())) + if (Ty->isDoubleTy() && (!Subtarget->hasVFP2Base() || !Subtarget->hasFP64())) return false; // Check to see if the 2nd operand is a constant that we can encode directly @@ -1509,7 +1508,7 @@ bool ARMFastISel::SelectCmp(const Instruction *I) { bool ARMFastISel::SelectFPExt(const Instruction *I) { // Make sure we have VFP and that we're extending float to double. - if (!Subtarget->hasVFP2() || Subtarget->isFPOnlySP()) return false; + if (!Subtarget->hasVFP2Base() || !Subtarget->hasFP64()) return false; Value *V = I->getOperand(0); if (!I->getType()->isDoubleTy() || @@ -1528,7 +1527,7 @@ bool ARMFastISel::SelectFPExt(const Instruction *I) { bool ARMFastISel::SelectFPTrunc(const Instruction *I) { // Make sure we have VFP and that we're truncating double to float. - if (!Subtarget->hasVFP2() || Subtarget->isFPOnlySP()) return false; + if (!Subtarget->hasVFP2Base() || !Subtarget->hasFP64()) return false; Value *V = I->getOperand(0); if (!(I->getType()->isFloatTy() && @@ -1547,7 +1546,7 @@ bool ARMFastISel::SelectFPTrunc(const Instruction *I) { bool ARMFastISel::SelectIToFP(const Instruction *I, bool isSigned) { // Make sure we have VFP. - if (!Subtarget->hasVFP2()) return false; + if (!Subtarget->hasVFP2Base()) return false; MVT DstVT; Type *Ty = I->getType(); @@ -1579,7 +1578,7 @@ bool ARMFastISel::SelectIToFP(const Instruction *I, bool isSigned) { unsigned Opc; if (Ty->isFloatTy()) Opc = isSigned ? ARM::VSITOS : ARM::VUITOS; - else if (Ty->isDoubleTy() && !Subtarget->isFPOnlySP()) + else if (Ty->isDoubleTy() && Subtarget->hasFP64()) Opc = isSigned ? ARM::VSITOD : ARM::VUITOD; else return false; @@ -1592,7 +1591,7 @@ bool ARMFastISel::SelectIToFP(const Instruction *I, bool isSigned) { bool ARMFastISel::SelectFPToI(const Instruction *I, bool isSigned) { // Make sure we have VFP. - if (!Subtarget->hasVFP2()) return false; + if (!Subtarget->hasVFP2Base()) return false; MVT DstVT; Type *RetTy = I->getType(); @@ -1605,7 +1604,7 @@ bool ARMFastISel::SelectFPToI(const Instruction *I, bool isSigned) { unsigned Opc; Type *OpTy = I->getOperand(0)->getType(); if (OpTy->isFloatTy()) Opc = isSigned ? ARM::VTOSIZS : ARM::VTOUIZS; - else if (OpTy->isDoubleTy() && !Subtarget->isFPOnlySP()) + else if (OpTy->isDoubleTy() && Subtarget->hasFP64()) Opc = isSigned ? ARM::VTOSIZD : ARM::VTOUIZD; else return false; @@ -1811,9 +1810,9 @@ bool ARMFastISel::SelectBinaryFPOp(const Instruction *I, unsigned ISDOpcode) { // if we have them. // FIXME: It'd be nice to use NEON instructions. Type *Ty = I->getType(); - if (Ty->isFloatTy() && !Subtarget->hasVFP2()) + if (Ty->isFloatTy() && !Subtarget->hasVFP2Base()) return false; - if (Ty->isDoubleTy() && (!Subtarget->hasVFP2() || Subtarget->isFPOnlySP())) + if (Ty->isDoubleTy() && (!Subtarget->hasVFP2Base() || !Subtarget->hasFP64())) return false; unsigned Opc; @@ -1855,7 +1854,7 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, default: report_fatal_error("Unsupported calling convention"); case CallingConv::Fast: - if (Subtarget->hasVFP2() && !isVarArg) { + if (Subtarget->hasVFP2Base() && !isVarArg) { if (!Subtarget->isAAPCS_ABI()) return (Return ? RetFastCC_ARM_APCS : FastCC_ARM_APCS); // For AAPCS ABI targets, just use VFP variant of the calling convention. @@ -1866,7 +1865,7 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, case CallingConv::CXX_FAST_TLS: // Use target triple & subtarget features to do actual dispatch. if (Subtarget->isAAPCS_ABI()) { - if (Subtarget->hasVFP2() && + if (Subtarget->hasVFP2Base() && TM.Options.FloatABIType == FloatABI::Hard && !isVarArg) return (Return ? RetCC_ARM_AAPCS_VFP: CC_ARM_AAPCS_VFP); else @@ -1935,11 +1934,11 @@ bool ARMFastISel::ProcessCallArgs(SmallVectorImpl<Value*> &Args, case MVT::i32: break; case MVT::f32: - if (!Subtarget->hasVFP2()) + if (!Subtarget->hasVFP2Base()) return false; break; case MVT::f64: - if (!Subtarget->hasVFP2()) + if (!Subtarget->hasVFP2Base()) return false; break; } diff --git a/lib/Target/ARM/ARMFeatures.h b/lib/Target/ARM/ARMFeatures.h index 8c0df4c2cbf9..5cd7006c22fc 100644 --- a/lib/Target/ARM/ARMFeatures.h +++ b/lib/Target/ARM/ARMFeatures.h @@ -1,9 +1,8 @@ //===-- ARMFeatures.h - Checks for ARM instruction features -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMFrameLowering.cpp b/lib/Target/ARM/ARMFrameLowering.cpp index a9d87ced31f3..bedb779bcba0 100644 --- a/lib/Target/ARM/ARMFrameLowering.cpp +++ b/lib/Target/ARM/ARMFrameLowering.cpp @@ -1,9 +1,8 @@ //===- ARMFrameLowering.cpp - ARM Frame Information -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -30,6 +29,7 @@ #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -344,6 +344,10 @@ static void emitAligningInstructions(MachineFunction &MF, ARMFunctionInfo *AFI, /// as assignCalleeSavedSpillSlots() hasn't run at this point. Instead we use /// this to produce a conservative estimate that we check in an assert() later. static int getMaxFPOffset(const Function &F, const ARMFunctionInfo &AFI) { + // For Thumb1, push.w isn't available, so the first push will always push + // r7 and lr onto the stack first. + if (AFI.isThumb1OnlyFunction()) + return -AFI.getArgRegsSaveSize() - (2 * 4); // This is a conservative estimation: Assume the frame pointer being r7 and // pc("r15") up to r8 getting spilled before (= 8 registers). return -AFI.getArgRegsSaveSize() - (8 * 4); @@ -954,8 +958,12 @@ ARMFrameLowering::ResolveFrameIndexReference(const MachineFunction &MF, } } // Use the base pointer if we have one. - if (RegInfo->hasBasePointer(MF)) + // FIXME: Maybe prefer sp on Thumb1 if it's legal and the offset is cheaper? + // That can happen if we forced a base pointer for a large call frame. + if (RegInfo->hasBasePointer(MF)) { FrameReg = RegInfo->getBaseRegister(); + Offset -= SPAdj; + } return Offset; } @@ -1476,13 +1484,17 @@ bool ARMFrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB, } // FIXME: Make generic? -static unsigned GetFunctionSizeInBytes(const MachineFunction &MF, - const ARMBaseInstrInfo &TII) { +static unsigned EstimateFunctionSizeInBytes(const MachineFunction &MF, + const ARMBaseInstrInfo &TII) { unsigned FnSize = 0; for (auto &MBB : MF) { for (auto &MI : MBB) FnSize += TII.getInstSizeInBytes(MI); } + if (MF.getJumpTableInfo()) + for (auto &Table: MF.getJumpTableInfo()->getJumpTables()) + FnSize += Table.MBBs.size() * 4; + FnSize += MF.getConstantPool()->getConstants().size() * 4; return FnSize; } @@ -1726,7 +1738,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, bool ForceLRSpill = false; if (!LRSpilled && AFI->isThumb1OnlyFunction()) { - unsigned FnSize = GetFunctionSizeInBytes(MF, TII); + unsigned FnSize = EstimateFunctionSizeInBytes(MF, TII); // Force LR to be spilled if the Thumb function size is > 2048. This enables // use of BL to implement far jump. If it turns out that it's not needed // then the branch fix up path will undo it. @@ -1771,13 +1783,59 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, } EstimatedStackSize += 16; // For possible paddings. - unsigned EstimatedRSStackSizeLimit = estimateRSStackSizeLimit(MF, this); + unsigned EstimatedRSStackSizeLimit, EstimatedRSFixedSizeLimit; + if (AFI->isThumb1OnlyFunction()) { + // For Thumb1, don't bother to iterate over the function. The only + // instruction that requires an emergency spill slot is a store to a + // frame index. + // + // tSTRspi, which is used for sp-relative accesses, has an 8-bit unsigned + // immediate. tSTRi, which is used for bp- and fp-relative accesses, has + // a 5-bit unsigned immediate. + // + // We could try to check if the function actually contains a tSTRspi + // that might need the spill slot, but it's not really important. + // Functions with VLAs or extremely large call frames are rare, and + // if a function is allocating more than 1KB of stack, an extra 4-byte + // slot probably isn't relevant. + if (RegInfo->hasBasePointer(MF)) + EstimatedRSStackSizeLimit = (1U << 5) * 4; + else + EstimatedRSStackSizeLimit = (1U << 8) * 4; + EstimatedRSFixedSizeLimit = (1U << 5) * 4; + } else { + EstimatedRSStackSizeLimit = estimateRSStackSizeLimit(MF, this); + EstimatedRSFixedSizeLimit = EstimatedRSStackSizeLimit; + } + // Final estimate of whether sp or bp-relative accesses might require + // scavenging. + bool HasLargeStack = EstimatedStackSize > EstimatedRSStackSizeLimit; + + // If the stack pointer moves and we don't have a base pointer, the + // estimate logic doesn't work. The actual offsets might be larger when + // we're constructing a call frame, or we might need to use negative + // offsets from fp. + bool HasMovingSP = MFI.hasVarSizedObjects() || + (MFI.adjustsStack() && !canSimplifyCallFramePseudos(MF)); + bool HasBPOrFixedSP = RegInfo->hasBasePointer(MF) || !HasMovingSP; + + // If we have a frame pointer, we assume arguments will be accessed + // relative to the frame pointer. Check whether fp-relative accesses to + // arguments require scavenging. + // + // We could do slightly better on Thumb1; in some cases, an sp-relative + // offset would be legal even though an fp-relative offset is not. int MaxFPOffset = getMaxFPOffset(MF.getFunction(), *AFI); - bool BigFrameOffsets = EstimatedStackSize >= EstimatedRSStackSizeLimit || - MFI.hasVarSizedObjects() || - (MFI.adjustsStack() && !canSimplifyCallFramePseudos(MF)) || - // For large argument stacks fp relative addressed may overflow. - (HasFP && (MaxFixedOffset - MaxFPOffset) >= (int)EstimatedRSStackSizeLimit); + bool HasLargeArgumentList = + HasFP && (MaxFixedOffset - MaxFPOffset) > (int)EstimatedRSFixedSizeLimit; + + bool BigFrameOffsets = HasLargeStack || !HasBPOrFixedSP || + HasLargeArgumentList; + LLVM_DEBUG(dbgs() << "EstimatedLimit: " << EstimatedRSStackSizeLimit + << "; EstimatedStack" << EstimatedStackSize + << "; EstimatedFPStack" << MaxFixedOffset - MaxFPOffset + << "; BigFrameOffsets: " << BigFrameOffsets + << "\n"); if (BigFrameOffsets || !CanEliminateFrame || RegInfo->cannotEliminateFrame(MF)) { AFI->setHasStackFrame(true); @@ -1802,8 +1860,17 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, CS1Spilled = true; } - // This is true when we inserted a spill for an unused register that can now - // be used for register scavenging. + // This is true when we inserted a spill for a callee-save GPR which is + // not otherwise used by the function. This guaranteees it is possible + // to scavenge a register to hold the address of a stack slot. On Thumb1, + // the register must be a valid operand to tSTRi, i.e. r4-r7. For other + // subtargets, this is any GPR, i.e. r4-r11 or lr. + // + // If we don't insert a spill, we instead allocate an emergency spill + // slot, which can be used by scavenging to spill an arbitrary register. + // + // We currently don't try to figure out whether any specific instruction + // requires scavening an additional register. bool ExtraCSSpill = false; if (AFI->isThumb1OnlyFunction()) { @@ -1912,7 +1979,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, NumGPRSpills++; CS1Spilled = true; assert(!MRI.isReserved(Reg) && "Should not be reserved"); - if (!MRI.isPhysRegUsed(Reg)) + if (Reg != ARM::LR && !MRI.isPhysRegUsed(Reg)) ExtraCSSpill = true; UnspilledCS1GPRs.erase(llvm::find(UnspilledCS1GPRs, Reg)); if (Reg == ARM::LR) @@ -1937,7 +2004,8 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, UnspilledCS1GPRs.erase(LRPos); ForceLRSpill = false; - if (!MRI.isReserved(ARM::LR) && !MRI.isPhysRegUsed(ARM::LR)) + if (!MRI.isReserved(ARM::LR) && !MRI.isPhysRegUsed(ARM::LR) && + !AFI->isThumb1OnlyFunction()) ExtraCSSpill = true; } @@ -1959,7 +2027,8 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, SavedRegs.set(Reg); LLVM_DEBUG(dbgs() << "Spilling " << printReg(Reg, TRI) << " to make up alignment\n"); - if (!MRI.isReserved(Reg) && !MRI.isPhysRegUsed(Reg)) + if (!MRI.isReserved(Reg) && !MRI.isPhysRegUsed(Reg) && + !(Reg == ARM::LR && AFI->isThumb1OnlyFunction())) ExtraCSSpill = true; break; } @@ -1988,8 +2057,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, unsigned Reg = UnspilledCS1GPRs.back(); UnspilledCS1GPRs.pop_back(); if (!MRI.isReserved(Reg) && - (!AFI->isThumb1OnlyFunction() || isARMLowRegister(Reg) || - Reg == ARM::LR)) { + (!AFI->isThumb1OnlyFunction() || isARMLowRegister(Reg))) { Extras.push_back(Reg); NumExtras--; } @@ -2012,10 +2080,10 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, ExtraCSSpill = true; } } - if (!ExtraCSSpill && !AFI->isThumb1OnlyFunction()) { - // note: Thumb1 functions spill to R12, not the stack. Reserve a slot - // closest to SP or frame pointer. + if (!ExtraCSSpill) { + // Reserve a slot closest to SP or frame pointer. assert(RS && "Register scavenging not provided"); + LLVM_DEBUG(dbgs() << "Reserving emergency spill slot\n"); const TargetRegisterClass &RC = ARM::GPRRegClass; unsigned Size = TRI->getSpillSize(RC); unsigned Align = TRI->getSpillAlignment(RC); @@ -2028,6 +2096,7 @@ void ARMFrameLowering::determineCalleeSaves(MachineFunction &MF, SavedRegs.set(ARM::LR); AFI->setLRIsSpilledForFarJump(true); } + AFI->setLRIsSpilled(SavedRegs.test(ARM::LR)); } MachineBasicBlock::iterator ARMFrameLowering::eliminateCallFramePseudoInstr( diff --git a/lib/Target/ARM/ARMFrameLowering.h b/lib/Target/ARM/ARMFrameLowering.h index 2f7e23840e75..7544ca3c38d6 100644 --- a/lib/Target/ARM/ARMFrameLowering.h +++ b/lib/Target/ARM/ARMFrameLowering.h @@ -1,9 +1,8 @@ //===- ARMTargetFrameLowering.h - Define frame lowering for ARM -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMHazardRecognizer.cpp b/lib/Target/ARM/ARMHazardRecognizer.cpp index d5dacbe08770..0fa32a0abeff 100644 --- a/lib/Target/ARM/ARMHazardRecognizer.cpp +++ b/lib/Target/ARM/ARMHazardRecognizer.cpp @@ -1,9 +1,8 @@ //===-- ARMHazardRecognizer.cpp - ARM postra hazard recognizer ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMHazardRecognizer.h b/lib/Target/ARM/ARMHazardRecognizer.h index ccf09db69937..b5ac694e01f7 100644 --- a/lib/Target/ARM/ARMHazardRecognizer.h +++ b/lib/Target/ARM/ARMHazardRecognizer.h @@ -1,9 +1,8 @@ //===-- ARMHazardRecognizer.h - ARM Hazard Recognizers ----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index 8e0e82388251..b349627b67b1 100644 --- a/lib/Target/ARM/ARMISelDAGToDAG.cpp +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -1,9 +1,8 @@ //===-- ARMISelDAGToDAG.cpp - A dag to dag inst selector for ARM ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -120,8 +119,7 @@ public: SDValue &Offset, SDValue &Opc); bool SelectAddrMode3Offset(SDNode *Op, SDValue N, SDValue &Offset, SDValue &Opc); - bool IsAddressingMode5(SDValue N, SDValue &Base, SDValue &Offset, - int Lwb, int Upb, bool FP16); + bool IsAddressingMode5(SDValue N, SDValue &Base, SDValue &Offset, bool FP16); bool SelectAddrMode5(SDValue N, SDValue &Base, SDValue &Offset); bool SelectAddrMode5FP16(SDValue N, SDValue &Base, SDValue &Offset); bool SelectAddrMode6(SDNode *Parent, SDValue N, SDValue &Addr,SDValue &Align); @@ -131,6 +129,7 @@ public: // Thumb Addressing Modes: bool SelectThumbAddrModeRR(SDValue N, SDValue &Base, SDValue &Offset); + bool SelectThumbAddrModeRRSext(SDValue N, SDValue &Base, SDValue &Offset); bool SelectThumbAddrModeImm5S(SDValue N, unsigned Scale, SDValue &Base, SDValue &OffImm); bool SelectThumbAddrModeImm5S1(SDValue N, SDValue &Base, @@ -147,6 +146,9 @@ public: SDValue &OffImm); bool SelectT2AddrModeImm8Offset(SDNode *Op, SDValue N, SDValue &OffImm); + template<unsigned Shift> + bool SelectT2AddrModeImm7(SDValue N, SDValue &Base, + SDValue &OffImm); bool SelectT2AddrModeSoReg(SDValue N, SDValue &Base, SDValue &OffReg, SDValue &ShImm); bool SelectT2AddrModeExclusive(SDValue N, SDValue &Base, SDValue &OffImm); @@ -452,8 +454,10 @@ unsigned ARMDAGToDAGISel::ConstantMaterializationCost(unsigned Val) const { if (Subtarget->isThumb()) { if (Val <= 255) return 1; // MOV if (Subtarget->hasV6T2Ops() && - (Val <= 0xffff || ARM_AM::getT2SOImmValSplatVal(Val) != -1)) - return 1; // MOVW + (Val <= 0xffff || // MOV + ARM_AM::getT2SOImmVal(Val) != -1 || // MOVW + ARM_AM::getT2SOImmVal(~Val) != -1)) // MVN + return 1; if (Val <= 510) return 2; // MOV + ADDi8 if (~Val <= 255) return 2; // MOV + MVN if (ARM_AM::isThumbImmShiftedVal(Val)) return 2; // MOV + LSL @@ -463,7 +467,7 @@ unsigned ARMDAGToDAGISel::ConstantMaterializationCost(unsigned Val) const { if (Subtarget->hasV6T2Ops() && Val <= 0xffff) return 1; // MOVW if (ARM_AM::isSOImmTwoPartVal(Val)) return 2; // two instrs } - if (Subtarget->useMovt(*MF)) return 2; // MOVW + MOVT + if (Subtarget->useMovt()) return 2; // MOVW + MOVT return 3; // Literal pool load } @@ -900,7 +904,7 @@ bool ARMDAGToDAGISel::SelectAddrMode3Offset(SDNode *Op, SDValue N, } bool ARMDAGToDAGISel::IsAddressingMode5(SDValue N, SDValue &Base, SDValue &Offset, - int Lwb, int Upb, bool FP16) { + bool FP16) { if (!CurDAG->isBaseWithConstantOffset(N)) { Base = N; if (N.getOpcode() == ISD::FrameIndex) { @@ -922,7 +926,7 @@ bool ARMDAGToDAGISel::IsAddressingMode5(SDValue N, SDValue &Base, SDValue &Offse int RHSC; const int Scale = FP16 ? 2 : 4; - if (isScaledConstantInRange(N.getOperand(1), Scale, Lwb, Upb, RHSC)) { + if (isScaledConstantInRange(N.getOperand(1), Scale, -255, 256, RHSC)) { Base = N.getOperand(0); if (Base.getOpcode() == ISD::FrameIndex) { int FI = cast<FrameIndexSDNode>(Base)->getIndex(); @@ -960,16 +964,12 @@ bool ARMDAGToDAGISel::IsAddressingMode5(SDValue N, SDValue &Base, SDValue &Offse bool ARMDAGToDAGISel::SelectAddrMode5(SDValue N, SDValue &Base, SDValue &Offset) { - int Lwb = -256 + 1; - int Upb = 256; - return IsAddressingMode5(N, Base, Offset, Lwb, Upb, /*FP16=*/ false); + return IsAddressingMode5(N, Base, Offset, /*FP16=*/ false); } bool ARMDAGToDAGISel::SelectAddrMode5FP16(SDValue N, SDValue &Base, SDValue &Offset) { - int Lwb = -512 + 1; - int Upb = 512; - return IsAddressingMode5(N, Base, Offset, Lwb, Upb, /*FP16=*/ true); + return IsAddressingMode5(N, Base, Offset, /*FP16=*/ true); } bool ARMDAGToDAGISel::SelectAddrMode6(SDNode *Parent, SDValue N, SDValue &Addr, @@ -1033,8 +1033,22 @@ bool ARMDAGToDAGISel::SelectAddrModePC(SDValue N, // Thumb Addressing Modes //===----------------------------------------------------------------------===// -bool ARMDAGToDAGISel::SelectThumbAddrModeRR(SDValue N, - SDValue &Base, SDValue &Offset){ +static bool shouldUseZeroOffsetLdSt(SDValue N) { + // Negative numbers are difficult to materialise in thumb1. If we are + // selecting the add of a negative, instead try to select ri with a zero + // offset, so create the add node directly which will become a sub. + if (N.getOpcode() != ISD::ADD) + return false; + + // Look for an imm which is not legal for ld/st, but is legal for sub. + if (auto C = dyn_cast<ConstantSDNode>(N.getOperand(1))) + return C->getSExtValue() < 0 && C->getSExtValue() >= -255; + + return false; +} + +bool ARMDAGToDAGISel::SelectThumbAddrModeRRSext(SDValue N, SDValue &Base, + SDValue &Offset) { if (N.getOpcode() != ISD::ADD && !CurDAG->isBaseWithConstantOffset(N)) { ConstantSDNode *NC = dyn_cast<ConstantSDNode>(N); if (!NC || !NC->isNullValue()) @@ -1049,9 +1063,22 @@ bool ARMDAGToDAGISel::SelectThumbAddrModeRR(SDValue N, return true; } +bool ARMDAGToDAGISel::SelectThumbAddrModeRR(SDValue N, SDValue &Base, + SDValue &Offset) { + if (shouldUseZeroOffsetLdSt(N)) + return false; // Select ri instead + return SelectThumbAddrModeRRSext(N, Base, Offset); +} + bool ARMDAGToDAGISel::SelectThumbAddrModeImm5S(SDValue N, unsigned Scale, SDValue &Base, SDValue &OffImm) { + if (shouldUseZeroOffsetLdSt(N)) { + Base = N; + OffImm = CurDAG->getTargetConstant(0, SDLoc(N), MVT::i32); + return true; + } + if (!CurDAG->isBaseWithConstantOffset(N)) { if (N.getOpcode() == ISD::ADD) { return false; // We want to select register offset instead @@ -1117,25 +1144,28 @@ bool ARMDAGToDAGISel::SelectThumbAddrModeSP(SDValue N, if (!CurDAG->isBaseWithConstantOffset(N)) return false; - RegisterSDNode *LHSR = dyn_cast<RegisterSDNode>(N.getOperand(0)); - if (N.getOperand(0).getOpcode() == ISD::FrameIndex || - (LHSR && LHSR->getReg() == ARM::SP)) { + if (N.getOperand(0).getOpcode() == ISD::FrameIndex) { // If the RHS is + imm8 * scale, fold into addr mode. int RHSC; if (isScaledConstantInRange(N.getOperand(1), /*Scale=*/4, 0, 256, RHSC)) { Base = N.getOperand(0); - if (Base.getOpcode() == ISD::FrameIndex) { - int FI = cast<FrameIndexSDNode>(Base)->getIndex(); + int FI = cast<FrameIndexSDNode>(Base)->getIndex(); + // Make sure the offset is inside the object, or we might fail to + // allocate an emergency spill slot. (An out-of-range access is UB, but + // it could show up anyway.) + MachineFrameInfo &MFI = MF->getFrameInfo(); + if (RHSC * 4 < MFI.getObjectSize(FI)) { // For LHS+RHS to result in an offset that's a multiple of 4 the object // indexed by the LHS must be 4-byte aligned. - MachineFrameInfo &MFI = MF->getFrameInfo(); - if (MFI.getObjectAlignment(FI) < 4) + if (!MFI.isFixedObjectIndex(FI) && MFI.getObjectAlignment(FI) < 4) MFI.setObjectAlignment(FI, 4); - Base = CurDAG->getTargetFrameIndex( - FI, TLI->getPointerTy(CurDAG->getDataLayout())); + if (MFI.getObjectAlignment(FI) >= 4) { + Base = CurDAG->getTargetFrameIndex( + FI, TLI->getPointerTy(CurDAG->getDataLayout())); + OffImm = CurDAG->getTargetConstant(RHSC, SDLoc(N), MVT::i32); + return true; + } } - OffImm = CurDAG->getTargetConstant(RHSC, SDLoc(N), MVT::i32); - return true; } } @@ -1248,6 +1278,35 @@ bool ARMDAGToDAGISel::SelectT2AddrModeImm8Offset(SDNode *Op, SDValue N, return false; } +template<unsigned Shift> +bool ARMDAGToDAGISel::SelectT2AddrModeImm7(SDValue N, + SDValue &Base, SDValue &OffImm) { + if (N.getOpcode() == ISD::SUB || + CurDAG->isBaseWithConstantOffset(N)) { + if (auto RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) { + int RHSC = (int)RHS->getZExtValue(); + if (N.getOpcode() == ISD::SUB) + RHSC = -RHSC; + + if (isShiftedInt<7, Shift>(RHSC)) { + Base = N.getOperand(0); + if (Base.getOpcode() == ISD::FrameIndex) { + int FI = cast<FrameIndexSDNode>(Base)->getIndex(); + Base = CurDAG->getTargetFrameIndex( + FI, TLI->getPointerTy(CurDAG->getDataLayout())); + } + OffImm = CurDAG->getTargetConstant(RHSC, SDLoc(N), MVT::i32); + return true; + } + } + } + + // Base only. + Base = N; + OffImm = CurDAG->getTargetConstant(0, SDLoc(N), MVT::i32); + return true; +} + bool ARMDAGToDAGISel::SelectT2AddrModeSoReg(SDValue N, SDValue &Base, SDValue &OffReg, SDValue &ShImm) { @@ -2072,10 +2131,12 @@ void ARMDAGToDAGISel::SelectVLDSTLane(SDNode *N, bool IsLoad, bool isUpdating, default: llvm_unreachable("unhandled vld/vst lane type"); // Double-register operations: case MVT::v8i8: OpcodeIndex = 0; break; + case MVT::v4f16: case MVT::v4i16: OpcodeIndex = 1; break; case MVT::v2f32: case MVT::v2i32: OpcodeIndex = 2; break; // Quad-register operations: + case MVT::v8f16: case MVT::v8i16: OpcodeIndex = 0; break; case MVT::v4f32: case MVT::v4i32: OpcodeIndex = 1; break; @@ -2192,7 +2253,10 @@ void ARMDAGToDAGISel::SelectVLDDup(SDNode *N, bool IsIntrinsic, case MVT::v8i8: case MVT::v16i8: OpcodeIndex = 0; break; case MVT::v4i16: - case MVT::v8i16: OpcodeIndex = 1; break; + case MVT::v8i16: + case MVT::v4f16: + case MVT::v8f16: + OpcodeIndex = 1; break; case MVT::v2f32: case MVT::v2i32: case MVT::v4f32: @@ -2577,6 +2641,44 @@ void ARMDAGToDAGISel::Select(SDNode *N) { switch (N->getOpcode()) { default: break; + case ISD::STORE: { + // For Thumb1, match an sp-relative store in C++. This is a little + // unfortunate, but I don't think I can make the chain check work + // otherwise. (The chain of the store has to be the same as the chain + // of the CopyFromReg, or else we can't replace the CopyFromReg with + // a direct reference to "SP".) + // + // This is only necessary on Thumb1 because Thumb1 sp-relative stores use + // a different addressing mode from other four-byte stores. + // + // This pattern usually comes up with call arguments. + StoreSDNode *ST = cast<StoreSDNode>(N); + SDValue Ptr = ST->getBasePtr(); + if (Subtarget->isThumb1Only() && ST->isUnindexed()) { + int RHSC = 0; + if (Ptr.getOpcode() == ISD::ADD && + isScaledConstantInRange(Ptr.getOperand(1), /*Scale=*/4, 0, 256, RHSC)) + Ptr = Ptr.getOperand(0); + + if (Ptr.getOpcode() == ISD::CopyFromReg && + cast<RegisterSDNode>(Ptr.getOperand(1))->getReg() == ARM::SP && + Ptr.getOperand(0) == ST->getChain()) { + SDValue Ops[] = {ST->getValue(), + CurDAG->getRegister(ARM::SP, MVT::i32), + CurDAG->getTargetConstant(RHSC, dl, MVT::i32), + getAL(CurDAG, dl), + CurDAG->getRegister(0, MVT::i32), + ST->getChain()}; + MachineSDNode *ResNode = + CurDAG->getMachineNode(ARM::tSTRspi, dl, MVT::Other, Ops); + MachineMemOperand *MemOp = ST->getMemOperand(); + CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {MemOp}); + ReplaceNode(N, ResNode); + return; + } + } + break; + } case ISD::WRITE_REGISTER: if (tryWriteRegister(N)) return; @@ -2586,6 +2688,7 @@ void ARMDAGToDAGISel::Select(SDNode *N) { return; break; case ISD::INLINEASM: + case ISD::INLINEASM_BR: if (tryInlineAsm(N)) return; break; @@ -2895,6 +2998,16 @@ void ARMDAGToDAGISel::Select(SDNode *N) { // Other cases are autogenerated. break; } + case ARMISD::WLS: { + SDValue Ops[] = { N->getOperand(1), // Loop count + N->getOperand(2), // Exit target + N->getOperand(0) }; + SDNode *LoopStart = + CurDAG->getMachineNode(ARM::t2WhileLoopStart, dl, MVT::Other, Ops); + ReplaceUses(N, LoopStart); + CurDAG->RemoveDeadNode(N); + return; + } case ARMISD::BRCOND: { // Pattern: (ARMbrcond:void (bb:Other):$dst, (imm:i32):$cc) // Emits: (Bcc:void (bb:Other):$dst, (imm:i32):$cc) @@ -2922,6 +3035,36 @@ void ARMDAGToDAGISel::Select(SDNode *N) { unsigned CC = (unsigned) cast<ConstantSDNode>(N2)->getZExtValue(); if (InFlag.getOpcode() == ARMISD::CMPZ) { + if (InFlag.getOperand(0).getOpcode() == ISD::INTRINSIC_W_CHAIN) { + SDValue Int = InFlag.getOperand(0); + uint64_t ID = cast<ConstantSDNode>(Int->getOperand(1))->getZExtValue(); + + // Handle low-overhead loops. + if (ID == Intrinsic::loop_decrement_reg) { + SDValue Elements = Int.getOperand(2); + SDValue Size = CurDAG->getTargetConstant( + cast<ConstantSDNode>(Int.getOperand(3))->getZExtValue(), dl, + MVT::i32); + + SDValue Args[] = { Elements, Size, Int.getOperand(0) }; + SDNode *LoopDec = + CurDAG->getMachineNode(ARM::t2LoopDec, dl, + CurDAG->getVTList(MVT::i32, MVT::Other), + Args); + ReplaceUses(Int.getNode(), LoopDec); + + SDValue EndArgs[] = { SDValue(LoopDec, 0), N1, Chain }; + SDNode *LoopEnd = + CurDAG->getMachineNode(ARM::t2LoopEnd, dl, MVT::Other, EndArgs); + + ReplaceUses(N, LoopEnd); + CurDAG->RemoveDeadNode(N); + CurDAG->RemoveDeadNode(InFlag.getNode()); + CurDAG->RemoveDeadNode(Int.getNode()); + return; + } + } + bool SwitchEQNEToPLMI; SelectCMPZ(InFlag.getNode(), SwitchEQNEToPLMI); InFlag = N->getOperand(4); @@ -3979,9 +4122,9 @@ bool ARMDAGToDAGISel::tryReadRegister(SDNode *N){ // If an opcode was found then we can lower the read to a VFP instruction. if (Opcode) { - if (!Subtarget->hasVFP2()) + if (!Subtarget->hasVFP2Base()) return false; - if (Opcode == ARM::VMRS_MVFR2 && !Subtarget->hasFPARMv8()) + if (Opcode == ARM::VMRS_MVFR2 && !Subtarget->hasFPARMv8Base()) return false; Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32), @@ -4090,7 +4233,7 @@ bool ARMDAGToDAGISel::tryWriteRegister(SDNode *N){ .Default(0); if (Opcode) { - if (!Subtarget->hasVFP2()) + if (!Subtarget->hasVFP2Base()) return false; Ops = { N->getOperand(2), getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32), N->getOperand(0) }; @@ -4290,7 +4433,7 @@ bool ARMDAGToDAGISel::tryInlineAsm(SDNode *N){ if (!Changed) return false; - SDValue New = CurDAG->getNode(ISD::INLINEASM, SDLoc(N), + SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N), CurDAG->getVTList(MVT::Other, MVT::Glue), AsmNodeOperands); New->setNodeId(-1); ReplaceNode(N, New.getNode()); diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp index 21de0f6a7630..18bb9bf3eccc 100644 --- a/lib/Target/ARM/ARMISelLowering.cpp +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -1,9 +1,8 @@ //===- ARMISelLowering.cpp - ARM DAG Lowering Implementation --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -80,6 +79,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" +#include "llvm/IR/PatternMatch.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -113,6 +113,7 @@ #include <vector> using namespace llvm; +using namespace llvm::PatternMatch; #define DEBUG_TYPE "arm-isel" @@ -220,6 +221,121 @@ void ARMTargetLowering::addQRTypeForNEON(MVT VT) { addTypeForNEON(VT, MVT::v2f64, MVT::v4i32); } +void ARMTargetLowering::setAllExpand(MVT VT) { + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, VT, Expand); + + // We support these really simple operations even on types where all + // the actual arithmetic has to be broken down into simpler + // operations or turned into library calls. + setOperationAction(ISD::BITCAST, VT, Legal); + setOperationAction(ISD::LOAD, VT, Legal); + setOperationAction(ISD::STORE, VT, Legal); + setOperationAction(ISD::UNDEF, VT, Legal); +} + +void ARMTargetLowering::addAllExtLoads(const MVT From, const MVT To, + LegalizeAction Action) { + setLoadExtAction(ISD::EXTLOAD, From, To, Action); + setLoadExtAction(ISD::ZEXTLOAD, From, To, Action); + setLoadExtAction(ISD::SEXTLOAD, From, To, Action); +} + +void ARMTargetLowering::addMVEVectorTypes(bool HasMVEFP) { + const MVT IntTypes[] = { MVT::v16i8, MVT::v8i16, MVT::v4i32 }; + + for (auto VT : IntTypes) { + addRegisterClass(VT, &ARM::QPRRegClass); + setOperationAction(ISD::VECTOR_SHUFFLE, VT, Custom); + setOperationAction(ISD::INSERT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::EXTRACT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::BUILD_VECTOR, VT, Custom); + setOperationAction(ISD::SHL, VT, Custom); + setOperationAction(ISD::SRA, VT, Custom); + setOperationAction(ISD::SRL, VT, Custom); + setOperationAction(ISD::SMIN, VT, Legal); + setOperationAction(ISD::SMAX, VT, Legal); + setOperationAction(ISD::UMIN, VT, Legal); + setOperationAction(ISD::UMAX, VT, Legal); + setOperationAction(ISD::ABS, VT, Legal); + + // No native support for these. + setOperationAction(ISD::UDIV, VT, Expand); + setOperationAction(ISD::SDIV, VT, Expand); + setOperationAction(ISD::UREM, VT, Expand); + setOperationAction(ISD::SREM, VT, Expand); + + if (!HasMVEFP) { + setOperationAction(ISD::SINT_TO_FP, VT, Expand); + setOperationAction(ISD::UINT_TO_FP, VT, Expand); + setOperationAction(ISD::FP_TO_SINT, VT, Expand); + setOperationAction(ISD::FP_TO_UINT, VT, Expand); + } + } + + const MVT FloatTypes[] = { MVT::v8f16, MVT::v4f32 }; + for (auto VT : FloatTypes) { + addRegisterClass(VT, &ARM::QPRRegClass); + if (!HasMVEFP) + setAllExpand(VT); + + // These are legal or custom whether we have MVE.fp or not + setOperationAction(ISD::VECTOR_SHUFFLE, VT, Custom); + setOperationAction(ISD::INSERT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::INSERT_VECTOR_ELT, VT.getVectorElementType(), Custom); + setOperationAction(ISD::EXTRACT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::BUILD_VECTOR, VT, Custom); + setOperationAction(ISD::BUILD_VECTOR, VT.getVectorElementType(), Custom); + setOperationAction(ISD::SCALAR_TO_VECTOR, VT, Legal); + + if (HasMVEFP) { + setOperationAction(ISD::FMINNUM, VT, Legal); + setOperationAction(ISD::FMAXNUM, VT, Legal); + setOperationAction(ISD::FROUND, VT, Legal); + + // No native support for these. + setOperationAction(ISD::FDIV, VT, Expand); + setOperationAction(ISD::FREM, VT, Expand); + setOperationAction(ISD::FSQRT, VT, Expand); + setOperationAction(ISD::FSIN, VT, Expand); + setOperationAction(ISD::FCOS, VT, Expand); + setOperationAction(ISD::FPOW, VT, Expand); + setOperationAction(ISD::FLOG, VT, Expand); + setOperationAction(ISD::FLOG2, VT, Expand); + setOperationAction(ISD::FLOG10, VT, Expand); + setOperationAction(ISD::FEXP, VT, Expand); + setOperationAction(ISD::FEXP2, VT, Expand); + setOperationAction(ISD::FNEARBYINT, VT, Expand); + } + } + + // We 'support' these types up to bitcast/load/store level, regardless of + // MVE integer-only / float support. Only doing FP data processing on the FP + // vector types is inhibited at integer-only level. + const MVT LongTypes[] = { MVT::v2i64, MVT::v2f64 }; + for (auto VT : LongTypes) { + addRegisterClass(VT, &ARM::QPRRegClass); + setAllExpand(VT); + setOperationAction(ISD::INSERT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::EXTRACT_VECTOR_ELT, VT, Custom); + setOperationAction(ISD::BUILD_VECTOR, VT, Custom); + } + // We can do bitwise operations on v2i64 vectors + setOperationAction(ISD::AND, MVT::v2i64, Legal); + setOperationAction(ISD::OR, MVT::v2i64, Legal); + setOperationAction(ISD::XOR, MVT::v2i64, Legal); + + // It is legal to extload from v4i8 to v4i16 or v4i32. + addAllExtLoads(MVT::v8i16, MVT::v8i8, Legal); + addAllExtLoads(MVT::v4i32, MVT::v4i16, Legal); + addAllExtLoads(MVT::v4i32, MVT::v4i8, Legal); + + // Some truncating stores are legal too. + setTruncStoreAction(MVT::v4i32, MVT::v4i16, Legal); + setTruncStoreAction(MVT::v4i32, MVT::v4i8, Legal); + setTruncStoreAction(MVT::v8i16, MVT::v8i8, Legal); +} + ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, const ARMSubtarget &STI) : TargetLowering(TM), Subtarget(&STI) { @@ -240,7 +356,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, if (Subtarget->isTargetMachO()) { // Uses VFP for Thumb libfuncs if available. - if (Subtarget->isThumb() && Subtarget->hasVFP2() && + if (Subtarget->isThumb() && Subtarget->hasVFP2Base() && Subtarget->hasARMOps() && !Subtarget->useSoftFloat()) { static const struct { const RTLIB::Libcall Op; @@ -509,10 +625,14 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, else addRegisterClass(MVT::i32, &ARM::GPRRegClass); - if (!Subtarget->useSoftFloat() && Subtarget->hasVFP2() && - !Subtarget->isThumb1Only()) { + if (!Subtarget->useSoftFloat() && !Subtarget->isThumb1Only() && + Subtarget->hasFPRegs()) { addRegisterClass(MVT::f32, &ARM::SPRRegClass); addRegisterClass(MVT::f64, &ARM::DPRRegClass); + if (!Subtarget->hasVFP2Base()) + setAllExpand(MVT::f32); + if (!Subtarget->hasFP64()) + setAllExpand(MVT::f64); } if (Subtarget->hasFullFP16()) { @@ -528,9 +648,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, for (MVT VT : MVT::vector_valuetypes()) { for (MVT InnerVT : MVT::vector_valuetypes()) { setTruncStoreAction(VT, InnerVT, Expand); - setLoadExtAction(ISD::SEXTLOAD, VT, InnerVT, Expand); - setLoadExtAction(ISD::ZEXTLOAD, VT, InnerVT, Expand); - setLoadExtAction(ISD::EXTLOAD, VT, InnerVT, Expand); + addAllExtLoads(VT, InnerVT, Expand); } setOperationAction(ISD::MULHS, VT, Expand); @@ -547,6 +665,13 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::READ_REGISTER, MVT::i64, Custom); setOperationAction(ISD::WRITE_REGISTER, MVT::i64, Custom); + if (Subtarget->hasMVEIntegerOps()) + addMVEVectorTypes(Subtarget->hasMVEFloatOps()); + + // Combine low-overhead loop intrinsics so that we can lower i1 types. + if (Subtarget->hasLOB()) + setTargetDAGCombine(ISD::BRCOND); + if (Subtarget->hasNEON()) { addDRTypeForNEON(MVT::v2f32); addDRTypeForNEON(MVT::v8i8); @@ -565,11 +690,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, addQRTypeForNEON(MVT::v8f16); addDRTypeForNEON(MVT::v4f16); } + } + if (Subtarget->hasMVEIntegerOps() || Subtarget->hasNEON()) { // v2f64 is legal so that QR subregs can be extracted as f64 elements, but - // neither Neon nor VFP support any arithmetic operations on it. - // The same with v4f32. But keep in mind that vadd, vsub, vmul are natively - // supported for v4f32. + // none of Neon, MVE or VFP supports any arithmetic operations on it. setOperationAction(ISD::FADD, MVT::v2f64, Expand); setOperationAction(ISD::FSUB, MVT::v2f64, Expand); setOperationAction(ISD::FMUL, MVT::v2f64, Expand); @@ -603,7 +728,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FNEARBYINT, MVT::v2f64, Expand); setOperationAction(ISD::FFLOOR, MVT::v2f64, Expand); setOperationAction(ISD::FMA, MVT::v2f64, Expand); + } + if (Subtarget->hasNEON()) { + // The same with v4f32. But keep in mind that vadd, vsub, vmul are natively + // supported for v4f32. setOperationAction(ISD::FSQRT, MVT::v4f32, Expand); setOperationAction(ISD::FSIN, MVT::v4f32, Expand); setOperationAction(ISD::FCOS, MVT::v4f32, Expand); @@ -697,7 +826,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::v2i64, Custom); // NEON only has FMA instructions as of VFP4. - if (!Subtarget->hasVFP4()) { + if (!Subtarget->hasVFP4Base()) { setOperationAction(ISD::FMA, MVT::v2f32, Expand); setOperationAction(ISD::FMA, MVT::v4f32, Expand); } @@ -711,9 +840,6 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setTargetDAGCombine(ISD::SIGN_EXTEND); setTargetDAGCombine(ISD::ZERO_EXTEND); setTargetDAGCombine(ISD::ANY_EXTEND); - setTargetDAGCombine(ISD::BUILD_VECTOR); - setTargetDAGCombine(ISD::VECTOR_SHUFFLE); - setTargetDAGCombine(ISD::INSERT_VECTOR_ELT); setTargetDAGCombine(ISD::STORE); setTargetDAGCombine(ISD::FP_TO_SINT); setTargetDAGCombine(ISD::FP_TO_UINT); @@ -731,7 +857,13 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, } } - if (Subtarget->isFPOnlySP()) { + if (Subtarget->hasNEON() || Subtarget->hasMVEIntegerOps()) { + setTargetDAGCombine(ISD::BUILD_VECTOR); + setTargetDAGCombine(ISD::VECTOR_SHUFFLE); + setTargetDAGCombine(ISD::INSERT_VECTOR_ELT); + } + + if (!Subtarget->hasFP64()) { // When targeting a floating-point unit with only single-precision // operations, f64 is legal for the few double-precision instructions which // are present However, no double-precision operations other than moves, @@ -767,9 +899,19 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FP_TO_SINT, MVT::f64, Custom); setOperationAction(ISD::FP_TO_UINT, MVT::f64, Custom); setOperationAction(ISD::FP_ROUND, MVT::f32, Custom); + } + + if (!Subtarget->hasFP64() || !Subtarget->hasFPARMv8Base()){ setOperationAction(ISD::FP_EXTEND, MVT::f64, Custom); + setOperationAction(ISD::FP_ROUND, MVT::f16, Custom); } + if (!Subtarget->hasFP16()) + setOperationAction(ISD::FP_EXTEND, MVT::f32, Custom); + + if (!Subtarget->hasFP64()) + setOperationAction(ISD::FP_ROUND, MVT::f32, Custom); + computeRegisterProperties(Subtarget->getRegisterInfo()); // ARM does not have floating-point extending loads. @@ -832,6 +974,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::SRA, MVT::i64, Custom); setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i64, Custom); + // MVE lowers 64 bit shifts to lsll and lsrl + // assuming that ISD::SRL and SRA of i64 are already marked custom + if (Subtarget->hasMVEIntegerOps()) + setOperationAction(ISD::SHL, MVT::i64, Custom); + // Expand to __aeabi_l{lsl,lsr,asr} calls for Thumb1. if (Subtarget->isThumb1Only()) { setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand); @@ -1029,7 +1176,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, } setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); - if (!Subtarget->useSoftFloat() && Subtarget->hasVFP2() && + if (!Subtarget->useSoftFloat() && Subtarget->hasFPRegs() && !Subtarget->isThumb1Only()) { // Turn f64->i64 into VMOVRRD, i64 -> f64 to VMOVDRR // iff target supports vfp2. @@ -1079,7 +1226,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FSINCOS, MVT::f32, Expand); setOperationAction(ISD::FREM, MVT::f64, Expand); setOperationAction(ISD::FREM, MVT::f32, Expand); - if (!Subtarget->useSoftFloat() && Subtarget->hasVFP2() && + if (!Subtarget->useSoftFloat() && Subtarget->hasVFP2Base() && !Subtarget->isThumb1Only()) { setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom); setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom); @@ -1087,7 +1234,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FPOW, MVT::f64, Expand); setOperationAction(ISD::FPOW, MVT::f32, Expand); - if (!Subtarget->hasVFP4()) { + if (!Subtarget->hasVFP4Base()) { setOperationAction(ISD::FMA, MVT::f64, Expand); setOperationAction(ISD::FMA, MVT::f32, Expand); } @@ -1095,7 +1242,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, // Various VFP goodness if (!Subtarget->useSoftFloat() && !Subtarget->isThumb1Only()) { // FP-ARMv8 adds f64 <-> f16 conversion. Before that it should be expanded. - if (!Subtarget->hasFPARMv8() || Subtarget->isFPOnlySP()) { + if (!Subtarget->hasFPARMv8Base() || !Subtarget->hasFP64()) { setOperationAction(ISD::FP16_TO_FP, MVT::f64, Expand); setOperationAction(ISD::FP_TO_FP16, MVT::f64, Expand); } @@ -1115,7 +1262,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, } // FP-ARMv8 implements a lot of rounding-like FP operations. - if (Subtarget->hasFPARMv8()) { + if (Subtarget->hasFPARMv8Base()) { setOperationAction(ISD::FFLOOR, MVT::f32, Legal); setOperationAction(ISD::FCEIL, MVT::f32, Legal); setOperationAction(ISD::FROUND, MVT::f32, Legal); @@ -1124,12 +1271,14 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FRINT, MVT::f32, Legal); setOperationAction(ISD::FMINNUM, MVT::f32, Legal); setOperationAction(ISD::FMAXNUM, MVT::f32, Legal); - setOperationAction(ISD::FMINNUM, MVT::v2f32, Legal); - setOperationAction(ISD::FMAXNUM, MVT::v2f32, Legal); - setOperationAction(ISD::FMINNUM, MVT::v4f32, Legal); - setOperationAction(ISD::FMAXNUM, MVT::v4f32, Legal); + if (Subtarget->hasNEON()) { + setOperationAction(ISD::FMINNUM, MVT::v2f32, Legal); + setOperationAction(ISD::FMAXNUM, MVT::v2f32, Legal); + setOperationAction(ISD::FMINNUM, MVT::v4f32, Legal); + setOperationAction(ISD::FMAXNUM, MVT::v4f32, Legal); + } - if (!Subtarget->isFPOnlySP()) { + if (Subtarget->hasFP64()) { setOperationAction(ISD::FFLOOR, MVT::f64, Legal); setOperationAction(ISD::FCEIL, MVT::f64, Legal); setOperationAction(ISD::FROUND, MVT::f64, Legal); @@ -1141,6 +1290,24 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, } } + // FP16 often need to be promoted to call lib functions + if (Subtarget->hasFullFP16()) { + setOperationAction(ISD::FREM, MVT::f16, Promote); + setOperationAction(ISD::FCOPYSIGN, MVT::f16, Expand); + setOperationAction(ISD::FSIN, MVT::f16, Promote); + setOperationAction(ISD::FCOS, MVT::f16, Promote); + setOperationAction(ISD::FSINCOS, MVT::f16, Promote); + setOperationAction(ISD::FPOWI, MVT::f16, Promote); + setOperationAction(ISD::FPOW, MVT::f16, Promote); + setOperationAction(ISD::FEXP, MVT::f16, Promote); + setOperationAction(ISD::FEXP2, MVT::f16, Promote); + setOperationAction(ISD::FLOG, MVT::f16, Promote); + setOperationAction(ISD::FLOG10, MVT::f16, Promote); + setOperationAction(ISD::FLOG2, MVT::f16, Promote); + + setOperationAction(ISD::FROUND, MVT::f16, Legal); + } + if (Subtarget->hasNEON()) { // vmin and vmax aren't available in a scalar form, so we use // a NEON instruction with an undef lane instead. @@ -1177,11 +1344,13 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, if (Subtarget->hasV6Ops()) setTargetDAGCombine(ISD::SRL); + if (Subtarget->isThumb1Only()) + setTargetDAGCombine(ISD::SHL); setStackPointerRegisterToSaveRestore(ARM::SP); if (Subtarget->useSoftFloat() || Subtarget->isThumb1Only() || - !Subtarget->hasVFP2()) + !Subtarget->hasVFP2Base() || Subtarget->hasMinSize()) setSchedulingPreference(Sched::RegPressure); else setSchedulingPreference(Sched::Hybrid); @@ -1204,6 +1373,9 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM, setPrefLoopAlignment(Subtarget->getPrefLoopAlignment()); setMinFunctionAlignment(Subtarget->isThumb() ? 1 : 2); + + if (Subtarget->isThumb() || Subtarget->isThumb2()) + setTargetDAGCombine(ISD::ABS); } bool ARMTargetLowering::useSoftFloat() const { @@ -1288,6 +1460,10 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const { case ARMISD::SSAT: return "ARMISD::SSAT"; case ARMISD::USAT: return "ARMISD::USAT"; + case ARMISD::ASRL: return "ARMISD::ASRL"; + case ARMISD::LSRL: return "ARMISD::LSRL"; + case ARMISD::LSLL: return "ARMISD::LSLL"; + case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG"; case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG"; case ARMISD::RRX: return "ARMISD::RRX"; @@ -1332,23 +1508,25 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const { case ARMISD::VCGTU: return "ARMISD::VCGTU"; case ARMISD::VTST: return "ARMISD::VTST"; - case ARMISD::VSHL: return "ARMISD::VSHL"; - case ARMISD::VSHRs: return "ARMISD::VSHRs"; - case ARMISD::VSHRu: return "ARMISD::VSHRu"; - case ARMISD::VRSHRs: return "ARMISD::VRSHRs"; - case ARMISD::VRSHRu: return "ARMISD::VRSHRu"; - case ARMISD::VRSHRN: return "ARMISD::VRSHRN"; - case ARMISD::VQSHLs: return "ARMISD::VQSHLs"; - case ARMISD::VQSHLu: return "ARMISD::VQSHLu"; - case ARMISD::VQSHLsu: return "ARMISD::VQSHLsu"; - case ARMISD::VQSHRNs: return "ARMISD::VQSHRNs"; - case ARMISD::VQSHRNu: return "ARMISD::VQSHRNu"; - case ARMISD::VQSHRNsu: return "ARMISD::VQSHRNsu"; - case ARMISD::VQRSHRNs: return "ARMISD::VQRSHRNs"; - case ARMISD::VQRSHRNu: return "ARMISD::VQRSHRNu"; - case ARMISD::VQRSHRNsu: return "ARMISD::VQRSHRNsu"; - case ARMISD::VSLI: return "ARMISD::VSLI"; - case ARMISD::VSRI: return "ARMISD::VSRI"; + case ARMISD::VSHLs: return "ARMISD::VSHLs"; + case ARMISD::VSHLu: return "ARMISD::VSHLu"; + case ARMISD::VSHLIMM: return "ARMISD::VSHLIMM"; + case ARMISD::VSHRsIMM: return "ARMISD::VSHRsIMM"; + case ARMISD::VSHRuIMM: return "ARMISD::VSHRuIMM"; + case ARMISD::VRSHRsIMM: return "ARMISD::VRSHRsIMM"; + case ARMISD::VRSHRuIMM: return "ARMISD::VRSHRuIMM"; + case ARMISD::VRSHRNIMM: return "ARMISD::VRSHRNIMM"; + case ARMISD::VQSHLsIMM: return "ARMISD::VQSHLsIMM"; + case ARMISD::VQSHLuIMM: return "ARMISD::VQSHLuIMM"; + case ARMISD::VQSHLsuIMM: return "ARMISD::VQSHLsuIMM"; + case ARMISD::VQSHRNsIMM: return "ARMISD::VQSHRNsIMM"; + case ARMISD::VQSHRNuIMM: return "ARMISD::VQSHRNuIMM"; + case ARMISD::VQSHRNsuIMM: return "ARMISD::VQSHRNsuIMM"; + case ARMISD::VQRSHRNsIMM: return "ARMISD::VQRSHRNsIMM"; + case ARMISD::VQRSHRNuIMM: return "ARMISD::VQRSHRNuIMM"; + case ARMISD::VQRSHRNsuIMM: return "ARMISD::VQRSHRNsuIMM"; + case ARMISD::VSLIIMM: return "ARMISD::VSLIIMM"; + case ARMISD::VSRIIMM: return "ARMISD::VSRIIMM"; case ARMISD::VGETLANEu: return "ARMISD::VGETLANEu"; case ARMISD::VGETLANEs: return "ARMISD::VGETLANEs"; case ARMISD::VMOVIMM: return "ARMISD::VMOVIMM"; @@ -1410,6 +1588,7 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const { case ARMISD::VST2LN_UPD: return "ARMISD::VST2LN_UPD"; case ARMISD::VST3LN_UPD: return "ARMISD::VST3LN_UPD"; case ARMISD::VST4LN_UPD: return "ARMISD::VST4LN_UPD"; + case ARMISD::WLS: return "ARMISD::WLS"; } return nullptr; } @@ -1423,11 +1602,14 @@ EVT ARMTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &, /// getRegClassFor - Return the register class that should be used for the /// specified value type. -const TargetRegisterClass *ARMTargetLowering::getRegClassFor(MVT VT) const { +const TargetRegisterClass * +ARMTargetLowering::getRegClassFor(MVT VT, bool isDivergent) const { + (void)isDivergent; // Map v4i64 to QQ registers but do not make the type legal. Similarly map // v8i64 to QQQQ registers. v4i64 and v8i64 are only used for REG_SEQUENCE to - // load / store 4 to 8 consecutive D registers. - if (Subtarget->hasNEON()) { + // load / store 4 to 8 consecutive NEON D registers, or 2 to 4 consecutive + // MVE Q registers. + if (Subtarget->hasNEON() || Subtarget->hasMVEIntegerOps()) { if (VT == MVT::v4i64) return &ARM::QQPRRegClass; if (VT == MVT::v8i64) @@ -1590,8 +1772,6 @@ static void FPCCToARMCC(ISD::CondCode CC, ARMCC::CondCodes &CondCode, // Calling Convention Implementation //===----------------------------------------------------------------------===// -#include "ARMGenCallingConv.inc" - /// getEffectiveCallingConv - Get the effective calling convention, taking into /// account presence of floating point hardware and calling convention /// limitations, such as support for variadic functions. @@ -1613,7 +1793,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, case CallingConv::C: if (!Subtarget->isAAPCS_ABI()) return CallingConv::ARM_APCS; - else if (Subtarget->hasVFP2() && !Subtarget->isThumb1Only() && + else if (Subtarget->hasVFP2Base() && !Subtarget->isThumb1Only() && getTargetMachine().Options.FloatABIType == FloatABI::Hard && !isVarArg) return CallingConv::ARM_AAPCS_VFP; @@ -1622,10 +1802,11 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, case CallingConv::Fast: case CallingConv::CXX_FAST_TLS: if (!Subtarget->isAAPCS_ABI()) { - if (Subtarget->hasVFP2() && !Subtarget->isThumb1Only() && !isVarArg) + if (Subtarget->hasVFP2Base() && !Subtarget->isThumb1Only() && !isVarArg) return CallingConv::Fast; return CallingConv::ARM_APCS; - } else if (Subtarget->hasVFP2() && !Subtarget->isThumb1Only() && !isVarArg) + } else if (Subtarget->hasVFP2Base() && + !Subtarget->isThumb1Only() && !isVarArg) return CallingConv::ARM_AAPCS_VFP; else return CallingConv::ARM_AAPCS; @@ -1807,29 +1988,42 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, bool isVarArg = CLI.IsVarArg; MachineFunction &MF = DAG.getMachineFunction(); - bool isStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet(); - bool isThisReturn = false; - bool isSibCall = false; + bool isStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet(); + bool isThisReturn = false; auto Attr = MF.getFunction().getFnAttribute("disable-tail-calls"); + bool PreferIndirect = false; // Disable tail calls if they're not supported. if (!Subtarget->supportsTailCall() || Attr.getValueAsString() == "true") isTailCall = false; + if (isa<GlobalAddressSDNode>(Callee)) { + // If we're optimizing for minimum size and the function is called three or + // more times in this block, we can improve codesize by calling indirectly + // as BLXr has a 16-bit encoding. + auto *GV = cast<GlobalAddressSDNode>(Callee)->getGlobal(); + if (CLI.CS) { + auto *BB = CLI.CS.getParent(); + PreferIndirect = Subtarget->isThumb() && Subtarget->hasMinSize() && + count_if(GV->users(), [&BB](const User *U) { + return isa<Instruction>(U) && + cast<Instruction>(U)->getParent() == BB; + }) > 2; + } + } if (isTailCall) { // Check if it's really possible to do a tail call. - isTailCall = IsEligibleForTailCallOptimization(Callee, CallConv, - isVarArg, isStructRet, MF.getFunction().hasStructRetAttr(), - Outs, OutVals, Ins, DAG); + isTailCall = IsEligibleForTailCallOptimization( + Callee, CallConv, isVarArg, isStructRet, + MF.getFunction().hasStructRetAttr(), Outs, OutVals, Ins, DAG, + PreferIndirect); if (!isTailCall && CLI.CS && CLI.CS.isMustTailCall()) report_fatal_error("failed to perform tail call elimination on a call " "site marked musttail"); // We don't support GuaranteedTailCallOpt for ARM, only automatically // detected sibcalls. - if (isTailCall) { + if (isTailCall) ++NumTailCalls; - isSibCall = true; - } } // Analyze operands of the call, assigning locations to each operand. @@ -1841,14 +2035,14 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = CCInfo.getNextStackOffset(); - // For tail calls, memory operands are available in our caller's stack. - if (isSibCall) + if (isTailCall) { + // For tail calls, memory operands are available in our caller's stack. NumBytes = 0; - - // Adjust the stack pointer for the new arguments... - // These operations are automatically eliminated by the prolog/epilog pass - if (!isSibCall) + } else { + // Adjust the stack pointer for the new arguments... + // These operations are automatically eliminated by the prolog/epilog pass Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, dl); + } SDValue StackPtr = DAG.getCopyFromReg(Chain, dl, ARM::SP, getPointerTy(DAG.getDataLayout())); @@ -1970,7 +2164,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, MemOpChains.push_back(DAG.getNode(ARMISD::COPY_STRUCT_BYVAL, dl, VTs, Ops)); } - } else if (!isSibCall) { + } else if (!isTailCall) { assert(VA.isMemLoc()); MemOpChains.push_back(LowerMemOpCallTo(Chain, StackPtr, Arg, @@ -1984,32 +2178,10 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Build a sequence of copy-to-reg nodes chained together with token chain // and flag operands which copy the outgoing args into the appropriate regs. SDValue InFlag; - // Tail call byval lowering might overwrite argument registers so in case of - // tail call optimization the copies to registers are lowered later. - if (!isTailCall) - for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { - Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first, - RegsToPass[i].second, InFlag); - InFlag = Chain.getValue(1); - } - - // For tail calls lower the arguments to the 'real' stack slot. - if (isTailCall) { - // Force all the incoming stack arguments to be loaded from the stack - // before any new outgoing arguments are stored to the stack, because the - // outgoing stack slots may alias the incoming argument stack slots, and - // the alias isn't otherwise explicit. This is slightly more conservative - // than necessary, because it means that each store effectively depends - // on every argument instead of just those arguments it would clobber. - - // Do not flag preceding copytoreg stuff together with the following stuff. - InFlag = SDValue(); - for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { - Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first, - RegsToPass[i].second, InFlag); - InFlag = Chain.getValue(1); - } - InFlag = SDValue(); + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first, + RegsToPass[i].second, InFlag); + InFlag = Chain.getValue(1); } // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every @@ -2064,17 +2236,6 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, MachinePointerInfo::getConstantPool(DAG.getMachineFunction())); } } else if (isa<GlobalAddressSDNode>(Callee)) { - // If we're optimizing for minimum size and the function is called three or - // more times in this block, we can improve codesize by calling indirectly - // as BLXr has a 16-bit encoding. - auto *GV = cast<GlobalAddressSDNode>(Callee)->getGlobal(); - auto *BB = CLI.CS.getParent(); - bool PreferIndirect = - Subtarget->isThumb() && MF.getFunction().optForMinSize() && - count_if(GV->users(), [&BB](const User *U) { - return isa<Instruction>(U) && cast<Instruction>(U)->getParent() == BB; - }) > 2; - if (!PreferIndirect) { isDirect = true; bool isDef = GV->isStrongDefinitionForLinker(); @@ -2098,7 +2259,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, unsigned TargetFlags = GV->hasDLLImportStorageClass() ? ARMII::MO_DLLIMPORT : ARMII::MO_NO_FLAG; - Callee = DAG.getTargetGlobalAddress(GV, dl, PtrVt, /*Offset=*/0, + Callee = DAG.getTargetGlobalAddress(GV, dl, PtrVt, /*offset=*/0, TargetFlags); if (GV->hasDLLImportStorageClass()) Callee = @@ -2142,7 +2303,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, CallOpc = ARMISD::CALL_NOLINK; else if (doesNotRet && isDirect && Subtarget->hasRetAddrStack() && // Emit regular call when code size is the priority - !MF.getFunction().optForMinSize()) + !Subtarget->hasMinSize()) // "mov lr, pc; b _foo" to avoid confusing the RSP CallOpc = ARMISD::CALL_NOLINK; else @@ -2306,28 +2467,25 @@ bool MatchingStackOffset(SDValue Arg, unsigned Offset, ISD::ArgFlagsTy Flags, /// IsEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. Targets which want to do tail call /// optimization should implement this function. -bool -ARMTargetLowering::IsEligibleForTailCallOptimization(SDValue Callee, - CallingConv::ID CalleeCC, - bool isVarArg, - bool isCalleeStructRet, - bool isCallerStructRet, - const SmallVectorImpl<ISD::OutputArg> &Outs, - const SmallVectorImpl<SDValue> &OutVals, - const SmallVectorImpl<ISD::InputArg> &Ins, - SelectionDAG& DAG) const { +bool ARMTargetLowering::IsEligibleForTailCallOptimization( + SDValue Callee, CallingConv::ID CalleeCC, bool isVarArg, + bool isCalleeStructRet, bool isCallerStructRet, + const SmallVectorImpl<ISD::OutputArg> &Outs, + const SmallVectorImpl<SDValue> &OutVals, + const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG, + const bool isIndirect) const { MachineFunction &MF = DAG.getMachineFunction(); const Function &CallerF = MF.getFunction(); CallingConv::ID CallerCC = CallerF.getCallingConv(); assert(Subtarget->supportsTailCall()); - // Tail calls to function pointers cannot be optimized for Thumb1 if the args + // Indirect tail calls cannot be optimized for Thumb1 if the args // to the call take up r0-r3. The reason is that there are no legal registers // left to hold the pointer to the function to be called. if (Subtarget->isThumb1Only() && Outs.size() >= 4 && - !isa<GlobalAddressSDNode>(Callee.getNode())) - return false; + (!isa<GlobalAddressSDNode>(Callee.getNode()) || isIndirect)) + return false; // Look for obvious safe cases to perform tail call optimization that do not // require ABI changes. This is what gcc calls sibcall. @@ -2756,7 +2914,7 @@ SDValue ARMTargetLowering::LowerConstantPool(SDValue Op, auto M = const_cast<Module*>(DAG.getMachineFunction(). getFunction().getParent()); auto GV = new GlobalVariable( - *M, T, /*isConst=*/true, GlobalVariable::InternalLinkage, C, + *M, T, /*isConstant=*/true, GlobalVariable::InternalLinkage, C, Twine(DAG.getDataLayout().getPrivateGlobalPrefix()) + "CP" + Twine(DAG.getMachineFunction().getFunctionNumber()) + "_" + Twine(AFI->createPICLabelUId()) @@ -3225,7 +3383,7 @@ SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op, } else if (Subtarget->isRWPI() && !IsRO) { // SB-relative. SDValue RelAddr; - if (Subtarget->useMovt(DAG.getMachineFunction())) { + if (Subtarget->useMovt()) { ++NumMovwMovt; SDValue G = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, ARMII::MO_SBREL); RelAddr = DAG.getNode(ARMISD::Wrapper, dl, PtrVT, G); @@ -3245,7 +3403,7 @@ SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op, // If we have T2 ops, we can materialize the address directly via movt/movw // pair. This is always cheaper. - if (Subtarget->useMovt(DAG.getMachineFunction())) { + if (Subtarget->useMovt()) { ++NumMovwMovt; // FIXME: Once remat is capable of dealing with instructions with register // operands, expand this into two nodes. @@ -3268,7 +3426,7 @@ SDValue ARMTargetLowering::LowerGlobalAddressDarwin(SDValue Op, SDLoc dl(Op); const GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); - if (Subtarget->useMovt(DAG.getMachineFunction())) + if (Subtarget->useMovt()) ++NumMovwMovt; // FIXME: Once remat is capable of dealing with instructions with register @@ -3288,7 +3446,7 @@ SDValue ARMTargetLowering::LowerGlobalAddressDarwin(SDValue Op, SDValue ARMTargetLowering::LowerGlobalAddressWindows(SDValue Op, SelectionDAG &DAG) const { assert(Subtarget->isTargetWindows() && "non-Windows COFF is not supported"); - assert(Subtarget->useMovt(DAG.getMachineFunction()) && + assert(Subtarget->useMovt() && "Windows on ARM expects to use movw/movt"); assert(!Subtarget->isROPI() && !Subtarget->isRWPI() && "ROPI/RWPI not currently supported for Windows"); @@ -3309,7 +3467,7 @@ SDValue ARMTargetLowering::LowerGlobalAddressWindows(SDValue Op, // FIXME: Once remat is capable of dealing with instructions with register // operands, expand this into two nodes. Result = DAG.getNode(ARMISD::Wrapper, DL, PtrVT, - DAG.getTargetGlobalAddress(GV, DL, PtrVT, /*Offset=*/0, + DAG.getTargetGlobalAddress(GV, DL, PtrVT, /*offset=*/0, TargetFlags)); if (TargetFlags & (ARMII::MO_DLLIMPORT | ARMII::MO_COFFSTUB)) Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, @@ -3615,7 +3773,8 @@ void ARMTargetLowering::VarArgStyleRegisters(CCState &CCInfo, SelectionDAG &DAG, // argument passed via stack. int FrameIndex = StoreByValRegs(CCInfo, DAG, dl, Chain, nullptr, CCInfo.getInRegsParamsCount(), - CCInfo.getNextStackOffset(), 4); + CCInfo.getNextStackOffset(), + std::max(4U, TotalArgRegsSaveSize)); AFI->setVarArgsFrameIndex(FrameIndex); } @@ -3891,6 +4050,22 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, } ARMCC::CondCodes CondCode = IntCCToARMCC(CC); + + // If the RHS is a constant zero then the V (overflow) flag will never be + // set. This can allow us to simplify GE to PL or LT to MI, which can be + // simpler for other passes (like the peephole optimiser) to deal with. + if (isNullConstant(RHS)) { + switch (CondCode) { + default: break; + case ARMCC::GE: + CondCode = ARMCC::PL; + break; + case ARMCC::LT: + CondCode = ARMCC::MI; + break; + } + } + ARMISD::NodeType CompareType; switch (CondCode) { default: @@ -3910,7 +4085,7 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, SDValue ARMTargetLowering::getVFPCmp(SDValue LHS, SDValue RHS, SelectionDAG &DAG, const SDLoc &dl, bool InvalidOnQNaN) const { - assert(!Subtarget->isFPOnlySP() || RHS.getValueType() != MVT::f64); + assert(Subtarget->hasFP64() || RHS.getValueType() != MVT::f64); SDValue Cmp; SDValue C = DAG.getConstant(InvalidOnQNaN, dl, MVT::i32); if (!isFloatingPointZero(RHS)) @@ -4175,18 +4350,18 @@ static void checkVSELConstraints(ISD::CondCode CC, ARMCC::CondCodes &CondCode, // Start by selecting the GE condition code for opcodes that return true for // 'equality' if (CC == ISD::SETUGE || CC == ISD::SETOGE || CC == ISD::SETOLE || - CC == ISD::SETULE) + CC == ISD::SETULE || CC == ISD::SETGE || CC == ISD::SETLE) CondCode = ARMCC::GE; // and GT for opcodes that return false for 'equality'. else if (CC == ISD::SETUGT || CC == ISD::SETOGT || CC == ISD::SETOLT || - CC == ISD::SETULT) + CC == ISD::SETULT || CC == ISD::SETGT || CC == ISD::SETLT) CondCode = ARMCC::GT; // Since we are constrained to GE/GT, if the opcode contains 'less', we need // to swap the compare operands. if (CC == ISD::SETOLE || CC == ISD::SETULE || CC == ISD::SETOLT || - CC == ISD::SETULT) + CC == ISD::SETULT || CC == ISD::SETLE || CC == ISD::SETLT) swpCmpOps = true; // Both GT and GE are ordered comparisons, and return false for 'unordered'. @@ -4212,8 +4387,9 @@ static void checkVSELConstraints(ISD::CondCode CC, ARMCC::CondCodes &CondCode, } // 'unordered or not equal' is 'anything but equal', so use the EQ condition - // code and swap the VSEL operands. - if (CC == ISD::SETUNE) { + // code and swap the VSEL operands. Also do this if we don't care about the + // unordered case. + if (CC == ISD::SETUNE || CC == ISD::SETNE) { CondCode = ARMCC::EQ; swpVselOps = true; } @@ -4222,7 +4398,7 @@ static void checkVSELConstraints(ISD::CondCode CC, ARMCC::CondCodes &CondCode, SDValue ARMTargetLowering::getCMOV(const SDLoc &dl, EVT VT, SDValue FalseVal, SDValue TrueVal, SDValue ARMcc, SDValue CCR, SDValue Cmp, SelectionDAG &DAG) const { - if (Subtarget->isFPOnlySP() && VT == MVT::f64) { + if (!Subtarget->hasFP64() && VT == MVT::f64) { FalseVal = DAG.getNode(ARMISD::VMOVRRD, dl, DAG.getVTList(MVT::i32, MVT::i32), FalseVal); TrueVal = DAG.getNode(ARMISD::VMOVRRD, dl, @@ -4428,6 +4604,16 @@ static bool isLowerSaturatingConditional(const SDValue &Op, SDValue &V, return false; } +bool ARMTargetLowering::isUnsupportedFloatingType(EVT VT) const { + if (VT == MVT::f32) + return !Subtarget->hasVFP2Base(); + if (VT == MVT::f64) + return !Subtarget->hasFP64(); + if (VT == MVT::f16) + return !Subtarget->hasFullFP16(); + return false; +} + SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { EVT VT = Op.getValueType(); SDLoc dl(Op); @@ -4471,9 +4657,9 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { SDValue TrueVal = Op.getOperand(2); SDValue FalseVal = Op.getOperand(3); - if (Subtarget->isFPOnlySP() && LHS.getValueType() == MVT::f64) { - DAG.getTargetLoweringInfo().softenSetCCOperands(DAG, MVT::f64, LHS, RHS, CC, - dl); + if (isUnsupportedFloatingType(LHS.getValueType())) { + DAG.getTargetLoweringInfo().softenSetCCOperands( + DAG, LHS.getValueType(), LHS, RHS, CC, dl); // If softenSetCCOperands only returned one value, we should compare it to // zero. @@ -4494,8 +4680,9 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { // inverting the compare condition, swapping 'less' and 'greater') and // sometimes need to swap the operands to the VSEL (which inverts the // condition in the sense of firing whenever the previous condition didn't) - if (Subtarget->hasFPARMv8() && (TrueVal.getValueType() == MVT::f32 || - TrueVal.getValueType() == MVT::f64)) { + if (Subtarget->hasFPARMv8Base() && (TrueVal.getValueType() == MVT::f16 || + TrueVal.getValueType() == MVT::f32 || + TrueVal.getValueType() == MVT::f64)) { ARMCC::CondCodes CondCode = IntCCToARMCC(CC); if (CondCode == ARMCC::LT || CondCode == ARMCC::LE || CondCode == ARMCC::VC || CondCode == ARMCC::NE) { @@ -4507,6 +4694,9 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { SDValue ARMcc; SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32); SDValue Cmp = getARMCmp(LHS, RHS, CC, ARMcc, DAG, dl); + // Choose GE over PL, which vsel does now support + if (cast<ConstantSDNode>(ARMcc)->getZExtValue() == ARMCC::PL) + ARMcc = DAG.getConstant(ARMCC::GE, dl, MVT::i32); return getCMOV(dl, VT, FalseVal, TrueVal, ARMcc, CCR, Cmp, DAG); } @@ -4514,12 +4704,15 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { bool InvalidOnQNaN; FPCCToARMCC(CC, CondCode, CondCode2, InvalidOnQNaN); - // Normalize the fp compare. If RHS is zero we keep it there so we match - // CMPFPw0 instead of CMPFP. - if (Subtarget->hasFPARMv8() && !isFloatingPointZero(RHS) && - (TrueVal.getValueType() == MVT::f16 || - TrueVal.getValueType() == MVT::f32 || - TrueVal.getValueType() == MVT::f64)) { + // Normalize the fp compare. If RHS is zero we prefer to keep it there so we + // match CMPFPw0 instead of CMPFP, though we don't do this for f16 because we + // must use VSEL (limited condition codes), due to not having conditional f16 + // moves. + if (Subtarget->hasFPARMv8Base() && + !(isFloatingPointZero(RHS) && TrueVal.getValueType() != MVT::f16) && + (TrueVal.getValueType() == MVT::f16 || + TrueVal.getValueType() == MVT::f32 || + TrueVal.getValueType() == MVT::f64)) { bool swpCmpOps = false; bool swpVselOps = false; checkVSELConstraints(CC, CondCode, swpCmpOps, swpVselOps); @@ -4708,9 +4901,9 @@ SDValue ARMTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { SDValue Dest = Op.getOperand(4); SDLoc dl(Op); - if (Subtarget->isFPOnlySP() && LHS.getValueType() == MVT::f64) { - DAG.getTargetLoweringInfo().softenSetCCOperands(DAG, MVT::f64, LHS, RHS, CC, - dl); + if (isUnsupportedFloatingType(LHS.getValueType())) { + DAG.getTargetLoweringInfo().softenSetCCOperands( + DAG, LHS.getValueType(), LHS, RHS, CC, dl); // If softenSetCCOperands only returned one value, we should compare it to // zero. @@ -4855,7 +5048,7 @@ SDValue ARMTargetLowering::LowerFP_TO_INT(SDValue Op, SelectionDAG &DAG) const { EVT VT = Op.getValueType(); if (VT.isVector()) return LowerVectorFP_TO_INT(Op, DAG); - if (Subtarget->isFPOnlySP() && Op.getOperand(0).getValueType() == MVT::f64) { + if (isUnsupportedFloatingType(Op.getOperand(0).getValueType())) { RTLIB::Libcall LC; if (Op.getOpcode() == ISD::FP_TO_SINT) LC = RTLIB::getFPTOSINT(Op.getOperand(0).getValueType(), @@ -4919,7 +5112,7 @@ SDValue ARMTargetLowering::LowerINT_TO_FP(SDValue Op, SelectionDAG &DAG) const { EVT VT = Op.getValueType(); if (VT.isVector()) return LowerVectorINT_TO_FP(Op, DAG); - if (Subtarget->isFPOnlySP() && Op.getValueType() == MVT::f64) { + if (isUnsupportedFloatingType(VT)) { RTLIB::Libcall LC; if (Op.getOpcode() == ISD::SINT_TO_FP) LC = RTLIB::getSINTTOFP(Op.getOperand(0).getValueType(), @@ -4952,7 +5145,7 @@ SDValue ARMTargetLowering::LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const { DAG.getTargetConstant(EncodedVal, dl, MVT::i32)); EVT OpVT = (VT == MVT::f32) ? MVT::v2i32 : MVT::v1i64; if (VT == MVT::f64) - Mask = DAG.getNode(ARMISD::VSHL, dl, OpVT, + Mask = DAG.getNode(ARMISD::VSHLIMM, dl, OpVT, DAG.getNode(ISD::BITCAST, dl, OpVT, Mask), DAG.getConstant(32, dl, MVT::i32)); else /*if (VT == MVT::f32)*/ @@ -4960,11 +5153,11 @@ SDValue ARMTargetLowering::LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const { if (SrcVT == MVT::f32) { Tmp1 = DAG.getNode(ISD::SCALAR_TO_VECTOR, dl, MVT::v2f32, Tmp1); if (VT == MVT::f64) - Tmp1 = DAG.getNode(ARMISD::VSHL, dl, OpVT, + Tmp1 = DAG.getNode(ARMISD::VSHLIMM, dl, OpVT, DAG.getNode(ISD::BITCAST, dl, OpVT, Tmp1), DAG.getConstant(32, dl, MVT::i32)); } else if (VT == MVT::f32) - Tmp1 = DAG.getNode(ARMISD::VSHRu, dl, MVT::v1i64, + Tmp1 = DAG.getNode(ARMISD::VSHRuIMM, dl, MVT::v1i64, DAG.getNode(ISD::BITCAST, dl, MVT::v1i64, Tmp1), DAG.getConstant(32, dl, MVT::i32)); Tmp0 = DAG.getNode(ISD::BITCAST, dl, OpVT, Tmp0); @@ -5469,40 +5662,100 @@ static SDValue LowerCTPOP(SDNode *N, SelectionDAG &DAG, return Res; } +/// Getvshiftimm - Check if this is a valid build_vector for the immediate +/// operand of a vector shift operation, where all the elements of the +/// build_vector must have the same constant integer value. +static bool getVShiftImm(SDValue Op, unsigned ElementBits, int64_t &Cnt) { + // Ignore bit_converts. + while (Op.getOpcode() == ISD::BITCAST) + Op = Op.getOperand(0); + BuildVectorSDNode *BVN = dyn_cast<BuildVectorSDNode>(Op.getNode()); + APInt SplatBits, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + if (!BVN || + !BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize, HasAnyUndefs, + ElementBits) || + SplatBitSize > ElementBits) + return false; + Cnt = SplatBits.getSExtValue(); + return true; +} + +/// isVShiftLImm - Check if this is a valid build_vector for the immediate +/// operand of a vector shift left operation. That value must be in the range: +/// 0 <= Value < ElementBits for a left shift; or +/// 0 <= Value <= ElementBits for a long left shift. +static bool isVShiftLImm(SDValue Op, EVT VT, bool isLong, int64_t &Cnt) { + assert(VT.isVector() && "vector shift count is not a vector type"); + int64_t ElementBits = VT.getScalarSizeInBits(); + if (!getVShiftImm(Op, ElementBits, Cnt)) + return false; + return (Cnt >= 0 && (isLong ? Cnt - 1 : Cnt) < ElementBits); +} + +/// isVShiftRImm - Check if this is a valid build_vector for the immediate +/// operand of a vector shift right operation. For a shift opcode, the value +/// is positive, but for an intrinsic the value count must be negative. The +/// absolute value must be in the range: +/// 1 <= |Value| <= ElementBits for a right shift; or +/// 1 <= |Value| <= ElementBits/2 for a narrow right shift. +static bool isVShiftRImm(SDValue Op, EVT VT, bool isNarrow, bool isIntrinsic, + int64_t &Cnt) { + assert(VT.isVector() && "vector shift count is not a vector type"); + int64_t ElementBits = VT.getScalarSizeInBits(); + if (!getVShiftImm(Op, ElementBits, Cnt)) + return false; + if (!isIntrinsic) + return (Cnt >= 1 && Cnt <= (isNarrow ? ElementBits / 2 : ElementBits)); + if (Cnt >= -(isNarrow ? ElementBits / 2 : ElementBits) && Cnt <= -1) { + Cnt = -Cnt; + return true; + } + return false; +} + static SDValue LowerShift(SDNode *N, SelectionDAG &DAG, const ARMSubtarget *ST) { EVT VT = N->getValueType(0); SDLoc dl(N); + int64_t Cnt; if (!VT.isVector()) return SDValue(); - // Lower vector shifts on NEON to use VSHL. - assert(ST->hasNEON() && "unexpected vector shift"); + // We essentially have two forms here. Shift by an immediate and shift by a + // vector register (there are also shift by a gpr, but that is just handled + // with a tablegen pattern). We cannot easily match shift by an immediate in + // tablegen so we do that here and generate a VSHLIMM/VSHRsIMM/VSHRuIMM. + // For shifting by a vector, we don't have VSHR, only VSHL (which can be + // signed or unsigned, and a negative shift indicates a shift right). + if (N->getOpcode() == ISD::SHL) { + if (isVShiftLImm(N->getOperand(1), VT, false, Cnt)) + return DAG.getNode(ARMISD::VSHLIMM, dl, VT, N->getOperand(0), + DAG.getConstant(Cnt, dl, MVT::i32)); + return DAG.getNode(ARMISD::VSHLu, dl, VT, N->getOperand(0), + N->getOperand(1)); + } - // Left shifts translate directly to the vshiftu intrinsic. - if (N->getOpcode() == ISD::SHL) - return DAG.getNode(ISD::INTRINSIC_WO_CHAIN, dl, VT, - DAG.getConstant(Intrinsic::arm_neon_vshiftu, dl, - MVT::i32), - N->getOperand(0), N->getOperand(1)); + assert((N->getOpcode() == ISD::SRA || N->getOpcode() == ISD::SRL) && + "unexpected vector shift opcode"); - assert((N->getOpcode() == ISD::SRA || - N->getOpcode() == ISD::SRL) && "unexpected vector shift opcode"); + if (isVShiftRImm(N->getOperand(1), VT, false, false, Cnt)) { + unsigned VShiftOpc = + (N->getOpcode() == ISD::SRA ? ARMISD::VSHRsIMM : ARMISD::VSHRuIMM); + return DAG.getNode(VShiftOpc, dl, VT, N->getOperand(0), + DAG.getConstant(Cnt, dl, MVT::i32)); + } - // NEON uses the same intrinsics for both left and right shifts. For - // right shifts, the shift amounts are negative, so negate the vector of - // shift amounts. + // Other right shifts we don't have operations for (we use a shift left by a + // negative number). EVT ShiftVT = N->getOperand(1).getValueType(); - SDValue NegatedCount = DAG.getNode(ISD::SUB, dl, ShiftVT, - getZeroVector(ShiftVT, DAG, dl), - N->getOperand(1)); - Intrinsic::ID vshiftInt = (N->getOpcode() == ISD::SRA ? - Intrinsic::arm_neon_vshifts : - Intrinsic::arm_neon_vshiftu); - return DAG.getNode(ISD::INTRINSIC_WO_CHAIN, dl, VT, - DAG.getConstant(vshiftInt, dl, MVT::i32), - N->getOperand(0), NegatedCount); + SDValue NegatedCount = DAG.getNode( + ISD::SUB, dl, ShiftVT, getZeroVector(ShiftVT, DAG, dl), N->getOperand(1)); + unsigned VShiftOpc = + (N->getOpcode() == ISD::SRA ? ARMISD::VSHLs : ARMISD::VSHLu); + return DAG.getNode(VShiftOpc, dl, VT, N->getOperand(0), NegatedCount); } static SDValue Expand64BitShift(SDNode *N, SelectionDAG &DAG, @@ -5514,15 +5767,59 @@ static SDValue Expand64BitShift(SDNode *N, SelectionDAG &DAG, if (VT != MVT::i64) return SDValue(); - assert((N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) && + assert((N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA || + N->getOpcode() == ISD::SHL) && "Unknown shift to lower!"); + unsigned ShOpc = N->getOpcode(); + if (ST->hasMVEIntegerOps()) { + SDValue ShAmt = N->getOperand(1); + unsigned ShPartsOpc = ARMISD::LSLL; + ConstantSDNode *Con = dyn_cast<ConstantSDNode>(ShAmt); + + // If the shift amount is greater than 32 then do the default optimisation + if (Con && Con->getZExtValue() > 32) + return SDValue(); + + // Extract the lower 32 bits of the shift amount if it's an i64 + if (ShAmt->getValueType(0) == MVT::i64) + ShAmt = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, ShAmt, + DAG.getConstant(0, dl, MVT::i32)); + + if (ShOpc == ISD::SRL) { + if (!Con) + // There is no t2LSRLr instruction so negate and perform an lsll if the + // shift amount is in a register, emulating a right shift. + ShAmt = DAG.getNode(ISD::SUB, dl, MVT::i32, + DAG.getConstant(0, dl, MVT::i32), ShAmt); + else + // Else generate an lsrl on the immediate shift amount + ShPartsOpc = ARMISD::LSRL; + } else if (ShOpc == ISD::SRA) + ShPartsOpc = ARMISD::ASRL; + + // Lower 32 bits of the destination/source + SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0), + DAG.getConstant(0, dl, MVT::i32)); + // Upper 32 bits of the destination/source + SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0), + DAG.getConstant(1, dl, MVT::i32)); + + // Generate the shift operation as computed above + Lo = DAG.getNode(ShPartsOpc, dl, DAG.getVTList(MVT::i32, MVT::i32), Lo, Hi, + ShAmt); + // The upper 32 bits come from the second return value of lsll + Hi = SDValue(Lo.getNode(), 1); + return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Lo, Hi); + } + // We only lower SRA, SRL of 1 here, all others use generic lowering. - if (!isOneConstant(N->getOperand(1))) + if (!isOneConstant(N->getOperand(1)) || N->getOpcode() == ISD::SHL) return SDValue(); // If we are in thumb mode, we don't have RRX. - if (ST->isThumb1Only()) return SDValue(); + if (ST->isThumb1Only()) + return SDValue(); // Okay, we have a 64-bit SRA or SRL of 1. Lower this to an RRX expr. SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, MVT::i32, N->getOperand(0), @@ -5731,7 +6028,7 @@ static SDValue LowerSETCCCARRY(SDValue Op, SelectionDAG &DAG) { } /// isNEONModifiedImm - Check if the specified splat value corresponds to a -/// valid vector constant for a NEON instruction with a "modified immediate" +/// valid vector constant for a NEON or MVE instruction with a "modified immediate" /// operand (e.g., VMOV). If so, return the encoded value. static SDValue isNEONModifiedImm(uint64_t SplatBits, uint64_t SplatUndef, unsigned SplatBitSize, SelectionDAG &DAG, @@ -5817,6 +6114,10 @@ static SDValue isNEONModifiedImm(uint64_t SplatBits, uint64_t SplatUndef, break; } + // cmode == 0b1101 is not supported for MVE VMVN + if (type == MVEVMVNModImm) + return SDValue(); + if ((SplatBits & ~0xffffff) == 0 && ((SplatBits | SplatUndef) & 0xffff) == 0xffff) { // Value = 0x00nnffff: Op=x, Cmode=1101. @@ -5902,12 +6203,12 @@ SDValue ARMTargetLowering::LowerConstantFP(SDValue Op, SelectionDAG &DAG, } } - if (!ST->hasVFP3()) + if (!ST->hasVFP3Base()) return SDValue(); // Use the default (constant pool) lowering for double constants when we have // an SP-only FPU - if (IsDouble && Subtarget->isFPOnlySP()) + if (IsDouble && !Subtarget->hasFP64()) return SDValue(); // Try splatting with a VMOV.f32... @@ -6383,13 +6684,15 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG, if (SplatUndef.isAllOnesValue()) return DAG.getUNDEF(VT); - if (SplatBitSize <= 64) { + if ((ST->hasNEON() && SplatBitSize <= 64) || + (ST->hasMVEIntegerOps() && SplatBitSize <= 32)) { // Check if an immediate VMOV works. EVT VmovVT; SDValue Val = isNEONModifiedImm(SplatBits.getZExtValue(), SplatUndef.getZExtValue(), SplatBitSize, DAG, dl, VmovVT, VT.is128BitVector(), VMOVModImm); + if (Val.getNode()) { SDValue Vmov = DAG.getNode(ARMISD::VMOVIMM, dl, VmovVT, Val); return DAG.getNode(ISD::BITCAST, dl, VT, Vmov); @@ -6397,10 +6700,10 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG, // Try an immediate VMVN. uint64_t NegatedImm = (~SplatBits).getZExtValue(); - Val = isNEONModifiedImm(NegatedImm, - SplatUndef.getZExtValue(), SplatBitSize, - DAG, dl, VmovVT, VT.is128BitVector(), - VMVNModImm); + Val = isNEONModifiedImm( + NegatedImm, SplatUndef.getZExtValue(), SplatBitSize, + DAG, dl, VmovVT, VT.is128BitVector(), + ST->hasMVEIntegerOps() ? MVEVMVNModImm : VMVNModImm); if (Val.getNode()) { SDValue Vmov = DAG.getNode(ARMISD::VMVNIMM, dl, VmovVT, Val); return DAG.getNode(ISD::BITCAST, dl, VT, Vmov); @@ -6515,10 +6818,13 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG, } if (VT.getVectorElementType().isFloatingPoint()) { SmallVector<SDValue, 8> Ops; + MVT FVT = VT.getVectorElementType().getSimpleVT(); + assert(FVT == MVT::f32 || FVT == MVT::f16); + MVT IVT = (FVT == MVT::f32) ? MVT::i32 : MVT::i16; for (unsigned i = 0; i < NumElts; ++i) - Ops.push_back(DAG.getNode(ISD::BITCAST, dl, MVT::i32, + Ops.push_back(DAG.getNode(ISD::BITCAST, dl, IVT, Op.getOperand(i))); - EVT VecVT = EVT::getVectorVT(*DAG.getContext(), MVT::i32, NumElts); + EVT VecVT = EVT::getVectorVT(*DAG.getContext(), IVT, NumElts); SDValue Val = DAG.getBuildVector(VecVT, dl, Ops); Val = LowerBUILD_VECTOR(Val, DAG, ST); if (Val.getNode()) @@ -6544,7 +6850,7 @@ SDValue ARMTargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG, return shuffle; } - if (VT.is128BitVector() && VT != MVT::v2f64 && VT != MVT::v4f32) { + if (ST->hasNEON() && VT.is128BitVector() && VT != MVT::v2f64 && VT != MVT::v4f32) { // If we haven't found an efficient lowering, try splitting a 128-bit vector // into two 64-bit vectors; we might discover a better way to lower it. SmallVector<SDValue, 64> Ops(Op->op_begin(), Op->op_begin() + NumElts); @@ -6799,6 +7105,38 @@ SDValue ARMTargetLowering::ReconstructShuffle(SDValue Op, return DAG.getNode(ISD::BITCAST, dl, VT, Shuffle); } +enum ShuffleOpCodes { + OP_COPY = 0, // Copy, used for things like <u,u,u,3> to say it is <0,1,2,3> + OP_VREV, + OP_VDUP0, + OP_VDUP1, + OP_VDUP2, + OP_VDUP3, + OP_VEXT1, + OP_VEXT2, + OP_VEXT3, + OP_VUZPL, // VUZP, left result + OP_VUZPR, // VUZP, right result + OP_VZIPL, // VZIP, left result + OP_VZIPR, // VZIP, right result + OP_VTRNL, // VTRN, left result + OP_VTRNR // VTRN, right result +}; + +static bool isLegalMVEShuffleOp(unsigned PFEntry) { + unsigned OpNum = (PFEntry >> 26) & 0x0F; + switch (OpNum) { + case OP_COPY: + case OP_VREV: + case OP_VDUP0: + case OP_VDUP1: + case OP_VDUP2: + case OP_VDUP3: + return true; + } + return false; +} + /// isShuffleMaskLegal - Targets can use this to indicate that they only /// support *some* VECTOR_SHUFFLE operations, those with specific masks. /// By default, if a target supports the VECTOR_SHUFFLE node, all mask values @@ -6820,7 +7158,7 @@ bool ARMTargetLowering::isShuffleMaskLegal(ArrayRef<int> M, EVT VT) const { unsigned PFEntry = PerfectShuffleTable[PFTableIndex]; unsigned Cost = (PFEntry >> 30); - if (Cost <= 4) + if (Cost <= 4 && (Subtarget->hasNEON() || isLegalMVEShuffleOp(PFEntry))) return true; } @@ -6828,15 +7166,22 @@ bool ARMTargetLowering::isShuffleMaskLegal(ArrayRef<int> M, EVT VT) const { unsigned Imm, WhichResult; unsigned EltSize = VT.getScalarSizeInBits(); - return (EltSize >= 32 || - ShuffleVectorSDNode::isSplatMask(&M[0], VT) || - isVREVMask(M, VT, 64) || - isVREVMask(M, VT, 32) || - isVREVMask(M, VT, 16) || - isVEXTMask(M, VT, ReverseVEXT, Imm) || - isVTBLMask(M, VT) || - isNEONTwoResultShuffleMask(M, VT, WhichResult, isV_UNDEF) || - ((VT == MVT::v8i16 || VT == MVT::v16i8) && isReverseMask(M, VT))); + if (EltSize >= 32 || + ShuffleVectorSDNode::isSplatMask(&M[0], VT) || + isVREVMask(M, VT, 64) || + isVREVMask(M, VT, 32) || + isVREVMask(M, VT, 16)) + return true; + else if (Subtarget->hasNEON() && + (isVEXTMask(M, VT, ReverseVEXT, Imm) || + isVTBLMask(M, VT) || + isNEONTwoResultShuffleMask(M, VT, WhichResult, isV_UNDEF))) + return true; + else if (Subtarget->hasNEON() && (VT == MVT::v8i16 || VT == MVT::v16i8) && + isReverseMask(M, VT)) + return true; + else + return false; } /// GeneratePerfectShuffle - Given an entry in the perfect-shuffle table, emit @@ -6848,24 +7193,6 @@ static SDValue GeneratePerfectShuffle(unsigned PFEntry, SDValue LHS, unsigned LHSID = (PFEntry >> 13) & ((1 << 13)-1); unsigned RHSID = (PFEntry >> 0) & ((1 << 13)-1); - enum { - OP_COPY = 0, // Copy, used for things like <u,u,u,3> to say it is <0,1,2,3> - OP_VREV, - OP_VDUP0, - OP_VDUP1, - OP_VDUP2, - OP_VDUP3, - OP_VEXT1, - OP_VEXT2, - OP_VEXT3, - OP_VUZPL, // VUZP, left result - OP_VUZPR, // VUZP, right result - OP_VZIPL, // VZIP, left result - OP_VZIPR, // VZIP, right result - OP_VTRNL, // VTRN, left result - OP_VTRNR // VTRN, right result - }; - if (OpNum == OP_COPY) { if (LHSID == (1*9+2)*9+3) return LHS; assert(LHSID == ((4*9+5)*9+6)*9+7 && "Illegal OP_COPY!"); @@ -6955,7 +7282,8 @@ static SDValue LowerReverse_VECTOR_SHUFFLEv16i8_v8i16(SDValue Op, DAG.getConstant(ExtractNum, DL, MVT::i32)); } -static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { +static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG, + const ARMSubtarget *ST) { SDValue V1 = Op.getOperand(0); SDValue V2 = Op.getOperand(1); SDLoc dl(Op); @@ -6999,9 +7327,9 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { DAG.getConstant(Lane, dl, MVT::i32)); } - bool ReverseVEXT; - unsigned Imm; - if (isVEXTMask(ShuffleMask, VT, ReverseVEXT, Imm)) { + bool ReverseVEXT = false; + unsigned Imm = 0; + if (ST->hasNEON() && isVEXTMask(ShuffleMask, VT, ReverseVEXT, Imm)) { if (ReverseVEXT) std::swap(V1, V2); return DAG.getNode(ARMISD::VEXT, dl, VT, V1, V2, @@ -7015,7 +7343,7 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { if (isVREVMask(ShuffleMask, VT, 16)) return DAG.getNode(ARMISD::VREV16, dl, VT, V1); - if (V2->isUndef() && isSingletonVEXTMask(ShuffleMask, VT, Imm)) { + if (ST->hasNEON() && V2->isUndef() && isSingletonVEXTMask(ShuffleMask, VT, Imm)) { return DAG.getNode(ARMISD::VEXT, dl, VT, V1, V1, DAG.getConstant(Imm, dl, MVT::i32)); } @@ -7025,14 +7353,16 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { // source operands and with masks corresponding to both results of one of // these operations, DAG memoization will ensure that a single node is // used for both shuffles. - unsigned WhichResult; - bool isV_UNDEF; - if (unsigned ShuffleOpc = isNEONTwoResultShuffleMask( - ShuffleMask, VT, WhichResult, isV_UNDEF)) { - if (isV_UNDEF) - V2 = V1; - return DAG.getNode(ShuffleOpc, dl, DAG.getVTList(VT, VT), V1, V2) - .getValue(WhichResult); + unsigned WhichResult = 0; + bool isV_UNDEF = false; + if (ST->hasNEON()) { + if (unsigned ShuffleOpc = isNEONTwoResultShuffleMask( + ShuffleMask, VT, WhichResult, isV_UNDEF)) { + if (isV_UNDEF) + V2 = V1; + return DAG.getNode(ShuffleOpc, dl, DAG.getVTList(VT, VT), V1, V2) + .getValue(WhichResult); + } } // Also check for these shuffles through CONCAT_VECTORS: we canonicalize @@ -7050,7 +7380,7 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { // -> // concat(VZIP(v1, v2):0, :1) // - if (V1->getOpcode() == ISD::CONCAT_VECTORS && V2->isUndef()) { + if (ST->hasNEON() && V1->getOpcode() == ISD::CONCAT_VECTORS && V2->isUndef()) { SDValue SubV1 = V1->getOperand(0); SDValue SubV2 = V1->getOperand(1); EVT SubVT = SubV1.getValueType(); @@ -7092,8 +7422,18 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { unsigned PFEntry = PerfectShuffleTable[PFTableIndex]; unsigned Cost = (PFEntry >> 30); - if (Cost <= 4) - return GeneratePerfectShuffle(PFEntry, V1, V2, DAG, dl); + if (Cost <= 4) { + if (ST->hasNEON()) + return GeneratePerfectShuffle(PFEntry, V1, V2, DAG, dl); + else if (isLegalMVEShuffleOp(PFEntry)) { + unsigned LHSID = (PFEntry >> 13) & ((1 << 13)-1); + unsigned RHSID = (PFEntry >> 0) & ((1 << 13)-1); + unsigned PFEntryLHS = PerfectShuffleTable[LHSID]; + unsigned PFEntryRHS = PerfectShuffleTable[RHSID]; + if (isLegalMVEShuffleOp(PFEntryLHS) && isLegalMVEShuffleOp(PFEntryRHS)) + return GeneratePerfectShuffle(PFEntry, V1, V2, DAG, dl); + } + } } // Implement shuffles with 32- or 64-bit elements as ARMISD::BUILD_VECTORs. @@ -7118,22 +7458,50 @@ static SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) { return DAG.getNode(ISD::BITCAST, dl, VT, Val); } - if ((VT == MVT::v8i16 || VT == MVT::v16i8) && isReverseMask(ShuffleMask, VT)) + if (ST->hasNEON() && (VT == MVT::v8i16 || VT == MVT::v16i8) && isReverseMask(ShuffleMask, VT)) return LowerReverse_VECTOR_SHUFFLEv16i8_v8i16(Op, DAG); - if (VT == MVT::v8i8) + if (ST->hasNEON() && VT == MVT::v8i8) if (SDValue NewOp = LowerVECTOR_SHUFFLEv8i8(Op, ShuffleMask, DAG)) return NewOp; return SDValue(); } -static SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) { +SDValue ARMTargetLowering:: +LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const { // INSERT_VECTOR_ELT is legal only for immediate indexes. SDValue Lane = Op.getOperand(2); if (!isa<ConstantSDNode>(Lane)) return SDValue(); + SDValue Elt = Op.getOperand(1); + EVT EltVT = Elt.getValueType(); + if (getTypeAction(*DAG.getContext(), EltVT) == + TargetLowering::TypePromoteFloat) { + // INSERT_VECTOR_ELT doesn't want f16 operands promoting to f32, + // but the type system will try to do that if we don't intervene. + // Reinterpret any such vector-element insertion as one with the + // corresponding integer types. + + SDLoc dl(Op); + + EVT IEltVT = MVT::getIntegerVT(EltVT.getScalarSizeInBits()); + assert(getTypeAction(*DAG.getContext(), IEltVT) != + TargetLowering::TypePromoteFloat); + + SDValue VecIn = Op.getOperand(0); + EVT VecVT = VecIn.getValueType(); + EVT IVecVT = EVT::getVectorVT(*DAG.getContext(), IEltVT, + VecVT.getVectorNumElements()); + + SDValue IElt = DAG.getNode(ISD::BITCAST, dl, IEltVT, Elt); + SDValue IVecIn = DAG.getNode(ISD::BITCAST, dl, IVecVT, VecIn); + SDValue IVecOut = DAG.getNode(ISD::INSERT_VECTOR_ELT, dl, IVecVT, + IVecIn, IElt, Lane); + return DAG.getNode(ISD::BITCAST, dl, VecVT, IVecOut); + } + return Op; } @@ -7809,8 +8177,7 @@ ARMTargetLowering::BuildSDIVPow2(SDNode *N, const APInt &Divisor, return SDValue(); const auto &ST = static_cast<const ARMSubtarget&>(DAG.getSubtarget()); - const auto &MF = DAG.getMachineFunction(); - const bool MinSize = MF.getFunction().optForMinSize(); + const bool MinSize = ST.hasMinSize(); const bool HasDivide = ST.isThumb() ? ST.hasDivideInThumbMode() : ST.hasDivideInARMMode(); @@ -8063,7 +8430,7 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { case ISD::SETCCCARRY: return LowerSETCCCARRY(Op, DAG); case ISD::ConstantFP: return LowerConstantFP(Op, DAG, Subtarget); case ISD::BUILD_VECTOR: return LowerBUILD_VECTOR(Op, DAG, Subtarget); - case ISD::VECTOR_SHUFFLE: return LowerVECTOR_SHUFFLE(Op, DAG); + case ISD::VECTOR_SHUFFLE: return LowerVECTOR_SHUFFLE(Op, DAG, Subtarget); case ISD::INSERT_VECTOR_ELT: return LowerINSERT_VECTOR_ELT(Op, DAG); case ISD::EXTRACT_VECTOR_ELT: return LowerEXTRACT_VECTOR_ELT(Op, DAG); case ISD::CONCAT_VECTORS: return LowerCONCAT_VECTORS(Op, DAG); @@ -8149,6 +8516,7 @@ void ARMTargetLowering::ReplaceNodeResults(SDNode *N, break; case ISD::SRL: case ISD::SRA: + case ISD::SHL: Res = Expand64BitShift(N, DAG, Subtarget); break; case ISD::SREM: @@ -8175,6 +8543,10 @@ void ARMTargetLowering::ReplaceNodeResults(SDNode *N, return; case ISD::INTRINSIC_WO_CHAIN: return ReplaceLongIntrinsic(N, Results, DAG); + case ISD::ABS: + lowerABS(N, Results, DAG); + return ; + } if (Res.getNode()) Results.push_back(Res); @@ -8980,7 +9352,7 @@ ARMTargetLowering::EmitStructByval(MachineInstr &MI, // Load an immediate to varEnd. unsigned varEnd = MRI.createVirtualRegister(TRC); - if (Subtarget->useMovt(*MF)) { + if (Subtarget->useMovt()) { unsigned Vtmp = varEnd; if ((LoopSize & 0xFFFF0000) != 0) Vtmp = MRI.createVirtualRegister(TRC); @@ -9003,18 +9375,23 @@ ARMTargetLowering::EmitStructByval(MachineInstr &MI, if (Align == 0) Align = MF->getDataLayout().getTypeAllocSize(C->getType()); unsigned Idx = ConstantPool->getConstantPoolIndex(C, Align); + MachineMemOperand *CPMMO = + MF->getMachineMemOperand(MachinePointerInfo::getConstantPool(*MF), + MachineMemOperand::MOLoad, 4, 4); if (IsThumb) BuildMI(*BB, MI, dl, TII->get(ARM::tLDRpci)) .addReg(varEnd, RegState::Define) .addConstantPoolIndex(Idx) - .add(predOps(ARMCC::AL)); + .add(predOps(ARMCC::AL)) + .addMemOperand(CPMMO); else BuildMI(*BB, MI, dl, TII->get(ARM::LDRcp)) .addReg(varEnd, RegState::Define) .addConstantPoolIndex(Idx) .addImm(0) - .add(predOps(ARMCC::AL)); + .add(predOps(ARMCC::AL)) + .addMemOperand(CPMMO); } BB->addSuccessor(loopMBB); @@ -9262,7 +9639,8 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, .add(MI.getOperand(2)) // Rn .add(MI.getOperand(3)) // PredImm .add(MI.getOperand(4)) // PredReg - .add(MI.getOperand(0)); // Rt + .add(MI.getOperand(0)) // Rt + .cloneMemRefs(MI); MI.eraseFromParent(); return BB; } @@ -10372,6 +10750,22 @@ static SDValue PerformAddeSubeCombine(SDNode *N, return SDValue(); } +static SDValue PerformABSCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI, + const ARMSubtarget *Subtarget) { + SDValue res; + SelectionDAG &DAG = DCI.DAG; + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + + if (TLI.isOperationLegal(N->getOpcode(), N->getValueType(0))) + return SDValue(); + + if (!TLI.expandABS(N, res, DAG)) + return SDValue(); + + return res; +} + /// PerformADDECombine - Target-specific dag combine transform from /// ARMISD::ADDC, ARMISD::ADDE, and ISD::MUL_LOHI to MLAL or /// ARMISD::ADDC, ARMISD::ADDE and ARMISD::UMLAL to ARMISD::UMAAL @@ -10419,11 +10813,28 @@ ARMTargetLowering::isDesirableToCommuteWithShift(const SDNode *N, if (Level == BeforeLegalizeTypes) return true; - if (Subtarget->isThumb() && Subtarget->isThumb1Only()) + if (N->getOpcode() != ISD::SHL) return true; - if (N->getOpcode() != ISD::SHL) + if (Subtarget->isThumb1Only()) { + // Avoid making expensive immediates by commuting shifts. (This logic + // only applies to Thumb1 because ARM and Thumb2 immediates can be shifted + // for free.) + if (N->getOpcode() != ISD::SHL) + return true; + SDValue N1 = N->getOperand(0); + if (N1->getOpcode() != ISD::ADD && N1->getOpcode() != ISD::AND && + N1->getOpcode() != ISD::OR && N1->getOpcode() != ISD::XOR) + return true; + if (auto *Const = dyn_cast<ConstantSDNode>(N1->getOperand(1))) { + if (Const->getAPIntValue().ult(256)) + return false; + if (N1->getOpcode() == ISD::ADD && Const->getAPIntValue().slt(0) && + Const->getAPIntValue().sgt(-256)) + return false; + } return true; + } // Turn off commute-with-shift transform after legalization, so it doesn't // conflict with PerformSHLSimplify. (We could try to detect when @@ -10432,9 +10843,8 @@ ARMTargetLowering::isDesirableToCommuteWithShift(const SDNode *N, return false; } -bool -ARMTargetLowering::shouldFoldShiftPairToMask(const SDNode *N, - CombineLevel Level) const { +bool ARMTargetLowering::shouldFoldConstantShiftPairToMask( + const SDNode *N, CombineLevel Level) const { if (!Subtarget->isThumb1Only()) return true; @@ -10444,6 +10854,15 @@ ARMTargetLowering::shouldFoldShiftPairToMask(const SDNode *N, return false; } +bool ARMTargetLowering::preferIncOfAddToSubOfNot(EVT VT) const { + if (!Subtarget->hasNEON()) { + if (Subtarget->isThumb1Only()) + return VT.getScalarSizeInBits() <= 32; + return true; + } + return VT.isScalarInteger(); +} + static SDValue PerformSHLSimplify(SDNode *N, TargetLowering::DAGCombinerInfo &DCI, const ARMSubtarget *ST) { @@ -10830,7 +11249,7 @@ static SDValue PerformANDCombine(SDNode *N, APInt SplatBits, SplatUndef; unsigned SplatBitSize; bool HasAnyUndefs; - if (BVN && + if (BVN && Subtarget->hasNEON() && BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize, HasAnyUndefs)) { if (SplatBitSize <= 64) { EVT VbicVT; @@ -11308,7 +11727,7 @@ static SDValue PerformVMOVRRDCombine(SDNode *N, const ARMSubtarget *Subtarget) { // vmovrrd(vmovdrr x, y) -> x,y SDValue InDouble = N->getOperand(0); - if (InDouble.getOpcode() == ARMISD::VMOVDRR && !Subtarget->isFPOnlySP()) + if (InDouble.getOpcode() == ARMISD::VMOVDRR && Subtarget->hasFP64()) return DCI.CombineTo(N, InDouble.getOperand(0), InDouble.getOperand(1)); // vmovrrd(load f64) -> (load i32), (load i32) @@ -11329,9 +11748,11 @@ static SDValue PerformVMOVRRDCombine(SDNode *N, SDValue OffsetPtr = DAG.getNode(ISD::ADD, DL, MVT::i32, BasePtr, DAG.getConstant(4, DL, MVT::i32)); - SDValue NewLD2 = DAG.getLoad( - MVT::i32, DL, NewLD1.getValue(1), OffsetPtr, LD->getPointerInfo(), - std::min(4U, LD->getAlignment() / 2), LD->getMemOperand()->getFlags()); + + SDValue NewLD2 = DAG.getLoad(MVT::i32, DL, LD->getChain(), OffsetPtr, + LD->getPointerInfo().getWithOffset(4), + std::min(4U, LD->getAlignment()), + LD->getMemOperand()->getFlags()); DAG.ReplaceAllUsesOfValueWith(SDValue(LD, 1), NewLD2.getValue(1)); if (DCI.DAG.getDataLayout().isBigEndian()) @@ -11922,10 +12343,14 @@ static SDValue PerformVDUPLANECombine(SDNode *N, /// PerformVDUPCombine - Target-specific dag combine xforms for ARMISD::VDUP. static SDValue PerformVDUPCombine(SDNode *N, - TargetLowering::DAGCombinerInfo &DCI) { + TargetLowering::DAGCombinerInfo &DCI, + const ARMSubtarget *Subtarget) { SelectionDAG &DAG = DCI.DAG; SDValue Op = N->getOperand(0); + if (!Subtarget->hasNEON()) + return SDValue(); + // Match VDUP(LOAD) -> VLD1DUP. // We match this pattern here rather than waiting for isel because the // transform is only legal for unindexed loads. @@ -12132,11 +12557,11 @@ static SDValue PerformVCVTCombine(SDNode *N, SelectionDAG &DAG, MVT IntTy = N->getSimpleValueType(0).getVectorElementType(); uint32_t IntBits = IntTy.getSizeInBits(); unsigned NumLanes = Op.getValueType().getVectorNumElements(); - if (FloatBits != 32 || IntBits > 32 || NumLanes > 4) { + if (FloatBits != 32 || IntBits > 32 || (NumLanes != 4 && NumLanes != 2)) { // These instructions only exist converting from f32 to i32. We can handle // smaller integers by generating an extra truncate, but larger ones would - // be lossy. We also can't handle more then 4 lanes, since these intructions - // only support v2i32/v4i32 types. + // be lossy. We also can't handle anything other than 2 or 4 lanes, since + // these intructions only support v2i32/v4i32 types. return SDValue(); } @@ -12190,11 +12615,11 @@ static SDValue PerformVDIVCombine(SDNode *N, SelectionDAG &DAG, MVT IntTy = Op.getOperand(0).getSimpleValueType().getVectorElementType(); uint32_t IntBits = IntTy.getSizeInBits(); unsigned NumLanes = Op.getValueType().getVectorNumElements(); - if (FloatBits != 32 || IntBits > 32 || NumLanes > 4) { + if (FloatBits != 32 || IntBits > 32 || (NumLanes != 4 && NumLanes != 2)) { // These instructions only exist converting from i32 to f32. We can handle // smaller integers by generating an extra extend, but larger ones would - // be lossy. We also can't handle more then 4 lanes, since these intructions - // only support v2i32/v4i32 types. + // be lossy. We also can't handle anything other than 2 or 4 lanes, since + // these intructions only support v2i32/v4i32 types. return SDValue(); } @@ -12220,58 +12645,6 @@ static SDValue PerformVDIVCombine(SDNode *N, SelectionDAG &DAG, ConvInput, DAG.getConstant(C, dl, MVT::i32)); } -/// Getvshiftimm - Check if this is a valid build_vector for the immediate -/// operand of a vector shift operation, where all the elements of the -/// build_vector must have the same constant integer value. -static bool getVShiftImm(SDValue Op, unsigned ElementBits, int64_t &Cnt) { - // Ignore bit_converts. - while (Op.getOpcode() == ISD::BITCAST) - Op = Op.getOperand(0); - BuildVectorSDNode *BVN = dyn_cast<BuildVectorSDNode>(Op.getNode()); - APInt SplatBits, SplatUndef; - unsigned SplatBitSize; - bool HasAnyUndefs; - if (! BVN || ! BVN->isConstantSplat(SplatBits, SplatUndef, SplatBitSize, - HasAnyUndefs, ElementBits) || - SplatBitSize > ElementBits) - return false; - Cnt = SplatBits.getSExtValue(); - return true; -} - -/// isVShiftLImm - Check if this is a valid build_vector for the immediate -/// operand of a vector shift left operation. That value must be in the range: -/// 0 <= Value < ElementBits for a left shift; or -/// 0 <= Value <= ElementBits for a long left shift. -static bool isVShiftLImm(SDValue Op, EVT VT, bool isLong, int64_t &Cnt) { - assert(VT.isVector() && "vector shift count is not a vector type"); - int64_t ElementBits = VT.getScalarSizeInBits(); - if (! getVShiftImm(Op, ElementBits, Cnt)) - return false; - return (Cnt >= 0 && (isLong ? Cnt-1 : Cnt) < ElementBits); -} - -/// isVShiftRImm - Check if this is a valid build_vector for the immediate -/// operand of a vector shift right operation. For a shift opcode, the value -/// is positive, but for an intrinsic the value count must be negative. The -/// absolute value must be in the range: -/// 1 <= |Value| <= ElementBits for a right shift; or -/// 1 <= |Value| <= ElementBits/2 for a narrow right shift. -static bool isVShiftRImm(SDValue Op, EVT VT, bool isNarrow, bool isIntrinsic, - int64_t &Cnt) { - assert(VT.isVector() && "vector shift count is not a vector type"); - int64_t ElementBits = VT.getScalarSizeInBits(); - if (! getVShiftImm(Op, ElementBits, Cnt)) - return false; - if (!isIntrinsic) - return (Cnt >= 1 && Cnt <= (isNarrow ? ElementBits/2 : ElementBits)); - if (Cnt >= -(isNarrow ? ElementBits/2 : ElementBits) && Cnt <= -1) { - Cnt = -Cnt; - return true; - } - return false; -} - /// PerformIntrinsicCombine - ARM-specific DAG combining for intrinsics. static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) { unsigned IntNo = cast<ConstantSDNode>(N->getOperand(0))->getZExtValue(); @@ -12307,12 +12680,12 @@ static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) { case Intrinsic::arm_neon_vshifts: case Intrinsic::arm_neon_vshiftu: if (isVShiftLImm(N->getOperand(2), VT, false, Cnt)) { - VShiftOpc = ARMISD::VSHL; + VShiftOpc = ARMISD::VSHLIMM; break; } if (isVShiftRImm(N->getOperand(2), VT, false, true, Cnt)) { - VShiftOpc = (IntNo == Intrinsic::arm_neon_vshifts ? - ARMISD::VSHRs : ARMISD::VSHRu); + VShiftOpc = (IntNo == Intrinsic::arm_neon_vshifts ? ARMISD::VSHRsIMM + : ARMISD::VSHRuIMM); break; } return SDValue(); @@ -12357,29 +12730,41 @@ static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) { // Opcode already set above. break; case Intrinsic::arm_neon_vrshifts: - VShiftOpc = ARMISD::VRSHRs; break; + VShiftOpc = ARMISD::VRSHRsIMM; + break; case Intrinsic::arm_neon_vrshiftu: - VShiftOpc = ARMISD::VRSHRu; break; + VShiftOpc = ARMISD::VRSHRuIMM; + break; case Intrinsic::arm_neon_vrshiftn: - VShiftOpc = ARMISD::VRSHRN; break; + VShiftOpc = ARMISD::VRSHRNIMM; + break; case Intrinsic::arm_neon_vqshifts: - VShiftOpc = ARMISD::VQSHLs; break; + VShiftOpc = ARMISD::VQSHLsIMM; + break; case Intrinsic::arm_neon_vqshiftu: - VShiftOpc = ARMISD::VQSHLu; break; + VShiftOpc = ARMISD::VQSHLuIMM; + break; case Intrinsic::arm_neon_vqshiftsu: - VShiftOpc = ARMISD::VQSHLsu; break; + VShiftOpc = ARMISD::VQSHLsuIMM; + break; case Intrinsic::arm_neon_vqshiftns: - VShiftOpc = ARMISD::VQSHRNs; break; + VShiftOpc = ARMISD::VQSHRNsIMM; + break; case Intrinsic::arm_neon_vqshiftnu: - VShiftOpc = ARMISD::VQSHRNu; break; + VShiftOpc = ARMISD::VQSHRNuIMM; + break; case Intrinsic::arm_neon_vqshiftnsu: - VShiftOpc = ARMISD::VQSHRNsu; break; + VShiftOpc = ARMISD::VQSHRNsuIMM; + break; case Intrinsic::arm_neon_vqrshiftns: - VShiftOpc = ARMISD::VQRSHRNs; break; + VShiftOpc = ARMISD::VQRSHRNsIMM; + break; case Intrinsic::arm_neon_vqrshiftnu: - VShiftOpc = ARMISD::VQRSHRNu; break; + VShiftOpc = ARMISD::VQRSHRNuIMM; + break; case Intrinsic::arm_neon_vqrshiftnsu: - VShiftOpc = ARMISD::VQRSHRNsu; break; + VShiftOpc = ARMISD::VQRSHRNsuIMM; + break; } SDLoc dl(N); @@ -12393,9 +12778,9 @@ static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) { unsigned VShiftOpc = 0; if (isVShiftLImm(N->getOperand(3), VT, false, Cnt)) - VShiftOpc = ARMISD::VSLI; + VShiftOpc = ARMISD::VSLIIMM; else if (isVShiftRImm(N->getOperand(3), VT, false, true, Cnt)) - VShiftOpc = ARMISD::VSRI; + VShiftOpc = ARMISD::VSRIIMM; else { llvm_unreachable("invalid shift count for vsli/vsri intrinsic"); } @@ -12420,8 +12805,10 @@ static SDValue PerformIntrinsicCombine(SDNode *N, SelectionDAG &DAG) { /// combining instead of DAG legalizing because the build_vectors for 64-bit /// vector element shift counts are generally not legal, and it is hard to see /// their values after they get legalized to loads from a constant pool. -static SDValue PerformShiftCombine(SDNode *N, SelectionDAG &DAG, +static SDValue PerformShiftCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI, const ARMSubtarget *ST) { + SelectionDAG &DAG = DCI.DAG; EVT VT = N->getValueType(0); if (N->getOpcode() == ISD::SRL && VT == MVT::i32 && ST->hasV6Ops()) { // Canonicalize (srl (bswap x), 16) to (rotr (bswap x), 16) if the high @@ -12436,12 +12823,47 @@ static SDValue PerformShiftCombine(SDNode *N, SelectionDAG &DAG, } } + if (ST->isThumb1Only() && N->getOpcode() == ISD::SHL && VT == MVT::i32 && + N->getOperand(0)->getOpcode() == ISD::AND && + N->getOperand(0)->hasOneUse()) { + if (DCI.isBeforeLegalize() || DCI.isCalledByLegalizer()) + return SDValue(); + // Look for the pattern (shl (and x, AndMask), ShiftAmt). This doesn't + // usually show up because instcombine prefers to canonicalize it to + // (and (shl x, ShiftAmt) (shl AndMask, ShiftAmt)), but the shift can come + // out of GEP lowering in some cases. + SDValue N0 = N->getOperand(0); + ConstantSDNode *ShiftAmtNode = dyn_cast<ConstantSDNode>(N->getOperand(1)); + if (!ShiftAmtNode) + return SDValue(); + uint32_t ShiftAmt = static_cast<uint32_t>(ShiftAmtNode->getZExtValue()); + ConstantSDNode *AndMaskNode = dyn_cast<ConstantSDNode>(N0->getOperand(1)); + if (!AndMaskNode) + return SDValue(); + uint32_t AndMask = static_cast<uint32_t>(AndMaskNode->getZExtValue()); + // Don't transform uxtb/uxth. + if (AndMask == 255 || AndMask == 65535) + return SDValue(); + if (isMask_32(AndMask)) { + uint32_t MaskedBits = countLeadingZeros(AndMask); + if (MaskedBits > ShiftAmt) { + SDLoc DL(N); + SDValue SHL = DAG.getNode(ISD::SHL, DL, MVT::i32, N0->getOperand(0), + DAG.getConstant(MaskedBits, DL, MVT::i32)); + return DAG.getNode( + ISD::SRL, DL, MVT::i32, SHL, + DAG.getConstant(MaskedBits - ShiftAmt, DL, MVT::i32)); + } + } + } + // Nothing to be done for scalar shifts. const TargetLowering &TLI = DAG.getTargetLoweringInfo(); if (!VT.isVector() || !TLI.isTypeLegal(VT)) return SDValue(); + if (ST->hasMVEIntegerOps() && VT == MVT::v2i64) + return SDValue(); - assert(ST->hasNEON() && "unexpected vector shift"); int64_t Cnt; switch (N->getOpcode()) { @@ -12450,7 +12872,7 @@ static SDValue PerformShiftCombine(SDNode *N, SelectionDAG &DAG, case ISD::SHL: if (isVShiftLImm(N->getOperand(1), VT, false, Cnt)) { SDLoc dl(N); - return DAG.getNode(ARMISD::VSHL, dl, VT, N->getOperand(0), + return DAG.getNode(ARMISD::VSHLIMM, dl, VT, N->getOperand(0), DAG.getConstant(Cnt, dl, MVT::i32)); } break; @@ -12458,8 +12880,8 @@ static SDValue PerformShiftCombine(SDNode *N, SelectionDAG &DAG, case ISD::SRA: case ISD::SRL: if (isVShiftRImm(N->getOperand(1), VT, false, false, Cnt)) { - unsigned VShiftOpc = (N->getOpcode() == ISD::SRA ? - ARMISD::VSHRs : ARMISD::VSHRu); + unsigned VShiftOpc = + (N->getOpcode() == ISD::SRA ? ARMISD::VSHRsIMM : ARMISD::VSHRuIMM); SDLoc dl(N); return DAG.getNode(VShiftOpc, dl, VT, N->getOperand(0), DAG.getConstant(Cnt, dl, MVT::i32)); @@ -12606,6 +13028,45 @@ SDValue ARMTargetLowering::PerformCMOVToBFICombine(SDNode *CMOV, SelectionDAG &D return V; } +static SDValue PerformHWLoopCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI, + const ARMSubtarget *ST) { + // Look for (brcond (xor test.set.loop.iterations, -1) + SDValue CC = N->getOperand(1); + unsigned Opc = CC->getOpcode(); + SDValue Int; + + if ((Opc == ISD::XOR || Opc == ISD::SETCC) && + (CC->getOperand(0)->getOpcode() == ISD::INTRINSIC_W_CHAIN)) { + + assert((isa<ConstantSDNode>(CC->getOperand(1)) && + cast<ConstantSDNode>(CC->getOperand(1))->isOne()) && + "Expected to compare against 1"); + + Int = CC->getOperand(0); + } else if (CC->getOpcode() == ISD::INTRINSIC_W_CHAIN) + Int = CC; + else + return SDValue(); + + unsigned IntOp = cast<ConstantSDNode>(Int.getOperand(1))->getZExtValue(); + if (IntOp != Intrinsic::test_set_loop_iterations) + return SDValue(); + + SDLoc dl(Int); + SDValue Chain = N->getOperand(0); + SDValue Elements = Int.getOperand(2); + SDValue ExitBlock = N->getOperand(2); + + // TODO: Once we start supporting tail predication, we can add another + // operand to WLS for the number of elements processed in a vector loop. + + SDValue Ops[] = { Chain, Elements, ExitBlock }; + SDValue Res = DCI.DAG.getNode(ARMISD::WLS, dl, MVT::Other, Ops); + DCI.DAG.ReplaceAllUsesOfValueWith(Int.getValue(1), Int.getOperand(0)); + return Res; +} + /// PerformBRCONDCombine - Target-specific DAG combining for ARMISD::BRCOND. SDValue ARMTargetLowering::PerformBRCONDCombine(SDNode *N, SelectionDAG &DAG) const { @@ -12779,15 +13240,21 @@ ARMTargetLowering::PerformCMOVCombine(SDNode *N, SelectionDAG &DAG) const { // On Thumb1, the DAG above may be further combined if z is a power of 2 // (z == 2 ^ K). // CMOV (SUBS x, y), z, !=, (SUBS x, y):1 -> - // merge t3, t4 - // where t1 = (SUBCARRY (SUB x, y), z, 0) - // t2 = (SUBCARRY (SUB x, y), t1:0, t1:1) - // t3 = if K != 0 then (SHL t2:0, K) else t2:0 - // t4 = (SUB 1, t2:1) [ we want a carry, not a borrow ] + // t1 = (USUBO (SUB x, y), 1) + // t2 = (SUBCARRY (SUB x, y), t1:0, t1:1) + // Result = if K != 0 then (SHL t2:0, K) else t2:0 + // + // This also handles the special case of comparing against zero; it's + // essentially, the same pattern, except there's no SUBS: + // CMOV x, z, !=, (CMPZ x, 0) -> + // t1 = (USUBO x, 1) + // t2 = (SUBCARRY x, t1:0, t1:1) + // Result = if K != 0 then (SHL t2:0, K) else t2:0 const APInt *TrueConst; if (Subtarget->isThumb1Only() && CC == ARMCC::NE && - (FalseVal.getOpcode() == ARMISD::SUBS) && - (FalseVal.getOperand(0) == LHS) && (FalseVal.getOperand(1) == RHS) && + ((FalseVal.getOpcode() == ARMISD::SUBS && + FalseVal.getOperand(0) == LHS && FalseVal.getOperand(1) == RHS) || + (FalseVal == LHS && isNullConstant(RHS))) && (TrueConst = isPowerOf2Constant(TrueVal))) { SDVTList VTs = DAG.getVTList(VT, MVT::i32); unsigned ShiftAmount = TrueConst->logBase2(); @@ -12795,10 +13262,6 @@ ARMTargetLowering::PerformCMOVCombine(SDNode *N, SelectionDAG &DAG) const { TrueVal = DAG.getConstant(1, dl, VT); SDValue Subc = DAG.getNode(ISD::USUBO, dl, VTs, FalseVal, TrueVal); Res = DAG.getNode(ISD::SUBCARRY, dl, VTs, FalseVal, Subc, Subc.getValue(1)); - // Make it a carry, not a borrow. - SDValue Carry = DAG.getNode( - ISD::SUB, dl, VT, DAG.getConstant(1, dl, MVT::i32), Res.getValue(1)); - Res = DAG.getNode(ISD::MERGE_VALUES, dl, VTs, Res, Carry); if (ShiftAmount) Res = DAG.getNode(ISD::SHL, dl, VT, Res, @@ -12826,6 +13289,7 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { switch (N->getOpcode()) { default: break; + case ISD::ABS: return PerformABSCombine(N, DCI, Subtarget); case ARMISD::ADDE: return PerformADDECombine(N, DCI, Subtarget); case ARMISD::UMLAL: return PerformUMLALCombine(N, DCI.DAG, Subtarget); case ISD::ADD: return PerformADDCombine(N, DCI, Subtarget); @@ -12834,6 +13298,7 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N, case ISD::OR: return PerformORCombine(N, DCI, Subtarget); case ISD::XOR: return PerformXORCombine(N, DCI, Subtarget); case ISD::AND: return PerformANDCombine(N, DCI, Subtarget); + case ISD::BRCOND: return PerformHWLoopCombine(N, DCI, Subtarget); case ARMISD::ADDC: case ARMISD::SUBC: return PerformAddcSubcCombine(N, DCI, Subtarget); case ARMISD::SUBE: return PerformAddeSubeCombine(N, DCI, Subtarget); @@ -12845,7 +13310,7 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N, case ISD::INSERT_VECTOR_ELT: return PerformInsertEltCombine(N, DCI); case ISD::VECTOR_SHUFFLE: return PerformVECTOR_SHUFFLECombine(N, DCI.DAG); case ARMISD::VDUPLANE: return PerformVDUPLANECombine(N, DCI); - case ARMISD::VDUP: return PerformVDUPCombine(N, DCI); + case ARMISD::VDUP: return PerformVDUPCombine(N, DCI, Subtarget); case ISD::FP_TO_SINT: case ISD::FP_TO_UINT: return PerformVCVTCombine(N, DCI.DAG, Subtarget); @@ -12854,7 +13319,8 @@ SDValue ARMTargetLowering::PerformDAGCombine(SDNode *N, case ISD::INTRINSIC_WO_CHAIN: return PerformIntrinsicCombine(N, DCI.DAG); case ISD::SHL: case ISD::SRA: - case ISD::SRL: return PerformShiftCombine(N, DCI.DAG, Subtarget); + case ISD::SRL: + return PerformShiftCombine(N, DCI, Subtarget); case ISD::SIGN_EXTEND: case ISD::ZERO_EXTEND: case ISD::ANY_EXTEND: return PerformExtendCombine(N, DCI.DAG, Subtarget); @@ -12957,9 +13423,9 @@ bool ARMTargetLowering::isDesirableToTransformToIntegerOp(unsigned Opc, return (VT == MVT::f32) && (Opc == ISD::LOAD || Opc == ISD::STORE); } -bool ARMTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, - unsigned, - unsigned, +bool ARMTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, unsigned, + unsigned Alignment, + MachineMemOperand::Flags, bool *Fast) const { // Depends what it gets converted into if the type is weird. if (!VT.isSimple()) @@ -12967,23 +13433,18 @@ bool ARMTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, // The AllowsUnaliged flag models the SCTLR.A setting in ARM cpus bool AllowsUnaligned = Subtarget->allowsUnalignedMem(); + auto Ty = VT.getSimpleVT().SimpleTy; - switch (VT.getSimpleVT().SimpleTy) { - default: - return false; - case MVT::i8: - case MVT::i16: - case MVT::i32: { + if (Ty == MVT::i8 || Ty == MVT::i16 || Ty == MVT::i32) { // Unaligned access can use (for example) LRDB, LRDH, LDR if (AllowsUnaligned) { if (Fast) *Fast = Subtarget->hasV7Ops(); return true; } - return false; } - case MVT::f64: - case MVT::v2f64: { + + if (Ty == MVT::f64 || Ty == MVT::v2f64) { // For any little-endian targets with neon, we can support unaligned ld/st // of D and Q (e.g. {D0,D1}) registers by using vld1.i8/vst1.i8. // A big-endian target may also explicitly support unaligned accesses @@ -12992,9 +13453,54 @@ bool ARMTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, *Fast = true; return true; } - return false; } + + if (!Subtarget->hasMVEIntegerOps()) + return false; + if (Ty != MVT::v16i8 && Ty != MVT::v8i16 && Ty != MVT::v8f16 && + Ty != MVT::v4i32 && Ty != MVT::v4f32 && Ty != MVT::v2i64 && + Ty != MVT::v2f64 && + // These are for truncated stores + Ty != MVT::v4i8 && Ty != MVT::v8i8 && Ty != MVT::v4i16) + return false; + + if (Subtarget->isLittle()) { + // In little-endian MVE, the store instructions VSTRB.U8, + // VSTRH.U16 and VSTRW.U32 all store the vector register in + // exactly the same format, and differ only in the range of + // their immediate offset field and the required alignment. + // + // In particular, VSTRB.U8 can store a vector at byte alignment. + // So at this stage we can simply say that loads/stores of all + // 128-bit wide vector types are permitted at any alignment, + // because we know at least _one_ instruction can manage that. + // + // Later on we might find that some of those loads are better + // generated as VLDRW.U32 if alignment permits, to take + // advantage of the larger immediate range. But for the moment, + // all that matters is that if we don't lower the load then + // _some_ instruction can handle it. + if (Fast) + *Fast = true; + return true; + } else { + // In big-endian MVE, those instructions aren't so similar + // after all, because they reorder the bytes of the vector + // differently. So this time we can only store a particular + // kind of vector if its alignment is at least the element + // type. And we can't store vectors of i64 or f64 at all + // without having to do some postprocessing, because there's + // no VSTRD.U64. + if (Ty == MVT::v16i8 || + ((Ty == MVT::v8i16 || Ty == MVT::v8f16) && Alignment >= 2) || + ((Ty == MVT::v4i32 || Ty == MVT::v4f32) && Alignment >= 4)) { + if (Fast) + *Fast = true; + return true; + } } + + return false; } static bool memOpAlign(unsigned DstAlign, unsigned SrcAlign, @@ -13003,24 +13509,24 @@ static bool memOpAlign(unsigned DstAlign, unsigned SrcAlign, (DstAlign == 0 || DstAlign % AlignCheck == 0)); } -EVT ARMTargetLowering::getOptimalMemOpType(uint64_t Size, - unsigned DstAlign, unsigned SrcAlign, - bool IsMemset, bool ZeroMemset, - bool MemcpyStrSrc, - MachineFunction &MF) const { - const Function &F = MF.getFunction(); - +EVT ARMTargetLowering::getOptimalMemOpType( + uint64_t Size, unsigned DstAlign, unsigned SrcAlign, bool IsMemset, + bool ZeroMemset, bool MemcpyStrSrc, + const AttributeList &FuncAttributes) const { // See if we can use NEON instructions for this... if ((!IsMemset || ZeroMemset) && Subtarget->hasNEON() && - !F.hasFnAttribute(Attribute::NoImplicitFloat)) { + !FuncAttributes.hasFnAttribute(Attribute::NoImplicitFloat)) { bool Fast; if (Size >= 16 && (memOpAlign(SrcAlign, DstAlign, 16) || - (allowsMisalignedMemoryAccesses(MVT::v2f64, 0, 1, &Fast) && Fast))) { + (allowsMisalignedMemoryAccesses(MVT::v2f64, 0, 1, + MachineMemOperand::MONone, &Fast) && + Fast))) { return MVT::v2f64; } else if (Size >= 8 && (memOpAlign(SrcAlign, DstAlign, 8) || - (allowsMisalignedMemoryAccesses(MVT::f64, 0, 1, &Fast) && + (allowsMisalignedMemoryAccesses( + MVT::f64, 0, 1, MachineMemOperand::MONone, &Fast) && Fast))) { return MVT::f64; } @@ -13089,6 +13595,46 @@ bool ARMTargetLowering::isFNegFree(EVT VT) const { return false; } +/// Check if Ext1 and Ext2 are extends of the same type, doubling the bitwidth +/// of the vector elements. +static bool areExtractExts(Value *Ext1, Value *Ext2) { + auto areExtDoubled = [](Instruction *Ext) { + return Ext->getType()->getScalarSizeInBits() == + 2 * Ext->getOperand(0)->getType()->getScalarSizeInBits(); + }; + + if (!match(Ext1, m_ZExtOrSExt(m_Value())) || + !match(Ext2, m_ZExtOrSExt(m_Value())) || + !areExtDoubled(cast<Instruction>(Ext1)) || + !areExtDoubled(cast<Instruction>(Ext2))) + return false; + + return true; +} + +/// Check if sinking \p I's operands to I's basic block is profitable, because +/// the operands can be folded into a target instruction, e.g. +/// sext/zext can be folded into vsubl. +bool ARMTargetLowering::shouldSinkOperands(Instruction *I, + SmallVectorImpl<Use *> &Ops) const { + if (!Subtarget->hasNEON() || !I->getType()->isVectorTy()) + return false; + + switch (I->getOpcode()) { + case Instruction::Sub: + case Instruction::Add: { + if (!areExtractExts(I->getOperand(0), I->getOperand(1))) + return false; + Ops.push_back(&I->getOperandUse(0)); + Ops.push_back(&I->getOperandUse(1)); + return true; + } + default: + return false; + } + return false; +} + bool ARMTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { EVT VT = ExtVal.getValueType(); @@ -13105,7 +13651,7 @@ bool ARMTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { SDNode *U = *ExtVal->use_begin(); if ((U->getOpcode() == ISD::ADD || U->getOpcode() == ISD::SUB || - U->getOpcode() == ISD::SHL || U->getOpcode() == ARMISD::VSHL)) + U->getOpcode() == ISD::SHL || U->getOpcode() == ARMISD::VSHLIMM)) return false; return true; @@ -13142,7 +13688,6 @@ static bool isLegalT1AddressImmediate(int64_t V, EVT VT) { unsigned Scale = 1; switch (VT.getSimpleVT().SimpleTy) { - default: return false; case MVT::i1: case MVT::i8: // Scale == 1; @@ -13151,7 +13696,8 @@ static bool isLegalT1AddressImmediate(int64_t V, EVT VT) { // Scale == 2; Scale = 2; break; - case MVT::i32: + default: + // On thumb1 we load most things (i32, i64, floats, etc) with a LDR // Scale == 4; Scale = 4; break; @@ -13159,38 +13705,58 @@ static bool isLegalT1AddressImmediate(int64_t V, EVT VT) { if ((V & (Scale - 1)) != 0) return false; - V /= Scale; - return V == (V & ((1LL << 5) - 1)); + return isUInt<5>(V / Scale); } static bool isLegalT2AddressImmediate(int64_t V, EVT VT, const ARMSubtarget *Subtarget) { - bool isNeg = false; + if (!VT.isInteger() && !VT.isFloatingPoint()) + return false; + if (VT.isVector() && Subtarget->hasNEON()) + return false; + if (VT.isVector() && VT.isFloatingPoint() && Subtarget->hasMVEIntegerOps() && + !Subtarget->hasMVEFloatOps()) + return false; + + bool IsNeg = false; if (V < 0) { - isNeg = true; - V = - V; + IsNeg = true; + V = -V; } - switch (VT.getSimpleVT().SimpleTy) { - default: return false; - case MVT::i1: - case MVT::i8: - case MVT::i16: - case MVT::i32: - // + imm12 or - imm8 - if (isNeg) - return V == (V & ((1LL << 8) - 1)); - return V == (V & ((1LL << 12) - 1)); - case MVT::f32: - case MVT::f64: - // Same as ARM mode. FIXME: NEON? - if (!Subtarget->hasVFP2()) - return false; - if ((V & 3) != 0) + unsigned NumBytes = std::max(VT.getSizeInBits() / 8, 1U); + + // MVE: size * imm7 + if (VT.isVector() && Subtarget->hasMVEIntegerOps()) { + switch (VT.getSimpleVT().getVectorElementType().SimpleTy) { + case MVT::i32: + case MVT::f32: + return isShiftedUInt<7,2>(V); + case MVT::i16: + case MVT::f16: + return isShiftedUInt<7,1>(V); + case MVT::i8: + return isUInt<7>(V); + default: return false; - V >>= 2; - return V == (V & ((1LL << 8) - 1)); + } } + + // half VLDR: 2 * imm8 + if (VT.isFloatingPoint() && NumBytes == 2 && Subtarget->hasFPRegs16()) + return isShiftedUInt<8, 1>(V); + // VLDR and LDRD: 4 * imm8 + if ((VT.isFloatingPoint() && Subtarget->hasVFP2Base()) || NumBytes == 8) + return isShiftedUInt<8, 2>(V); + + if (NumBytes == 1 || NumBytes == 2 || NumBytes == 4) { + // + imm12 or - imm8 + if (IsNeg) + return isUInt<8>(V); + return isUInt<12>(V); + } + + return false; } /// isLegalAddressImmediate - Return true if the integer value can be used @@ -13218,18 +13784,15 @@ static bool isLegalAddressImmediate(int64_t V, EVT VT, case MVT::i8: case MVT::i32: // +- imm12 - return V == (V & ((1LL << 12) - 1)); + return isUInt<12>(V); case MVT::i16: // +- imm8 - return V == (V & ((1LL << 8) - 1)); + return isUInt<8>(V); case MVT::f32: case MVT::f64: - if (!Subtarget->hasVFP2()) // FIXME: NEON? - return false; - if ((V & 3) != 0) + if (!Subtarget->hasVFP2Base()) // FIXME: NEON? return false; - V >>= 2; - return V == (V & ((1LL << 8) - 1)); + return isShiftedUInt<8, 2>(V); } } @@ -13649,13 +14212,13 @@ void ARMTargetLowering::computeKnownBitsForTargetNode(const SDValue Op, EVT VT = Op.getValueType(); const unsigned DstSz = VT.getScalarSizeInBits(); const unsigned SrcSz = VecVT.getVectorElementType().getSizeInBits(); + (void)SrcSz; assert(SrcSz == Known.getBitWidth()); assert(DstSz > SrcSz); if (Op.getOpcode() == ARMISD::VGETLANEs) Known = Known.sext(DstSz); else { - Known = Known.zext(DstSz); - Known.Zero.setBitsFrom(SrcSz); + Known = Known.zext(DstSz, true /* extended bits are known zero */); } assert(DstSz == Known.getBitWidth()); break; @@ -13790,7 +14353,7 @@ const char *ARMTargetLowering::LowerXConstraint(EVT ConstraintVT) const { // Although we are correct (we are free to emit anything, without // constraints), we might break use cases that would expect us to be more // efficient and emit something else. - if (!Subtarget->hasVFP2()) + if (!Subtarget->hasVFP2Base()) return "r"; if (ConstraintVT.isFloatingPoint()) return "w"; @@ -13822,6 +14385,7 @@ ARMTargetLowering::getConstraintType(StringRef Constraint) const { } else if (Constraint.size() == 2) { switch (Constraint[0]) { default: break; + case 'T': return C_RegisterClass; // All 'U+' constraints are addresses. case 'U': return C_Memory; } @@ -13867,7 +14431,8 @@ using RCPair = std::pair<unsigned, const TargetRegisterClass *>; RCPair ARMTargetLowering::getRegForInlineAsmConstraint( const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { - if (Constraint.size() == 1) { + switch (Constraint.size()) { + case 1: // GCC ARM Constraint Letters switch (Constraint[0]) { case 'l': // Low regs or general regs. @@ -13913,7 +14478,25 @@ RCPair ARMTargetLowering::getRegForInlineAsmConstraint( return RCPair(0U, &ARM::QPR_VFP2RegClass); break; } + break; + + case 2: + if (Constraint[0] == 'T') { + switch (Constraint[1]) { + default: + break; + case 'e': + return RCPair(0U, &ARM::tGPREvenRegClass); + case 'o': + return RCPair(0U, &ARM::tGPROddRegClass); + } + } + break; + + default: + break; } + if (StringRef("{cc}").equals_lower(Constraint)) return std::make_pair(unsigned(ARM::CPSR), &ARM::CCRRegClass); @@ -14272,28 +14855,107 @@ ARMTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const } SDValue ARMTargetLowering::LowerFP_EXTEND(SDValue Op, SelectionDAG &DAG) const { - assert(Op.getValueType() == MVT::f64 && Subtarget->isFPOnlySP() && + SDValue SrcVal = Op.getOperand(0); + const unsigned DstSz = Op.getValueType().getSizeInBits(); + const unsigned SrcSz = SrcVal.getValueType().getSizeInBits(); + assert(DstSz > SrcSz && DstSz <= 64 && SrcSz >= 16 && "Unexpected type for custom-lowering FP_EXTEND"); + assert((!Subtarget->hasFP64() || !Subtarget->hasFPARMv8Base()) && + "With both FP DP and 16, any FP conversion is legal!"); + + assert(!(DstSz == 32 && Subtarget->hasFP16()) && + "With FP16, 16 to 32 conversion is legal!"); + + // Either we are converting from 16 -> 64, without FP16 and/or + // FP.double-precision or without Armv8-fp. So we must do it in two + // steps. + // Or we are converting from 32 -> 64 without fp.double-precision or 16 -> 32 + // without FP16. So we must do a function call. + SDLoc Loc(Op); RTLIB::Libcall LC; - LC = RTLIB::getFPEXT(Op.getOperand(0).getValueType(), Op.getValueType()); + if (SrcSz == 16) { + // Instruction from 16 -> 32 + if (Subtarget->hasFP16()) + SrcVal = DAG.getNode(ISD::FP_EXTEND, Loc, MVT::f32, SrcVal); + // Lib call from 16 -> 32 + else { + LC = RTLIB::getFPEXT(MVT::f16, MVT::f32); + assert(LC != RTLIB::UNKNOWN_LIBCALL && + "Unexpected type for custom-lowering FP_EXTEND"); + SrcVal = + makeLibCall(DAG, LC, MVT::f32, SrcVal, /*isSigned*/ false, Loc).first; + } + } - SDValue SrcVal = Op.getOperand(0); - return makeLibCall(DAG, LC, Op.getValueType(), SrcVal, /*isSigned*/ false, - SDLoc(Op)).first; + if (DstSz != 64) + return SrcVal; + // For sure now SrcVal is 32 bits + if (Subtarget->hasFP64()) // Instruction from 32 -> 64 + return DAG.getNode(ISD::FP_EXTEND, Loc, MVT::f64, SrcVal); + + LC = RTLIB::getFPEXT(MVT::f32, MVT::f64); + assert(LC != RTLIB::UNKNOWN_LIBCALL && + "Unexpected type for custom-lowering FP_EXTEND"); + return makeLibCall(DAG, LC, MVT::f64, SrcVal, /*isSigned*/ false, Loc).first; } SDValue ARMTargetLowering::LowerFP_ROUND(SDValue Op, SelectionDAG &DAG) const { - assert(Op.getOperand(0).getValueType() == MVT::f64 && - Subtarget->isFPOnlySP() && + SDValue SrcVal = Op.getOperand(0); + EVT SrcVT = SrcVal.getValueType(); + EVT DstVT = Op.getValueType(); + const unsigned DstSz = Op.getValueType().getSizeInBits(); + const unsigned SrcSz = SrcVT.getSizeInBits(); + (void)DstSz; + assert(DstSz < SrcSz && SrcSz <= 64 && DstSz >= 16 && "Unexpected type for custom-lowering FP_ROUND"); - RTLIB::Libcall LC; - LC = RTLIB::getFPROUND(Op.getOperand(0).getValueType(), Op.getValueType()); + assert((!Subtarget->hasFP64() || !Subtarget->hasFPARMv8Base()) && + "With both FP DP and 16, any FP conversion is legal!"); - SDValue SrcVal = Op.getOperand(0); - return makeLibCall(DAG, LC, Op.getValueType(), SrcVal, /*isSigned*/ false, - SDLoc(Op)).first; + SDLoc Loc(Op); + + // Instruction from 32 -> 16 if hasFP16 is valid + if (SrcSz == 32 && Subtarget->hasFP16()) + return Op; + + // Lib call from 32 -> 16 / 64 -> [32, 16] + RTLIB::Libcall LC = RTLIB::getFPROUND(SrcVT, DstVT); + assert(LC != RTLIB::UNKNOWN_LIBCALL && + "Unexpected type for custom-lowering FP_ROUND"); + return makeLibCall(DAG, LC, DstVT, SrcVal, /*isSigned*/ false, Loc).first; +} + +void ARMTargetLowering::lowerABS(SDNode *N, SmallVectorImpl<SDValue> &Results, + SelectionDAG &DAG) const { + assert(N->getValueType(0) == MVT::i64 && "Unexpected type (!= i64) on ABS."); + MVT HalfT = MVT::i32; + SDLoc dl(N); + SDValue Hi, Lo, Tmp; + + if (!isOperationLegalOrCustom(ISD::ADDCARRY, HalfT) || + !isOperationLegalOrCustom(ISD::UADDO, HalfT)) + return ; + + unsigned OpTypeBits = HalfT.getScalarSizeInBits(); + SDVTList VTList = DAG.getVTList(HalfT, MVT::i1); + + Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, HalfT, N->getOperand(0), + DAG.getConstant(0, dl, HalfT)); + Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, dl, HalfT, N->getOperand(0), + DAG.getConstant(1, dl, HalfT)); + + Tmp = DAG.getNode(ISD::SRA, dl, HalfT, Hi, + DAG.getConstant(OpTypeBits - 1, dl, + getShiftAmountTy(HalfT, DAG.getDataLayout()))); + Lo = DAG.getNode(ISD::UADDO, dl, VTList, Tmp, Lo); + Hi = DAG.getNode(ISD::ADDCARRY, dl, VTList, Tmp, Hi, + SDValue(Lo.getNode(), 1)); + Hi = DAG.getNode(ISD::XOR, dl, HalfT, Tmp, Hi); + Lo = DAG.getNode(ISD::XOR, dl, HalfT, Tmp, Lo); + + Results.push_back(Lo); + Results.push_back(Hi); } bool @@ -14314,14 +14976,15 @@ bool ARM::isBitFieldInvertedMask(unsigned v) { /// isFPImmLegal - Returns true if the target can instruction select the /// specified FP immediate natively. If false, the legalizer will /// materialize the FP immediate as a load from a constant pool. -bool ARMTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT) const { - if (!Subtarget->hasVFP3()) +bool ARMTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize) const { + if (!Subtarget->hasVFP3Base()) return false; if (VT == MVT::f16 && Subtarget->hasFullFP16()) return ARM_AM::getFP16Imm(Imm) != -1; if (VT == MVT::f32) return ARM_AM::getFP32Imm(Imm) != -1; - if (VT == MVT::f64 && !Subtarget->isFPOnlySP()) + if (VT == MVT::f64 && Subtarget->hasFP64()) return ARM_AM::getFP64Imm(Imm) != -1; return false; } @@ -14590,6 +15253,9 @@ ARMTargetLowering::shouldExpandAtomicLoadInIR(LoadInst *LI) const { // and up to 64 bits on the non-M profiles TargetLowering::AtomicExpansionKind ARMTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { + if (AI->isFloatingPointOperation()) + return AtomicExpansionKind::CmpXChg; + unsigned Size = AI->getType()->getPrimitiveSizeInBits(); bool hasAtomicRMW = !Subtarget->isThumb() || Subtarget->hasV8MBaselineOps(); return (Size <= (Subtarget->isMClass() ? 32U : 64U) && hasAtomicRMW) @@ -14621,6 +15287,36 @@ bool ARMTargetLowering::useLoadStackGuardNode() const { return Subtarget->isTargetMachO(); } +void ARMTargetLowering::insertSSPDeclarations(Module &M) const { + if (!Subtarget->getTargetTriple().isWindowsMSVCEnvironment()) + return TargetLowering::insertSSPDeclarations(M); + + // 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. + FunctionCallee SecurityCheckCookie = M.getOrInsertFunction( + "__security_check_cookie", Type::getVoidTy(M.getContext()), + Type::getInt8PtrTy(M.getContext())); + if (Function *F = dyn_cast<Function>(SecurityCheckCookie.getCallee())) + F->addAttribute(1, Attribute::AttrKind::InReg); +} + +Value *ARMTargetLowering::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); +} + +Function *ARMTargetLowering::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); +} + bool ARMTargetLowering::canCombineStoreAndExtract(Type *VectorTy, Value *Idx, unsigned &Cost) const { // If we do not have NEON, vector types are not natively supported. @@ -14658,6 +15354,10 @@ bool ARMTargetLowering::isCheapToSpeculateCtlz() const { return Subtarget->hasV6T2Ops(); } +bool ARMTargetLowering::shouldExpandShift(SelectionDAG &DAG, SDNode *N) const { + return !Subtarget->hasMinSize(); +} + Value *ARMTargetLowering::emitLoadLinked(IRBuilder<> &Builder, Value *Addr, AtomicOrdering Ord) const { Module *M = Builder.GetInsertBlock()->getParent()->getParent(); @@ -14850,8 +15550,9 @@ bool ARMTargetLowering::lowerInterleavedLoad( // If we're generating more than one load, compute the base address of // subsequent loads as an offset from the previous. if (LoadCount > 0) - BaseAddr = Builder.CreateConstGEP1_32( - BaseAddr, VecTy->getVectorNumElements() * Factor); + BaseAddr = + Builder.CreateConstGEP1_32(VecTy->getVectorElementType(), BaseAddr, + VecTy->getVectorNumElements() * Factor); SmallVector<Value *, 2> Ops; Ops.push_back(Builder.CreateBitCast(BaseAddr, Int8Ptr)); @@ -14990,7 +15691,8 @@ bool ARMTargetLowering::lowerInterleavedStore(StoreInst *SI, // If we generating more than one store, we compute the base address of // subsequent stores as an offset from the previous. if (StoreCount > 0) - BaseAddr = Builder.CreateConstGEP1_32(BaseAddr, LaneLen * Factor); + BaseAddr = Builder.CreateConstGEP1_32(SubVecTy->getVectorElementType(), + BaseAddr, LaneLen * Factor); SmallVector<Value *, 6> Ops; Ops.push_back(Builder.CreateBitCast(BaseAddr, Int8Ptr)); diff --git a/lib/Target/ARM/ARMISelLowering.h b/lib/Target/ARM/ARMISelLowering.h index 7a9fc739fc13..1675ec59a354 100644 --- a/lib/Target/ARM/ARMISelLowering.h +++ b/lib/Target/ARM/ARMISelLowering.h @@ -1,9 +1,8 @@ //===- ARMISelLowering.h - ARM DAG Lowering Interface -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -77,6 +76,10 @@ class VectorType; PIC_ADD, // Add with a PC operand and a PIC label. + ASRL, // MVE long arithmetic shift right. + LSRL, // MVE long shift right. + LSLL, // MVE long shift left. + CMP, // ARM compare instructions. CMN, // ARM CMN instructions. CMPZ, // ARM compare that sets only Z flag. @@ -122,6 +125,8 @@ class VectorType; WIN__CHKSTK, // Windows' __chkstk call to do stack probing. WIN__DBZCHK, // Windows' divide by zero check + WLS, // Low-overhead loops, While Loop Start + VCEQ, // Vector compare equal. VCEQZ, // Vector compare equal to zero. VCGE, // Vector compare greater than or equal. @@ -134,32 +139,36 @@ class VectorType; VCGTU, // Vector compare unsigned greater than. VTST, // Vector test bits. + // Vector shift by vector + VSHLs, // ...left/right by signed + VSHLu, // ...left/right by unsigned + // Vector shift by immediate: - VSHL, // ...left - VSHRs, // ...right (signed) - VSHRu, // ...right (unsigned) + VSHLIMM, // ...left + VSHRsIMM, // ...right (signed) + VSHRuIMM, // ...right (unsigned) // Vector rounding shift by immediate: - VRSHRs, // ...right (signed) - VRSHRu, // ...right (unsigned) - VRSHRN, // ...right narrow + VRSHRsIMM, // ...right (signed) + VRSHRuIMM, // ...right (unsigned) + VRSHRNIMM, // ...right narrow // Vector saturating shift by immediate: - VQSHLs, // ...left (signed) - VQSHLu, // ...left (unsigned) - VQSHLsu, // ...left (signed to unsigned) - VQSHRNs, // ...right narrow (signed) - VQSHRNu, // ...right narrow (unsigned) - VQSHRNsu, // ...right narrow (signed to unsigned) + VQSHLsIMM, // ...left (signed) + VQSHLuIMM, // ...left (unsigned) + VQSHLsuIMM, // ...left (signed to unsigned) + VQSHRNsIMM, // ...right narrow (signed) + VQSHRNuIMM, // ...right narrow (unsigned) + VQSHRNsuIMM, // ...right narrow (signed to unsigned) // Vector saturating rounding shift by immediate: - VQRSHRNs, // ...right narrow (signed) - VQRSHRNu, // ...right narrow (unsigned) - VQRSHRNsu, // ...right narrow (signed to unsigned) + VQRSHRNsIMM, // ...right narrow (signed) + VQRSHRNuIMM, // ...right narrow (unsigned) + VQRSHRNsuIMM, // ...right narrow (signed to unsigned) // Vector shift and insert: - VSLI, // ...left - VSRI, // ...right + VSLIIMM, // ...left + VSRIIMM, // ...right // Vector get lane (VMOV scalar to ARM core register) // (These are used for 8- and 16-bit element types only.) @@ -322,17 +331,21 @@ class VectorType; /// is "fast" by reference in the second argument. bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace, unsigned Align, + MachineMemOperand::Flags Flags, bool *Fast) const override; EVT getOptimalMemOpType(uint64_t Size, unsigned DstAlign, unsigned SrcAlign, bool IsMemset, bool ZeroMemset, bool MemcpyStrSrc, - MachineFunction &MF) const override; + const AttributeList &FuncAttributes) const override; bool isTruncateFree(Type *SrcTy, Type *DstTy) const override; bool isTruncateFree(EVT SrcVT, EVT DstVT) const override; bool isZExtFree(SDValue Val, EVT VT2) const override; + bool shouldSinkOperands(Instruction *I, + SmallVectorImpl<Use *> &Ops) const override; + bool isFNegFree(EVT VT) const override; bool isVectorLoadExtDesirable(SDValue ExtVal) const override; @@ -454,7 +467,8 @@ class VectorType; /// getRegClassFor - Return the register class that should be used for the /// specified value type. - const TargetRegisterClass *getRegClassFor(MVT VT) const override; + const TargetRegisterClass * + getRegClassFor(MVT VT, bool isDivergent = false) const override; /// Returns true if a cast between SrcAS and DestAS is a noop. bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override { @@ -479,7 +493,8 @@ class VectorType; /// isFPImmLegal - Returns true if the target can instruction select the /// specified FP immediate natively. If false, the legalizer will /// materialize the FP immediate as a load from a constant pool. - bool isFPImmLegal(const APFloat &Imm, EVT VT) const override; + bool isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize = false) const override; bool getTgtMemIntrinsic(IntrinsicInfo &Info, const CallInst &I, @@ -544,6 +559,10 @@ class VectorType; bool useLoadStackGuardNode() const override; + void insertSSPDeclarations(Module &M) const override; + Value *getSDagStackGuard(const Module &M) const override; + Function *getSSPStackGuardCheck(const Module &M) const override; + bool canCombineStoreAndExtract(Type *VectorTy, Value *Idx, unsigned &Cost) const override; @@ -568,6 +587,8 @@ class VectorType; return HasStandaloneRem; } + bool shouldExpandShift(SelectionDAG &DAG, SDNode *N) const override; + CCAssignFn *CCAssignFnForCall(CallingConv::ID CC, bool isVarArg) const; CCAssignFn *CCAssignFnForReturn(CallingConv::ID CC, bool isVarArg) const; @@ -593,8 +614,11 @@ class VectorType; bool isDesirableToCommuteWithShift(const SDNode *N, CombineLevel Level) const override; - bool shouldFoldShiftPairToMask(const SDNode *N, - CombineLevel Level) const override; + bool shouldFoldConstantShiftPairToMask(const SDNode *N, + CombineLevel Level) const override; + + bool preferIncOfAddToSubOfNot(EVT VT) const override; + protected: std::pair<const TargetRegisterClass *, uint8_t> findRepresentativeClass(const TargetRegisterInfo *TRI, @@ -680,6 +704,7 @@ class VectorType; const ARMSubtarget *ST) const; SDValue LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG, const ARMSubtarget *ST) const; + SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFSINCOS(SDValue Op, SelectionDAG &DAG) const; SDValue LowerDivRem(SDValue Op, SelectionDAG &DAG) const; SDValue LowerDIV_Windows(SDValue Op, SelectionDAG &DAG, bool Signed) const; @@ -693,6 +718,8 @@ class VectorType; SDValue LowerFP_EXTEND(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFP_TO_INT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerINT_TO_FP(SDValue Op, SelectionDAG &DAG) const; + void lowerABS(SDNode *N, SmallVectorImpl<SDValue> &Results, + SelectionDAG &DAG) const; unsigned getRegisterByName(const char* RegName, EVT VT, SelectionDAG &DAG) const override; @@ -755,15 +782,13 @@ class VectorType; /// IsEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. Targets which want to do tail call /// optimization should implement this function. - bool IsEligibleForTailCallOptimization(SDValue Callee, - CallingConv::ID CalleeCC, - bool isVarArg, - bool isCalleeStructRet, - bool isCallerStructRet, - const SmallVectorImpl<ISD::OutputArg> &Outs, - const SmallVectorImpl<SDValue> &OutVals, - const SmallVectorImpl<ISD::InputArg> &Ins, - SelectionDAG& DAG) const; + bool IsEligibleForTailCallOptimization( + SDValue Callee, CallingConv::ID CalleeCC, bool isVarArg, + bool isCalleeStructRet, bool isCallerStructRet, + const SmallVectorImpl<ISD::OutputArg> &Outs, + const SmallVectorImpl<SDValue> &OutVals, + const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG, + const bool isIndirect) const; bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg, @@ -781,6 +806,8 @@ class VectorType; bool shouldConsiderGEPOffsetSplit() const override { return true; } + bool isUnsupportedFloatingType(EVT VT) const; + SDValue getCMOV(const SDLoc &dl, EVT VT, SDValue FalseVal, SDValue TrueVal, SDValue ARMcc, SDValue CCR, SDValue Cmp, SelectionDAG &DAG) const; @@ -806,11 +833,15 @@ class VectorType; MachineBasicBlock *MBB) const; MachineBasicBlock *EmitLowered__dbzchk(MachineInstr &MI, MachineBasicBlock *MBB) const; + void addMVEVectorTypes(bool HasMVEFP); + void addAllExtLoads(const MVT From, const MVT To, LegalizeAction Action); + void setAllExpand(MVT VT); }; enum NEONModImmType { VMOVModImm, VMVNModImm, + MVEVMVNModImm, OtherModImm }; diff --git a/lib/Target/ARM/ARMInstrFormats.td b/lib/Target/ARM/ARMInstrFormats.td index 0df48ba61299..bc93a058720c 100644 --- a/lib/Target/ARM/ARMInstrFormats.td +++ b/lib/Target/ARM/ARMInstrFormats.td @@ -1,9 +1,8 @@ //===-- ARMInstrFormats.td - ARM Instruction Formats -------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -110,6 +109,9 @@ def AddrModeT2_i8s4 : AddrMode<15>; def AddrMode_i12 : AddrMode<16>; def AddrMode5FP16 : AddrMode<17>; def AddrModeT2_ldrex : AddrMode<18>; +def AddrModeT2_i7s4 : AddrMode<19>; +def AddrModeT2_i7s2 : AddrMode<20>; +def AddrModeT2_i7 : AddrMode<21>; // Load / store index mode. class IndexMode<bits<2> val> { @@ -121,14 +123,15 @@ def IndexModePost : IndexMode<2>; def IndexModeUpd : IndexMode<3>; // Instruction execution domain. -class Domain<bits<3> val> { - bits<3> Value = val; +class Domain<bits<4> val> { + bits<4> Value = val; } def GenericDomain : Domain<0>; def VFPDomain : Domain<1>; // Instructions in VFP domain only def NeonDomain : Domain<2>; // Instructions in Neon domain only def VFPNeonDomain : Domain<3>; // Instructions in both VFP & Neon domains def VFPNeonA8Domain : Domain<5>; // Instructions in VFP & Neon under A8 +def MVEDomain : Domain<8>; // Instructions in MVE and ARMv8.1m //===----------------------------------------------------------------------===// // ARM special operands. @@ -185,6 +188,86 @@ def s_cc_out : OptionalDefOperand<OtherVT, (ops CCR), (ops (i32 CPSR))> { let DecoderMethod = "DecodeCCOutOperand"; } +// VPT predicate + +def VPTPredNOperand : AsmOperandClass { + let Name = "VPTPredN"; + let PredicateMethod = "isVPTPred"; +} +def VPTPredROperand : AsmOperandClass { + let Name = "VPTPredR"; + let PredicateMethod = "isVPTPred"; +} +def undef_tied_input; + +// Operand classes for the cluster of MC operands describing a +// VPT-predicated MVE instruction. +// +// There are two of these classes. Both of them have the same first +// two options: +// +// $cond (an integer) indicates the instruction's predication status: +// * ARMVCC::None means it's unpredicated +// * ARMVCC::Then means it's in a VPT block and appears with the T suffix +// * ARMVCC::Else means it's in a VPT block and appears with the E suffix. +// During code generation, unpredicated and predicated instructions +// are indicated by setting this parameter to 'None' or to 'Then'; the +// third value 'Else' is only used for assembly and disassembly. +// +// $cond_reg (type VCCR) gives the input predicate register. This is +// always either zero_reg or VPR, but needs to be modelled as an +// explicit operand so that it can be register-allocated and spilled +// when these operands are used in code generation). +// +// For 'vpred_r', there's an extra operand $inactive, which specifies +// the vector register which will supply any lanes of the output +// register that the predication mask prevents from being written by +// this instruction. It's always tied to the actual output register +// (i.e. must be allocated into the same physical reg), but again, +// code generation will need to model it as a separate input value. +// +// 'vpred_n' doesn't have that extra operand: it only has $cond and +// $cond_reg. This variant is used for any instruction that can't, or +// doesn't want to, tie $inactive to the output register. Sometimes +// that's because another input parameter is already tied to it (e.g. +// instructions that both read and write their Qd register even when +// unpredicated, either because they only partially overwrite it like +// a narrowing integer conversion, or simply because the instruction +// encoding doesn't have enough register fields to make the output +// independent of all inputs). It can also be because the instruction +// is defined to set disabled output lanes to zero rather than leaving +// them unchanged (vector loads), or because it doesn't output a +// vector register at all (stores, compares). In any of these +// situations it's unnecessary to have an extra operand tied to the +// output, and inconvenient to leave it there unused. + +// Base class for both kinds of vpred. +class vpred_ops<dag extra_op, dag extra_mi> : OperandWithDefaultOps<OtherVT, + !con((ops (i32 0), (i32 zero_reg)), extra_op)> { + let PrintMethod = "printVPTPredicateOperand"; + let OperandNamespace = "ARM"; + let MIOperandInfo = !con((ops i32imm:$cond, VCCR:$cond_reg), extra_mi); + + // For convenience, we provide a string value that can be appended + // to the constraints string. It's empty for vpred_n, and for + // vpred_r it ties the $inactive operand to the output q-register + // (which by convention will be called $Qd). + string vpred_constraint; +} + +def vpred_r : vpred_ops<(ops (v4i32 undef_tied_input)), (ops MQPR:$inactive)> { + let ParserMatchClass = VPTPredROperand; + let OperandType = "OPERAND_VPRED_R"; + let DecoderMethod = "DecodeVpredROperand"; + let vpred_constraint = ",$Qd = $vp.inactive"; +} + +def vpred_n : vpred_ops<(ops), (ops)> { + let ParserMatchClass = VPTPredNOperand; + let OperandType = "OPERAND_VPRED_N"; + let vpred_constraint = ""; +} + // ARM special operands for disassembly only. // def SetEndAsmOperand : ImmAsmOperand<0,1> { @@ -285,6 +368,8 @@ class VFP3InstAlias<string Asm, dag Result, bit EmitPriority = 0> : InstAlias<Asm, Result, EmitPriority>, Requires<[HasVFP3]>; class NEONInstAlias<string Asm, dag Result, bit EmitPriority = 0> : InstAlias<Asm, Result, EmitPriority>, Requires<[HasNEON]>; +class MVEInstAlias<string Asm, dag Result, bit EmitPriority = 1> + : InstAlias<Asm, Result, EmitPriority>, Requires<[HasMVEInt, IsThumb]>; class VFP2MnemonicAlias<string src, string dst> : MnemonicAlias<src, dst>, @@ -325,8 +410,8 @@ class InstTemplate<AddrMode am, int sz, IndexMode im, let TSFlags{12-7} = Form; let TSFlags{13} = isUnaryDataProc; let TSFlags{14} = canXformTo16Bit; - let TSFlags{17-15} = D.Value; - let TSFlags{18} = thumbArithFlagSetting; + let TSFlags{18-15} = D.Value; + let TSFlags{19} = thumbArithFlagSetting; let Constraints = cstr; let Itinerary = itin; @@ -382,6 +467,8 @@ class VFP2AsmPseudo<string asm, dag iops, dag oops = (outs)> : AsmPseudoInst<asm, iops, oops>, Requires<[HasVFP2]>; class NEONAsmPseudo<string asm, dag iops, dag oops = (outs)> : AsmPseudoInst<asm, iops, oops>, Requires<[HasNEON]>; +class MVEAsmPseudo<string asm, dag iops, dag oops = (outs)> + : AsmPseudoInst<asm, iops, oops>, Requires<[HasMVEInt]>; // Pseudo instructions for the code generator. class PseudoInst<dag oops, dag iops, InstrItinClass itin, list<dag> pattern> @@ -1556,6 +1643,8 @@ class AHI5<bits<4> opcod1, bits<2> opcod2, dag oops, dag iops, // Loads & stores operate on both NEON and VFP pipelines. let D = VFPNeonDomain; + + let isUnpredicable = 1; // FP16 instructions cannot in general be conditional } // VFP Load / store multiple pseudo instructions. @@ -1903,6 +1992,8 @@ class AHuI<bits<5> opcod1, bits<2> opcod2, bits<4> opcod3, bits<2> opcod4, let Inst{11-8} = 0b1001; // Half precision let Inst{7-6} = opcod4; let Inst{4} = opcod5; + + let isUnpredicable = 1; // FP16 instructions cannot in general be conditional } // Half precision, unary, non-predicated @@ -1931,6 +2022,8 @@ class AHuInp<bits<5> opcod1, bits<2> opcod2, bits<4> opcod3, bits<2> opcod4, let Inst{11-8} = 0b1001; // Half precision let Inst{7-6} = opcod4; let Inst{4} = opcod5; + + let isUnpredicable = 1; // FP16 instructions cannot in general be conditional } // Half precision, binary @@ -1957,6 +2050,8 @@ class AHbI<bits<5> opcod1, bits<2> opcod2, bit op6, bit op4, dag oops, dag iops, let Inst{11-8} = 0b1001; // Half precision let Inst{6} = op6; let Inst{4} = op4; + + let isUnpredicable = 1; // FP16 instructions cannot in general be conditional } // Half precision, binary, not predicated @@ -1986,6 +2081,8 @@ class AHbInp<bits<5> opcod1, bits<2> opcod2, bit opcod3, dag oops, dag iops, let Inst{11-8} = 0b1001; // Half precision let Inst{6} = opcod3; let Inst{4} = 0; + + let isUnpredicable = 1; // FP16 instructions cannot in general be conditional } // VFP conversion instructions @@ -2494,7 +2591,7 @@ class NEONFPPat<dag pattern, dag result> : Pat<pattern, result> { // VFP/NEON Instruction aliases for type suffices. // Note: When EmitPriority == 1, the alias will be used for printing class VFPDataTypeInstAlias<string opc, string dt, string asm, dag Result, bit EmitPriority = 0> : - InstAlias<!strconcat(opc, dt, "\t", asm), Result, EmitPriority>, Requires<[HasVFP2]>; + InstAlias<!strconcat(opc, dt, "\t", asm), Result, EmitPriority>, Requires<[HasFPRegs]>; // Note: When EmitPriority == 1, the alias will be used for printing multiclass VFPDTAnyInstAlias<string opc, string asm, dag Result, bit EmitPriority = 0> { diff --git a/lib/Target/ARM/ARMInstrInfo.cpp b/lib/Target/ARM/ARMInstrInfo.cpp index bcc31f5fa4cc..388c889349b7 100644 --- a/lib/Target/ARM/ARMInstrInfo.cpp +++ b/lib/Target/ARM/ARMInstrInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMInstrInfo.cpp - ARM Instruction Information --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -95,7 +94,7 @@ void ARMInstrInfo::expandLoadStackGuard(MachineBasicBlock::iterator MI) const { const ARMSubtarget &Subtarget = MF.getSubtarget<ARMSubtarget>(); const TargetMachine &TM = MF.getTarget(); - if (!Subtarget.useMovt(MF)) { + if (!Subtarget.useMovt()) { if (TM.isPositionIndependent()) expandLoadStackGuardBase(MI, ARM::LDRLIT_ga_pcrel, ARM::LDRi12); else diff --git a/lib/Target/ARM/ARMInstrInfo.h b/lib/Target/ARM/ARMInstrInfo.h index c87fb97448c9..042b53f0f8c3 100644 --- a/lib/Target/ARM/ARMInstrInfo.h +++ b/lib/Target/ARM/ARMInstrInfo.h @@ -1,9 +1,8 @@ //===-- ARMInstrInfo.h - ARM Instruction Information ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index 13abdc9687ec..e35145463852 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -1,9 +1,8 @@ //===- ARMInstrInfo.td - Target Description for ARM Target -*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -100,6 +99,18 @@ def SDT_LongMac : SDTypeProfile<2, 4, [SDTCisVT<0, i32>, SDTCisSameAs<0, 4>, SDTCisSameAs<0, 5>]>; +// ARMlsll, ARMlsrl, ARMasrl +def SDT_ARMIntShiftParts : SDTypeProfile<2, 3, [SDTCisSameAs<0, 1>, + SDTCisSameAs<0, 2>, + SDTCisSameAs<0, 3>, + SDTCisInt<0>, + SDTCisInt<4>]>; + +// TODO Add another operand for 'Size' so that we can re-use this node when we +// start supporting *TP versions. +def SDT_ARMWhileLoop : SDTypeProfile<0, 2, [SDTCisVT<0, i32>, + SDTCisVT<1, OtherVT>]>; + def ARMSmlald : SDNode<"ARMISD::SMLALD", SDT_LongMac>; def ARMSmlaldx : SDNode<"ARMISD::SMLALDX", SDT_LongMac>; def ARMSmlsld : SDNode<"ARMISD::SMLSLD", SDT_LongMac>; @@ -172,6 +183,10 @@ def ARMcmpZ : SDNode<"ARMISD::CMPZ", SDT_ARMCmp, def ARMpic_add : SDNode<"ARMISD::PIC_ADD", SDT_ARMPICAdd>; +def ARMasrl : SDNode<"ARMISD::ASRL", SDT_ARMIntShiftParts, []>; +def ARMlsrl : SDNode<"ARMISD::LSRL", SDT_ARMIntShiftParts, []>; +def ARMlsll : SDNode<"ARMISD::LSLL", SDT_ARMIntShiftParts, []>; + def ARMsrl_flag : SDNode<"ARMISD::SRL_FLAG", SDTIntUnaryOp, [SDNPOutGlue]>; def ARMsra_flag : SDNode<"ARMISD::SRA_FLAG", SDTIntUnaryOp, [SDNPOutGlue]>; def ARMrrx : SDNode<"ARMISD::RRX" , SDTIntUnaryOp, [SDNPInGlue ]>; @@ -214,189 +229,44 @@ def ARMsmlalbt : SDNode<"ARMISD::SMLALBT", SDT_LongMac, []>; def ARMsmlaltb : SDNode<"ARMISD::SMLALTB", SDT_LongMac, []>; def ARMsmlaltt : SDNode<"ARMISD::SMLALTT", SDT_LongMac, []>; -//===----------------------------------------------------------------------===// -// ARM Instruction Predicate Definitions. -// -def HasV4T : Predicate<"Subtarget->hasV4TOps()">, - AssemblerPredicate<"HasV4TOps", "armv4t">; -def NoV4T : Predicate<"!Subtarget->hasV4TOps()">; -def HasV5T : Predicate<"Subtarget->hasV5TOps()">, - AssemblerPredicate<"HasV5TOps", "armv5t">; -def NoV5T : Predicate<"!Subtarget->hasV5TOps()">; -def HasV5TE : Predicate<"Subtarget->hasV5TEOps()">, - AssemblerPredicate<"HasV5TEOps", "armv5te">; -def HasV6 : Predicate<"Subtarget->hasV6Ops()">, - AssemblerPredicate<"HasV6Ops", "armv6">; -def NoV6 : Predicate<"!Subtarget->hasV6Ops()">; -def HasV6M : Predicate<"Subtarget->hasV6MOps()">, - AssemblerPredicate<"HasV6MOps", - "armv6m or armv6t2">; -def HasV8MBaseline : Predicate<"Subtarget->hasV8MBaselineOps()">, - AssemblerPredicate<"HasV8MBaselineOps", - "armv8m.base">; -def HasV8MMainline : Predicate<"Subtarget->hasV8MMainlineOps()">, - AssemblerPredicate<"HasV8MMainlineOps", - "armv8m.main">; -def HasV6T2 : Predicate<"Subtarget->hasV6T2Ops()">, - AssemblerPredicate<"HasV6T2Ops", "armv6t2">; -def NoV6T2 : Predicate<"!Subtarget->hasV6T2Ops()">; -def HasV6K : Predicate<"Subtarget->hasV6KOps()">, - AssemblerPredicate<"HasV6KOps", "armv6k">; -def NoV6K : Predicate<"!Subtarget->hasV6KOps()">; -def HasV7 : Predicate<"Subtarget->hasV7Ops()">, - AssemblerPredicate<"HasV7Ops", "armv7">; -def HasV8 : Predicate<"Subtarget->hasV8Ops()">, - AssemblerPredicate<"HasV8Ops", "armv8">; -def PreV8 : Predicate<"!Subtarget->hasV8Ops()">, - AssemblerPredicate<"!HasV8Ops", "armv7 or earlier">; -def HasV8_1a : Predicate<"Subtarget->hasV8_1aOps()">, - AssemblerPredicate<"HasV8_1aOps", "armv8.1a">; -def HasV8_2a : Predicate<"Subtarget->hasV8_2aOps()">, - AssemblerPredicate<"HasV8_2aOps", "armv8.2a">; -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 NoVFP : Predicate<"!Subtarget->hasVFP2()">; -def HasVFP2 : Predicate<"Subtarget->hasVFP2()">, - AssemblerPredicate<"FeatureVFP2", "VFP2">; -def HasVFP3 : Predicate<"Subtarget->hasVFP3()">, - AssemblerPredicate<"FeatureVFP3", "VFP3">; -def HasVFP4 : Predicate<"Subtarget->hasVFP4()">, - AssemblerPredicate<"FeatureVFP4", "VFP4">; -def HasDPVFP : Predicate<"!Subtarget->isFPOnlySP()">, - AssemblerPredicate<"!FeatureVFPOnlySP", - "double precision VFP">; -def HasFPARMv8 : Predicate<"Subtarget->hasFPARMv8()">, - AssemblerPredicate<"FeatureFPARMv8", "FPARMv8">; -def HasNEON : Predicate<"Subtarget->hasNEON()">, - AssemblerPredicate<"FeatureNEON", "NEON">; -def HasSHA2 : Predicate<"Subtarget->hasSHA2()">, - AssemblerPredicate<"FeatureSHA2", "sha2">; -def HasAES : Predicate<"Subtarget->hasAES()">, - AssemblerPredicate<"FeatureAES", "aes">; -def HasCrypto : Predicate<"Subtarget->hasCrypto()">, - AssemblerPredicate<"FeatureCrypto", "crypto">; -def HasDotProd : Predicate<"Subtarget->hasDotProd()">, - AssemblerPredicate<"FeatureDotProd", "dotprod">; -def HasCRC : Predicate<"Subtarget->hasCRC()">, - AssemblerPredicate<"FeatureCRC", "crc">; -def HasRAS : Predicate<"Subtarget->hasRAS()">, - AssemblerPredicate<"FeatureRAS", "ras">; -def HasFP16 : Predicate<"Subtarget->hasFP16()">, - AssemblerPredicate<"FeatureFP16","half-float conversions">; -def HasFullFP16 : Predicate<"Subtarget->hasFullFP16()">, - AssemblerPredicate<"FeatureFullFP16","full half-float">; -def HasFP16FML : Predicate<"Subtarget->hasFP16FML()">, - AssemblerPredicate<"FeatureFP16FML","full half-float fml">; -def HasDivideInThumb : Predicate<"Subtarget->hasDivideInThumbMode()">, - AssemblerPredicate<"FeatureHWDivThumb", "divide in THUMB">; -def HasDivideInARM : Predicate<"Subtarget->hasDivideInARMMode()">, - AssemblerPredicate<"FeatureHWDivARM", "divide in ARM">; -def HasDSP : Predicate<"Subtarget->hasDSP()">, - AssemblerPredicate<"FeatureDSP", "dsp">; -def HasDB : Predicate<"Subtarget->hasDataBarrier()">, - AssemblerPredicate<"FeatureDB", - "data-barriers">; -def HasDFB : Predicate<"Subtarget->hasFullDataBarrier()">, - AssemblerPredicate<"FeatureDFB", - "full-data-barrier">; -def HasV7Clrex : Predicate<"Subtarget->hasV7Clrex()">, - AssemblerPredicate<"FeatureV7Clrex", - "v7 clrex">; -def HasAcquireRelease : Predicate<"Subtarget->hasAcquireRelease()">, - AssemblerPredicate<"FeatureAcquireRelease", - "acquire/release">; -def HasMP : Predicate<"Subtarget->hasMPExtension()">, - AssemblerPredicate<"FeatureMP", - "mp-extensions">; -def HasVirtualization: Predicate<"false">, - AssemblerPredicate<"FeatureVirtualization", - "virtualization-extensions">; -def HasTrustZone : Predicate<"Subtarget->hasTrustZone()">, - AssemblerPredicate<"FeatureTrustZone", - "TrustZone">; -def Has8MSecExt : Predicate<"Subtarget->has8MSecExt()">, - AssemblerPredicate<"Feature8MSecExt", - "ARMv8-M Security Extensions">; -def HasZCZ : Predicate<"Subtarget->hasZeroCycleZeroing()">; -def UseNEONForFP : Predicate<"Subtarget->useNEONForSinglePrecisionFP()">; -def DontUseNEONForFP : Predicate<"!Subtarget->useNEONForSinglePrecisionFP()">; -def IsThumb : Predicate<"Subtarget->isThumb()">, - AssemblerPredicate<"ModeThumb", "thumb">; -def IsThumb1Only : Predicate<"Subtarget->isThumb1Only()">; -def IsThumb2 : Predicate<"Subtarget->isThumb2()">, - AssemblerPredicate<"ModeThumb,FeatureThumb2", - "thumb2">; -def IsMClass : Predicate<"Subtarget->isMClass()">, - AssemblerPredicate<"FeatureMClass", "armv*m">; -def IsNotMClass : Predicate<"!Subtarget->isMClass()">, - AssemblerPredicate<"!FeatureMClass", - "!armv*m">; -def IsARM : Predicate<"!Subtarget->isThumb()">, - AssemblerPredicate<"!ModeThumb", "arm-mode">; -def IsMachO : Predicate<"Subtarget->isTargetMachO()">; -def IsNotMachO : Predicate<"!Subtarget->isTargetMachO()">; -def IsNaCl : Predicate<"Subtarget->isTargetNaCl()">; -def IsWindows : Predicate<"Subtarget->isTargetWindows()">; -def IsNotWindows : Predicate<"!Subtarget->isTargetWindows()">; -def IsReadTPHard : Predicate<"Subtarget->isReadTPHard()">; -def IsReadTPSoft : Predicate<"!Subtarget->isReadTPHard()">; -def UseNaClTrap : Predicate<"Subtarget->useNaClTrap()">, - AssemblerPredicate<"FeatureNaClTrap", "NaCl">; -def DontUseNaClTrap : Predicate<"!Subtarget->useNaClTrap()">; - -def UseNegativeImmediates : - Predicate<"false">, - AssemblerPredicate<"!FeatureNoNegativeImmediates", - "NegativeImmediates">; - -// FIXME: Eventually this will be just "hasV6T2Ops". -let RecomputePerFunction = 1 in { - def UseMovt : Predicate<"Subtarget->useMovt(*MF)">; - def DontUseMovt : Predicate<"!Subtarget->useMovt(*MF)">; - def UseMovtInPic : Predicate<"Subtarget->useMovt(*MF) && Subtarget->allowPositionIndependentMovt()">; - def DontUseMovtInPic : Predicate<"!Subtarget->useMovt(*MF) || !Subtarget->allowPositionIndependentMovt()">; +// Vector operations shared between NEON and MVE - def UseFPVMLx: Predicate<"((Subtarget->useFPVMLx() &&" - " TM.Options.AllowFPOpFusion != FPOpFusion::Fast) ||" - "MF->getFunction().optForMinSize())">; -} -def UseMulOps : Predicate<"Subtarget->useMulOps()">; +def ARMvdup : SDNode<"ARMISD::VDUP", SDTypeProfile<1, 1, [SDTCisVec<0>]>>; -// Prefer fused MAC for fp mul + add over fp VMLA / VMLS if they are available. -// But only select them if more precision in FP computation is allowed, and when -// they are not slower than a mul + add sequence. -// Do not use them for Darwin platforms. -def UseFusedMAC : Predicate<"(TM.Options.AllowFPOpFusion ==" - " FPOpFusion::Fast && " - " Subtarget->hasVFP4()) && " - "!Subtarget->isTargetDarwin() &&" - "Subtarget->useFPVMLx()">; +// VDUPLANE can produce a quad-register result from a double-register source, +// so the result is not constrained to match the source. +def ARMvduplane : SDNode<"ARMISD::VDUPLANE", + SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, + SDTCisVT<2, i32>]>>; -def HasFastVGETLNi32 : Predicate<"!Subtarget->hasSlowVGETLNi32()">; -def HasSlowVGETLNi32 : Predicate<"Subtarget->hasSlowVGETLNi32()">; +def SDTARMVSHUF : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisSameAs<0, 1>]>; +def ARMvrev64 : SDNode<"ARMISD::VREV64", SDTARMVSHUF>; +def ARMvrev32 : SDNode<"ARMISD::VREV32", SDTARMVSHUF>; +def ARMvrev16 : SDNode<"ARMISD::VREV16", SDTARMVSHUF>; -def HasFastVDUP32 : Predicate<"!Subtarget->hasSlowVDUP32()">; -def HasSlowVDUP32 : Predicate<"Subtarget->hasSlowVDUP32()">; +def SDTARMVGETLN : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, SDTCisInt<1>, + SDTCisVT<2, i32>]>; +def ARMvgetlaneu : SDNode<"ARMISD::VGETLANEu", SDTARMVGETLN>; +def ARMvgetlanes : SDNode<"ARMISD::VGETLANEs", SDTARMVGETLN>; -def UseVMOVSR : Predicate<"Subtarget->preferVMOVSR() ||" - "!Subtarget->useNEONForSinglePrecisionFP()">; -def DontUseVMOVSR : Predicate<"!Subtarget->preferVMOVSR() &&" - "Subtarget->useNEONForSinglePrecisionFP()">; +def SDTARMVMOVIMM : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisVT<1, i32>]>; +def ARMvmovImm : SDNode<"ARMISD::VMOVIMM", SDTARMVMOVIMM>; +def ARMvmvnImm : SDNode<"ARMISD::VMVNIMM", SDTARMVMOVIMM>; +def ARMvmovFPImm : SDNode<"ARMISD::VMOVFPIMM", SDTARMVMOVIMM>; -let RecomputePerFunction = 1 in { - def IsLE : Predicate<"MF->getDataLayout().isLittleEndian()">; - def IsBE : Predicate<"MF->getDataLayout().isBigEndian()">; -} -def GenExecuteOnly : Predicate<"Subtarget->genExecuteOnly()">; +def SDTARMVSHIMM : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, i32>]>; +def SDTARMVSH : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisSameAs<0, 2>,]>; +def ARMvshlImm : SDNode<"ARMISD::VSHLIMM", SDTARMVSHIMM>; +def ARMvshrsImm : SDNode<"ARMISD::VSHRsIMM", SDTARMVSHIMM>; +def ARMvshruImm : SDNode<"ARMISD::VSHRuIMM", SDTARMVSHIMM>; +def ARMvshls : SDNode<"ARMISD::VSHLs", SDTARMVSH>; +def ARMvshlu : SDNode<"ARMISD::VSHLu", SDTARMVSH>; -// Armv8.5-A extensions -def HasSB : Predicate<"Subtarget->hasSB()">, - AssemblerPredicate<"FeatureSB", "sb">; +def ARMWLS : SDNode<"ARMISD::WLS", SDT_ARMWhileLoop, + [SDNPHasChain]>; //===----------------------------------------------------------------------===// // ARM Flag Definitions. @@ -552,6 +422,16 @@ def reglist : Operand<i32> { let DecoderMethod = "DecodeRegListOperand"; } +// A list of general purpose registers and APSR separated by comma. +// Used by CLRM +def RegListWithAPSRAsmOperand : AsmOperandClass { let Name = "RegListWithAPSR"; } +def reglist_with_apsr : Operand<i32> { + let EncoderMethod = "getRegisterListOpValue"; + let ParserMatchClass = RegListWithAPSRAsmOperand; + let PrintMethod = "printRegisterList"; + let DecoderMethod = "DecodeRegListOperand"; +} + def GPRPairOp : RegisterOperand<GPRPair, "printGPRPairOperand">; def DPRRegListAsmOperand : AsmOperandClass { @@ -576,6 +456,21 @@ def spr_reglist : Operand<i32> { let DecoderMethod = "DecodeSPRRegListOperand"; } +def FPSRegListWithVPRAsmOperand : AsmOperandClass { let Name = + "FPSRegListWithVPR"; } +def fp_sreglist_with_vpr : Operand<i32> { + let EncoderMethod = "getRegisterListOpValue"; + let ParserMatchClass = FPSRegListWithVPRAsmOperand; + let PrintMethod = "printRegisterList"; +} +def FPDRegListWithVPRAsmOperand : AsmOperandClass { let Name = + "FPDRegListWithVPR"; } +def fp_dreglist_with_vpr : Operand<i32> { + let EncoderMethod = "getRegisterListOpValue"; + let ParserMatchClass = FPDRegListWithVPRAsmOperand; + let PrintMethod = "printRegisterList"; +} + // An operand for the CONSTPOOL_ENTRY pseudo-instruction. def cpinst_operand : Operand<i32> { let PrintMethod = "printCPInstOperand"; @@ -621,6 +516,55 @@ def rot_imm : Operand<i32>, PatLeaf<(i32 imm), [{ let ParserMatchClass = RotImmAsmOperand; } +// Power-of-two operand for MVE VIDUP and friends, which encode +// {1,2,4,8} as its log to base 2, i.e. as {0,1,2,3} respectively +def MVE_VIDUP_imm_asmoperand : AsmOperandClass { + let Name = "VIDUP_imm"; + let PredicateMethod = "isPowerTwoInRange<1,8>"; + let RenderMethod = "addPowerTwoOperands"; + let DiagnosticString = "vector increment immediate must be 1, 2, 4 or 8"; +} +def MVE_VIDUP_imm : Operand<i32> { + let EncoderMethod = "getPowerTwoOpValue"; + let DecoderMethod = "DecodePowerTwoOperand<0,3>"; + let ParserMatchClass = MVE_VIDUP_imm_asmoperand; +} + +// Pair vector indexing +class MVEPairVectorIndexOperand<string start, string end> : AsmOperandClass { + let Name = "MVEPairVectorIndex"#start; + let RenderMethod = "addMVEPairVectorIndexOperands"; + let PredicateMethod = "isMVEPairVectorIndex<"#start#", "#end#">"; +} + +class MVEPairVectorIndex<string opval> : Operand<i32> { + let PrintMethod = "printVectorIndex"; + let EncoderMethod = "getMVEPairVectorIndexOpValue<"#opval#">"; + let DecoderMethod = "DecodeMVEPairVectorIndexOperand<"#opval#">"; + let MIOperandInfo = (ops i32imm); +} + +def MVEPairVectorIndex0 : MVEPairVectorIndex<"0"> { + let ParserMatchClass = MVEPairVectorIndexOperand<"0", "1">; +} + +def MVEPairVectorIndex2 : MVEPairVectorIndex<"2"> { + let ParserMatchClass = MVEPairVectorIndexOperand<"2", "3">; +} + +// Vector indexing +class MVEVectorIndexOperand<int NumLanes> : AsmOperandClass { + let Name = "MVEVectorIndex"#NumLanes; + let RenderMethod = "addMVEVectorIndexOperands"; + let PredicateMethod = "isVectorIndexInRange<"#NumLanes#">"; +} + +class MVEVectorIndex<int NumLanes> : Operand<i32> { + let PrintMethod = "printVectorIndex"; + let ParserMatchClass = MVEVectorIndexOperand<NumLanes>; + let MIOperandInfo = (ops i32imm); +} + // shift_imm: An integer that encodes a shift amount and the type of shift // (asr or lsl). The 6-bit immediate encodes as: // {5} 0 ==> lsl @@ -718,24 +662,11 @@ def mod_imm_neg : Operand<i32>, PatLeaf<(imm), [{ } /// arm_i32imm - True for +V6T2, or when isSOImmTwoParVal() -def arm_i32imm : PatLeaf<(imm), [{ - if (Subtarget->useMovt(*MF)) +def arm_i32imm : IntImmLeaf<i32, [{ + if (Subtarget->useMovt()) return true; - return ARM_AM::isSOImmTwoPartVal((unsigned)N->getZExtValue()); -}]> { - // Ideally this would be an IntImmLeaf, but then we wouldn't have access to - // the MachineFunction. - let GISelPredicateCode = [{ - const auto &MF = *MI.getParent()->getParent(); - if (STI.useMovt(MF)) - return true; - - const auto &MO = MI.getOperand(1); - if (!MO.isCImm()) - return false; - return ARM_AM::isSOImmTwoPartVal(MO.getCImm()->getZExtValue()); - }]; -} + return ARM_AM::isSOImmTwoPartVal(Imm.getZExtValue()); +}]>; /// imm0_1 predicate - Immediate in the range [0,1]. def Imm0_1AsmOperand: ImmAsmOperand<0,1> { let Name = "Imm0_1"; } @@ -952,6 +883,32 @@ def imm1_16 : Operand<i32>, ImmLeaf<i32, [{ let ParserMatchClass = Imm1_16AsmOperand; } +def MVEShiftImm1_7AsmOperand: ImmAsmOperand<1,7> { + let Name = "MVEShiftImm1_7"; + // Reason we're doing this is because instruction vshll.s8 t1 encoding + // accepts 1,7 but the t2 encoding accepts 8. By doing this we can get a + // better diagnostic message if someone uses bigger immediate than the t1/t2 + // encodings allow. + let DiagnosticString = "operand must be an immediate in the range [1,8]"; +} +def mve_shift_imm1_7 : Operand<i32> { + let ParserMatchClass = MVEShiftImm1_7AsmOperand; + let EncoderMethod = "getMVEShiftImmOpValue"; +} + +def MVEShiftImm1_15AsmOperand: ImmAsmOperand<1,15> { + let Name = "MVEShiftImm1_15"; + // Reason we're doing this is because instruction vshll.s16 t1 encoding + // accepts 1,15 but the t2 encoding accepts 16. By doing this we can get a + // better diagnostic message if someone uses bigger immediate than the t1/t2 + // encodings allow. + let DiagnosticString = "operand must be an immediate in the range [1,16]"; +} +def mve_shift_imm1_15 : Operand<i32> { + let ParserMatchClass = MVEShiftImm1_15AsmOperand; + let EncoderMethod = "getMVEShiftImmOpValue"; +} + // Define ARM specific addressing modes. // addrmode_imm12 := reg +/- imm12 // @@ -1332,6 +1289,15 @@ def addr_offset_none : MemOperand, let MIOperandInfo = (ops GPR:$base); } +// t_addr_offset_none := reg [r0-r7] +def MemNoOffsetTAsmOperand : AsmOperandClass { let Name = "MemNoOffsetT"; } +def t_addr_offset_none : MemOperand { + let PrintMethod = "printAddrMode7Operand"; + let DecoderMethod = "DecodetGPRRegisterClass"; + let ParserMatchClass = MemNoOffsetTAsmOperand; + let MIOperandInfo = (ops tGPR:$base); +} + def nohash_imm : Operand<i32> { let PrintMethod = "printNoHashImmediate"; } @@ -5932,6 +5898,12 @@ include "ARMInstrVFP.td" include "ARMInstrNEON.td" //===----------------------------------------------------------------------===// +// MVE Support +// + +include "ARMInstrMVE.td" + +//===----------------------------------------------------------------------===// // Assembler aliases // diff --git a/lib/Target/ARM/ARMInstrMVE.td b/lib/Target/ARM/ARMInstrMVE.td new file mode 100644 index 000000000000..3e7ae55c7fc8 --- /dev/null +++ b/lib/Target/ARM/ARMInstrMVE.td @@ -0,0 +1,4591 @@ +//===-- ARMInstrMVE.td - MVE support for ARM ---------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the ARM MVE instruction set. +// +//===----------------------------------------------------------------------===// + +class ExpandImmAsmOp<string shift> : AsmOperandClass { + let Name = !strconcat("ExpandImm", shift); + let PredicateMethod = !strconcat("isExpImm<", shift, ">"); + let RenderMethod = "addImmOperands"; +} +class InvertedExpandImmAsmOp<string shift, string size> : AsmOperandClass { + let Name = !strconcat("InvertedExpandImm", shift, "_", size); + let PredicateMethod = !strconcat("isInvertedExpImm<", shift, ",", size, ">"); + let RenderMethod = "addImmOperands"; +} + +class ExpandImm<string shift> : Operand<i32> { + let ParserMatchClass = ExpandImmAsmOp<shift>; + let EncoderMethod = !strconcat("getExpandedImmOpValue<",shift,",false>"); + let DecoderMethod = !strconcat("DecodeExpandedImmOperand<",shift,">"); + let PrintMethod = "printExpandedImmOperand"; +} +class InvertedExpandImm<string shift, string size> : Operand<i32> { + let ParserMatchClass = InvertedExpandImmAsmOp<shift, size>; + let EncoderMethod = !strconcat("getExpandedImmOpValue<",shift,",true>"); + let PrintMethod = "printExpandedImmOperand"; + // No decoder method needed, because this operand type is only used + // by aliases (VAND and VORN) +} + +def expzero00 : ExpandImm<"0">; +def expzero08 : ExpandImm<"8">; +def expzero16 : ExpandImm<"16">; +def expzero24 : ExpandImm<"24">; + +def expzero00inv16 : InvertedExpandImm<"0", "16">; +def expzero08inv16 : InvertedExpandImm<"8", "16">; + +def expzero00inv32 : InvertedExpandImm<"0", "32">; +def expzero08inv32 : InvertedExpandImm<"8", "32">; +def expzero16inv32 : InvertedExpandImm<"16", "32">; +def expzero24inv32 : InvertedExpandImm<"24", "32">; + +// VPT condition mask +def vpt_mask : Operand<i32> { + let PrintMethod = "printVPTMask"; + let ParserMatchClass = it_mask_asmoperand; + let EncoderMethod = "getVPTMaskOpValue"; + let DecoderMethod = "DecodeVPTMaskOperand"; +} + +// VPT/VCMP restricted predicate for sign invariant types +def pred_restricted_i_asmoperand : AsmOperandClass { + let Name = "CondCodeRestrictedI"; + let RenderMethod = "addITCondCodeOperands"; + let PredicateMethod = "isITCondCodeRestrictedI"; + let ParserMethod = "parseITCondCode"; + let DiagnosticString = "condition code for sign-independent integer "# + "comparison must be EQ or NE"; +} + +// VPT/VCMP restricted predicate for signed types +def pred_restricted_s_asmoperand : AsmOperandClass { + let Name = "CondCodeRestrictedS"; + let RenderMethod = "addITCondCodeOperands"; + let PredicateMethod = "isITCondCodeRestrictedS"; + let ParserMethod = "parseITCondCode"; + let DiagnosticString = "condition code for signed integer "# + "comparison must be EQ, NE, LT, GT, LE or GE"; +} + +// VPT/VCMP restricted predicate for unsigned types +def pred_restricted_u_asmoperand : AsmOperandClass { + let Name = "CondCodeRestrictedU"; + let RenderMethod = "addITCondCodeOperands"; + let PredicateMethod = "isITCondCodeRestrictedU"; + let ParserMethod = "parseITCondCode"; + let DiagnosticString = "condition code for unsigned integer "# + "comparison must be EQ, NE, HS or HI"; +} + +// VPT/VCMP restricted predicate for floating point +def pred_restricted_fp_asmoperand : AsmOperandClass { + let Name = "CondCodeRestrictedFP"; + let RenderMethod = "addITCondCodeOperands"; + let PredicateMethod = "isITCondCodeRestrictedFP"; + let ParserMethod = "parseITCondCode"; + let DiagnosticString = "condition code for floating-point "# + "comparison must be EQ, NE, LT, GT, LE or GE"; +} + +class VCMPPredicateOperand : Operand<i32>; + +def pred_basic_i : VCMPPredicateOperand { + let PrintMethod = "printMandatoryRestrictedPredicateOperand"; + let ParserMatchClass = pred_restricted_i_asmoperand; + let DecoderMethod = "DecodeRestrictedIPredicateOperand"; + let EncoderMethod = "getRestrictedCondCodeOpValue"; +} + +def pred_basic_u : VCMPPredicateOperand { + let PrintMethod = "printMandatoryRestrictedPredicateOperand"; + let ParserMatchClass = pred_restricted_u_asmoperand; + let DecoderMethod = "DecodeRestrictedUPredicateOperand"; + let EncoderMethod = "getRestrictedCondCodeOpValue"; +} + +def pred_basic_s : VCMPPredicateOperand { + let PrintMethod = "printMandatoryRestrictedPredicateOperand"; + let ParserMatchClass = pred_restricted_s_asmoperand; + let DecoderMethod = "DecodeRestrictedSPredicateOperand"; + let EncoderMethod = "getRestrictedCondCodeOpValue"; +} + +def pred_basic_fp : VCMPPredicateOperand { + let PrintMethod = "printMandatoryRestrictedPredicateOperand"; + let ParserMatchClass = pred_restricted_fp_asmoperand; + let DecoderMethod = "DecodeRestrictedFPPredicateOperand"; + let EncoderMethod = "getRestrictedCondCodeOpValue"; +} + +// Register list operands for interleaving load/stores +def VecList2QAsmOperand : AsmOperandClass { + let Name = "VecListTwoMQ"; + let ParserMethod = "parseVectorList"; + let RenderMethod = "addMVEVecListOperands"; + let DiagnosticString = "operand must be a list of two consecutive "# + "q-registers in range [q0,q7]"; +} + +def VecList2Q : RegisterOperand<QQPR, "printMVEVectorListTwoQ"> { + let ParserMatchClass = VecList2QAsmOperand; + let PrintMethod = "printMVEVectorList<2>"; +} + +def VecList4QAsmOperand : AsmOperandClass { + let Name = "VecListFourMQ"; + let ParserMethod = "parseVectorList"; + let RenderMethod = "addMVEVecListOperands"; + let DiagnosticString = "operand must be a list of four consecutive "# + "q-registers in range [q0,q7]"; +} + +def VecList4Q : RegisterOperand<QQQQPR, "printMVEVectorListFourQ"> { + let ParserMatchClass = VecList4QAsmOperand; + let PrintMethod = "printMVEVectorList<4>"; +} + +// taddrmode_imm7 := reg[r0-r7] +/- (imm7 << shift) +class TMemImm7ShiftOffsetAsmOperand<int shift> : AsmOperandClass { + let Name = "TMemImm7Shift"#shift#"Offset"; + let PredicateMethod = "isMemImm7ShiftedOffset<"#shift#",ARM::tGPRRegClassID>"; + let RenderMethod = "addMemImmOffsetOperands"; +} + +class taddrmode_imm7<int shift> : MemOperand { + let ParserMatchClass = TMemImm7ShiftOffsetAsmOperand<shift>; + // They are printed the same way as the T2 imm8 version + let PrintMethod = "printT2AddrModeImm8Operand<false>"; + // This can also be the same as the T2 version. + let EncoderMethod = "getT2AddrModeImmOpValue<7,"#shift#">"; + let DecoderMethod = "DecodeTAddrModeImm7<"#shift#">"; + let MIOperandInfo = (ops tGPR:$base, i32imm:$offsimm); +} + +// t2addrmode_imm7 := reg +/- (imm7) +class MemImm7ShiftOffsetAsmOperand<int shift> : AsmOperandClass { + let Name = "MemImm7Shift"#shift#"Offset"; + let PredicateMethod = "isMemImm7ShiftedOffset<" # shift # + ",ARM::GPRnopcRegClassID>"; + let RenderMethod = "addMemImmOffsetOperands"; +} + +def MemImm7Shift0OffsetAsmOperand : MemImm7ShiftOffsetAsmOperand<0>; +def MemImm7Shift1OffsetAsmOperand : MemImm7ShiftOffsetAsmOperand<1>; +def MemImm7Shift2OffsetAsmOperand : MemImm7ShiftOffsetAsmOperand<2>; +class T2AddrMode_Imm7<int shift> : MemOperand, + ComplexPattern<i32, 2, "SelectT2AddrModeImm7<"#shift#">", []> { + let EncoderMethod = "getT2AddrModeImmOpValue<7,"#shift#">"; + let DecoderMethod = "DecodeT2AddrModeImm7<"#shift#", 0>"; + let ParserMatchClass = + !cast<AsmOperandClass>("MemImm7Shift"#shift#"OffsetAsmOperand"); + let MIOperandInfo = (ops GPRnopc:$base, i32imm:$offsimm); +} + +class t2addrmode_imm7<int shift> : T2AddrMode_Imm7<shift> { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8Operand<false>"; +} + +class MemImm7ShiftOffsetWBAsmOperand<int shift> : AsmOperandClass { + let Name = "MemImm7Shift"#shift#"OffsetWB"; + let PredicateMethod = "isMemImm7ShiftedOffset<" # shift # + ",ARM::rGPRRegClassID>"; + let RenderMethod = "addMemImmOffsetOperands"; +} + +def MemImm7Shift0OffsetWBAsmOperand : MemImm7ShiftOffsetWBAsmOperand<0>; +def MemImm7Shift1OffsetWBAsmOperand : MemImm7ShiftOffsetWBAsmOperand<1>; +def MemImm7Shift2OffsetWBAsmOperand : MemImm7ShiftOffsetWBAsmOperand<2>; + +class t2addrmode_imm7_pre<int shift> : T2AddrMode_Imm7<shift> { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8Operand<true>"; + let ParserMatchClass = + !cast<AsmOperandClass>("MemImm7Shift"#shift#"OffsetWBAsmOperand"); + let DecoderMethod = "DecodeT2AddrModeImm7<"#shift#", 1>"; + let MIOperandInfo = (ops rGPR:$base, i32imm:$offsim); +} + +class t2am_imm7shiftOffsetAsmOperand<int shift> + : AsmOperandClass { let Name = "Imm7Shift"#shift; } +def t2am_imm7shift0OffsetAsmOperand : t2am_imm7shiftOffsetAsmOperand<0>; +def t2am_imm7shift1OffsetAsmOperand : t2am_imm7shiftOffsetAsmOperand<1>; +def t2am_imm7shift2OffsetAsmOperand : t2am_imm7shiftOffsetAsmOperand<2>; + +class t2am_imm7_offset<int shift> : MemOperand { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8OffsetOperand"; + let ParserMatchClass = + !cast<AsmOperandClass>("t2am_imm7shift"#shift#"OffsetAsmOperand"); + let EncoderMethod = "getT2ScaledImmOpValue<7,"#shift#">"; + let DecoderMethod = "DecodeT2Imm7<"#shift#">"; +} + +// Operands for gather/scatter loads of the form [Rbase, Qoffsets] +class MemRegRQOffsetAsmOperand<int shift> : AsmOperandClass { + let Name = "MemRegRQS"#shift#"Offset"; + let PredicateMethod = "isMemRegRQOffset<"#shift#">"; + let RenderMethod = "addMemRegRQOffsetOperands"; +} + +def MemRegRQS0OffsetAsmOperand : MemRegRQOffsetAsmOperand<0>; +def MemRegRQS1OffsetAsmOperand : MemRegRQOffsetAsmOperand<1>; +def MemRegRQS2OffsetAsmOperand : MemRegRQOffsetAsmOperand<2>; +def MemRegRQS3OffsetAsmOperand : MemRegRQOffsetAsmOperand<3>; + +// mve_addr_rq_shift := reg + vreg{ << UXTW #shift} +class mve_addr_rq_shift<int shift> : MemOperand { + let EncoderMethod = "getMveAddrModeRQOpValue"; + let PrintMethod = "printMveAddrModeRQOperand<"#shift#">"; + let ParserMatchClass = + !cast<AsmOperandClass>("MemRegRQS"#shift#"OffsetAsmOperand"); + let DecoderMethod = "DecodeMveAddrModeRQ"; + let MIOperandInfo = (ops GPRnopc:$base, MQPR:$offsreg); +} + +class MemRegQOffsetAsmOperand<int shift> : AsmOperandClass { + let Name = "MemRegQS"#shift#"Offset"; + let PredicateMethod = "isMemRegQOffset<"#shift#">"; + let RenderMethod = "addMemImmOffsetOperands"; +} + +def MemRegQS2OffsetAsmOperand : MemRegQOffsetAsmOperand<2>; +def MemRegQS3OffsetAsmOperand : MemRegQOffsetAsmOperand<3>; + +// mve_addr_q_shift := vreg {+ #imm7s2/4} +class mve_addr_q_shift<int shift> : MemOperand { + let EncoderMethod = "getMveAddrModeQOpValue<"#shift#">"; + // Can be printed same way as other reg + imm operands + let PrintMethod = "printT2AddrModeImm8Operand<false>"; + let ParserMatchClass = + !cast<AsmOperandClass>("MemRegQS"#shift#"OffsetAsmOperand"); + let DecoderMethod = "DecodeMveAddrModeQ<"#shift#">"; + let MIOperandInfo = (ops MQPR:$base, i32imm:$imm); +} + +// --------- Start of base classes for the instructions themselves + +class MVE_MI<dag oops, dag iops, InstrItinClass itin, string asm, + string ops, string cstr, list<dag> pattern> + : Thumb2XI<oops, iops, AddrModeNone, 4, itin, !strconcat(asm, "\t", ops), cstr, + pattern>, + Requires<[HasMVEInt]> { + let D = MVEDomain; + let DecoderNamespace = "MVE"; +} + +// MVE_p is used for most predicated instructions, to add the cluster +// of input operands that provides the VPT suffix (none, T or E) and +// the input predicate register. +class MVE_p<dag oops, dag iops, InstrItinClass itin, string iname, + string suffix, string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_MI<oops, !con(iops, (ins vpred:$vp)), itin, + // If the instruction has a suffix, like vadd.f32, then the + // VPT predication suffix goes before the dot, so the full + // name has to be "vadd${vp}.f32". + !strconcat(iname, "${vp}", + !if(!eq(suffix, ""), "", !strconcat(".", suffix))), + ops, !strconcat(cstr, vpred.vpred_constraint), pattern> { + let Inst{31-29} = 0b111; + let Inst{27-26} = 0b11; +} + +class MVE_f<dag oops, dag iops, InstrItinClass itin, string iname, + string suffix, string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_p<oops, iops, itin, iname, suffix, ops, vpred, cstr, pattern> { + let Predicates = [HasMVEFloat]; +} + +class MVE_MI_with_pred<dag oops, dag iops, InstrItinClass itin, string asm, + string ops, string cstr, list<dag> pattern> + : Thumb2I<oops, iops, AddrModeNone, 4, itin, asm, !strconcat("\t", ops), cstr, + pattern>, + Requires<[HasV8_1MMainline, HasMVEInt]> { + let D = MVEDomain; + let DecoderNamespace = "MVE"; +} + +class MVE_VMOV_lane_base<dag oops, dag iops, InstrItinClass itin, string asm, + string suffix, string ops, string cstr, + list<dag> pattern> + : Thumb2I<oops, iops, AddrModeNone, 4, itin, asm, + !if(!eq(suffix, ""), "", "." # suffix) # "\t" # ops, + cstr, pattern>, + Requires<[HasV8_1MMainline, HasMVEInt]> { + let D = MVEDomain; + let DecoderNamespace = "MVE"; +} + +class MVE_ScalarShift<string iname, dag oops, dag iops, string asm, string cstr, + list<dag> pattern=[]> + : MVE_MI_with_pred<oops, iops, NoItinerary, iname, asm, cstr, pattern> { + let Inst{31-20} = 0b111010100101; + let Inst{8} = 0b1; + +} + +class MVE_ScalarShiftSingleReg<string iname, dag iops, string asm, string cstr, + list<dag> pattern=[]> + : MVE_ScalarShift<iname, (outs rGPR:$RdaDest), iops, asm, cstr, pattern> { + bits<4> RdaDest; + + let Inst{19-16} = RdaDest{3-0}; +} + +class MVE_ScalarShiftSRegImm<string iname, bits<2> op5_4, list<dag> pattern=[]> + : MVE_ScalarShiftSingleReg<iname, (ins rGPR:$RdaSrc, long_shift:$imm), + "$RdaSrc, $imm", "$RdaDest = $RdaSrc", pattern> { + bits<5> imm; + + let Inst{15} = 0b0; + let Inst{14-12} = imm{4-2}; + let Inst{11-8} = 0b1111; + let Inst{7-6} = imm{1-0}; + let Inst{5-4} = op5_4{1-0}; + let Inst{3-0} = 0b1111; +} + +def MVE_SQSHL : MVE_ScalarShiftSRegImm<"sqshl", 0b11>; +def MVE_SRSHR : MVE_ScalarShiftSRegImm<"srshr", 0b10>; +def MVE_UQSHL : MVE_ScalarShiftSRegImm<"uqshl", 0b00>; +def MVE_URSHR : MVE_ScalarShiftSRegImm<"urshr", 0b01>; + +class MVE_ScalarShiftSRegReg<string iname, bits<2> op5_4, list<dag> pattern=[]> + : MVE_ScalarShiftSingleReg<iname, (ins rGPR:$RdaSrc, rGPR:$Rm), + "$RdaSrc, $Rm", "$RdaDest = $RdaSrc", pattern> { + bits<4> Rm; + + let Inst{15-12} = Rm{3-0}; + let Inst{11-8} = 0b1111; + let Inst{7-6} = 0b00; + let Inst{5-4} = op5_4{1-0}; + let Inst{3-0} = 0b1101; +} + +def MVE_SQRSHR : MVE_ScalarShiftSRegReg<"sqrshr", 0b10>; +def MVE_UQRSHL : MVE_ScalarShiftSRegReg<"uqrshl", 0b00>; + +class MVE_ScalarShiftDoubleReg<string iname, dag iops, string asm, + string cstr, list<dag> pattern=[]> + : MVE_ScalarShift<iname, (outs tGPREven:$RdaLo, tGPROdd:$RdaHi), + iops, asm, cstr, pattern> { + bits<4> RdaLo; + bits<4> RdaHi; + + let Inst{19-17} = RdaLo{3-1}; + let Inst{11-9} = RdaHi{3-1}; +} + +class MVE_ScalarShiftDRegImm<string iname, bits<2> op5_4, bit op16, + list<dag> pattern=[]> + : MVE_ScalarShiftDoubleReg< + iname, (ins tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, long_shift:$imm), + "$RdaLo, $RdaHi, $imm", "$RdaLo = $RdaLo_src,$RdaHi = $RdaHi_src", + pattern> { + bits<5> imm; + + let Inst{16} = op16; + let Inst{15} = 0b0; + let Inst{14-12} = imm{4-2}; + let Inst{7-6} = imm{1-0}; + let Inst{5-4} = op5_4{1-0}; + let Inst{3-0} = 0b1111; +} + +class MVE_ScalarShiftDRegReg<string iname, bit op5, bit op16, + list<dag> pattern=[]> + : MVE_ScalarShiftDoubleReg< + iname, (ins tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, rGPR:$Rm), + "$RdaLo, $RdaHi, $Rm", "@earlyclobber $RdaHi,@earlyclobber $RdaLo," + "$RdaLo = $RdaLo_src,$RdaHi = $RdaHi_src", + pattern> { + bits<4> Rm; + + let Inst{16} = op16; + let Inst{15-12} = Rm{3-0}; + let Inst{7-6} = 0b00; + let Inst{5} = op5; + let Inst{4} = 0b0; + let Inst{3-0} = 0b1101; + + // Custom decoder method because of the following overlapping encodings: + // ASRL and SQRSHR + // LSLL and UQRSHL + // SQRSHRL and SQRSHR + // UQRSHLL and UQRSHL + let DecoderMethod = "DecodeMVEOverlappingLongShift"; +} + +def MVE_ASRLr : MVE_ScalarShiftDRegReg<"asrl", 0b1, 0b0, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, + (ARMasrl tGPREven:$RdaLo_src, + tGPROdd:$RdaHi_src, rGPR:$Rm))]>; +def MVE_ASRLi : MVE_ScalarShiftDRegImm<"asrl", 0b10, ?, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, + (ARMasrl tGPREven:$RdaLo_src, + tGPROdd:$RdaHi_src, (i32 imm:$imm)))]>; +def MVE_LSLLr : MVE_ScalarShiftDRegReg<"lsll", 0b0, 0b0, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, + (ARMlsll tGPREven:$RdaLo_src, + tGPROdd:$RdaHi_src, rGPR:$Rm))]>; +def MVE_LSLLi : MVE_ScalarShiftDRegImm<"lsll", 0b00, ?, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, + (ARMlsll tGPREven:$RdaLo_src, + tGPROdd:$RdaHi_src, (i32 imm:$imm)))]>; +def MVE_LSRL : MVE_ScalarShiftDRegImm<"lsrl", 0b01, ?, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, + (ARMlsrl tGPREven:$RdaLo_src, + tGPROdd:$RdaHi_src, (i32 imm:$imm)))]>; + +def MVE_SQRSHRL : MVE_ScalarShiftDRegReg<"sqrshrl", 0b1, 0b1>; +def MVE_SQSHLL : MVE_ScalarShiftDRegImm<"sqshll", 0b11, 0b1>; +def MVE_SRSHRL : MVE_ScalarShiftDRegImm<"srshrl", 0b10, 0b1>; + +def MVE_UQRSHLL : MVE_ScalarShiftDRegReg<"uqrshll", 0b0, 0b1>; +def MVE_UQSHLL : MVE_ScalarShiftDRegImm<"uqshll", 0b00, 0b1>; +def MVE_URSHRL : MVE_ScalarShiftDRegImm<"urshrl", 0b01, 0b1>; + +// start of mve_rDest instructions + +class MVE_rDest<dag oops, dag iops, InstrItinClass itin, + string iname, string suffix, + string ops, string cstr, list<dag> pattern=[]> +// Always use vpred_n and not vpred_r: with the output register being +// a GPR and not a vector register, there can't be any question of +// what to put in its inactive lanes. + : MVE_p<oops, iops, itin, iname, suffix, ops, vpred_n, cstr, pattern> { + + let Inst{25-23} = 0b101; + let Inst{11-9} = 0b111; + let Inst{4} = 0b0; +} + +class MVE_VABAV<string suffix, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_rDest<(outs rGPR:$Rda), (ins rGPR:$Rda_src, MQPR:$Qn, MQPR:$Qm), + NoItinerary, "vabav", suffix, "$Rda, $Qn, $Qm", "$Rda = $Rda_src", + pattern> { + bits<4> Qm; + bits<4> Qn; + bits<4> Rda; + + let Inst{28} = U; + let Inst{22} = 0b0; + let Inst{21-20} = size{1-0}; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{15-12} = Rda{3-0}; + let Inst{8} = 0b1; + let Inst{7} = Qn{3}; + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b1; +} + +def MVE_VABAVs8 : MVE_VABAV<"s8", 0b0, 0b00>; +def MVE_VABAVs16 : MVE_VABAV<"s16", 0b0, 0b01>; +def MVE_VABAVs32 : MVE_VABAV<"s32", 0b0, 0b10>; +def MVE_VABAVu8 : MVE_VABAV<"u8", 0b1, 0b00>; +def MVE_VABAVu16 : MVE_VABAV<"u16", 0b1, 0b01>; +def MVE_VABAVu32 : MVE_VABAV<"u32", 0b1, 0b10>; + +class MVE_VADDV<string iname, string suffix, dag iops, string cstr, + bit A, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_rDest<(outs tGPREven:$Rda), iops, NoItinerary, + iname, suffix, "$Rda, $Qm", cstr, pattern> { + bits<3> Qm; + bits<4> Rda; + + let Inst{28} = U; + let Inst{22-20} = 0b111; + let Inst{19-18} = size{1-0}; + let Inst{17-16} = 0b01; + let Inst{15-13} = Rda{3-1}; + let Inst{12} = 0b0; + let Inst{8-6} = 0b100; + let Inst{5} = A; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +multiclass MVE_VADDV_A<string suffix, bit U, bits<2> size, + list<dag> pattern=[]> { + def acc : MVE_VADDV<"vaddva", suffix, + (ins tGPREven:$Rda_src, MQPR:$Qm), "$Rda = $Rda_src", + 0b1, U, size, pattern>; + def no_acc : MVE_VADDV<"vaddv", suffix, + (ins MQPR:$Qm), "", + 0b0, U, size, pattern>; +} + +defm MVE_VADDVs8 : MVE_VADDV_A<"s8", 0b0, 0b00>; +defm MVE_VADDVs16 : MVE_VADDV_A<"s16", 0b0, 0b01>; +defm MVE_VADDVs32 : MVE_VADDV_A<"s32", 0b0, 0b10>; +defm MVE_VADDVu8 : MVE_VADDV_A<"u8", 0b1, 0b00>; +defm MVE_VADDVu16 : MVE_VADDV_A<"u16", 0b1, 0b01>; +defm MVE_VADDVu32 : MVE_VADDV_A<"u32", 0b1, 0b10>; + +class MVE_VADDLV<string iname, string suffix, dag iops, string cstr, + bit A, bit U, list<dag> pattern=[]> + : MVE_rDest<(outs tGPREven:$RdaLo, tGPROdd:$RdaHi), iops, NoItinerary, iname, + suffix, "$RdaLo, $RdaHi, $Qm", cstr, pattern> { + bits<3> Qm; + bits<4> RdaLo; + bits<4> RdaHi; + + let Inst{28} = U; + let Inst{22-20} = RdaHi{3-1}; + let Inst{19-18} = 0b10; + let Inst{17-16} = 0b01; + let Inst{15-13} = RdaLo{3-1}; + let Inst{12} = 0b0; + let Inst{8-6} = 0b100; + let Inst{5} = A; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +multiclass MVE_VADDLV_A<string suffix, bit U, list<dag> pattern=[]> { + def acc : MVE_VADDLV<"vaddlva", suffix, + (ins tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, MQPR:$Qm), + "$RdaLo = $RdaLo_src,$RdaHi = $RdaHi_src", + 0b1, U, pattern>; + def no_acc : MVE_VADDLV<"vaddlv", suffix, + (ins MQPR:$Qm), "", + 0b0, U, pattern>; +} + + +defm MVE_VADDLVs32 : MVE_VADDLV_A<"s32", 0b0>; +defm MVE_VADDLVu32 : MVE_VADDLV_A<"u32", 0b1>; + +class MVE_VMINMAXNMV<string iname, string suffix, bit sz, + bit bit_17, bit bit_7, list<dag> pattern=[]> + : MVE_rDest<(outs rGPR:$RdaDest), (ins rGPR:$RdaSrc, MQPR:$Qm), + NoItinerary, iname, suffix, "$RdaSrc, $Qm", + "$RdaDest = $RdaSrc", pattern> { + bits<3> Qm; + bits<4> RdaDest; + + let Inst{28} = sz; + let Inst{22-20} = 0b110; + let Inst{19-18} = 0b11; + let Inst{17} = bit_17; + let Inst{16} = 0b0; + let Inst{15-12} = RdaDest{3-0}; + let Inst{8} = 0b1; + let Inst{7} = bit_7; + let Inst{6-5} = 0b00; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; + + let Predicates = [HasMVEFloat]; +} + +multiclass MVE_VMINMAXNMV_fty<string iname, bit bit_7, list<dag> pattern=[]> { + def f32 : MVE_VMINMAXNMV<iname, "f32", 0b0, 0b1, bit_7, pattern>; + def f16 : MVE_VMINMAXNMV<iname, "f16", 0b1, 0b1, bit_7, pattern>; +} + +defm MVE_VMINNMV : MVE_VMINMAXNMV_fty<"vminnmv", 0b1>; +defm MVE_VMAXNMV : MVE_VMINMAXNMV_fty<"vmaxnmv", 0b0>; + +multiclass MVE_VMINMAXNMAV_fty<string iname, bit bit_7, list<dag> pattern=[]> { + def f32 : MVE_VMINMAXNMV<iname, "f32", 0b0, 0b0, bit_7, pattern>; + def f16 : MVE_VMINMAXNMV<iname, "f16", 0b1, 0b0, bit_7, pattern>; +} + +defm MVE_VMINNMAV : MVE_VMINMAXNMAV_fty<"vminnmav", 0b1>; +defm MVE_VMAXNMAV : MVE_VMINMAXNMAV_fty<"vmaxnmav", 0b0>; + +class MVE_VMINMAXV<string iname, string suffix, bit U, bits<2> size, + bit bit_17, bit bit_7, list<dag> pattern=[]> + : MVE_rDest<(outs rGPR:$RdaDest), (ins rGPR:$RdaSrc, MQPR:$Qm), NoItinerary, + iname, suffix, "$RdaSrc, $Qm", "$RdaDest = $RdaSrc", pattern> { + bits<3> Qm; + bits<4> RdaDest; + + let Inst{28} = U; + let Inst{22-20} = 0b110; + let Inst{19-18} = size{1-0}; + let Inst{17} = bit_17; + let Inst{16} = 0b0; + let Inst{15-12} = RdaDest{3-0}; + let Inst{8} = 0b1; + let Inst{7} = bit_7; + let Inst{6-5} = 0b00; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +multiclass MVE_VMINMAXV_ty<string iname, bit bit_7, list<dag> pattern=[]> { + def s8 : MVE_VMINMAXV<iname, "s8", 0b0, 0b00, 0b1, bit_7>; + def s16 : MVE_VMINMAXV<iname, "s16", 0b0, 0b01, 0b1, bit_7>; + def s32 : MVE_VMINMAXV<iname, "s32", 0b0, 0b10, 0b1, bit_7>; + def u8 : MVE_VMINMAXV<iname, "u8", 0b1, 0b00, 0b1, bit_7>; + def u16 : MVE_VMINMAXV<iname, "u16", 0b1, 0b01, 0b1, bit_7>; + def u32 : MVE_VMINMAXV<iname, "u32", 0b1, 0b10, 0b1, bit_7>; +} + +defm MVE_VMINV : MVE_VMINMAXV_ty<"vminv", 0b1>; +defm MVE_VMAXV : MVE_VMINMAXV_ty<"vmaxv", 0b0>; + +multiclass MVE_VMINMAXAV_ty<string iname, bit bit_7, list<dag> pattern=[]> { + def s8 : MVE_VMINMAXV<iname, "s8", 0b0, 0b00, 0b0, bit_7>; + def s16 : MVE_VMINMAXV<iname, "s16", 0b0, 0b01, 0b0, bit_7>; + def s32 : MVE_VMINMAXV<iname, "s32", 0b0, 0b10, 0b0, bit_7>; +} + +defm MVE_VMINAV : MVE_VMINMAXAV_ty<"vminav", 0b1>; +defm MVE_VMAXAV : MVE_VMINMAXAV_ty<"vmaxav", 0b0>; + +class MVE_VMLAMLSDAV<string iname, string suffix, dag iops, string cstr, + bit sz, bit bit_28, bit A, bit X, bit bit_8, bit bit_0, + list<dag> pattern=[]> + : MVE_rDest<(outs tGPREven:$RdaDest), iops, NoItinerary, iname, suffix, + "$RdaDest, $Qn, $Qm", cstr, pattern> { + bits<4> RdaDest; + bits<3> Qm; + bits<3> Qn; + + let Inst{28} = bit_28; + let Inst{22-20} = 0b111; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = sz; + let Inst{15-13} = RdaDest{3-1}; + let Inst{12} = X; + let Inst{8} = bit_8; + let Inst{7-6} = 0b00; + let Inst{5} = A; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = bit_0; +} + +multiclass MVE_VMLAMLSDAV_X<string iname, string suffix, dag iops, string cstr, + bit sz, bit bit_28, bit A, bit bit_8, bit bit_0, + list<dag> pattern=[]> { + def _noexch : MVE_VMLAMLSDAV<iname, suffix, iops, cstr, sz, + bit_28, A, 0b0, bit_8, bit_0, pattern>; + def _exch : MVE_VMLAMLSDAV<iname # "x", suffix, iops, cstr, sz, + bit_28, A, 0b1, bit_8, bit_0, pattern>; +} + +multiclass MVE_VMLAMLSDAV_XA<string iname, string suffix, bit sz, bit bit_28, + bit bit_8, bit bit_0, list<dag> pattern=[]> { + defm _noacc : MVE_VMLAMLSDAV_X<iname, suffix, (ins MQPR:$Qn, MQPR:$Qm), "", + sz, bit_28, 0b0, bit_8, bit_0, pattern>; + defm _acc : MVE_VMLAMLSDAV_X<iname # "a", suffix, + (ins tGPREven:$RdaSrc, MQPR:$Qn, MQPR:$Qm), + "$RdaDest = $RdaSrc", + sz, bit_28, 0b1, bit_8, bit_0, pattern>; +} + +multiclass MVE_VMLADAV_multi<string suffix, bit sz, bit U, bit bit_8, + list<dag> pattern=[]> { + defm "" : MVE_VMLAMLSDAV_XA<"vmladav", suffix, sz, U, bit_8, 0b0, pattern>; +} + +defm MVE_VMLADAVs16 : MVE_VMLADAV_multi<"s16", 0b0, 0b0, 0b0>; +defm MVE_VMLADAVs32 : MVE_VMLADAV_multi<"s32", 0b1, 0b0, 0b0>; +defm MVE_VMLADAVu16 : MVE_VMLADAV_multi<"u16", 0b0, 0b1, 0b0>; +defm MVE_VMLADAVu32 : MVE_VMLADAV_multi<"u32", 0b1, 0b1, 0b0>; + +defm MVE_VMLADAVs8 : MVE_VMLADAV_multi<"s8", 0b0, 0b0, 0b1>; +defm MVE_VMLADAVu8 : MVE_VMLADAV_multi<"u8", 0b0, 0b1, 0b1>; + +// vmlav aliases vmladav +foreach acc = ["_acc", "_noacc"] in { + foreach suffix = ["s8", "s16", "s32", "u8", "u16", "u32"] in { + def : MVEInstAlias<!strconcat("vmlav", !if(!eq(acc, "_acc"), "a", ""), + "${vp}.", suffix, "\t$RdaDest, $Qn, $Qm"), + (!cast<Instruction>("MVE_VMLADAV"#suffix#acc#"_noexch") + tGPREven:$RdaDest, MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; + } +} + +multiclass MVE_VMLSDAV_multi<string suffix, bit sz, bit bit_28, + list<dag> pattern=[]> { + defm "" : MVE_VMLAMLSDAV_XA<"vmlsdav", suffix, sz, bit_28, 0b0, 0b1, pattern>; +} + +defm MVE_VMLSDAVs8 : MVE_VMLSDAV_multi<"s8", 0, 0b1>; +defm MVE_VMLSDAVs16 : MVE_VMLSDAV_multi<"s16", 0, 0b0>; +defm MVE_VMLSDAVs32 : MVE_VMLSDAV_multi<"s32", 1, 0b0>; + +// Base class for VMLALDAV and VMLSLDAV, VRMLALDAVH, VRMLSLDAVH +class MVE_VMLALDAVBase<string iname, string suffix, dag iops, string cstr, + bit sz, bit bit_28, bit A, bit X, bit bit_8, bit bit_0, + list<dag> pattern=[]> + : MVE_rDest<(outs tGPREven:$RdaLoDest, tGPROdd:$RdaHiDest), iops, NoItinerary, + iname, suffix, "$RdaLoDest, $RdaHiDest, $Qn, $Qm", cstr, pattern> { + bits<4> RdaLoDest; + bits<4> RdaHiDest; + bits<3> Qm; + bits<3> Qn; + + let Inst{28} = bit_28; + let Inst{22-20} = RdaHiDest{3-1}; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = sz; + let Inst{15-13} = RdaLoDest{3-1}; + let Inst{12} = X; + let Inst{8} = bit_8; + let Inst{7-6} = 0b00; + let Inst{5} = A; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = bit_0; +} + +multiclass MVE_VMLALDAVBase_X<string iname, string suffix, dag iops, + string cstr, bit sz, bit bit_28, bit A, + bit bit_8, bit bit_0, list<dag> pattern=[]> { + def _noexch : MVE_VMLALDAVBase<iname, suffix, iops, cstr, sz, + bit_28, A, 0b0, bit_8, bit_0, pattern>; + def _exch : MVE_VMLALDAVBase<iname # "x", suffix, iops, cstr, sz, + bit_28, A, 0b1, bit_8, bit_0, pattern>; +} + +multiclass MVE_VMLALDAVBase_XA<string iname, string suffix, bit sz, bit bit_28, + bit bit_8, bit bit_0, list<dag> pattern=[]> { + defm _noacc : MVE_VMLALDAVBase_X< + iname, suffix, (ins MQPR:$Qn, MQPR:$Qm), "", + sz, bit_28, 0b0, bit_8, bit_0, pattern>; + defm _acc : MVE_VMLALDAVBase_X< + iname # "a", suffix, (ins tGPREven:$RdaLoSrc, tGPROdd:$RdaHiSrc, + MQPR:$Qn, MQPR:$Qm), + "$RdaLoDest = $RdaLoSrc,$RdaHiDest = $RdaHiSrc", + sz, bit_28, 0b1, bit_8, bit_0, pattern>; +} + +multiclass MVE_VRMLALDAVH_multi<string suffix, bit U, list<dag> pattern=[]> { + defm "" : MVE_VMLALDAVBase_XA< + "vrmlaldavh", suffix, 0b0, U, 0b1, 0b0, pattern>; +} + +defm MVE_VRMLALDAVHs32 : MVE_VRMLALDAVH_multi<"s32", 0>; +defm MVE_VRMLALDAVHu32 : MVE_VRMLALDAVH_multi<"u32", 1>; + +// vrmlalvh aliases for vrmlaldavh +def : MVEInstAlias<"vrmlalvh${vp}.s32\t$RdaLo, $RdaHi, $Qn, $Qm", + (MVE_VRMLALDAVHs32_noacc_noexch + tGPREven:$RdaLo, tGPROdd:$RdaHi, + MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; +def : MVEInstAlias<"vrmlalvha${vp}.s32\t$RdaLo, $RdaHi, $Qn, $Qm", + (MVE_VRMLALDAVHs32_acc_noexch + tGPREven:$RdaLo, tGPROdd:$RdaHi, + MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; +def : MVEInstAlias<"vrmlalvh${vp}.u32\t$RdaLo, $RdaHi, $Qn, $Qm", + (MVE_VRMLALDAVHu32_noacc_noexch + tGPREven:$RdaLo, tGPROdd:$RdaHi, + MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; +def : MVEInstAlias<"vrmlalvha${vp}.u32\t$RdaLo, $RdaHi, $Qn, $Qm", + (MVE_VRMLALDAVHu32_acc_noexch + tGPREven:$RdaLo, tGPROdd:$RdaHi, + MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; + +multiclass MVE_VMLALDAV_multi<string suffix, bit sz, bit U, + list<dag> pattern=[]> { + defm "" : MVE_VMLALDAVBase_XA<"vmlaldav", suffix, sz, U, 0b0, 0b0, pattern>; +} + +defm MVE_VMLALDAVs16 : MVE_VMLALDAV_multi<"s16", 0b0, 0b0>; +defm MVE_VMLALDAVs32 : MVE_VMLALDAV_multi<"s32", 0b1, 0b0>; +defm MVE_VMLALDAVu16 : MVE_VMLALDAV_multi<"u16", 0b0, 0b1>; +defm MVE_VMLALDAVu32 : MVE_VMLALDAV_multi<"u32", 0b1, 0b1>; + +// vmlalv aliases vmlaldav +foreach acc = ["_acc", "_noacc"] in { + foreach suffix = ["s16", "s32", "u16", "u32"] in { + def : MVEInstAlias<!strconcat("vmlalv", !if(!eq(acc, "_acc"), "a", ""), + "${vp}.", suffix, "\t$RdaLoDest, $RdaHiDest, $Qn, $Qm"), + (!cast<Instruction>("MVE_VMLALDAV"#suffix#acc#"_noexch") + tGPREven:$RdaLoDest, tGPROdd:$RdaHiDest, + MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; + } +} + +multiclass MVE_VMLSLDAV_multi<string iname, string suffix, bit sz, + bit bit_28, list<dag> pattern=[]> { + defm "" : MVE_VMLALDAVBase_XA<iname, suffix, sz, bit_28, 0b0, 0b1, pattern>; +} + +defm MVE_VMLSLDAVs16 : MVE_VMLSLDAV_multi<"vmlsldav", "s16", 0b0, 0b0>; +defm MVE_VMLSLDAVs32 : MVE_VMLSLDAV_multi<"vmlsldav", "s32", 0b1, 0b0>; +defm MVE_VRMLSLDAVHs32 : MVE_VMLSLDAV_multi<"vrmlsldavh", "s32", 0b0, 0b1>; + +// end of mve_rDest instructions + +// start of mve_comp instructions + +class MVE_comp<InstrItinClass itin, string iname, string suffix, + string cstr, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), itin, iname, suffix, + "$Qd, $Qn, $Qm", vpred_r, cstr, pattern> { + bits<4> Qd; + bits<4> Qn; + bits<4> Qm; + + let Inst{22} = Qd{3}; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = 0b0; + let Inst{10-9} = 0b11; + let Inst{7} = Qn{3}; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +class MVE_VMINMAXNM<string iname, string suffix, bit sz, bit bit_21, + list<dag> pattern=[]> + : MVE_comp<NoItinerary, iname, suffix, "", pattern> { + + let Inst{28} = 0b1; + let Inst{25-24} = 0b11; + let Inst{23} = 0b0; + let Inst{21} = bit_21; + let Inst{20} = sz; + let Inst{11} = 0b1; + let Inst{8} = 0b1; + let Inst{6} = 0b1; + let Inst{4} = 0b1; + + let Predicates = [HasMVEFloat]; +} + +def MVE_VMAXNMf32 : MVE_VMINMAXNM<"vmaxnm", "f32", 0b0, 0b0>; +def MVE_VMAXNMf16 : MVE_VMINMAXNM<"vmaxnm", "f16", 0b1, 0b0>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (fmaxnum (v4f32 MQPR:$val1), (v4f32 MQPR:$val2))), + (v4f32 (MVE_VMAXNMf32 (v4f32 MQPR:$val1), (v4f32 MQPR:$val2)))>; + def : Pat<(v8f16 (fmaxnum (v8f16 MQPR:$val1), (v8f16 MQPR:$val2))), + (v8f16 (MVE_VMAXNMf16 (v8f16 MQPR:$val1), (v8f16 MQPR:$val2)))>; +} + +def MVE_VMINNMf32 : MVE_VMINMAXNM<"vminnm", "f32", 0b0, 0b1>; +def MVE_VMINNMf16 : MVE_VMINMAXNM<"vminnm", "f16", 0b1, 0b1>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (fminnum (v4f32 MQPR:$val1), (v4f32 MQPR:$val2))), + (v4f32 (MVE_VMINNMf32 (v4f32 MQPR:$val1), (v4f32 MQPR:$val2)))>; + def : Pat<(v8f16 (fminnum (v8f16 MQPR:$val1), (v8f16 MQPR:$val2))), + (v8f16 (MVE_VMINNMf16 (v8f16 MQPR:$val1), (v8f16 MQPR:$val2)))>; +} + + +class MVE_VMINMAX<string iname, string suffix, bit U, bits<2> size, + bit bit_4, list<dag> pattern=[]> + : MVE_comp<NoItinerary, iname, suffix, "", pattern> { + + let Inst{28} = U; + let Inst{25-24} = 0b11; + let Inst{23} = 0b0; + let Inst{21-20} = size{1-0}; + let Inst{11} = 0b0; + let Inst{8} = 0b0; + let Inst{6} = 0b1; + let Inst{4} = bit_4; +} + +multiclass MVE_VMINMAX_all_sizes<string iname, bit bit_4> { + def s8 : MVE_VMINMAX<iname, "s8", 0b0, 0b00, bit_4>; + def s16 : MVE_VMINMAX<iname, "s16", 0b0, 0b01, bit_4>; + def s32 : MVE_VMINMAX<iname, "s32", 0b0, 0b10, bit_4>; + def u8 : MVE_VMINMAX<iname, "u8", 0b1, 0b00, bit_4>; + def u16 : MVE_VMINMAX<iname, "u16", 0b1, 0b01, bit_4>; + def u32 : MVE_VMINMAX<iname, "u32", 0b1, 0b10, bit_4>; +} + +defm MVE_VMAX : MVE_VMINMAX_all_sizes<"vmax", 0b0>; +defm MVE_VMIN : MVE_VMINMAX_all_sizes<"vmin", 0b1>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (smin (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VMINs8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (smin (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VMINs16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (smin (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VMINs32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + + def : Pat<(v16i8 (smax (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VMAXs8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (smax (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VMAXs16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (smax (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VMAXs32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + + def : Pat<(v16i8 (umin (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VMINu8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (umin (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VMINu16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (umin (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VMINu32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + + def : Pat<(v16i8 (umax (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VMAXu8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (umax (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VMAXu16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (umax (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VMAXu32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; +} + +// end of mve_comp instructions + +// start of mve_bit instructions + +class MVE_bit_arith<dag oops, dag iops, string iname, string suffix, + string ops, string cstr, list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, iname, suffix, ops, vpred_r, cstr, pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{22} = Qd{3}; + let Inst{15-13} = Qd{2-0}; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; +} + +def MVE_VBIC : MVE_bit_arith<(outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), + "vbic", "", "$Qd, $Qn, $Qm", ""> { + bits<4> Qn; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b110; + let Inst{21-20} = 0b01; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12-8} = 0b00001; + let Inst{7} = Qn{3}; + let Inst{6} = 0b1; + let Inst{4} = 0b1; + let Inst{0} = 0b0; +} + +class MVE_VREV<string iname, string suffix, bits<2> size, bits<2> bit_8_7> + : MVE_bit_arith<(outs MQPR:$Qd), (ins MQPR:$Qm), iname, + suffix, "$Qd, $Qm", ""> { + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b00; + let Inst{12-9} = 0b0000; + let Inst{8-7} = bit_8_7; + let Inst{6} = 0b1; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VREV64_8 : MVE_VREV<"vrev64", "8", 0b00, 0b00>; +def MVE_VREV64_16 : MVE_VREV<"vrev64", "16", 0b01, 0b00>; +def MVE_VREV64_32 : MVE_VREV<"vrev64", "32", 0b10, 0b00>; + +def MVE_VREV32_8 : MVE_VREV<"vrev32", "8", 0b00, 0b01>; +def MVE_VREV32_16 : MVE_VREV<"vrev32", "16", 0b01, 0b01>; + +def MVE_VREV16_8 : MVE_VREV<"vrev16", "8", 0b00, 0b10>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v4i32 (ARMvrev64 (v4i32 MQPR:$src))), + (v4i32 (MVE_VREV64_32 (v4i32 MQPR:$src)))>; + def : Pat<(v8i16 (ARMvrev64 (v8i16 MQPR:$src))), + (v8i16 (MVE_VREV64_16 (v8i16 MQPR:$src)))>; + def : Pat<(v16i8 (ARMvrev64 (v16i8 MQPR:$src))), + (v16i8 (MVE_VREV64_8 (v16i8 MQPR:$src)))>; + + def : Pat<(v8i16 (ARMvrev32 (v8i16 MQPR:$src))), + (v8i16 (MVE_VREV32_16 (v8i16 MQPR:$src)))>; + def : Pat<(v16i8 (ARMvrev32 (v16i8 MQPR:$src))), + (v16i8 (MVE_VREV32_8 (v16i8 MQPR:$src)))>; + + def : Pat<(v16i8 (ARMvrev16 (v16i8 MQPR:$src))), + (v16i8 (MVE_VREV16_8 (v16i8 MQPR:$src)))>; + + def : Pat<(v4f32 (ARMvrev64 (v4f32 MQPR:$src))), + (v4f32 (MVE_VREV64_32 (v4f32 MQPR:$src)))>; + def : Pat<(v8f16 (ARMvrev64 (v8f16 MQPR:$src))), + (v8f16 (MVE_VREV64_16 (v8f16 MQPR:$src)))>; + def : Pat<(v8f16 (ARMvrev32 (v8f16 MQPR:$src))), + (v8f16 (MVE_VREV32_16 (v8f16 MQPR:$src)))>; +} + +def MVE_VMVN : MVE_bit_arith<(outs MQPR:$Qd), (ins MQPR:$Qm), + "vmvn", "", "$Qd, $Qm", ""> { + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{21-16} = 0b110000; + let Inst{12-6} = 0b0010111; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (vnotq (v16i8 MQPR:$val1))), + (v16i8 (MVE_VMVN (v16i8 MQPR:$val1)))>; + def : Pat<(v8i16 (vnotq (v8i16 MQPR:$val1))), + (v8i16 (MVE_VMVN (v8i16 MQPR:$val1)))>; + def : Pat<(v4i32 (vnotq (v4i32 MQPR:$val1))), + (v4i32 (MVE_VMVN (v4i32 MQPR:$val1)))>; + def : Pat<(v2i64 (vnotq (v2i64 MQPR:$val1))), + (v2i64 (MVE_VMVN (v2i64 MQPR:$val1)))>; +} + +class MVE_bit_ops<string iname, bits<2> bit_21_20, bit bit_28> + : MVE_bit_arith<(outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), + iname, "", "$Qd, $Qn, $Qm", ""> { + bits<4> Qn; + + let Inst{28} = bit_28; + let Inst{25-23} = 0b110; + let Inst{21-20} = bit_21_20; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12-8} = 0b00001; + let Inst{7} = Qn{3}; + let Inst{6} = 0b1; + let Inst{4} = 0b1; + let Inst{0} = 0b0; +} + +def MVE_VEOR : MVE_bit_ops<"veor", 0b00, 0b1>; +def MVE_VORN : MVE_bit_ops<"vorn", 0b11, 0b0>; +def MVE_VORR : MVE_bit_ops<"vorr", 0b10, 0b0>; +def MVE_VAND : MVE_bit_ops<"vand", 0b00, 0b0>; + +// add ignored suffixes as aliases + +foreach s=["s8", "s16", "s32", "u8", "u16", "u32", "i8", "i16", "i32", "f16", "f32"] in { + def : MVEInstAlias<"vbic${vp}." # s # "\t$QdSrc, $QnSrc, $QmSrc", + (MVE_VBIC MQPR:$QdSrc, MQPR:$QnSrc, MQPR:$QmSrc, vpred_r:$vp)>; + def : MVEInstAlias<"veor${vp}." # s # "\t$QdSrc, $QnSrc, $QmSrc", + (MVE_VEOR MQPR:$QdSrc, MQPR:$QnSrc, MQPR:$QmSrc, vpred_r:$vp)>; + def : MVEInstAlias<"vorn${vp}." # s # "\t$QdSrc, $QnSrc, $QmSrc", + (MVE_VORN MQPR:$QdSrc, MQPR:$QnSrc, MQPR:$QmSrc, vpred_r:$vp)>; + def : MVEInstAlias<"vorr${vp}." # s # "\t$QdSrc, $QnSrc, $QmSrc", + (MVE_VORR MQPR:$QdSrc, MQPR:$QnSrc, MQPR:$QmSrc, vpred_r:$vp)>; + def : MVEInstAlias<"vand${vp}." # s # "\t$QdSrc, $QnSrc, $QmSrc", + (MVE_VAND MQPR:$QdSrc, MQPR:$QnSrc, MQPR:$QmSrc, vpred_r:$vp)>; +} + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (and (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VAND (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (and (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VAND (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (and (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VAND (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + def : Pat<(v2i64 (and (v2i64 MQPR:$val1), (v2i64 MQPR:$val2))), + (v2i64 (MVE_VAND (v2i64 MQPR:$val1), (v2i64 MQPR:$val2)))>; + + def : Pat<(v16i8 (or (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VORR (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (or (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VORR (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (or (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VORR (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + def : Pat<(v2i64 (or (v2i64 MQPR:$val1), (v2i64 MQPR:$val2))), + (v2i64 (MVE_VORR (v2i64 MQPR:$val1), (v2i64 MQPR:$val2)))>; + + def : Pat<(v16i8 (xor (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VEOR (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (xor (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VEOR (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (xor (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VEOR (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + def : Pat<(v2i64 (xor (v2i64 MQPR:$val1), (v2i64 MQPR:$val2))), + (v2i64 (MVE_VEOR (v2i64 MQPR:$val1), (v2i64 MQPR:$val2)))>; + + def : Pat<(v16i8 (and (v16i8 MQPR:$val1), (vnotq MQPR:$val2))), + (v16i8 (MVE_VBIC (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (and (v8i16 MQPR:$val1), (vnotq MQPR:$val2))), + (v8i16 (MVE_VBIC (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (and (v4i32 MQPR:$val1), (vnotq MQPR:$val2))), + (v4i32 (MVE_VBIC (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + def : Pat<(v2i64 (and (v2i64 MQPR:$val1), (vnotq MQPR:$val2))), + (v2i64 (MVE_VBIC (v2i64 MQPR:$val1), (v2i64 MQPR:$val2)))>; + + def : Pat<(v16i8 (or (v16i8 MQPR:$val1), (vnotq MQPR:$val2))), + (v16i8 (MVE_VORN (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (or (v8i16 MQPR:$val1), (vnotq MQPR:$val2))), + (v8i16 (MVE_VORN (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (or (v4i32 MQPR:$val1), (vnotq MQPR:$val2))), + (v4i32 (MVE_VORN (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; + def : Pat<(v2i64 (or (v2i64 MQPR:$val1), (vnotq MQPR:$val2))), + (v2i64 (MVE_VORN (v2i64 MQPR:$val1), (v2i64 MQPR:$val2)))>; +} + +class MVE_bit_cmode<string iname, string suffix, bits<4> cmode, dag inOps> + : MVE_p<(outs MQPR:$Qd), inOps, NoItinerary, + iname, suffix, "$Qd, $imm", vpred_n, "$Qd = $Qd_src"> { + bits<8> imm; + bits<4> Qd; + + let Inst{28} = imm{7}; + let Inst{27-23} = 0b11111; + let Inst{22} = Qd{3}; + let Inst{21-19} = 0b000; + let Inst{18-16} = imm{6-4}; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = 0b0; + let Inst{11-8} = cmode; + let Inst{7-6} = 0b01; + let Inst{4} = 0b1; + let Inst{3-0} = imm{3-0}; +} + +class MVE_VORR<string suffix, bits<4> cmode, ExpandImm imm_type> + : MVE_bit_cmode<"vorr", suffix, cmode, (ins MQPR:$Qd_src, imm_type:$imm)> { + let Inst{5} = 0b0; +} + +def MVE_VORRIZ0v4i32 : MVE_VORR<"i32", 0b0001, expzero00>; +def MVE_VORRIZ0v8i16 : MVE_VORR<"i16", 0b1001, expzero00>; +def MVE_VORRIZ8v4i32 : MVE_VORR<"i32", 0b0011, expzero08>; +def MVE_VORRIZ8v8i16 : MVE_VORR<"i16", 0b1011, expzero08>; +def MVE_VORRIZ16v4i32 : MVE_VORR<"i32", 0b0101, expzero16>; +def MVE_VORRIZ24v4i32 : MVE_VORR<"i32", 0b0111, expzero24>; + +def MVE_VORNIZ0v4i32 : MVEAsmPseudo<"vorn${vp}.i32\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero00inv32:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; +def MVE_VORNIZ0v8i16 : MVEAsmPseudo<"vorn${vp}.i16\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero00inv16:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; +def MVE_VORNIZ8v4i32 : MVEAsmPseudo<"vorn${vp}.i32\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero08inv32:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; +def MVE_VORNIZ8v8i16 : MVEAsmPseudo<"vorn${vp}.i16\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero08inv16:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; +def MVE_VORNIZ16v4i32 : MVEAsmPseudo<"vorn${vp}.i32\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero16inv32:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; +def MVE_VORNIZ24v4i32 : MVEAsmPseudo<"vorn${vp}.i32\t$Qd, $imm", + (ins MQPR:$Qd_src, expzero24inv32:$imm, vpred_n:$vp), (outs MQPR:$Qd)>; + +def MVE_VMOV : MVEInstAlias<"vmov${vp}\t$Qd, $Qm", + (MVE_VORR MQPR:$Qd, MQPR:$Qm, MQPR:$Qm, vpred_r:$vp)>; + +class MVE_VBIC<string suffix, bits<4> cmode, ExpandImm imm_type> + : MVE_bit_cmode<"vbic", suffix, cmode, (ins MQPR:$Qd_src, imm_type:$imm)> { + let Inst{5} = 0b1; +} + +def MVE_VBICIZ0v4i32 : MVE_VBIC<"i32", 0b0001, expzero00>; +def MVE_VBICIZ0v8i16 : MVE_VBIC<"i16", 0b1001, expzero00>; +def MVE_VBICIZ8v4i32 : MVE_VBIC<"i32", 0b0011, expzero08>; +def MVE_VBICIZ8v8i16 : MVE_VBIC<"i16", 0b1011, expzero08>; +def MVE_VBICIZ16v4i32 : MVE_VBIC<"i32", 0b0101, expzero16>; +def MVE_VBICIZ24v4i32 : MVE_VBIC<"i32", 0b0111, expzero24>; + +def MVE_VANDIZ0v4i32 : MVEAsmPseudo<"vand${vp}.i32\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero00inv32:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; +def MVE_VANDIZ0v8i16 : MVEAsmPseudo<"vand${vp}.i16\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero00inv16:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; +def MVE_VANDIZ8v4i32 : MVEAsmPseudo<"vand${vp}.i32\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero08inv32:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; +def MVE_VANDIZ8v8i16 : MVEAsmPseudo<"vand${vp}.i16\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero08inv16:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; +def MVE_VANDIZ16v4i32 : MVEAsmPseudo<"vand${vp}.i32\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero16inv32:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; +def MVE_VANDIZ24v4i32 : MVEAsmPseudo<"vand${vp}.i32\t$Qda, $imm", + (ins MQPR:$Qda_src, expzero24inv32:$imm, vpred_n:$vp), (outs MQPR:$Qda)>; + +class MVE_VMOV_lane_direction { + bit bit_20; + dag oops; + dag iops; + string ops; + string cstr; +} +def MVE_VMOV_from_lane : MVE_VMOV_lane_direction { + let bit_20 = 0b1; + let oops = (outs rGPR:$Rt); + let iops = (ins MQPR:$Qd); + let ops = "$Rt, $Qd$Idx"; + let cstr = ""; +} +def MVE_VMOV_to_lane : MVE_VMOV_lane_direction { + let bit_20 = 0b0; + let oops = (outs MQPR:$Qd); + let iops = (ins MQPR:$Qd_src, rGPR:$Rt); + let ops = "$Qd$Idx, $Rt"; + let cstr = "$Qd = $Qd_src"; +} + +class MVE_VMOV_lane<string suffix, bit U, dag indexop, + MVE_VMOV_lane_direction dir> + : MVE_VMOV_lane_base<dir.oops, !con(dir.iops, indexop), NoItinerary, + "vmov", suffix, dir.ops, dir.cstr, []> { + bits<4> Qd; + bits<4> Rt; + + let Inst{31-24} = 0b11101110; + let Inst{23} = U; + let Inst{20} = dir.bit_20; + let Inst{19-17} = Qd{2-0}; + let Inst{15-12} = Rt{3-0}; + let Inst{11-8} = 0b1011; + let Inst{7} = Qd{3}; + let Inst{4-0} = 0b10000; +} + +class MVE_VMOV_lane_32<MVE_VMOV_lane_direction dir> + : MVE_VMOV_lane<"32", 0b0, (ins MVEVectorIndex<4>:$Idx), dir> { + bits<2> Idx; + let Inst{22} = 0b0; + let Inst{6-5} = 0b00; + let Inst{16} = Idx{1}; + let Inst{21} = Idx{0}; + + let Predicates = [HasFPRegsV8_1M]; +} + +class MVE_VMOV_lane_16<string suffix, bit U, MVE_VMOV_lane_direction dir> + : MVE_VMOV_lane<suffix, U, (ins MVEVectorIndex<8>:$Idx), dir> { + bits<3> Idx; + let Inst{22} = 0b0; + let Inst{5} = 0b1; + let Inst{16} = Idx{2}; + let Inst{21} = Idx{1}; + let Inst{6} = Idx{0}; +} + +class MVE_VMOV_lane_8<string suffix, bit U, MVE_VMOV_lane_direction dir> + : MVE_VMOV_lane<suffix, U, (ins MVEVectorIndex<16>:$Idx), dir> { + bits<4> Idx; + let Inst{22} = 0b1; + let Inst{16} = Idx{3}; + let Inst{21} = Idx{2}; + let Inst{6} = Idx{1}; + let Inst{5} = Idx{0}; +} + +def MVE_VMOV_from_lane_32 : MVE_VMOV_lane_32< MVE_VMOV_from_lane>; +def MVE_VMOV_to_lane_32 : MVE_VMOV_lane_32< MVE_VMOV_to_lane>; +def MVE_VMOV_from_lane_s16 : MVE_VMOV_lane_16<"s16", 0b0, MVE_VMOV_from_lane>; +def MVE_VMOV_from_lane_u16 : MVE_VMOV_lane_16<"u16", 0b1, MVE_VMOV_from_lane>; +def MVE_VMOV_to_lane_16 : MVE_VMOV_lane_16< "16", 0b0, MVE_VMOV_to_lane>; +def MVE_VMOV_from_lane_s8 : MVE_VMOV_lane_8 < "s8", 0b0, MVE_VMOV_from_lane>; +def MVE_VMOV_from_lane_u8 : MVE_VMOV_lane_8 < "u8", 0b1, MVE_VMOV_from_lane>; +def MVE_VMOV_to_lane_8 : MVE_VMOV_lane_8 < "8", 0b0, MVE_VMOV_to_lane>; + +let Predicates = [HasMVEInt] in { + def : Pat<(extractelt (v2f64 MQPR:$src), imm:$lane), + (f64 (EXTRACT_SUBREG MQPR:$src, (DSubReg_f64_reg imm:$lane)))>; + def : Pat<(insertelt (v2f64 MQPR:$src1), DPR:$src2, imm:$lane), + (INSERT_SUBREG (v2f64 (COPY_TO_REGCLASS MQPR:$src1, MQPR)), DPR:$src2, (DSubReg_f64_reg imm:$lane))>; + + def : Pat<(extractelt (v4i32 MQPR:$src), imm:$lane), + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG MQPR:$src, (SSubReg_f32_reg imm:$lane))), rGPR)>; + def : Pat<(insertelt (v4i32 MQPR:$src1), rGPR:$src2, imm:$lane), + (MVE_VMOV_to_lane_32 MQPR:$src1, rGPR:$src2, imm:$lane)>; + + def : Pat<(vector_insert (v16i8 MQPR:$src1), rGPR:$src2, imm:$lane), + (MVE_VMOV_to_lane_8 MQPR:$src1, rGPR:$src2, imm:$lane)>; + def : Pat<(vector_insert (v8i16 MQPR:$src1), rGPR:$src2, imm:$lane), + (MVE_VMOV_to_lane_16 MQPR:$src1, rGPR:$src2, imm:$lane)>; + + def : Pat<(ARMvgetlanes (v16i8 MQPR:$src), imm:$lane), + (MVE_VMOV_from_lane_s8 MQPR:$src, imm:$lane)>; + def : Pat<(ARMvgetlanes (v8i16 MQPR:$src), imm:$lane), + (MVE_VMOV_from_lane_s16 MQPR:$src, imm:$lane)>; + def : Pat<(ARMvgetlaneu (v16i8 MQPR:$src), imm:$lane), + (MVE_VMOV_from_lane_u8 MQPR:$src, imm:$lane)>; + def : Pat<(ARMvgetlaneu (v8i16 MQPR:$src), imm:$lane), + (MVE_VMOV_from_lane_u16 MQPR:$src, imm:$lane)>; + + def : Pat<(v16i8 (scalar_to_vector GPR:$src)), + (MVE_VMOV_to_lane_8 (v16i8 (IMPLICIT_DEF)), rGPR:$src, (i32 0))>; + def : Pat<(v8i16 (scalar_to_vector GPR:$src)), + (MVE_VMOV_to_lane_16 (v8i16 (IMPLICIT_DEF)), rGPR:$src, (i32 0))>; + def : Pat<(v4i32 (scalar_to_vector GPR:$src)), + (MVE_VMOV_to_lane_32 (v4i32 (IMPLICIT_DEF)), rGPR:$src, (i32 0))>; + + // Floating point patterns, still enabled under HasMVEInt + def : Pat<(extractelt (v4f32 MQPR:$src), imm:$lane), + (COPY_TO_REGCLASS (f32 (EXTRACT_SUBREG MQPR:$src, (SSubReg_f32_reg imm:$lane))), SPR)>; + def : Pat<(insertelt (v4f32 MQPR:$src1), (f32 SPR:$src2), imm:$lane), + (INSERT_SUBREG (v4f32 (COPY_TO_REGCLASS MQPR:$src1, MQPR)), SPR:$src2, (SSubReg_f32_reg imm:$lane))>; + + def : Pat<(insertelt (v8f16 MQPR:$src1), HPR:$src2, imm:$lane), + (MVE_VMOV_to_lane_16 MQPR:$src1, (COPY_TO_REGCLASS HPR:$src2, rGPR), imm:$lane)>; + def : Pat<(extractelt (v8f16 MQPR:$src), imm:$lane), + (COPY_TO_REGCLASS (MVE_VMOV_from_lane_u16 MQPR:$src, imm:$lane), HPR)>; + + def : Pat<(v4f32 (scalar_to_vector SPR:$src)), + (INSERT_SUBREG (v4f32 (IMPLICIT_DEF)), SPR:$src, ssub_0)>; + def : Pat<(v4f32 (scalar_to_vector GPR:$src)), + (MVE_VMOV_to_lane_32 (v4f32 (IMPLICIT_DEF)), rGPR:$src, (i32 0))>; + def : Pat<(v8f16 (scalar_to_vector HPR:$src)), + (INSERT_SUBREG (v8f16 (IMPLICIT_DEF)), HPR:$src, ssub_0)>; + def : Pat<(v8f16 (scalar_to_vector GPR:$src)), + (MVE_VMOV_to_lane_16 (v8f16 (IMPLICIT_DEF)), rGPR:$src, (i32 0))>; +} + +// end of mve_bit instructions + +// start of MVE Integer instructions + +class MVE_int<string iname, string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), NoItinerary, + iname, suffix, "$Qd, $Qn, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + bits<4> Qn; + bits<4> Qm; + + let Inst{22} = Qd{3}; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{7} = Qn{3}; + let Inst{6} = 0b1; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; +} + +class MVE_VMULt1<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_int<"vmul", suffix, size, pattern> { + + let Inst{28} = 0b0; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-8} = 0b01001; + let Inst{4} = 0b1; + let Inst{0} = 0b0; +} + +def MVE_VMULt1i8 : MVE_VMULt1<"i8", 0b00>; +def MVE_VMULt1i16 : MVE_VMULt1<"i16", 0b01>; +def MVE_VMULt1i32 : MVE_VMULt1<"i32", 0b10>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (mul (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VMULt1i8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (mul (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VMULt1i16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (mul (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VMULt1i32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; +} + +class MVE_VQxDMULH<string iname, string suffix, bits<2> size, bit rounding, + list<dag> pattern=[]> + : MVE_int<iname, suffix, size, pattern> { + + let Inst{28} = rounding; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-8} = 0b01011; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +class MVE_VQDMULH<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_VQxDMULH<"vqdmulh", suffix, size, 0b0, pattern>; +class MVE_VQRDMULH<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_VQxDMULH<"vqrdmulh", suffix, size, 0b1, pattern>; + +def MVE_VQDMULHi8 : MVE_VQDMULH<"s8", 0b00>; +def MVE_VQDMULHi16 : MVE_VQDMULH<"s16", 0b01>; +def MVE_VQDMULHi32 : MVE_VQDMULH<"s32", 0b10>; + +def MVE_VQRDMULHi8 : MVE_VQRDMULH<"s8", 0b00>; +def MVE_VQRDMULHi16 : MVE_VQRDMULH<"s16", 0b01>; +def MVE_VQRDMULHi32 : MVE_VQRDMULH<"s32", 0b10>; + +class MVE_VADDSUB<string iname, string suffix, bits<2> size, bit subtract, + list<dag> pattern=[]> + : MVE_int<iname, suffix, size, pattern> { + + let Inst{28} = subtract; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-8} = 0b01000; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +class MVE_VADD<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_VADDSUB<"vadd", suffix, size, 0b0, pattern>; +class MVE_VSUB<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_VADDSUB<"vsub", suffix, size, 0b1, pattern>; + +def MVE_VADDi8 : MVE_VADD<"i8", 0b00>; +def MVE_VADDi16 : MVE_VADD<"i16", 0b01>; +def MVE_VADDi32 : MVE_VADD<"i32", 0b10>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (add (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VADDi8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (add (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VADDi16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (add (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VADDi32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; +} + +def MVE_VSUBi8 : MVE_VSUB<"i8", 0b00>; +def MVE_VSUBi16 : MVE_VSUB<"i16", 0b01>; +def MVE_VSUBi32 : MVE_VSUB<"i32", 0b10>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (sub (v16i8 MQPR:$val1), (v16i8 MQPR:$val2))), + (v16i8 (MVE_VSUBi8 (v16i8 MQPR:$val1), (v16i8 MQPR:$val2)))>; + def : Pat<(v8i16 (sub (v8i16 MQPR:$val1), (v8i16 MQPR:$val2))), + (v8i16 (MVE_VSUBi16 (v8i16 MQPR:$val1), (v8i16 MQPR:$val2)))>; + def : Pat<(v4i32 (sub (v4i32 MQPR:$val1), (v4i32 MQPR:$val2))), + (v4i32 (MVE_VSUBi32 (v4i32 MQPR:$val1), (v4i32 MQPR:$val2)))>; +} + +class MVE_VQADDSUB<string iname, string suffix, bit U, bit subtract, + bits<2> size, list<dag> pattern=[]> + : MVE_int<iname, suffix, size, pattern> { + + let Inst{28} = U; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-10} = 0b000; + let Inst{9} = subtract; + let Inst{8} = 0b0; + let Inst{4} = 0b1; + let Inst{0} = 0b0; +} + +class MVE_VQADD<string suffix, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_VQADDSUB<"vqadd", suffix, U, 0b0, size, pattern>; +class MVE_VQSUB<string suffix, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_VQADDSUB<"vqsub", suffix, U, 0b1, size, pattern>; + +def MVE_VQADDs8 : MVE_VQADD<"s8", 0b0, 0b00>; +def MVE_VQADDs16 : MVE_VQADD<"s16", 0b0, 0b01>; +def MVE_VQADDs32 : MVE_VQADD<"s32", 0b0, 0b10>; +def MVE_VQADDu8 : MVE_VQADD<"u8", 0b1, 0b00>; +def MVE_VQADDu16 : MVE_VQADD<"u16", 0b1, 0b01>; +def MVE_VQADDu32 : MVE_VQADD<"u32", 0b1, 0b10>; + +def MVE_VQSUBs8 : MVE_VQSUB<"s8", 0b0, 0b00>; +def MVE_VQSUBs16 : MVE_VQSUB<"s16", 0b0, 0b01>; +def MVE_VQSUBs32 : MVE_VQSUB<"s32", 0b0, 0b10>; +def MVE_VQSUBu8 : MVE_VQSUB<"u8", 0b1, 0b00>; +def MVE_VQSUBu16 : MVE_VQSUB<"u16", 0b1, 0b01>; +def MVE_VQSUBu32 : MVE_VQSUB<"u32", 0b1, 0b10>; + +class MVE_VABD_int<string suffix, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_int<"vabd", suffix, size, pattern> { + + let Inst{28} = U; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-8} = 0b00111; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VABDs8 : MVE_VABD_int<"s8", 0b0, 0b00>; +def MVE_VABDs16 : MVE_VABD_int<"s16", 0b0, 0b01>; +def MVE_VABDs32 : MVE_VABD_int<"s32", 0b0, 0b10>; +def MVE_VABDu8 : MVE_VABD_int<"u8", 0b1, 0b00>; +def MVE_VABDu16 : MVE_VABD_int<"u16", 0b1, 0b01>; +def MVE_VABDu32 : MVE_VABD_int<"u32", 0b1, 0b10>; + +class MVE_VRHADD<string suffix, bit U, bits<2> size, list<dag> pattern=[]> + : MVE_int<"vrhadd", suffix, size, pattern> { + + let Inst{28} = U; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-8} = 0b00001; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VRHADDs8 : MVE_VRHADD<"s8", 0b0, 0b00>; +def MVE_VRHADDs16 : MVE_VRHADD<"s16", 0b0, 0b01>; +def MVE_VRHADDs32 : MVE_VRHADD<"s32", 0b0, 0b10>; +def MVE_VRHADDu8 : MVE_VRHADD<"u8", 0b1, 0b00>; +def MVE_VRHADDu16 : MVE_VRHADD<"u16", 0b1, 0b01>; +def MVE_VRHADDu32 : MVE_VRHADD<"u32", 0b1, 0b10>; + +class MVE_VHADDSUB<string iname, string suffix, bit U, bit subtract, + bits<2> size, list<dag> pattern=[]> + : MVE_int<iname, suffix, size, pattern> { + + let Inst{28} = U; + let Inst{25-23} = 0b110; + let Inst{16} = 0b0; + let Inst{12-10} = 0b000; + let Inst{9} = subtract; + let Inst{8} = 0b0; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +class MVE_VHADD<string suffix, bit U, bits<2> size, + list<dag> pattern=[]> + : MVE_VHADDSUB<"vhadd", suffix, U, 0b0, size, pattern>; +class MVE_VHSUB<string suffix, bit U, bits<2> size, + list<dag> pattern=[]> + : MVE_VHADDSUB<"vhsub", suffix, U, 0b1, size, pattern>; + +def MVE_VHADDs8 : MVE_VHADD<"s8", 0b0, 0b00>; +def MVE_VHADDs16 : MVE_VHADD<"s16", 0b0, 0b01>; +def MVE_VHADDs32 : MVE_VHADD<"s32", 0b0, 0b10>; +def MVE_VHADDu8 : MVE_VHADD<"u8", 0b1, 0b00>; +def MVE_VHADDu16 : MVE_VHADD<"u16", 0b1, 0b01>; +def MVE_VHADDu32 : MVE_VHADD<"u32", 0b1, 0b10>; + +def MVE_VHSUBs8 : MVE_VHSUB<"s8", 0b0, 0b00>; +def MVE_VHSUBs16 : MVE_VHSUB<"s16", 0b0, 0b01>; +def MVE_VHSUBs32 : MVE_VHSUB<"s32", 0b0, 0b10>; +def MVE_VHSUBu8 : MVE_VHSUB<"u8", 0b1, 0b00>; +def MVE_VHSUBu16 : MVE_VHSUB<"u16", 0b1, 0b01>; +def MVE_VHSUBu32 : MVE_VHSUB<"u32", 0b1, 0b10>; + +class MVE_VDUP<string suffix, bit B, bit E, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins rGPR:$Rt), NoItinerary, + "vdup", suffix, "$Qd, $Rt", vpred_r, "", pattern> { + bits<4> Qd; + bits<4> Rt; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b101; + let Inst{22} = B; + let Inst{21-20} = 0b10; + let Inst{19-17} = Qd{2-0}; + let Inst{16} = 0b0; + let Inst{15-12} = Rt; + let Inst{11-8} = 0b1011; + let Inst{7} = Qd{3}; + let Inst{6} = 0b0; + let Inst{5} = E; + let Inst{4-0} = 0b10000; +} + +def MVE_VDUP32 : MVE_VDUP<"32", 0b0, 0b0>; +def MVE_VDUP16 : MVE_VDUP<"16", 0b0, 0b1>; +def MVE_VDUP8 : MVE_VDUP<"8", 0b1, 0b0>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (ARMvdup (i32 rGPR:$elem))), + (MVE_VDUP8 rGPR:$elem)>; + def : Pat<(v8i16 (ARMvdup (i32 rGPR:$elem))), + (MVE_VDUP16 rGPR:$elem)>; + def : Pat<(v4i32 (ARMvdup (i32 rGPR:$elem))), + (MVE_VDUP32 rGPR:$elem)>; + + def : Pat<(v4i32 (ARMvduplane (v4i32 MQPR:$src), imm:$lane)), + (MVE_VDUP32 (MVE_VMOV_from_lane_32 MQPR:$src, imm:$lane))>; + // For the 16-bit and 8-bit vduplanes we don't care about the signedness + // of the lane move operation as we only want the lowest 8/16 bits anyway. + def : Pat<(v8i16 (ARMvduplane (v8i16 MQPR:$src), imm:$lane)), + (MVE_VDUP16 (MVE_VMOV_from_lane_u16 MQPR:$src, imm:$lane))>; + def : Pat<(v16i8 (ARMvduplane (v16i8 MQPR:$src), imm:$lane)), + (MVE_VDUP8 (MVE_VMOV_from_lane_u8 MQPR:$src, imm:$lane))>; + + def : Pat<(v4f32 (ARMvdup (f32 SPR:$elem))), + (v4f32 (MVE_VDUP32 (i32 (COPY_TO_REGCLASS (f32 SPR:$elem), rGPR))))>; + def : Pat<(v8f16 (ARMvdup (f16 HPR:$elem))), + (v8f16 (MVE_VDUP16 (i32 (COPY_TO_REGCLASS (f16 HPR:$elem), rGPR))))>; + + def : Pat<(v4f32 (ARMvduplane (v4f32 MQPR:$src), imm:$lane)), + (MVE_VDUP32 (MVE_VMOV_from_lane_32 MQPR:$src, imm:$lane))>; + def : Pat<(v8f16 (ARMvduplane (v8f16 MQPR:$src), imm:$lane)), + (MVE_VDUP16 (MVE_VMOV_from_lane_u16 MQPR:$src, imm:$lane))>; +} + + +class MVEIntSingleSrc<string iname, string suffix, bits<2> size, + list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qm), NoItinerary, + iname, suffix, "$Qd, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{22} = Qd{3}; + let Inst{19-18} = size{1-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; +} + +class MVE_VCLSCLZ<string iname, string suffix, bits<2> size, + bit count_zeroes, list<dag> pattern=[]> + : MVEIntSingleSrc<iname, suffix, size, pattern> { + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{21-20} = 0b11; + let Inst{17-16} = 0b00; + let Inst{12-8} = 0b00100; + let Inst{7} = count_zeroes; + let Inst{6} = 0b1; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VCLSs8 : MVE_VCLSCLZ<"vcls", "s8", 0b00, 0b0>; +def MVE_VCLSs16 : MVE_VCLSCLZ<"vcls", "s16", 0b01, 0b0>; +def MVE_VCLSs32 : MVE_VCLSCLZ<"vcls", "s32", 0b10, 0b0>; + +def MVE_VCLZs8 : MVE_VCLSCLZ<"vclz", "i8", 0b00, 0b1>; +def MVE_VCLZs16 : MVE_VCLSCLZ<"vclz", "i16", 0b01, 0b1>; +def MVE_VCLZs32 : MVE_VCLSCLZ<"vclz", "i32", 0b10, 0b1>; + +class MVE_VABSNEG_int<string iname, string suffix, bits<2> size, bit negate, + list<dag> pattern=[]> + : MVEIntSingleSrc<iname, suffix, size, pattern> { + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{21-20} = 0b11; + let Inst{17-16} = 0b01; + let Inst{12-8} = 0b00011; + let Inst{7} = negate; + let Inst{6} = 0b1; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VABSs8 : MVE_VABSNEG_int<"vabs", "s8", 0b00, 0b0>; +def MVE_VABSs16 : MVE_VABSNEG_int<"vabs", "s16", 0b01, 0b0>; +def MVE_VABSs32 : MVE_VABSNEG_int<"vabs", "s32", 0b10, 0b0>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (abs (v16i8 MQPR:$v))), + (v16i8 (MVE_VABSs8 $v))>; + def : Pat<(v8i16 (abs (v8i16 MQPR:$v))), + (v8i16 (MVE_VABSs16 $v))>; + def : Pat<(v4i32 (abs (v4i32 MQPR:$v))), + (v4i32 (MVE_VABSs32 $v))>; +} + +def MVE_VNEGs8 : MVE_VABSNEG_int<"vneg", "s8", 0b00, 0b1>; +def MVE_VNEGs16 : MVE_VABSNEG_int<"vneg", "s16", 0b01, 0b1>; +def MVE_VNEGs32 : MVE_VABSNEG_int<"vneg", "s32", 0b10, 0b1>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (vnegq (v16i8 MQPR:$v))), + (v16i8 (MVE_VNEGs8 $v))>; + def : Pat<(v8i16 (vnegq (v8i16 MQPR:$v))), + (v8i16 (MVE_VNEGs16 $v))>; + def : Pat<(v4i32 (vnegq (v4i32 MQPR:$v))), + (v4i32 (MVE_VNEGs32 $v))>; +} + +class MVE_VQABSNEG<string iname, string suffix, bits<2> size, + bit negate, list<dag> pattern=[]> + : MVEIntSingleSrc<iname, suffix, size, pattern> { + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{21-20} = 0b11; + let Inst{17-16} = 0b00; + let Inst{12-8} = 0b00111; + let Inst{7} = negate; + let Inst{6} = 0b1; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VQABSs8 : MVE_VQABSNEG<"vqabs", "s8", 0b00, 0b0>; +def MVE_VQABSs16 : MVE_VQABSNEG<"vqabs", "s16", 0b01, 0b0>; +def MVE_VQABSs32 : MVE_VQABSNEG<"vqabs", "s32", 0b10, 0b0>; + +def MVE_VQNEGs8 : MVE_VQABSNEG<"vqneg", "s8", 0b00, 0b1>; +def MVE_VQNEGs16 : MVE_VQABSNEG<"vqneg", "s16", 0b01, 0b1>; +def MVE_VQNEGs32 : MVE_VQABSNEG<"vqneg", "s32", 0b10, 0b1>; + +class MVE_mod_imm<string iname, string suffix, bits<4> cmode, bit op, + dag iops, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), iops, NoItinerary, iname, suffix, "$Qd, $imm", + vpred_r, "", pattern> { + bits<13> imm; + bits<4> Qd; + + let Inst{28} = imm{7}; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21-19} = 0b000; + let Inst{18-16} = imm{6-4}; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = 0b0; + let Inst{11-8} = cmode{3-0}; + let Inst{7-6} = 0b01; + let Inst{5} = op; + let Inst{4} = 0b1; + let Inst{3-0} = imm{3-0}; + + let DecoderMethod = "DecodeMVEModImmInstruction"; +} + +let isReMaterializable = 1 in { +let isAsCheapAsAMove = 1 in { +def MVE_VMOVimmi8 : MVE_mod_imm<"vmov", "i8", {1,1,1,0}, 0b0, (ins nImmSplatI8:$imm)>; +def MVE_VMOVimmi16 : MVE_mod_imm<"vmov", "i16", {1,0,?,0}, 0b0, (ins nImmSplatI16:$imm)> { + let Inst{9} = imm{9}; +} +def MVE_VMOVimmi32 : MVE_mod_imm<"vmov", "i32", {?,?,?,?}, 0b0, (ins nImmVMOVI32:$imm)> { + let Inst{11-8} = imm{11-8}; +} +def MVE_VMOVimmi64 : MVE_mod_imm<"vmov", "i64", {1,1,1,0}, 0b1, (ins nImmSplatI64:$imm)>; +def MVE_VMOVimmf32 : MVE_mod_imm<"vmov", "f32", {1,1,1,1}, 0b0, (ins nImmVMOVF32:$imm)>; +} // let isAsCheapAsAMove = 1 + +def MVE_VMVNimmi16 : MVE_mod_imm<"vmvn", "i16", {1,0,?,0}, 0b1, (ins nImmSplatI16:$imm)> { + let Inst{9} = imm{9}; +} +def MVE_VMVNimmi32 : MVE_mod_imm<"vmvn", "i32", {?,?,?,?}, 0b1, (ins nImmVMOVI32:$imm)> { + let Inst{11-8} = imm{11-8}; +} +} // let isReMaterializable = 1 + +let Predicates = [HasMVEInt] in { + def : Pat<(v16i8 (ARMvmovImm timm:$simm)), + (v16i8 (MVE_VMOVimmi8 nImmSplatI8:$simm))>; + def : Pat<(v8i16 (ARMvmovImm timm:$simm)), + (v8i16 (MVE_VMOVimmi16 nImmSplatI16:$simm))>; + def : Pat<(v4i32 (ARMvmovImm timm:$simm)), + (v4i32 (MVE_VMOVimmi32 nImmVMOVI32:$simm))>; + + def : Pat<(v8i16 (ARMvmvnImm timm:$simm)), + (v8i16 (MVE_VMVNimmi16 nImmSplatI16:$simm))>; + def : Pat<(v4i32 (ARMvmvnImm timm:$simm)), + (v4i32 (MVE_VMVNimmi32 nImmVMOVI32:$simm))>; + + def : Pat<(v4f32 (ARMvmovFPImm timm:$simm)), + (v4f32 (MVE_VMOVimmf32 nImmVMOVF32:$simm))>; +} + +class MVE_VMINMAXA<string iname, string suffix, bits<2> size, + bit bit_12, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qd_src, MQPR:$Qm), + NoItinerary, iname, suffix, "$Qd, $Qm", vpred_n, "$Qd = $Qd_src", + pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b11; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = bit_12; + let Inst{11-6} = 0b111010; + let Inst{5} = Qm{3}; + let Inst{4} = 0b0; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b1; +} + +def MVE_VMAXAs8 : MVE_VMINMAXA<"vmaxa", "s8", 0b00, 0b0>; +def MVE_VMAXAs16 : MVE_VMINMAXA<"vmaxa", "s16", 0b01, 0b0>; +def MVE_VMAXAs32 : MVE_VMINMAXA<"vmaxa", "s32", 0b10, 0b0>; + +def MVE_VMINAs8 : MVE_VMINMAXA<"vmina", "s8", 0b00, 0b1>; +def MVE_VMINAs16 : MVE_VMINMAXA<"vmina", "s16", 0b01, 0b1>; +def MVE_VMINAs32 : MVE_VMINMAXA<"vmina", "s32", 0b10, 0b1>; + +// end of MVE Integer instructions + +// start of mve_imm_shift instructions + +def MVE_VSHLC : MVE_p<(outs rGPR:$RdmDest, MQPR:$Qd), + (ins MQPR:$QdSrc, rGPR:$RdmSrc, long_shift:$imm), + NoItinerary, "vshlc", "", "$QdSrc, $RdmSrc, $imm", + vpred_n, "$RdmDest = $RdmSrc,$Qd = $QdSrc"> { + bits<5> imm; + bits<4> Qd; + bits<4> RdmDest; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b101; + let Inst{22} = Qd{3}; + let Inst{21} = 0b1; + let Inst{20-16} = imm{4-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-4} = 0b011111100; + let Inst{3-0} = RdmDest{3-0}; +} + +class MVE_shift_imm<dag oops, dag iops, string iname, string suffix, + string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, iname, suffix, ops, vpred, cstr, pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{22} = Qd{3}; + let Inst{15-13} = Qd{2-0}; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; +} + +class MVE_VMOVL<string iname, string suffix, bits<2> sz, bit U, + list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), (ins MQPR:$Qm), + iname, suffix, "$Qd, $Qm", vpred_r, "", + pattern> { + let Inst{28} = U; + let Inst{25-23} = 0b101; + let Inst{21} = 0b1; + let Inst{20-19} = sz{1-0}; + let Inst{18-16} = 0b000; + let Inst{11-6} = 0b111101; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +multiclass MVE_VMOVL_shift_half<string iname, string suffix, bits<2> sz, bit U, + list<dag> pattern=[]> { + def bh : MVE_VMOVL<!strconcat(iname, "b"), suffix, sz, U, pattern> { + let Inst{12} = 0b0; + } + def th : MVE_VMOVL<!strconcat(iname, "t"), suffix, sz, U, pattern> { + let Inst{12} = 0b1; + } +} + +defm MVE_VMOVLs8 : MVE_VMOVL_shift_half<"vmovl", "s8", 0b01, 0b0>; +defm MVE_VMOVLu8 : MVE_VMOVL_shift_half<"vmovl", "u8", 0b01, 0b1>; +defm MVE_VMOVLs16 : MVE_VMOVL_shift_half<"vmovl", "s16", 0b10, 0b0>; +defm MVE_VMOVLu16 : MVE_VMOVL_shift_half<"vmovl", "u16", 0b10, 0b1>; + +let Predicates = [HasMVEInt] in { + def : Pat<(sext_inreg (v4i32 MQPR:$src), v4i16), + (MVE_VMOVLs16bh MQPR:$src)>; + def : Pat<(sext_inreg (v8i16 MQPR:$src), v8i8), + (MVE_VMOVLs8bh MQPR:$src)>; + def : Pat<(sext_inreg (v4i32 MQPR:$src), v4i8), + (MVE_VMOVLs16bh (MVE_VMOVLs8bh MQPR:$src))>; + + // zext_inreg 16 -> 32 + def : Pat<(and (v4i32 MQPR:$src), (v4i32 (ARMvmovImm (i32 0xCFF)))), + (MVE_VMOVLu16bh MQPR:$src)>; + // zext_inreg 8 -> 16 + def : Pat<(and (v8i16 MQPR:$src), (v8i16 (ARMvmovImm (i32 0x8FF)))), + (MVE_VMOVLu8bh MQPR:$src)>; +} + + +class MVE_VSHLL_imm<string iname, string suffix, bit U, bit th, + dag immops, list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), !con((ins MQPR:$Qm), immops), + iname, suffix, "$Qd, $Qm, $imm", vpred_r, "", pattern> { + let Inst{28} = U; + let Inst{25-23} = 0b101; + let Inst{21} = 0b1; + let Inst{12} = th; + let Inst{11-6} = 0b111101; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +// The immediate VSHLL instructions accept shift counts from 1 up to +// the lane width (8 or 16), but the full-width shifts have an +// entirely separate encoding, given below with 'lw' in the name. + +class MVE_VSHLL_imm8<string iname, string suffix, + bit U, bit th, list<dag> pattern=[]> + : MVE_VSHLL_imm<iname, suffix, U, th, (ins mve_shift_imm1_7:$imm), pattern> { + bits<3> imm; + let Inst{20-19} = 0b01; + let Inst{18-16} = imm; +} + +class MVE_VSHLL_imm16<string iname, string suffix, + bit U, bit th, list<dag> pattern=[]> + : MVE_VSHLL_imm<iname, suffix, U, th, (ins mve_shift_imm1_15:$imm), pattern> { + bits<4> imm; + let Inst{20} = 0b1; + let Inst{19-16} = imm; +} + +def MVE_VSHLL_imms8bh : MVE_VSHLL_imm8 <"vshllb", "s8", 0b0, 0b0>; +def MVE_VSHLL_imms8th : MVE_VSHLL_imm8 <"vshllt", "s8", 0b0, 0b1>; +def MVE_VSHLL_immu8bh : MVE_VSHLL_imm8 <"vshllb", "u8", 0b1, 0b0>; +def MVE_VSHLL_immu8th : MVE_VSHLL_imm8 <"vshllt", "u8", 0b1, 0b1>; +def MVE_VSHLL_imms16bh : MVE_VSHLL_imm16<"vshllb", "s16", 0b0, 0b0>; +def MVE_VSHLL_imms16th : MVE_VSHLL_imm16<"vshllt", "s16", 0b0, 0b1>; +def MVE_VSHLL_immu16bh : MVE_VSHLL_imm16<"vshllb", "u16", 0b1, 0b0>; +def MVE_VSHLL_immu16th : MVE_VSHLL_imm16<"vshllt", "u16", 0b1, 0b1>; + +class MVE_VSHLL_by_lane_width<string iname, string suffix, bits<2> size, + bit U, string ops, list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), (ins MQPR:$Qm), + iname, suffix, ops, vpred_r, "", pattern> { + let Inst{28} = U; + let Inst{25-23} = 0b100; + let Inst{21-20} = 0b11; + let Inst{19-18} = size{1-0}; + let Inst{17-16} = 0b01; + let Inst{11-6} = 0b111000; + let Inst{4} = 0b0; + let Inst{0} = 0b1; +} + +multiclass MVE_VSHLL_lw<string iname, string suffix, bits<2> sz, bit U, + string ops, list<dag> pattern=[]> { + def bh : MVE_VSHLL_by_lane_width<iname#"b", suffix, sz, U, ops, pattern> { + let Inst{12} = 0b0; + } + def th : MVE_VSHLL_by_lane_width<iname#"t", suffix, sz, U, ops, pattern> { + let Inst{12} = 0b1; + } +} + +defm MVE_VSHLL_lws8 : MVE_VSHLL_lw<"vshll", "s8", 0b00, 0b0, "$Qd, $Qm, #8">; +defm MVE_VSHLL_lws16 : MVE_VSHLL_lw<"vshll", "s16", 0b01, 0b0, "$Qd, $Qm, #16">; +defm MVE_VSHLL_lwu8 : MVE_VSHLL_lw<"vshll", "u8", 0b00, 0b1, "$Qd, $Qm, #8">; +defm MVE_VSHLL_lwu16 : MVE_VSHLL_lw<"vshll", "u16", 0b01, 0b1, "$Qd, $Qm, #16">; + +class MVE_VxSHRN<string iname, string suffix, bit bit_12, bit bit_28, + dag immops, list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), !con((ins MQPR:$QdSrc, MQPR:$Qm), immops), + iname, suffix, "$Qd, $Qm, $imm", vpred_n, "$Qd = $QdSrc", + pattern> { + bits<5> imm; + + let Inst{28} = bit_28; + let Inst{25-23} = 0b101; + let Inst{21} = 0b0; + let Inst{20-16} = imm{4-0}; + let Inst{12} = bit_12; + let Inst{11-6} = 0b111111; + let Inst{4} = 0b0; + let Inst{0} = 0b1; +} + +def MVE_VRSHRNi16bh : MVE_VxSHRN< + "vrshrnb", "i16", 0b0, 0b1, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VRSHRNi16th : MVE_VxSHRN< + "vrshrnt", "i16", 0b1, 0b1,(ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VRSHRNi32bh : MVE_VxSHRN< + "vrshrnb", "i32", 0b0, 0b1, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} +def MVE_VRSHRNi32th : MVE_VxSHRN< + "vrshrnt", "i32", 0b1, 0b1, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} + +def MVE_VSHRNi16bh : MVE_VxSHRN< + "vshrnb", "i16", 0b0, 0b0, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VSHRNi16th : MVE_VxSHRN< + "vshrnt", "i16", 0b1, 0b0, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VSHRNi32bh : MVE_VxSHRN< + "vshrnb", "i32", 0b0, 0b0, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} +def MVE_VSHRNi32th : MVE_VxSHRN< + "vshrnt", "i32", 0b1, 0b0, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} + +class MVE_VxQRSHRUN<string iname, string suffix, bit bit_28, bit bit_12, dag immops, + list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), !con((ins MQPR:$QdSrc, MQPR:$Qm), immops), + iname, suffix, "$Qd, $Qm, $imm", vpred_n, "$Qd = $QdSrc", + pattern> { + bits<5> imm; + + let Inst{28} = bit_28; + let Inst{25-23} = 0b101; + let Inst{21} = 0b0; + let Inst{20-16} = imm{4-0}; + let Inst{12} = bit_12; + let Inst{11-6} = 0b111111; + let Inst{4} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VQRSHRUNs16bh : MVE_VxQRSHRUN< + "vqrshrunb", "s16", 0b1, 0b0, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VQRSHRUNs16th : MVE_VxQRSHRUN< + "vqrshrunt", "s16", 0b1, 0b1, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VQRSHRUNs32bh : MVE_VxQRSHRUN< + "vqrshrunb", "s32", 0b1, 0b0, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} +def MVE_VQRSHRUNs32th : MVE_VxQRSHRUN< + "vqrshrunt", "s32", 0b1, 0b1, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} + +def MVE_VQSHRUNs16bh : MVE_VxQRSHRUN< + "vqshrunb", "s16", 0b0, 0b0, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VQSHRUNs16th : MVE_VxQRSHRUN< + "vqshrunt", "s16", 0b0, 0b1, (ins shr_imm8:$imm)> { + let Inst{20-19} = 0b01; +} +def MVE_VQSHRUNs32bh : MVE_VxQRSHRUN< + "vqshrunb", "s32", 0b0, 0b0, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} +def MVE_VQSHRUNs32th : MVE_VxQRSHRUN< + "vqshrunt", "s32", 0b0, 0b1, (ins shr_imm16:$imm)> { + let Inst{20} = 0b1; +} + +class MVE_VxQRSHRN<string iname, string suffix, bit bit_0, bit bit_12, + dag immops, list<dag> pattern=[]> + : MVE_shift_imm<(outs MQPR:$Qd), !con((ins MQPR:$QdSrc, MQPR:$Qm), immops), + iname, suffix, "$Qd, $Qm, $imm", vpred_n, "$Qd = $QdSrc", + pattern> { + bits<5> imm; + + let Inst{25-23} = 0b101; + let Inst{21} = 0b0; + let Inst{20-16} = imm{4-0}; + let Inst{12} = bit_12; + let Inst{11-6} = 0b111101; + let Inst{4} = 0b0; + let Inst{0} = bit_0; +} + +multiclass MVE_VxQRSHRN_types<string iname, bit bit_0, bit bit_12> { + def s16 : MVE_VxQRSHRN<iname, "s16", bit_0, bit_12, (ins shr_imm8:$imm)> { + let Inst{28} = 0b0; + let Inst{20-19} = 0b01; + } + def u16 : MVE_VxQRSHRN<iname, "u16", bit_0, bit_12, (ins shr_imm8:$imm)> { + let Inst{28} = 0b1; + let Inst{20-19} = 0b01; + } + def s32 : MVE_VxQRSHRN<iname, "s32", bit_0, bit_12, (ins shr_imm16:$imm)> { + let Inst{28} = 0b0; + let Inst{20} = 0b1; + } + def u32 : MVE_VxQRSHRN<iname, "u32", bit_0, bit_12, (ins shr_imm16:$imm)> { + let Inst{28} = 0b1; + let Inst{20} = 0b1; + } +} + +defm MVE_VQRSHRNbh : MVE_VxQRSHRN_types<"vqrshrnb", 0b1, 0b0>; +defm MVE_VQRSHRNth : MVE_VxQRSHRN_types<"vqrshrnt", 0b1, 0b1>; +defm MVE_VQSHRNbh : MVE_VxQRSHRN_types<"vqshrnb", 0b0, 0b0>; +defm MVE_VQSHRNth : MVE_VxQRSHRN_types<"vqshrnt", 0b0, 0b1>; + +// end of mve_imm_shift instructions + +// start of mve_shift instructions + +class MVE_shift_by_vec<string iname, string suffix, bit U, + bits<2> size, bit bit_4, bit bit_8> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qm, MQPR:$Qn), NoItinerary, + iname, suffix, "$Qd, $Qm, $Qn", vpred_r, "", []> { + // Shift instructions which take a vector of shift counts + bits<4> Qd; + bits<4> Qm; + bits<4> Qn; + + let Inst{28} = U; + let Inst{25-24} = 0b11; + let Inst{23} = 0b0; + let Inst{22} = Qd{3}; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{15-13} = Qd{2-0}; + let Inst{12-9} = 0b0010; + let Inst{8} = bit_8; + let Inst{7} = Qn{3}; + let Inst{6} = 0b1; + let Inst{5} = Qm{3}; + let Inst{4} = bit_4; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +multiclass mve_shift_by_vec_multi<string iname, bit bit_4, bit bit_8> { + def s8 : MVE_shift_by_vec<iname, "s8", 0b0, 0b00, bit_4, bit_8>; + def s16 : MVE_shift_by_vec<iname, "s16", 0b0, 0b01, bit_4, bit_8>; + def s32 : MVE_shift_by_vec<iname, "s32", 0b0, 0b10, bit_4, bit_8>; + def u8 : MVE_shift_by_vec<iname, "u8", 0b1, 0b00, bit_4, bit_8>; + def u16 : MVE_shift_by_vec<iname, "u16", 0b1, 0b01, bit_4, bit_8>; + def u32 : MVE_shift_by_vec<iname, "u32", 0b1, 0b10, bit_4, bit_8>; +} + +defm MVE_VSHL_by_vec : mve_shift_by_vec_multi<"vshl", 0b0, 0b0>; +defm MVE_VQSHL_by_vec : mve_shift_by_vec_multi<"vqshl", 0b1, 0b0>; +defm MVE_VQRSHL_by_vec : mve_shift_by_vec_multi<"vqrshl", 0b1, 0b1>; +defm MVE_VRSHL_by_vec : mve_shift_by_vec_multi<"vrshl", 0b0, 0b1>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v4i32 (ARMvshlu (v4i32 MQPR:$Qm), (v4i32 MQPR:$Qn))), + (v4i32 (MVE_VSHL_by_vecu32 (v4i32 MQPR:$Qm), (v4i32 MQPR:$Qn)))>; + def : Pat<(v8i16 (ARMvshlu (v8i16 MQPR:$Qm), (v8i16 MQPR:$Qn))), + (v8i16 (MVE_VSHL_by_vecu16 (v8i16 MQPR:$Qm), (v8i16 MQPR:$Qn)))>; + def : Pat<(v16i8 (ARMvshlu (v16i8 MQPR:$Qm), (v16i8 MQPR:$Qn))), + (v16i8 (MVE_VSHL_by_vecu8 (v16i8 MQPR:$Qm), (v16i8 MQPR:$Qn)))>; + + def : Pat<(v4i32 (ARMvshls (v4i32 MQPR:$Qm), (v4i32 MQPR:$Qn))), + (v4i32 (MVE_VSHL_by_vecs32 (v4i32 MQPR:$Qm), (v4i32 MQPR:$Qn)))>; + def : Pat<(v8i16 (ARMvshls (v8i16 MQPR:$Qm), (v8i16 MQPR:$Qn))), + (v8i16 (MVE_VSHL_by_vecs16 (v8i16 MQPR:$Qm), (v8i16 MQPR:$Qn)))>; + def : Pat<(v16i8 (ARMvshls (v16i8 MQPR:$Qm), (v16i8 MQPR:$Qn))), + (v16i8 (MVE_VSHL_by_vecs8 (v16i8 MQPR:$Qm), (v16i8 MQPR:$Qn)))>; +} + +class MVE_shift_with_imm<string iname, string suffix, dag oops, dag iops, + string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, iname, suffix, ops, vpred, cstr, pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{23} = 0b1; + let Inst{22} = Qd{3}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-11} = 0b00; + let Inst{7-6} = 0b01; + let Inst{5} = Qm{3}; + let Inst{4} = 0b1; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +class MVE_VSxI_imm<string iname, string suffix, bit bit_8, dag imm> + : MVE_shift_with_imm<iname, suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qd_src, MQPR:$Qm), imm), + "$Qd, $Qm, $imm", vpred_n, "$Qd = $Qd_src"> { + bits<6> imm; + let Inst{28} = 0b1; + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-9} = 0b10; + let Inst{8} = bit_8; +} + +def MVE_VSRIimm8 : MVE_VSxI_imm<"vsri", "8", 0b0, (ins shr_imm8:$imm)> { + let Inst{21-19} = 0b001; +} + +def MVE_VSRIimm16 : MVE_VSxI_imm<"vsri", "16", 0b0, (ins shr_imm16:$imm)> { + let Inst{21-20} = 0b01; +} + +def MVE_VSRIimm32 : MVE_VSxI_imm<"vsri", "32", 0b0, (ins shr_imm32:$imm)> { + let Inst{21} = 0b1; +} + +def MVE_VSLIimm8 : MVE_VSxI_imm<"vsli", "8", 0b1, (ins imm0_7:$imm)> { + let Inst{21-19} = 0b001; +} + +def MVE_VSLIimm16 : MVE_VSxI_imm<"vsli", "16", 0b1, (ins imm0_15:$imm)> { + let Inst{21-20} = 0b01; +} + +def MVE_VSLIimm32 : MVE_VSxI_imm<"vsli", "32", 0b1,(ins imm0_31:$imm)> { + let Inst{21} = 0b1; +} + +class MVE_VQSHL_imm<string suffix, dag imm> + : MVE_shift_with_imm<"vqshl", suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qm), imm), "$Qd, $Qm, $imm", + vpred_r, ""> { + bits<6> imm; + + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-8} = 0b111; +} + +def MVE_VSLIimms8 : MVE_VQSHL_imm<"s8", (ins imm0_7:$imm)> { + let Inst{28} = 0b0; + let Inst{21-19} = 0b001; +} + +def MVE_VSLIimmu8 : MVE_VQSHL_imm<"u8", (ins imm0_7:$imm)> { + let Inst{28} = 0b1; + let Inst{21-19} = 0b001; +} + +def MVE_VSLIimms16 : MVE_VQSHL_imm<"s16", (ins imm0_15:$imm)> { + let Inst{28} = 0b0; + let Inst{21-20} = 0b01; +} + +def MVE_VSLIimmu16 : MVE_VQSHL_imm<"u16", (ins imm0_15:$imm)> { + let Inst{28} = 0b1; + let Inst{21-20} = 0b01; +} + +def MVE_VSLIimms32 : MVE_VQSHL_imm<"s32", (ins imm0_31:$imm)> { + let Inst{28} = 0b0; + let Inst{21} = 0b1; +} + +def MVE_VSLIimmu32 : MVE_VQSHL_imm<"u32", (ins imm0_31:$imm)> { + let Inst{28} = 0b1; + let Inst{21} = 0b1; +} + +class MVE_VQSHLU_imm<string suffix, dag imm> + : MVE_shift_with_imm<"vqshlu", suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qm), imm), "$Qd, $Qm, $imm", + vpred_r, ""> { + bits<6> imm; + + let Inst{28} = 0b1; + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-8} = 0b110; +} + +def MVE_VQSHLU_imms8 : MVE_VQSHLU_imm<"s8", (ins imm0_7:$imm)> { + let Inst{21-19} = 0b001; +} + +def MVE_VQSHLU_imms16 : MVE_VQSHLU_imm<"s16", (ins imm0_15:$imm)> { + let Inst{21-20} = 0b01; +} + +def MVE_VQSHLU_imms32 : MVE_VQSHLU_imm<"s32", (ins imm0_31:$imm)> { + let Inst{21} = 0b1; +} + +class MVE_VRSHR_imm<string suffix, dag imm> + : MVE_shift_with_imm<"vrshr", suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qm), imm), "$Qd, $Qm, $imm", + vpred_r, ""> { + bits<6> imm; + + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-8} = 0b010; +} + +def MVE_VRSHR_imms8 : MVE_VRSHR_imm<"s8", (ins shr_imm8:$imm)> { + let Inst{28} = 0b0; + let Inst{21-19} = 0b001; +} + +def MVE_VRSHR_immu8 : MVE_VRSHR_imm<"u8", (ins shr_imm8:$imm)> { + let Inst{28} = 0b1; + let Inst{21-19} = 0b001; +} + +def MVE_VRSHR_imms16 : MVE_VRSHR_imm<"s16", (ins shr_imm16:$imm)> { + let Inst{28} = 0b0; + let Inst{21-20} = 0b01; +} + +def MVE_VRSHR_immu16 : MVE_VRSHR_imm<"u16", (ins shr_imm16:$imm)> { + let Inst{28} = 0b1; + let Inst{21-20} = 0b01; +} + +def MVE_VRSHR_imms32 : MVE_VRSHR_imm<"s32", (ins shr_imm32:$imm)> { + let Inst{28} = 0b0; + let Inst{21} = 0b1; +} + +def MVE_VRSHR_immu32 : MVE_VRSHR_imm<"u32", (ins shr_imm32:$imm)> { + let Inst{28} = 0b1; + let Inst{21} = 0b1; +} + +class MVE_VSHR_imm<string suffix, dag imm> + : MVE_shift_with_imm<"vshr", suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qm), imm), "$Qd, $Qm, $imm", + vpred_r, ""> { + bits<6> imm; + + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-8} = 0b000; +} + +def MVE_VSHR_imms8 : MVE_VSHR_imm<"s8", (ins shr_imm8:$imm)> { + let Inst{28} = 0b0; + let Inst{21-19} = 0b001; +} + +def MVE_VSHR_immu8 : MVE_VSHR_imm<"u8", (ins shr_imm8:$imm)> { + let Inst{28} = 0b1; + let Inst{21-19} = 0b001; +} + +def MVE_VSHR_imms16 : MVE_VSHR_imm<"s16", (ins shr_imm16:$imm)> { + let Inst{28} = 0b0; + let Inst{21-20} = 0b01; +} + +def MVE_VSHR_immu16 : MVE_VSHR_imm<"u16", (ins shr_imm16:$imm)> { + let Inst{28} = 0b1; + let Inst{21-20} = 0b01; +} + +def MVE_VSHR_imms32 : MVE_VSHR_imm<"s32", (ins shr_imm32:$imm)> { + let Inst{28} = 0b0; + let Inst{21} = 0b1; +} + +def MVE_VSHR_immu32 : MVE_VSHR_imm<"u32", (ins shr_imm32:$imm)> { + let Inst{28} = 0b1; + let Inst{21} = 0b1; +} + +class MVE_VSHL_imm<string suffix, dag imm> + : MVE_shift_with_imm<"vshl", suffix, (outs MQPR:$Qd), + !con((ins MQPR:$Qm), imm), "$Qd, $Qm, $imm", + vpred_r, ""> { + bits<6> imm; + + let Inst{28} = 0b0; + let Inst{25-24} = 0b11; + let Inst{21-16} = imm; + let Inst{10-8} = 0b101; +} + +def MVE_VSHL_immi8 : MVE_VSHL_imm<"i8", (ins imm0_7:$imm)> { + let Inst{21-19} = 0b001; +} + +def MVE_VSHL_immi16 : MVE_VSHL_imm<"i16", (ins imm0_15:$imm)> { + let Inst{21-20} = 0b01; +} + +def MVE_VSHL_immi32 : MVE_VSHL_imm<"i32", (ins imm0_31:$imm)> { + let Inst{21} = 0b1; +} + +let Predicates = [HasMVEInt] in { + def : Pat<(v4i32 (ARMvshlImm (v4i32 MQPR:$src), imm0_31:$imm)), + (v4i32 (MVE_VSHL_immi32 (v4i32 MQPR:$src), imm0_31:$imm))>; + def : Pat<(v8i16 (ARMvshlImm (v8i16 MQPR:$src), imm0_15:$imm)), + (v8i16 (MVE_VSHL_immi16 (v8i16 MQPR:$src), imm0_15:$imm))>; + def : Pat<(v16i8 (ARMvshlImm (v16i8 MQPR:$src), imm0_7:$imm)), + (v16i8 (MVE_VSHL_immi8 (v16i8 MQPR:$src), imm0_7:$imm))>; + + def : Pat<(v4i32 (ARMvshruImm (v4i32 MQPR:$src), imm0_31:$imm)), + (v4i32 (MVE_VSHR_immu32 (v4i32 MQPR:$src), imm0_31:$imm))>; + def : Pat<(v8i16 (ARMvshruImm (v8i16 MQPR:$src), imm0_15:$imm)), + (v8i16 (MVE_VSHR_immu16 (v8i16 MQPR:$src), imm0_15:$imm))>; + def : Pat<(v16i8 (ARMvshruImm (v16i8 MQPR:$src), imm0_7:$imm)), + (v16i8 (MVE_VSHR_immu8 (v16i8 MQPR:$src), imm0_7:$imm))>; + + def : Pat<(v4i32 (ARMvshrsImm (v4i32 MQPR:$src), imm0_31:$imm)), + (v4i32 (MVE_VSHR_imms32 (v4i32 MQPR:$src), imm0_31:$imm))>; + def : Pat<(v8i16 (ARMvshrsImm (v8i16 MQPR:$src), imm0_15:$imm)), + (v8i16 (MVE_VSHR_imms16 (v8i16 MQPR:$src), imm0_15:$imm))>; + def : Pat<(v16i8 (ARMvshrsImm (v16i8 MQPR:$src), imm0_7:$imm)), + (v16i8 (MVE_VSHR_imms8 (v16i8 MQPR:$src), imm0_7:$imm))>; +} + +// end of mve_shift instructions + +// start of MVE Floating Point instructions + +class MVE_float<string iname, string suffix, dag oops, dag iops, string ops, + vpred_ops vpred, string cstr, list<dag> pattern=[]> + : MVE_f<oops, iops, NoItinerary, iname, suffix, ops, vpred, cstr, pattern> { + bits<4> Qm; + + let Inst{12} = 0b0; + let Inst{6} = 0b1; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b0; +} + +class MVE_VRINT<string rmode, bits<3> op, string suffix, bits<2> size, + list<dag> pattern=[]> + : MVE_float<!strconcat("vrint", rmode), suffix, (outs MQPR:$Qd), + (ins MQPR:$Qm), "$Qd, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b10; + let Inst{15-13} = Qd{2-0}; + let Inst{11-10} = 0b01; + let Inst{9-7} = op{2-0}; + let Inst{4} = 0b0; + +} + +multiclass MVE_VRINT_ops<string suffix, bits<2> size, list<dag> pattern=[]> { + def N : MVE_VRINT<"n", 0b000, suffix, size, pattern>; + def X : MVE_VRINT<"x", 0b001, suffix, size, pattern>; + def A : MVE_VRINT<"a", 0b010, suffix, size, pattern>; + def Z : MVE_VRINT<"z", 0b011, suffix, size, pattern>; + def M : MVE_VRINT<"m", 0b101, suffix, size, pattern>; + def P : MVE_VRINT<"p", 0b111, suffix, size, pattern>; +} + +defm MVE_VRINTf16 : MVE_VRINT_ops<"f16", 0b01>; +defm MVE_VRINTf32 : MVE_VRINT_ops<"f32", 0b10>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (frint (v4f32 MQPR:$val1))), + (v4f32 (MVE_VRINTf32X (v4f32 MQPR:$val1)))>; + def : Pat<(v8f16 (frint (v8f16 MQPR:$val1))), + (v8f16 (MVE_VRINTf16X (v8f16 MQPR:$val1)))>; + def : Pat<(v4f32 (fround (v4f32 MQPR:$val1))), + (v4f32 (MVE_VRINTf32A (v4f32 MQPR:$val1)))>; + def : Pat<(v8f16 (fround (v8f16 MQPR:$val1))), + (v8f16 (MVE_VRINTf16A (v8f16 MQPR:$val1)))>; + def : Pat<(v4f32 (ftrunc (v4f32 MQPR:$val1))), + (v4f32 (MVE_VRINTf32Z (v4f32 MQPR:$val1)))>; + def : Pat<(v8f16 (ftrunc (v8f16 MQPR:$val1))), + (v8f16 (MVE_VRINTf16Z (v8f16 MQPR:$val1)))>; + def : Pat<(v4f32 (ffloor (v4f32 MQPR:$val1))), + (v4f32 (MVE_VRINTf32M (v4f32 MQPR:$val1)))>; + def : Pat<(v8f16 (ffloor (v8f16 MQPR:$val1))), + (v8f16 (MVE_VRINTf16M (v8f16 MQPR:$val1)))>; + def : Pat<(v4f32 (fceil (v4f32 MQPR:$val1))), + (v4f32 (MVE_VRINTf32P (v4f32 MQPR:$val1)))>; + def : Pat<(v8f16 (fceil (v8f16 MQPR:$val1))), + (v8f16 (MVE_VRINTf16P (v8f16 MQPR:$val1)))>; +} + +class MVEFloatArithNeon<string iname, string suffix, bit size, + dag oops, dag iops, string ops, + vpred_ops vpred, string cstr, list<dag> pattern=[]> + : MVE_float<iname, suffix, oops, iops, ops, vpred, cstr, pattern> { + let Inst{20} = size; + let Inst{16} = 0b0; +} + +class MVE_VMUL_fp<string suffix, bit size, list<dag> pattern=[]> + : MVEFloatArithNeon<"vmul", suffix, size, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm), "$Qd, $Qn, $Qm", vpred_r, "", + pattern> { + bits<4> Qd; + bits<4> Qn; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b110; + let Inst{22} = Qd{3}; + let Inst{21} = 0b0; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-8} = 0b01101; + let Inst{7} = Qn{3}; + let Inst{4} = 0b1; +} + +def MVE_VMULf32 : MVE_VMUL_fp<"f32", 0b0>; +def MVE_VMULf16 : MVE_VMUL_fp<"f16", 0b1>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (fmul (v4f32 MQPR:$val1), (v4f32 MQPR:$val2))), + (v4f32 (MVE_VMULf32 (v4f32 MQPR:$val1), (v4f32 MQPR:$val2)))>; + def : Pat<(v8f16 (fmul (v8f16 MQPR:$val1), (v8f16 MQPR:$val2))), + (v8f16 (MVE_VMULf16 (v8f16 MQPR:$val1), (v8f16 MQPR:$val2)))>; +} + +class MVE_VCMLA<string suffix, bit size, list<dag> pattern=[]> + : MVEFloatArithNeon<"vcmla", suffix, size, (outs MQPR:$Qd), + (ins MQPR:$Qd_src, MQPR:$Qn, MQPR:$Qm, complexrotateop:$rot), + "$Qd, $Qn, $Qm, $rot", vpred_n, "$Qd = $Qd_src", pattern> { + bits<4> Qd; + bits<4> Qn; + bits<2> rot; + + let Inst{28} = 0b1; + let Inst{25} = 0b0; + let Inst{24-23} = rot; + let Inst{22} = Qd{3}; + let Inst{21} = 0b1; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-8} = 0b01000; + let Inst{7} = Qn{3}; + let Inst{4} = 0b0; +} + +def MVE_VCMLAf16 : MVE_VCMLA<"f16", 0b0>; +def MVE_VCMLAf32 : MVE_VCMLA<"f32", 0b1>; + +class MVE_VADDSUBFMA_fp<string iname, string suffix, bit size, bit bit_4, + bit bit_8, bit bit_21, dag iops=(ins), + vpred_ops vpred=vpred_r, string cstr="", + list<dag> pattern=[]> + : MVEFloatArithNeon<iname, suffix, size, (outs MQPR:$Qd), + !con(iops, (ins MQPR:$Qn, MQPR:$Qm)), "$Qd, $Qn, $Qm", + vpred, cstr, pattern> { + bits<4> Qd; + bits<4> Qn; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b110; + let Inst{22} = Qd{3}; + let Inst{21} = bit_21; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{11-9} = 0b110; + let Inst{8} = bit_8; + let Inst{7} = Qn{3}; + let Inst{4} = bit_4; +} + +def MVE_VFMAf32 : MVE_VADDSUBFMA_fp<"vfma", "f32", 0b0, 0b1, 0b0, 0b0, + (ins MQPR:$Qd_src), vpred_n, "$Qd = $Qd_src">; +def MVE_VFMAf16 : MVE_VADDSUBFMA_fp<"vfma", "f16", 0b1, 0b1, 0b0, 0b0, + (ins MQPR:$Qd_src), vpred_n, "$Qd = $Qd_src">; + +def MVE_VFMSf32 : MVE_VADDSUBFMA_fp<"vfms", "f32", 0b0, 0b1, 0b0, 0b1, + (ins MQPR:$Qd_src), vpred_n, "$Qd = $Qd_src">; +def MVE_VFMSf16 : MVE_VADDSUBFMA_fp<"vfms", "f16", 0b1, 0b1, 0b0, 0b1, + (ins MQPR:$Qd_src), vpred_n, "$Qd = $Qd_src">; + +def MVE_VADDf32 : MVE_VADDSUBFMA_fp<"vadd", "f32", 0b0, 0b0, 0b1, 0b0>; +def MVE_VADDf16 : MVE_VADDSUBFMA_fp<"vadd", "f16", 0b1, 0b0, 0b1, 0b0>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (fadd (v4f32 MQPR:$val1), (v4f32 MQPR:$val2))), + (v4f32 (MVE_VADDf32 (v4f32 MQPR:$val1), (v4f32 MQPR:$val2)))>; + def : Pat<(v8f16 (fadd (v8f16 MQPR:$val1), (v8f16 MQPR:$val2))), + (v8f16 (MVE_VADDf16 (v8f16 MQPR:$val1), (v8f16 MQPR:$val2)))>; +} + +def MVE_VSUBf32 : MVE_VADDSUBFMA_fp<"vsub", "f32", 0b0, 0b0, 0b1, 0b1>; +def MVE_VSUBf16 : MVE_VADDSUBFMA_fp<"vsub", "f16", 0b1, 0b0, 0b1, 0b1>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4f32 (fsub (v4f32 MQPR:$val1), (v4f32 MQPR:$val2))), + (v4f32 (MVE_VSUBf32 (v4f32 MQPR:$val1), (v4f32 MQPR:$val2)))>; + def : Pat<(v8f16 (fsub (v8f16 MQPR:$val1), (v8f16 MQPR:$val2))), + (v8f16 (MVE_VSUBf16 (v8f16 MQPR:$val1), (v8f16 MQPR:$val2)))>; +} + +class MVE_VCADD<string suffix, bit size, list<dag> pattern=[]> + : MVEFloatArithNeon<"vcadd", suffix, size, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm, complexrotateopodd:$rot), + "$Qd, $Qn, $Qm, $rot", vpred_r, "", pattern> { + bits<4> Qd; + bits<4> Qn; + bit rot; + + let Inst{28} = 0b1; + let Inst{25} = 0b0; + let Inst{24} = rot; + let Inst{23} = 0b1; + let Inst{22} = Qd{3}; + let Inst{21} = 0b0; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-8} = 0b01000; + let Inst{7} = Qn{3}; + let Inst{4} = 0b0; +} + +def MVE_VCADDf16 : MVE_VCADD<"f16", 0b0>; +def MVE_VCADDf32 : MVE_VCADD<"f32", 0b1>; + +class MVE_VABD_fp<string suffix, bit size> + : MVE_float<"vabd", suffix, (outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), + "$Qd, $Qn, $Qm", vpred_r, ""> { + bits<4> Qd; + bits<4> Qn; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b110; + let Inst{22} = Qd{3}; + let Inst{21} = 0b1; + let Inst{20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{15-13} = Qd{2-0}; + let Inst{11-8} = 0b1101; + let Inst{7} = Qn{3}; + let Inst{4} = 0b0; +} + +def MVE_VABDf32 : MVE_VABD_fp<"f32", 0b0>; +def MVE_VABDf16 : MVE_VABD_fp<"f16", 0b1>; + +class MVE_VCVT_fix<string suffix, bit fsi, bit U, bit op, + Operand imm_operand_type, list<dag> pattern=[]> + : MVE_float<"vcvt", suffix, + (outs MQPR:$Qd), (ins MQPR:$Qm, imm_operand_type:$imm6), + "$Qd, $Qm, $imm6", vpred_r, "", pattern> { + bits<4> Qd; + bits<6> imm6; + + let Inst{28} = U; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21} = 0b1; + let Inst{19-16} = imm6{3-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{11-10} = 0b11; + let Inst{9} = fsi; + let Inst{8} = op; + let Inst{7} = 0b0; + let Inst{4} = 0b1; + + let DecoderMethod = "DecodeMVEVCVTt1fp"; +} + +class MVE_VCVT_imm_asmop<int Bits> : AsmOperandClass { + let PredicateMethod = "isImmediate<1," # Bits # ">"; + let DiagnosticString = + "MVE fixed-point immediate operand must be between 1 and " # Bits; + let Name = "MVEVcvtImm" # Bits; + let RenderMethod = "addImmOperands"; +} +class MVE_VCVT_imm<int Bits>: Operand<i32> { + let ParserMatchClass = MVE_VCVT_imm_asmop<Bits>; + let EncoderMethod = "getNEONVcvtImm32OpValue"; + let DecoderMethod = "DecodeVCVTImmOperand"; +} + +class MVE_VCVT_fix_f32<string suffix, bit U, bit op> + : MVE_VCVT_fix<suffix, 0b1, U, op, MVE_VCVT_imm<32>> { + let Inst{20} = imm6{4}; +} +class MVE_VCVT_fix_f16<string suffix, bit U, bit op> + : MVE_VCVT_fix<suffix, 0b0, U, op, MVE_VCVT_imm<16>> { + let Inst{20} = 0b1; +} + +def MVE_VCVTf16s16_fix : MVE_VCVT_fix_f16<"f16.s16", 0b0, 0b0>; +def MVE_VCVTs16f16_fix : MVE_VCVT_fix_f16<"s16.f16", 0b0, 0b1>; +def MVE_VCVTf16u16_fix : MVE_VCVT_fix_f16<"f16.u16", 0b1, 0b0>; +def MVE_VCVTu16f16_fix : MVE_VCVT_fix_f16<"u16.f16", 0b1, 0b1>; +def MVE_VCVTf32s32_fix : MVE_VCVT_fix_f32<"f32.s32", 0b0, 0b0>; +def MVE_VCVTs32f32_fix : MVE_VCVT_fix_f32<"s32.f32", 0b0, 0b1>; +def MVE_VCVTf32u32_fix : MVE_VCVT_fix_f32<"f32.u32", 0b1, 0b0>; +def MVE_VCVTu32f32_fix : MVE_VCVT_fix_f32<"u32.f32", 0b1, 0b1>; + +class MVE_VCVT_fp_int_anpm<string suffix, bits<2> size, bit op, string anpm, + bits<2> rm, list<dag> pattern=[]> + : MVE_float<!strconcat("vcvt", anpm), suffix, (outs MQPR:$Qd), + (ins MQPR:$Qm), "$Qd, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b11; + let Inst{15-13} = Qd{2-0}; + let Inst{12-10} = 0b000; + let Inst{9-8} = rm; + let Inst{7} = op; + let Inst{4} = 0b0; +} + +multiclass MVE_VCVT_fp_int_anpm_multi<string suffix, bits<2> size, bit op, + list<dag> pattern=[]> { + def a : MVE_VCVT_fp_int_anpm<suffix, size, op, "a", 0b00>; + def n : MVE_VCVT_fp_int_anpm<suffix, size, op, "n", 0b01>; + def p : MVE_VCVT_fp_int_anpm<suffix, size, op, "p", 0b10>; + def m : MVE_VCVT_fp_int_anpm<suffix, size, op, "m", 0b11>; +} + +// This defines instructions such as MVE_VCVTu16f16a, with an explicit +// rounding-mode suffix on the mnemonic. The class below will define +// the bare MVE_VCVTu16f16 (with implied rounding toward zero). +defm MVE_VCVTs16f16 : MVE_VCVT_fp_int_anpm_multi<"s16.f16", 0b01, 0b0>; +defm MVE_VCVTu16f16 : MVE_VCVT_fp_int_anpm_multi<"u16.f16", 0b01, 0b1>; +defm MVE_VCVTs32f32 : MVE_VCVT_fp_int_anpm_multi<"s32.f32", 0b10, 0b0>; +defm MVE_VCVTu32f32 : MVE_VCVT_fp_int_anpm_multi<"u32.f32", 0b10, 0b1>; + +class MVE_VCVT_fp_int<string suffix, bits<2> size, bits<2> op, + list<dag> pattern=[]> + : MVE_float<"vcvt", suffix, (outs MQPR:$Qd), + (ins MQPR:$Qm), "$Qd, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b11; + let Inst{15-13} = Qd{2-0}; + let Inst{12-9} = 0b0011; + let Inst{8-7} = op; + let Inst{4} = 0b0; +} + +// The unsuffixed VCVT for float->int implicitly rounds toward zero, +// which I reflect here in the llvm instruction names +def MVE_VCVTs16f16z : MVE_VCVT_fp_int<"s16.f16", 0b01, 0b10>; +def MVE_VCVTu16f16z : MVE_VCVT_fp_int<"u16.f16", 0b01, 0b11>; +def MVE_VCVTs32f32z : MVE_VCVT_fp_int<"s32.f32", 0b10, 0b10>; +def MVE_VCVTu32f32z : MVE_VCVT_fp_int<"u32.f32", 0b10, 0b11>; +// Whereas VCVT for int->float rounds to nearest +def MVE_VCVTf16s16n : MVE_VCVT_fp_int<"f16.s16", 0b01, 0b00>; +def MVE_VCVTf16u16n : MVE_VCVT_fp_int<"f16.u16", 0b01, 0b01>; +def MVE_VCVTf32s32n : MVE_VCVT_fp_int<"f32.s32", 0b10, 0b00>; +def MVE_VCVTf32u32n : MVE_VCVT_fp_int<"f32.u32", 0b10, 0b01>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v4i32 (fp_to_sint (v4f32 MQPR:$src))), + (v4i32 (MVE_VCVTs32f32z (v4f32 MQPR:$src)))>; + def : Pat<(v4i32 (fp_to_uint (v4f32 MQPR:$src))), + (v4i32 (MVE_VCVTu32f32z (v4f32 MQPR:$src)))>; + def : Pat<(v8i16 (fp_to_sint (v8f16 MQPR:$src))), + (v8i16 (MVE_VCVTs16f16z (v8f16 MQPR:$src)))>; + def : Pat<(v8i16 (fp_to_uint (v8f16 MQPR:$src))), + (v8i16 (MVE_VCVTu16f16z (v8f16 MQPR:$src)))>; + def : Pat<(v4f32 (sint_to_fp (v4i32 MQPR:$src))), + (v4f32 (MVE_VCVTf32s32n (v4i32 MQPR:$src)))>; + def : Pat<(v4f32 (uint_to_fp (v4i32 MQPR:$src))), + (v4f32 (MVE_VCVTf32u32n (v4i32 MQPR:$src)))>; + def : Pat<(v8f16 (sint_to_fp (v8i16 MQPR:$src))), + (v8f16 (MVE_VCVTf16s16n (v8i16 MQPR:$src)))>; + def : Pat<(v8f16 (uint_to_fp (v8i16 MQPR:$src))), + (v8f16 (MVE_VCVTf16u16n (v8i16 MQPR:$src)))>; +} + +class MVE_VABSNEG_fp<string iname, string suffix, bits<2> size, bit negate, + list<dag> pattern=[]> + : MVE_float<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qm), "$Qd, $Qm", vpred_r, "", pattern> { + bits<4> Qd; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b111; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17-16} = 0b01; + let Inst{15-13} = Qd{2-0}; + let Inst{11-8} = 0b0111; + let Inst{7} = negate; + let Inst{4} = 0b0; +} + +def MVE_VABSf16 : MVE_VABSNEG_fp<"vabs", "f16", 0b01, 0b0>; +def MVE_VABSf32 : MVE_VABSNEG_fp<"vabs", "f32", 0b10, 0b0>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v8f16 (fabs MQPR:$src)), + (MVE_VABSf16 MQPR:$src)>; + def : Pat<(v4f32 (fabs MQPR:$src)), + (MVE_VABSf32 MQPR:$src)>; +} + +def MVE_VNEGf16 : MVE_VABSNEG_fp<"vneg", "f16", 0b01, 0b1>; +def MVE_VNEGf32 : MVE_VABSNEG_fp<"vneg", "f32", 0b10, 0b1>; + +let Predicates = [HasMVEFloat] in { + def : Pat<(v8f16 (fneg MQPR:$src)), + (MVE_VNEGf16 MQPR:$src)>; + def : Pat<(v4f32 (fneg MQPR:$src)), + (MVE_VNEGf32 MQPR:$src)>; +} + +class MVE_VMAXMINNMA<string iname, string suffix, bit size, bit bit_12, + list<dag> pattern=[]> + : MVE_f<(outs MQPR:$Qd), (ins MQPR:$Qd_src, MQPR:$Qm), + NoItinerary, iname, suffix, "$Qd, $Qm", vpred_n, "$Qd = $Qd_src", + pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{28} = size; + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{21-16} = 0b111111; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = bit_12; + let Inst{11-6} = 0b111010; + let Inst{5} = Qm{3}; + let Inst{4} = 0b0; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b1; +} + +def MVE_VMAXNMAf32 : MVE_VMAXMINNMA<"vmaxnma", "f32", 0b0, 0b0>; +def MVE_VMAXNMAf16 : MVE_VMAXMINNMA<"vmaxnma", "f16", 0b1, 0b0>; + +def MVE_VMINNMAf32 : MVE_VMAXMINNMA<"vminnma", "f32", 0b0, 0b1>; +def MVE_VMINNMAf16 : MVE_VMAXMINNMA<"vminnma", "f16", 0b1, 0b1>; + +// end of MVE Floating Point instructions + +// start of MVE compares + +class MVE_VCMPqq<string suffix, bit bit_28, bits<2> bits_21_20, + VCMPPredicateOperand predtype, list<dag> pattern=[]> + : MVE_p<(outs VCCR:$P0), (ins MQPR:$Qn, MQPR:$Qm, predtype:$fc), + NoItinerary, "vcmp", suffix, "$fc, $Qn, $Qm", vpred_n, "", pattern> { + // Base class for comparing two vector registers + bits<3> fc; + bits<4> Qn; + bits<4> Qm; + + let Inst{28} = bit_28; + let Inst{25-22} = 0b1000; + let Inst{21-20} = bits_21_20; + let Inst{19-17} = Qn{2-0}; + let Inst{16-13} = 0b1000; + let Inst{12} = fc{2}; + let Inst{11-8} = 0b1111; + let Inst{7} = fc{0}; + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{4} = 0b0; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = fc{1}; + + let Constraints = ""; + + // We need a custom decoder method for these instructions because of + // the output VCCR operand, which isn't encoded in the instruction + // bits anywhere (there is only one choice for it) but has to be + // included in the MC operands so that codegen will be able to track + // its data flow between instructions, spill/reload it when + // necessary, etc. There seems to be no way to get the Tablegen + // decoder to emit an operand that isn't affected by any instruction + // bit. + let DecoderMethod = "DecodeMVEVCMP<false," # predtype.DecoderMethod # ">"; +} + +class MVE_VCMPqqf<string suffix, bit size> + : MVE_VCMPqq<suffix, size, 0b11, pred_basic_fp> { + let Predicates = [HasMVEFloat]; +} + +class MVE_VCMPqqi<string suffix, bits<2> size> + : MVE_VCMPqq<suffix, 0b1, size, pred_basic_i> { + let Inst{12} = 0b0; + let Inst{0} = 0b0; +} + +class MVE_VCMPqqu<string suffix, bits<2> size> + : MVE_VCMPqq<suffix, 0b1, size, pred_basic_u> { + let Inst{12} = 0b0; + let Inst{0} = 0b1; +} + +class MVE_VCMPqqs<string suffix, bits<2> size> + : MVE_VCMPqq<suffix, 0b1, size, pred_basic_s> { + let Inst{12} = 0b1; +} + +def MVE_VCMPf32 : MVE_VCMPqqf<"f32", 0b0>; +def MVE_VCMPf16 : MVE_VCMPqqf<"f16", 0b1>; + +def MVE_VCMPi8 : MVE_VCMPqqi<"i8", 0b00>; +def MVE_VCMPi16 : MVE_VCMPqqi<"i16", 0b01>; +def MVE_VCMPi32 : MVE_VCMPqqi<"i32", 0b10>; + +def MVE_VCMPu8 : MVE_VCMPqqu<"u8", 0b00>; +def MVE_VCMPu16 : MVE_VCMPqqu<"u16", 0b01>; +def MVE_VCMPu32 : MVE_VCMPqqu<"u32", 0b10>; + +def MVE_VCMPs8 : MVE_VCMPqqs<"s8", 0b00>; +def MVE_VCMPs16 : MVE_VCMPqqs<"s16", 0b01>; +def MVE_VCMPs32 : MVE_VCMPqqs<"s32", 0b10>; + +class MVE_VCMPqr<string suffix, bit bit_28, bits<2> bits_21_20, + VCMPPredicateOperand predtype, list<dag> pattern=[]> + : MVE_p<(outs VCCR:$P0), (ins MQPR:$Qn, GPRwithZR:$Rm, predtype:$fc), + NoItinerary, "vcmp", suffix, "$fc, $Qn, $Rm", vpred_n, "", pattern> { + // Base class for comparing a vector register with a scalar + bits<3> fc; + bits<4> Qn; + bits<4> Rm; + + let Inst{28} = bit_28; + let Inst{25-22} = 0b1000; + let Inst{21-20} = bits_21_20; + let Inst{19-17} = Qn{2-0}; + let Inst{16-13} = 0b1000; + let Inst{12} = fc{2}; + let Inst{11-8} = 0b1111; + let Inst{7} = fc{0}; + let Inst{6} = 0b1; + let Inst{5} = fc{1}; + let Inst{4} = 0b0; + let Inst{3-0} = Rm{3-0}; + + let Constraints = ""; + // Custom decoder method, for the same reason as MVE_VCMPqq + let DecoderMethod = "DecodeMVEVCMP<true," # predtype.DecoderMethod # ">"; +} + +class MVE_VCMPqrf<string suffix, bit size> + : MVE_VCMPqr<suffix, size, 0b11, pred_basic_fp> { + let Predicates = [HasMVEFloat]; +} + +class MVE_VCMPqri<string suffix, bits<2> size> + : MVE_VCMPqr<suffix, 0b1, size, pred_basic_i> { + let Inst{12} = 0b0; + let Inst{5} = 0b0; +} + +class MVE_VCMPqru<string suffix, bits<2> size> + : MVE_VCMPqr<suffix, 0b1, size, pred_basic_u> { + let Inst{12} = 0b0; + let Inst{5} = 0b1; +} + +class MVE_VCMPqrs<string suffix, bits<2> size> + : MVE_VCMPqr<suffix, 0b1, size, pred_basic_s> { + let Inst{12} = 0b1; +} + +def MVE_VCMPf32r : MVE_VCMPqrf<"f32", 0b0>; +def MVE_VCMPf16r : MVE_VCMPqrf<"f16", 0b1>; + +def MVE_VCMPi8r : MVE_VCMPqri<"i8", 0b00>; +def MVE_VCMPi16r : MVE_VCMPqri<"i16", 0b01>; +def MVE_VCMPi32r : MVE_VCMPqri<"i32", 0b10>; + +def MVE_VCMPu8r : MVE_VCMPqru<"u8", 0b00>; +def MVE_VCMPu16r : MVE_VCMPqru<"u16", 0b01>; +def MVE_VCMPu32r : MVE_VCMPqru<"u32", 0b10>; + +def MVE_VCMPs8r : MVE_VCMPqrs<"s8", 0b00>; +def MVE_VCMPs16r : MVE_VCMPqrs<"s16", 0b01>; +def MVE_VCMPs32r : MVE_VCMPqrs<"s32", 0b10>; + +// end of MVE compares + +// start of MVE_qDest_qSrc + +class MVE_qDest_qSrc<string iname, string suffix, dag oops, dag iops, + string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, iname, suffix, + ops, vpred, cstr, pattern> { + bits<4> Qd; + bits<4> Qm; + + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{15-13} = Qd{2-0}; + let Inst{11-9} = 0b111; + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{4} = 0b0; + let Inst{3-1} = Qm{2-0}; +} + +class MVE_VQxDMLxDH<string iname, bit exch, bit round, bit subtract, + string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qd_src, MQPR:$Qn, MQPR:$Qm), "$Qd, $Qn, $Qm", + vpred_n, "$Qd = $Qd_src", pattern> { + bits<4> Qn; + + let Inst{28} = subtract; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12} = exch; + let Inst{8} = 0b0; + let Inst{7} = Qn{3}; + let Inst{0} = round; +} + +multiclass MVE_VQxDMLxDH_multi<string iname, bit exch, + bit round, bit subtract> { + def s8 : MVE_VQxDMLxDH<iname, exch, round, subtract, "s8", 0b00>; + def s16 : MVE_VQxDMLxDH<iname, exch, round, subtract, "s16", 0b01>; + def s32 : MVE_VQxDMLxDH<iname, exch, round, subtract, "s32", 0b10>; +} + +defm MVE_VQDMLADH : MVE_VQxDMLxDH_multi<"vqdmladh", 0b0, 0b0, 0b0>; +defm MVE_VQDMLADHX : MVE_VQxDMLxDH_multi<"vqdmladhx", 0b1, 0b0, 0b0>; +defm MVE_VQRDMLADH : MVE_VQxDMLxDH_multi<"vqrdmladh", 0b0, 0b1, 0b0>; +defm MVE_VQRDMLADHX : MVE_VQxDMLxDH_multi<"vqrdmladhx", 0b1, 0b1, 0b0>; +defm MVE_VQDMLSDH : MVE_VQxDMLxDH_multi<"vqdmlsdh", 0b0, 0b0, 0b1>; +defm MVE_VQDMLSDHX : MVE_VQxDMLxDH_multi<"vqdmlsdhx", 0b1, 0b0, 0b1>; +defm MVE_VQRDMLSDH : MVE_VQxDMLxDH_multi<"vqrdmlsdh", 0b0, 0b1, 0b1>; +defm MVE_VQRDMLSDHX : MVE_VQxDMLxDH_multi<"vqrdmlsdhx", 0b1, 0b1, 0b1>; + +class MVE_VCMUL<string iname, string suffix, bit size, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm, complexrotateop:$rot), + "$Qd, $Qn, $Qm, $rot", vpred_r, "", pattern> { + bits<4> Qn; + bits<2> rot; + + let Inst{28} = size; + let Inst{21-20} = 0b11; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12} = rot{1}; + let Inst{8} = 0b0; + let Inst{7} = Qn{3}; + let Inst{0} = rot{0}; + + let Predicates = [HasMVEFloat]; +} + +def MVE_VCMULf16 : MVE_VCMUL<"vcmul", "f16", 0b0>; +def MVE_VCMULf32 : MVE_VCMUL<"vcmul", "f32", 0b1>; + +class MVE_VMULL<string iname, string suffix, bit bit_28, bits<2> bits_21_20, + bit T, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm), "$Qd, $Qn, $Qm", + vpred_r, "", pattern> { + bits<4> Qd; + bits<4> Qn; + bits<4> Qm; + + let Inst{28} = bit_28; + let Inst{21-20} = bits_21_20; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b1; + let Inst{12} = T; + let Inst{8} = 0b0; + let Inst{7} = Qn{3}; + let Inst{0} = 0b0; +} + +multiclass MVE_VMULL_multi<string iname, string suffix, + bit bit_28, bits<2> bits_21_20> { + def bh : MVE_VMULL<iname # "b", suffix, bit_28, bits_21_20, 0b0>; + def th : MVE_VMULL<iname # "t", suffix, bit_28, bits_21_20, 0b1>; +} + +// For integer multiplies, bits 21:20 encode size, and bit 28 signedness. +// For polynomial multiplies, bits 21:20 take the unused value 0b11, and +// bit 28 switches to encoding the size. + +defm MVE_VMULLs8 : MVE_VMULL_multi<"vmull", "s8", 0b0, 0b00>; +defm MVE_VMULLs16 : MVE_VMULL_multi<"vmull", "s16", 0b0, 0b01>; +defm MVE_VMULLs32 : MVE_VMULL_multi<"vmull", "s32", 0b0, 0b10>; +defm MVE_VMULLu8 : MVE_VMULL_multi<"vmull", "u8", 0b1, 0b00>; +defm MVE_VMULLu16 : MVE_VMULL_multi<"vmull", "u16", 0b1, 0b01>; +defm MVE_VMULLu32 : MVE_VMULL_multi<"vmull", "u32", 0b1, 0b10>; +defm MVE_VMULLp8 : MVE_VMULL_multi<"vmull", "p8", 0b0, 0b11>; +defm MVE_VMULLp16 : MVE_VMULL_multi<"vmull", "p16", 0b1, 0b11>; + +class MVE_VxMULH<string iname, string suffix, bit U, bits<2> size, + bit round, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm), "$Qd, $Qn, $Qm", + vpred_r, "", pattern> { + bits<4> Qn; + + let Inst{28} = U; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b1; + let Inst{12} = round; + let Inst{8} = 0b0; + let Inst{7} = Qn{3}; + let Inst{0} = 0b1; +} + +def MVE_VMULHs8 : MVE_VxMULH<"vmulh", "s8", 0b0, 0b00, 0b0>; +def MVE_VMULHs16 : MVE_VxMULH<"vmulh", "s16", 0b0, 0b01, 0b0>; +def MVE_VMULHs32 : MVE_VxMULH<"vmulh", "s32", 0b0, 0b10, 0b0>; +def MVE_VMULHu8 : MVE_VxMULH<"vmulh", "u8", 0b1, 0b00, 0b0>; +def MVE_VMULHu16 : MVE_VxMULH<"vmulh", "u16", 0b1, 0b01, 0b0>; +def MVE_VMULHu32 : MVE_VxMULH<"vmulh", "u32", 0b1, 0b10, 0b0>; + +def MVE_VRMULHs8 : MVE_VxMULH<"vrmulh", "s8", 0b0, 0b00, 0b1>; +def MVE_VRMULHs16 : MVE_VxMULH<"vrmulh", "s16", 0b0, 0b01, 0b1>; +def MVE_VRMULHs32 : MVE_VxMULH<"vrmulh", "s32", 0b0, 0b10, 0b1>; +def MVE_VRMULHu8 : MVE_VxMULH<"vrmulh", "u8", 0b1, 0b00, 0b1>; +def MVE_VRMULHu16 : MVE_VxMULH<"vrmulh", "u16", 0b1, 0b01, 0b1>; +def MVE_VRMULHu32 : MVE_VxMULH<"vrmulh", "u32", 0b1, 0b10, 0b1>; + +class MVE_VxMOVxN<string iname, string suffix, bit bit_28, bit bit_17, + bits<2> size, bit T, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qd_src, MQPR:$Qm), "$Qd, $Qm", + vpred_n, "$Qd = $Qd_src", pattern> { + + let Inst{28} = bit_28; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17} = bit_17; + let Inst{16} = 0b1; + let Inst{12} = T; + let Inst{8} = 0b0; + let Inst{7} = !if(!eq(bit_17, 0), 1, 0); + let Inst{0} = 0b1; +} + +multiclass MVE_VxMOVxN_halves<string iname, string suffix, + bit bit_28, bit bit_17, bits<2> size> { + def bh : MVE_VxMOVxN<iname # "b", suffix, bit_28, bit_17, size, 0b0>; + def th : MVE_VxMOVxN<iname # "t", suffix, bit_28, bit_17, size, 0b1>; +} + +defm MVE_VMOVNi16 : MVE_VxMOVxN_halves<"vmovn", "i16", 0b1, 0b0, 0b00>; +defm MVE_VMOVNi32 : MVE_VxMOVxN_halves<"vmovn", "i32", 0b1, 0b0, 0b01>; +defm MVE_VQMOVNs16 : MVE_VxMOVxN_halves<"vqmovn", "s16", 0b0, 0b1, 0b00>; +defm MVE_VQMOVNs32 : MVE_VxMOVxN_halves<"vqmovn", "s32", 0b0, 0b1, 0b01>; +defm MVE_VQMOVNu16 : MVE_VxMOVxN_halves<"vqmovn", "u16", 0b1, 0b1, 0b00>; +defm MVE_VQMOVNu32 : MVE_VxMOVxN_halves<"vqmovn", "u32", 0b1, 0b1, 0b01>; +defm MVE_VQMOVUNs16 : MVE_VxMOVxN_halves<"vqmovun", "s16", 0b0, 0b0, 0b00>; +defm MVE_VQMOVUNs32 : MVE_VxMOVxN_halves<"vqmovun", "s32", 0b0, 0b0, 0b01>; + +class MVE_VCVT_ff<string iname, string suffix, bit op, bit T, + list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), (ins MQPR:$Qd_src, MQPR:$Qm), + "$Qd, $Qm", vpred_n, "$Qd = $Qd_src", pattern> { + let Inst{28} = op; + let Inst{21-16} = 0b111111; + let Inst{12} = T; + let Inst{8-7} = 0b00; + let Inst{0} = 0b1; + + let Predicates = [HasMVEFloat]; +} + +multiclass MVE_VCVT_ff_halves<string suffix, bit op> { + def bh : MVE_VCVT_ff<"vcvtb", suffix, op, 0b0>; + def th : MVE_VCVT_ff<"vcvtt", suffix, op, 0b1>; +} + +defm MVE_VCVTf16f32 : MVE_VCVT_ff_halves<"f16.f32", 0b0>; +defm MVE_VCVTf32f16 : MVE_VCVT_ff_halves<"f32.f16", 0b1>; + +class MVE_VxCADD<string iname, string suffix, bits<2> size, bit halve, + list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm, complexrotateopodd:$rot), + "$Qd, $Qn, $Qm, $rot", vpred_r, "", + pattern> { + bits<4> Qn; + bit rot; + + let Inst{28} = halve; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12} = rot; + let Inst{8} = 0b1; + let Inst{7} = Qn{3}; + let Inst{0} = 0b0; +} + +def MVE_VCADDi8 : MVE_VxCADD<"vcadd", "i8", 0b00, 0b1>; +def MVE_VCADDi16 : MVE_VxCADD<"vcadd", "i16", 0b01, 0b1>; +def MVE_VCADDi32 : MVE_VxCADD<"vcadd", "i32", 0b10, 0b1>; + +def MVE_VHCADDs8 : MVE_VxCADD<"vhcadd", "s8", 0b00, 0b0>; +def MVE_VHCADDs16 : MVE_VxCADD<"vhcadd", "s16", 0b01, 0b0>; +def MVE_VHCADDs32 : MVE_VxCADD<"vhcadd", "s32", 0b10, 0b0>; + +class MVE_VADCSBC<string iname, bit I, bit subtract, + dag carryin, list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, "i32", (outs MQPR:$Qd, cl_FPSCR_NZCV:$carryout), + !con((ins MQPR:$Qn, MQPR:$Qm), carryin), + "$Qd, $Qn, $Qm", vpred_r, "", pattern> { + bits<4> Qn; + + let Inst{28} = subtract; + let Inst{21-20} = 0b11; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12} = I; + let Inst{8} = 0b1; + let Inst{7} = Qn{3}; + let Inst{0} = 0b0; + + // Custom decoder method in order to add the FPSCR operand(s), which + // Tablegen won't do right + let DecoderMethod = "DecodeMVEVADCInstruction"; +} + +def MVE_VADC : MVE_VADCSBC<"vadc", 0b0, 0b0, (ins cl_FPSCR_NZCV:$carryin)>; +def MVE_VADCI : MVE_VADCSBC<"vadci", 0b1, 0b0, (ins)>; + +def MVE_VSBC : MVE_VADCSBC<"vsbc", 0b0, 0b1, (ins cl_FPSCR_NZCV:$carryin)>; +def MVE_VSBCI : MVE_VADCSBC<"vsbci", 0b1, 0b1, (ins)>; + +class MVE_VQDMULL<string iname, string suffix, bit size, bit T, + list<dag> pattern=[]> + : MVE_qDest_qSrc<iname, suffix, (outs MQPR:$Qd), + (ins MQPR:$Qn, MQPR:$Qm), "$Qd, $Qn, $Qm", + vpred_r, "", pattern> { + bits<4> Qn; + + let Inst{28} = size; + let Inst{21-20} = 0b11; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b0; + let Inst{12} = T; + let Inst{8} = 0b1; + let Inst{7} = Qn{3}; + let Inst{0} = 0b1; +} + +multiclass MVE_VQDMULL_halves<string suffix, bit size> { + def bh : MVE_VQDMULL<"vqdmullb", suffix, size, 0b0>; + def th : MVE_VQDMULL<"vqdmullt", suffix, size, 0b1>; +} + +defm MVE_VQDMULLs16 : MVE_VQDMULL_halves<"s16", 0b0>; +defm MVE_VQDMULLs32 : MVE_VQDMULL_halves<"s32", 0b1>; + +// end of mve_qDest_qSrc + +// start of mve_qDest_rSrc + +class MVE_qr_base<dag oops, dag iops, InstrItinClass itin, string iname, + string suffix, string ops, vpred_ops vpred, string cstr, + list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, iname, suffix, ops, vpred, cstr, pattern> { + bits<4> Qd; + bits<4> Qn; + bits<4> Rm; + + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{19-17} = Qn{2-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{11-9} = 0b111; + let Inst{7} = Qn{3}; + let Inst{6} = 0b1; + let Inst{4} = 0b0; + let Inst{3-0} = Rm{3-0}; +} + +class MVE_qDest_rSrc<string iname, string suffix, list<dag> pattern=[]> + : MVE_qr_base<(outs MQPR:$Qd), (ins MQPR:$Qn, rGPR:$Rm), + NoItinerary, iname, suffix, "$Qd, $Qn, $Rm", vpred_r, "", + pattern>; + +class MVE_qDestSrc_rSrc<string iname, string suffix, list<dag> pattern=[]> + : MVE_qr_base<(outs MQPR:$Qd), (ins MQPR:$Qd_src, MQPR:$Qn, rGPR:$Rm), + NoItinerary, iname, suffix, "$Qd, $Qn, $Rm", vpred_n, "$Qd = $Qd_src", + pattern>; + +class MVE_qDest_single_rSrc<string iname, string suffix, list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qd_src, rGPR:$Rm), NoItinerary, iname, + suffix, "$Qd, $Rm", vpred_n, "$Qd = $Qd_src", pattern> { + bits<4> Qd; + bits<4> Rm; + + let Inst{22} = Qd{3}; + let Inst{15-13} = Qd{2-0}; + let Inst{3-0} = Rm{3-0}; +} + +class MVE_VADDSUB_qr<string iname, string suffix, bits<2> size, + bit bit_5, bit bit_12, bit bit_16, + bit bit_28, list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = bit_28; + let Inst{21-20} = size; + let Inst{16} = bit_16; + let Inst{12} = bit_12; + let Inst{8} = 0b1; + let Inst{5} = bit_5; +} + +multiclass MVE_VADDSUB_qr_sizes<string iname, string suffix, + bit bit_5, bit bit_12, bit bit_16, + bit bit_28, list<dag> pattern=[]> { + def "8" : MVE_VADDSUB_qr<iname, suffix#"8", 0b00, + bit_5, bit_12, bit_16, bit_28>; + def "16" : MVE_VADDSUB_qr<iname, suffix#"16", 0b01, + bit_5, bit_12, bit_16, bit_28>; + def "32" : MVE_VADDSUB_qr<iname, suffix#"32", 0b10, + bit_5, bit_12, bit_16, bit_28>; +} + +defm MVE_VADD_qr_i : MVE_VADDSUB_qr_sizes<"vadd", "i", 0b0, 0b0, 0b1, 0b0>; +defm MVE_VQADD_qr_s : MVE_VADDSUB_qr_sizes<"vqadd", "s", 0b1, 0b0, 0b0, 0b0>; +defm MVE_VQADD_qr_u : MVE_VADDSUB_qr_sizes<"vqadd", "u", 0b1, 0b0, 0b0, 0b1>; + +defm MVE_VSUB_qr_i : MVE_VADDSUB_qr_sizes<"vsub", "i", 0b0, 0b1, 0b1, 0b0>; +defm MVE_VQSUB_qr_s : MVE_VADDSUB_qr_sizes<"vqsub", "s", 0b1, 0b1, 0b0, 0b0>; +defm MVE_VQSUB_qr_u : MVE_VADDSUB_qr_sizes<"vqsub", "u", 0b1, 0b1, 0b0, 0b1>; + +class MVE_VQDMULL_qr<string iname, string suffix, bit size, + bit T, list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = size; + let Inst{21-20} = 0b11; + let Inst{16} = 0b0; + let Inst{12} = T; + let Inst{8} = 0b1; + let Inst{5} = 0b1; +} + +multiclass MVE_VQDMULL_qr_halves<string suffix, bit size> { + def bh : MVE_VQDMULL_qr<"vqdmullb", suffix, size, 0b0>; + def th : MVE_VQDMULL_qr<"vqdmullt", suffix, size, 0b1>; +} + +defm MVE_VQDMULL_qr_s16 : MVE_VQDMULL_qr_halves<"s16", 0b0>; +defm MVE_VQDMULL_qr_s32 : MVE_VQDMULL_qr_halves<"s32", 0b1>; + +class MVE_VxADDSUB_qr<string iname, string suffix, + bit bit_28, bits<2> bits_21_20, bit subtract, + list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = bit_28; + let Inst{21-20} = bits_21_20; + let Inst{16} = 0b0; + let Inst{12} = subtract; + let Inst{8} = 0b1; + let Inst{5} = 0b0; +} + +def MVE_VHADD_qr_s8 : MVE_VxADDSUB_qr<"vhadd", "s8", 0b0, 0b00, 0b0>; +def MVE_VHADD_qr_s16 : MVE_VxADDSUB_qr<"vhadd", "s16", 0b0, 0b01, 0b0>; +def MVE_VHADD_qr_s32 : MVE_VxADDSUB_qr<"vhadd", "s32", 0b0, 0b10, 0b0>; +def MVE_VHADD_qr_u8 : MVE_VxADDSUB_qr<"vhadd", "u8", 0b1, 0b00, 0b0>; +def MVE_VHADD_qr_u16 : MVE_VxADDSUB_qr<"vhadd", "u16", 0b1, 0b01, 0b0>; +def MVE_VHADD_qr_u32 : MVE_VxADDSUB_qr<"vhadd", "u32", 0b1, 0b10, 0b0>; + +def MVE_VHSUB_qr_s8 : MVE_VxADDSUB_qr<"vhsub", "s8", 0b0, 0b00, 0b1>; +def MVE_VHSUB_qr_s16 : MVE_VxADDSUB_qr<"vhsub", "s16", 0b0, 0b01, 0b1>; +def MVE_VHSUB_qr_s32 : MVE_VxADDSUB_qr<"vhsub", "s32", 0b0, 0b10, 0b1>; +def MVE_VHSUB_qr_u8 : MVE_VxADDSUB_qr<"vhsub", "u8", 0b1, 0b00, 0b1>; +def MVE_VHSUB_qr_u16 : MVE_VxADDSUB_qr<"vhsub", "u16", 0b1, 0b01, 0b1>; +def MVE_VHSUB_qr_u32 : MVE_VxADDSUB_qr<"vhsub", "u32", 0b1, 0b10, 0b1>; + +let Predicates = [HasMVEFloat] in { + def MVE_VADD_qr_f32 : MVE_VxADDSUB_qr<"vadd", "f32", 0b0, 0b11, 0b0>; + def MVE_VADD_qr_f16 : MVE_VxADDSUB_qr<"vadd", "f16", 0b1, 0b11, 0b0>; + + def MVE_VSUB_qr_f32 : MVE_VxADDSUB_qr<"vsub", "f32", 0b0, 0b11, 0b1>; + def MVE_VSUB_qr_f16 : MVE_VxADDSUB_qr<"vsub", "f16", 0b1, 0b11, 0b1>; +} + +class MVE_VxSHL_qr<string iname, string suffix, bit U, bits<2> size, + bit bit_7, bit bit_17, list<dag> pattern=[]> + : MVE_qDest_single_rSrc<iname, suffix, pattern> { + + let Inst{28} = U; + let Inst{25-23} = 0b100; + let Inst{21-20} = 0b11; + let Inst{19-18} = size; + let Inst{17} = bit_17; + let Inst{16} = 0b1; + let Inst{12-8} = 0b11110; + let Inst{7} = bit_7; + let Inst{6-4} = 0b110; +} + +multiclass MVE_VxSHL_qr_types<string iname, bit bit_7, bit bit_17> { + def s8 : MVE_VxSHL_qr<iname, "s8", 0b0, 0b00, bit_7, bit_17>; + def s16 : MVE_VxSHL_qr<iname, "s16", 0b0, 0b01, bit_7, bit_17>; + def s32 : MVE_VxSHL_qr<iname, "s32", 0b0, 0b10, bit_7, bit_17>; + def u8 : MVE_VxSHL_qr<iname, "u8", 0b1, 0b00, bit_7, bit_17>; + def u16 : MVE_VxSHL_qr<iname, "u16", 0b1, 0b01, bit_7, bit_17>; + def u32 : MVE_VxSHL_qr<iname, "u32", 0b1, 0b10, bit_7, bit_17>; +} + +defm MVE_VSHL_qr : MVE_VxSHL_qr_types<"vshl", 0b0, 0b0>; +defm MVE_VRSHL_qr : MVE_VxSHL_qr_types<"vrshl", 0b0, 0b1>; +defm MVE_VQSHL_qr : MVE_VxSHL_qr_types<"vqshl", 0b1, 0b0>; +defm MVE_VQRSHL_qr : MVE_VxSHL_qr_types<"vqrshl", 0b1, 0b1>; + +let Predicates = [HasMVEInt] in { + def : Pat<(v4i32 (ARMvshlu (v4i32 MQPR:$Qm), (v4i32 (ARMvdup GPR:$Rm)))), + (v4i32 (MVE_VSHL_qru32 (v4i32 MQPR:$Qm), GPR:$Rm))>; + def : Pat<(v8i16 (ARMvshlu (v8i16 MQPR:$Qm), (v8i16 (ARMvdup GPR:$Rm)))), + (v8i16 (MVE_VSHL_qru16 (v8i16 MQPR:$Qm), GPR:$Rm))>; + def : Pat<(v16i8 (ARMvshlu (v16i8 MQPR:$Qm), (v16i8 (ARMvdup GPR:$Rm)))), + (v16i8 (MVE_VSHL_qru8 (v16i8 MQPR:$Qm), GPR:$Rm))>; + + def : Pat<(v4i32 (ARMvshls (v4i32 MQPR:$Qm), (v4i32 (ARMvdup GPR:$Rm)))), + (v4i32 (MVE_VSHL_qrs32 (v4i32 MQPR:$Qm), GPR:$Rm))>; + def : Pat<(v8i16 (ARMvshls (v8i16 MQPR:$Qm), (v8i16 (ARMvdup GPR:$Rm)))), + (v8i16 (MVE_VSHL_qrs16 (v8i16 MQPR:$Qm), GPR:$Rm))>; + def : Pat<(v16i8 (ARMvshls (v16i8 MQPR:$Qm), (v16i8 (ARMvdup GPR:$Rm)))), + (v16i8 (MVE_VSHL_qrs8 (v16i8 MQPR:$Qm), GPR:$Rm))>; +} + +class MVE_VBRSR<string iname, string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = 0b1; + let Inst{21-20} = size; + let Inst{16} = 0b1; + let Inst{12} = 0b1; + let Inst{8} = 0b0; + let Inst{5} = 0b1; +} + +def MVE_VBRSR8 : MVE_VBRSR<"vbrsr", "8", 0b00>; +def MVE_VBRSR16 : MVE_VBRSR<"vbrsr", "16", 0b01>; +def MVE_VBRSR32 : MVE_VBRSR<"vbrsr", "32", 0b10>; + +class MVE_VMUL_qr_int<string iname, string suffix, + bits<2> size, list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = 0b0; + let Inst{21-20} = size; + let Inst{16} = 0b1; + let Inst{12} = 0b1; + let Inst{8} = 0b0; + let Inst{5} = 0b1; +} + +def MVE_VMUL_qr_i8 : MVE_VMUL_qr_int<"vmul", "i8", 0b00>; +def MVE_VMUL_qr_i16 : MVE_VMUL_qr_int<"vmul", "i16", 0b01>; +def MVE_VMUL_qr_i32 : MVE_VMUL_qr_int<"vmul", "i32", 0b10>; + +class MVE_VxxMUL_qr<string iname, string suffix, + bit bit_28, bits<2> bits_21_20, list<dag> pattern=[]> + : MVE_qDest_rSrc<iname, suffix, pattern> { + + let Inst{28} = bit_28; + let Inst{21-20} = bits_21_20; + let Inst{16} = 0b1; + let Inst{12} = 0b0; + let Inst{8} = 0b0; + let Inst{5} = 0b1; +} + +def MVE_VQDMULH_qr_s8 : MVE_VxxMUL_qr<"vqdmulh", "s8", 0b0, 0b00>; +def MVE_VQDMULH_qr_s16 : MVE_VxxMUL_qr<"vqdmulh", "s16", 0b0, 0b01>; +def MVE_VQDMULH_qr_s32 : MVE_VxxMUL_qr<"vqdmulh", "s32", 0b0, 0b10>; + +def MVE_VQRDMULH_qr_s8 : MVE_VxxMUL_qr<"vqrdmulh", "s8", 0b1, 0b00>; +def MVE_VQRDMULH_qr_s16 : MVE_VxxMUL_qr<"vqrdmulh", "s16", 0b1, 0b01>; +def MVE_VQRDMULH_qr_s32 : MVE_VxxMUL_qr<"vqrdmulh", "s32", 0b1, 0b10>; + +let Predicates = [HasMVEFloat] in { + def MVE_VMUL_qr_f16 : MVE_VxxMUL_qr<"vmul", "f16", 0b1, 0b11>; + def MVE_VMUL_qr_f32 : MVE_VxxMUL_qr<"vmul", "f32", 0b0, 0b11>; +} + +class MVE_VFMAMLA_qr<string iname, string suffix, + bit bit_28, bits<2> bits_21_20, bit S, + list<dag> pattern=[]> + : MVE_qDestSrc_rSrc<iname, suffix, pattern> { + + let Inst{28} = bit_28; + let Inst{21-20} = bits_21_20; + let Inst{16} = 0b1; + let Inst{12} = S; + let Inst{8} = 0b0; + let Inst{5} = 0b0; +} + +def MVE_VMLA_qr_s8 : MVE_VFMAMLA_qr<"vmla", "s8", 0b0, 0b00, 0b0>; +def MVE_VMLA_qr_s16 : MVE_VFMAMLA_qr<"vmla", "s16", 0b0, 0b01, 0b0>; +def MVE_VMLA_qr_s32 : MVE_VFMAMLA_qr<"vmla", "s32", 0b0, 0b10, 0b0>; +def MVE_VMLA_qr_u8 : MVE_VFMAMLA_qr<"vmla", "u8", 0b1, 0b00, 0b0>; +def MVE_VMLA_qr_u16 : MVE_VFMAMLA_qr<"vmla", "u16", 0b1, 0b01, 0b0>; +def MVE_VMLA_qr_u32 : MVE_VFMAMLA_qr<"vmla", "u32", 0b1, 0b10, 0b0>; + +def MVE_VMLAS_qr_s8 : MVE_VFMAMLA_qr<"vmlas", "s8", 0b0, 0b00, 0b1>; +def MVE_VMLAS_qr_s16 : MVE_VFMAMLA_qr<"vmlas", "s16", 0b0, 0b01, 0b1>; +def MVE_VMLAS_qr_s32 : MVE_VFMAMLA_qr<"vmlas", "s32", 0b0, 0b10, 0b1>; +def MVE_VMLAS_qr_u8 : MVE_VFMAMLA_qr<"vmlas", "u8", 0b1, 0b00, 0b1>; +def MVE_VMLAS_qr_u16 : MVE_VFMAMLA_qr<"vmlas", "u16", 0b1, 0b01, 0b1>; +def MVE_VMLAS_qr_u32 : MVE_VFMAMLA_qr<"vmlas", "u32", 0b1, 0b10, 0b1>; + +let Predicates = [HasMVEFloat] in { + def MVE_VFMA_qr_f16 : MVE_VFMAMLA_qr<"vfma", "f16", 0b1, 0b11, 0b0>; + def MVE_VFMA_qr_f32 : MVE_VFMAMLA_qr<"vfma", "f32", 0b0, 0b11, 0b0>; + def MVE_VFMA_qr_Sf16 : MVE_VFMAMLA_qr<"vfmas", "f16", 0b1, 0b11, 0b1>; + def MVE_VFMA_qr_Sf32 : MVE_VFMAMLA_qr<"vfmas", "f32", 0b0, 0b11, 0b1>; +} + +class MVE_VQDMLAH_qr<string iname, string suffix, bit U, bits<2> size, + bit bit_5, bit bit_12, list<dag> pattern=[]> + : MVE_qDestSrc_rSrc<iname, suffix, pattern> { + + let Inst{28} = U; + let Inst{21-20} = size; + let Inst{16} = 0b0; + let Inst{12} = bit_12; + let Inst{8} = 0b0; + let Inst{5} = bit_5; +} + +multiclass MVE_VQDMLAH_qr_types<string iname, bit bit_5, bit bit_12> { + def s8 : MVE_VQDMLAH_qr<iname, "s8", 0b0, 0b00, bit_5, bit_12>; + def s16 : MVE_VQDMLAH_qr<iname, "s16", 0b0, 0b01, bit_5, bit_12>; + def s32 : MVE_VQDMLAH_qr<iname, "s32", 0b0, 0b10, bit_5, bit_12>; +} + +defm MVE_VQDMLAH_qr : MVE_VQDMLAH_qr_types<"vqdmlah", 0b1, 0b0>; +defm MVE_VQRDMLAH_qr : MVE_VQDMLAH_qr_types<"vqrdmlah", 0b0, 0b0>; +defm MVE_VQDMLASH_qr : MVE_VQDMLAH_qr_types<"vqdmlash", 0b1, 0b1>; +defm MVE_VQRDMLASH_qr : MVE_VQDMLAH_qr_types<"vqrdmlash", 0b0, 0b1>; + +class MVE_VxDUP<string iname, string suffix, bits<2> size, bit bit_12, + list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd, tGPREven:$Rn), + (ins tGPREven:$Rn_src, MVE_VIDUP_imm:$imm), NoItinerary, + iname, suffix, "$Qd, $Rn, $imm", vpred_r, "$Rn = $Rn_src", + pattern> { + bits<4> Qd; + bits<4> Rn; + bits<2> imm; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{21-20} = size; + let Inst{19-17} = Rn{3-1}; + let Inst{16} = 0b1; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = bit_12; + let Inst{11-8} = 0b1111; + let Inst{7} = imm{1}; + let Inst{6-1} = 0b110111; + let Inst{0} = imm{0}; +} + +def MVE_VIDUPu8 : MVE_VxDUP<"vidup", "u8", 0b00, 0b0>; +def MVE_VIDUPu16 : MVE_VxDUP<"vidup", "u16", 0b01, 0b0>; +def MVE_VIDUPu32 : MVE_VxDUP<"vidup", "u32", 0b10, 0b0>; + +def MVE_VDDUPu8 : MVE_VxDUP<"vddup", "u8", 0b00, 0b1>; +def MVE_VDDUPu16 : MVE_VxDUP<"vddup", "u16", 0b01, 0b1>; +def MVE_VDDUPu32 : MVE_VxDUP<"vddup", "u32", 0b10, 0b1>; + +class MVE_VxWDUP<string iname, string suffix, bits<2> size, bit bit_12, + list<dag> pattern=[]> + : MVE_p<(outs MQPR:$Qd, tGPREven:$Rn), + (ins tGPREven:$Rn_src, tGPROdd:$Rm, MVE_VIDUP_imm:$imm), NoItinerary, + iname, suffix, "$Qd, $Rn, $Rm, $imm", vpred_r, "$Rn = $Rn_src", + pattern> { + bits<4> Qd; + bits<4> Rm; + bits<4> Rn; + bits<2> imm; + + let Inst{28} = 0b0; + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{21-20} = size; + let Inst{19-17} = Rn{3-1}; + let Inst{16} = 0b1; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = bit_12; + let Inst{11-8} = 0b1111; + let Inst{7} = imm{1}; + let Inst{6-4} = 0b110; + let Inst{3-1} = Rm{3-1}; + let Inst{0} = imm{0}; +} + +def MVE_VIWDUPu8 : MVE_VxWDUP<"viwdup", "u8", 0b00, 0b0>; +def MVE_VIWDUPu16 : MVE_VxWDUP<"viwdup", "u16", 0b01, 0b0>; +def MVE_VIWDUPu32 : MVE_VxWDUP<"viwdup", "u32", 0b10, 0b0>; + +def MVE_VDWDUPu8 : MVE_VxWDUP<"vdwdup", "u8", 0b00, 0b1>; +def MVE_VDWDUPu16 : MVE_VxWDUP<"vdwdup", "u16", 0b01, 0b1>; +def MVE_VDWDUPu32 : MVE_VxWDUP<"vdwdup", "u32", 0b10, 0b1>; + +class MVE_VCTP<string suffix, bits<2> size, list<dag> pattern=[]> + : MVE_p<(outs VCCR:$P0), (ins rGPR:$Rn), NoItinerary, "vctp", suffix, + "$Rn", vpred_n, "", pattern> { + bits<4> Rn; + + let Inst{28-27} = 0b10; + let Inst{26-22} = 0b00000; + let Inst{21-20} = size; + let Inst{19-16} = Rn{3-0}; + let Inst{15-11} = 0b11101; + let Inst{10-0} = 0b00000000001; + let Unpredictable{10-0} = 0b11111111111; + + let Constraints = ""; + let DecoderMethod = "DecodeMveVCTP"; +} + +def MVE_VCTP8 : MVE_VCTP<"8", 0b00>; +def MVE_VCTP16 : MVE_VCTP<"16", 0b01>; +def MVE_VCTP32 : MVE_VCTP<"32", 0b10>; +def MVE_VCTP64 : MVE_VCTP<"64", 0b11>; + +// end of mve_qDest_rSrc + +// start of coproc mov + +class MVE_VMOV_64bit<dag oops, dag iops, bit to_qreg, string ops, string cstr> + : MVE_VMOV_lane_base<oops, !con(iops, (ins MVEPairVectorIndex2:$idx, + MVEPairVectorIndex0:$idx2)), + NoItinerary, "vmov", "", ops, cstr, []> { + bits<5> Rt; + bits<5> Rt2; + bits<4> Qd; + bit idx; + bit idx2; + + let Inst{31-23} = 0b111011000; + let Inst{22} = Qd{3}; + let Inst{21} = 0b0; + let Inst{20} = to_qreg; + let Inst{19-16} = Rt2{3-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-5} = 0b01111000; + let Inst{4} = idx2; + let Inst{3-0} = Rt{3-0}; +} + +// The assembly syntax for these instructions mentions the vector +// register name twice, e.g. +// +// vmov q2[2], q2[0], r0, r1 +// vmov r0, r1, q2[2], q2[0] +// +// which needs a bit of juggling with MC operand handling. +// +// For the move _into_ a vector register, the MC operand list also has +// to mention the register name twice: once as the output, and once as +// an extra input to represent where the unchanged half of the output +// register comes from (when this instruction is used in code +// generation). So we arrange that the first mention of the vector reg +// in the instruction is considered by the AsmMatcher to be the output +// ($Qd), and the second one is the input ($QdSrc). Binding them +// together with the existing 'tie' constraint is enough to enforce at +// register allocation time that they have to be the same register. +// +// For the move _from_ a vector register, there's no way to get round +// the fact that both instances of that register name have to be +// inputs. They have to be the same register again, but this time, we +// can't use a tie constraint, because that has to be between an +// output and an input operand. So this time, we have to arrange that +// the q-reg appears just once in the MC operand list, in spite of +// being mentioned twice in the asm syntax - which needs a custom +// AsmMatchConverter. + +def MVE_VMOV_q_rr : MVE_VMOV_64bit<(outs MQPR:$Qd), + (ins MQPR:$QdSrc, rGPR:$Rt, rGPR:$Rt2), + 0b1, "$Qd$idx, $QdSrc$idx2, $Rt, $Rt2", + "$Qd = $QdSrc"> { + let DecoderMethod = "DecodeMVEVMOVDRegtoQ"; +} + +def MVE_VMOV_rr_q : MVE_VMOV_64bit<(outs rGPR:$Rt, rGPR:$Rt2), (ins MQPR:$Qd), + 0b0, "$Rt, $Rt2, $Qd$idx, $Qd$idx2", ""> { + let DecoderMethod = "DecodeMVEVMOVQtoDReg"; + let AsmMatchConverter = "cvtMVEVMOVQtoDReg"; +} + +// end of coproc mov + +// start of MVE interleaving load/store + +// Base class for the family of interleaving/deinterleaving +// load/stores with names like VLD20.8 and VST43.32. +class MVE_vldst24_base<bit writeback, bit fourregs, bits<2> stage, bits<2> size, + bit load, dag Oops, dag loadIops, dag wbIops, + string iname, string ops, + string cstr, list<dag> pattern=[]> + : MVE_MI<Oops, !con(loadIops, wbIops), NoItinerary, iname, ops, cstr, pattern> { + bits<4> VQd; + bits<4> Rn; + + let Inst{31-22} = 0b1111110010; + let Inst{21} = writeback; + let Inst{20} = load; + let Inst{19-16} = Rn; + let Inst{15-13} = VQd{2-0}; + let Inst{12-9} = 0b1111; + let Inst{8-7} = size; + let Inst{6-5} = stage; + let Inst{4-1} = 0b0000; + let Inst{0} = fourregs; + + let mayLoad = load; + let mayStore = !eq(load,0); +} + +// A parameter class used to encapsulate all the ways the writeback +// variants of VLD20 and friends differ from the non-writeback ones. +class MVE_vldst24_writeback<bit b, dag Oo, dag Io, + string sy="", string c="", string n=""> { + bit writeback = b; + dag Oops = Oo; + dag Iops = Io; + string syntax = sy; + string cstr = c; + string id_suffix = n; +} + +// Another parameter class that encapsulates the differences between VLD2x +// and VLD4x. +class MVE_vldst24_nvecs<int n, list<int> s, bit b, RegisterOperand vl> { + int nvecs = n; + list<int> stages = s; + bit bit0 = b; + RegisterOperand VecList = vl; +} + +// A third parameter class that distinguishes VLDnn.8 from .16 from .32. +class MVE_vldst24_lanesize<int i, bits<2> b> { + int lanesize = i; + bits<2> sizebits = b; +} + +// A base class for each direction of transfer: one for load, one for +// store. I can't make these a fourth independent parametric tuple +// class, because they have to take the nvecs tuple class as a +// parameter, in order to find the right VecList operand type. + +class MVE_vld24_base<MVE_vldst24_nvecs n, bits<2> pat, bits<2> size, + MVE_vldst24_writeback wb, string iname, + list<dag> pattern=[]> + : MVE_vldst24_base<wb.writeback, n.bit0, pat, size, 1, + !con((outs n.VecList:$VQd), wb.Oops), + (ins n.VecList:$VQdSrc), wb.Iops, + iname, "$VQd, $Rn" # wb.syntax, + wb.cstr # ",$VQdSrc = $VQd", pattern>; + +class MVE_vst24_base<MVE_vldst24_nvecs n, bits<2> pat, bits<2> size, + MVE_vldst24_writeback wb, string iname, + list<dag> pattern=[]> + : MVE_vldst24_base<wb.writeback, n.bit0, pat, size, 0, + wb.Oops, (ins n.VecList:$VQd), wb.Iops, + iname, "$VQd, $Rn" # wb.syntax, + wb.cstr, pattern>; + +// Actually define all the interleaving loads and stores, by a series +// of nested foreaches over number of vectors (VLD2/VLD4); stage +// within one of those series (VLDx0/VLDx1/VLDx2/VLDx3); size of +// vector lane; writeback or no writeback. +foreach n = [MVE_vldst24_nvecs<2, [0,1], 0, VecList2Q>, + MVE_vldst24_nvecs<4, [0,1,2,3], 1, VecList4Q>] in +foreach stage = n.stages in +foreach s = [MVE_vldst24_lanesize< 8, 0b00>, + MVE_vldst24_lanesize<16, 0b01>, + MVE_vldst24_lanesize<32, 0b10>] in +foreach wb = [MVE_vldst24_writeback< + 1, (outs rGPR:$wb), (ins t2_nosp_addr_offset_none:$Rn), + "!", "$Rn.base = $wb", "_wb">, + MVE_vldst24_writeback<0, (outs), (ins t2_addr_offset_none:$Rn)>] in { + + // For each case within all of those foreaches, define the actual + // instructions. The def names are made by gluing together pieces + // from all the parameter classes, and will end up being things like + // MVE_VLD20_8 and MVE_VST43_16_wb. + + def "MVE_VLD" # n.nvecs # stage # "_" # s.lanesize # wb.id_suffix + : MVE_vld24_base<n, stage, s.sizebits, wb, + "vld" # n.nvecs # stage # "." # s.lanesize>; + + def "MVE_VST" # n.nvecs # stage # "_" # s.lanesize # wb.id_suffix + : MVE_vst24_base<n, stage, s.sizebits, wb, + "vst" # n.nvecs # stage # "." # s.lanesize>; +} + +// end of MVE interleaving load/store + +// start of MVE predicable load/store + +// A parameter class for the direction of transfer. +class MVE_ldst_direction<bit b, dag Oo, dag Io, string c=""> { + bit load = b; + dag Oops = Oo; + dag Iops = Io; + string cstr = c; +} +def MVE_ld: MVE_ldst_direction<1, (outs MQPR:$Qd), (ins), ",@earlyclobber $Qd">; +def MVE_st: MVE_ldst_direction<0, (outs), (ins MQPR:$Qd)>; + +// A parameter class for the size of memory access in a load. +class MVE_memsz<bits<2> e, int s, AddrMode m, string mn, list<string> types> { + bits<2> encoding = e; // opcode bit(s) for encoding + int shift = s; // shift applied to immediate load offset + AddrMode AM = m; + + // For instruction aliases: define the complete list of type + // suffixes at this size, and the canonical ones for loads and + // stores. + string MnemonicLetter = mn; + int TypeBits = !shl(8, s); + string CanonLoadSuffix = ".u" # TypeBits; + string CanonStoreSuffix = "." # TypeBits; + list<string> suffixes = !foreach(letter, types, "." # letter # TypeBits); +} + +// Instances of MVE_memsz. +// +// (memD doesn't need an AddrMode, because those are only for +// contiguous loads, and memD is only used by gather/scatters.) +def MVE_memB: MVE_memsz<0b00, 0, AddrModeT2_i7, "b", ["", "u", "s"]>; +def MVE_memH: MVE_memsz<0b01, 1, AddrModeT2_i7s2, "h", ["", "u", "s", "f"]>; +def MVE_memW: MVE_memsz<0b10, 2, AddrModeT2_i7s4, "w", ["", "u", "s", "f"]>; +def MVE_memD: MVE_memsz<0b11, 3, ?, "d", ["", "u", "s", "f"]>; + +// This is the base class for all the MVE loads and stores other than +// the interleaving ones. All the non-interleaving loads/stores share +// the characteristic that they operate on just one vector register, +// so they are VPT-predicable. +// +// The predication operand is vpred_n, for both loads and stores. For +// store instructions, the reason is obvious: if there is no output +// register, there can't be a need for an input parameter giving the +// output register's previous value. Load instructions also don't need +// that input parameter, because unlike MVE data processing +// instructions, predicated loads are defined to set the inactive +// lanes of the output register to zero, instead of preserving their +// input values. +class MVE_VLDRSTR_base<MVE_ldst_direction dir, bit U, bit P, bit W, bit opc, + dag oops, dag iops, string asm, string suffix, + string ops, string cstr, list<dag> pattern=[]> + : MVE_p<oops, iops, NoItinerary, asm, suffix, ops, vpred_n, cstr, pattern> { + bits<3> Qd; + + let Inst{28} = U; + let Inst{25} = 0b0; + let Inst{24} = P; + let Inst{22} = 0b0; + let Inst{21} = W; + let Inst{20} = dir.load; + let Inst{15-13} = Qd{2-0}; + let Inst{12} = opc; + let Inst{11-9} = 0b111; + + let mayLoad = dir.load; + let mayStore = !eq(dir.load,0); +} + +// Contiguous load and store instructions. These come in two main +// categories: same-size loads/stores in which 128 bits of vector +// register is transferred to or from 128 bits of memory in the most +// obvious way, and widening loads / narrowing stores, in which the +// size of memory accessed is less than the size of a vector register, +// so the load instructions sign- or zero-extend each memory value +// into a wider vector lane, and the store instructions truncate +// correspondingly. +// +// The instruction mnemonics for these two classes look reasonably +// similar, but the actual encodings are different enough to need two +// separate base classes. + +// Contiguous, same size +class MVE_VLDRSTR_cs<MVE_ldst_direction dir, MVE_memsz memsz, bit P, bit W, + dag oops, dag iops, string asm, string suffix, + IndexMode im, string ops, string cstr> + : MVE_VLDRSTR_base<dir, 0, P, W, 1, oops, iops, asm, suffix, ops, cstr> { + bits<12> addr; + let Inst{23} = addr{7}; + let Inst{19-16} = addr{11-8}; + let Inst{8-7} = memsz.encoding; + let Inst{6-0} = addr{6-0}; +} + +// Contiguous, widening/narrowing +class MVE_VLDRSTR_cw<MVE_ldst_direction dir, MVE_memsz memsz, bit U, + bit P, bit W, bits<2> size, dag oops, dag iops, + string asm, string suffix, IndexMode im, + string ops, string cstr> + : MVE_VLDRSTR_base<dir, U, P, W, 0, oops, iops, asm, suffix, ops, cstr> { + bits<11> addr; + let Inst{23} = addr{7}; + let Inst{19} = memsz.encoding{0}; // enough to tell 16- from 32-bit + let Inst{18-16} = addr{10-8}; + let Inst{8-7} = size; + let Inst{6-0} = addr{6-0}; + + let IM = im; +} + +// Multiclass wrapper on each of the _cw and _cs base classes, to +// generate three writeback modes (none, preindex, postindex). + +multiclass MVE_VLDRSTR_cw_m<MVE_ldst_direction dir, MVE_memsz memsz, + string asm, string suffix, bit U, bits<2> size> { + let AM = memsz.AM in { + def "" : MVE_VLDRSTR_cw< + dir, memsz, U, 1, 0, size, + dir.Oops, !con(dir.Iops, (ins taddrmode_imm7<memsz.shift>:$addr)), + asm, suffix, IndexModeNone, "$Qd, $addr", "">; + + def _pre : MVE_VLDRSTR_cw< + dir, memsz, U, 1, 1, size, + !con((outs tGPR:$wb), dir.Oops), + !con(dir.Iops, (ins taddrmode_imm7<memsz.shift>:$addr)), + asm, suffix, IndexModePre, "$Qd, $addr!", "$addr.base = $wb"> { + let DecoderMethod = "DecodeMVE_MEM_1_pre<"#memsz.shift#">"; + } + + def _post : MVE_VLDRSTR_cw< + dir, memsz, U, 0, 1, size, + !con((outs tGPR:$wb), dir.Oops), + !con(dir.Iops, (ins t_addr_offset_none:$Rn, + t2am_imm7_offset<memsz.shift>:$addr)), + asm, suffix, IndexModePost, "$Qd, $Rn$addr", "$Rn.base = $wb"> { + bits<4> Rn; + let Inst{18-16} = Rn{2-0}; + } + } +} + +multiclass MVE_VLDRSTR_cs_m<MVE_ldst_direction dir, MVE_memsz memsz, + string asm, string suffix> { + let AM = memsz.AM in { + def "" : MVE_VLDRSTR_cs< + dir, memsz, 1, 0, + dir.Oops, !con(dir.Iops, (ins t2addrmode_imm7<memsz.shift>:$addr)), + asm, suffix, IndexModeNone, "$Qd, $addr", "">; + + def _pre : MVE_VLDRSTR_cs< + dir, memsz, 1, 1, + !con((outs rGPR:$wb), dir.Oops), + !con(dir.Iops, (ins t2addrmode_imm7_pre<memsz.shift>:$addr)), + asm, suffix, IndexModePre, "$Qd, $addr!", "$addr.base = $wb"> { + let DecoderMethod = "DecodeMVE_MEM_2_pre<"#memsz.shift#">"; + } + + def _post : MVE_VLDRSTR_cs< + dir, memsz, 0, 1, + !con((outs rGPR:$wb), dir.Oops), + // We need an !if here to select the base register class, + // because it's legal to write back to SP in a load of this + // type, but not in a store. + !con(dir.Iops, (ins !if(dir.load, t2_addr_offset_none, + t2_nosp_addr_offset_none):$Rn, + t2am_imm7_offset<memsz.shift>:$addr)), + asm, suffix, IndexModePost, "$Qd, $Rn$addr", "$Rn.base = $wb"> { + bits<4> Rn; + let Inst{19-16} = Rn{3-0}; + } + } +} + +// Now actually declare all the contiguous load/stores, via those +// multiclasses. The instruction ids coming out of this are the bare +// names shown in the defm, with _pre or _post appended for writeback, +// e.g. MVE_VLDRBS16, MVE_VSTRB16_pre, MVE_VSTRHU16_post. + +defm MVE_VLDRBS16: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memB, "vldrb", "s16", 0, 0b01>; +defm MVE_VLDRBS32: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memB, "vldrb", "s32", 0, 0b10>; +defm MVE_VLDRBU16: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memB, "vldrb", "u16", 1, 0b01>; +defm MVE_VLDRBU32: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memB, "vldrb", "u32", 1, 0b10>; +defm MVE_VLDRHS32: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memH, "vldrh", "s32", 0, 0b10>; +defm MVE_VLDRHU32: MVE_VLDRSTR_cw_m<MVE_ld, MVE_memH, "vldrh", "u32", 1, 0b10>; + +defm MVE_VLDRBU8: MVE_VLDRSTR_cs_m<MVE_ld, MVE_memB, "vldrb", "u8">; +defm MVE_VLDRHU16: MVE_VLDRSTR_cs_m<MVE_ld, MVE_memH, "vldrh", "u16">; +defm MVE_VLDRWU32: MVE_VLDRSTR_cs_m<MVE_ld, MVE_memW, "vldrw", "u32">; + +defm MVE_VSTRB16: MVE_VLDRSTR_cw_m<MVE_st, MVE_memB, "vstrb", "16", 0, 0b01>; +defm MVE_VSTRB32: MVE_VLDRSTR_cw_m<MVE_st, MVE_memB, "vstrb", "32", 0, 0b10>; +defm MVE_VSTRH32: MVE_VLDRSTR_cw_m<MVE_st, MVE_memH, "vstrh", "32", 0, 0b10>; + +defm MVE_VSTRBU8 : MVE_VLDRSTR_cs_m<MVE_st, MVE_memB, "vstrb", "8">; +defm MVE_VSTRHU16: MVE_VLDRSTR_cs_m<MVE_st, MVE_memH, "vstrh", "16">; +defm MVE_VSTRWU32: MVE_VLDRSTR_cs_m<MVE_st, MVE_memW, "vstrw", "32">; + +// Gather loads / scatter stores whose address operand is of the form +// [Rn,Qm], i.e. a single GPR as the common base address, plus a +// vector of offset from it. ('Load/store this sequence of elements of +// the same array.') +// +// Like the contiguous family, these loads and stores can widen the +// loaded values / truncate the stored ones, or they can just +// load/store the same size of memory and vector lane. But unlike the +// contiguous family, there's no particular difference in encoding +// between those two cases. +// +// This family also comes with the option to scale the offset values +// in Qm by the size of the loaded memory (i.e. to treat them as array +// indices), or not to scale them (to treat them as plain byte offsets +// in memory, so that perhaps the loaded values are unaligned). The +// scaled instructions' address operand in assembly looks like +// [Rn,Qm,UXTW #2] or similar. + +// Base class. +class MVE_VLDRSTR_rq<MVE_ldst_direction dir, MVE_memsz memsz, bit U, + bits<2> size, bit os, string asm, string suffix, int shift> + : MVE_VLDRSTR_base<dir, U, 0b0, 0b0, 0, dir.Oops, + !con(dir.Iops, (ins mve_addr_rq_shift<shift>:$addr)), + asm, suffix, "$Qd, $addr", dir.cstr> { + bits<7> addr; + let Inst{23} = 0b1; + let Inst{19-16} = addr{6-3}; + let Inst{8-7} = size; + let Inst{6} = memsz.encoding{1}; + let Inst{5} = 0; + let Inst{4} = memsz.encoding{0}; + let Inst{3-1} = addr{2-0}; + let Inst{0} = os; +} + +// Multiclass that defines the scaled and unscaled versions of an +// instruction, when the memory size is wider than a byte. The scaled +// version gets the default name like MVE_VLDRBU16_rq; the unscaled / +// potentially unaligned version gets a "_u" suffix, e.g. +// MVE_VLDRBU16_rq_u. +multiclass MVE_VLDRSTR_rq_w<MVE_ldst_direction dir, MVE_memsz memsz, + string asm, string suffix, bit U, bits<2> size> { + def _u : MVE_VLDRSTR_rq<dir, memsz, U, size, 0, asm, suffix, 0>; + def "" : MVE_VLDRSTR_rq<dir, memsz, U, size, 1, asm, suffix, memsz.shift>; +} + +// Subclass of MVE_VLDRSTR_rq with the same API as that multiclass, +// for use when the memory size is one byte, so there's no 'scaled' +// version of the instruction at all. (This is encoded as if it were +// unscaled, but named in the default way with no _u suffix.) +class MVE_VLDRSTR_rq_b<MVE_ldst_direction dir, MVE_memsz memsz, + string asm, string suffix, bit U, bits<2> size> + : MVE_VLDRSTR_rq<dir, memsz, U, size, 0, asm, suffix, 0>; + +// Actually define all the loads and stores in this family. + +def MVE_VLDRBU8_rq : MVE_VLDRSTR_rq_b<MVE_ld, MVE_memB, "vldrb","u8", 1,0b00>; +def MVE_VLDRBU16_rq: MVE_VLDRSTR_rq_b<MVE_ld, MVE_memB, "vldrb","u16", 1,0b01>; +def MVE_VLDRBS16_rq: MVE_VLDRSTR_rq_b<MVE_ld, MVE_memB, "vldrb","s16", 0,0b01>; +def MVE_VLDRBU32_rq: MVE_VLDRSTR_rq_b<MVE_ld, MVE_memB, "vldrb","u32", 1,0b10>; +def MVE_VLDRBS32_rq: MVE_VLDRSTR_rq_b<MVE_ld, MVE_memB, "vldrb","s32", 0,0b10>; + +defm MVE_VLDRHU16_rq: MVE_VLDRSTR_rq_w<MVE_ld, MVE_memH, "vldrh","u16", 1,0b01>; +defm MVE_VLDRHU32_rq: MVE_VLDRSTR_rq_w<MVE_ld, MVE_memH, "vldrh","u32", 1,0b10>; +defm MVE_VLDRHS32_rq: MVE_VLDRSTR_rq_w<MVE_ld, MVE_memH, "vldrh","s32", 0,0b10>; +defm MVE_VLDRWU32_rq: MVE_VLDRSTR_rq_w<MVE_ld, MVE_memW, "vldrw","u32", 1,0b10>; +defm MVE_VLDRDU64_rq: MVE_VLDRSTR_rq_w<MVE_ld, MVE_memD, "vldrd","u64", 1,0b11>; + +def MVE_VSTRB8_rq : MVE_VLDRSTR_rq_b<MVE_st, MVE_memB, "vstrb","8", 0,0b00>; +def MVE_VSTRB16_rq : MVE_VLDRSTR_rq_b<MVE_st, MVE_memB, "vstrb","16", 0,0b01>; +def MVE_VSTRB32_rq : MVE_VLDRSTR_rq_b<MVE_st, MVE_memB, "vstrb","32", 0,0b10>; + +defm MVE_VSTRH16_rq : MVE_VLDRSTR_rq_w<MVE_st, MVE_memH, "vstrh","16", 0,0b01>; +defm MVE_VSTRH32_rq : MVE_VLDRSTR_rq_w<MVE_st, MVE_memH, "vstrh","32", 0,0b10>; +defm MVE_VSTRW32_rq : MVE_VLDRSTR_rq_w<MVE_st, MVE_memW, "vstrw","32", 0,0b10>; +defm MVE_VSTRD64_rq : MVE_VLDRSTR_rq_w<MVE_st, MVE_memD, "vstrd","64", 0,0b11>; + +// Gather loads / scatter stores whose address operand is of the form +// [Qm,#imm], i.e. a vector containing a full base address for each +// loaded item, plus an immediate offset applied consistently to all +// of them. ('Load/store the same field from this vector of pointers +// to a structure type.') +// +// This family requires the vector lane size to be at least 32 bits +// (so there's room for an address in each lane at all). It has no +// widening/narrowing variants. But it does support preindex +// writeback, in which the address vector is updated to hold the +// addresses actually loaded from. + +// Base class. +class MVE_VLDRSTR_qi<MVE_ldst_direction dir, MVE_memsz memsz, bit W, dag wbops, + string asm, string wbAsm, string suffix, string cstr = ""> + : MVE_VLDRSTR_base<dir, 1, 1, W, 1, !con(wbops, dir.Oops), + !con(dir.Iops, (ins mve_addr_q_shift<memsz.shift>:$addr)), + asm, suffix, "$Qd, $addr" # wbAsm, cstr # dir.cstr> { + bits<11> addr; + let Inst{23} = addr{7}; + let Inst{19-17} = addr{10-8}; + let Inst{16} = 0; + let Inst{8} = memsz.encoding{0}; // enough to distinguish 32- from 64-bit + let Inst{7} = 0; + let Inst{6-0} = addr{6-0}; +} + +// Multiclass that generates the non-writeback and writeback variants. +multiclass MVE_VLDRSTR_qi_m<MVE_ldst_direction dir, MVE_memsz memsz, + string asm, string suffix> { + def "" : MVE_VLDRSTR_qi<dir, memsz, 0, (outs), asm, "", suffix>; + def _pre : MVE_VLDRSTR_qi<dir, memsz, 1, (outs MQPR:$wb), asm, "!", suffix, + "$addr.base = $wb"> { + let DecoderMethod="DecodeMVE_MEM_3_pre<"#memsz.shift#">"; + } +} + +// Actual instruction definitions. +defm MVE_VLDRWU32_qi: MVE_VLDRSTR_qi_m<MVE_ld, MVE_memW, "vldrw", "u32">; +defm MVE_VLDRDU64_qi: MVE_VLDRSTR_qi_m<MVE_ld, MVE_memD, "vldrd", "u64">; +defm MVE_VSTRW32_qi: MVE_VLDRSTR_qi_m<MVE_st, MVE_memW, "vstrw", "32">; +defm MVE_VSTRD64_qi: MVE_VLDRSTR_qi_m<MVE_st, MVE_memD, "vstrd", "64">; + +// Define aliases for all the instructions where memory size and +// vector lane size are the same. These are mnemonic aliases, so they +// apply consistently across all of the above families - contiguous +// loads, and both the rq and qi types of gather/scatter. +// +// Rationale: As long as you're loading (for example) 16-bit memory +// values into 16-bit vector lanes, you can think of them as signed or +// unsigned integers, fp16 or just raw 16-bit blobs and it makes no +// difference. So we permit all of vldrh.16, vldrh.u16, vldrh.s16, +// vldrh.f16 and treat them all as equivalent to the canonical +// spelling (which happens to be .u16 for loads, and just .16 for +// stores). + +foreach vpt_cond = ["", "t", "e"] in +foreach memsz = [MVE_memB, MVE_memH, MVE_memW, MVE_memD] in +foreach suffix = memsz.suffixes in { + + // These foreaches are conceptually ifs, implemented by iterating a + // dummy variable over a list with 0 or 1 elements depending on the + // condition. The idea is to iterate over _nearly_ all the suffixes + // in memsz.suffixes, but omit the one we want all the others to alias. + + foreach _ = !if(!ne(suffix, memsz.CanonLoadSuffix), [1], []<int>) in + def : MnemonicAlias< + "vldr" # memsz.MnemonicLetter # vpt_cond # suffix, + "vldr" # memsz.MnemonicLetter # vpt_cond # memsz.CanonLoadSuffix>; + + foreach _ = !if(!ne(suffix, memsz.CanonStoreSuffix), [1], []<int>) in + def : MnemonicAlias< + "vstr" # memsz.MnemonicLetter # vpt_cond # suffix, + "vstr" # memsz.MnemonicLetter # vpt_cond # memsz.CanonStoreSuffix>; +} + +// end of MVE predicable load/store + +class MVE_VPT<string suffix, bits<2> size, dag iops, string asm, list<dag> pattern=[]> + : MVE_MI<(outs ), iops, NoItinerary, !strconcat("vpt", "${Mk}", ".", suffix), asm, "", pattern> { + bits<3> fc; + bits<4> Mk; + bits<3> Qn; + + let Inst{31-23} = 0b111111100; + let Inst{22} = Mk{3}; + let Inst{21-20} = size; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b1; + let Inst{15-13} = Mk{2-0}; + let Inst{12} = fc{2}; + let Inst{11-8} = 0b1111; + let Inst{7} = fc{0}; + let Inst{4} = 0b0; + + let Defs = [VPR, P0]; +} + +class MVE_VPTt1<string suffix, bits<2> size, dag iops> + : MVE_VPT<suffix, size, iops, "$fc, $Qn, $Qm"> { + bits<4> Qm; + bits<4> Mk; + + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = fc{1}; +} + +class MVE_VPTt1i<string suffix, bits<2> size> + : MVE_VPTt1<suffix, size, + (ins vpt_mask:$Mk, pred_basic_i:$fc, MQPR:$Qn, MQPR:$Qm)> { + let Inst{12} = 0b0; + let Inst{0} = 0b0; +} + +def MVE_VPTv4i32 : MVE_VPTt1i<"i32", 0b10>; +def MVE_VPTv8i16 : MVE_VPTt1i<"i16", 0b01>; +def MVE_VPTv16i8 : MVE_VPTt1i<"i8", 0b00>; + +class MVE_VPTt1u<string suffix, bits<2> size> + : MVE_VPTt1<suffix, size, + (ins vpt_mask:$Mk, pred_basic_u:$fc, MQPR:$Qn, MQPR:$Qm)> { + let Inst{12} = 0b0; + let Inst{0} = 0b1; +} + +def MVE_VPTv4u32 : MVE_VPTt1u<"u32", 0b10>; +def MVE_VPTv8u16 : MVE_VPTt1u<"u16", 0b01>; +def MVE_VPTv16u8 : MVE_VPTt1u<"u8", 0b00>; + +class MVE_VPTt1s<string suffix, bits<2> size> + : MVE_VPTt1<suffix, size, + (ins vpt_mask:$Mk, pred_basic_s:$fc, MQPR:$Qn, MQPR:$Qm)> { + let Inst{12} = 0b1; +} + +def MVE_VPTv4s32 : MVE_VPTt1s<"s32", 0b10>; +def MVE_VPTv8s16 : MVE_VPTt1s<"s16", 0b01>; +def MVE_VPTv16s8 : MVE_VPTt1s<"s8", 0b00>; + +class MVE_VPTt2<string suffix, bits<2> size, dag iops> + : MVE_VPT<suffix, size, iops, + "$fc, $Qn, $Rm"> { + bits<4> Rm; + bits<3> fc; + bits<4> Mk; + + let Inst{6} = 0b1; + let Inst{5} = fc{1}; + let Inst{3-0} = Rm{3-0}; +} + +class MVE_VPTt2i<string suffix, bits<2> size> + : MVE_VPTt2<suffix, size, + (ins vpt_mask:$Mk, pred_basic_i:$fc, MQPR:$Qn, GPRwithZR:$Rm)> { + let Inst{12} = 0b0; + let Inst{5} = 0b0; +} + +def MVE_VPTv4i32r : MVE_VPTt2i<"i32", 0b10>; +def MVE_VPTv8i16r : MVE_VPTt2i<"i16", 0b01>; +def MVE_VPTv16i8r : MVE_VPTt2i<"i8", 0b00>; + +class MVE_VPTt2u<string suffix, bits<2> size> + : MVE_VPTt2<suffix, size, + (ins vpt_mask:$Mk, pred_basic_u:$fc, MQPR:$Qn, GPRwithZR:$Rm)> { + let Inst{12} = 0b0; + let Inst{5} = 0b1; +} + +def MVE_VPTv4u32r : MVE_VPTt2u<"u32", 0b10>; +def MVE_VPTv8u16r : MVE_VPTt2u<"u16", 0b01>; +def MVE_VPTv16u8r : MVE_VPTt2u<"u8", 0b00>; + +class MVE_VPTt2s<string suffix, bits<2> size> + : MVE_VPTt2<suffix, size, + (ins vpt_mask:$Mk, pred_basic_s:$fc, MQPR:$Qn, GPRwithZR:$Rm)> { + let Inst{12} = 0b1; +} + +def MVE_VPTv4s32r : MVE_VPTt2s<"s32", 0b10>; +def MVE_VPTv8s16r : MVE_VPTt2s<"s16", 0b01>; +def MVE_VPTv16s8r : MVE_VPTt2s<"s8", 0b00>; + + +class MVE_VPTf<string suffix, bit size, dag iops, string asm, list<dag> pattern=[]> + : MVE_MI<(outs ), iops, NoItinerary, !strconcat("vpt", "${Mk}", ".", suffix), asm, + "", pattern> { + bits<3> fc; + bits<4> Mk; + bits<3> Qn; + + let Inst{31-29} = 0b111; + let Inst{28} = size; + let Inst{27-23} = 0b11100; + let Inst{22} = Mk{3}; + let Inst{21-20} = 0b11; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b1; + let Inst{15-13} = Mk{2-0}; + let Inst{12} = fc{2}; + let Inst{11-8} = 0b1111; + let Inst{7} = fc{0}; + let Inst{4} = 0b0; + + let Defs = [P0]; + let Predicates = [HasMVEFloat]; +} + +class MVE_VPTft1<string suffix, bit size> + : MVE_VPTf<suffix, size, (ins vpt_mask:$Mk, pred_basic_fp:$fc, MQPR:$Qn, MQPR:$Qm), + "$fc, $Qn, $Qm"> { + bits<3> fc; + bits<4> Qm; + + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = fc{1}; +} + +def MVE_VPTv4f32 : MVE_VPTft1<"f32", 0b0>; +def MVE_VPTv8f16 : MVE_VPTft1<"f16", 0b1>; + +class MVE_VPTft2<string suffix, bit size> + : MVE_VPTf<suffix, size, (ins vpt_mask:$Mk, pred_basic_fp:$fc, MQPR:$Qn, GPRwithZR:$Rm), + "$fc, $Qn, $Rm"> { + bits<3> fc; + bits<4> Rm; + + let Inst{6} = 0b1; + let Inst{5} = fc{1}; + let Inst{3-0} = Rm{3-0}; +} + +def MVE_VPTv4f32r : MVE_VPTft2<"f32", 0b0>; +def MVE_VPTv8f16r : MVE_VPTft2<"f16", 0b1>; + +def MVE_VPST : MVE_MI<(outs ), (ins vpt_mask:$Mk), NoItinerary, + !strconcat("vpst", "${Mk}"), "", "", []> { + bits<4> Mk; + + let Inst{31-23} = 0b111111100; + let Inst{22} = Mk{3}; + let Inst{21-16} = 0b110001; + let Inst{15-13} = Mk{2-0}; + let Inst{12-0} = 0b0111101001101; + let Unpredictable{12} = 0b1; + let Unpredictable{7} = 0b1; + let Unpredictable{5} = 0b1; + + let Defs = [P0]; +} + +def MVE_VPSEL : MVE_p<(outs MQPR:$Qd), (ins MQPR:$Qn, MQPR:$Qm), NoItinerary, + "vpsel", "", "$Qd, $Qn, $Qm", vpred_n, "", []> { + bits<4> Qn; + bits<4> Qd; + bits<4> Qm; + + let Inst{28} = 0b1; + let Inst{25-23} = 0b100; + let Inst{22} = Qd{3}; + let Inst{21-20} = 0b11; + let Inst{19-17} = Qn{2-0}; + let Inst{16} = 0b1; + let Inst{15-13} = Qd{2-0}; + let Inst{12-9} = 0b0111; + let Inst{8} = 0b1; + let Inst{7} = Qn{3}; + let Inst{6} = 0b0; + let Inst{5} = Qm{3}; + let Inst{4} = 0b0; + let Inst{3-1} = Qm{2-0}; + let Inst{0} = 0b1; +} + +foreach suffix = ["s8", "s16", "s32", "u8", "u16", "u32", + "i8", "i16", "i32", "f16", "f32"] in +def : MVEInstAlias<"vpsel${vp}." # suffix # "\t$Qd, $Qn, $Qm", + (MVE_VPSEL MQPR:$Qd, MQPR:$Qn, MQPR:$Qm, vpred_n:$vp)>; + +def MVE_VPNOT : MVE_p<(outs), (ins), NoItinerary, + "vpnot", "", "", vpred_n, "", []> { + let Inst{31-0} = 0b11111110001100010000111101001101; + let Unpredictable{19-17} = 0b111; + let Unpredictable{12} = 0b1; + let Unpredictable{7} = 0b1; + let Unpredictable{5} = 0b1; + let Defs = [P0]; + let Uses = [P0]; + + let Constraints = ""; +} + +class MVE_loltp_start<dag iops, string asm, string ops, bits<2> size> + : t2LOL<(outs GPRlr:$LR), iops, asm, ops> { + bits<4> Rn; + let Predicates = [HasMVEInt]; + let Inst{22} = 0b0; + let Inst{21-20} = size; + let Inst{19-16} = Rn{3-0}; + let Inst{12} = 0b0; +} + +class MVE_DLSTP<string asm, bits<2> size> + : MVE_loltp_start<(ins rGPR:$Rn), asm, "$LR, $Rn", size> { + let Inst{13} = 0b1; + let Inst{11-1} = 0b00000000000; + let Unpredictable{10-1} = 0b1111111111; +} + +class MVE_WLSTP<string asm, bits<2> size> + : MVE_loltp_start<(ins rGPR:$Rn, wlslabel_u11:$label), + asm, "$LR, $Rn, $label", size> { + bits<11> label; + let Inst{13} = 0b0; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def MVE_DLSTP_8 : MVE_DLSTP<"dlstp.8", 0b00>; +def MVE_DLSTP_16 : MVE_DLSTP<"dlstp.16", 0b01>; +def MVE_DLSTP_32 : MVE_DLSTP<"dlstp.32", 0b10>; +def MVE_DLSTP_64 : MVE_DLSTP<"dlstp.64", 0b11>; + +def MVE_WLSTP_8 : MVE_WLSTP<"wlstp.8", 0b00>; +def MVE_WLSTP_16 : MVE_WLSTP<"wlstp.16", 0b01>; +def MVE_WLSTP_32 : MVE_WLSTP<"wlstp.32", 0b10>; +def MVE_WLSTP_64 : MVE_WLSTP<"wlstp.64", 0b11>; + +class MVE_loltp_end<dag oops, dag iops, string asm, string ops> + : t2LOL<oops, iops, asm, ops> { + let Predicates = [HasMVEInt]; + let Inst{22-21} = 0b00; + let Inst{19-16} = 0b1111; + let Inst{12} = 0b0; +} + +def MVE_LETP : MVE_loltp_end<(outs GPRlr:$LRout), + (ins GPRlr:$LRin, lelabel_u11:$label), + "letp", "$LRin, $label"> { + bits<11> label; + let Inst{20} = 0b1; + let Inst{13} = 0b0; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def MVE_LCTP : MVE_loltp_end<(outs), (ins pred:$p), "lctp${p}", ""> { + let Inst{20} = 0b0; + let Inst{13} = 0b1; + let Inst{11-1} = 0b00000000000; + let Unpredictable{21-20} = 0b11; + let Unpredictable{11-1} = 0b11111111111; +} + + +//===----------------------------------------------------------------------===// +// Patterns +//===----------------------------------------------------------------------===// + +class MVE_unpred_vector_store_typed<ValueType Ty, Instruction RegImmInst, + PatFrag StoreKind, int shift> + : Pat<(StoreKind (Ty MQPR:$val), t2addrmode_imm7<shift>:$addr), + (RegImmInst (Ty MQPR:$val), t2addrmode_imm7<shift>:$addr)>; + +multiclass MVE_unpred_vector_store<Instruction RegImmInst, PatFrag StoreKind, + int shift> { + def : MVE_unpred_vector_store_typed<v16i8, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v8i16, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v8f16, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v4i32, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v4f32, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v2i64, RegImmInst, StoreKind, shift>; + def : MVE_unpred_vector_store_typed<v2f64, RegImmInst, StoreKind, shift>; +} + +class MVE_unpred_vector_load_typed<ValueType Ty, Instruction RegImmInst, + PatFrag LoadKind, int shift> + : Pat<(Ty (LoadKind t2addrmode_imm7<shift>:$addr)), + (Ty (RegImmInst t2addrmode_imm7<shift>:$addr))>; + +multiclass MVE_unpred_vector_load<Instruction RegImmInst, PatFrag LoadKind, + int shift> { + def : MVE_unpred_vector_load_typed<v16i8, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v8i16, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v8f16, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v4i32, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v4f32, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v2i64, RegImmInst, LoadKind, shift>; + def : MVE_unpred_vector_load_typed<v2f64, RegImmInst, LoadKind, shift>; +} + +let Predicates = [HasMVEInt, IsLE] in { + defm : MVE_unpred_vector_store<MVE_VSTRBU8, byte_alignedstore, 0>; + defm : MVE_unpred_vector_store<MVE_VSTRHU16, hword_alignedstore, 1>; + defm : MVE_unpred_vector_store<MVE_VSTRWU32, alignedstore32, 2>; + + defm : MVE_unpred_vector_load<MVE_VLDRBU8, byte_alignedload, 0>; + defm : MVE_unpred_vector_load<MVE_VLDRHU16, hword_alignedload, 1>; + defm : MVE_unpred_vector_load<MVE_VLDRWU32, alignedload32, 2>; + + def : Pat<(v16i1 (load t2addrmode_imm7<2>:$addr)), + (v16i1 (VLDR_P0_off t2addrmode_imm7<2>:$addr))>; + def : Pat<(v8i1 (load t2addrmode_imm7<2>:$addr)), + (v8i1 (VLDR_P0_off t2addrmode_imm7<2>:$addr))>; + def : Pat<(v4i1 (load t2addrmode_imm7<2>:$addr)), + (v4i1 (VLDR_P0_off t2addrmode_imm7<2>:$addr))>; +} + +let Predicates = [HasMVEInt, IsBE] in { + def : MVE_unpred_vector_store_typed<v16i8, MVE_VSTRBU8, store, 0>; + def : MVE_unpred_vector_store_typed<v8i16, MVE_VSTRHU16, alignedstore16, 1>; + def : MVE_unpred_vector_store_typed<v8f16, MVE_VSTRHU16, alignedstore16, 1>; + def : MVE_unpred_vector_store_typed<v4i32, MVE_VSTRWU32, alignedstore32, 2>; + def : MVE_unpred_vector_store_typed<v4f32, MVE_VSTRWU32, alignedstore32, 2>; + + def : MVE_unpred_vector_load_typed<v16i8, MVE_VLDRBU8, load, 0>; + def : MVE_unpred_vector_load_typed<v8i16, MVE_VLDRHU16, alignedload16, 1>; + def : MVE_unpred_vector_load_typed<v8f16, MVE_VLDRHU16, alignedload16, 1>; + def : MVE_unpred_vector_load_typed<v4i32, MVE_VLDRWU32, alignedload32, 2>; + def : MVE_unpred_vector_load_typed<v4f32, MVE_VLDRWU32, alignedload32, 2>; +} + + +// Widening/Narrowing Loads/Stores + +let Predicates = [HasMVEInt] in { + def : Pat<(truncstorevi8 (v8i16 MQPR:$val), t2addrmode_imm7<1>:$addr), + (MVE_VSTRB16 MQPR:$val, t2addrmode_imm7<1>:$addr)>; + def : Pat<(truncstorevi8 (v4i32 MQPR:$val), t2addrmode_imm7<1>:$addr), + (MVE_VSTRB32 MQPR:$val, t2addrmode_imm7<1>:$addr)>; + def : Pat<(truncstorevi16 (v4i32 MQPR:$val), t2addrmode_imm7<2>:$addr), + (MVE_VSTRH32 MQPR:$val, t2addrmode_imm7<2>:$addr)>; +} + +multiclass MVEExtLoad<string DestLanes, string DestElemBits, + string SrcElemBits, string SrcElemType, + Operand am> { + def _Any : Pat<(!cast<ValueType>("v" # DestLanes # "i" # DestElemBits) + (!cast<PatFrag>("extloadvi" # SrcElemBits) am:$addr)), + (!cast<Instruction>("MVE_VLDR" # SrcElemType # "U" # DestElemBits) + am:$addr)>; + def _Z : Pat<(!cast<ValueType>("v" # DestLanes # "i" # DestElemBits) + (!cast<PatFrag>("zextloadvi" # SrcElemBits) am:$addr)), + (!cast<Instruction>("MVE_VLDR" # SrcElemType # "U" # DestElemBits) + am:$addr)>; + def _S : Pat<(!cast<ValueType>("v" # DestLanes # "i" # DestElemBits) + (!cast<PatFrag>("sextloadvi" # SrcElemBits) am:$addr)), + (!cast<Instruction>("MVE_VLDR" # SrcElemType # "S" # DestElemBits) + am:$addr)>; +} + +let Predicates = [HasMVEInt] in { + defm : MVEExtLoad<"4", "32", "8", "B", t2addrmode_imm7<1>>; + defm : MVEExtLoad<"8", "16", "8", "B", t2addrmode_imm7<1>>; + defm : MVEExtLoad<"4", "32", "16", "H", t2addrmode_imm7<2>>; +} + + +// Bit convert patterns + +let Predicates = [HasMVEInt] in { + def : Pat<(v2f64 (bitconvert (v2i64 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v2f64 QPR:$src))), (v2i64 QPR:$src)>; + + def : Pat<(v4i32 (bitconvert (v4f32 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v4i32 QPR:$src))), (v4f32 QPR:$src)>; + + def : Pat<(v8i16 (bitconvert (v8f16 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v8i16 QPR:$src))), (v8f16 QPR:$src)>; +} + +let Predicates = [IsLE,HasMVEInt] in { + def : Pat<(v2f64 (bitconvert (v4f32 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v4i32 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8f16 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8i16 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v16i8 QPR:$src))), (v2f64 QPR:$src)>; + + def : Pat<(v2i64 (bitconvert (v4f32 QPR:$src))), (v2i64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v4i32 QPR:$src))), (v2i64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v8f16 QPR:$src))), (v2i64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v8i16 QPR:$src))), (v2i64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v16i8 QPR:$src))), (v2i64 QPR:$src)>; + + def : Pat<(v4f32 (bitconvert (v2f64 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v2i64 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8f16 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8i16 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v16i8 QPR:$src))), (v4f32 QPR:$src)>; + + def : Pat<(v4i32 (bitconvert (v2f64 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v2i64 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v8f16 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v8i16 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v16i8 QPR:$src))), (v4i32 QPR:$src)>; + + def : Pat<(v8f16 (bitconvert (v2f64 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v2i64 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4f32 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4i32 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v16i8 QPR:$src))), (v8f16 QPR:$src)>; + + def : Pat<(v8i16 (bitconvert (v2f64 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v2i64 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v4f32 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v4i32 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v16i8 QPR:$src))), (v8i16 QPR:$src)>; + + def : Pat<(v16i8 (bitconvert (v2f64 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v2i64 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v4f32 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v4i32 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v8f16 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v8i16 QPR:$src))), (v16i8 QPR:$src)>; +} diff --git a/lib/Target/ARM/ARMInstrNEON.td b/lib/Target/ARM/ARMInstrNEON.td index 96986e74415b..806681df102c 100644 --- a/lib/Target/ARM/ARMInstrNEON.td +++ b/lib/Target/ARM/ARMInstrNEON.td @@ -1,9 +1,8 @@ //===-- ARMInstrNEON.td - NEON support for ARM -------------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -497,45 +496,30 @@ def NEONvtst : SDNode<"ARMISD::VTST", SDTARMVCMP>; // Types for vector shift by immediates. The "SHX" version is for long and // narrow operations where the source and destination vectors have different // types. The "SHINS" version is for shift and insert operations. -def SDTARMVSH : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>, - SDTCisVT<2, i32>]>; -def SDTARMVSHX : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisInt<1>, - SDTCisVT<2, i32>]>; -def SDTARMVSHINS : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, - SDTCisSameAs<0, 2>, SDTCisVT<3, i32>]>; - -def NEONvshl : SDNode<"ARMISD::VSHL", SDTARMVSH>; -def NEONvshrs : SDNode<"ARMISD::VSHRs", SDTARMVSH>; -def NEONvshru : SDNode<"ARMISD::VSHRu", SDTARMVSH>; -def NEONvshrn : SDNode<"ARMISD::VSHRN", SDTARMVSHX>; +def SDTARMVSHXIMM : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisInt<1>, + SDTCisVT<2, i32>]>; +def SDTARMVSHINSIMM : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisSameAs<0, 2>, SDTCisVT<3, i32>]>; -def NEONvrshrs : SDNode<"ARMISD::VRSHRs", SDTARMVSH>; -def NEONvrshru : SDNode<"ARMISD::VRSHRu", SDTARMVSH>; -def NEONvrshrn : SDNode<"ARMISD::VRSHRN", SDTARMVSHX>; +def NEONvshrnImm : SDNode<"ARMISD::VSHRNIMM", SDTARMVSHXIMM>; -def NEONvqshls : SDNode<"ARMISD::VQSHLs", SDTARMVSH>; -def NEONvqshlu : SDNode<"ARMISD::VQSHLu", SDTARMVSH>; -def NEONvqshlsu : SDNode<"ARMISD::VQSHLsu", SDTARMVSH>; -def NEONvqshrns : SDNode<"ARMISD::VQSHRNs", SDTARMVSHX>; -def NEONvqshrnu : SDNode<"ARMISD::VQSHRNu", SDTARMVSHX>; -def NEONvqshrnsu : SDNode<"ARMISD::VQSHRNsu", SDTARMVSHX>; +def NEONvrshrsImm : SDNode<"ARMISD::VRSHRsIMM", SDTARMVSHIMM>; +def NEONvrshruImm : SDNode<"ARMISD::VRSHRuIMM", SDTARMVSHIMM>; +def NEONvrshrnImm : SDNode<"ARMISD::VRSHRNIMM", SDTARMVSHXIMM>; -def NEONvqrshrns : SDNode<"ARMISD::VQRSHRNs", SDTARMVSHX>; -def NEONvqrshrnu : SDNode<"ARMISD::VQRSHRNu", SDTARMVSHX>; -def NEONvqrshrnsu : SDNode<"ARMISD::VQRSHRNsu", SDTARMVSHX>; +def NEONvqshlsImm : SDNode<"ARMISD::VQSHLsIMM", SDTARMVSHIMM>; +def NEONvqshluImm : SDNode<"ARMISD::VQSHLuIMM", SDTARMVSHIMM>; +def NEONvqshlsuImm : SDNode<"ARMISD::VQSHLsuIMM", SDTARMVSHIMM>; +def NEONvqshrnsImm : SDNode<"ARMISD::VQSHRNsIMM", SDTARMVSHXIMM>; +def NEONvqshrnuImm : SDNode<"ARMISD::VQSHRNuIMM", SDTARMVSHXIMM>; +def NEONvqshrnsuImm : SDNode<"ARMISD::VQSHRNsuIMM", SDTARMVSHXIMM>; -def NEONvsli : SDNode<"ARMISD::VSLI", SDTARMVSHINS>; -def NEONvsri : SDNode<"ARMISD::VSRI", SDTARMVSHINS>; +def NEONvqrshrnsImm : SDNode<"ARMISD::VQRSHRNsIMM", SDTARMVSHXIMM>; +def NEONvqrshrnuImm : SDNode<"ARMISD::VQRSHRNuIMM", SDTARMVSHXIMM>; +def NEONvqrshrnsuImm : SDNode<"ARMISD::VQRSHRNsuIMM", SDTARMVSHXIMM>; -def SDTARMVGETLN : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, SDTCisInt<1>, - SDTCisVT<2, i32>]>; -def NEONvgetlaneu : SDNode<"ARMISD::VGETLANEu", SDTARMVGETLN>; -def NEONvgetlanes : SDNode<"ARMISD::VGETLANEs", SDTARMVGETLN>; - -def SDTARMVMOVIMM : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisVT<1, i32>]>; -def NEONvmovImm : SDNode<"ARMISD::VMOVIMM", SDTARMVMOVIMM>; -def NEONvmvnImm : SDNode<"ARMISD::VMVNIMM", SDTARMVMOVIMM>; -def NEONvmovFPImm : SDNode<"ARMISD::VMOVFPIMM", SDTARMVMOVIMM>; +def NEONvsliImm : SDNode<"ARMISD::VSLIIMM", SDTARMVSHINSIMM>; +def NEONvsriImm : SDNode<"ARMISD::VSRIIMM", SDTARMVSHINSIMM>; def SDTARMVORRIMM : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>]>; @@ -548,23 +532,10 @@ def NEONvbsl : SDNode<"ARMISD::VBSL", SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>]>>; -def NEONvdup : SDNode<"ARMISD::VDUP", SDTypeProfile<1, 1, [SDTCisVec<0>]>>; - -// VDUPLANE can produce a quad-register result from a double-register source, -// so the result is not constrained to match the source. -def NEONvduplane : SDNode<"ARMISD::VDUPLANE", - SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, - SDTCisVT<2, i32>]>>; - def SDTARMVEXT : SDTypeProfile<1, 3, [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisVT<3, i32>]>; def NEONvext : SDNode<"ARMISD::VEXT", SDTARMVEXT>; -def SDTARMVSHUF : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisSameAs<0, 1>]>; -def NEONvrev64 : SDNode<"ARMISD::VREV64", SDTARMVSHUF>; -def NEONvrev32 : SDNode<"ARMISD::VREV32", SDTARMVSHUF>; -def NEONvrev16 : SDNode<"ARMISD::VREV16", SDTARMVSHUF>; - def SDTARMVSHUF2 : SDTypeProfile<2, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>]>; @@ -585,14 +556,14 @@ def NEONvtbl1 : SDNode<"ARMISD::VTBL1", SDTARMVTBL1>; def NEONvtbl2 : SDNode<"ARMISD::VTBL2", SDTARMVTBL2>; -def NEONimmAllZerosV: PatLeaf<(NEONvmovImm (i32 timm)), [{ +def NEONimmAllZerosV: PatLeaf<(ARMvmovImm (i32 timm)), [{ ConstantSDNode *ConstVal = cast<ConstantSDNode>(N->getOperand(0)); unsigned EltBits = 0; uint64_t EltVal = ARM_AM::decodeNEONModImm(ConstVal->getZExtValue(), EltBits); return (EltBits == 32 && EltVal == 0); }]>; -def NEONimmAllOnesV: PatLeaf<(NEONvmovImm (i32 timm)), [{ +def NEONimmAllOnesV: PatLeaf<(ARMvmovImm (i32 timm)), [{ ConstantSDNode *ConstVal = cast<ConstantSDNode>(N->getOperand(0)); unsigned EltBits = 0; uint64_t EltVal = ARM_AM::decodeNEONModImm(ConstVal->getZExtValue(), EltBits); @@ -1118,6 +1089,13 @@ def VLD1LNq8Pseudo : VLD1QLNPseudo<v16i8, extloadi8>; def VLD1LNq16Pseudo : VLD1QLNPseudo<v8i16, extloadi16>; def VLD1LNq32Pseudo : VLD1QLNPseudo<v4i32, load>; +let Predicates = [HasNEON] in { +def : Pat<(vector_insert (v4f16 DPR:$src), + (f16 (load addrmode6:$addr)), imm:$lane), + (VLD1LNd16 addrmode6:$addr, DPR:$src, imm:$lane)>; +def : Pat<(vector_insert (v8f16 QPR:$src), + (f16 (load addrmode6:$addr)), imm:$lane), + (VLD1LNq16Pseudo addrmode6:$addr, QPR:$src, imm:$lane)>; def : Pat<(vector_insert (v2f32 DPR:$src), (f32 (load addrmode6:$addr)), imm:$lane), (VLD1LNd32 addrmode6:$addr, DPR:$src, imm:$lane)>; @@ -1139,6 +1117,7 @@ def : Pat<(insert_subvector undef, (v4f16 DPR:$src), (i32 0)), (INSERT_SUBREG (v8f16 (IMPLICIT_DEF)), DPR:$src, dsub_0)>; def : Pat<(insert_subvector (v16i8 undef), (v8i8 DPR:$src), (i32 0)), (INSERT_SUBREG (v16i8 (IMPLICIT_DEF)), DPR:$src, dsub_0)>; +} let mayLoad = 1, hasSideEffects = 0, hasExtraDefRegAllocReq = 1 in { @@ -1404,7 +1383,7 @@ class VLD1DUP<bits<4> op7_4, string Dt, ValueType Ty, PatFrag LoadOp, (ins AddrMode:$Rn), IIC_VLD1dup, "vld1", Dt, "$Vd, $Rn", "", [(set VecListOneDAllLanes:$Vd, - (Ty (NEONvdup (i32 (LoadOp AddrMode:$Rn)))))]>, + (Ty (ARMvdup (i32 (LoadOp AddrMode:$Rn)))))]>, Sched<[WriteVLD2]> { let Rm = 0b1111; let Inst{4} = Rn{4}; @@ -1417,8 +1396,10 @@ def VLD1DUPd16 : VLD1DUP<{0,1,0,?}, "16", v4i16, extloadi16, def VLD1DUPd32 : VLD1DUP<{1,0,0,?}, "32", v2i32, load, addrmode6dupalign32>; -def : Pat<(v2f32 (NEONvdup (f32 (load addrmode6dup:$addr)))), +let Predicates = [HasNEON] in { +def : Pat<(v2f32 (ARMvdup (f32 (load addrmode6dup:$addr)))), (VLD1DUPd32 addrmode6:$addr)>; +} class VLD1QDUP<bits<4> op7_4, string Dt, ValueType Ty, PatFrag LoadOp, Operand AddrMode> @@ -1426,7 +1407,7 @@ class VLD1QDUP<bits<4> op7_4, string Dt, ValueType Ty, PatFrag LoadOp, (ins AddrMode:$Rn), IIC_VLD1dup, "vld1", Dt, "$Vd, $Rn", "", [(set VecListDPairAllLanes:$Vd, - (Ty (NEONvdup (i32 (LoadOp AddrMode:$Rn)))))]> { + (Ty (ARMvdup (i32 (LoadOp AddrMode:$Rn)))))]> { let Rm = 0b1111; let Inst{4} = Rn{4}; let DecoderMethod = "DecodeVLD1DupInstruction"; @@ -1439,8 +1420,10 @@ def VLD1DUPq16 : VLD1QDUP<{0,1,1,?}, "16", v8i16, extloadi16, def VLD1DUPq32 : VLD1QDUP<{1,0,1,?}, "32", v4i32, load, addrmode6dupalign32>; -def : Pat<(v4f32 (NEONvdup (f32 (load addrmode6dup:$addr)))), +let Predicates = [HasNEON] in { +def : Pat<(v4f32 (ARMvdup (f32 (load addrmode6dup:$addr)))), (VLD1DUPq32 addrmode6:$addr)>; +} let mayLoad = 1, hasSideEffects = 0, hasExtraDefRegAllocReq = 1 in { // ...with address register writeback: @@ -2152,11 +2135,11 @@ class VST1QLNPseudo<ValueType Ty, PatFrag StoreOp, SDNode ExtractOp> } def VST1LNd8 : VST1LN<0b0000, {?,?,?,0}, "8", v8i8, truncstorei8, - NEONvgetlaneu, addrmode6> { + ARMvgetlaneu, addrmode6> { let Inst{7-5} = lane{2-0}; } def VST1LNd16 : VST1LN<0b0100, {?,?,0,?}, "16", v4i16, truncstorei16, - NEONvgetlaneu, addrmode6> { + ARMvgetlaneu, addrmode6> { let Inst{7-6} = lane{1-0}; let Inst{4} = Rn{4}; } @@ -2167,15 +2150,22 @@ def VST1LNd32 : VST1LN<0b1000, {?,0,?,?}, "32", v2i32, store, extractelt, let Inst{5-4} = Rn{5-4}; } -def VST1LNq8Pseudo : VST1QLNPseudo<v16i8, truncstorei8, NEONvgetlaneu>; -def VST1LNq16Pseudo : VST1QLNPseudo<v8i16, truncstorei16, NEONvgetlaneu>; +def VST1LNq8Pseudo : VST1QLNPseudo<v16i8, truncstorei8, ARMvgetlaneu>; +def VST1LNq16Pseudo : VST1QLNPseudo<v8i16, truncstorei16, ARMvgetlaneu>; def VST1LNq32Pseudo : VST1QLNPseudo<v4i32, store, extractelt>; +let Predicates = [HasNEON] in { def : Pat<(store (extractelt (v2f32 DPR:$src), imm:$lane), addrmode6:$addr), (VST1LNd32 addrmode6:$addr, DPR:$src, imm:$lane)>; def : Pat<(store (extractelt (v4f32 QPR:$src), imm:$lane), addrmode6:$addr), (VST1LNq32Pseudo addrmode6:$addr, QPR:$src, imm:$lane)>; +def : Pat<(store (extractelt (v4f16 DPR:$src), imm:$lane), addrmode6:$addr), + (VST1LNd16 addrmode6:$addr, DPR:$src, imm:$lane)>; +def : Pat<(store (extractelt (v8f16 QPR:$src), imm:$lane), addrmode6:$addr), + (VST1LNq16Pseudo addrmode6:$addr, QPR:$src, imm:$lane)>; +} + // ...with address register writeback: class VST1LNWB<bits<4> op11_8, bits<4> op7_4, string Dt, ValueType Ty, PatFrag StoreOp, SDNode ExtractOp, Operand AdrMode> @@ -2196,11 +2186,11 @@ class VST1QLNWBPseudo<ValueType Ty, PatFrag StoreOp, SDNode ExtractOp> } def VST1LNd8_UPD : VST1LNWB<0b0000, {?,?,?,0}, "8", v8i8, post_truncsti8, - NEONvgetlaneu, addrmode6> { + ARMvgetlaneu, addrmode6> { let Inst{7-5} = lane{2-0}; } def VST1LNd16_UPD : VST1LNWB<0b0100, {?,?,0,?}, "16", v4i16, post_truncsti16, - NEONvgetlaneu, addrmode6> { + ARMvgetlaneu, addrmode6> { let Inst{7-6} = lane{1-0}; let Inst{4} = Rn{4}; } @@ -2210,8 +2200,8 @@ def VST1LNd32_UPD : VST1LNWB<0b1000, {?,0,?,?}, "32", v2i32, post_store, let Inst{5-4} = Rn{5-4}; } -def VST1LNq8Pseudo_UPD : VST1QLNWBPseudo<v16i8, post_truncsti8, NEONvgetlaneu>; -def VST1LNq16Pseudo_UPD : VST1QLNWBPseudo<v8i16, post_truncsti16,NEONvgetlaneu>; +def VST1LNq8Pseudo_UPD : VST1QLNWBPseudo<v16i8, post_truncsti8, ARMvgetlaneu>; +def VST1LNq16Pseudo_UPD : VST1QLNWBPseudo<v8i16, post_truncsti16,ARMvgetlaneu>; def VST1LNq32Pseudo_UPD : VST1QLNWBPseudo<v4i32, post_store, extractelt>; let mayStore = 1, hasSideEffects = 0, hasExtraSrcRegAllocReq = 1 in { @@ -2440,37 +2430,45 @@ def VST4LNq32Pseudo_UPD : VSTQQQQLNWBPseudo<IIC_VST4lnu>, Sched<[WriteVST2]>; } // mayStore = 1, hasSideEffects = 0, hasExtraSrcRegAllocReq = 1 // Use vld1/vst1 for unaligned f64 load / store +let Predicates = [IsLE,HasNEON] in { def : Pat<(f64 (hword_alignedload addrmode6:$addr)), - (VLD1d16 addrmode6:$addr)>, Requires<[IsLE]>; + (VLD1d16 addrmode6:$addr)>; def : Pat<(hword_alignedstore (f64 DPR:$value), addrmode6:$addr), - (VST1d16 addrmode6:$addr, DPR:$value)>, Requires<[IsLE]>; + (VST1d16 addrmode6:$addr, DPR:$value)>; def : Pat<(f64 (byte_alignedload addrmode6:$addr)), - (VLD1d8 addrmode6:$addr)>, Requires<[IsLE]>; + (VLD1d8 addrmode6:$addr)>; def : Pat<(byte_alignedstore (f64 DPR:$value), addrmode6:$addr), - (VST1d8 addrmode6:$addr, DPR:$value)>, Requires<[IsLE]>; + (VST1d8 addrmode6:$addr, DPR:$value)>; +} +let Predicates = [IsBE,HasNEON] in { def : Pat<(f64 (non_word_alignedload addrmode6:$addr)), - (VLD1d64 addrmode6:$addr)>, Requires<[IsBE]>; + (VLD1d64 addrmode6:$addr)>; def : Pat<(non_word_alignedstore (f64 DPR:$value), addrmode6:$addr), - (VST1d64 addrmode6:$addr, DPR:$value)>, Requires<[IsBE]>; + (VST1d64 addrmode6:$addr, DPR:$value)>; +} // Use vld1/vst1 for Q and QQ. Also use them for unaligned v2f64 // load / store if it's legal. +let Predicates = [HasNEON] in { def : Pat<(v2f64 (dword_alignedload addrmode6:$addr)), (VLD1q64 addrmode6:$addr)>; def : Pat<(dword_alignedstore (v2f64 QPR:$value), addrmode6:$addr), (VST1q64 addrmode6:$addr, QPR:$value)>; +} +let Predicates = [IsLE,HasNEON] in { def : Pat<(v2f64 (word_alignedload addrmode6:$addr)), - (VLD1q32 addrmode6:$addr)>, Requires<[IsLE]>; + (VLD1q32 addrmode6:$addr)>; def : Pat<(word_alignedstore (v2f64 QPR:$value), addrmode6:$addr), - (VST1q32 addrmode6:$addr, QPR:$value)>, Requires<[IsLE]>; + (VST1q32 addrmode6:$addr, QPR:$value)>; def : Pat<(v2f64 (hword_alignedload addrmode6:$addr)), - (VLD1q16 addrmode6:$addr)>, Requires<[IsLE]>; + (VLD1q16 addrmode6:$addr)>; def : Pat<(hword_alignedstore (v2f64 QPR:$value), addrmode6:$addr), - (VST1q16 addrmode6:$addr, QPR:$value)>, Requires<[IsLE]>; + (VST1q16 addrmode6:$addr, QPR:$value)>; def : Pat<(v2f64 (byte_alignedload addrmode6:$addr)), - (VLD1q8 addrmode6:$addr)>, Requires<[IsLE]>; + (VLD1q8 addrmode6:$addr)>; def : Pat<(byte_alignedstore (v2f64 QPR:$value), addrmode6:$addr), - (VST1q8 addrmode6:$addr, QPR:$value)>, Requires<[IsLE]>; + (VST1q8 addrmode6:$addr, QPR:$value)>; +} //===----------------------------------------------------------------------===// // NEON pattern fragments @@ -2505,6 +2503,13 @@ def SSubReg_f32_reg : SDNodeXForm<imm, [{ MVT::i32); }]>; +// Extract S sub-registers of Q/D registers containing a given f16 lane. +def SSubReg_f16_reg : SDNodeXForm<imm, [{ + assert(ARM::ssub_3 == ARM::ssub_0+3 && "Unexpected subreg numbering"); + return CurDAG->getTargetConstant(ARM::ssub_0 + N->getZExtValue()/2, SDLoc(N), + MVT::i32); +}]>; + // Translate lane numbers from Q registers to D subregs. def SubReg_i8_lane : SDNodeXForm<imm, [{ return CurDAG->getTargetConstant(N->getZExtValue() & 7, SDLoc(N), MVT::i32); @@ -2666,7 +2671,7 @@ class N3VDSL<bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (Ty DPR:$Vd), (Ty (ShOp (Ty DPR:$Vn), - (Ty (NEONvduplane (Ty DPR_VFP2:$Vm),imm:$lane)))))]> { + (Ty (ARMvduplane (Ty DPR_VFP2:$Vm),imm:$lane)))))]> { // All of these have a two-operand InstAlias. let TwoOperandAliasConstraint = "$Vn = $Vd"; let isCommutable = 0; @@ -2678,7 +2683,7 @@ class N3VDSL16<bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, IIC_VMULi16D, OpcodeStr, Dt,"$Vd, $Vn, $Vm$lane","", [(set (Ty DPR:$Vd), (Ty (ShOp (Ty DPR:$Vn), - (Ty (NEONvduplane (Ty DPR_8:$Vm), imm:$lane)))))]> { + (Ty (ARMvduplane (Ty DPR_8:$Vm), imm:$lane)))))]> { // All of these have a two-operand InstAlias. let TwoOperandAliasConstraint = "$Vn = $Vd"; let isCommutable = 0; @@ -2714,7 +2719,7 @@ class N3VQSL<bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (ShOp (ResTy QPR:$Vn), - (ResTy (NEONvduplane (OpTy DPR_VFP2:$Vm), + (ResTy (ARMvduplane (OpTy DPR_VFP2:$Vm), imm:$lane)))))]> { // All of these have a two-operand InstAlias. let TwoOperandAliasConstraint = "$Vn = $Vd"; @@ -2727,7 +2732,7 @@ class N3VQSL16<bits<2> op21_20, bits<4> op11_8, string OpcodeStr, string Dt, NVMulSLFrm, IIC_VMULi16Q, OpcodeStr, Dt,"$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (ShOp (ResTy QPR:$Vn), - (ResTy (NEONvduplane (OpTy DPR_8:$Vm), + (ResTy (ARMvduplane (OpTy DPR_8:$Vm), imm:$lane)))))]> { // All of these have a two-operand InstAlias. let TwoOperandAliasConstraint = "$Vn = $Vd"; @@ -2762,7 +2767,7 @@ class N3VDIntSL<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (Ty DPR:$Vd), (Ty (IntOp (Ty DPR:$Vn), - (Ty (NEONvduplane (Ty DPR_VFP2:$Vm), + (Ty (ARMvduplane (Ty DPR_VFP2:$Vm), imm:$lane)))))]> { let isCommutable = 0; } @@ -2774,7 +2779,7 @@ class N3VDIntSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (Ty DPR:$Vd), (Ty (IntOp (Ty DPR:$Vn), - (Ty (NEONvduplane (Ty DPR_8:$Vm), imm:$lane)))))]> { + (Ty (ARMvduplane (Ty DPR_8:$Vm), imm:$lane)))))]> { let isCommutable = 0; } class N3VDIntSh<bit op24, bit op23, bits<2> op21_20, bits<4> op11_8, bit op4, @@ -2829,7 +2834,7 @@ class N3VQIntSL<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (IntOp (ResTy QPR:$Vn), - (ResTy (NEONvduplane (OpTy DPR_VFP2:$Vm), + (ResTy (ARMvduplane (OpTy DPR_VFP2:$Vm), imm:$lane)))))]> { let isCommutable = 0; } @@ -2841,7 +2846,7 @@ class N3VQIntSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (IntOp (ResTy QPR:$Vn), - (ResTy (NEONvduplane (OpTy DPR_8:$Vm), + (ResTy (ARMvduplane (OpTy DPR_8:$Vm), imm:$lane)))))]> { let isCommutable = 0; } @@ -2877,7 +2882,7 @@ class N3VDMulOpSL<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, [(set (Ty DPR:$Vd), (Ty (ShOp (Ty DPR:$src1), (Ty (MulOp DPR:$Vn, - (Ty (NEONvduplane (Ty DPR_VFP2:$Vm), + (Ty (ARMvduplane (Ty DPR_VFP2:$Vm), imm:$lane)))))))]>; class N3VDMulOpSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, @@ -2890,7 +2895,7 @@ class N3VDMulOpSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, [(set (Ty DPR:$Vd), (Ty (ShOp (Ty DPR:$src1), (Ty (MulOp DPR:$Vn, - (Ty (NEONvduplane (Ty DPR_8:$Vm), + (Ty (ARMvduplane (Ty DPR_8:$Vm), imm:$lane)))))))]>; class N3VQMulOp<bit op24, bit op23, bits<2> op21_20, bits<4> op11_8, bit op4, @@ -2912,7 +2917,7 @@ class N3VQMulOpSL<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, [(set (ResTy QPR:$Vd), (ResTy (ShOp (ResTy QPR:$src1), (ResTy (MulOp QPR:$Vn, - (ResTy (NEONvduplane (OpTy DPR_VFP2:$Vm), + (ResTy (ARMvduplane (OpTy DPR_VFP2:$Vm), imm:$lane)))))))]>; class N3VQMulOpSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, @@ -2926,7 +2931,7 @@ class N3VQMulOpSL16<bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, [(set (ResTy QPR:$Vd), (ResTy (ShOp (ResTy QPR:$src1), (ResTy (MulOp QPR:$Vn, - (ResTy (NEONvduplane (OpTy DPR_8:$Vm), + (ResTy (ARMvduplane (OpTy DPR_8:$Vm), imm:$lane)))))))]>; // Neon Intrinsic-Op instructions (VABA): double- and quad-register. @@ -2986,7 +2991,7 @@ class N3VLMulOpSL<bit op24, bits<2> op21_20, bits<4> op11_8, [(set QPR:$Vd, (OpNode (TyQ QPR:$src1), (TyQ (MulOp (TyD DPR:$Vn), - (TyD (NEONvduplane (TyD DPR_VFP2:$Vm), + (TyD (ARMvduplane (TyD DPR_VFP2:$Vm), imm:$lane))))))]>; class N3VLMulOpSL16<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, @@ -2998,7 +3003,7 @@ class N3VLMulOpSL16<bit op24, bits<2> op21_20, bits<4> op11_8, [(set QPR:$Vd, (OpNode (TyQ QPR:$src1), (TyQ (MulOp (TyD DPR:$Vn), - (TyD (NEONvduplane (TyD DPR_8:$Vm), + (TyD (ARMvduplane (TyD DPR_8:$Vm), imm:$lane))))))]>; // Long Intrinsic-Op vector operations with explicit extend (VABAL). @@ -3034,7 +3039,7 @@ class N3VLInt3SL<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, [(set (ResTy QPR:$Vd), (ResTy (IntOp (ResTy QPR:$src1), (OpTy DPR:$Vn), - (OpTy (NEONvduplane (OpTy DPR_VFP2:$Vm), + (OpTy (ARMvduplane (OpTy DPR_VFP2:$Vm), imm:$lane)))))]>; class N3VLInt3SL16<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, @@ -3047,7 +3052,7 @@ class N3VLInt3SL16<bit op24, bits<2> op21_20, bits<4> op11_8, [(set (ResTy QPR:$Vd), (ResTy (IntOp (ResTy QPR:$src1), (OpTy DPR:$Vn), - (OpTy (NEONvduplane (OpTy DPR_8:$Vm), + (OpTy (ARMvduplane (OpTy DPR_8:$Vm), imm:$lane)))))]>; // Narrowing 3-register intrinsics. @@ -3080,7 +3085,7 @@ class N3VLSL<bit op24, bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set QPR:$Vd, (TyQ (OpNode (TyD DPR:$Vn), - (TyD (NEONvduplane (TyD DPR_VFP2:$Vm),imm:$lane)))))]>; + (TyD (ARMvduplane (TyD DPR_VFP2:$Vm),imm:$lane)))))]>; class N3VLSL16<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, ValueType TyQ, ValueType TyD, SDNode OpNode> @@ -3089,7 +3094,7 @@ class N3VLSL16<bit op24, bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set QPR:$Vd, (TyQ (OpNode (TyD DPR:$Vn), - (TyD (NEONvduplane (TyD DPR_8:$Vm), imm:$lane)))))]>; + (TyD (ARMvduplane (TyD DPR_8:$Vm), imm:$lane)))))]>; // Long 3-register operations with explicitly extended operands. class N3VLExt<bit op24, bit op23, bits<2> op21_20, bits<4> op11_8, bit op4, @@ -3145,7 +3150,7 @@ class N3VLIntSL<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (IntOp (OpTy DPR:$Vn), - (OpTy (NEONvduplane (OpTy DPR_VFP2:$Vm), + (OpTy (ARMvduplane (OpTy DPR_VFP2:$Vm), imm:$lane)))))]>; class N3VLIntSL16<bit op24, bits<2> op21_20, bits<4> op11_8, InstrItinClass itin, string OpcodeStr, string Dt, @@ -3155,7 +3160,7 @@ class N3VLIntSL16<bit op24, bits<2> op21_20, bits<4> op11_8, NVMulSLFrm, itin, OpcodeStr, Dt, "$Vd, $Vn, $Vm$lane", "", [(set (ResTy QPR:$Vd), (ResTy (IntOp (OpTy DPR:$Vn), - (OpTy (NEONvduplane (OpTy DPR_8:$Vm), + (OpTy (ARMvduplane (OpTy DPR_8:$Vm), imm:$lane)))))]>; // Wide 3-register operations. @@ -4087,72 +4092,72 @@ multiclass N2VShInsL_QHSD<bit op24, bit op23, bits<4> op11_8, bit op4, string OpcodeStr> { // 64-bit vector types. def v8i8 : N2VDShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "8", v8i8, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "8", v8i8, NEONvsliImm> { let Inst{21-19} = 0b001; // imm6 = 001xxx } def v4i16 : N2VDShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "16", v4i16, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "16", v4i16, NEONvsliImm> { let Inst{21-20} = 0b01; // imm6 = 01xxxx } def v2i32 : N2VDShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "32", v2i32, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "32", v2i32, NEONvsliImm> { let Inst{21} = 0b1; // imm6 = 1xxxxx } def v1i64 : N2VDShIns<op24, op23, op11_8, 1, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "64", v1i64, NEONvsli>; + N2RegVShLFrm, OpcodeStr, "64", v1i64, NEONvsliImm>; // imm6 = xxxxxx // 128-bit vector types. def v16i8 : N2VQShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "8", v16i8, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "8", v16i8, NEONvsliImm> { let Inst{21-19} = 0b001; // imm6 = 001xxx } def v8i16 : N2VQShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "16", v8i16, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "16", v8i16, NEONvsliImm> { let Inst{21-20} = 0b01; // imm6 = 01xxxx } def v4i32 : N2VQShIns<op24, op23, op11_8, 0, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "32", v4i32, NEONvsli> { + N2RegVShLFrm, OpcodeStr, "32", v4i32, NEONvsliImm> { let Inst{21} = 0b1; // imm6 = 1xxxxx } def v2i64 : N2VQShIns<op24, op23, op11_8, 1, op4, i32imm, - N2RegVShLFrm, OpcodeStr, "64", v2i64, NEONvsli>; + N2RegVShLFrm, OpcodeStr, "64", v2i64, NEONvsliImm>; // imm6 = xxxxxx } multiclass N2VShInsR_QHSD<bit op24, bit op23, bits<4> op11_8, bit op4, string OpcodeStr> { // 64-bit vector types. def v8i8 : N2VDShIns<op24, op23, op11_8, 0, op4, shr_imm8, - N2RegVShRFrm, OpcodeStr, "8", v8i8, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "8", v8i8, NEONvsriImm> { let Inst{21-19} = 0b001; // imm6 = 001xxx } def v4i16 : N2VDShIns<op24, op23, op11_8, 0, op4, shr_imm16, - N2RegVShRFrm, OpcodeStr, "16", v4i16, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "16", v4i16, NEONvsriImm> { let Inst{21-20} = 0b01; // imm6 = 01xxxx } def v2i32 : N2VDShIns<op24, op23, op11_8, 0, op4, shr_imm32, - N2RegVShRFrm, OpcodeStr, "32", v2i32, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "32", v2i32, NEONvsriImm> { let Inst{21} = 0b1; // imm6 = 1xxxxx } def v1i64 : N2VDShIns<op24, op23, op11_8, 1, op4, shr_imm64, - N2RegVShRFrm, OpcodeStr, "64", v1i64, NEONvsri>; + N2RegVShRFrm, OpcodeStr, "64", v1i64, NEONvsriImm>; // imm6 = xxxxxx // 128-bit vector types. def v16i8 : N2VQShIns<op24, op23, op11_8, 0, op4, shr_imm8, - N2RegVShRFrm, OpcodeStr, "8", v16i8, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "8", v16i8, NEONvsriImm> { let Inst{21-19} = 0b001; // imm6 = 001xxx } def v8i16 : N2VQShIns<op24, op23, op11_8, 0, op4, shr_imm16, - N2RegVShRFrm, OpcodeStr, "16", v8i16, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "16", v8i16, NEONvsriImm> { let Inst{21-20} = 0b01; // imm6 = 01xxxx } def v4i32 : N2VQShIns<op24, op23, op11_8, 0, op4, shr_imm32, - N2RegVShRFrm, OpcodeStr, "32", v4i32, NEONvsri> { + N2RegVShRFrm, OpcodeStr, "32", v4i32, NEONvsriImm> { let Inst{21} = 0b1; // imm6 = 1xxxxx } def v2i64 : N2VQShIns<op24, op23, op11_8, 1, op4, shr_imm64, - N2RegVShRFrm, OpcodeStr, "64", v2i64, NEONvsri>; + N2RegVShRFrm, OpcodeStr, "64", v2i64, NEONvsriImm>; // imm6 = xxxxxx } @@ -4251,12 +4256,14 @@ defm VADDHN : N3VNInt_HSD<0,1,0b0100,0, "vaddhn", "i", null_frag, 1>; defm VRADDHN : N3VNInt_HSD<1,1,0b0100,0, "vraddhn", "i", int_arm_neon_vraddhn, 1>; -def : Pat<(v8i8 (trunc (NEONvshru (add (v8i16 QPR:$Vn), QPR:$Vm), 8))), +let Predicates = [HasNEON] in { +def : Pat<(v8i8 (trunc (ARMvshruImm (add (v8i16 QPR:$Vn), QPR:$Vm), 8))), (VADDHNv8i8 QPR:$Vn, QPR:$Vm)>; -def : Pat<(v4i16 (trunc (NEONvshru (add (v4i32 QPR:$Vn), QPR:$Vm), 16))), +def : Pat<(v4i16 (trunc (ARMvshruImm (add (v4i32 QPR:$Vn), QPR:$Vm), 16))), (VADDHNv4i16 QPR:$Vn, QPR:$Vm)>; -def : Pat<(v2i32 (trunc (NEONvshru (add (v2i64 QPR:$Vn), QPR:$Vm), 32))), +def : Pat<(v2i32 (trunc (ARMvshruImm (add (v2i64 QPR:$Vn), QPR:$Vm), 32))), (VADDHNv2i32 QPR:$Vn, QPR:$Vm)>; +} // Vector Multiply Operations. @@ -4287,47 +4294,49 @@ def VMULslhq : N3VQSL16<0b01, 0b1001, "vmul", "f16", v8f16, v4f16, fmul>, Requires<[HasNEON,HasFullFP16]>; +let Predicates = [HasNEON] in { def : Pat<(v8i16 (mul (v8i16 QPR:$src1), - (v8i16 (NEONvduplane (v8i16 QPR:$src2), imm:$lane)))), + (v8i16 (ARMvduplane (v8i16 QPR:$src2), imm:$lane)))), (v8i16 (VMULslv8i16 (v8i16 QPR:$src1), (v4i16 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; def : Pat<(v4i32 (mul (v4i32 QPR:$src1), - (v4i32 (NEONvduplane (v4i32 QPR:$src2), imm:$lane)))), + (v4i32 (ARMvduplane (v4i32 QPR:$src2), imm:$lane)))), (v4i32 (VMULslv4i32 (v4i32 QPR:$src1), (v2i32 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; def : Pat<(v4f32 (fmul (v4f32 QPR:$src1), - (v4f32 (NEONvduplane (v4f32 QPR:$src2), imm:$lane)))), + (v4f32 (ARMvduplane (v4f32 QPR:$src2), imm:$lane)))), (v4f32 (VMULslfq (v4f32 QPR:$src1), (v2f32 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; def : Pat<(v8f16 (fmul (v8f16 QPR:$src1), - (v8f16 (NEONvduplane (v8f16 QPR:$src2), imm:$lane)))), + (v8f16 (ARMvduplane (v8f16 QPR:$src2), imm:$lane)))), (v8f16 (VMULslhq(v8f16 QPR:$src1), (v4f16 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; -def : Pat<(v2f32 (fmul DPR:$Rn, (NEONvdup (f32 SPR:$Rm)))), +def : Pat<(v2f32 (fmul DPR:$Rn, (ARMvdup (f32 SPR:$Rm)))), (VMULslfd DPR:$Rn, (INSERT_SUBREG (v2f32 (IMPLICIT_DEF)), SPR:$Rm, ssub_0), (i32 0))>; -def : Pat<(v4f16 (fmul DPR:$Rn, (NEONvdup (f16 HPR:$Rm)))), +def : Pat<(v4f16 (fmul DPR:$Rn, (ARMvdup (f16 HPR:$Rm)))), (VMULslhd DPR:$Rn, (INSERT_SUBREG (v4f16 (IMPLICIT_DEF)), HPR:$Rm, ssub_0), (i32 0))>; -def : Pat<(v4f32 (fmul QPR:$Rn, (NEONvdup (f32 SPR:$Rm)))), +def : Pat<(v4f32 (fmul QPR:$Rn, (ARMvdup (f32 SPR:$Rm)))), (VMULslfq QPR:$Rn, (INSERT_SUBREG (v2f32 (IMPLICIT_DEF)), SPR:$Rm, ssub_0), (i32 0))>; -def : Pat<(v8f16 (fmul QPR:$Rn, (NEONvdup (f16 HPR:$Rm)))), +def : Pat<(v8f16 (fmul QPR:$Rn, (ARMvdup (f16 HPR:$Rm)))), (VMULslhq QPR:$Rn, (INSERT_SUBREG (v4f16 (IMPLICIT_DEF)), HPR:$Rm, ssub_0), (i32 0))>; +} // VQDMULH : Vector Saturating Doubling Multiply Returning High Half defm VQDMULH : N3VInt_HS<0, 0, 0b1011, 0, N3RegFrm, IIC_VMULi16D, IIC_VMULi32D, @@ -4336,20 +4345,23 @@ defm VQDMULH : N3VInt_HS<0, 0, 0b1011, 0, N3RegFrm, IIC_VMULi16D, IIC_VMULi32D, defm VQDMULHsl: N3VIntSL_HS<0b1100, IIC_VMULi16D, IIC_VMULi32D, IIC_VMULi16Q, IIC_VMULi32Q, "vqdmulh", "s", int_arm_neon_vqdmulh>; + +let Predicates = [HasNEON] in { def : Pat<(v8i16 (int_arm_neon_vqdmulh (v8i16 QPR:$src1), - (v8i16 (NEONvduplane (v8i16 QPR:$src2), + (v8i16 (ARMvduplane (v8i16 QPR:$src2), imm:$lane)))), (v8i16 (VQDMULHslv8i16 (v8i16 QPR:$src1), (v4i16 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; def : Pat<(v4i32 (int_arm_neon_vqdmulh (v4i32 QPR:$src1), - (v4i32 (NEONvduplane (v4i32 QPR:$src2), + (v4i32 (ARMvduplane (v4i32 QPR:$src2), imm:$lane)))), (v4i32 (VQDMULHslv4i32 (v4i32 QPR:$src1), (v2i32 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; +} // VQRDMULH : Vector Rounding Saturating Doubling Multiply Returning High Half defm VQRDMULH : N3VInt_HS<1, 0, 0b1011, 0, N3RegFrm, @@ -4358,20 +4370,23 @@ defm VQRDMULH : N3VInt_HS<1, 0, 0b1011, 0, N3RegFrm, defm VQRDMULHsl : N3VIntSL_HS<0b1101, IIC_VMULi16D, IIC_VMULi32D, IIC_VMULi16Q, IIC_VMULi32Q, "vqrdmulh", "s", int_arm_neon_vqrdmulh>; + +let Predicates = [HasNEON] in { def : Pat<(v8i16 (int_arm_neon_vqrdmulh (v8i16 QPR:$src1), - (v8i16 (NEONvduplane (v8i16 QPR:$src2), + (v8i16 (ARMvduplane (v8i16 QPR:$src2), imm:$lane)))), (v8i16 (VQRDMULHslv8i16 (v8i16 QPR:$src1), (v4i16 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; def : Pat<(v4i32 (int_arm_neon_vqrdmulh (v4i32 QPR:$src1), - (v4i32 (NEONvduplane (v4i32 QPR:$src2), + (v4i32 (ARMvduplane (v4i32 QPR:$src2), imm:$lane)))), (v4i32 (VQRDMULHslv4i32 (v4i32 QPR:$src1), (v2i32 (EXTRACT_SUBREG QPR:$src2, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; +} // VMULL : Vector Multiply Long (integer and polynomial) (Q = D * D) let PostEncoderMethod = "NEONThumb2DataIPostEncoder", @@ -4427,9 +4442,10 @@ def VMLAslhq : N3VQMulOpSL16<0b01, 0b0001, IIC_VMACQ, "vmla", "f16", v8f16, v4f16, fmul, fadd>, Requires<[HasNEON, HasFullFP16, UseFPVMLx]>; +let Predicates = [HasNEON] in { def : Pat<(v8i16 (add (v8i16 QPR:$src1), (mul (v8i16 QPR:$src2), - (v8i16 (NEONvduplane (v8i16 QPR:$src3), imm:$lane))))), + (v8i16 (ARMvduplane (v8i16 QPR:$src3), imm:$lane))))), (v8i16 (VMLAslv8i16 (v8i16 QPR:$src1), (v8i16 QPR:$src2), (v4i16 (EXTRACT_SUBREG QPR:$src3, (DSubReg_i16_reg imm:$lane))), @@ -4437,15 +4453,16 @@ def : Pat<(v8i16 (add (v8i16 QPR:$src1), def : Pat<(v4i32 (add (v4i32 QPR:$src1), (mul (v4i32 QPR:$src2), - (v4i32 (NEONvduplane (v4i32 QPR:$src3), imm:$lane))))), + (v4i32 (ARMvduplane (v4i32 QPR:$src3), imm:$lane))))), (v4i32 (VMLAslv4i32 (v4i32 QPR:$src1), (v4i32 QPR:$src2), (v2i32 (EXTRACT_SUBREG QPR:$src3, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; +} def : Pat<(v4f32 (fadd_mlx (v4f32 QPR:$src1), (fmul_su (v4f32 QPR:$src2), - (v4f32 (NEONvduplane (v4f32 QPR:$src3), imm:$lane))))), + (v4f32 (ARMvduplane (v4f32 QPR:$src3), imm:$lane))))), (v4f32 (VMLAslfq (v4f32 QPR:$src1), (v4f32 QPR:$src2), (v2f32 (EXTRACT_SUBREG QPR:$src3, @@ -4497,7 +4514,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v4i16 DPR:$src1), (v4i16 (int_arm_neon_vqrdmulh (v4i16 DPR:$Vn), - (v4i16 (NEONvduplane (v4i16 DPR_8:$Vm), + (v4i16 (ARMvduplane (v4i16 DPR_8:$Vm), imm:$lane)))))), (v4i16 (VQRDMLAHslv4i16 DPR:$src1, DPR:$Vn, DPR_8:$Vm, imm:$lane))>; @@ -4505,7 +4522,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v2i32 DPR:$src1), (v2i32 (int_arm_neon_vqrdmulh (v2i32 DPR:$Vn), - (v2i32 (NEONvduplane (v2i32 DPR_VFP2:$Vm), + (v2i32 (ARMvduplane (v2i32 DPR_VFP2:$Vm), imm:$lane)))))), (v2i32 (VQRDMLAHslv2i32 DPR:$src1, DPR:$Vn, DPR_VFP2:$Vm, imm:$lane))>; @@ -4513,7 +4530,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v8i16 QPR:$src1), (v8i16 (int_arm_neon_vqrdmulh (v8i16 QPR:$src2), - (v8i16 (NEONvduplane (v8i16 QPR:$src3), + (v8i16 (ARMvduplane (v8i16 QPR:$src3), imm:$lane)))))), (v8i16 (VQRDMLAHslv8i16 (v8i16 QPR:$src1), (v8i16 QPR:$src2), @@ -4525,7 +4542,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqrdmulh (v4i32 QPR:$src2), - (v4i32 (NEONvduplane (v4i32 QPR:$src3), + (v4i32 (ARMvduplane (v4i32 QPR:$src3), imm:$lane)))))), (v4i32 (VQRDMLAHslv4i32 (v4i32 QPR:$src1), (v4i32 QPR:$src2), @@ -4567,14 +4584,14 @@ let Predicates = [HasNEON, HasV8_1a] in { (v4i16 DPR:$src1), (v4i16 (int_arm_neon_vqrdmulh (v4i16 DPR:$Vn), - (v4i16 (NEONvduplane (v4i16 DPR_8:$Vm), + (v4i16 (ARMvduplane (v4i16 DPR_8:$Vm), imm:$lane)))))), (v4i16 (VQRDMLSHslv4i16 DPR:$src1, DPR:$Vn, DPR_8:$Vm, imm:$lane))>; def : Pat<(v2i32 (int_arm_neon_vqsubs (v2i32 DPR:$src1), (v2i32 (int_arm_neon_vqrdmulh (v2i32 DPR:$Vn), - (v2i32 (NEONvduplane (v2i32 DPR_VFP2:$Vm), + (v2i32 (ARMvduplane (v2i32 DPR_VFP2:$Vm), imm:$lane)))))), (v2i32 (VQRDMLSHslv2i32 DPR:$src1, DPR:$Vn, DPR_VFP2:$Vm, imm:$lane))>; @@ -4582,7 +4599,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v8i16 QPR:$src1), (v8i16 (int_arm_neon_vqrdmulh (v8i16 QPR:$src2), - (v8i16 (NEONvduplane (v8i16 QPR:$src3), + (v8i16 (ARMvduplane (v8i16 QPR:$src3), imm:$lane)))))), (v8i16 (VQRDMLSHslv8i16 (v8i16 QPR:$src1), (v8i16 QPR:$src2), @@ -4594,7 +4611,7 @@ let Predicates = [HasNEON, HasV8_1a] in { (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqrdmulh (v4i32 QPR:$src2), - (v4i32 (NEONvduplane (v4i32 QPR:$src3), + (v4i32 (ARMvduplane (v4i32 QPR:$src3), imm:$lane)))))), (v4i32 (VQRDMLSHslv4i32 (v4i32 QPR:$src1), (v4i32 QPR:$src2), @@ -4608,6 +4625,7 @@ defm VQDMLAL : N3VLInt3_HS<0, 1, 0b1001, 0, IIC_VMACi16D, IIC_VMACi32D, "vqdmlal", "s", null_frag>; defm VQDMLALsl: N3VLInt3SL_HS<0, 0b0011, "vqdmlal", "s", null_frag>; +let Predicates = [HasNEON] in { def : Pat<(v4i32 (int_arm_neon_vqadds (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqdmull (v4i16 DPR:$Vn), (v4i16 DPR:$Vm))))), @@ -4618,14 +4636,15 @@ def : Pat<(v2i64 (int_arm_neon_vqadds (v2i64 QPR:$src1), (VQDMLALv2i64 QPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v4i32 (int_arm_neon_vqadds (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqdmull (v4i16 DPR:$Vn), - (v4i16 (NEONvduplane (v4i16 DPR_8:$Vm), + (v4i16 (ARMvduplane (v4i16 DPR_8:$Vm), imm:$lane)))))), (VQDMLALslv4i16 QPR:$src1, DPR:$Vn, DPR_8:$Vm, imm:$lane)>; def : Pat<(v2i64 (int_arm_neon_vqadds (v2i64 QPR:$src1), (v2i64 (int_arm_neon_vqdmull (v2i32 DPR:$Vn), - (v2i32 (NEONvduplane (v2i32 DPR_VFP2:$Vm), + (v2i32 (ARMvduplane (v2i32 DPR_VFP2:$Vm), imm:$lane)))))), (VQDMLALslv2i32 QPR:$src1, DPR:$Vn, DPR_VFP2:$Vm, imm:$lane)>; +} // VMLS : Vector Multiply Subtract (integer and floating-point) defm VMLS : N3VMulOp_QHS<1, 0, 0b1001, 0, IIC_VMACi16D, IIC_VMACi32D, @@ -4657,9 +4676,10 @@ def VMLSslhq : N3VQMulOpSL16<0b01, 0b0101, IIC_VMACQ, "vmls", "f16", v8f16, v4f16, fmul, fsub>, Requires<[HasNEON, HasFullFP16, UseFPVMLx]>; +let Predicates = [HasNEON] in { def : Pat<(v8i16 (sub (v8i16 QPR:$src1), (mul (v8i16 QPR:$src2), - (v8i16 (NEONvduplane (v8i16 QPR:$src3), imm:$lane))))), + (v8i16 (ARMvduplane (v8i16 QPR:$src3), imm:$lane))))), (v8i16 (VMLSslv8i16 (v8i16 QPR:$src1), (v8i16 QPR:$src2), (v4i16 (EXTRACT_SUBREG QPR:$src3, (DSubReg_i16_reg imm:$lane))), @@ -4667,15 +4687,16 @@ def : Pat<(v8i16 (sub (v8i16 QPR:$src1), def : Pat<(v4i32 (sub (v4i32 QPR:$src1), (mul (v4i32 QPR:$src2), - (v4i32 (NEONvduplane (v4i32 QPR:$src3), imm:$lane))))), + (v4i32 (ARMvduplane (v4i32 QPR:$src3), imm:$lane))))), (v4i32 (VMLSslv4i32 (v4i32 QPR:$src1), (v4i32 QPR:$src2), (v2i32 (EXTRACT_SUBREG QPR:$src3, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; +} def : Pat<(v4f32 (fsub_mlx (v4f32 QPR:$src1), (fmul_su (v4f32 QPR:$src2), - (v4f32 (NEONvduplane (v4f32 QPR:$src3), imm:$lane))))), + (v4f32 (ARMvduplane (v4f32 QPR:$src3), imm:$lane))))), (v4f32 (VMLSslfq (v4f32 QPR:$src1), (v4f32 QPR:$src2), (v2f32 (EXTRACT_SUBREG QPR:$src3, (DSubReg_i32_reg imm:$lane))), @@ -4696,6 +4717,7 @@ defm VQDMLSL : N3VLInt3_HS<0, 1, 0b1011, 0, IIC_VMACi16D, IIC_VMACi32D, "vqdmlsl", "s", null_frag>; defm VQDMLSLsl: N3VLInt3SL_HS<0, 0b0111, "vqdmlsl", "s", null_frag>; +let Predicates = [HasNEON] in { def : Pat<(v4i32 (int_arm_neon_vqsubs (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqdmull (v4i16 DPR:$Vn), (v4i16 DPR:$Vm))))), @@ -4706,14 +4728,15 @@ def : Pat<(v2i64 (int_arm_neon_vqsubs (v2i64 QPR:$src1), (VQDMLSLv2i64 QPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v4i32 (int_arm_neon_vqsubs (v4i32 QPR:$src1), (v4i32 (int_arm_neon_vqdmull (v4i16 DPR:$Vn), - (v4i16 (NEONvduplane (v4i16 DPR_8:$Vm), + (v4i16 (ARMvduplane (v4i16 DPR_8:$Vm), imm:$lane)))))), (VQDMLSLslv4i16 QPR:$src1, DPR:$Vn, DPR_8:$Vm, imm:$lane)>; def : Pat<(v2i64 (int_arm_neon_vqsubs (v2i64 QPR:$src1), (v2i64 (int_arm_neon_vqdmull (v2i32 DPR:$Vn), - (v2i32 (NEONvduplane (v2i32 DPR_VFP2:$Vm), + (v2i32 (ARMvduplane (v2i32 DPR_VFP2:$Vm), imm:$lane)))))), (VQDMLSLslv2i32 QPR:$src1, DPR:$Vn, DPR_VFP2:$Vm, imm:$lane)>; +} // Fused Vector Multiply-Accumulate and Fused Multiply-Subtract Operations. def VFMAfd : N3VDMulOp<0, 0, 0b00, 0b1100, 1, IIC_VFMACD, "vfma", "f32", @@ -4754,16 +4777,16 @@ def : Pat<(v8f16 (fma QPR:$Vn, QPR:$Vm, QPR:$src1)), Requires<[HasNEON,HasFullFP16]>; def : Pat<(v2f32 (fma DPR:$Vn, DPR:$Vm, DPR:$src1)), (VFMAfd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasVFP4]>; + Requires<[HasNEON,HasVFP4]>; def : Pat<(v4f32 (fma QPR:$Vn, QPR:$Vm, QPR:$src1)), (VFMAfq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasVFP4]>; + Requires<[HasNEON,HasVFP4]>; def : Pat<(v2f32 (fma (fneg DPR:$Vn), DPR:$Vm, DPR:$src1)), (VFMSfd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasVFP4]>; + Requires<[HasNEON,HasVFP4]>; def : Pat<(v4f32 (fma (fneg QPR:$Vn), QPR:$Vm, QPR:$src1)), (VFMSfq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasVFP4]>; + Requires<[HasNEON,HasVFP4]>; // ARMv8.2a dot product instructions. // We put them in the VFPV8 decoder namespace because the ARM and Thumb @@ -4808,7 +4831,7 @@ multiclass DOTI<string opc, string dt, bit Q, bit U, RegisterClass Ty, (AccumType (OpNode (AccumType Ty:$Vd), (InputType Ty:$Vn), (InputType (bitconvert (AccumType - (NEONvduplane (AccumType Ty:$Vm), + (ARMvduplane (AccumType Ty:$Vm), VectorIndex32:$lane)))))), (!cast<Instruction>(NAME) Ty:$Vd, Ty:$Vn, RHS, VectorIndex32:$lane)>; } @@ -4991,12 +5014,14 @@ defm VSUBHN : N3VNInt_HSD<0,1,0b0110,0, "vsubhn", "i", null_frag, 0>; defm VRSUBHN : N3VNInt_HSD<1,1,0b0110,0, "vrsubhn", "i", int_arm_neon_vrsubhn, 0>; -def : Pat<(v8i8 (trunc (NEONvshru (sub (v8i16 QPR:$Vn), QPR:$Vm), 8))), +let Predicates = [HasNEON] in { +def : Pat<(v8i8 (trunc (ARMvshruImm (sub (v8i16 QPR:$Vn), QPR:$Vm), 8))), (VSUBHNv8i8 QPR:$Vn, QPR:$Vm)>; -def : Pat<(v4i16 (trunc (NEONvshru (sub (v4i32 QPR:$Vn), QPR:$Vm), 16))), +def : Pat<(v4i16 (trunc (ARMvshruImm (sub (v4i32 QPR:$Vn), QPR:$Vm), 16))), (VSUBHNv4i16 QPR:$Vn, QPR:$Vm)>; -def : Pat<(v2i32 (trunc (NEONvshru (sub (v2i64 QPR:$Vn), QPR:$Vm), 32))), +def : Pat<(v2i32 (trunc (ARMvshruImm (sub (v2i64 QPR:$Vn), QPR:$Vm), 32))), (VSUBHNv2i32 QPR:$Vn, QPR:$Vm)>; +} // Vector Comparisons. @@ -5122,10 +5147,11 @@ class N3VCP8F16Q0<string asm, RegisterClass Td, RegisterClass Tn, : N3VCP8Q0<op1, op2, 0, op3, (outs Td:$Vd), (ins Tn:$Vn, Tm:$Vm), NoItinerary, asm, "f16", "$Vd, $Vn, $Vm", "", []>; -class VFMQ0<string opc, bits<2> S> +// Vd, Vs, Vs[0-15], Idx[0-1] +class VFMD<string opc, string type, bits<2> S> : N3VLaneCP8<0, S, 0, 1, (outs DPR:$Vd), - (ins SPR:$Vn, SPR:$Vm, VectorIndex32:$idx), - IIC_VMACD, opc, "f16", "$Vd, $Vn, $Vm$idx", "", []> { + (ins SPR:$Vn, SPR_8:$Vm, VectorIndex32:$idx), + IIC_VMACD, opc, type, "$Vd, $Vn, $Vm$idx", "", []> { bit idx; let Inst{3} = idx; let Inst{19-16} = Vn{4-1}; @@ -5134,10 +5160,11 @@ class VFMQ0<string opc, bits<2> S> let Inst{2-0} = Vm{3-1}; } -class VFMQ1<string opc, bits<2> S> +// Vq, Vd, Vd[0-7], Idx[0-3] +class VFMQ<string opc, string type, bits<2> S> : N3VLaneCP8<0, S, 1, 1, (outs QPR:$Vd), - (ins DPR:$Vn, DPR:$Vm, VectorIndex16:$idx), - IIC_VMACD, opc, "f16", "$Vd, $Vn, $Vm$idx", "", []> { + (ins DPR:$Vn, DPR_8:$Vm, VectorIndex16:$idx), + IIC_VMACD, opc, type, "$Vd, $Vn, $Vm$idx", "", []> { bits<2> idx; let Inst{5} = idx{1}; let Inst{3} = idx{0}; @@ -5149,10 +5176,10 @@ def VFMALD : N3VCP8F16Q0<"vfmal", DPR, SPR, SPR, 0b00, 0b10, 1>; def VFMSLD : N3VCP8F16Q0<"vfmsl", DPR, SPR, SPR, 0b01, 0b10, 1>; def VFMALQ : N3VCP8F16Q1<"vfmal", QPR, DPR, DPR, 0b00, 0b10, 1>; def VFMSLQ : N3VCP8F16Q1<"vfmsl", QPR, DPR, DPR, 0b01, 0b10, 1>; -def VFMALDI : VFMQ0<"vfmal", 0b00>; -def VFMSLDI : VFMQ0<"vfmsl", 0b01>; -def VFMALQI : VFMQ1<"vfmal", 0b00>; -def VFMSLQI : VFMQ1<"vfmsl", 0b01>; +def VFMALDI : VFMD<"vfmal", "f16", 0b00>; +def VFMSLDI : VFMD<"vfmsl", "f16", 0b01>; +def VFMALQI : VFMQ<"vfmal", "f16", 0b00>; +def VFMSLQI : VFMQ<"vfmsl", "f16", 0b01>; } } // HasNEON, HasFP16FML @@ -5308,28 +5335,28 @@ let isReMaterializable = 1 in { def VMVNv4i16 : N1ModImm<1, 0b000, {1,0,?,0}, 0, 0, 1, 1, (outs DPR:$Vd), (ins nImmSplatI16:$SIMM), IIC_VMOVImm, "vmvn", "i16", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v4i16 (NEONvmvnImm timm:$SIMM)))]> { + [(set DPR:$Vd, (v4i16 (ARMvmvnImm timm:$SIMM)))]> { let Inst{9} = SIMM{9}; } def VMVNv8i16 : N1ModImm<1, 0b000, {1,0,?,0}, 0, 1, 1, 1, (outs QPR:$Vd), (ins nImmSplatI16:$SIMM), IIC_VMOVImm, "vmvn", "i16", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v8i16 (NEONvmvnImm timm:$SIMM)))]> { + [(set QPR:$Vd, (v8i16 (ARMvmvnImm timm:$SIMM)))]> { let Inst{9} = SIMM{9}; } def VMVNv2i32 : N1ModImm<1, 0b000, {?,?,?,?}, 0, 0, 1, 1, (outs DPR:$Vd), (ins nImmVMOVI32:$SIMM), IIC_VMOVImm, "vmvn", "i32", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v2i32 (NEONvmvnImm timm:$SIMM)))]> { + [(set DPR:$Vd, (v2i32 (ARMvmvnImm timm:$SIMM)))]> { let Inst{11-8} = SIMM{11-8}; } def VMVNv4i32 : N1ModImm<1, 0b000, {?,?,?,?}, 0, 1, 1, 1, (outs QPR:$Vd), (ins nImmVMOVI32:$SIMM), IIC_VMOVImm, "vmvn", "i32", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v4i32 (NEONvmvnImm timm:$SIMM)))]> { + [(set QPR:$Vd, (v4i32 (ARMvmvnImm timm:$SIMM)))]> { let Inst{11-8} = SIMM{11-8}; } } @@ -5343,8 +5370,10 @@ def VMVNq : N2VX<0b11, 0b11, 0b00, 0b00, 0b01011, 1, 0, (outs QPR:$Vd), (ins QPR:$Vm), IIC_VSUBiD, "vmvn", "$Vd, $Vm", "", [(set QPR:$Vd, (v4i32 (vnotq QPR:$Vm)))]>; +let Predicates = [HasNEON] in { def : Pat<(v2i32 (vnotd DPR:$src)), (VMVNd DPR:$src)>; def : Pat<(v4i32 (vnotq QPR:$src)), (VMVNq QPR:$src)>; +} // VBSL : Vector Bitwise Select def VBSLd : N3VX<1, 0, 0b01, 0b0001, 0, 1, (outs DPR:$Vd), @@ -5353,36 +5382,31 @@ def VBSLd : N3VX<1, 0, 0b01, 0b0001, 0, 1, (outs DPR:$Vd), "vbsl", "$Vd, $Vn, $Vm", "$src1 = $Vd", [(set DPR:$Vd, (v2i32 (NEONvbsl DPR:$src1, DPR:$Vn, DPR:$Vm)))]>; +let Predicates = [HasNEON] in { def : Pat<(v8i8 (int_arm_neon_vbsl (v8i8 DPR:$src1), (v8i8 DPR:$Vn), (v8i8 DPR:$Vm))), - (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v4i16 (int_arm_neon_vbsl (v4i16 DPR:$src1), (v4i16 DPR:$Vn), (v4i16 DPR:$Vm))), - (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v2i32 (int_arm_neon_vbsl (v2i32 DPR:$src1), (v2i32 DPR:$Vn), (v2i32 DPR:$Vm))), - (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v2f32 (int_arm_neon_vbsl (v2f32 DPR:$src1), (v2f32 DPR:$Vn), (v2f32 DPR:$Vm))), - (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v1i64 (int_arm_neon_vbsl (v1i64 DPR:$src1), (v1i64 DPR:$Vn), (v1i64 DPR:$Vm))), - (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$src1, DPR:$Vn, DPR:$Vm)>; def : Pat<(v2i32 (or (and DPR:$Vn, DPR:$Vd), (and DPR:$Vm, (vnotd DPR:$Vd)))), - (VBSLd DPR:$Vd, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$Vd, DPR:$Vn, DPR:$Vm)>; def : Pat<(v1i64 (or (and DPR:$Vn, DPR:$Vd), (and DPR:$Vm, (vnotd DPR:$Vd)))), - (VBSLd DPR:$Vd, DPR:$Vn, DPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLd DPR:$Vd, DPR:$Vn, DPR:$Vm)>; +} def VBSLq : N3VX<1, 0, 0b01, 0b0001, 1, 1, (outs QPR:$Vd), (ins QPR:$src1, QPR:$Vn, QPR:$Vm), @@ -5391,35 +5415,30 @@ def VBSLq : N3VX<1, 0, 0b01, 0b0001, 1, 1, (outs QPR:$Vd), [(set QPR:$Vd, (v4i32 (NEONvbsl QPR:$src1, QPR:$Vn, QPR:$Vm)))]>; +let Predicates = [HasNEON] in { def : Pat<(v16i8 (int_arm_neon_vbsl (v16i8 QPR:$src1), (v16i8 QPR:$Vn), (v16i8 QPR:$Vm))), - (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>; def : Pat<(v8i16 (int_arm_neon_vbsl (v8i16 QPR:$src1), (v8i16 QPR:$Vn), (v8i16 QPR:$Vm))), - (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>; def : Pat<(v4i32 (int_arm_neon_vbsl (v4i32 QPR:$src1), (v4i32 QPR:$Vn), (v4i32 QPR:$Vm))), - (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>; def : Pat<(v4f32 (int_arm_neon_vbsl (v4f32 QPR:$src1), (v4f32 QPR:$Vn), (v4f32 QPR:$Vm))), - (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>; def : Pat<(v2i64 (int_arm_neon_vbsl (v2i64 QPR:$src1), (v2i64 QPR:$Vn), (v2i64 QPR:$Vm))), - (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$src1, QPR:$Vn, QPR:$Vm)>; def : Pat<(v4i32 (or (and QPR:$Vn, QPR:$Vd), (and QPR:$Vm, (vnotq QPR:$Vd)))), - (VBSLq QPR:$Vd, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$Vd, QPR:$Vn, QPR:$Vm)>; def : Pat<(v2i64 (or (and QPR:$Vn, QPR:$Vd), (and QPR:$Vm, (vnotq QPR:$Vd)))), - (VBSLq QPR:$Vd, QPR:$Vn, QPR:$Vm)>, - Requires<[HasNEON]>; + (VBSLq QPR:$Vd, QPR:$Vn, QPR:$Vm)>; +} // VBIF : Vector Bitwise Insert if False // like VBSL but with: "vbif $dst, $src3, $src1", "$src2 = $dst", @@ -5479,24 +5498,28 @@ defm VABDLs : N3VLIntExt_QHS<0,1,0b0111,0, IIC_VSUBi4Q, defm VABDLu : N3VLIntExt_QHS<1,1,0b0111,0, IIC_VSUBi4Q, "vabdl", "u", int_arm_neon_vabdu, zext, 1>; +let Predicates = [HasNEON] in { def : Pat<(v8i16 (abs (sub (zext (v8i8 DPR:$opA)), (zext (v8i8 DPR:$opB))))), (VABDLuv8i16 DPR:$opA, DPR:$opB)>; def : Pat<(v4i32 (abs (sub (zext (v4i16 DPR:$opA)), (zext (v4i16 DPR:$opB))))), (VABDLuv4i32 DPR:$opA, DPR:$opB)>; +} // ISD::ABS is not legal for v2i64, so VABDL needs to be matched from the // shift/xor pattern for ABS. def abd_shr : PatFrag<(ops node:$in1, node:$in2, node:$shift), - (NEONvshrs (sub (zext node:$in1), + (ARMvshrsImm (sub (zext node:$in1), (zext node:$in2)), (i32 $shift))>; +let Predicates = [HasNEON] in { def : Pat<(xor (v4i32 (bitconvert (v2i64 (abd_shr (v2i32 DPR:$opA), (v2i32 DPR:$opB), 63)))), (v4i32 (bitconvert (v2i64 (add (sub (zext (v2i32 DPR:$opA)), (zext (v2i32 DPR:$opB))), (abd_shr (v2i32 DPR:$opA), (v2i32 DPR:$opB), 63)))))), (VABDLuv2i64 DPR:$opA, DPR:$opB)>; +} // VABA : Vector Absolute Difference and Accumulate defm VABAs : N3VIntOp_QHS<0,0,0b0111,1, IIC_VABAD, IIC_VABAQ, @@ -5536,22 +5559,22 @@ def VMAXhq : N3VQInt<0, 0, 0b01, 0b1111, 0, N3RegFrm, IIC_VBINQ, // VMAXNM let PostEncoderMethod = "NEONThumb2V8PostEncoder", DecoderNamespace = "v8NEON" in { - def VMAXNMNDf : N3VDIntnp<0b00110, 0b00, 0b1111, 0, 1, - N3RegFrm, NoItinerary, "vmaxnm", "f32", - v2f32, v2f32, fmaxnum, 1>, - Requires<[HasV8, HasNEON]>; - def VMAXNMNQf : N3VQIntnp<0b00110, 0b00, 0b1111, 1, 1, - N3RegFrm, NoItinerary, "vmaxnm", "f32", - v4f32, v4f32, fmaxnum, 1>, - Requires<[HasV8, HasNEON]>; - def VMAXNMNDh : N3VDIntnp<0b00110, 0b01, 0b1111, 0, 1, - N3RegFrm, NoItinerary, "vmaxnm", "f16", - v4f16, v4f16, fmaxnum, 1>, - Requires<[HasV8, HasNEON, HasFullFP16]>; - def VMAXNMNQh : N3VQIntnp<0b00110, 0b01, 0b1111, 1, 1, - N3RegFrm, NoItinerary, "vmaxnm", "f16", - v8f16, v8f16, fmaxnum, 1>, - Requires<[HasV8, HasNEON, HasFullFP16]>; + def NEON_VMAXNMNDf : N3VDIntnp<0b00110, 0b00, 0b1111, 0, 1, + N3RegFrm, NoItinerary, "vmaxnm", "f32", + v2f32, v2f32, fmaxnum, 1>, + Requires<[HasV8, HasNEON]>; + def NEON_VMAXNMNQf : N3VQIntnp<0b00110, 0b00, 0b1111, 1, 1, + N3RegFrm, NoItinerary, "vmaxnm", "f32", + v4f32, v4f32, fmaxnum, 1>, + Requires<[HasV8, HasNEON]>; + def NEON_VMAXNMNDh : N3VDIntnp<0b00110, 0b01, 0b1111, 0, 1, + N3RegFrm, NoItinerary, "vmaxnm", "f16", + v4f16, v4f16, fmaxnum, 1>, + Requires<[HasV8, HasNEON, HasFullFP16]>; + def NEON_VMAXNMNQh : N3VQIntnp<0b00110, 0b01, 0b1111, 1, 1, + N3RegFrm, NoItinerary, "vmaxnm", "f16", + v8f16, v8f16, fmaxnum, 1>, + Requires<[HasV8, HasNEON, HasFullFP16]>; } // VMIN : Vector Minimum @@ -5578,22 +5601,22 @@ def VMINhq : N3VQInt<0, 0, 0b11, 0b1111, 0, N3RegFrm, IIC_VBINQ, // VMINNM let PostEncoderMethod = "NEONThumb2V8PostEncoder", DecoderNamespace = "v8NEON" in { - def VMINNMNDf : N3VDIntnp<0b00110, 0b10, 0b1111, 0, 1, - N3RegFrm, NoItinerary, "vminnm", "f32", - v2f32, v2f32, fminnum, 1>, - Requires<[HasV8, HasNEON]>; - def VMINNMNQf : N3VQIntnp<0b00110, 0b10, 0b1111, 1, 1, - N3RegFrm, NoItinerary, "vminnm", "f32", - v4f32, v4f32, fminnum, 1>, - Requires<[HasV8, HasNEON]>; - def VMINNMNDh : N3VDIntnp<0b00110, 0b11, 0b1111, 0, 1, - N3RegFrm, NoItinerary, "vminnm", "f16", - v4f16, v4f16, fminnum, 1>, - Requires<[HasV8, HasNEON, HasFullFP16]>; - def VMINNMNQh : N3VQIntnp<0b00110, 0b11, 0b1111, 1, 1, - N3RegFrm, NoItinerary, "vminnm", "f16", - v8f16, v8f16, fminnum, 1>, - Requires<[HasV8, HasNEON, HasFullFP16]>; + def NEON_VMINNMNDf : N3VDIntnp<0b00110, 0b10, 0b1111, 0, 1, + N3RegFrm, NoItinerary, "vminnm", "f32", + v2f32, v2f32, fminnum, 1>, + Requires<[HasV8, HasNEON]>; + def NEON_VMINNMNQf : N3VQIntnp<0b00110, 0b10, 0b1111, 1, 1, + N3RegFrm, NoItinerary, "vminnm", "f32", + v4f32, v4f32, fminnum, 1>, + Requires<[HasV8, HasNEON]>; + def NEON_VMINNMNDh : N3VDIntnp<0b00110, 0b11, 0b1111, 0, 1, + N3RegFrm, NoItinerary, "vminnm", "f16", + v4f16, v4f16, fminnum, 1>, + Requires<[HasV8, HasNEON, HasFullFP16]>; + def NEON_VMINNMNQh : N3VQIntnp<0b00110, 0b11, 0b1111, 1, 1, + N3RegFrm, NoItinerary, "vminnm", "f16", + v8f16, v8f16, fminnum, 1>, + Requires<[HasV8, HasNEON, HasFullFP16]>; } // Vector Pairwise Operations. @@ -5754,20 +5777,57 @@ defm VSHLu : N3VInt_QHSDSh<1, 0, 0b0100, 0, N3RegVShFrm, IIC_VSHLiD, IIC_VSHLiD, IIC_VSHLiQ, IIC_VSHLiQ, "vshl", "u", int_arm_neon_vshiftu>; +let Predicates = [HasNEON] in { +def : Pat<(v8i8 (ARMvshls (v8i8 DPR:$Dn), (v8i8 DPR:$Dm))), + (VSHLsv8i8 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v4i16 (ARMvshls (v4i16 DPR:$Dn), (v4i16 DPR:$Dm))), + (VSHLsv4i16 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v2i32 (ARMvshls (v2i32 DPR:$Dn), (v2i32 DPR:$Dm))), + (VSHLsv2i32 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v1i64 (ARMvshls (v1i64 DPR:$Dn), (v1i64 DPR:$Dm))), + (VSHLsv1i64 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v16i8 (ARMvshls (v16i8 QPR:$Dn), (v16i8 QPR:$Dm))), + (VSHLsv16i8 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v8i16 (ARMvshls (v8i16 QPR:$Dn), (v8i16 QPR:$Dm))), + (VSHLsv8i16 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v4i32 (ARMvshls (v4i32 QPR:$Dn), (v4i32 QPR:$Dm))), + (VSHLsv4i32 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v2i64 (ARMvshls (v2i64 QPR:$Dn), (v2i64 QPR:$Dm))), + (VSHLsv2i64 QPR:$Dn, QPR:$Dm)>; + +def : Pat<(v8i8 (ARMvshlu (v8i8 DPR:$Dn), (v8i8 DPR:$Dm))), + (VSHLuv8i8 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v4i16 (ARMvshlu (v4i16 DPR:$Dn), (v4i16 DPR:$Dm))), + (VSHLuv4i16 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v2i32 (ARMvshlu (v2i32 DPR:$Dn), (v2i32 DPR:$Dm))), + (VSHLuv2i32 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v1i64 (ARMvshlu (v1i64 DPR:$Dn), (v1i64 DPR:$Dm))), + (VSHLuv1i64 DPR:$Dn, DPR:$Dm)>; +def : Pat<(v16i8 (ARMvshlu (v16i8 QPR:$Dn), (v16i8 QPR:$Dm))), + (VSHLuv16i8 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v8i16 (ARMvshlu (v8i16 QPR:$Dn), (v8i16 QPR:$Dm))), + (VSHLuv8i16 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v4i32 (ARMvshlu (v4i32 QPR:$Dn), (v4i32 QPR:$Dm))), + (VSHLuv4i32 QPR:$Dn, QPR:$Dm)>; +def : Pat<(v2i64 (ARMvshlu (v2i64 QPR:$Dn), (v2i64 QPR:$Dm))), + (VSHLuv2i64 QPR:$Dn, QPR:$Dm)>; + +} + // VSHL : Vector Shift Left (Immediate) -defm VSHLi : N2VShL_QHSD<0, 1, 0b0101, 1, IIC_VSHLiD, "vshl", "i", NEONvshl>; +defm VSHLi : N2VShL_QHSD<0, 1, 0b0101, 1, IIC_VSHLiD, "vshl", "i", ARMvshlImm>; // VSHR : Vector Shift Right (Immediate) defm VSHRs : N2VShR_QHSD<0, 1, 0b0000, 1, IIC_VSHLiD, "vshr", "s", "VSHRs", - NEONvshrs>; + ARMvshrsImm>; defm VSHRu : N2VShR_QHSD<1, 1, 0b0000, 1, IIC_VSHLiD, "vshr", "u", "VSHRu", - NEONvshru>; + ARMvshruImm>; // VSHLL : Vector Shift Left Long defm VSHLLs : N2VLSh_QHS<0, 1, 0b1010, 0, 0, 1, "vshll", "s", - PatFrag<(ops node:$LHS, node:$RHS), (NEONvshl (sext node:$LHS), node:$RHS)>>; + PatFrag<(ops node:$LHS, node:$RHS), (ARMvshlImm (sext node:$LHS), node:$RHS)>>; defm VSHLLu : N2VLSh_QHS<1, 1, 0b1010, 0, 0, 1, "vshll", "u", - PatFrag<(ops node:$LHS, node:$RHS), (NEONvshl (zext node:$LHS), node:$RHS)>>; + PatFrag<(ops node:$LHS, node:$RHS), (ARMvshlImm (zext node:$LHS), node:$RHS)>>; // VSHLL : Vector Shift Left Long (with maximum shift count) class N2VLShMax<bit op24, bit op23, bits<6> op21_16, bits<4> op11_8, bit op7, @@ -5785,36 +5845,40 @@ def VSHLLi16 : N2VLShMax<1, 1, 0b110110, 0b0011, 0, 0, 0, "vshll", "i16", def VSHLLi32 : N2VLShMax<1, 1, 0b111010, 0b0011, 0, 0, 0, "vshll", "i32", v2i64, v2i32, imm32>; -def : Pat<(v8i16 (NEONvshl (zext (v8i8 DPR:$Rn)), (i32 8))), +let Predicates = [HasNEON] in { +def : Pat<(v8i16 (ARMvshlImm (zext (v8i8 DPR:$Rn)), (i32 8))), (VSHLLi8 DPR:$Rn, 8)>; -def : Pat<(v4i32 (NEONvshl (zext (v4i16 DPR:$Rn)), (i32 16))), +def : Pat<(v4i32 (ARMvshlImm (zext (v4i16 DPR:$Rn)), (i32 16))), (VSHLLi16 DPR:$Rn, 16)>; -def : Pat<(v2i64 (NEONvshl (zext (v2i32 DPR:$Rn)), (i32 32))), +def : Pat<(v2i64 (ARMvshlImm (zext (v2i32 DPR:$Rn)), (i32 32))), (VSHLLi32 DPR:$Rn, 32)>; -def : Pat<(v8i16 (NEONvshl (sext (v8i8 DPR:$Rn)), (i32 8))), +def : Pat<(v8i16 (ARMvshlImm (sext (v8i8 DPR:$Rn)), (i32 8))), (VSHLLi8 DPR:$Rn, 8)>; -def : Pat<(v4i32 (NEONvshl (sext (v4i16 DPR:$Rn)), (i32 16))), +def : Pat<(v4i32 (ARMvshlImm (sext (v4i16 DPR:$Rn)), (i32 16))), (VSHLLi16 DPR:$Rn, 16)>; -def : Pat<(v2i64 (NEONvshl (sext (v2i32 DPR:$Rn)), (i32 32))), +def : Pat<(v2i64 (ARMvshlImm (sext (v2i32 DPR:$Rn)), (i32 32))), (VSHLLi32 DPR:$Rn, 32)>; -def : Pat<(v8i16 (NEONvshl (anyext (v8i8 DPR:$Rn)), (i32 8))), +def : Pat<(v8i16 (ARMvshlImm (anyext (v8i8 DPR:$Rn)), (i32 8))), (VSHLLi8 DPR:$Rn, 8)>; -def : Pat<(v4i32 (NEONvshl (anyext (v4i16 DPR:$Rn)), (i32 16))), +def : Pat<(v4i32 (ARMvshlImm (anyext (v4i16 DPR:$Rn)), (i32 16))), (VSHLLi16 DPR:$Rn, 16)>; -def : Pat<(v2i64 (NEONvshl (anyext (v2i32 DPR:$Rn)), (i32 32))), +def : Pat<(v2i64 (ARMvshlImm (anyext (v2i32 DPR:$Rn)), (i32 32))), (VSHLLi32 DPR:$Rn, 32)>; +} // VSHRN : Vector Shift Right and Narrow defm VSHRN : N2VNSh_HSD<0,1,0b1000,0,0,1, IIC_VSHLiD, "vshrn", "i", PatFrag<(ops node:$Rn, node:$amt), - (trunc (NEONvshrs node:$Rn, node:$amt))>>; + (trunc (ARMvshrsImm node:$Rn, node:$amt))>>; -def : Pat<(v8i8 (trunc (NEONvshru (v8i16 QPR:$Vn), shr_imm8:$amt))), +let Predicates = [HasNEON] in { +def : Pat<(v8i8 (trunc (ARMvshruImm (v8i16 QPR:$Vn), shr_imm8:$amt))), (VSHRNv8i8 QPR:$Vn, shr_imm8:$amt)>; -def : Pat<(v4i16 (trunc (NEONvshru (v4i32 QPR:$Vn), shr_imm16:$amt))), +def : Pat<(v4i16 (trunc (ARMvshruImm (v4i32 QPR:$Vn), shr_imm16:$amt))), (VSHRNv4i16 QPR:$Vn, shr_imm16:$amt)>; -def : Pat<(v2i32 (trunc (NEONvshru (v2i64 QPR:$Vn), shr_imm32:$amt))), +def : Pat<(v2i32 (trunc (ARMvshruImm (v2i64 QPR:$Vn), shr_imm32:$amt))), (VSHRNv2i32 QPR:$Vn, shr_imm32:$amt)>; +} // VRSHL : Vector Rounding Shift defm VRSHLs : N3VInt_QHSDSh<0, 0, 0b0101, 0, N3RegVShFrm, @@ -5825,13 +5889,13 @@ defm VRSHLu : N3VInt_QHSDSh<1, 0, 0b0101, 0, N3RegVShFrm, "vrshl", "u", int_arm_neon_vrshiftu>; // VRSHR : Vector Rounding Shift Right defm VRSHRs : N2VShR_QHSD<0,1,0b0010,1, IIC_VSHLi4D, "vrshr", "s", "VRSHRs", - NEONvrshrs>; + NEONvrshrsImm>; defm VRSHRu : N2VShR_QHSD<1,1,0b0010,1, IIC_VSHLi4D, "vrshr", "u", "VRSHRu", - NEONvrshru>; + NEONvrshruImm>; // VRSHRN : Vector Rounding Shift Right and Narrow defm VRSHRN : N2VNSh_HSD<0, 1, 0b1000, 0, 1, 1, IIC_VSHLi4D, "vrshrn", "i", - NEONvrshrn>; + NEONvrshrnImm>; // VQSHL : Vector Saturating Shift defm VQSHLs : N3VInt_QHSDSh<0, 0, 0b0100, 1, N3RegVShFrm, @@ -5841,21 +5905,21 @@ defm VQSHLu : N3VInt_QHSDSh<1, 0, 0b0100, 1, N3RegVShFrm, IIC_VSHLi4D, IIC_VSHLi4D, IIC_VSHLi4Q, IIC_VSHLi4Q, "vqshl", "u", int_arm_neon_vqshiftu>; // VQSHL : Vector Saturating Shift Left (Immediate) -defm VQSHLsi : N2VShL_QHSD<0,1,0b0111,1, IIC_VSHLi4D, "vqshl", "s",NEONvqshls>; -defm VQSHLui : N2VShL_QHSD<1,1,0b0111,1, IIC_VSHLi4D, "vqshl", "u",NEONvqshlu>; +defm VQSHLsi : N2VShL_QHSD<0,1,0b0111,1, IIC_VSHLi4D, "vqshl", "s",NEONvqshlsImm>; +defm VQSHLui : N2VShL_QHSD<1,1,0b0111,1, IIC_VSHLi4D, "vqshl", "u",NEONvqshluImm>; // VQSHLU : Vector Saturating Shift Left (Immediate, Unsigned) -defm VQSHLsu : N2VShL_QHSD<1,1,0b0110,1, IIC_VSHLi4D,"vqshlu","s",NEONvqshlsu>; +defm VQSHLsu : N2VShL_QHSD<1,1,0b0110,1, IIC_VSHLi4D,"vqshlu","s",NEONvqshlsuImm>; // VQSHRN : Vector Saturating Shift Right and Narrow defm VQSHRNs : N2VNSh_HSD<0, 1, 0b1001, 0, 0, 1, IIC_VSHLi4D, "vqshrn", "s", - NEONvqshrns>; + NEONvqshrnsImm>; defm VQSHRNu : N2VNSh_HSD<1, 1, 0b1001, 0, 0, 1, IIC_VSHLi4D, "vqshrn", "u", - NEONvqshrnu>; + NEONvqshrnuImm>; // VQSHRUN : Vector Saturating Shift Right and Narrow (Unsigned) defm VQSHRUN : N2VNSh_HSD<1, 1, 0b1000, 0, 0, 1, IIC_VSHLi4D, "vqshrun", "s", - NEONvqshrnsu>; + NEONvqshrnsuImm>; // VQRSHL : Vector Saturating Rounding Shift defm VQRSHLs : N3VInt_QHSDSh<0, 0, 0b0101, 1, N3RegVShFrm, @@ -5867,20 +5931,20 @@ defm VQRSHLu : N3VInt_QHSDSh<1, 0, 0b0101, 1, N3RegVShFrm, // VQRSHRN : Vector Saturating Rounding Shift Right and Narrow defm VQRSHRNs : N2VNSh_HSD<0, 1, 0b1001, 0, 1, 1, IIC_VSHLi4D, "vqrshrn", "s", - NEONvqrshrns>; + NEONvqrshrnsImm>; defm VQRSHRNu : N2VNSh_HSD<1, 1, 0b1001, 0, 1, 1, IIC_VSHLi4D, "vqrshrn", "u", - NEONvqrshrnu>; + NEONvqrshrnuImm>; // VQRSHRUN : Vector Saturating Rounding Shift Right and Narrow (Unsigned) defm VQRSHRUN : N2VNSh_HSD<1, 1, 0b1000, 0, 1, 1, IIC_VSHLi4D, "vqrshrun", "s", - NEONvqrshrnsu>; + NEONvqrshrnsuImm>; // VSRA : Vector Shift Right and Accumulate -defm VSRAs : N2VShAdd_QHSD<0, 1, 0b0001, 1, "vsra", "s", NEONvshrs>; -defm VSRAu : N2VShAdd_QHSD<1, 1, 0b0001, 1, "vsra", "u", NEONvshru>; +defm VSRAs : N2VShAdd_QHSD<0, 1, 0b0001, 1, "vsra", "s", ARMvshrsImm>; +defm VSRAu : N2VShAdd_QHSD<1, 1, 0b0001, 1, "vsra", "u", ARMvshruImm>; // VRSRA : Vector Rounding Shift Right and Accumulate -defm VRSRAs : N2VShAdd_QHSD<0, 1, 0b0011, 1, "vrsra", "s", NEONvrshrs>; -defm VRSRAu : N2VShAdd_QHSD<1, 1, 0b0011, 1, "vrsra", "u", NEONvrshru>; +defm VRSRAs : N2VShAdd_QHSD<0, 1, 0b0011, 1, "vrsra", "s", NEONvrshrsImm>; +defm VRSRAu : N2VShAdd_QHSD<1, 1, 0b0011, 1, "vrsra", "u", NEONvrshruImm>; // VSLI : Vector Shift Left and Insert defm VSLI : N2VShInsL_QHSD<1, 1, 0b0101, 1, "vsli">; @@ -5957,12 +6021,14 @@ def VNEGhq : N2V<0b11, 0b11, 0b01, 0b01, 0b01111, 1, 0, [(set QPR:$Vd, (v8f16 (fneg QPR:$Vm)))]>, Requires<[HasNEON, HasFullFP16]>; +let Predicates = [HasNEON] in { def : Pat<(v8i8 (vnegd DPR:$src)), (VNEGs8d DPR:$src)>; def : Pat<(v4i16 (vnegd DPR:$src)), (VNEGs16d DPR:$src)>; def : Pat<(v2i32 (vnegd DPR:$src)), (VNEGs32d DPR:$src)>; def : Pat<(v16i8 (vnegq QPR:$src)), (VNEGs8q QPR:$src)>; def : Pat<(v8i16 (vnegq QPR:$src)), (VNEGs16q QPR:$src)>; def : Pat<(v4i32 (vnegq QPR:$src)), (VNEGs32q QPR:$src)>; +} // VQNEG : Vector Saturating Negate defm VQNEG : N2VInt_QHS<0b11, 0b11, 0b00, 0b01111, 0, @@ -6014,57 +6080,57 @@ let isReMaterializable = 1, isAsCheapAsAMove=1 in { def VMOVv8i8 : N1ModImm<1, 0b000, 0b1110, 0, 0, 0, 1, (outs DPR:$Vd), (ins nImmSplatI8:$SIMM), IIC_VMOVImm, "vmov", "i8", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v8i8 (NEONvmovImm timm:$SIMM)))]>; + [(set DPR:$Vd, (v8i8 (ARMvmovImm timm:$SIMM)))]>; def VMOVv16i8 : N1ModImm<1, 0b000, 0b1110, 0, 1, 0, 1, (outs QPR:$Vd), (ins nImmSplatI8:$SIMM), IIC_VMOVImm, "vmov", "i8", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v16i8 (NEONvmovImm timm:$SIMM)))]>; + [(set QPR:$Vd, (v16i8 (ARMvmovImm timm:$SIMM)))]>; def VMOVv4i16 : N1ModImm<1, 0b000, {1,0,?,0}, 0, 0, 0, 1, (outs DPR:$Vd), (ins nImmSplatI16:$SIMM), IIC_VMOVImm, "vmov", "i16", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v4i16 (NEONvmovImm timm:$SIMM)))]> { + [(set DPR:$Vd, (v4i16 (ARMvmovImm timm:$SIMM)))]> { let Inst{9} = SIMM{9}; } def VMOVv8i16 : N1ModImm<1, 0b000, {1,0,?,0}, 0, 1, 0, 1, (outs QPR:$Vd), (ins nImmSplatI16:$SIMM), IIC_VMOVImm, "vmov", "i16", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v8i16 (NEONvmovImm timm:$SIMM)))]> { + [(set QPR:$Vd, (v8i16 (ARMvmovImm timm:$SIMM)))]> { let Inst{9} = SIMM{9}; } def VMOVv2i32 : N1ModImm<1, 0b000, {?,?,?,?}, 0, 0, 0, 1, (outs DPR:$Vd), (ins nImmVMOVI32:$SIMM), IIC_VMOVImm, "vmov", "i32", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v2i32 (NEONvmovImm timm:$SIMM)))]> { + [(set DPR:$Vd, (v2i32 (ARMvmovImm timm:$SIMM)))]> { let Inst{11-8} = SIMM{11-8}; } def VMOVv4i32 : N1ModImm<1, 0b000, {?,?,?,?}, 0, 1, 0, 1, (outs QPR:$Vd), (ins nImmVMOVI32:$SIMM), IIC_VMOVImm, "vmov", "i32", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v4i32 (NEONvmovImm timm:$SIMM)))]> { + [(set QPR:$Vd, (v4i32 (ARMvmovImm timm:$SIMM)))]> { let Inst{11-8} = SIMM{11-8}; } def VMOVv1i64 : N1ModImm<1, 0b000, 0b1110, 0, 0, 1, 1, (outs DPR:$Vd), (ins nImmSplatI64:$SIMM), IIC_VMOVImm, "vmov", "i64", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v1i64 (NEONvmovImm timm:$SIMM)))]>; + [(set DPR:$Vd, (v1i64 (ARMvmovImm timm:$SIMM)))]>; def VMOVv2i64 : N1ModImm<1, 0b000, 0b1110, 0, 1, 1, 1, (outs QPR:$Vd), (ins nImmSplatI64:$SIMM), IIC_VMOVImm, "vmov", "i64", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v2i64 (NEONvmovImm timm:$SIMM)))]>; + [(set QPR:$Vd, (v2i64 (ARMvmovImm timm:$SIMM)))]>; def VMOVv2f32 : N1ModImm<1, 0b000, 0b1111, 0, 0, 0, 1, (outs DPR:$Vd), (ins nImmVMOVF32:$SIMM), IIC_VMOVImm, "vmov", "f32", "$Vd, $SIMM", "", - [(set DPR:$Vd, (v2f32 (NEONvmovFPImm timm:$SIMM)))]>; + [(set DPR:$Vd, (v2f32 (ARMvmovFPImm timm:$SIMM)))]>; def VMOVv4f32 : N1ModImm<1, 0b000, 0b1111, 0, 1, 0, 1, (outs QPR:$Vd), (ins nImmVMOVF32:$SIMM), IIC_VMOVImm, "vmov", "f32", "$Vd, $SIMM", "", - [(set QPR:$Vd, (v4f32 (NEONvmovFPImm timm:$SIMM)))]>; + [(set QPR:$Vd, (v4f32 (ARMvmovFPImm timm:$SIMM)))]>; } // isReMaterializable, isAsCheapAsAMove // Add support for bytes replication feature, so it could be GAS compatible. @@ -6144,7 +6210,7 @@ let AddedComplexity = 50, isAsCheapAsAMove = 1, isReMaterializable = 1 in { def VGETLNs8 : NVGetLane<{1,1,1,0,0,1,?,1}, 0b1011, {?,?}, (outs GPR:$R), (ins DPR:$V, VectorIndex8:$lane), IIC_VMOVSI, "vmov", "s8", "$R, $V$lane", - [(set GPR:$R, (NEONvgetlanes (v8i8 DPR:$V), + [(set GPR:$R, (ARMvgetlanes (v8i8 DPR:$V), imm:$lane))]> { let Inst{21} = lane{2}; let Inst{6-5} = lane{1-0}; @@ -6152,7 +6218,7 @@ def VGETLNs8 : NVGetLane<{1,1,1,0,0,1,?,1}, 0b1011, {?,?}, def VGETLNs16 : NVGetLane<{1,1,1,0,0,0,?,1}, 0b1011, {?,1}, (outs GPR:$R), (ins DPR:$V, VectorIndex16:$lane), IIC_VMOVSI, "vmov", "s16", "$R, $V$lane", - [(set GPR:$R, (NEONvgetlanes (v4i16 DPR:$V), + [(set GPR:$R, (ARMvgetlanes (v4i16 DPR:$V), imm:$lane))]> { let Inst{21} = lane{1}; let Inst{6} = lane{0}; @@ -6160,7 +6226,7 @@ def VGETLNs16 : NVGetLane<{1,1,1,0,0,0,?,1}, 0b1011, {?,1}, def VGETLNu8 : NVGetLane<{1,1,1,0,1,1,?,1}, 0b1011, {?,?}, (outs GPR:$R), (ins DPR:$V, VectorIndex8:$lane), IIC_VMOVSI, "vmov", "u8", "$R, $V$lane", - [(set GPR:$R, (NEONvgetlaneu (v8i8 DPR:$V), + [(set GPR:$R, (ARMvgetlaneu (v8i8 DPR:$V), imm:$lane))]> { let Inst{21} = lane{2}; let Inst{6-5} = lane{1-0}; @@ -6168,7 +6234,7 @@ def VGETLNu8 : NVGetLane<{1,1,1,0,1,1,?,1}, 0b1011, {?,?}, def VGETLNu16 : NVGetLane<{1,1,1,0,1,0,?,1}, 0b1011, {?,1}, (outs GPR:$R), (ins DPR:$V, VectorIndex16:$lane), IIC_VMOVSI, "vmov", "u16", "$R, $V$lane", - [(set GPR:$R, (NEONvgetlaneu (v4i16 DPR:$V), + [(set GPR:$R, (ARMvgetlaneu (v4i16 DPR:$V), imm:$lane))]> { let Inst{21} = lane{1}; let Inst{6} = lane{0}; @@ -6178,26 +6244,28 @@ def VGETLNi32 : NVGetLane<{1,1,1,0,0,0,?,1}, 0b1011, 0b00, IIC_VMOVSI, "vmov", "32", "$R, $V$lane", [(set GPR:$R, (extractelt (v2i32 DPR:$V), imm:$lane))]>, - Requires<[HasVFP2, HasFastVGETLNi32]> { + Requires<[HasFPRegs, HasFastVGETLNi32]> { let Inst{21} = lane{0}; } +let Predicates = [HasNEON] in { // def VGETLNf32: see FMRDH and FMRDL in ARMInstrVFP.td -def : Pat<(NEONvgetlanes (v16i8 QPR:$src), imm:$lane), +def : Pat<(ARMvgetlanes (v16i8 QPR:$src), imm:$lane), (VGETLNs8 (v8i8 (EXTRACT_SUBREG QPR:$src, (DSubReg_i8_reg imm:$lane))), (SubReg_i8_lane imm:$lane))>; -def : Pat<(NEONvgetlanes (v8i16 QPR:$src), imm:$lane), +def : Pat<(ARMvgetlanes (v8i16 QPR:$src), imm:$lane), (VGETLNs16 (v4i16 (EXTRACT_SUBREG QPR:$src, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane))>; -def : Pat<(NEONvgetlaneu (v16i8 QPR:$src), imm:$lane), +def : Pat<(ARMvgetlaneu (v16i8 QPR:$src), imm:$lane), (VGETLNu8 (v8i8 (EXTRACT_SUBREG QPR:$src, (DSubReg_i8_reg imm:$lane))), (SubReg_i8_lane imm:$lane))>; -def : Pat<(NEONvgetlaneu (v8i16 QPR:$src), imm:$lane), +def : Pat<(ARMvgetlaneu (v8i16 QPR:$src), imm:$lane), (VGETLNu16 (v4i16 (EXTRACT_SUBREG QPR:$src, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane))>; +} def : Pat<(extractelt (v4i32 QPR:$src), imm:$lane), (VGETLNi32 (v2i32 (EXTRACT_SUBREG QPR:$src, (DSubReg_i32_reg imm:$lane))), @@ -6211,6 +6279,7 @@ def : Pat<(extractelt (v4i32 QPR:$src), imm:$lane), (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG QPR:$src, (SSubReg_f32_reg imm:$lane))), GPR)>, Requires<[HasNEON, HasSlowVGETLNi32]>; +let Predicates = [HasNEON] in { def : Pat<(extractelt (v2f32 DPR:$src1), imm:$src2), (EXTRACT_SUBREG (v2f32 (COPY_TO_REGCLASS (v2f32 DPR:$src1),DPR_VFP2)), (SSubReg_f32_reg imm:$src2))>; @@ -6221,7 +6290,36 @@ def : Pat<(extractelt (v4f32 QPR:$src1), imm:$src2), // (EXTRACT_SUBREG QPR:$src1, (DSubReg_f64_reg imm:$src2))>; def : Pat<(extractelt (v2f64 QPR:$src1), imm:$src2), (EXTRACT_SUBREG QPR:$src1, (DSubReg_f64_reg imm:$src2))>; +} + +def imm_even : ImmLeaf<i32, [{ return (Imm & 1) == 0; }]>; +def imm_odd : ImmLeaf<i32, [{ return (Imm & 1) == 1; }]>; +let Predicates = [HasNEON] in { +def : Pat<(extractelt (v4f16 DPR:$src), imm_even:$lane), + (EXTRACT_SUBREG + (v2f32 (COPY_TO_REGCLASS (v4f16 DPR:$src), DPR_VFP2)), + (SSubReg_f16_reg imm_even:$lane))>; + +def : Pat<(extractelt (v4f16 DPR:$src), imm_odd:$lane), + (COPY_TO_REGCLASS + (VMOVH (EXTRACT_SUBREG + (v2f32 (COPY_TO_REGCLASS (v4f16 DPR:$src), DPR_VFP2)), + (SSubReg_f16_reg imm_odd:$lane))), + HPR)>; + +def : Pat<(extractelt (v8f16 QPR:$src), imm_even:$lane), + (EXTRACT_SUBREG + (v4f32 (COPY_TO_REGCLASS (v8f16 QPR:$src), QPR_VFP2)), + (SSubReg_f16_reg imm_even:$lane))>; + +def : Pat<(extractelt (v8f16 QPR:$src), imm_odd:$lane), + (COPY_TO_REGCLASS + (VMOVH (EXTRACT_SUBREG + (v4f32 (COPY_TO_REGCLASS (v8f16 QPR:$src), QPR_VFP2)), + (SSubReg_f16_reg imm_odd:$lane))), + HPR)>; +} // VMOV : Vector Set Lane (move ARM core register to scalar) @@ -6254,6 +6352,8 @@ def VSETLNi32 : NVSetLane<{1,1,1,0,0,0,?,0}, 0b1011, 0b00, (outs DPR:$V), let isInsertSubreg = 1; } } + +let Predicates = [HasNEON] in { def : Pat<(vector_insert (v16i8 QPR:$src1), GPR:$src2, imm:$lane), (v16i8 (INSERT_SUBREG QPR:$src1, (v8i8 (VSETLNi8 (v8i8 (EXTRACT_SUBREG QPR:$src1, @@ -6280,6 +6380,15 @@ def : Pat<(v4f32 (insertelt QPR:$src1, SPR:$src2, imm:$src3)), (INSERT_SUBREG (v4f32 (COPY_TO_REGCLASS QPR:$src1, QPR_VFP2)), SPR:$src2, (SSubReg_f32_reg imm:$src3))>; +def : Pat<(insertelt (v4f16 DPR:$src1), HPR:$src2, imm:$lane), + (v4f16 (VSETLNi16 DPR:$src1, (VMOVRH $src2), imm:$lane))>; +def : Pat<(insertelt (v8f16 QPR:$src1), HPR:$src2, imm:$lane), + (v8f16 (INSERT_SUBREG QPR:$src1, + (v4i16 (VSETLNi16 (v4i16 (EXTRACT_SUBREG QPR:$src1, + (DSubReg_i16_reg imm:$lane))), + (VMOVRH $src2), (SubReg_i16_lane imm:$lane))), + (DSubReg_i16_reg imm:$lane)))>; + //def : Pat<(v2i64 (insertelt QPR:$src1, DPR:$src2, imm:$src3)), // (INSERT_SUBREG QPR:$src1, DPR:$src2, (DSubReg_f64_reg imm:$src3))>; def : Pat<(v2f64 (insertelt QPR:$src1, DPR:$src2, imm:$src3)), @@ -6311,17 +6420,18 @@ def : Pat<(v4i32 (scalar_to_vector GPR:$src)), (INSERT_SUBREG (v4i32 (IMPLICIT_DEF)), (VSETLNi32 (v2i32 (IMPLICIT_DEF)), GPR:$src, (i32 0)), dsub_0)>; +} // VDUP : Vector Duplicate (from ARM core register to all elements) class VDUPD<bits<8> opcod1, bits<2> opcod3, string Dt, ValueType Ty> : NVDup<opcod1, 0b1011, opcod3, (outs DPR:$V), (ins GPR:$R), IIC_VMOVIS, "vdup", Dt, "$V, $R", - [(set DPR:$V, (Ty (NEONvdup (i32 GPR:$R))))]>; + [(set DPR:$V, (Ty (ARMvdup (i32 GPR:$R))))]>; class VDUPQ<bits<8> opcod1, bits<2> opcod3, string Dt, ValueType Ty> : NVDup<opcod1, 0b1011, opcod3, (outs QPR:$V), (ins GPR:$R), IIC_VMOVIS, "vdup", Dt, "$V, $R", - [(set QPR:$V, (Ty (NEONvdup (i32 GPR:$R))))]>; + [(set QPR:$V, (Ty (ARMvdup (i32 GPR:$R))))]>; def VDUP8d : VDUPD<0b11101100, 0b00, "8", v8i8>; def VDUP16d : VDUPD<0b11101000, 0b01, "16", v4i16>; @@ -6331,15 +6441,16 @@ def VDUP8q : VDUPQ<0b11101110, 0b00, "8", v16i8>; def VDUP16q : VDUPQ<0b11101010, 0b01, "16", v8i16>; def VDUP32q : VDUPQ<0b11101010, 0b00, "32", v4i32>; -// NEONvdup patterns for uarchs with fast VDUP.32. -def : Pat<(v2f32 (NEONvdup (f32 (bitconvert GPR:$R)))), (VDUP32d GPR:$R)>, +// ARMvdup patterns for uarchs with fast VDUP.32. +def : Pat<(v2f32 (ARMvdup (f32 (bitconvert GPR:$R)))), (VDUP32d GPR:$R)>, Requires<[HasNEON,HasFastVDUP32]>; -def : Pat<(v4f32 (NEONvdup (f32 (bitconvert GPR:$R)))), (VDUP32q GPR:$R)>; +def : Pat<(v4f32 (ARMvdup (f32 (bitconvert GPR:$R)))), (VDUP32q GPR:$R)>, + Requires<[HasNEON]>; -// NEONvdup patterns for uarchs with slow VDUP.32 - use VMOVDRR instead. -def : Pat<(v2i32 (NEONvdup (i32 GPR:$R))), (VMOVDRR GPR:$R, GPR:$R)>, +// ARMvdup patterns for uarchs with slow VDUP.32 - use VMOVDRR instead. +def : Pat<(v2i32 (ARMvdup (i32 GPR:$R))), (VMOVDRR GPR:$R, GPR:$R)>, Requires<[HasNEON,HasSlowVDUP32]>; -def : Pat<(v2f32 (NEONvdup (f32 (bitconvert GPR:$R)))), (VMOVDRR GPR:$R, GPR:$R)>, +def : Pat<(v2f32 (ARMvdup (f32 (bitconvert GPR:$R)))), (VMOVDRR GPR:$R, GPR:$R)>, Requires<[HasNEON,HasSlowVDUP32]>; // VDUP : Vector Duplicate Lane (from scalar to all elements) @@ -6348,13 +6459,13 @@ class VDUPLND<bits<4> op19_16, string OpcodeStr, string Dt, ValueType Ty, Operand IdxTy> : NVDupLane<op19_16, 0, (outs DPR:$Vd), (ins DPR:$Vm, IdxTy:$lane), IIC_VMOVD, OpcodeStr, Dt, "$Vd, $Vm$lane", - [(set DPR:$Vd, (Ty (NEONvduplane (Ty DPR:$Vm), imm:$lane)))]>; + [(set DPR:$Vd, (Ty (ARMvduplane (Ty DPR:$Vm), imm:$lane)))]>; class VDUPLNQ<bits<4> op19_16, string OpcodeStr, string Dt, ValueType ResTy, ValueType OpTy, Operand IdxTy> : NVDupLane<op19_16, 1, (outs QPR:$Vd), (ins DPR:$Vm, IdxTy:$lane), IIC_VMOVQ, OpcodeStr, Dt, "$Vd, $Vm$lane", - [(set QPR:$Vd, (ResTy (NEONvduplane (OpTy DPR:$Vm), + [(set QPR:$Vd, (ResTy (ARMvduplane (OpTy DPR:$Vm), VectorIndex32:$lane)))]>; // Inst{19-16} is partially specified depending on the element size. @@ -6384,48 +6495,50 @@ def VDUPLN32q : VDUPLNQ<{?,1,0,0}, "vdup", "32", v4i32, v2i32, VectorIndex32> { let Inst{19} = lane{0}; } -def : Pat<(v4f16 (NEONvduplane (v4f16 DPR:$Vm), imm:$lane)), +let Predicates = [HasNEON] in { +def : Pat<(v4f16 (ARMvduplane (v4f16 DPR:$Vm), imm:$lane)), (VDUPLN32d DPR:$Vm, imm:$lane)>; -def : Pat<(v2f32 (NEONvduplane (v2f32 DPR:$Vm), imm:$lane)), +def : Pat<(v2f32 (ARMvduplane (v2f32 DPR:$Vm), imm:$lane)), (VDUPLN32d DPR:$Vm, imm:$lane)>; -def : Pat<(v4f32 (NEONvduplane (v2f32 DPR:$Vm), imm:$lane)), +def : Pat<(v4f32 (ARMvduplane (v2f32 DPR:$Vm), imm:$lane)), (VDUPLN32q DPR:$Vm, imm:$lane)>; -def : Pat<(v16i8 (NEONvduplane (v16i8 QPR:$src), imm:$lane)), +def : Pat<(v16i8 (ARMvduplane (v16i8 QPR:$src), imm:$lane)), (v16i8 (VDUPLN8q (v8i8 (EXTRACT_SUBREG QPR:$src, (DSubReg_i8_reg imm:$lane))), (SubReg_i8_lane imm:$lane)))>; -def : Pat<(v8i16 (NEONvduplane (v8i16 QPR:$src), imm:$lane)), +def : Pat<(v8i16 (ARMvduplane (v8i16 QPR:$src), imm:$lane)), (v8i16 (VDUPLN16q (v4i16 (EXTRACT_SUBREG QPR:$src, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; -def : Pat<(v8f16 (NEONvduplane (v8f16 QPR:$src), imm:$lane)), +def : Pat<(v8f16 (ARMvduplane (v8f16 QPR:$src), imm:$lane)), (v8f16 (VDUPLN16q (v4f16 (EXTRACT_SUBREG QPR:$src, (DSubReg_i16_reg imm:$lane))), (SubReg_i16_lane imm:$lane)))>; -def : Pat<(v4i32 (NEONvduplane (v4i32 QPR:$src), imm:$lane)), +def : Pat<(v4i32 (ARMvduplane (v4i32 QPR:$src), imm:$lane)), (v4i32 (VDUPLN32q (v2i32 (EXTRACT_SUBREG QPR:$src, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; -def : Pat<(v4f32 (NEONvduplane (v4f32 QPR:$src), imm:$lane)), +def : Pat<(v4f32 (ARMvduplane (v4f32 QPR:$src), imm:$lane)), (v4f32 (VDUPLN32q (v2f32 (EXTRACT_SUBREG QPR:$src, (DSubReg_i32_reg imm:$lane))), (SubReg_i32_lane imm:$lane)))>; -def : Pat<(v4f16 (NEONvdup HPR:$src)), +def : Pat<(v4f16 (ARMvdup HPR:$src)), (v4f16 (VDUPLN16d (INSERT_SUBREG (v4f16 (IMPLICIT_DEF)), HPR:$src, ssub_0), (i32 0)))>; -def : Pat<(v2f32 (NEONvdup (f32 SPR:$src))), +def : Pat<(v2f32 (ARMvdup (f32 SPR:$src))), (v2f32 (VDUPLN32d (INSERT_SUBREG (v2f32 (IMPLICIT_DEF)), SPR:$src, ssub_0), (i32 0)))>; -def : Pat<(v4f32 (NEONvdup (f32 SPR:$src))), +def : Pat<(v4f32 (ARMvdup (f32 SPR:$src))), (v4f32 (VDUPLN32q (INSERT_SUBREG (v2f32 (IMPLICIT_DEF)), SPR:$src, ssub_0), (i32 0)))>; -def : Pat<(v8f16 (NEONvdup HPR:$src)), +def : Pat<(v8f16 (ARMvdup HPR:$src)), (v8f16 (VDUPLN16q (INSERT_SUBREG (v4f16 (IMPLICIT_DEF)), HPR:$src, ssub_0), (i32 0)))>; +} // VMOVN : Vector Narrowing Move defm VMOVN : N2VN_HSD<0b11,0b11,0b10,0b00100,0,0, IIC_VMOVN, @@ -6440,9 +6553,12 @@ defm VQMOVNsu : N2VNInt_HSD<0b11,0b11,0b10,0b00100,1,0, IIC_VQUNAiD, // VMOVL : Vector Lengthening Move defm VMOVLs : N2VL_QHS<0b01,0b10100,0,1, "vmovl", "s", sext>; defm VMOVLu : N2VL_QHS<0b11,0b10100,0,1, "vmovl", "u", zext>; + +let Predicates = [HasNEON] in { def : Pat<(v8i16 (anyext (v8i8 DPR:$Vm))), (VMOVLuv8i16 DPR:$Vm)>; def : Pat<(v4i32 (anyext (v4i16 DPR:$Vm))), (VMOVLuv4i32 DPR:$Vm)>; def : Pat<(v2i64 (anyext (v2i32 DPR:$Vm))), (VMOVLuv2i64 DPR:$Vm)>; +} // Vector Conversions. @@ -6621,24 +6737,29 @@ class VREV64D<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00000, 0, 0, (outs DPR:$Vd), (ins DPR:$Vm), IIC_VMOVD, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set DPR:$Vd, (Ty (NEONvrev64 (Ty DPR:$Vm))))]>; + [(set DPR:$Vd, (Ty (ARMvrev64 (Ty DPR:$Vm))))]>; class VREV64Q<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00000, 1, 0, (outs QPR:$Vd), (ins QPR:$Vm), IIC_VMOVQ, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set QPR:$Vd, (Ty (NEONvrev64 (Ty QPR:$Vm))))]>; + [(set QPR:$Vd, (Ty (ARMvrev64 (Ty QPR:$Vm))))]>; def VREV64d8 : VREV64D<0b00, "vrev64", "8", v8i8>; def VREV64d16 : VREV64D<0b01, "vrev64", "16", v4i16>; def VREV64d32 : VREV64D<0b10, "vrev64", "32", v2i32>; -def : Pat<(v2f32 (NEONvrev64 (v2f32 DPR:$Vm))), (VREV64d32 DPR:$Vm)>; +let Predicates = [HasNEON] in { +def : Pat<(v2f32 (ARMvrev64 (v2f32 DPR:$Vm))), (VREV64d32 DPR:$Vm)>; +} def VREV64q8 : VREV64Q<0b00, "vrev64", "8", v16i8>; def VREV64q16 : VREV64Q<0b01, "vrev64", "16", v8i16>; def VREV64q32 : VREV64Q<0b10, "vrev64", "32", v4i32>; -def : Pat<(v4f32 (NEONvrev64 (v4f32 QPR:$Vm))), (VREV64q32 QPR:$Vm)>; -def : Pat<(v8f16 (NEONvrev64 (v8f16 QPR:$Vm))), (VREV64q16 QPR:$Vm)>; -def : Pat<(v4f16 (NEONvrev64 (v4f16 DPR:$Vm))), (VREV64d16 DPR:$Vm)>; + +let Predicates = [HasNEON] in { +def : Pat<(v4f32 (ARMvrev64 (v4f32 QPR:$Vm))), (VREV64q32 QPR:$Vm)>; +def : Pat<(v8f16 (ARMvrev64 (v8f16 QPR:$Vm))), (VREV64q16 QPR:$Vm)>; +def : Pat<(v4f16 (ARMvrev64 (v4f16 DPR:$Vm))), (VREV64d16 DPR:$Vm)>; +} // VREV32 : Vector Reverse elements within 32-bit words @@ -6646,12 +6767,12 @@ class VREV32D<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00001, 0, 0, (outs DPR:$Vd), (ins DPR:$Vm), IIC_VMOVD, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set DPR:$Vd, (Ty (NEONvrev32 (Ty DPR:$Vm))))]>; + [(set DPR:$Vd, (Ty (ARMvrev32 (Ty DPR:$Vm))))]>; class VREV32Q<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00001, 1, 0, (outs QPR:$Vd), (ins QPR:$Vm), IIC_VMOVQ, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set QPR:$Vd, (Ty (NEONvrev32 (Ty QPR:$Vm))))]>; + [(set QPR:$Vd, (Ty (ARMvrev32 (Ty QPR:$Vm))))]>; def VREV32d8 : VREV32D<0b00, "vrev32", "8", v8i8>; def VREV32d16 : VREV32D<0b01, "vrev32", "16", v4i16>; @@ -6665,12 +6786,12 @@ class VREV16D<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00010, 0, 0, (outs DPR:$Vd), (ins DPR:$Vm), IIC_VMOVD, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set DPR:$Vd, (Ty (NEONvrev16 (Ty DPR:$Vm))))]>; + [(set DPR:$Vd, (Ty (ARMvrev16 (Ty DPR:$Vm))))]>; class VREV16Q<bits<2> op19_18, string OpcodeStr, string Dt, ValueType Ty> : N2V<0b11, 0b11, op19_18, 0b00, 0b00010, 1, 0, (outs QPR:$Vd), (ins QPR:$Vm), IIC_VMOVQ, OpcodeStr, Dt, "$Vd, $Vm", "", - [(set QPR:$Vd, (Ty (NEONvrev16 (Ty QPR:$Vm))))]>; + [(set QPR:$Vd, (Ty (ARMvrev16 (Ty QPR:$Vm))))]>; def VREV16d8 : VREV16D<0b00, "vrev16", "8", v8i8>; def VREV16q8 : VREV16Q<0b00, "vrev16", "8", v16i8>; @@ -6681,7 +6802,8 @@ def VREV16q8 : VREV16Q<0b00, "vrev16", "8", v16i8>; class AlignedVEXTq<ValueType DestTy, ValueType SrcTy, SDNodeXForm LaneCVT> : Pat<(DestTy (vector_extract_subvec (SrcTy QPR:$src), (i32 imm:$start))), - (EXTRACT_SUBREG (SrcTy QPR:$src), (LaneCVT imm:$start))>; + (EXTRACT_SUBREG (SrcTy QPR:$src), (LaneCVT imm:$start))>, + Requires<[HasNEON]>; def : AlignedVEXTq<v8i8, v16i8, DSubReg_i8_reg>; @@ -6693,6 +6815,7 @@ def : AlignedVEXTq<v1i64, v2i64, DSubReg_f64_reg>; def : AlignedVEXTq<v2f32, v4f32, DSubReg_i32_reg>; +def : AlignedVEXTq<v4f16, v8f16, DSubReg_i16_reg>; // v8f16 -> v4f16 // VEXT : Vector Extract @@ -6728,15 +6851,19 @@ def VEXTd16 : VEXTd<"vext", "16", v4i16, imm0_3> { let Inst{10-9} = index{1-0}; let Inst{8} = 0b0; } +let Predicates = [HasNEON] in { def : Pat<(v4f16 (NEONvext (v4f16 DPR:$Vn), (v4f16 DPR:$Vm), (i32 imm:$index))), (VEXTd16 DPR:$Vn, DPR:$Vm, imm:$index)>; +} def VEXTd32 : VEXTd<"vext", "32", v2i32, imm0_1> { let Inst{10} = index{0}; let Inst{9-8} = 0b00; } +let Predicates = [HasNEON] in { def : Pat<(v2f32 (NEONvext (v2f32 DPR:$Vn), (v2f32 DPR:$Vm), (i32 imm:$index))), (VEXTd32 DPR:$Vn, DPR:$Vm, imm:$index)>; +} def VEXTq8 : VEXTq<"vext", "8", v16i8, imm0_15> { let Inst{11-8} = index{3-0}; @@ -6745,8 +6872,10 @@ def VEXTq16 : VEXTq<"vext", "16", v8i16, imm0_7> { let Inst{11-9} = index{2-0}; let Inst{8} = 0b0; } +let Predicates = [HasNEON] in { def : Pat<(v8f16 (NEONvext (v8f16 QPR:$Vn), (v8f16 QPR:$Vm), (i32 imm:$index))), (VEXTq16 QPR:$Vn, QPR:$Vm, imm:$index)>; +} def VEXTq32 : VEXTq<"vext", "32", v4i32, imm0_3> { let Inst{11-10} = index{1-0}; @@ -6756,8 +6885,10 @@ def VEXTq64 : VEXTq<"vext", "64", v2i64, imm0_1> { let Inst{11} = index{0}; let Inst{10-8} = 0b000; } +let Predicates = [HasNEON] in { def : Pat<(v4f32 (NEONvext (v4f32 QPR:$Vn), (v4f32 QPR:$Vm), (i32 imm:$index))), (VEXTq32 QPR:$Vn, QPR:$Vm, imm:$index)>; +} // VTRN : Vector Transpose @@ -6857,6 +6988,7 @@ def VTBX4Pseudo IIC_VTBX4, "$orig = $dst", []>; } // DecoderMethod = "DecodeTBLInstruction" +let Predicates = [HasNEON] in { def : Pat<(v8i8 (NEONvtbl2 v8i8:$Vn0, v8i8:$Vn1, v8i8:$Vm)), (v8i8 (VTBL2 (REG_SEQUENCE DPair, v8i8:$Vn0, dsub_0, v8i8:$Vn1, dsub_1), @@ -6899,6 +7031,7 @@ def : Pat<(v8i8 (int_arm_neon_vtbx4 v8i8:$orig, v8i8:$Vn0, v8i8:$Vn1, v8i8:$Vn2, dsub_2, v8i8:$Vn3, dsub_3), v8i8:$Vm))>; +} // VRINT : Vector Rounding multiclass VRINT_FPI<string op, bits<3> op9_7, SDPatternOperator Int> { @@ -6989,6 +7122,7 @@ def SHA256H : N3SHA3Op<"256h", 0b00110, 0b00, int_arm_neon_sha256h>; def SHA256H2 : N3SHA3Op<"256h2", 0b00110, 0b01, int_arm_neon_sha256h2>; def SHA256SU1 : N3SHA3Op<"256su1", 0b00110, 0b10, int_arm_neon_sha256su1>; +let Predicates = [HasNEON] in { def : Pat<(i32 (int_arm_neon_sha1h i32:$Rn)), (COPY_TO_REGCLASS (f32 (EXTRACT_SUBREG (SHA1H (SUBREG_TO_REG (i64 0), @@ -7016,6 +7150,7 @@ def : Pat<(v4i32 (int_arm_neon_sha1p v4i32:$hash_abcd, i32:$hash_e, v4i32:$wk)), (f32 (COPY_TO_REGCLASS i32:$hash_e, SPR)), ssub_0), v4i32:$wk)>; +} //===----------------------------------------------------------------------===// // NEON instructions for single-precision FP math @@ -7123,171 +7258,228 @@ def : Pat<(arm_vmovsr GPR:$a), Requires<[HasNEON, DontUseVMOVSR]>; //===----------------------------------------------------------------------===// -// Non-Instruction Patterns +// Non-Instruction Patterns or Endiness - Revert Patterns //===----------------------------------------------------------------------===// // bit_convert -let Predicates = [IsLE] in { +// 64 bit conversions +let Predicates = [HasNEON] in { +def : Pat<(f64 (bitconvert (v1i64 DPR:$src))), (f64 DPR:$src)>; +def : Pat<(v1i64 (bitconvert (f64 DPR:$src))), (v1i64 DPR:$src)>; + +def : Pat<(v2f32 (bitconvert (v2i32 DPR:$src))), (v2f32 DPR:$src)>; +def : Pat<(v2i32 (bitconvert (v2f32 DPR:$src))), (v2i32 DPR:$src)>; + +def : Pat<(v4i16 (bitconvert (v4f16 DPR:$src))), (v4i16 DPR:$src)>; +def : Pat<(v4f16 (bitconvert (v4i16 DPR:$src))), (v4f16 DPR:$src)>; + +// 128 bit conversions +def : Pat<(v2f64 (bitconvert (v2i64 QPR:$src))), (v2f64 QPR:$src)>; +def : Pat<(v2i64 (bitconvert (v2f64 QPR:$src))), (v2i64 QPR:$src)>; + +def : Pat<(v4i32 (bitconvert (v4f32 QPR:$src))), (v4i32 QPR:$src)>; +def : Pat<(v4f32 (bitconvert (v4i32 QPR:$src))), (v4f32 QPR:$src)>; + +def : Pat<(v8i16 (bitconvert (v8f16 QPR:$src))), (v8i16 QPR:$src)>; +def : Pat<(v8f16 (bitconvert (v8i16 QPR:$src))), (v8f16 QPR:$src)>; +} + +let Predicates = [IsLE,HasNEON] in { + // 64 bit conversions + def : Pat<(f64 (bitconvert (v2f32 DPR:$src))), (f64 DPR:$src)>; + def : Pat<(f64 (bitconvert (v2i32 DPR:$src))), (f64 DPR:$src)>; + def : Pat<(f64 (bitconvert (v4f16 DPR:$src))), (f64 DPR:$src)>; + def : Pat<(f64 (bitconvert (v4i16 DPR:$src))), (f64 DPR:$src)>; + def : Pat<(f64 (bitconvert (v8i8 DPR:$src))), (f64 DPR:$src)>; + + def : Pat<(v1i64 (bitconvert (v2f32 DPR:$src))), (v1i64 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v2i32 DPR:$src))), (v1i64 DPR:$src)>; + def : Pat<(v1i64 (bitconvert (v4f16 DPR:$src))), (v1i64 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v4i16 DPR:$src))), (v1i64 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v8i8 DPR:$src))), (v1i64 DPR:$src)>; -} -def : Pat<(v1i64 (bitconvert (f64 DPR:$src))), (v1i64 DPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(v1i64 (bitconvert (v2f32 DPR:$src))), (v1i64 DPR:$src)>; + + def : Pat<(v2f32 (bitconvert (f64 DPR:$src))), (v2f32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v1i64 DPR:$src))), (v2f32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v4f16 DPR:$src))), (v2f32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v4i16 DPR:$src))), (v2f32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v8i8 DPR:$src))), (v2f32 DPR:$src)>; + + def : Pat<(v2i32 (bitconvert (f64 DPR:$src))), (v2i32 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v1i64 DPR:$src))), (v2i32 DPR:$src)>; + def : Pat<(v2i32 (bitconvert (v4f16 DPR:$src))), (v2i32 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v4i16 DPR:$src))), (v2i32 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v8i8 DPR:$src))), (v2i32 DPR:$src)>; - def : Pat<(v2i32 (bitconvert (f64 DPR:$src))), (v2i32 DPR:$src)>; -} -def : Pat<(v2i32 (bitconvert (v2f32 DPR:$src))), (v2i32 DPR:$src)>; -let Predicates = [IsLE] in { + + def : Pat<(v4f16 (bitconvert (f64 DPR:$src))), (v4f16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v1i64 DPR:$src))), (v4f16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v2f32 DPR:$src))), (v4f16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v2i32 DPR:$src))), (v4f16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v8i8 DPR:$src))), (v4f16 DPR:$src)>; + + def : Pat<(v4i16 (bitconvert (f64 DPR:$src))), (v4i16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v1i64 DPR:$src))), (v4i16 DPR:$src)>; + def : Pat<(v4i16 (bitconvert (v2f32 DPR:$src))), (v4i16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v2i32 DPR:$src))), (v4i16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v8i8 DPR:$src))), (v4i16 DPR:$src)>; - def : Pat<(v4i16 (bitconvert (f64 DPR:$src))), (v4i16 DPR:$src)>; - def : Pat<(v4i16 (bitconvert (v2f32 DPR:$src))), (v4i16 DPR:$src)>; + + def : Pat<(v8i8 (bitconvert (f64 DPR:$src))), (v8i8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v1i64 DPR:$src))), (v8i8 DPR:$src)>; + def : Pat<(v8i8 (bitconvert (v2f32 DPR:$src))), (v8i8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v2i32 DPR:$src))), (v8i8 DPR:$src)>; + def : Pat<(v8i8 (bitconvert (v4f16 DPR:$src))), (v8i8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v4i16 DPR:$src))), (v8i8 DPR:$src)>; - def : Pat<(v8i8 (bitconvert (f64 DPR:$src))), (v8i8 DPR:$src)>; - def : Pat<(v8i8 (bitconvert (v2f32 DPR:$src))), (v8i8 DPR:$src)>; -} -def : Pat<(f64 (bitconvert (v1i64 DPR:$src))), (f64 DPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(f64 (bitconvert (v2i32 DPR:$src))), (f64 DPR:$src)>; - def : Pat<(f64 (bitconvert (v4i16 DPR:$src))), (f64 DPR:$src)>; - def : Pat<(f64 (bitconvert (v4f16 DPR:$src))), (f64 DPR:$src)>; - def : Pat<(f64 (bitconvert (v8i8 DPR:$src))), (f64 DPR:$src)>; - def : Pat<(f64 (bitconvert (v2f32 DPR:$src))), (f64 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (f64 DPR:$src))), (v2f32 DPR:$src)>; - def : Pat<(v4f16 (bitconvert (f64 DPR:$src))), (v4f16 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (v1i64 DPR:$src))), (v2f32 DPR:$src)>; -} -def : Pat<(v2f32 (bitconvert (v2i32 DPR:$src))), (v2f32 DPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(v2f32 (bitconvert (v4i16 DPR:$src))), (v2f32 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (v8i8 DPR:$src))), (v2f32 DPR:$src)>; -} -let Predicates = [IsLE] in { + // 128 bit conversions + def : Pat<(v2f64 (bitconvert (v4f32 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v4i32 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8f16 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8i16 QPR:$src))), (v2f64 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v16i8 QPR:$src))), (v2f64 QPR:$src)>; + + def : Pat<(v2i64 (bitconvert (v4f32 QPR:$src))), (v2i64 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v4i32 QPR:$src))), (v2i64 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v8f16 QPR:$src))), (v2i64 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v8i16 QPR:$src))), (v2i64 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v16i8 QPR:$src))), (v2i64 QPR:$src)>; -} -def : Pat<(v2i64 (bitconvert (v2f64 QPR:$src))), (v2i64 QPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(v2i64 (bitconvert (v4f32 QPR:$src))), (v2i64 QPR:$src)>; + + def : Pat<(v4f32 (bitconvert (v2f64 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v2i64 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8f16 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8i16 QPR:$src))), (v4f32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v16i8 QPR:$src))), (v4f32 QPR:$src)>; + + def : Pat<(v4i32 (bitconvert (v2f64 QPR:$src))), (v4i32 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v2i64 QPR:$src))), (v4i32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v8f16 QPR:$src))), (v4i32 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v8i16 QPR:$src))), (v4i32 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v16i8 QPR:$src))), (v4i32 QPR:$src)>; - def : Pat<(v4i32 (bitconvert (v2f64 QPR:$src))), (v4i32 QPR:$src)>; -} -def : Pat<(v4i32 (bitconvert (v4f32 QPR:$src))), (v4i32 QPR:$src)>; -let Predicates = [IsLE] in { + + def : Pat<(v8f16 (bitconvert (v2f64 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v2i64 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4f32 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4i32 QPR:$src))), (v8f16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v16i8 QPR:$src))), (v8f16 QPR:$src)>; + + def : Pat<(v8i16 (bitconvert (v2f64 QPR:$src))), (v8i16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v2i64 QPR:$src))), (v8i16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v4f32 QPR:$src))), (v8i16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v4i32 QPR:$src))), (v8i16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v16i8 QPR:$src))), (v8i16 QPR:$src)>; - def : Pat<(v8i16 (bitconvert (v2f64 QPR:$src))), (v8i16 QPR:$src)>; - def : Pat<(v8i16 (bitconvert (v4f32 QPR:$src))), (v8i16 QPR:$src)>; - def : Pat<(v8f16 (bitconvert (v2f64 QPR:$src))), (v8f16 QPR:$src)>; + + def : Pat<(v16i8 (bitconvert (v2f64 QPR:$src))), (v16i8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v2i64 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v4f32 QPR:$src))), (v16i8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v4i32 QPR:$src))), (v16i8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v8f16 QPR:$src))), (v16i8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v8i16 QPR:$src))), (v16i8 QPR:$src)>; - def : Pat<(v16i8 (bitconvert (v2f64 QPR:$src))), (v16i8 QPR:$src)>; - def : Pat<(v16i8 (bitconvert (v4f32 QPR:$src))), (v16i8 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v2i64 QPR:$src))), (v4f32 QPR:$src)>; -} -def : Pat<(v4f32 (bitconvert (v4i32 QPR:$src))), (v4f32 QPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(v4f32 (bitconvert (v8i16 QPR:$src))), (v4f32 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v16i8 QPR:$src))), (v4f32 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v2f64 QPR:$src))), (v4f32 QPR:$src)>; -} -def : Pat<(v2f64 (bitconvert (v2i64 QPR:$src))), (v2f64 QPR:$src)>; -let Predicates = [IsLE] in { - def : Pat<(v2f64 (bitconvert (v4i32 QPR:$src))), (v2f64 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v8i16 QPR:$src))), (v2f64 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v8f16 QPR:$src))), (v2f64 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v16i8 QPR:$src))), (v2f64 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v4f32 QPR:$src))), (v2f64 QPR:$src)>; } -let Predicates = [IsBE] in { +let Predicates = [IsBE,HasNEON] in { // 64 bit conversions + def : Pat<(f64 (bitconvert (v2f32 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(f64 (bitconvert (v2i32 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(f64 (bitconvert (v4f16 DPR:$src))), (VREV64d16 DPR:$src)>; + def : Pat<(f64 (bitconvert (v4i16 DPR:$src))), (VREV64d16 DPR:$src)>; + def : Pat<(f64 (bitconvert (v8i8 DPR:$src))), (VREV64d8 DPR:$src)>; + + def : Pat<(v1i64 (bitconvert (v2f32 DPR:$src))), (VREV64d32 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v2i32 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(v1i64 (bitconvert (v4f16 DPR:$src))), (VREV64d16 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v4i16 DPR:$src))), (VREV64d16 DPR:$src)>; def : Pat<(v1i64 (bitconvert (v8i8 DPR:$src))), (VREV64d8 DPR:$src)>; - def : Pat<(v1i64 (bitconvert (v2f32 DPR:$src))), (VREV64d32 DPR:$src)>; + + def : Pat<(v2f32 (bitconvert (f64 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v1i64 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v4f16 DPR:$src))), (VREV32d16 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v4i16 DPR:$src))), (VREV32d16 DPR:$src)>; + def : Pat<(v2f32 (bitconvert (v8i8 DPR:$src))), (VREV32d8 DPR:$src)>; + + def : Pat<(v2i32 (bitconvert (f64 DPR:$src))), (VREV64d32 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v1i64 DPR:$src))), (VREV64d32 DPR:$src)>; + def : Pat<(v2i32 (bitconvert (v4f16 DPR:$src))), (VREV32d16 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v4i16 DPR:$src))), (VREV32d16 DPR:$src)>; def : Pat<(v2i32 (bitconvert (v8i8 DPR:$src))), (VREV32d8 DPR:$src)>; - def : Pat<(v2i32 (bitconvert (f64 DPR:$src))), (VREV64d32 DPR:$src)>; + + def : Pat<(v4f16 (bitconvert (f64 DPR:$src))), (VREV64d16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v1i64 DPR:$src))), (VREV64d16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v2f32 DPR:$src))), (VREV32d16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v2i32 DPR:$src))), (VREV32d16 DPR:$src)>; + def : Pat<(v4f16 (bitconvert (v8i8 DPR:$src))), (VREV16d8 DPR:$src)>; + + def : Pat<(v4i16 (bitconvert (f64 DPR:$src))), (VREV64d16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v1i64 DPR:$src))), (VREV64d16 DPR:$src)>; + def : Pat<(v4i16 (bitconvert (v2f32 DPR:$src))), (VREV32d16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v2i32 DPR:$src))), (VREV32d16 DPR:$src)>; def : Pat<(v4i16 (bitconvert (v8i8 DPR:$src))), (VREV16d8 DPR:$src)>; - def : Pat<(v4i16 (bitconvert (f64 DPR:$src))), (VREV64d16 DPR:$src)>; - def : Pat<(v4i16 (bitconvert (v2f32 DPR:$src))), (VREV32d16 DPR:$src)>; + + def : Pat<(v8i8 (bitconvert (f64 DPR:$src))), (VREV64d8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v1i64 DPR:$src))), (VREV64d8 DPR:$src)>; + def : Pat<(v8i8 (bitconvert (v2f32 DPR:$src))), (VREV32d8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v2i32 DPR:$src))), (VREV32d8 DPR:$src)>; + def : Pat<(v8i8 (bitconvert (v4f16 DPR:$src))), (VREV16d8 DPR:$src)>; def : Pat<(v8i8 (bitconvert (v4i16 DPR:$src))), (VREV16d8 DPR:$src)>; - def : Pat<(v8i8 (bitconvert (f64 DPR:$src))), (VREV64d8 DPR:$src)>; - def : Pat<(v8i8 (bitconvert (v2f32 DPR:$src))), (VREV32d8 DPR:$src)>; - def : Pat<(f64 (bitconvert (v2i32 DPR:$src))), (VREV64d32 DPR:$src)>; - def : Pat<(f64 (bitconvert (v4f16 DPR:$src))), (VREV64d16 DPR:$src)>; - def : Pat<(f64 (bitconvert (v4i16 DPR:$src))), (VREV64d16 DPR:$src)>; - def : Pat<(f64 (bitconvert (v8i8 DPR:$src))), (VREV64d8 DPR:$src)>; - def : Pat<(f64 (bitconvert (v2f32 DPR:$src))), (VREV64d32 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (f64 DPR:$src))), (VREV64d32 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (v1i64 DPR:$src))), (VREV64d32 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (v4i16 DPR:$src))), (VREV32d16 DPR:$src)>; - def : Pat<(v2f32 (bitconvert (v8i8 DPR:$src))), (VREV32d8 DPR:$src)>; // 128 bit conversions + def : Pat<(v2f64 (bitconvert (v4f32 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v4i32 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8f16 QPR:$src))), (VREV64q16 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v8i16 QPR:$src))), (VREV64q16 QPR:$src)>; + def : Pat<(v2f64 (bitconvert (v16i8 QPR:$src))), (VREV64q8 QPR:$src)>; + + def : Pat<(v2i64 (bitconvert (v4f32 QPR:$src))), (VREV64q32 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v4i32 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v2i64 (bitconvert (v8f16 QPR:$src))), (VREV64q16 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v8i16 QPR:$src))), (VREV64q16 QPR:$src)>; def : Pat<(v2i64 (bitconvert (v16i8 QPR:$src))), (VREV64q8 QPR:$src)>; - def : Pat<(v2i64 (bitconvert (v4f32 QPR:$src))), (VREV64q32 QPR:$src)>; + + def : Pat<(v4f32 (bitconvert (v2f64 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v2i64 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8f16 QPR:$src))), (VREV32q16 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v8i16 QPR:$src))), (VREV32q16 QPR:$src)>; + def : Pat<(v4f32 (bitconvert (v16i8 QPR:$src))), (VREV32q8 QPR:$src)>; + + def : Pat<(v4i32 (bitconvert (v2f64 QPR:$src))), (VREV64q32 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v2i64 QPR:$src))), (VREV64q32 QPR:$src)>; + def : Pat<(v4i32 (bitconvert (v8f16 QPR:$src))), (VREV32q16 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v8i16 QPR:$src))), (VREV32q16 QPR:$src)>; def : Pat<(v4i32 (bitconvert (v16i8 QPR:$src))), (VREV32q8 QPR:$src)>; - def : Pat<(v4i32 (bitconvert (v2f64 QPR:$src))), (VREV64q32 QPR:$src)>; + + def : Pat<(v8f16 (bitconvert (v2f64 QPR:$src))), (VREV64q16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v2i64 QPR:$src))), (VREV64q16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4f32 QPR:$src))), (VREV32q16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v4i32 QPR:$src))), (VREV32q16 QPR:$src)>; + def : Pat<(v8f16 (bitconvert (v16i8 QPR:$src))), (VREV16q8 QPR:$src)>; + + def : Pat<(v8i16 (bitconvert (v2f64 QPR:$src))), (VREV64q16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v2i64 QPR:$src))), (VREV64q16 QPR:$src)>; + def : Pat<(v8i16 (bitconvert (v4f32 QPR:$src))), (VREV32q16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v4i32 QPR:$src))), (VREV32q16 QPR:$src)>; def : Pat<(v8i16 (bitconvert (v16i8 QPR:$src))), (VREV16q8 QPR:$src)>; - def : Pat<(v8i16 (bitconvert (v2f64 QPR:$src))), (VREV64q16 QPR:$src)>; - def : Pat<(v8f16 (bitconvert (v2f64 QPR:$src))), (VREV64q16 QPR:$src)>; - def : Pat<(v8i16 (bitconvert (v4f32 QPR:$src))), (VREV32q16 QPR:$src)>; + + def : Pat<(v16i8 (bitconvert (v2f64 QPR:$src))), (VREV64q8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v2i64 QPR:$src))), (VREV64q8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v4f32 QPR:$src))), (VREV32q8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v4i32 QPR:$src))), (VREV32q8 QPR:$src)>; + def : Pat<(v16i8 (bitconvert (v8f16 QPR:$src))), (VREV16q8 QPR:$src)>; def : Pat<(v16i8 (bitconvert (v8i16 QPR:$src))), (VREV16q8 QPR:$src)>; - def : Pat<(v16i8 (bitconvert (v2f64 QPR:$src))), (VREV64q8 QPR:$src)>; - def : Pat<(v16i8 (bitconvert (v4f32 QPR:$src))), (VREV32q8 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v2i64 QPR:$src))), (VREV64q32 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v8i16 QPR:$src))), (VREV32q16 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v8f16 QPR:$src))), (VREV32q16 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v16i8 QPR:$src))), (VREV32q8 QPR:$src)>; - def : Pat<(v4f32 (bitconvert (v2f64 QPR:$src))), (VREV64q32 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v4i32 QPR:$src))), (VREV64q32 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v8i16 QPR:$src))), (VREV64q16 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v8f16 QPR:$src))), (VREV64q16 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v16i8 QPR:$src))), (VREV64q8 QPR:$src)>; - def : Pat<(v2f64 (bitconvert (v4f32 QPR:$src))), (VREV64q32 QPR:$src)>; } // Use VLD1/VST1 + VREV for non-word-aligned v2f64 load/store on Big Endian +let Predicates = [IsBE,HasNEON] in { def : Pat<(v2f64 (byte_alignedload addrmode6:$addr)), - (VREV64q8 (VLD1q8 addrmode6:$addr))>, Requires<[IsBE]>; + (VREV64q8 (VLD1q8 addrmode6:$addr))>; def : Pat<(byte_alignedstore (v2f64 QPR:$value), addrmode6:$addr), - (VST1q8 addrmode6:$addr, (VREV64q8 QPR:$value))>, Requires<[IsBE]>; + (VST1q8 addrmode6:$addr, (VREV64q8 QPR:$value))>; def : Pat<(v2f64 (hword_alignedload addrmode6:$addr)), - (VREV64q16 (VLD1q16 addrmode6:$addr))>, Requires<[IsBE]>; + (VREV64q16 (VLD1q16 addrmode6:$addr))>; def : Pat<(hword_alignedstore (v2f64 QPR:$value), addrmode6:$addr), - (VST1q16 addrmode6:$addr, (VREV64q16 QPR:$value))>, Requires<[IsBE]>; + (VST1q16 addrmode6:$addr, (VREV64q16 QPR:$value))>; +} // Fold extracting an element out of a v2i32 into a vfp register. def : Pat<(f32 (bitconvert (i32 (extractelt (v2i32 DPR:$src), imm:$lane)))), - (f32 (EXTRACT_SUBREG DPR:$src, (SSubReg_f32_reg imm:$lane)))>; + (f32 (EXTRACT_SUBREG DPR:$src, (SSubReg_f32_reg imm:$lane)))>, + Requires<[HasNEON]>; // Vector lengthening move with load, matching extending loads. @@ -7301,17 +7493,20 @@ multiclass Lengthen_Single<string DestLanes, string DestTy, string SrcTy> { def _Any : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("extloadvi" # SrcTy) addrmode6:$addr)), (!cast<Instruction>("VMOVLuv" # DestLanes # DestTy) - (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>; + (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadvi" # SrcTy) addrmode6:$addr)), (!cast<Instruction>("VMOVLuv" # DestLanes # DestTy) - (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>; + (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadvi" # SrcTy) addrmode6:$addr)), (!cast<Instruction>("VMOVLsv" # DestLanes # DestTy) - (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>; + (!cast<Instruction>("VLD1d" # SrcTy) addrmode6:$addr))>, + Requires<[HasNEON]>; } } @@ -7328,17 +7523,20 @@ multiclass Lengthen_HalfSingle<string DestLanes, string DestTy, string SrcTy, (!cast<PatFrag>("extloadv" # SrcTy) addrmode6oneL32:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # InsnLanes # InsnTy) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6oneL32:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # InsnLanes # InsnTy) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6oneL32:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # InsnLanes # InsnTy) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; } // The following class definition is basically a copy of the @@ -7352,19 +7550,22 @@ multiclass Lengthen_HalfSingle_Big_Endian<string DestLanes, string DestTy, strin (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # InsnLanes # InsnTy) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6oneL32:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # InsnLanes # InsnTy) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6oneL32:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # InsnLanes # InsnTy) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; } // extload, zextload and sextload for a lengthening load followed by another @@ -7386,19 +7587,22 @@ multiclass Lengthen_Double<string DestLanes, string DestTy, string SrcTy, (!cast<Instruction>("VMOVLuv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6oneL32:$addr)), (!cast<Instruction>("VMOVLuv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6oneL32:$addr)), (!cast<Instruction>("VMOVLsv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # Insn1Lanes # Insn1Ty) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; } // The following class definition is basically a copy of the @@ -7414,21 +7618,24 @@ multiclass Lengthen_Double_Big_Endian<string DestLanes, string DestTy, string Sr (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6oneL32:$addr)), (!cast<Instruction>("VMOVLuv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6oneL32:$addr)), (!cast<Instruction>("VMOVLsv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # Insn1Lanes # Insn1Ty) (!cast<Instruction>("VREV32d" # RevLanes) (VLD1LNd32 addrmode6oneL32:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), - dsub_0))>; + dsub_0))>, + Requires<[HasNEON]>; } // extload, zextload and sextload for a lengthening load followed by another @@ -7451,21 +7658,24 @@ multiclass Lengthen_HalfDouble<string DestLanes, string DestTy, string SrcTy, (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn1Lanes # Insn1Ty) (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # Insn2Lanes # Insn2Ty) (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # Insn1Lanes # Insn1Ty) (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; } // The following class definition is basically a copy of the @@ -7482,7 +7692,8 @@ multiclass Lengthen_HalfDouble_Big_Endian<string DestLanes, string DestTy, strin (!cast<Instruction>("VREV16d8") (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _Z : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("zextloadv" # SrcTy) addrmode6:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLuv" # Insn2Lanes # Insn2Ty) @@ -7490,7 +7701,8 @@ multiclass Lengthen_HalfDouble_Big_Endian<string DestLanes, string DestTy, strin (!cast<Instruction>("VREV16d8") (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; def _S : Pat<(!cast<ValueType>("v" # DestLanes # DestTy) (!cast<PatFrag>("sextloadv" # SrcTy) addrmode6:$addr)), (EXTRACT_SUBREG (!cast<Instruction>("VMOVLsv" # Insn2Lanes # Insn2Ty) @@ -7498,14 +7710,15 @@ multiclass Lengthen_HalfDouble_Big_Endian<string DestLanes, string DestTy, strin (!cast<Instruction>("VREV16d8") (VLD1LNd16 addrmode6:$addr, (f64 (IMPLICIT_DEF)), (i32 0)))), dsub_0)), - dsub_0)>; + dsub_0)>, + Requires<[HasNEON]>; } defm : Lengthen_Single<"8", "i16", "8">; // v8i8 -> v8i16 defm : Lengthen_Single<"4", "i32", "16">; // v4i16 -> v4i32 defm : Lengthen_Single<"2", "i64", "32">; // v2i32 -> v2i64 -let Predicates = [IsLE] in { +let Predicates = [HasNEON,IsLE] in { defm : Lengthen_HalfSingle<"4", "i16", "i8", "8", "i16">; // v4i8 -> v4i16 defm : Lengthen_HalfSingle<"2", "i32", "i16", "4", "i32">; // v2i16 -> v2i32 @@ -7517,7 +7730,7 @@ let Predicates = [IsLE] in { defm : Lengthen_Double<"2", "i64", "i16", "4", "i32", "2", "i64">; } -let Predicates = [IsBE] in { +let Predicates = [HasNEON,IsBE] in { defm : Lengthen_HalfSingle_Big_Endian<"4", "i16", "i8", "8", "i16", "8">; // v4i8 -> v4i16 defm : Lengthen_HalfSingle_Big_Endian<"2", "i32", "i16", "4", "i32", "16">; // v2i16 -> v2i32 @@ -7530,7 +7743,7 @@ let Predicates = [IsBE] in { } // Triple lengthening - v2i8 -> v2i16 -> v2i32 -> v2i64 -let Predicates = [IsLE] in { +let Predicates = [HasNEON,IsLE] in { def : Pat<(v2i64 (extloadvi8 addrmode6:$addr)), (VMOVLuv2i64 (EXTRACT_SUBREG (VMOVLuv4i32 (EXTRACT_SUBREG (VMOVLuv8i16 (VLD1LNd16 addrmode6:$addr, @@ -7547,7 +7760,7 @@ let Predicates = [IsLE] in { // The following patterns are basically a copy of the patterns above, // however with an additional VREV16d instruction to convert data // loaded by VLD1LN into proper vector format in big endian mode. -let Predicates = [IsBE] in { +let Predicates = [HasNEON,IsBE] in { def : Pat<(v2i64 (extloadvi8 addrmode6:$addr)), (VMOVLuv2i64 (EXTRACT_SUBREG (VMOVLuv4i32 (EXTRACT_SUBREG (VMOVLuv8i16 (!cast<Instruction>("VREV16d8") @@ -7565,6 +7778,7 @@ let Predicates = [IsBE] in { (f64 (IMPLICIT_DEF)), (i32 0)))), dsub_0)), dsub_0))>; } +let Predicates = [HasNEON] in { def : Pat<(v2i64 (concat_vectors DPR:$Dn, DPR:$Dm)), (REG_SEQUENCE QPR, DPR:$Dn, dsub_0, DPR:$Dm, dsub_1)>; def : Pat<(v4i32 (concat_vectors DPR:$Dn, DPR:$Dm)), @@ -7575,6 +7789,9 @@ def : Pat<(v16i8 (concat_vectors DPR:$Dn, DPR:$Dm)), (REG_SEQUENCE QPR, DPR:$Dn, dsub_0, DPR:$Dm, dsub_1)>; def : Pat<(v4f32 (concat_vectors DPR:$Dn, DPR:$Dm)), (REG_SEQUENCE QPR, DPR:$Dn, dsub_0, DPR:$Dm, dsub_1)>; +def : Pat<(v8f16 (concat_vectors DPR:$Dn, DPR:$Dm)), + (REG_SEQUENCE QPR, DPR:$Dn, dsub_0, DPR:$Dm, dsub_1)>; +} //===----------------------------------------------------------------------===// // Assembler aliases diff --git a/lib/Target/ARM/ARMInstrThumb.td b/lib/Target/ARM/ARMInstrThumb.td index b20b34eaa6a9..cfeb13c6acb6 100644 --- a/lib/Target/ARM/ARMInstrThumb.td +++ b/lib/Target/ARM/ARMInstrThumb.td @@ -1,9 +1,8 @@ //===-- ARMInstrThumb.td - Thumb support for ARM -----------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -188,6 +187,19 @@ def t_addrmode_rr : MemOperand, let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg); } +// t_addrmode_rr_sext := reg + reg +// +// This is similar to t_addrmode_rr, but uses different heuristics for +// ldrsb/ldrsh. +def t_addrmode_rr_sext : MemOperand, + ComplexPattern<i32, 2, "SelectThumbAddrModeRRSext", []> { + let EncoderMethod = "getThumbAddrModeRegRegOpValue"; + let PrintMethod = "printThumbAddrModeRROperand"; + let DecoderMethod = "DecodeThumbAddrModeRR"; + let ParserMatchClass = t_addrmode_rr_asm_operand; + let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg); +} + // t_addrmode_rrs := reg + reg // // We use separate scaled versions because the Select* functions need @@ -651,7 +663,7 @@ let canFoldAsLoad = 1, isReMaterializable = 1, AddedComplexity = 10 in def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i, "ldr", "\t$Rt, $addr", [(set tGPR:$Rt, (load (ARMWrapper tconstpool:$addr)))]>, - T1Encoding<{0,1,0,0,1,?}> { + T1Encoding<{0,1,0,0,1,?}>, Sched<[WriteLd]> { // A6.2 & A8.6.59 bits<3> Rt; bits<8> addr; @@ -665,7 +677,7 @@ let canFoldAsLoad = 1 in def tLDRspi : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_sp:$addr), IIC_iLoad_i, "ldr", "\t$Rt, $addr", [(set tGPR:$Rt, (load t_addrmode_sp:$addr))]>, - T1LdStSP<{1,?,?}> { + T1LdStSP<{1,?,?}>, Sched<[WriteLd]> { bits<3> Rt; bits<8> addr; let Inst{10-8} = Rt; @@ -716,39 +728,39 @@ multiclass thumb_st_rr_ri_enc<bits<3> reg_opc, bits<4> imm_opc, defm tLDR : thumb_ld_rr_ri_enc<0b100, 0b0110, t_addrmode_rr, t_addrmode_is4, AddrModeT1_4, IIC_iLoad_r, IIC_iLoad_i, "ldr", - load>; + load>, Sched<[WriteLd]>; // A8.6.64 & A8.6.61 defm tLDRB : thumb_ld_rr_ri_enc<0b110, 0b0111, t_addrmode_rr, t_addrmode_is1, AddrModeT1_1, IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrb", - zextloadi8>; + zextloadi8>, Sched<[WriteLd]>; // A8.6.76 & A8.6.73 defm tLDRH : thumb_ld_rr_ri_enc<0b101, 0b1000, t_addrmode_rr, t_addrmode_is2, AddrModeT1_2, IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrh", - zextloadi16>; + zextloadi16>, Sched<[WriteLd]>; let AddedComplexity = 10 in def tLDRSB : // A8.6.80 - T1pILdStEncode<0b011, (outs tGPR:$Rt), (ins t_addrmode_rr:$addr), + T1pILdStEncode<0b011, (outs tGPR:$Rt), (ins t_addrmode_rr_sext:$addr), AddrModeT1_1, IIC_iLoad_bh_r, "ldrsb", "\t$Rt, $addr", - [(set tGPR:$Rt, (sextloadi8 t_addrmode_rr:$addr))]>; + [(set tGPR:$Rt, (sextloadi8 t_addrmode_rr_sext:$addr))]>, Sched<[WriteLd]>; let AddedComplexity = 10 in def tLDRSH : // A8.6.84 - T1pILdStEncode<0b111, (outs tGPR:$Rt), (ins t_addrmode_rr:$addr), + T1pILdStEncode<0b111, (outs tGPR:$Rt), (ins t_addrmode_rr_sext:$addr), AddrModeT1_2, IIC_iLoad_bh_r, "ldrsh", "\t$Rt, $addr", - [(set tGPR:$Rt, (sextloadi16 t_addrmode_rr:$addr))]>; + [(set tGPR:$Rt, (sextloadi16 t_addrmode_rr_sext:$addr))]>, Sched<[WriteLd]>; def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i, "str", "\t$Rt, $addr", [(store tGPR:$Rt, t_addrmode_sp:$addr)]>, - T1LdStSP<{0,?,?}> { + T1LdStSP<{0,?,?}>, Sched<[WriteST]> { bits<3> Rt; bits<8> addr; let Inst{10-8} = Rt; @@ -759,19 +771,19 @@ def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i, defm tSTR : thumb_st_rr_ri_enc<0b000, 0b0110, t_addrmode_rr, t_addrmode_is4, AddrModeT1_4, IIC_iStore_r, IIC_iStore_i, "str", - store>; + store>, Sched<[WriteST]>; // A8.6.197 & A8.6.195 defm tSTRB : thumb_st_rr_ri_enc<0b010, 0b0111, t_addrmode_rr, t_addrmode_is1, AddrModeT1_1, IIC_iStore_bh_r, IIC_iStore_bh_i, "strb", - truncstorei8>; + truncstorei8>, Sched<[WriteST]>; // A8.6.207 & A8.6.205 defm tSTRH : thumb_st_rr_ri_enc<0b001, 0b1000, t_addrmode_rr, t_addrmode_is2, AddrModeT1_2, IIC_iStore_bh_r, IIC_iStore_bh_i, "strh", - truncstorei16>; + truncstorei16>, Sched<[WriteST]>; //===----------------------------------------------------------------------===// @@ -799,8 +811,8 @@ def tLDMIA_UPD : "$Rn = $wb", IIC_iLoad_mu>, PseudoInstExpansion<(tLDMIA tGPR:$Rn, pred:$p, reglist:$regs)> { let Size = 2; - let OutOperandList = (outs GPR:$wb); - let InOperandList = (ins GPR:$Rn, pred:$p, reglist:$regs, variable_ops); + let OutOperandList = (outs tGPR:$wb); + let InOperandList = (ins tGPR:$Rn, pred:$p, reglist:$regs, variable_ops); let Pattern = []; let isCodeGenOnly = 1; let isPseudo = 1; @@ -809,7 +821,7 @@ def tLDMIA_UPD : // There is no non-writeback version of STM for Thumb. let mayStore = 1, hasExtraSrcRegAllocReq = 1 in -def tSTMIA_UPD : Thumb1I<(outs GPR:$wb), +def tSTMIA_UPD : Thumb1I<(outs tGPR:$wb), (ins tGPR:$Rn, pred:$p, reglist:$regs, variable_ops), AddrModeNone, 2, IIC_iStore_mu, "stm${p}\t$Rn!, $regs", "$Rn = $wb", []>, @@ -831,7 +843,7 @@ let mayLoad = 1, Uses = [SP], Defs = [SP], hasExtraDefRegAllocReq = 1, def tPOP : T1I<(outs), (ins pred:$p, reglist:$regs, variable_ops), IIC_iPop, "pop${p}\t$regs", []>, - T1Misc<{1,1,0,?,?,?,?}> { + T1Misc<{1,1,0,?,?,?,?}>, Sched<[WriteLd]> { bits<16> regs; let Inst{8} = regs{15}; let Inst{7-0} = regs{7-0}; @@ -841,7 +853,7 @@ let mayStore = 1, Uses = [SP], Defs = [SP], hasExtraSrcRegAllocReq = 1 in def tPUSH : T1I<(outs), (ins pred:$p, reglist:$regs, variable_ops), IIC_iStore_m, "push${p}\t$regs", []>, - T1Misc<{0,1,0,?,?,?,?}> { + T1Misc<{0,1,0,?,?,?,?}>, Sched<[WriteST]> { bits<16> regs; let Inst{8} = regs{14}; let Inst{7-0} = regs{7-0}; @@ -1202,7 +1214,7 @@ def tMUL : // A8.6.105 T1 Thumb1sI<(outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm), AddrModeNone, 2, IIC_iMUL32, "mul", "\t$Rd, $Rn, $Rm", "$Rm = $Rd", [(set tGPR:$Rd, (mul tGPR:$Rn, tGPR:$Rm))]>, - T1DataProcessing<0b1101> { + T1DataProcessing<0b1101>, Sched<[WriteMUL32, ReadMUL, ReadMUL]> { bits<3> Rd; bits<3> Rn; let Inst{5-3} = Rn; @@ -1499,12 +1511,13 @@ def tInt_eh_sjlj_setjmp : ThumbXI<(outs),(ins tGPR:$src, tGPR:$val), // FIXME: Non-IOS version(s) let isBarrier = 1, hasSideEffects = 1, isTerminator = 1, isCodeGenOnly = 1, Defs = [ R7, LR, SP ] in -def tInt_eh_sjlj_longjmp : XI<(outs), (ins GPR:$src, GPR:$scratch), +def tInt_eh_sjlj_longjmp : XI<(outs), (ins tGPR:$src, tGPR:$scratch), AddrModeNone, 0, IndexModeNone, Pseudo, NoItinerary, "", "", - [(ARMeh_sjlj_longjmp GPR:$src, GPR:$scratch)]>, + [(ARMeh_sjlj_longjmp tGPR:$src, tGPR:$scratch)]>, Requires<[IsThumb,IsNotWindows]>; +// (Windows is Thumb2-only) let isBarrier = 1, hasSideEffects = 1, isTerminator = 1, isCodeGenOnly = 1, Defs = [ R11, LR, SP ] in def tInt_WIN_eh_sjlj_longjmp @@ -1599,16 +1612,16 @@ def : T1Pat<(extloadi16 t_addrmode_rr:$addr), (tLDRHr t_addrmode_rr:$addr)>; // and expand it just after ISel. let usesCustomInserter = 1, mayLoad =1, Constraints = "$Rn = $Rn_wb,@earlyclobber $Rn_wb" in - def tLDR_postidx: tPseudoInst<(outs rGPR:$Rt, rGPR:$Rn_wb), - (ins rGPR:$Rn, pred:$p), + def tLDR_postidx: tPseudoInst<(outs tGPR:$Rt, tGPR:$Rn_wb), + (ins tGPR:$Rn, pred:$p), 4, IIC_iStore_ru, []>; // post-inc STR -> STM r0!, {r1}. The layout of this (because it doesn't def // multiple registers) is the same in ISel as MachineInstr, so there's no need // for a pseudo. -def : T1Pat<(post_store rGPR:$Rt, rGPR:$Rn, 4), - (tSTMIA_UPD rGPR:$Rn, rGPR:$Rt)>; +def : T1Pat<(post_store tGPR:$Rt, tGPR:$Rn, 4), + (tSTMIA_UPD tGPR:$Rn, tGPR:$Rt)>; // If it's impossible to use [r,r] address mode for sextload, select to // ldr{b|h} + sxt{b|h} instead. @@ -1677,9 +1690,9 @@ def : T1Pat<(i32 imm256_510:$src), // be expanded into two instructions late to allow if-conversion and // scheduling. let isReMaterializable = 1 in -def tLDRpci_pic : PseudoInst<(outs GPR:$dst), (ins i32imm:$addr, pclabel:$cp), +def tLDRpci_pic : PseudoInst<(outs tGPR:$dst), (ins i32imm:$addr, pclabel:$cp), NoItinerary, - [(set GPR:$dst, (ARMpic_add (load (ARMWrapper tconstpool:$addr)), + [(set tGPR:$dst, (ARMpic_add (load (ARMWrapper tconstpool:$addr)), imm:$cp))]>, Requires<[IsThumb, IsThumb1Only]>; diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 7a6673b49d57..7cbfaba7a8eb 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -1,9 +1,8 @@ //===-- ARMInstrThumb2.td - Thumb2 support for ARM ---------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -26,6 +25,7 @@ def it_mask_asmoperand : AsmOperandClass { let Name = "ITMask"; } def it_mask : Operand<i32> { let PrintMethod = "printThumbITMask"; let ParserMatchClass = it_mask_asmoperand; + let EncoderMethod = "getITMaskOpValue"; } // t2_shift_imm: An integer that encodes a shift amount and the type of shift @@ -40,6 +40,16 @@ def t2_shift_imm : Operand<i32> { let DecoderMethod = "DecodeT2ShifterImmOperand"; } +def mve_shift_imm : AsmOperandClass { + let Name = "MVELongShift"; + let RenderMethod = "addImmOperands"; + let DiagnosticString = "operand must be an immediate in the range [1,32]"; +} +def long_shift : Operand<i32> { + let ParserMatchClass = mve_shift_imm; + let DecoderMethod = "DecodeLongShiftOperand"; +} + // Shifted operands. No register controlled shifts for Thumb2. // Note: We do not support rrx shifted operands yet. def t2_so_reg : Operand<i32>, // reg imm @@ -151,6 +161,26 @@ def lo5AllOne : PatLeaf<(i32 imm), [{ // Define Thumb2 specific addressing modes. +// t2_addr_offset_none := reg +def MemNoOffsetT2AsmOperand + : AsmOperandClass { let Name = "MemNoOffsetT2"; } +def t2_addr_offset_none : MemOperand { + let PrintMethod = "printAddrMode7Operand"; + let DecoderMethod = "DecodeGPRnopcRegisterClass"; + let ParserMatchClass = MemNoOffsetT2AsmOperand; + let MIOperandInfo = (ops GPRnopc:$base); +} + +// t2_nosp_addr_offset_none := reg +def MemNoOffsetT2NoSpAsmOperand + : AsmOperandClass { let Name = "MemNoOffsetT2NoSp"; } +def t2_nosp_addr_offset_none : MemOperand { + let PrintMethod = "printAddrMode7Operand"; + let DecoderMethod = "DecoderGPRRegisterClass"; + let ParserMatchClass = MemNoOffsetT2NoSpAsmOperand; + let MIOperandInfo = (ops rGPR:$base); +} + // t2addrmode_imm12 := reg + imm12 def t2addrmode_imm12_asmoperand : AsmOperandClass {let Name="MemUImm12Offset";} def t2addrmode_imm12 : MemOperand, @@ -182,31 +212,40 @@ def t2adrlabel : Operand<i32> { } // t2addrmode_posimm8 := reg + imm8 -def MemPosImm8OffsetAsmOperand : AsmOperandClass {let Name="MemPosImm8Offset";} +def MemPosImm8OffsetAsmOperand : AsmOperandClass { + let Name="MemPosImm8Offset"; + let RenderMethod = "addMemImmOffsetOperands"; +} def t2addrmode_posimm8 : MemOperand { let PrintMethod = "printT2AddrModeImm8Operand<false>"; - let EncoderMethod = "getT2AddrModeImm8OpValue"; + let EncoderMethod = "getT2AddrModeImmOpValue<8,0>"; let DecoderMethod = "DecodeT2AddrModeImm8"; let ParserMatchClass = MemPosImm8OffsetAsmOperand; let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } // t2addrmode_negimm8 := reg - imm8 -def MemNegImm8OffsetAsmOperand : AsmOperandClass {let Name="MemNegImm8Offset";} +def MemNegImm8OffsetAsmOperand : AsmOperandClass { + let Name="MemNegImm8Offset"; + let RenderMethod = "addMemImmOffsetOperands"; +} def t2addrmode_negimm8 : MemOperand, ComplexPattern<i32, 2, "SelectT2AddrModeImm8", []> { let PrintMethod = "printT2AddrModeImm8Operand<false>"; - let EncoderMethod = "getT2AddrModeImm8OpValue"; + let EncoderMethod = "getT2AddrModeImmOpValue<8,0>"; let DecoderMethod = "DecodeT2AddrModeImm8"; let ParserMatchClass = MemNegImm8OffsetAsmOperand; let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } // t2addrmode_imm8 := reg +/- imm8 -def MemImm8OffsetAsmOperand : AsmOperandClass { let Name = "MemImm8Offset"; } +def MemImm8OffsetAsmOperand : AsmOperandClass { + let Name = "MemImm8Offset"; + let RenderMethod = "addMemImmOffsetOperands"; +} class T2AddrMode_Imm8 : MemOperand, ComplexPattern<i32, 2, "SelectT2AddrModeImm8", []> { - let EncoderMethod = "getT2AddrModeImm8OpValue"; + let EncoderMethod = "getT2AddrModeImmOpValue<8,0>"; let DecoderMethod = "DecodeT2AddrModeImm8"; let ParserMatchClass = MemImm8OffsetAsmOperand; let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); @@ -248,10 +287,38 @@ def t2addrmode_imm8s4_pre : T2AddrMode_Imm8s4 { def t2am_imm8s4_offset_asmoperand : AsmOperandClass { let Name = "Imm8s4"; } def t2am_imm8s4_offset : MemOperand { let PrintMethod = "printT2AddrModeImm8s4OffsetOperand"; - let EncoderMethod = "getT2Imm8s4OpValue"; + let EncoderMethod = "getT2ScaledImmOpValue<8,2>"; let DecoderMethod = "DecodeT2Imm8S4"; } +// t2addrmode_imm7s4 := reg +/- (imm7 << 2) +def MemImm7s4OffsetAsmOperand : AsmOperandClass {let Name = "MemImm7s4Offset";} +class T2AddrMode_Imm7s4 : MemOperand { + let EncoderMethod = "getT2AddrModeImm7s4OpValue"; + let DecoderMethod = "DecodeT2AddrModeImm7<2,0>"; + let ParserMatchClass = MemImm7s4OffsetAsmOperand; + let MIOperandInfo = (ops GPRnopc:$base, i32imm:$offsimm); +} + +def t2addrmode_imm7s4 : T2AddrMode_Imm7s4 { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8s4Operand<false>"; +} + +def t2addrmode_imm7s4_pre : T2AddrMode_Imm7s4 { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8s4Operand<true>"; +} + +def t2am_imm7s4_offset_asmoperand : AsmOperandClass { let Name = "Imm7s4"; } +def t2am_imm7s4_offset : MemOperand { + // They are printed the same way as the imm8 version + let PrintMethod = "printT2AddrModeImm8s4OffsetOperand"; + let ParserMatchClass = t2am_imm7s4_offset_asmoperand; + let EncoderMethod = "getT2ScaledImmOpValue<7,2>"; + let DecoderMethod = "DecodeT2Imm7S4"; +} + // t2addrmode_imm0_1020s4 := reg + (imm8 << 2) def MemImm0_1020s4OffsetAsmOperand : AsmOperandClass { let Name = "MemImm0_1020s4Offset"; @@ -290,6 +357,75 @@ def addrmode_tbh : MemOperand { let MIOperandInfo = (ops GPR:$Rn, rGPR:$Rm); } +// Define ARMv8.1-M specific addressing modes. + +// Label operands for BF/BFL/WLS/DLS/LE +class BFLabelOp<string signed, string isNeg, string zeroPermitted, string size, + string fixup> + : Operand<OtherVT> { + let EncoderMethod = !strconcat("getBFTargetOpValue<", isNeg, ", ", + fixup, ">"); + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = !strconcat("DecodeBFLabelOperand<", signed, ", ", + isNeg, ", ", zeroPermitted, ", ", size, ">"); +} +def bflabel_u4 : BFLabelOp<"false", "false", "false", "4", "ARM::fixup_bf_branch">; +def bflabel_s12 : BFLabelOp<"true", "false", "true", "12", "ARM::fixup_bfc_target">; +def bflabel_s16 : BFLabelOp<"true", "false", "true", "16", "ARM::fixup_bf_target">; +def bflabel_s18 : BFLabelOp<"true", "false", "true", "18", "ARM::fixup_bfl_target">; + +def wlslabel_u11_asmoperand : AsmOperandClass { + let Name = "WLSLabel"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isUnsignedOffset<11, 1>"; + let DiagnosticString = + "loop end is out of range or not a positive multiple of 2"; +} +def wlslabel_u11 : BFLabelOp<"false", "false", "true", "11", "ARM::fixup_wls"> { + let ParserMatchClass = wlslabel_u11_asmoperand; +} +def lelabel_u11_asmoperand : AsmOperandClass { + let Name = "LELabel"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isLEOffset"; + let DiagnosticString = + "loop start is out of range or not a negative multiple of 2"; +} +def lelabel_u11 : BFLabelOp<"false", "true", "true", "11", "ARM::fixup_le"> { + let ParserMatchClass = lelabel_u11_asmoperand; +} + +def bfafter_target : Operand<OtherVT> { + let EncoderMethod = "getBFAfterTargetOpValue"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBFAfterTargetOperand"; +} + +// pred operand excluding AL +def pred_noal_asmoperand : AsmOperandClass { + let Name = "CondCodeNoAL"; + let RenderMethod = "addITCondCodeOperands"; + let PredicateMethod = "isITCondCodeNoAL"; + let ParserMethod = "parseITCondCode"; +} +def pred_noal : Operand<i32> { + let PrintMethod = "printMandatoryPredicateOperand"; + let ParserMatchClass = pred_noal_asmoperand; + let DecoderMethod = "DecodePredNoALOperand"; +} + + +// CSEL aliases inverted predicate +def pred_noal_inv_asmoperand : AsmOperandClass { + let Name = "CondCodeNoALInv"; + let RenderMethod = "addITCondCodeInvOperands"; + let PredicateMethod = "isITCondCodeNoAL"; + let ParserMethod = "parseITCondCode"; +} +def pred_noal_inv : Operand<i32> { + let PrintMethod = "printMandatoryInvertedPredicateOperand"; + let ParserMatchClass = pred_noal_inv_asmoperand; +} //===----------------------------------------------------------------------===// // Multiclass helpers... // @@ -604,6 +740,17 @@ multiclass T2I_bin_irs<bits<4> opcod, string opc, let Inst{31-27} = 0b11101; let Inst{26-25} = 0b01; let Inst{24-21} = opcod; + let Inst{15} = 0b0; + // In most of these instructions, and most versions of the Arm + // architecture, bit 15 of this encoding is listed as (0) rather + // than 0, i.e. setting it to 1 is UNPREDICTABLE or a soft-fail + // rather than a hard failure. In v8.1-M, this requirement is + // upgraded to a hard one for ORR, so that the encodings with 1 + // in this bit can be reused for other instructions (such as + // CSEL). Setting Unpredictable{15} = 1 here would reintroduce + // that encoding clash in the auto- generated MC decoder, so I + // comment it out. + let Unpredictable{15} = !if(!eq(opcod, 0b0010), 0b0, 0b1); let Inst{14-12} = 0b000; // imm3 let Inst{7-6} = 0b00; // imm2 let Inst{5-4} = 0b00; // type @@ -617,6 +764,8 @@ multiclass T2I_bin_irs<bits<4> opcod, string opc, let Inst{31-27} = 0b11101; let Inst{26-25} = 0b01; let Inst{24-21} = opcod; + let Inst{15} = 0; + let Unpredictable{15} = !if(!eq(opcod, 0b0010), 0b0, 0b1); // see above } // Assembly aliases for optional destination operand when it's the same // as the source operand. @@ -880,6 +1029,7 @@ multiclass T2I_sh_ir<bits<2> opcod, string opc, Operand ty, SDNode opnode> { let Inst{31-27} = 0b11101; let Inst{26-21} = 0b010010; let Inst{19-16} = 0b1111; // Rn + let Inst{15} = 0b0; let Inst{5-4} = opcod; } // register @@ -923,15 +1073,15 @@ multiclass T2I_sh_ir<bits<2> opcod, string opc, Operand ty, SDNode opnode> { /// T2I_cmp_irs - Defines a set of (op r, {so_imm|r|so_reg}) cmp / test /// patterns. Similar to T2I_bin_irs except the instruction does not produce /// a explicit result, only implicitly set CPSR. -multiclass T2I_cmp_irs<bits<4> opcod, string opc, +multiclass T2I_cmp_irs<bits<4> opcod, string opc, RegisterClass LHSGPR, InstrItinClass iii, InstrItinClass iir, InstrItinClass iis, SDPatternOperator opnode> { let isCompare = 1, Defs = [CPSR] in { // shifted imm def ri : T2OneRegCmpImm< - (outs), (ins GPRnopc:$Rn, t2_so_imm:$imm), iii, + (outs), (ins LHSGPR:$Rn, t2_so_imm:$imm), iii, opc, ".w\t$Rn, $imm", - [(opnode GPRnopc:$Rn, t2_so_imm:$imm)]>, Sched<[WriteCMP]> { + [(opnode LHSGPR:$Rn, t2_so_imm:$imm)]>, Sched<[WriteCMP]> { let Inst{31-27} = 0b11110; let Inst{25} = 0; let Inst{24-21} = opcod; @@ -941,9 +1091,9 @@ let isCompare = 1, Defs = [CPSR] in { } // register def rr : T2TwoRegCmp< - (outs), (ins GPRnopc:$Rn, rGPR:$Rm), iir, + (outs), (ins LHSGPR:$Rn, rGPR:$Rm), iir, opc, ".w\t$Rn, $Rm", - [(opnode GPRnopc:$Rn, rGPR:$Rm)]>, Sched<[WriteCMP]> { + [(opnode LHSGPR:$Rn, rGPR:$Rm)]>, Sched<[WriteCMP]> { let Inst{31-27} = 0b11101; let Inst{26-25} = 0b01; let Inst{24-21} = opcod; @@ -955,9 +1105,9 @@ let isCompare = 1, Defs = [CPSR] in { } // shifted register def rs : T2OneRegCmpShiftedReg< - (outs), (ins GPRnopc:$Rn, t2_so_reg:$ShiftedRm), iis, + (outs), (ins LHSGPR:$Rn, t2_so_reg:$ShiftedRm), iis, opc, ".w\t$Rn, $ShiftedRm", - [(opnode GPRnopc:$Rn, t2_so_reg:$ShiftedRm)]>, + [(opnode LHSGPR:$Rn, t2_so_reg:$ShiftedRm)]>, Sched<[WriteCMPsi]> { let Inst{31-27} = 0b11101; let Inst{26-25} = 0b01; @@ -971,9 +1121,9 @@ let isCompare = 1, Defs = [CPSR] in { // No alias here for 'rr' version as not all instantiations of this // multiclass want one (CMP in particular, does not). def : t2InstAlias<!strconcat(opc, "${p}", " $Rn, $imm"), - (!cast<Instruction>(NAME#"ri") GPRnopc:$Rn, t2_so_imm:$imm, pred:$p)>; + (!cast<Instruction>(NAME#"ri") LHSGPR:$Rn, t2_so_imm:$imm, pred:$p)>; def : t2InstAlias<!strconcat(opc, "${p}", " $Rn, $shift"), - (!cast<Instruction>(NAME#"rs") GPRnopc:$Rn, t2_so_reg:$shift, pred:$p)>; + (!cast<Instruction>(NAME#"rs") LHSGPR:$Rn, t2_so_reg:$shift, pred:$p)>; } /// T2I_ld - Defines a set of (op r, {imm12|imm8|so_reg}) load patterns. @@ -1334,7 +1484,8 @@ def t2LDRB_PRE : T2Ipreldst<0, 0b00, 1, 1, (outs GPR:$Rt, GPR:$Rn_wb), def t2LDRB_POST : T2Ipostldst<0, 0b00, 1, 0, (outs GPR:$Rt, GPR:$Rn_wb), (ins addr_offset_none:$Rn, t2am_imm8_offset:$offset), AddrModeT2_i8, IndexModePost, IIC_iLoad_bh_iu, - "ldrb", "\t$Rt, $Rn$offset", "$Rn = $Rn_wb", []>; + "ldrb", "\t$Rt, $Rn$offset", "$Rn = $Rn_wb", []>, + Sched<[WriteLd]>; def t2LDRH_PRE : T2Ipreldst<0, 0b01, 1, 1, (outs GPR:$Rt, GPR:$Rn_wb), (ins t2addrmode_imm8_pre:$addr), @@ -1872,6 +2023,7 @@ def t2MOVr : T2sTwoReg<(outs GPRnopc:$Rd), (ins GPRnopc:$Rm), IIC_iMOVr, let Inst{26-25} = 0b01; let Inst{24-21} = 0b0010; let Inst{19-16} = 0b1111; // Rn + let Inst{15} = 0b0; let Inst{14-12} = 0b000; let Inst{7-4} = 0b0000; } @@ -2148,6 +2300,11 @@ def : T2Pat<(add GPR:$src, imm0_4095_neg:$imm), def : T2Pat<(add GPR:$src, imm0_65535_neg:$imm), (t2SUBrr GPR:$src, (t2MOVi16 (imm_neg_XFORM imm:$imm)))>; +// Do the same for v8m targets since they support movw with a 16-bit value. +def : T1Pat<(add tGPR:$src, imm0_65535_neg:$imm), + (tSUBrr tGPR:$src, (t2MOVi16 (imm_neg_XFORM imm:$imm)))>, + Requires<[HasV8MBaseline]>; + let AddedComplexity = 1 in def : T2Pat<(ARMaddc rGPR:$src, imm1_255_neg:$imm), (t2SUBSri rGPR:$src, imm1_255_neg:$imm)>; @@ -2327,14 +2484,14 @@ class T2SatI<dag iops, string opc, string asm> def t2SSAT: T2SatI<(ins imm1_32:$sat_imm, rGPR:$Rn, t2_shift_imm:$sh), "ssat", "\t$Rd, $sat_imm, $Rn$sh">, - Requires<[IsThumb2]> { + Requires<[IsThumb2]>, Sched<[WriteALU]> { let Inst{23-22} = 0b00; let Inst{5} = 0; } def t2SSAT16: T2SatI<(ins imm1_16:$sat_imm, rGPR:$Rn), "ssat16", "\t$Rd, $sat_imm, $Rn">, - Requires<[IsThumb2, HasDSP]> { + Requires<[IsThumb2, HasDSP]>, Sched<[WriteALU]> { let Inst{23-22} = 0b00; let sh = 0b100000; let Inst{4} = 0; @@ -2342,13 +2499,13 @@ def t2SSAT16: T2SatI<(ins imm1_16:$sat_imm, rGPR:$Rn), def t2USAT: T2SatI<(ins imm0_31:$sat_imm, rGPR:$Rn, t2_shift_imm:$sh), "usat", "\t$Rd, $sat_imm, $Rn$sh">, - Requires<[IsThumb2]> { + Requires<[IsThumb2]>, Sched<[WriteALU]> { let Inst{23-22} = 0b10; } def t2USAT16: T2SatI<(ins imm0_15:$sat_imm, rGPR:$Rn), "usat16", "\t$Rd, $sat_imm, $Rn">, - Requires<[IsThumb2, HasDSP]> { + Requires<[IsThumb2, HasDSP]>, Sched<[WriteALU]> { let Inst{23-22} = 0b10; let sh = 0b100000; let Inst{4} = 0; @@ -2395,6 +2552,8 @@ def t2RRX : T2sTwoReg<(outs rGPR:$Rd), (ins rGPR:$Rm), IIC_iMOVsi, let Inst{26-25} = 0b01; let Inst{24-21} = 0b0010; let Inst{19-16} = 0b1111; // Rn + let Inst{15} = 0b0; + let Unpredictable{15} = 0b1; let Inst{14-12} = 0b000; let Inst{7-4} = 0b0011; } @@ -2472,7 +2631,7 @@ class T2TwoRegBitFI<dag oops, dag iops, InstrItinClass itin, let Constraints = "$src = $Rd" in def t2BFC : T2BitFI<(outs rGPR:$Rd), (ins rGPR:$src, bf_inv_mask_imm:$imm), IIC_iUNAsi, "bfc", "\t$Rd, $imm", - [(set rGPR:$Rd, (and rGPR:$src, bf_inv_mask_imm:$imm))]> { + [(set rGPR:$Rd, (and rGPR:$src, bf_inv_mask_imm:$imm))]>, Sched<[WriteALU]> { let Inst{31-27} = 0b11110; let Inst{26} = 0; // should be 0. let Inst{25} = 1; @@ -2488,7 +2647,7 @@ def t2BFC : T2BitFI<(outs rGPR:$Rd), (ins rGPR:$src, bf_inv_mask_imm:$imm), def t2SBFX: T2TwoRegBitFI< (outs rGPR:$Rd), (ins rGPR:$Rn, imm0_31:$lsb, imm1_32:$msb), - IIC_iUNAsi, "sbfx", "\t$Rd, $Rn, $lsb, $msb", []> { + IIC_iUNAsi, "sbfx", "\t$Rd, $Rn, $lsb, $msb", []>, Sched<[WriteALU]> { let Inst{31-27} = 0b11110; let Inst{25} = 1; let Inst{24-20} = 0b10100; @@ -2497,7 +2656,7 @@ def t2SBFX: T2TwoRegBitFI< def t2UBFX: T2TwoRegBitFI< (outs rGPR:$Rd), (ins rGPR:$Rn, imm0_31:$lsb, imm1_32:$msb), - IIC_iUNAsi, "ubfx", "\t$Rd, $Rn, $lsb, $msb", []> { + IIC_iUNAsi, "ubfx", "\t$Rd, $Rn, $lsb, $msb", []>, Sched<[WriteALU]> { let Inst{31-27} = 0b11110; let Inst{25} = 1; let Inst{24-20} = 0b11100; @@ -2523,7 +2682,7 @@ let Constraints = "$src = $Rd" in { (ins rGPR:$src, rGPR:$Rn, bf_inv_mask_imm:$imm), IIC_iBITi, "bfi", "\t$Rd, $Rn, $imm", [(set rGPR:$Rd, (ARMbfi rGPR:$src, rGPR:$Rn, - bf_inv_mask_imm:$imm))]> { + bf_inv_mask_imm:$imm))]>, Sched<[WriteALU]> { let Inst{31-27} = 0b11110; let Inst{26} = 0; // should be 0. let Inst{25} = 1; @@ -2597,7 +2756,8 @@ def : T2Pat<(and rGPR:$src, t2_so_imm_not:$imm), // top16Zero - answer true if the upper 16 bits of $src are 0, false otherwise def top16Zero: PatLeaf<(i32 rGPR:$src), [{ - return CurDAG->MaskedValueIsZero(SDValue(N,0), APInt::getHighBitsSet(32, 16)); + return !SDValue(N,0)->getValueType(0).isVector() && + CurDAG->MaskedValueIsZero(SDValue(N,0), APInt::getHighBitsSet(32, 16)); }]>; // so_imm_notSext is needed instead of so_imm_not, as the value of imm @@ -3054,7 +3214,7 @@ def t2CRC32CW : T2I_crc32<1, 0b10, "cw", int_arm_crc32cw>; //===----------------------------------------------------------------------===// // Comparison Instructions... // -defm t2CMP : T2I_cmp_irs<0b1101, "cmp", +defm t2CMP : T2I_cmp_irs<0b1101, "cmp", GPRnopc, IIC_iCMPi, IIC_iCMPr, IIC_iCMPsi, ARMcmp>; def : T2Pat<(ARMcmpZ GPRnopc:$lhs, t2_so_imm:$imm), @@ -3122,10 +3282,10 @@ def : T2Pat<(ARMcmp GPR:$src, t2_so_imm_neg:$imm), def : T2Pat<(ARMcmpZ GPRnopc:$src, t2_so_imm_neg:$imm), (t2CMNri GPRnopc:$src, t2_so_imm_neg:$imm)>; -defm t2TST : T2I_cmp_irs<0b0000, "tst", +defm t2TST : T2I_cmp_irs<0b0000, "tst", rGPR, IIC_iTSTi, IIC_iTSTr, IIC_iTSTsi, BinOpFrag<(ARMcmpZ (and_su node:$LHS, node:$RHS), 0)>>; -defm t2TEQ : T2I_cmp_irs<0b0100, "teq", +defm t2TEQ : T2I_cmp_irs<0b0100, "teq", rGPR, IIC_iTSTi, IIC_iTSTr, IIC_iTSTsi, BinOpFrag<(ARMcmpZ (xor_su node:$LHS, node:$RHS), 0)>>; @@ -3277,17 +3437,17 @@ def t2LDREXB : T2I_ldrex<0b0100, (outs rGPR:$Rt), (ins addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "ldrexb", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldrex_1 addr_offset_none:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]>; + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteLd]>; def t2LDREXH : T2I_ldrex<0b0101, (outs rGPR:$Rt), (ins addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "ldrexh", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldrex_2 addr_offset_none:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]>; + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteLd]>; def t2LDREX : Thumb2I<(outs rGPR:$Rt), (ins t2addrmode_imm0_1020s4:$addr), AddrModeT2_ldrex, 4, NoItinerary, "ldrex", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldrex_4 t2addrmode_imm0_1020s4:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]> { + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteLd]> { bits<4> Rt; bits<12> addr; let Inst{31-27} = 0b11101; @@ -3303,7 +3463,7 @@ def t2LDREXD : T2I_ldrex<0b0111, (outs rGPR:$Rt, rGPR:$Rt2), AddrModeNone, 4, NoItinerary, "ldrexd", "\t$Rt, $Rt2, $addr", "", [], {?, ?, ?, ?}>, - Requires<[IsThumb2, IsNotMClass]> { + Requires<[IsThumb2, IsNotMClass]>, Sched<[WriteLd]> { bits<4> Rt2; let Inst{11-8} = Rt2; } @@ -3311,17 +3471,17 @@ def t2LDAEXB : T2I_ldrex<0b1100, (outs rGPR:$Rt), (ins addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "ldaexb", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldaex_1 addr_offset_none:$addr))]>, - Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>; + Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>, Sched<[WriteLd]>; def t2LDAEXH : T2I_ldrex<0b1101, (outs rGPR:$Rt), (ins addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "ldaexh", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldaex_2 addr_offset_none:$addr))]>, - Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>; + Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>, Sched<[WriteLd]>; def t2LDAEX : Thumb2I<(outs rGPR:$Rt), (ins addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "ldaex", "\t$Rt, $addr", "", [(set rGPR:$Rt, (ldaex_4 addr_offset_none:$addr))]>, - Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]> { + Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>, Sched<[WriteLd]> { bits<4> Rt; bits<4> addr; let Inst{31-27} = 0b11101; @@ -3337,7 +3497,7 @@ def t2LDAEXD : T2I_ldrex<0b1111, (outs rGPR:$Rt, rGPR:$Rt2), AddrModeNone, 4, NoItinerary, "ldaexd", "\t$Rt, $Rt2, $addr", "", [], {?, ?, ?, ?}>, Requires<[IsThumb, - HasAcquireRelease, HasV7Clrex, IsNotMClass]> { + HasAcquireRelease, HasV7Clrex, IsNotMClass]>, Sched<[WriteLd]> { bits<4> Rt2; let Inst{11-8} = Rt2; @@ -3352,14 +3512,14 @@ def t2STREXB : T2I_strex<0b0100, (outs rGPR:$Rd), "strexb", "\t$Rd, $Rt, $addr", "", [(set rGPR:$Rd, (strex_1 rGPR:$Rt, addr_offset_none:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]>; + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteST]>; def t2STREXH : T2I_strex<0b0101, (outs rGPR:$Rd), (ins rGPR:$Rt, addr_offset_none:$addr), AddrModeNone, 4, NoItinerary, "strexh", "\t$Rd, $Rt, $addr", "", [(set rGPR:$Rd, (strex_2 rGPR:$Rt, addr_offset_none:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]>; + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteST]>; def t2STREX : Thumb2I<(outs rGPR:$Rd), (ins rGPR:$Rt, t2addrmode_imm0_1020s4:$addr), @@ -3367,7 +3527,7 @@ def t2STREX : Thumb2I<(outs rGPR:$Rd), (ins rGPR:$Rt, "strex", "\t$Rd, $Rt, $addr", "", [(set rGPR:$Rd, (strex_4 rGPR:$Rt, t2addrmode_imm0_1020s4:$addr))]>, - Requires<[IsThumb, HasV8MBaseline]> { + Requires<[IsThumb, HasV8MBaseline]>, Sched<[WriteST]> { bits<4> Rd; bits<4> Rt; bits<12> addr; @@ -3384,7 +3544,7 @@ def t2STREXD : T2I_strex<0b0111, (outs rGPR:$Rd), AddrModeNone, 4, NoItinerary, "strexd", "\t$Rd, $Rt, $Rt2, $addr", "", [], {?, ?, ?, ?}>, - Requires<[IsThumb2, IsNotMClass]> { + Requires<[IsThumb2, IsNotMClass]>, Sched<[WriteST]> { bits<4> Rt2; let Inst{11-8} = Rt2; } @@ -3395,7 +3555,7 @@ def t2STLEXB : T2I_strex<0b1100, (outs rGPR:$Rd), [(set rGPR:$Rd, (stlex_1 rGPR:$Rt, addr_offset_none:$addr))]>, Requires<[IsThumb, HasAcquireRelease, - HasV7Clrex]>; + HasV7Clrex]>, Sched<[WriteST]>; def t2STLEXH : T2I_strex<0b1101, (outs rGPR:$Rd), (ins rGPR:$Rt, addr_offset_none:$addr), @@ -3404,7 +3564,7 @@ def t2STLEXH : T2I_strex<0b1101, (outs rGPR:$Rd), [(set rGPR:$Rd, (stlex_2 rGPR:$Rt, addr_offset_none:$addr))]>, Requires<[IsThumb, HasAcquireRelease, - HasV7Clrex]>; + HasV7Clrex]>, Sched<[WriteST]>; def t2STLEX : Thumb2I<(outs rGPR:$Rd), (ins rGPR:$Rt, addr_offset_none:$addr), @@ -3412,7 +3572,8 @@ def t2STLEX : Thumb2I<(outs rGPR:$Rd), (ins rGPR:$Rt, "stlex", "\t$Rd, $Rt, $addr", "", [(set rGPR:$Rd, (stlex_4 rGPR:$Rt, addr_offset_none:$addr))]>, - Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]> { + Requires<[IsThumb, HasAcquireRelease, HasV7Clrex]>, + Sched<[WriteST]> { bits<4> Rd; bits<4> Rt; bits<4> addr; @@ -3429,7 +3590,7 @@ def t2STLEXD : T2I_strex<0b1111, (outs rGPR:$Rd), AddrModeNone, 4, NoItinerary, "stlexd", "\t$Rd, $Rt, $Rt2, $addr", "", [], {?, ?, ?, ?}>, Requires<[IsThumb, HasAcquireRelease, - HasV7Clrex, IsNotMClass]> { + HasV7Clrex, IsNotMClass]>, Sched<[WriteST]> { bits<4> Rt2; let Inst{11-8} = Rt2; } @@ -4547,9 +4708,9 @@ def : t2InstAlias<"sub${s}${p} $Rdn, $ShiftedRm", def : t2InstAlias<"cmn${p} $Rn, $Rm", (t2CMNzrr GPRnopc:$Rn, rGPR:$Rm, pred:$p)>; def : t2InstAlias<"teq${p} $Rn, $Rm", - (t2TEQrr GPRnopc:$Rn, rGPR:$Rm, pred:$p)>; + (t2TEQrr rGPR:$Rn, rGPR:$Rm, pred:$p)>; def : t2InstAlias<"tst${p} $Rn, $Rm", - (t2TSTrr GPRnopc:$Rn, rGPR:$Rm, pred:$p)>; + (t2TSTrr rGPR:$Rn, rGPR:$Rm, pred:$p)>; // Memory barriers def : InstAlias<"dmb${p}", (t2DMB 0xf, pred:$p), 0>, Requires<[HasDB]>; @@ -4888,3 +5049,227 @@ def : t2InstAlias<"pld${p} $addr", def : InstAlias<"pli${p} $addr", (t2PLIpci t2ldr_pcrel_imm12:$addr, pred:$p), 0>, Requires<[IsThumb2,HasV7]>; + + +//===----------------------------------------------------------------------===// +// ARMv8.1m instructions +// + +class V8_1MI<dag oops, dag iops, AddrMode am, InstrItinClass itin, string asm, + string ops, string cstr, list<dag> pattern> + : Thumb2XI<oops, iops, am, 4, itin, !strconcat(asm, "\t", ops), cstr, + pattern>, + Requires<[HasV8_1MMainline]>; + +def t2CLRM : V8_1MI<(outs), + (ins pred:$p, reglist_with_apsr:$regs, variable_ops), + AddrModeNone, NoItinerary, "clrm", "${p}\t$regs", "", []> { + bits<16> regs; + + let Inst{31-16} = 0b1110100010011111; + let Inst{15-14} = regs{15-14}; + let Inst{13} = 0b0; + let Inst{12-0} = regs{12-0}; +} + +class t2BF<dag iops, string asm, string ops> + : V8_1MI<(outs ), iops, AddrModeNone, NoItinerary, asm, ops, "", []> { + + let Inst{31-27} = 0b11110; + let Inst{15-14} = 0b11; + let Inst{12} = 0b0; + let Inst{0} = 0b1; + + let Predicates = [IsThumb2, HasV8_1MMainline, HasLOB]; +} + +def t2BF_LabelPseudo + : t2PseudoInst<(outs ), (ins pclabel:$cp), 0, NoItinerary, []> { + let isTerminator = 1; + let Predicates = [IsThumb2, HasV8_1MMainline, HasLOB]; +} + +def t2BFi : t2BF<(ins bflabel_u4:$b_label, bflabel_s16:$label, pred:$p), + !strconcat("bf", "${p}"), "$b_label, $label"> { + bits<4> b_label; + bits<16> label; + + let Inst{26-23} = b_label{3-0}; + let Inst{22-21} = 0b10; + let Inst{20-16} = label{15-11}; + let Inst{13} = 0b1; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def t2BFic : t2BF<(ins bflabel_u4:$b_label, bflabel_s12:$label, + bfafter_target:$ba_label, pred_noal:$bcond), "bfcsel", + "$b_label, $label, $ba_label, $bcond"> { + bits<4> bcond; + bits<12> label; + bits<1> ba_label; + bits<4> b_label; + + let Inst{26-23} = b_label{3-0}; + let Inst{22} = 0b0; + let Inst{21-18} = bcond{3-0}; + let Inst{17} = ba_label{0}; + let Inst{16} = label{11}; + let Inst{13} = 0b1; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def t2BFr : t2BF<(ins bflabel_u4:$b_label, rGPR:$Rn, pred:$p), + !strconcat("bfx", "${p}"), "$b_label, $Rn"> { + bits<4> b_label; + bits<4> Rn; + + let Inst{26-23} = b_label{3-0}; + let Inst{22-20} = 0b110; + let Inst{19-16} = Rn{3-0}; + let Inst{13-1} = 0b1000000000000; +} + +def t2BFLi : t2BF<(ins bflabel_u4:$b_label, bflabel_s18:$label, pred:$p), + !strconcat("bfl", "${p}"), "$b_label, $label"> { + bits<4> b_label; + bits<18> label; + + let Inst{26-23} = b_label{3-0}; + let Inst{22-16} = label{17-11}; + let Inst{13} = 0b0; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def t2BFLr : t2BF<(ins bflabel_u4:$b_label, rGPR:$Rn, pred:$p), + !strconcat("bflx", "${p}"), "$b_label, $Rn"> { + bits<4> b_label; + bits<4> Rn; + + let Inst{26-23} = b_label{3-0}; + let Inst{22-20} = 0b111; + let Inst{19-16} = Rn{3-0}; + let Inst{13-1} = 0b1000000000000; +} + +class t2LOL<dag oops, dag iops, string asm, string ops> + : V8_1MI<oops, iops, AddrModeNone, NoItinerary, asm, ops, "", [] > { + let Inst{31-23} = 0b111100000; + let Inst{15-14} = 0b11; + let Inst{0} = 0b1; + let isBranch = 1; + let isTerminator = 1; + let DecoderMethod = "DecodeLOLoop"; + let Predicates = [IsThumb2, HasV8_1MMainline, HasLOB]; +} + +let isNotDuplicable = 1 in { +def t2WLS : t2LOL<(outs GPRlr:$LR), + (ins rGPR:$Rn, wlslabel_u11:$label), + "wls", "$LR, $Rn, $label"> { + bits<4> Rn; + bits<11> label; + let Inst{22-20} = 0b100; + let Inst{19-16} = Rn{3-0}; + let Inst{13-12} = 0b00; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; + let usesCustomInserter = 1; +} + +def t2DLS : t2LOL<(outs GPRlr:$LR), (ins rGPR:$Rn), + "dls", "$LR, $Rn"> { + bits<4> Rn; + let isBranch = 0; + let isTerminator = 0; + let Inst{22-20} = 0b100; + let Inst{19-16} = Rn{3-0}; + let Inst{13-1} = 0b1000000000000; + let usesCustomInserter = 1; +} + +def t2LEUpdate : t2LOL<(outs GPRlr:$LRout), + (ins GPRlr:$LRin, lelabel_u11:$label), + "le", "$LRin, $label"> { + bits<11> label; + let Inst{22-16} = 0b0001111; + let Inst{13-12} = 0b00; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; + let usesCustomInserter = 1; +} + +def t2LE : t2LOL<(outs ), (ins lelabel_u11:$label), "le", "$label"> { + bits<11> label; + let Inst{22-16} = 0b0101111; + let Inst{13-12} = 0b00; + let Inst{11} = label{0}; + let Inst{10-1} = label{10-1}; +} + +def t2DoLoopStart : + t2PseudoInst<(outs), (ins rGPR:$elts), 4, IIC_Br, + [(int_set_loop_iterations rGPR:$elts)]>, Sched<[WriteBr]>; + +def t2LoopDec : + t2PseudoInst<(outs GPRlr:$Rm), (ins GPRlr:$Rn, imm0_7:$size), + 4, IIC_Br, []>, Sched<[WriteBr]>; + +let isBranch = 1, isTerminator = 1, hasSideEffects = 1 in { +def t2WhileLoopStart : + t2PseudoInst<(outs), + (ins rGPR:$elts, brtarget:$target), + 4, IIC_Br, []>, + Sched<[WriteBr]>; + +def t2LoopEnd : + t2PseudoInst<(outs), (ins GPRlr:$elts, brtarget:$target), + 8, IIC_Br, []>, Sched<[WriteBr]>; + +} // end isBranch, isTerminator, hasSideEffects + +} // end isNotDuplicable + +class CS<string iname, bits<4> opcode, list<dag> pattern=[]> + : V8_1MI<(outs rGPR:$Rd), (ins GPRwithZR:$Rn, GPRwithZRnosp:$Rm, pred_noal:$fcond), + AddrModeNone, NoItinerary, iname, "$Rd, $Rn, $Rm, $fcond", "", pattern> { + bits<4> Rd; + bits<4> Rm; + bits<4> Rn; + bits<4> fcond; + + let Inst{31-20} = 0b111010100101; + let Inst{19-16} = Rn{3-0}; + let Inst{15-12} = opcode; + let Inst{11-8} = Rd{3-0}; + let Inst{7-4} = fcond{3-0}; + let Inst{3-0} = Rm{3-0}; + + let Uses = [CPSR]; +} + +def t2CSEL : CS<"csel", 0b1000>; +def t2CSINC : CS<"csinc", 0b1001>; +def t2CSINV : CS<"csinv", 0b1010>; +def t2CSNEG : CS<"csneg", 0b1011>; + + +// CS aliases. +let Predicates = [HasV8_1MMainline] in { + def : InstAlias<"csetm\t$Rd, $fcond", + (t2CSINV rGPR:$Rd, ZR, ZR, pred_noal_inv:$fcond)>; + + def : InstAlias<"cset\t$Rd, $fcond", + (t2CSINC rGPR:$Rd, ZR, ZR, pred_noal_inv:$fcond)>; + + def : InstAlias<"cinc\t$Rd, $Rn, $fcond", + (t2CSINC rGPR:$Rd, GPRwithZRnosp:$Rn, GPRwithZRnosp:$Rn, pred_noal_inv:$fcond)>; + + def : InstAlias<"cinv\t$Rd, $Rn, $fcond", + (t2CSINV rGPR:$Rd, GPRwithZRnosp:$Rn, GPRwithZRnosp:$Rn, pred_noal_inv:$fcond)>; + + def : InstAlias<"cneg\t$Rd, $Rn, $fcond", + (t2CSNEG rGPR:$Rd, GPRwithZRnosp:$Rn, GPRwithZRnosp:$Rn, pred_noal_inv:$fcond)>; +} diff --git a/lib/Target/ARM/ARMInstrVFP.td b/lib/Target/ARM/ARMInstrVFP.td index b58730c452f7..a0dd25de07ee 100644 --- a/lib/Target/ARM/ARMInstrVFP.td +++ b/lib/Target/ARM/ARMInstrVFP.td @@ -1,9 +1,8 @@ //===-- ARMInstrVFP.td - VFP support for ARM ---------------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -53,28 +52,50 @@ def vfp_f16imm : Operand<f16>, let ParserMatchClass = FPImmOperand; } -def vfp_f32imm : Operand<f32>, - PatLeaf<(f32 fpimm), [{ - return ARM_AM::getFP32Imm(N->getValueAPF()) != -1; - }], SDNodeXForm<fpimm, [{ +def vfp_f32imm_xform : SDNodeXForm<fpimm, [{ APFloat InVal = N->getValueAPF(); uint32_t enc = ARM_AM::getFP32Imm(InVal); return CurDAG->getTargetConstant(enc, SDLoc(N), MVT::i32); - }]>> { + }]>; + +def gi_vfp_f32imm : GICustomOperandRenderer<"renderVFPF32Imm">, + GISDNodeXFormEquiv<vfp_f32imm_xform>; + +def vfp_f32imm : Operand<f32>, + PatLeaf<(f32 fpimm), [{ + return ARM_AM::getFP32Imm(N->getValueAPF()) != -1; + }], vfp_f32imm_xform> { let PrintMethod = "printFPImmOperand"; let ParserMatchClass = FPImmOperand; + let GISelPredicateCode = [{ + const auto &MO = MI.getOperand(1); + if (!MO.isFPImm()) + return false; + return ARM_AM::getFP32Imm(MO.getFPImm()->getValueAPF()) != -1; + }]; } -def vfp_f64imm : Operand<f64>, - PatLeaf<(f64 fpimm), [{ - return ARM_AM::getFP64Imm(N->getValueAPF()) != -1; - }], SDNodeXForm<fpimm, [{ +def vfp_f64imm_xform : SDNodeXForm<fpimm, [{ APFloat InVal = N->getValueAPF(); uint32_t enc = ARM_AM::getFP64Imm(InVal); return CurDAG->getTargetConstant(enc, SDLoc(N), MVT::i32); - }]>> { + }]>; + +def gi_vfp_f64imm : GICustomOperandRenderer<"renderVFPF64Imm">, + GISDNodeXFormEquiv<vfp_f64imm_xform>; + +def vfp_f64imm : Operand<f64>, + PatLeaf<(f64 fpimm), [{ + return ARM_AM::getFP64Imm(N->getValueAPF()) != -1; + }], vfp_f64imm_xform> { let PrintMethod = "printFPImmOperand"; let ParserMatchClass = FPImmOperand; + let GISelPredicateCode = [{ + const auto &MO = MI.getOperand(1); + if (!MO.isFPImm()) + return false; + return ARM_AM::getFP64Imm(MO.getFPImm()->getValueAPF()) != -1; + }]; } def alignedload16 : PatFrag<(ops node:$ptr), (load node:$ptr), [{ @@ -120,39 +141,45 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in { def VLDRD : ADI5<0b1101, 0b01, (outs DPR:$Dd), (ins addrmode5:$addr), IIC_fpLoad64, "vldr", "\t$Dd, $addr", - [(set DPR:$Dd, (f64 (alignedload32 addrmode5:$addr)))]>; + [(set DPR:$Dd, (f64 (alignedload32 addrmode5:$addr)))]>, + Requires<[HasFPRegs]>; def VLDRS : ASI5<0b1101, 0b01, (outs SPR:$Sd), (ins addrmode5:$addr), IIC_fpLoad32, "vldr", "\t$Sd, $addr", - [(set SPR:$Sd, (alignedload32 addrmode5:$addr))]> { + [(set SPR:$Sd, (alignedload32 addrmode5:$addr))]>, + Requires<[HasFPRegs]> { // Some single precision VFP instructions may be executed on both NEON and VFP // pipelines. let D = VFPNeonDomain; } +let isUnpredicable = 1 in def VLDRH : AHI5<0b1101, 0b01, (outs HPR:$Sd), (ins addrmode5fp16:$addr), IIC_fpLoad16, "vldr", ".16\t$Sd, $addr", [(set HPR:$Sd, (alignedload16 addrmode5fp16:$addr))]>, - Requires<[HasFullFP16]>; + Requires<[HasFPRegs16]>; } // End of 'let canFoldAsLoad = 1, isReMaterializable = 1 in' def VSTRD : ADI5<0b1101, 0b00, (outs), (ins DPR:$Dd, addrmode5:$addr), IIC_fpStore64, "vstr", "\t$Dd, $addr", - [(alignedstore32 (f64 DPR:$Dd), addrmode5:$addr)]>; + [(alignedstore32 (f64 DPR:$Dd), addrmode5:$addr)]>, + Requires<[HasFPRegs]>; def VSTRS : ASI5<0b1101, 0b00, (outs), (ins SPR:$Sd, addrmode5:$addr), IIC_fpStore32, "vstr", "\t$Sd, $addr", - [(alignedstore32 SPR:$Sd, addrmode5:$addr)]> { + [(alignedstore32 SPR:$Sd, addrmode5:$addr)]>, + Requires<[HasFPRegs]> { // Some single precision VFP instructions may be executed on both NEON and VFP // pipelines. let D = VFPNeonDomain; } +let isUnpredicable = 1 in def VSTRH : AHI5<0b1101, 0b00, (outs), (ins HPR:$Sd, addrmode5fp16:$addr), IIC_fpStore16, "vstr", ".16\t$Sd, $addr", [(alignedstore16 HPR:$Sd, addrmode5fp16:$addr)]>, - Requires<[HasFullFP16]>; + Requires<[HasFPRegs16]>; //===----------------------------------------------------------------------===// // Load / store multiple Instructions. @@ -160,6 +187,7 @@ def VSTRH : AHI5<0b1101, 0b00, (outs), (ins HPR:$Sd, addrmode5fp16:$addr), multiclass vfp_ldst_mult<string asm, bit L_bit, InstrItinClass itin, InstrItinClass itin_upd> { + let Predicates = [HasFPRegs] in { // Double Precision def DIA : AXDI4<(outs), (ins GPR:$Rn, pred:$p, dpr_reglist:$regs, variable_ops), @@ -227,6 +255,7 @@ multiclass vfp_ldst_mult<string asm, bit L_bit, // VFP pipelines. let D = VFPNeonDomain; } + } } let hasSideEffects = 0 in { @@ -273,13 +302,13 @@ def VLSTM : AXSI4<(outs), (ins GPRnopc:$Rn, pred:$p), IndexModeNone, } def : InstAlias<"vpush${p} $r", (VSTMDDB_UPD SP, pred:$p, dpr_reglist:$r), 0>, - Requires<[HasVFP2]>; + Requires<[HasFPRegs]>; def : InstAlias<"vpush${p} $r", (VSTMSDB_UPD SP, pred:$p, spr_reglist:$r), 0>, - Requires<[HasVFP2]>; + Requires<[HasFPRegs]>; def : InstAlias<"vpop${p} $r", (VLDMDIA_UPD SP, pred:$p, dpr_reglist:$r), 0>, - Requires<[HasVFP2]>; + Requires<[HasFPRegs]>; def : InstAlias<"vpop${p} $r", (VLDMSIA_UPD SP, pred:$p, spr_reglist:$r), 0>, - Requires<[HasVFP2]>; + Requires<[HasFPRegs]>; defm : VFPDTAnyInstAlias<"vpush${p}", "$r", (VSTMSDB_UPD SP, pred:$p, spr_reglist:$r)>; defm : VFPDTAnyInstAlias<"vpush${p}", "$r", @@ -295,6 +324,7 @@ defm : VFPDTAnyInstAlias<"vpop${p}", "$r", // However, there is no UAL syntax for them, so we keep them around for // (dis)assembly only. multiclass vfp_ldstx_mult<string asm, bit L_bit> { + let Predicates = [HasFPRegs] in { // Unknown precision def XIA : AXXI4<(outs), (ins GPR:$Rn, pred:$p, dpr_reglist:$regs, variable_ops), @@ -317,6 +347,7 @@ multiclass vfp_ldstx_mult<string asm, bit L_bit> { let Inst{21} = 1; // Writeback let Inst{20} = L_bit; } + } } defm FLDM : vfp_ldstx_mult<"fldm", 1>; @@ -452,7 +483,7 @@ def VNMULH : AHbI<0b11100, 0b10, 1, 0, multiclass vsel_inst<string op, bits<2> opc, int CC> { let DecoderNamespace = "VFPV8", PostEncoderMethod = "", - Uses = [CPSR], AddedComplexity = 4 in { + Uses = [CPSR], AddedComplexity = 4, isUnpredicable = 1 in { def H : AHbInp<0b11100, opc, 0, (outs HPR:$Sd), (ins HPR:$Sn, HPR:$Sm), NoItinerary, !strconcat("vsel", op, ".f16\t$Sd, $Sn, $Sm"), @@ -480,7 +511,8 @@ defm VSELEQ : vsel_inst<"eq", 0b00, 0>; defm VSELVS : vsel_inst<"vs", 0b01, 6>; multiclass vmaxmin_inst<string op, bit opc, SDNode SD> { - let DecoderNamespace = "VFPV8", PostEncoderMethod = "" in { + let DecoderNamespace = "VFPV8", PostEncoderMethod = "", + isUnpredicable = 1 in { def H : AHbInp<0b11101, 0b00, opc, (outs HPR:$Sd), (ins HPR:$Sn, HPR:$Sm), NoItinerary, !strconcat(op, ".f16\t$Sd, $Sn, $Sm"), @@ -501,8 +533,8 @@ multiclass vmaxmin_inst<string op, bit opc, SDNode SD> { } } -defm VMAXNM : vmaxmin_inst<"vmaxnm", 0, fmaxnum>; -defm VMINNM : vmaxmin_inst<"vminnm", 1, fminnum>; +defm VFP_VMAXNM : vmaxmin_inst<"vmaxnm", 0, fmaxnum>; +defm VFP_VMINNM : vmaxmin_inst<"vminnm", 1, fminnum>; // Match reassociated forms only if not sign dependent rounding. def : Pat<(fmul (fneg DPR:$a), (f64 DPR:$b)), @@ -571,9 +603,9 @@ def VABSS : ASuIn<0b11101, 0b11, 0b0000, 0b11, 0, } def VABSH : AHuI<0b11101, 0b11, 0b0000, 0b11, 0, - (outs SPR:$Sd), (ins SPR:$Sm), + (outs HPR:$Sd), (ins HPR:$Sm), IIC_fpUNA16, "vabs", ".f16\t$Sd, $Sm", - []>; + [(set HPR:$Sd, (fabs (f16 HPR:$Sm)))]>; let Defs = [FPSCR_NZCV] in { def VCMPEZD : ADuI<0b11101, 0b11, 0b0101, 0b11, 0, @@ -682,8 +714,8 @@ def VCVTBHS: ASuI<0b11101, 0b11, 0b0010, 0b01, 0, (outs SPR:$Sd), (ins SPR:$Sm), Requires<[HasFP16]>, Sched<[WriteFPCVT]>; -def : FullFP16Pat<(f32 (fpextend HPR:$Sm)), - (VCVTBHS (COPY_TO_REGCLASS HPR:$Sm, SPR))>; +def : FP16Pat<(f32 (fpextend HPR:$Sm)), + (VCVTBHS (COPY_TO_REGCLASS HPR:$Sm, SPR))>; def : FP16Pat<(f16_to_fp GPR:$a), (VCVTBHS (COPY_TO_REGCLASS GPR:$a, SPR))>; @@ -693,8 +725,8 @@ def VCVTBSH: ASuI<0b11101, 0b11, 0b0011, 0b01, 0, (outs SPR:$Sd), (ins SPR:$Sm), Requires<[HasFP16]>, Sched<[WriteFPCVT]>; -def : FullFP16Pat<(f16 (fpround SPR:$Sm)), - (COPY_TO_REGCLASS (VCVTBSH SPR:$Sm), HPR)>; +def : FP16Pat<(f16 (fpround SPR:$Sm)), + (COPY_TO_REGCLASS (VCVTBSH SPR:$Sm), HPR)>; def : FP16Pat<(fp_to_f16 SPR:$a), (i32 (COPY_TO_REGCLASS (VCVTBSH SPR:$a), GPR))>; @@ -825,7 +857,7 @@ multiclass vcvt_inst<string opc, bits<2> rm, let Inst{17-16} = rm; - // Encode instruction operands + // Encode instruction operands. let Inst{3-0} = Dm{3-0}; let Inst{5} = Dm{4}; let Inst{8} = 1; @@ -906,9 +938,9 @@ def VNEGH : AHuI<0b11101, 0b11, 0b0001, 0b01, 0, multiclass vrint_inst_zrx<string opc, bit op, bit op2, SDPatternOperator node> { def H : AHuI<0b11101, 0b11, 0b0110, 0b11, 0, - (outs SPR:$Sd), (ins SPR:$Sm), + (outs HPR:$Sd), (ins HPR:$Sm), NoItinerary, !strconcat("vrint", opc), ".f16\t$Sd, $Sm", - []>, + [(set (f16 HPR:$Sd), (node (f16 HPR:$Sm)))]>, Requires<[HasFullFP16]> { let Inst{7} = op2; let Inst{16} = op; @@ -948,11 +980,12 @@ defm VRINTX : vrint_inst_zrx<"x", 1, 0, frint>; multiclass vrint_inst_anpm<string opc, bits<2> rm, SDPatternOperator node = null_frag> { - let PostEncoderMethod = "", DecoderNamespace = "VFPV8" in { + let PostEncoderMethod = "", DecoderNamespace = "VFPV8", + isUnpredicable = 1 in { def H : AHuInp<0b11101, 0b11, 0b1000, 0b01, 0, - (outs SPR:$Sd), (ins SPR:$Sm), + (outs HPR:$Sd), (ins HPR:$Sm), NoItinerary, !strconcat("vrint", opc, ".f16\t$Sd, $Sm"), - []>, + [(set (f16 HPR:$Sd), (node (f16 HPR:$Sm)))]>, Requires<[HasFullFP16]> { let Inst{17-16} = rm; } @@ -998,22 +1031,24 @@ def VSQRTS : ASuI<0b11101, 0b11, 0b0001, 0b11, 0, Sched<[WriteFPSQRT32]>; def VSQRTH : AHuI<0b11101, 0b11, 0b0001, 0b11, 0, - (outs SPR:$Sd), (ins SPR:$Sm), + (outs HPR:$Sd), (ins HPR:$Sm), IIC_fpSQRT16, "vsqrt", ".f16\t$Sd, $Sm", - []>; + [(set HPR:$Sd, (fsqrt (f16 HPR:$Sm)))]>; let hasSideEffects = 0 in { let isMoveReg = 1 in { def VMOVD : ADuI<0b11101, 0b11, 0b0000, 0b01, 0, (outs DPR:$Dd), (ins DPR:$Dm), - IIC_fpUNA64, "vmov", ".f64\t$Dd, $Dm", []>; + IIC_fpUNA64, "vmov", ".f64\t$Dd, $Dm", []>, + Requires<[HasFPRegs64]>; def VMOVS : ASuI<0b11101, 0b11, 0b0000, 0b01, 0, (outs SPR:$Sd), (ins SPR:$Sm), - IIC_fpUNA32, "vmov", ".f32\t$Sd, $Sm", []>; + IIC_fpUNA32, "vmov", ".f32\t$Sd, $Sm", []>, + Requires<[HasFPRegs]>; } // isMoveReg -let PostEncoderMethod = "", DecoderNamespace = "VFPV8" in { +let PostEncoderMethod = "", DecoderNamespace = "VFPV8", isUnpredicable = 1 in { def VMOVH : ASuInp<0b11101, 0b11, 0b0000, 0b01, 0, (outs SPR:$Sd), (ins SPR:$Sm), IIC_fpUNA16, "vmovx.f16\t$Sd, $Sm", []>, @@ -1035,6 +1070,7 @@ def VMOVRS : AVConv2I<0b11100001, 0b1010, (outs GPR:$Rt), (ins SPR:$Sn), IIC_fpMOVSI, "vmov", "\t$Rt, $Sn", [(set GPR:$Rt, (bitconvert SPR:$Sn))]>, + Requires<[HasFPRegs]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<4> Rt; @@ -1058,7 +1094,7 @@ def VMOVSR : AVConv4I<0b11100000, 0b1010, (outs SPR:$Sn), (ins GPR:$Rt), IIC_fpMOVIS, "vmov", "\t$Sn, $Rt", [(set SPR:$Sn, (bitconvert GPR:$Rt))]>, - Requires<[HasVFP2, UseVMOVSR]>, + Requires<[HasFPRegs, UseVMOVSR]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<5> Sn; @@ -1084,6 +1120,7 @@ def VMOVRRD : AVConv3I<0b11000101, 0b1011, (outs GPR:$Rt, GPR:$Rt2), (ins DPR:$Dm), IIC_fpMOVDI, "vmov", "\t$Rt, $Rt2, $Dm", [(set GPR:$Rt, GPR:$Rt2, (arm_fmrrd DPR:$Dm))]>, + Requires<[HasFPRegs]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<5> Dm; @@ -1112,6 +1149,7 @@ def VMOVRRS : AVConv3I<0b11000101, 0b1010, (outs GPR:$Rt, GPR:$Rt2), (ins SPR:$src1, SPR:$src2), IIC_fpMOVDI, "vmov", "\t$Rt, $Rt2, $src1, $src2", [/* For disassembly only; pattern left blank */]>, + Requires<[HasFPRegs]>, Sched<[WriteFPMOV]> { bits<5> src1; bits<4> Rt; @@ -1139,6 +1177,7 @@ def VMOVDRR : AVConv5I<0b11000100, 0b1011, (outs DPR:$Dm), (ins GPR:$Rt, GPR:$Rt2), IIC_fpMOVID, "vmov", "\t$Dm, $Rt, $Rt2", [(set DPR:$Dm, (arm_fmdrr GPR:$Rt, GPR:$Rt2))]>, + Requires<[HasFPRegs]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<5> Dm; @@ -1183,6 +1222,7 @@ def VMOVSRR : AVConv5I<0b11000100, 0b1010, (outs SPR:$dst1, SPR:$dst2), (ins GPR:$src1, GPR:$src2), IIC_fpMOVID, "vmov", "\t$dst1, $dst2, $src1, $src2", [/* For disassembly only; pattern left blank */]>, + Requires<[HasFPRegs]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<5> dst1; @@ -1206,10 +1246,10 @@ def VMOVSRR : AVConv5I<0b11000100, 0b1010, // Move H->R, clearing top 16 bits def VMOVRH : AVConv2I<0b11100001, 0b1001, - (outs GPR:$Rt), (ins HPR:$Sn), + (outs rGPR:$Rt), (ins HPR:$Sn), IIC_fpMOVSI, "vmov", ".f16\t$Rt, $Sn", - [(set GPR:$Rt, (arm_vmovrh HPR:$Sn))]>, - Requires<[HasFullFP16]>, + [(set rGPR:$Rt, (arm_vmovrh HPR:$Sn))]>, + Requires<[HasFPRegs16]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<4> Rt; @@ -1222,14 +1262,16 @@ def VMOVRH : AVConv2I<0b11100001, 0b1001, let Inst{6-5} = 0b00; let Inst{3-0} = 0b0000; + + let isUnpredicable = 1; } // Move R->H, clearing top 16 bits def VMOVHR : AVConv4I<0b11100000, 0b1001, - (outs HPR:$Sn), (ins GPR:$Rt), + (outs HPR:$Sn), (ins rGPR:$Rt), IIC_fpMOVIS, "vmov", ".f16\t$Sn, $Rt", - [(set HPR:$Sn, (arm_vmovhr GPR:$Rt))]>, - Requires<[HasFullFP16]>, + [(set HPR:$Sn, (arm_vmovhr rGPR:$Rt))]>, + Requires<[HasFPRegs16]>, Sched<[WriteFPMOV]> { // Instruction operands. bits<5> Sn; @@ -1242,6 +1284,8 @@ def VMOVHR : AVConv4I<0b11100000, 0b1001, let Inst{6-5} = 0b00; let Inst{3-0} = 0b0000; + + let isUnpredicable = 1; } // FMRDH: SPR -> GPR @@ -1348,6 +1392,7 @@ def VSITOH : AVConv1IHs_Encode<0b11101, 0b11, 0b1000, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 1; // s32 + let isUnpredicable = 1; } def : VFPNoNEONPat<(f16 (sint_to_fp GPR:$a)), @@ -1393,6 +1438,7 @@ def VUITOH : AVConv1IHs_Encode<0b11101, 0b11, 0b1000, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 0; // u32 + let isUnpredicable = 1; } def : VFPNoNEONPat<(f16 (uint_to_fp GPR:$a)), @@ -1497,6 +1543,7 @@ def VTOSIZH : AVConv1IsH_Encode<0b11101, 0b11, 0b1101, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 1; // Z bit + let isUnpredicable = 1; } def : VFPNoNEONPat<(i32 (fp_to_sint HPR:$a)), @@ -1543,6 +1590,7 @@ def VTOUIZH : AVConv1IsH_Encode<0b11101, 0b11, 0b1100, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 1; // Z bit + let isUnpredicable = 1; } def : VFPNoNEONPat<(i32 (fp_to_uint HPR:$a)), @@ -1572,6 +1620,7 @@ def VTOSIRH : AVConv1IsH_Encode<0b11101, 0b11, 0b1101, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 0; // Z bit + let isUnpredicable = 1; } def VTOUIRD : AVConv1IsD_Encode<0b11101, 0b11, 0b1100, 0b1011, @@ -1596,6 +1645,7 @@ def VTOUIRH : AVConv1IsH_Encode<0b11101, 0b11, 0b1100, 0b1001, []>, Sched<[WriteFPCVT]> { let Inst{7} = 0; // Z bit + let isUnpredicable = 1; } } @@ -1643,6 +1693,8 @@ class AVConv1XInsD_Encode<bits<5> op1, bits<2> op2, bits<4> op3, bits<4> op4, let Predicates = [HasVFP2, HasDPVFP]; } +let isUnpredicable = 1 in { + def VTOSHH : AVConv1XInsS_Encode<0b11101, 0b11, 0b1110, 0b1001, 0, (outs SPR:$dst), (ins SPR:$a, fbits16:$fbits), IIC_fpCVTHI, "vcvt", ".s16.f16\t$dst, $a, $fbits", []>, @@ -1667,6 +1719,8 @@ def VTOULH : AVConv1XInsS_Encode<0b11101, 0b11, 0b1111, 0b1001, 1, Requires<[HasFullFP16]>, Sched<[WriteFPCVT]>; +} // End of 'let isUnpredicable = 1 in' + def VTOSHS : AVConv1XInsS_Encode<0b11101, 0b11, 0b1110, 0b1010, 0, (outs SPR:$dst), (ins SPR:$a, fbits16:$fbits), IIC_fpCVTSI, "vcvt", ".s16.f32\t$dst, $a, $fbits", []>, @@ -1722,6 +1776,8 @@ def VTOULD : AVConv1XInsD_Encode<0b11101, 0b11, 0b1111, 0b1011, 1, // Fixed-Point to FP: +let isUnpredicable = 1 in { + def VSHTOH : AVConv1XInsS_Encode<0b11101, 0b11, 0b1010, 0b1001, 0, (outs SPR:$dst), (ins SPR:$a, fbits16:$fbits), IIC_fpCVTIH, "vcvt", ".f16.s16\t$dst, $a, $fbits", []>, @@ -1746,6 +1802,8 @@ def VULTOH : AVConv1XInsS_Encode<0b11101, 0b11, 0b1011, 0b1001, 1, Requires<[HasFullFP16]>, Sched<[WriteFPCVT]>; +} // End of 'let isUnpredicable = 1 in' + def VSHTOS : AVConv1XInsS_Encode<0b11101, 0b11, 0b1010, 0b1010, 0, (outs SPR:$dst), (ins SPR:$a, fbits16:$fbits), IIC_fpCVTIS, "vcvt", ".f32.s16\t$dst, $a, $fbits", []>, @@ -2030,6 +2088,9 @@ def : Pat<(f64 (fma DPR:$Dn, DPR:$Dm, DPR:$Ddin)), def : Pat<(f32 (fma SPR:$Sn, SPR:$Sm, SPR:$Sdin)), (VFMAS SPR:$Sdin, SPR:$Sn, SPR:$Sm)>, Requires<[HasVFP4]>; +def : Pat<(f16 (fma HPR:$Sn, HPR:$Sm, HPR:$Sdin)), + (VFMAH HPR:$Sdin, HPR:$Sn, HPR:$Sm)>, + Requires<[HasFullFP16]>; def VFMSD : ADbI<0b11101, 0b10, 1, 0, (outs DPR:$Dd), (ins DPR:$Ddin, DPR:$Dn, DPR:$Dm), @@ -2208,13 +2269,13 @@ def VMOVDcc : PseudoInst<(outs DPR:$Dd), (ins DPR:$Dn, DPR:$Dm, cmovpred:$p), IIC_fpUNA64, [(set (f64 DPR:$Dd), (ARMcmov DPR:$Dn, DPR:$Dm, cmovpred:$p))]>, - RegConstraint<"$Dn = $Dd">, Requires<[HasVFP2,HasDPVFP]>; + RegConstraint<"$Dn = $Dd">, Requires<[HasFPRegs64]>; def VMOVScc : PseudoInst<(outs SPR:$Sd), (ins SPR:$Sn, SPR:$Sm, cmovpred:$p), IIC_fpUNA32, [(set (f32 SPR:$Sd), (ARMcmov SPR:$Sn, SPR:$Sm, cmovpred:$p))]>, - RegConstraint<"$Sn = $Sd">, Requires<[HasVFP2]>; + RegConstraint<"$Sn = $Sd">, Requires<[HasFPRegs]>; } // hasSideEffects //===----------------------------------------------------------------------===// @@ -2238,15 +2299,16 @@ class MovFromVFP<bits<4> opc19_16, dag oops, dag iops, string opc, string asm, let Inst{3-0} = 0b0000; } -// APSR is the application level alias of CPSR. This FPSCR N, Z, C, V flags -// to APSR. -let Defs = [CPSR], Uses = [FPSCR_NZCV], Rt = 0b1111 /* apsr_nzcv */ in -def FMSTAT : MovFromVFP<0b0001 /* fpscr */, (outs), (ins), - "vmrs", "\tAPSR_nzcv, fpscr", [(arm_fmstat)]>; - let DecoderMethod = "DecodeForVMRSandVMSR" in { + // APSR is the application level alias of CPSR. This FPSCR N, Z, C, V flags + // to APSR. + let Defs = [CPSR], Uses = [FPSCR_NZCV], Predicates = [HasFPRegs], + Rt = 0b1111 /* apsr_nzcv */ in + def FMSTAT : MovFromVFP<0b0001 /* fpscr */, (outs), (ins), + "vmrs", "\tAPSR_nzcv, fpscr", [(arm_fmstat)]>; + // Application level FPSCR -> GPR - let hasSideEffects = 1, Uses = [FPSCR] in + let hasSideEffects = 1, Uses = [FPSCR], Predicates = [HasFPRegs] in def VMRS : MovFromVFP<0b0001 /* fpscr */, (outs GPRnopc:$Rt), (ins), "vmrs", "\t$Rt, fpscr", [(set GPRnopc:$Rt, (int_arm_get_fpscr))]>; @@ -2269,6 +2331,33 @@ let DecoderMethod = "DecodeForVMRSandVMSR" in { "vmrs", "\t$Rt, fpinst", []>; def VMRS_FPINST2 : MovFromVFP<0b1010 /* fpinst2 */, (outs GPRnopc:$Rt), (ins), "vmrs", "\t$Rt, fpinst2", []>; + let Predicates = [HasV8_1MMainline, HasFPRegs] in { + // System level FPSCR_NZCVQC -> GPR + def VMRS_FPSCR_NZCVQC + : MovFromVFP<0b0010 /* fpscr_nzcvqc */, + (outs GPR:$Rt), (ins cl_FPSCR_NZCV:$fpscr_in), + "vmrs", "\t$Rt, fpscr_nzcvqc", []>; + } + } + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + // System level FPSCR -> GPR, with context saving for security extensions + def VMRS_FPCXTNS : MovFromVFP<0b1110 /* fpcxtns */, (outs GPR:$Rt), (ins), + "vmrs", "\t$Rt, fpcxtns", []>; + } + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + // System level FPSCR -> GPR, with context saving for security extensions + def VMRS_FPCXTS : MovFromVFP<0b1111 /* fpcxts */, (outs GPR:$Rt), (ins), + "vmrs", "\t$Rt, fpcxts", []>; + } + + let Predicates = [HasV8_1MMainline, HasMVEInt] in { + // System level VPR/P0 -> GPR + let Uses = [VPR] in + def VMRS_VPR : MovFromVFP<0b1100 /* vpr */, (outs GPR:$Rt), (ins), + "vmrs", "\t$Rt, vpr", []>; + + def VMRS_P0 : MovFromVFP<0b1101 /* p0 */, (outs GPR:$Rt), (ins VCCR:$cond), + "vmrs", "\t$Rt, p0", []>; } } @@ -2291,10 +2380,12 @@ class MovToVFP<bits<4> opc19_16, dag oops, dag iops, string opc, string asm, let Inst{11-8} = 0b1010; let Inst{7} = 0; let Inst{4} = 1; + let Predicates = [HasVFP2]; } let DecoderMethod = "DecodeForVMRSandVMSR" in { let Defs = [FPSCR] in { + let Predicates = [HasFPRegs] in // Application level GPR -> FPSCR def VMSR : MovToVFP<0b0001 /* fpscr */, (outs), (ins GPRnopc:$src), "vmsr", "\tfpscr, $src", @@ -2310,6 +2401,33 @@ let DecoderMethod = "DecodeForVMRSandVMSR" in { def VMSR_FPINST2 : MovToVFP<0b1010 /* fpinst2 */, (outs), (ins GPRnopc:$src), "vmsr", "\tfpinst2, $src", []>; } + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + // System level GPR -> FPSCR with context saving for security extensions + def VMSR_FPCXTNS : MovToVFP<0b1110 /* fpcxtns */, (outs), (ins GPR:$src), + "vmsr", "\tfpcxtns, $src", []>; + } + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + // System level GPR -> FPSCR with context saving for security extensions + def VMSR_FPCXTS : MovToVFP<0b1111 /* fpcxts */, (outs), (ins GPR:$src), + "vmsr", "\tfpcxts, $src", []>; + } + let Predicates = [HasV8_1MMainline, HasFPRegs] in { + // System level GPR -> FPSCR_NZCVQC + def VMSR_FPSCR_NZCVQC + : MovToVFP<0b0010 /* fpscr_nzcvqc */, + (outs cl_FPSCR_NZCV:$fpscr_out), (ins GPR:$src), + "vmsr", "\tfpscr_nzcvqc, $src", []>; + } + + let Predicates = [HasV8_1MMainline, HasMVEInt] in { + // System level GPR -> VPR/P0 + let Defs = [VPR] in + def VMSR_VPR : MovToVFP<0b1100 /* vpr */, (outs), (ins GPR:$src), + "vmsr", "\tvpr, $src", []>; + + def VMSR_P0 : MovToVFP<0b1101 /* p0 */, (outs VCCR:$cond), (ins GPR:$src), + "vmsr", "\tp0, $src", []>; + } } //===----------------------------------------------------------------------===// @@ -2371,6 +2489,8 @@ def FCONSTH : VFPAI<(outs HPR:$Sd), (ins vfp_f16imm:$imm), let Inst{11-8} = 0b1001; // Half precision let Inst{7-4} = 0b0000; let Inst{3-0} = imm{3-0}; + + let isUnpredicable = 1; } } @@ -2426,7 +2546,7 @@ def : VFP2DPInstAlias<"fcmpzd${p} $val", (VCMPZD DPR:$val, pred:$p)>; def : VFP2InstAlias<"fcmpzs${p} $val", (VCMPZS SPR:$val, pred:$p)>; -def : VFP2InstAlias<"fmstat${p}", (FMSTAT pred:$p)>; +def : InstAlias<"fmstat${p}", (FMSTAT pred:$p), 0>, Requires<[HasFPRegs]>; def : VFP2InstAlias<"fadds${p} $Sd, $Sn, $Sm", (VADDS SPR:$Sd, SPR:$Sn, SPR:$Sm, pred:$p)>; def : VFP2DPInstAlias<"faddd${p} $Dd, $Dn, $Dm", @@ -2484,3 +2604,126 @@ def : VFP3InstAlias<"fconstd${p} $Dd, $val", (FCONSTD DPR:$Dd, vfp_f64imm:$val, pred:$p)>; def : VFP3InstAlias<"fconsts${p} $Sd, $val", (FCONSTS SPR:$Sd, vfp_f32imm:$val, pred:$p)>; + +def VSCCLRMD : VFPXI<(outs), (ins pred:$p, fp_dreglist_with_vpr:$regs, variable_ops), + AddrModeNone, 4, IndexModeNone, VFPMiscFrm, NoItinerary, + "vscclrm{$p}\t$regs", "", []>, Sched<[]> { + bits<13> regs; + let Inst{31-23} = 0b111011001; + let Inst{22} = regs{12}; + let Inst{21-16} = 0b011111; + let Inst{15-12} = regs{11-8}; + let Inst{11-8} = 0b1011; + let Inst{7-0} = regs{7-0}; + + let DecoderMethod = "DecodeVSCCLRM"; + + list<Predicate> Predicates = [HasV8_1MMainline, Has8MSecExt]; +} + +def VSCCLRMS : VFPXI<(outs), (ins pred:$p, fp_sreglist_with_vpr:$regs, variable_ops), + AddrModeNone, 4, IndexModeNone, VFPMiscFrm, NoItinerary, + "vscclrm{$p}\t$regs", "", []>, Sched<[]> { + bits<13> regs; + let Inst{31-23} = 0b111011001; + let Inst{22} = regs{8}; + let Inst{21-16} = 0b011111; + let Inst{15-12} = regs{12-9}; + let Inst{11-8} = 0b1010; + let Inst{7-0} = regs{7-0}; + + let DecoderMethod = "DecodeVSCCLRM"; + + list<Predicate> Predicates = [HasV8_1MMainline, Has8MSecExt]; +} + +//===----------------------------------------------------------------------===// +// Store VFP System Register to memory. +// + +class vfp_vstrldr<bit opc, bit P, bit W, bits<4> SysReg, string sysreg, + dag oops, dag iops, IndexMode im, string Dest, string cstr> + : VFPI<oops, iops, AddrModeT2_i7s4, 4, im, VFPLdStFrm, IIC_fpSTAT, + !if(opc,"vldr","vstr"), !strconcat("\t", sysreg, ", ", Dest), cstr, []>, + Sched<[]> { + bits<12> addr; + let Inst{27-25} = 0b110; + let Inst{24} = P; + let Inst{23} = addr{7}; + let Inst{22} = SysReg{3}; + let Inst{21} = W; + let Inst{20} = opc; + let Inst{19-16} = addr{11-8}; + let Inst{15-13} = SysReg{2-0}; + let Inst{12-7} = 0b011111; + let Inst{6-0} = addr{6-0}; + list<Predicate> Predicates = [HasFPRegs, HasV8_1MMainline]; + let mayLoad = opc; + let mayStore = !if(opc, 0b0, 0b1); + let hasSideEffects = 1; +} + +multiclass vfp_vstrldr_sysreg<bit opc, bits<4> SysReg, string sysreg, + dag oops=(outs), dag iops=(ins)> { + def _off : + vfp_vstrldr<opc, 1, 0, SysReg, sysreg, + oops, !con(iops, (ins t2addrmode_imm7s4:$addr)), + IndexModePost, "$addr", "" > { + let DecoderMethod = "DecodeVSTRVLDR_SYSREG<false>"; + } + + def _pre : + vfp_vstrldr<opc, 1, 1, SysReg, sysreg, + !con(oops, (outs GPRnopc:$wb)), + !con(iops, (ins t2addrmode_imm7s4_pre:$addr)), + IndexModePre, "$addr!", "$addr.base = $wb"> { + let DecoderMethod = "DecodeVSTRVLDR_SYSREG<true>"; + } + + def _post : + vfp_vstrldr<opc, 0, 1, SysReg, sysreg, + !con(oops, (outs GPRnopc:$wb)), + !con(iops, (ins t2_addr_offset_none:$Rn, + t2am_imm7s4_offset:$addr)), + IndexModePost, "$Rn$addr", "$Rn.base = $wb"> { + bits<4> Rn; + let Inst{19-16} = Rn{3-0}; + let DecoderMethod = "DecodeVSTRVLDR_SYSREG<true>"; + } +} + +let Defs = [FPSCR] in { + defm VSTR_FPSCR : vfp_vstrldr_sysreg<0b0,0b0001, "fpscr">; + defm VSTR_FPSCR_NZCVQC : vfp_vstrldr_sysreg<0b0,0b0010, "fpscr_nzcvqc">; + + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + defm VSTR_FPCXTNS : vfp_vstrldr_sysreg<0b0,0b1110, "fpcxtns">; + defm VSTR_FPCXTS : vfp_vstrldr_sysreg<0b0,0b1111, "fpcxts">; + } +} + +let Predicates = [HasV8_1MMainline, HasMVEInt] in { + let Uses = [VPR] in { + defm VSTR_VPR : vfp_vstrldr_sysreg<0b0,0b1100, "vpr">; + } + defm VSTR_P0 : vfp_vstrldr_sysreg<0b0,0b1101, "p0", + (outs), (ins VCCR:$P0)>; +} + +let Uses = [FPSCR] in { + defm VLDR_FPSCR : vfp_vstrldr_sysreg<0b1,0b0001, "fpscr">; + defm VLDR_FPSCR_NZCVQC : vfp_vstrldr_sysreg<0b1,0b0010, "fpscr_nzcvqc">; + + let Predicates = [HasV8_1MMainline, Has8MSecExt] in { + defm VLDR_FPCXTNS : vfp_vstrldr_sysreg<0b1,0b1110, "fpcxtns">; + defm VLDR_FPCXTS : vfp_vstrldr_sysreg<0b1,0b1111, "fpcxts">; + } +} + +let Predicates = [HasV8_1MMainline, HasMVEInt] in { + let Defs = [VPR] in { + defm VLDR_VPR : vfp_vstrldr_sysreg<0b1,0b1100, "vpr">; + } + defm VLDR_P0 : vfp_vstrldr_sysreg<0b1,0b1101, "p0", + (outs VCCR:$P0), (ins)>; +} diff --git a/lib/Target/ARM/ARMInstructionSelector.cpp b/lib/Target/ARM/ARMInstructionSelector.cpp index 293e734c97cd..4485a474a6df 100644 --- a/lib/Target/ARM/ARMInstructionSelector.cpp +++ b/lib/Target/ARM/ARMInstructionSelector.cpp @@ -1,9 +1,8 @@ //===- ARMInstructionSelector.cpp ----------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file @@ -76,6 +75,11 @@ private: const ARMRegisterBankInfo &RBI; const ARMSubtarget &STI; + // FIXME: This is necessary because DAGISel uses "Subtarget->" and GlobalISel + // uses "STI." in the code generated by TableGen. If we want to reuse some of + // the custom C++ predicates written for DAGISel, we need to have both around. + const ARMSubtarget *Subtarget = &STI; + // Store the opcodes that we might need, so we don't have to check what kind // of subtarget (ARM vs Thumb) we have all the time. struct OpcodeCache { @@ -98,6 +102,27 @@ private: unsigned STORE8; unsigned LOAD8; + unsigned ADDrr; + unsigned ADDri; + + // Used for G_ICMP + unsigned CMPrr; + unsigned MOVi; + unsigned MOVCCi; + + // Used for G_SELECT + unsigned MOVCCr; + + unsigned TSTri; + unsigned Bcc; + + // Used for G_GLOBAL_VALUE + unsigned MOVi32imm; + unsigned ConstPoolLoad; + unsigned MOV_ga_pcrel; + unsigned LDRLIT_ga_pcrel; + unsigned LDRLIT_ga_abs; + OpcodeCache(const ARMSubtarget &STI); } const Opcodes; @@ -112,6 +137,9 @@ private: unsigned selectLoadStoreOpCode(unsigned Opc, unsigned RegBank, unsigned Size) const; + void renderVFPF32Imm(MachineInstrBuilder &New, const MachineInstr &Old) const; + void renderVFPF64Imm(MachineInstrBuilder &New, const MachineInstr &Old) const; + #define GET_GLOBALISEL_PREDICATES_DECL #include "ARMGenGlobalISel.inc" #undef GET_GLOBALISEL_PREDICATES_DECL @@ -204,7 +232,7 @@ static bool selectMergeValues(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) { - assert(TII.getSubtarget().hasVFP2() && "Can't select merge without VFP"); + assert(TII.getSubtarget().hasVFP2Base() && "Can't select merge without VFP"); // We only support G_MERGE_VALUES as a way to stick together two scalar GPRs // into one DPR. @@ -235,7 +263,8 @@ static bool selectUnmergeValues(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) { - assert(TII.getSubtarget().hasVFP2() && "Can't select unmerge without VFP"); + assert(TII.getSubtarget().hasVFP2Base() && + "Can't select unmerge without VFP"); // We only support G_UNMERGE_VALUES as a way to break up one DPR into two // GPRs. @@ -285,6 +314,24 @@ ARMInstructionSelector::OpcodeCache::OpcodeCache(const ARMSubtarget &STI) { STORE_OPCODE(STORE8, STRBi12); STORE_OPCODE(LOAD8, LDRBi12); + + STORE_OPCODE(ADDrr, ADDrr); + STORE_OPCODE(ADDri, ADDri); + + STORE_OPCODE(CMPrr, CMPrr); + STORE_OPCODE(MOVi, MOVi); + STORE_OPCODE(MOVCCi, MOVCCi); + + STORE_OPCODE(MOVCCr, MOVCCr); + + STORE_OPCODE(TSTri, TSTri); + STORE_OPCODE(Bcc, Bcc); + + STORE_OPCODE(MOVi32imm, MOVi32imm); + ConstPoolLoad = isThumb ? ARM::t2LDRpci : ARM::LDRi12; + STORE_OPCODE(MOV_ga_pcrel, MOV_ga_pcrel); + LDRLIT_ga_pcrel = isThumb ? ARM::tLDRLIT_ga_pcrel : ARM::LDRLIT_ga_pcrel; + LDRLIT_ga_abs = isThumb ? ARM::tLDRLIT_ga_abs : ARM::LDRLIT_ga_abs; #undef MAP_OPCODE } @@ -408,10 +455,11 @@ getComparePreds(CmpInst::Predicate Pred) { } struct ARMInstructionSelector::CmpConstants { - CmpConstants(unsigned CmpOpcode, unsigned FlagsOpcode, unsigned OpRegBank, - unsigned OpSize) + CmpConstants(unsigned CmpOpcode, unsigned FlagsOpcode, unsigned SelectOpcode, + unsigned OpRegBank, unsigned OpSize) : ComparisonOpcode(CmpOpcode), ReadFlagsOpcode(FlagsOpcode), - OperandRegBankID(OpRegBank), OperandSize(OpSize) {} + SelectResultOpcode(SelectOpcode), OperandRegBankID(OpRegBank), + OperandSize(OpSize) {} // The opcode used for performing the comparison. const unsigned ComparisonOpcode; @@ -420,6 +468,9 @@ struct ARMInstructionSelector::CmpConstants { // ARM::INSTRUCTION_LIST_END if we don't need to read the flags. const unsigned ReadFlagsOpcode; + // The opcode used for materializing the result of the comparison. + const unsigned SelectResultOpcode; + // The assumed register bank ID for the operands. const unsigned OperandRegBankID; @@ -439,7 +490,7 @@ struct ARMInstructionSelector::InsertInfo { void ARMInstructionSelector::putConstant(InsertInfo I, unsigned DestReg, unsigned Constant) const { - (void)BuildMI(I.MBB, I.InsertBefore, I.DbgLoc, TII.get(ARM::MOVi)) + (void)BuildMI(I.MBB, I.InsertBefore, I.DbgLoc, TII.get(Opcodes.MOVi)) .addDef(DestReg) .addImm(Constant) .add(predOps(ARMCC::AL)) @@ -542,7 +593,8 @@ bool ARMInstructionSelector::insertComparison(CmpConstants Helper, InsertInfo I, } // Select either 1 or the previous result based on the value of the flags. - auto Mov1I = BuildMI(I.MBB, I.InsertBefore, I.DbgLoc, TII.get(ARM::MOVCCi)) + auto Mov1I = BuildMI(I.MBB, I.InsertBefore, I.DbgLoc, + TII.get(Helper.SelectResultOpcode)) .addDef(ResReg) .addUse(PrevRes) .addImm(1) @@ -569,7 +621,7 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, auto &MBB = *MIB->getParent(); auto &MF = *MBB.getParent(); - bool UseMovt = STI.useMovt(MF); + bool UseMovt = STI.useMovt(); unsigned Size = TM.getPointerSize(0); unsigned Alignment = 4; @@ -577,7 +629,9 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, auto addOpsForConstantPoolLoad = [&MF, Alignment, Size](MachineInstrBuilder &MIB, const GlobalValue *GV, bool IsSBREL) { - assert(MIB->getOpcode() == ARM::LDRi12 && "Unsupported instruction"); + assert((MIB->getOpcode() == ARM::LDRi12 || + MIB->getOpcode() == ARM::t2LDRpci) && + "Unsupported instruction"); auto ConstPool = MF.getConstantPool(); auto CPIndex = // For SB relative entries we need a target-specific constant pool. @@ -587,21 +641,38 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, ARMConstantPoolConstant::Create(GV, ARMCP::SBREL), Alignment) : ConstPool->getConstantPoolIndex(GV, Alignment); MIB.addConstantPoolIndex(CPIndex, /*Offset*/ 0, /*TargetFlags*/ 0) - .addMemOperand( - MF.getMachineMemOperand(MachinePointerInfo::getConstantPool(MF), - MachineMemOperand::MOLoad, Size, Alignment)) - .addImm(0) - .add(predOps(ARMCC::AL)); + .addMemOperand(MF.getMachineMemOperand( + MachinePointerInfo::getConstantPool(MF), MachineMemOperand::MOLoad, + Size, Alignment)); + if (MIB->getOpcode() == ARM::LDRi12) + MIB.addImm(0); + MIB.add(predOps(ARMCC::AL)); + }; + + auto addGOTMemOperand = [this, &MF, Alignment](MachineInstrBuilder &MIB) { + MIB.addMemOperand(MF.getMachineMemOperand( + MachinePointerInfo::getGOT(MF), MachineMemOperand::MOLoad, + TM.getProgramPointerSize(), Alignment)); }; if (TM.isPositionIndependent()) { bool Indirect = STI.isGVIndirectSymbol(GV); + + // For ARM mode, we have different pseudoinstructions for direct accesses + // and indirect accesses, and the ones for indirect accesses include the + // load from GOT. For Thumb mode, we use the same pseudoinstruction for both + // direct and indirect accesses, and we need to manually generate the load + // from GOT. + bool UseOpcodeThatLoads = Indirect && !STI.isThumb(); + // FIXME: Taking advantage of MOVT for ELF is pretty involved, so we don't // support it yet. See PR28229. unsigned Opc = UseMovt && !STI.isTargetELF() - ? (Indirect ? ARM::MOV_ga_pcrel_ldr : ARM::MOV_ga_pcrel) - : (Indirect ? ARM::LDRLIT_ga_pcrel_ldr : ARM::LDRLIT_ga_pcrel); + ? (UseOpcodeThatLoads ? (unsigned)ARM::MOV_ga_pcrel_ldr + : Opcodes.MOV_ga_pcrel) + : (UseOpcodeThatLoads ? (unsigned)ARM::LDRLIT_ga_pcrel_ldr + : Opcodes.LDRLIT_ga_pcrel); MIB->setDesc(TII.get(Opc)); int TargetFlags = ARMII::MO_NO_FLAG; @@ -611,17 +682,35 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, TargetFlags |= ARMII::MO_GOT; MIB->getOperand(1).setTargetFlags(TargetFlags); - if (Indirect) - MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getGOT(MF), MachineMemOperand::MOLoad, - TM.getProgramPointerSize(), Alignment)); + if (Indirect) { + if (!UseOpcodeThatLoads) { + auto ResultReg = MIB->getOperand(0).getReg(); + auto AddressReg = MRI.createVirtualRegister(&ARM::GPRRegClass); + + MIB->getOperand(0).setReg(AddressReg); + + auto InsertBefore = std::next(MIB->getIterator()); + auto MIBLoad = BuildMI(MBB, InsertBefore, MIB->getDebugLoc(), + TII.get(Opcodes.LOAD32)) + .addDef(ResultReg) + .addReg(AddressReg) + .addImm(0) + .add(predOps(ARMCC::AL)); + addGOTMemOperand(MIBLoad); + + if (!constrainSelectedInstRegOperands(*MIBLoad, TII, TRI, RBI)) + return false; + } else { + addGOTMemOperand(MIB); + } + } return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI); } bool isReadOnly = STI.getTargetLowering()->isReadOnly(GV); if (STI.isROPI() && isReadOnly) { - unsigned Opc = UseMovt ? ARM::MOV_ga_pcrel : ARM::LDRLIT_ga_pcrel; + unsigned Opc = UseMovt ? Opcodes.MOV_ga_pcrel : Opcodes.LDRLIT_ga_pcrel; MIB->setDesc(TII.get(Opc)); return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI); } @@ -630,19 +719,19 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, MachineInstrBuilder OffsetMIB; if (UseMovt) { OffsetMIB = BuildMI(MBB, *MIB, MIB->getDebugLoc(), - TII.get(ARM::MOVi32imm), Offset); + TII.get(Opcodes.MOVi32imm), Offset); OffsetMIB.addGlobalAddress(GV, /*Offset*/ 0, ARMII::MO_SBREL); } else { // Load the offset from the constant pool. - OffsetMIB = - BuildMI(MBB, *MIB, MIB->getDebugLoc(), TII.get(ARM::LDRi12), Offset); + OffsetMIB = BuildMI(MBB, *MIB, MIB->getDebugLoc(), + TII.get(Opcodes.ConstPoolLoad), Offset); addOpsForConstantPoolLoad(OffsetMIB, GV, /*IsSBREL*/ true); } if (!constrainSelectedInstRegOperands(*OffsetMIB, TII, TRI, RBI)) return false; // Add the offset to the SB register. - MIB->setDesc(TII.get(ARM::ADDrr)); + MIB->setDesc(TII.get(Opcodes.ADDrr)); MIB->RemoveOperand(1); MIB.addReg(ARM::R9) // FIXME: don't hardcode R9 .addReg(Offset) @@ -654,18 +743,18 @@ bool ARMInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, if (STI.isTargetELF()) { if (UseMovt) { - MIB->setDesc(TII.get(ARM::MOVi32imm)); + MIB->setDesc(TII.get(Opcodes.MOVi32imm)); } else { // Load the global's address from the constant pool. - MIB->setDesc(TII.get(ARM::LDRi12)); + MIB->setDesc(TII.get(Opcodes.ConstPoolLoad)); MIB->RemoveOperand(1); addOpsForConstantPoolLoad(MIB, GV, /*IsSBREL*/ false); } } else if (STI.isTargetMachO()) { if (UseMovt) - MIB->setDesc(TII.get(ARM::MOVi32imm)); + MIB->setDesc(TII.get(Opcodes.MOVi32imm)); else - MIB->setDesc(TII.get(ARM::LDRLIT_ga_abs)); + MIB->setDesc(TII.get(Opcodes.LDRLIT_ga_abs)); } else { LLVM_DEBUG(dbgs() << "Object format not supported yet\n"); return false; @@ -680,13 +769,13 @@ bool ARMInstructionSelector::selectSelect(MachineInstrBuilder &MIB, auto InsertBefore = std::next(MIB->getIterator()); auto &DbgLoc = MIB->getDebugLoc(); - // Compare the condition to 0. + // Compare the condition to 1. auto CondReg = MIB->getOperand(1).getReg(); assert(validReg(MRI, CondReg, 1, ARM::GPRRegBankID) && "Unsupported types for select operation"); - auto CmpI = BuildMI(MBB, InsertBefore, DbgLoc, TII.get(ARM::CMPri)) + auto CmpI = BuildMI(MBB, InsertBefore, DbgLoc, TII.get(Opcodes.TSTri)) .addUse(CondReg) - .addImm(0) + .addImm(1) .add(predOps(ARMCC::AL)); if (!constrainSelectedInstRegOperands(*CmpI, TII, TRI, RBI)) return false; @@ -699,7 +788,7 @@ bool ARMInstructionSelector::selectSelect(MachineInstrBuilder &MIB, assert(validOpRegPair(MRI, ResReg, TrueReg, 32, ARM::GPRRegBankID) && validOpRegPair(MRI, TrueReg, FalseReg, 32, ARM::GPRRegBankID) && "Unsupported types for select operation"); - auto Mov1I = BuildMI(MBB, InsertBefore, DbgLoc, TII.get(ARM::MOVCCr)) + auto Mov1I = BuildMI(MBB, InsertBefore, DbgLoc, TII.get(Opcodes.MOVCCr)) .addDef(ResReg) .addUse(TrueReg) .addUse(FalseReg) @@ -713,12 +802,37 @@ bool ARMInstructionSelector::selectSelect(MachineInstrBuilder &MIB, bool ARMInstructionSelector::selectShift(unsigned ShiftOpc, MachineInstrBuilder &MIB) const { + assert(!STI.isThumb() && "Unsupported subtarget"); MIB->setDesc(TII.get(ARM::MOVsr)); MIB.addImm(ShiftOpc); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI); } +void ARMInstructionSelector::renderVFPF32Imm( + MachineInstrBuilder &NewInstBuilder, const MachineInstr &OldInst) const { + assert(OldInst.getOpcode() == TargetOpcode::G_FCONSTANT && + "Expected G_FCONSTANT"); + + APFloat FPImmValue = OldInst.getOperand(1).getFPImm()->getValueAPF(); + int FPImmEncoding = ARM_AM::getFP32Imm(FPImmValue); + assert(FPImmEncoding != -1 && "Invalid immediate value"); + + NewInstBuilder.addImm(FPImmEncoding); +} + +void ARMInstructionSelector::renderVFPF64Imm( + MachineInstrBuilder &NewInstBuilder, const MachineInstr &OldInst) const { + assert(OldInst.getOpcode() == TargetOpcode::G_FCONSTANT && + "Expected G_FCONSTANT"); + + APFloat FPImmValue = OldInst.getOperand(1).getFPImm()->getValueAPF(); + int FPImmEncoding = ARM_AM::getFP64Imm(FPImmValue); + assert(FPImmEncoding != -1 && "Invalid immediate value"); + + NewInstBuilder.addImm(FPImmEncoding); +} + bool ARMInstructionSelector::select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const { assert(I.getParent() && "Instruction should be in a basic block!"); @@ -748,12 +862,8 @@ bool ARMInstructionSelector::select(MachineInstr &I, isSExt = true; LLVM_FALLTHROUGH; case G_ZEXT: { - LLT DstTy = MRI.getType(I.getOperand(0).getReg()); - // FIXME: Smaller destination sizes coming soon! - if (DstTy.getSizeInBits() != 32) { - LLVM_DEBUG(dbgs() << "Unsupported destination size for extension"); - return false; - } + assert(MRI.getType(I.getOperand(0).getReg()).getSizeInBits() <= 32 && + "Unsupported destination size for extension"); LLT SrcTy = MRI.getType(I.getOperand(1).getReg()); unsigned SrcSize = SrcTy.getSizeInBits(); @@ -869,10 +979,32 @@ bool ARMInstructionSelector::select(MachineInstr &I, } } + assert(!STI.isThumb() && "Unsupported subtarget"); I.setDesc(TII.get(ARM::MOVi)); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); break; } + case G_FCONSTANT: { + // Load from constant pool + unsigned Size = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() / 8; + unsigned Alignment = Size; + + assert((Size == 4 || Size == 8) && "Unsupported FP constant type"); + auto LoadOpcode = Size == 4 ? ARM::VLDRS : ARM::VLDRD; + + auto ConstPool = MF.getConstantPool(); + auto CPIndex = + ConstPool->getConstantPoolIndex(I.getOperand(1).getFPImm(), Alignment); + MIB->setDesc(TII.get(LoadOpcode)); + MIB->RemoveOperand(1); + MIB.addConstantPoolIndex(CPIndex, /*Offset*/ 0, /*TargetFlags*/ 0) + .addMemOperand( + MF.getMachineMemOperand(MachinePointerInfo::getConstantPool(MF), + MachineMemOperand::MOLoad, Size, Alignment)) + .addImm(0) + .add(predOps(ARMCC::AL)); + break; + } case G_INTTOPTR: case G_PTRTOINT: { auto SrcReg = I.getOperand(1).getReg(); @@ -900,17 +1032,17 @@ bool ARMInstructionSelector::select(MachineInstr &I, case G_SELECT: return selectSelect(MIB, MRI); case G_ICMP: { - CmpConstants Helper(ARM::CMPrr, ARM::INSTRUCTION_LIST_END, - ARM::GPRRegBankID, 32); + CmpConstants Helper(Opcodes.CMPrr, ARM::INSTRUCTION_LIST_END, + Opcodes.MOVCCi, ARM::GPRRegBankID, 32); return selectCmp(Helper, MIB, MRI); } case G_FCMP: { - assert(STI.hasVFP2() && "Can't select fcmp without VFP"); + assert(STI.hasVFP2Base() && "Can't select fcmp without VFP"); unsigned OpReg = I.getOperand(2).getReg(); unsigned Size = MRI.getType(OpReg).getSizeInBits(); - if (Size == 64 && STI.isFPOnlySP()) { + if (Size == 64 && !STI.hasFP64()) { LLVM_DEBUG(dbgs() << "Subtarget only supports single precision"); return false; } @@ -920,7 +1052,7 @@ bool ARMInstructionSelector::select(MachineInstr &I, } CmpConstants Helper(Size == 32 ? ARM::VCMPS : ARM::VCMPD, ARM::FMSTAT, - ARM::FPRRegBankID, Size); + Opcodes.MOVCCi, ARM::FPRRegBankID, Size); return selectCmp(Helper, MIB, MRI); } case G_LSHR: @@ -931,13 +1063,13 @@ bool ARMInstructionSelector::select(MachineInstr &I, return selectShift(ARM_AM::ShiftOpc::lsl, MIB); } case G_GEP: - I.setDesc(TII.get(ARM::ADDrr)); + I.setDesc(TII.get(Opcodes.ADDrr)); MIB.add(predOps(ARMCC::AL)).add(condCodeOp()); break; case G_FRAME_INDEX: // Add 0 to the given frame index and hope it will eventually be folded into // the user(s). - I.setDesc(TII.get(ARM::ADDri)); + I.setDesc(TII.get(Opcodes.ADDri)); MIB.addImm(0).add(predOps(ARMCC::AL)).add(condCodeOp()); break; case G_GLOBAL_VALUE: @@ -956,13 +1088,31 @@ bool ARMInstructionSelector::select(MachineInstr &I, LLT ValTy = MRI.getType(Reg); const auto ValSize = ValTy.getSizeInBits(); - assert((ValSize != 64 || STI.hasVFP2()) && + assert((ValSize != 64 || STI.hasVFP2Base()) && "Don't know how to load/store 64-bit value without VFP"); const auto NewOpc = selectLoadStoreOpCode(I.getOpcode(), RegBank, ValSize); if (NewOpc == G_LOAD || NewOpc == G_STORE) return false; + if (ValSize == 1 && NewOpc == Opcodes.STORE8) { + // Before storing a 1-bit value, make sure to clear out any unneeded bits. + unsigned OriginalValue = I.getOperand(0).getReg(); + + unsigned ValueToStore = MRI.createVirtualRegister(&ARM::GPRRegClass); + I.getOperand(0).setReg(ValueToStore); + + auto InsertBefore = I.getIterator(); + auto AndI = BuildMI(MBB, InsertBefore, I.getDebugLoc(), TII.get(Opcodes.AND)) + .addDef(ValueToStore) + .addUse(OriginalValue) + .addImm(1) + .add(predOps(ARMCC::AL)) + .add(condCodeOp()); + if (!constrainSelectedInstRegOperands(*AndI, TII, TRI, RBI)) + return false; + } + I.setDesc(TII.get(NewOpc)); if (NewOpc == ARM::LDRH || NewOpc == ARM::STRH) @@ -988,17 +1138,19 @@ bool ARMInstructionSelector::select(MachineInstr &I, } // Set the flags. - auto Test = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(ARM::TSTri)) - .addReg(I.getOperand(0).getReg()) - .addImm(1) - .add(predOps(ARMCC::AL)); + auto Test = + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcodes.TSTri)) + .addReg(I.getOperand(0).getReg()) + .addImm(1) + .add(predOps(ARMCC::AL)); if (!constrainSelectedInstRegOperands(*Test, TII, TRI, RBI)) return false; // Branch conditionally. - auto Branch = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(ARM::Bcc)) - .add(I.getOperand(1)) - .add(predOps(ARMCC::NE, ARM::CPSR)); + auto Branch = + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcodes.Bcc)) + .add(I.getOperand(1)) + .add(predOps(ARMCC::NE, ARM::CPSR)); if (!constrainSelectedInstRegOperands(*Branch, TII, TRI, RBI)) return false; I.eraseFromParent(); diff --git a/lib/Target/ARM/ARMLegalizerInfo.cpp b/lib/Target/ARM/ARMLegalizerInfo.cpp index 4a0c24d58474..73a57b297ad6 100644 --- a/lib/Target/ARM/ARMLegalizerInfo.cpp +++ b/lib/Target/ARM/ARMLegalizerInfo.cpp @@ -1,9 +1,8 @@ //===- ARMLegalizerInfo.cpp --------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file @@ -83,41 +82,29 @@ ARMLegalizerInfo::ARMLegalizerInfo(const ARMSubtarget &ST) { } getActionDefinitionsBuilder({G_SEXT, G_ZEXT, G_ANYEXT}) - .legalForCartesianProduct({s32}, {s1, s8, s16}); + .legalForCartesianProduct({s8, s16, s32}, {s1, s8, s16}); - getActionDefinitionsBuilder({G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) + getActionDefinitionsBuilder({G_MUL, G_AND, G_OR, G_XOR}) .legalFor({s32}) .minScalar(0, s32); - getActionDefinitionsBuilder(G_INTTOPTR).legalFor({{p0, s32}}); - getActionDefinitionsBuilder(G_PTRTOINT).legalFor({{s32, p0}}); - - getActionDefinitionsBuilder(G_CONSTANT) - .legalFor({s32, p0}) - .clampScalar(0, s32, s32); - - // We're keeping these builders around because we'll want to add support for - // floating point to them. - auto &LoadStoreBuilder = - getActionDefinitionsBuilder({G_LOAD, G_STORE}) - .legalForTypesWithMemSize({ - {s1, p0, 8}, - {s8, p0, 8}, - {s16, p0, 16}, - {s32, p0, 32}, - {p0, p0, 32}}); - - if (ST.isThumb()) { - // FIXME: merge with the code for non-Thumb. - computeTables(); - verify(*ST.getInstrInfo()); - return; - } + if (ST.hasNEON()) + getActionDefinitionsBuilder({G_ADD, G_SUB}) + .legalFor({s32, s64}) + .minScalar(0, s32); + else + getActionDefinitionsBuilder({G_ADD, G_SUB}) + .legalFor({s32}) + .minScalar(0, s32); - getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0}); - getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0}); + getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL}) + .legalFor({{s32, s32}}) + .minScalar(0, s32) + .clampScalar(1, s32, s32); - if (ST.hasDivideInARMMode()) + bool HasHWDivide = (!ST.isThumb() && ST.hasDivideInARMMode()) || + (ST.isThumb() && ST.hasDivideInThumbMode()); + if (HasHWDivide) getActionDefinitionsBuilder({G_SDIV, G_UDIV}) .legalFor({s32}) .clampScalar(0, s32, s32); @@ -128,7 +115,7 @@ ARMLegalizerInfo::ARMLegalizerInfo(const ARMSubtarget &ST) { for (unsigned Op : {G_SREM, G_UREM}) { setLegalizeScalarToDifferentSizeStrategy(Op, 0, widen_8_16); - if (ST.hasDivideInARMMode()) + if (HasHWDivide) setAction({Op, s32}, Lower); else if (AEABI(ST)) setAction({Op, s32}, Custom); @@ -136,46 +123,57 @@ ARMLegalizerInfo::ARMLegalizerInfo(const ARMSubtarget &ST) { setAction({Op, s32}, Libcall); } - getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL}).legalFor({s32}); - - if (ST.hasV5TOps()) { - getActionDefinitionsBuilder(G_CTLZ) - .legalFor({s32}) - .clampScalar(0, s32, s32); - getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF) - .lowerFor({s32}) - .clampScalar(0, s32, s32); - } else { - getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF) - .libcallFor({s32}) - .clampScalar(0, s32, s32); - getActionDefinitionsBuilder(G_CTLZ) - .lowerFor({s32}) - .clampScalar(0, s32, s32); - } - - getActionDefinitionsBuilder(G_GEP).legalFor({{p0, s32}}); - - getActionDefinitionsBuilder(G_SELECT).legalForCartesianProduct({s32, p0}, - {s1}); + getActionDefinitionsBuilder(G_INTTOPTR) + .legalFor({{p0, s32}}) + .minScalar(1, s32); + getActionDefinitionsBuilder(G_PTRTOINT) + .legalFor({{s32, p0}}) + .minScalar(0, s32); - getActionDefinitionsBuilder(G_BRCOND).legalFor({s1}); + getActionDefinitionsBuilder(G_CONSTANT) + .legalFor({s32, p0}) + .clampScalar(0, s32, s32); getActionDefinitionsBuilder(G_ICMP) .legalForCartesianProduct({s1}, {s32, p0}) .minScalar(1, s32); + getActionDefinitionsBuilder(G_SELECT) + .legalForCartesianProduct({s32, p0}, {s1}) + .minScalar(0, s32); + // We're keeping these builders around because we'll want to add support for // floating point to them. + auto &LoadStoreBuilder = getActionDefinitionsBuilder({G_LOAD, G_STORE}) + .legalForTypesWithMemDesc({{s1, p0, 8, 8}, + {s8, p0, 8, 8}, + {s16, p0, 16, 8}, + {s32, p0, 32, 8}, + {p0, p0, 32, 8}}) + .unsupportedIfMemSizeNotPow2(); + + getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0}); + getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0}); + auto &PhiBuilder = - getActionDefinitionsBuilder(G_PHI).legalFor({s32, p0}).minScalar(0, s32); + getActionDefinitionsBuilder(G_PHI) + .legalFor({s32, p0}) + .minScalar(0, s32); + + getActionDefinitionsBuilder(G_GEP) + .legalFor({{p0, s32}}) + .minScalar(1, s32); - if (!ST.useSoftFloat() && ST.hasVFP2()) { + getActionDefinitionsBuilder(G_BRCOND).legalFor({s1}); + + if (!ST.useSoftFloat() && ST.hasVFP2Base()) { getActionDefinitionsBuilder( {G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FCONSTANT, G_FNEG}) .legalFor({s32, s64}); - LoadStoreBuilder.legalFor({{s64, p0}}); + LoadStoreBuilder + .legalForTypesWithMemDesc({{s64, p0, 64, 32}}) + .maxScalar(0, s32); PhiBuilder.legalFor({s64}); getActionDefinitionsBuilder(G_FCMP).legalForCartesianProduct({s1}, @@ -219,13 +217,33 @@ ARMLegalizerInfo::ARMLegalizerInfo(const ARMSubtarget &ST) { .libcallForCartesianProduct({s32, s64}, {s32}); } - if (!ST.useSoftFloat() && ST.hasVFP4()) + if (!ST.useSoftFloat() && ST.hasVFP4Base()) getActionDefinitionsBuilder(G_FMA).legalFor({s32, s64}); else getActionDefinitionsBuilder(G_FMA).libcallFor({s32, s64}); getActionDefinitionsBuilder({G_FREM, G_FPOW}).libcallFor({s32, s64}); + if (ST.hasV5TOps()) { + getActionDefinitionsBuilder(G_CTLZ) + .legalFor({s32, s32}) + .clampScalar(1, s32, s32) + .clampScalar(0, s32, s32); + getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF) + .lowerFor({s32, s32}) + .clampScalar(1, s32, s32) + .clampScalar(0, s32, s32); + } else { + getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF) + .libcallFor({s32, s32}) + .clampScalar(1, s32, s32) + .clampScalar(0, s32, s32); + getActionDefinitionsBuilder(G_CTLZ) + .lowerFor({s32, s32}) + .clampScalar(1, s32, s32) + .clampScalar(0, s32, s32); + } + computeTables(); verify(*ST.getInstrInfo()); } @@ -351,7 +369,7 @@ bool ARMLegalizerInfo::legalizeCustom(MachineInstr &MI, return false; case G_SREM: case G_UREM: { - unsigned OriginalResult = MI.getOperand(0).getReg(); + Register OriginalResult = MI.getOperand(0).getReg(); auto Size = MRI.getType(OriginalResult).getSizeInBits(); if (Size != 32) return false; @@ -360,24 +378,17 @@ bool ARMLegalizerInfo::legalizeCustom(MachineInstr &MI, MI.getOpcode() == G_SREM ? RTLIB::SDIVREM_I32 : RTLIB::UDIVREM_I32; // Our divmod libcalls return a struct containing the quotient and the - // remainder. We need to create a virtual register for it. + // remainder. Create a new, unused register for the quotient and use the + // destination of the original instruction for the remainder. Type *ArgTy = Type::getInt32Ty(Ctx); StructType *RetTy = StructType::get(Ctx, {ArgTy, ArgTy}, /* Packed */ true); - auto RetVal = MRI.createGenericVirtualRegister( - getLLTForType(*RetTy, MIRBuilder.getMF().getDataLayout())); - - auto Status = createLibcall(MIRBuilder, Libcall, {RetVal, RetTy}, + Register RetRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), + OriginalResult}; + auto Status = createLibcall(MIRBuilder, Libcall, {RetRegs, RetTy}, {{MI.getOperand(1).getReg(), ArgTy}, {MI.getOperand(2).getReg(), ArgTy}}); if (Status != LegalizerHelper::Legalized) return false; - - // The remainder is the second result of divmod. Split the return value into - // a new, unused register for the quotient and the destination of the - // original instruction for the remainder. - MIRBuilder.buildUnmerge( - {MRI.createGenericVirtualRegister(LLT::scalar(32)), OriginalResult}, - RetVal); break; } case G_FCMP: { @@ -405,7 +416,7 @@ bool ARMLegalizerInfo::legalizeCustom(MachineInstr &MI, auto *ArgTy = OpSize == 32 ? Type::getFloatTy(Ctx) : Type::getDoubleTy(Ctx); auto *RetTy = Type::getInt32Ty(Ctx); - SmallVector<unsigned, 2> Results; + SmallVector<Register, 2> Results; for (auto Libcall : Libcalls) { auto LibcallResult = MRI.createGenericVirtualRegister(LLT::scalar(32)); auto Status = diff --git a/lib/Target/ARM/ARMLegalizerInfo.h b/lib/Target/ARM/ARMLegalizerInfo.h index 527bf87f1093..e95f8cf76103 100644 --- a/lib/Target/ARM/ARMLegalizerInfo.h +++ b/lib/Target/ARM/ARMLegalizerInfo.h @@ -1,9 +1,8 @@ //===- ARMLegalizerInfo ------------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file diff --git a/lib/Target/ARM/ARMLoadStoreOptimizer.cpp b/lib/Target/ARM/ARMLoadStoreOptimizer.cpp index 6da7430a8e51..90a1ce238c3f 100644 --- a/lib/Target/ARM/ARMLoadStoreOptimizer.cpp +++ b/lib/Target/ARM/ARMLoadStoreOptimizer.cpp @@ -1,9 +1,8 @@ //===- ARMLoadStoreOptimizer.cpp - ARM load / store opt. pass -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -174,12 +173,14 @@ namespace { MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertBefore, int Offset, unsigned Base, bool BaseKill, unsigned Opcode, ARMCC::CondCodes Pred, unsigned PredReg, const DebugLoc &DL, - ArrayRef<std::pair<unsigned, bool>> Regs); + ArrayRef<std::pair<unsigned, bool>> Regs, + ArrayRef<MachineInstr*> Instrs); MachineInstr *CreateLoadStoreDouble( MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertBefore, int Offset, unsigned Base, bool BaseKill, unsigned Opcode, ARMCC::CondCodes Pred, unsigned PredReg, const DebugLoc &DL, - ArrayRef<std::pair<unsigned, bool>> Regs) const; + ArrayRef<std::pair<unsigned, bool>> Regs, + ArrayRef<MachineInstr*> Instrs) const; void FormCandidates(const MemOpQueue &MemOps); MachineInstr *MergeOpsUpdate(const MergeCandidate &Cand); bool FixInvalidRegPairOp(MachineBasicBlock &MBB, @@ -623,7 +624,8 @@ MachineInstr *ARMLoadStoreOpt::CreateLoadStoreMulti( MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertBefore, int Offset, unsigned Base, bool BaseKill, unsigned Opcode, ARMCC::CondCodes Pred, unsigned PredReg, const DebugLoc &DL, - ArrayRef<std::pair<unsigned, bool>> Regs) { + ArrayRef<std::pair<unsigned, bool>> Regs, + ArrayRef<MachineInstr*> Instrs) { unsigned NumRegs = Regs.size(); assert(NumRegs > 1); @@ -815,6 +817,8 @@ MachineInstr *ARMLoadStoreOpt::CreateLoadStoreMulti( for (const std::pair<unsigned, bool> &R : Regs) MIB.addReg(R.first, getDefRegState(isDef) | getKillRegState(R.second)); + MIB.cloneMergedMemRefs(Instrs); + return MIB.getInstr(); } @@ -822,7 +826,8 @@ MachineInstr *ARMLoadStoreOpt::CreateLoadStoreDouble( MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertBefore, int Offset, unsigned Base, bool BaseKill, unsigned Opcode, ARMCC::CondCodes Pred, unsigned PredReg, const DebugLoc &DL, - ArrayRef<std::pair<unsigned, bool>> Regs) const { + ArrayRef<std::pair<unsigned, bool>> Regs, + ArrayRef<MachineInstr*> Instrs) const { bool IsLoad = isi32Load(Opcode); assert((IsLoad || isi32Store(Opcode)) && "Must have integer load or store"); unsigned LoadStoreOpcode = IsLoad ? ARM::t2LDRDi8 : ARM::t2STRDi8; @@ -838,6 +843,7 @@ MachineInstr *ARMLoadStoreOpt::CreateLoadStoreDouble( .addReg(Regs[1].first, getKillRegState(Regs[1].second)); } MIB.addReg(Base).addImm(Offset).addImm(Pred).addReg(PredReg); + MIB.cloneMergedMemRefs(Instrs); return MIB.getInstr(); } @@ -895,10 +901,11 @@ MachineInstr *ARMLoadStoreOpt::MergeOpsUpdate(const MergeCandidate &Cand) { MachineInstr *Merged = nullptr; if (Cand.CanMergeToLSDouble) Merged = CreateLoadStoreDouble(MBB, InsertBefore, Offset, Base, BaseKill, - Opcode, Pred, PredReg, DL, Regs); + Opcode, Pred, PredReg, DL, Regs, + Cand.Instrs); if (!Merged && Cand.CanMergeToLSMulti) Merged = CreateLoadStoreMulti(MBB, InsertBefore, Offset, Base, BaseKill, - Opcode, Pred, PredReg, DL, Regs); + Opcode, Pred, PredReg, DL, Regs, Cand.Instrs); if (!Merged) return nullptr; @@ -1287,7 +1294,7 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLSMultiple(MachineInstr *MI) { // can still change to a writeback form as that will save us 2 bytes // of code size. It can create WAW hazards though, so only do it if // we're minimizing code size. - if (!MBB.getParent()->getFunction().optForMinSize() || !BaseKill) + if (!STI->hasMinSize() || !BaseKill) return false; bool HighRegsUsed = false; @@ -1436,14 +1443,16 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLoadStore(MachineInstr *MI) { .addReg(Base, getKillRegState(isLd ? BaseKill : false)) .addImm(Pred).addReg(PredReg) .addReg(MO.getReg(), (isLd ? getDefRegState(true) : - getKillRegState(MO.isKill()))); + getKillRegState(MO.isKill()))) + .cloneMemRefs(*MI); } else if (isLd) { if (isAM2) { // LDR_PRE, LDR_POST if (NewOpc == ARM::LDR_PRE_IMM || NewOpc == ARM::LDRB_PRE_IMM) { BuildMI(MBB, MBBI, DL, TII->get(NewOpc), MI->getOperand(0).getReg()) .addReg(Base, RegState::Define) - .addReg(Base).addImm(Offset).addImm(Pred).addReg(PredReg); + .addReg(Base).addImm(Offset).addImm(Pred).addReg(PredReg) + .cloneMemRefs(*MI); } else { int Imm = ARM_AM::getAM2Opc(AddSub, Bytes, ARM_AM::no_shift); BuildMI(MBB, MBBI, DL, TII->get(NewOpc), MI->getOperand(0).getReg()) @@ -1451,7 +1460,8 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLoadStore(MachineInstr *MI) { .addReg(Base) .addReg(0) .addImm(Imm) - .add(predOps(Pred, PredReg)); + .add(predOps(Pred, PredReg)) + .cloneMemRefs(*MI); } } else { // t2LDR_PRE, t2LDR_POST @@ -1459,7 +1469,8 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLoadStore(MachineInstr *MI) { .addReg(Base, RegState::Define) .addReg(Base) .addImm(Offset) - .add(predOps(Pred, PredReg)); + .add(predOps(Pred, PredReg)) + .cloneMemRefs(*MI); } } else { MachineOperand &MO = MI->getOperand(0); @@ -1474,14 +1485,16 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLoadStore(MachineInstr *MI) { .addReg(Base) .addReg(0) .addImm(Imm) - .add(predOps(Pred, PredReg)); + .add(predOps(Pred, PredReg)) + .cloneMemRefs(*MI); } else { // t2STR_PRE, t2STR_POST BuildMI(MBB, MBBI, DL, TII->get(NewOpc), Base) .addReg(MO.getReg(), getKillRegState(MO.isKill())) .addReg(Base) .addImm(Offset) - .add(predOps(Pred, PredReg)); + .add(predOps(Pred, PredReg)) + .cloneMemRefs(*MI); } } MBB.erase(MBBI); @@ -1541,7 +1554,7 @@ bool ARMLoadStoreOpt::MergeBaseUpdateLSDouble(MachineInstr &MI) const { // Transfer implicit operands. for (const MachineOperand &MO : MI.implicit_operands()) MIB.add(MO); - MIB.setMemRefs(MI.memoperands()); + MIB.cloneMemRefs(MI); MBB.erase(MBBI); return true; @@ -1581,7 +1594,9 @@ static bool isMemoryOp(const MachineInstr &MI) { const MachineMemOperand &MMO = **MI.memoperands_begin(); // Don't touch volatile memory accesses - we may be changing their order. - if (MMO.isVolatile()) + // TODO: We could allow unordered and monotonic atomics here, but we need to + // make sure the resulting ldm/stm is correctly marked as atomic. + if (MMO.isVolatile() || MMO.isAtomic()) return false; // Unaligned ldr/str is emulated by some kernels, but unaligned ldm/stm is @@ -1607,19 +1622,26 @@ static void InsertLDR_STR(MachineBasicBlock &MBB, bool isDef, unsigned NewOpc, unsigned Reg, bool RegDeadKill, bool RegUndef, unsigned BaseReg, bool BaseKill, bool BaseUndef, ARMCC::CondCodes Pred, - unsigned PredReg, const TargetInstrInfo *TII) { + unsigned PredReg, const TargetInstrInfo *TII, + MachineInstr *MI) { if (isDef) { MachineInstrBuilder MIB = BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc)) .addReg(Reg, getDefRegState(true) | getDeadRegState(RegDeadKill)) .addReg(BaseReg, getKillRegState(BaseKill)|getUndefRegState(BaseUndef)); MIB.addImm(Offset).addImm(Pred).addReg(PredReg); + // FIXME: This is overly conservative; the new instruction accesses 4 + // bytes, not 8. + MIB.cloneMemRefs(*MI); } else { MachineInstrBuilder MIB = BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc)) .addReg(Reg, getKillRegState(RegDeadKill) | getUndefRegState(RegUndef)) .addReg(BaseReg, getKillRegState(BaseKill)|getUndefRegState(BaseUndef)); MIB.addImm(Offset).addImm(Pred).addReg(PredReg); + // FIXME: This is overly conservative; the new instruction accesses 4 + // bytes, not 8. + MIB.cloneMemRefs(*MI); } } @@ -1677,7 +1699,8 @@ bool ARMLoadStoreOpt::FixInvalidRegPairOp(MachineBasicBlock &MBB, .addReg(BaseReg, getKillRegState(BaseKill)) .addImm(Pred).addReg(PredReg) .addReg(EvenReg, getDefRegState(isLd) | getDeadRegState(EvenDeadKill)) - .addReg(OddReg, getDefRegState(isLd) | getDeadRegState(OddDeadKill)); + .addReg(OddReg, getDefRegState(isLd) | getDeadRegState(OddDeadKill)) + .cloneMemRefs(*MI); ++NumLDRD2LDM; } else { BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc)) @@ -1686,7 +1709,8 @@ bool ARMLoadStoreOpt::FixInvalidRegPairOp(MachineBasicBlock &MBB, .addReg(EvenReg, getKillRegState(EvenDeadKill) | getUndefRegState(EvenUndef)) .addReg(OddReg, - getKillRegState(OddDeadKill) | getUndefRegState(OddUndef)); + getKillRegState(OddDeadKill) | getUndefRegState(OddUndef)) + .cloneMemRefs(*MI); ++NumSTRD2STM; } } else { @@ -1704,9 +1728,10 @@ bool ARMLoadStoreOpt::FixInvalidRegPairOp(MachineBasicBlock &MBB, if (isLd && TRI->regsOverlap(EvenReg, BaseReg)) { assert(!TRI->regsOverlap(OddReg, BaseReg)); InsertLDR_STR(MBB, MBBI, OffImm + 4, isLd, NewOpc2, OddReg, OddDeadKill, - false, BaseReg, false, BaseUndef, Pred, PredReg, TII); + false, BaseReg, false, BaseUndef, Pred, PredReg, TII, MI); InsertLDR_STR(MBB, MBBI, OffImm, isLd, NewOpc, EvenReg, EvenDeadKill, - false, BaseReg, BaseKill, BaseUndef, Pred, PredReg, TII); + false, BaseReg, BaseKill, BaseUndef, Pred, PredReg, TII, + MI); } else { if (OddReg == EvenReg && EvenDeadKill) { // If the two source operands are the same, the kill marker is @@ -1719,9 +1744,11 @@ bool ARMLoadStoreOpt::FixInvalidRegPairOp(MachineBasicBlock &MBB, if (EvenReg == BaseReg) EvenDeadKill = false; InsertLDR_STR(MBB, MBBI, OffImm, isLd, NewOpc, EvenReg, EvenDeadKill, - EvenUndef, BaseReg, false, BaseUndef, Pred, PredReg, TII); + EvenUndef, BaseReg, false, BaseUndef, Pred, PredReg, TII, + MI); InsertLDR_STR(MBB, MBBI, OffImm + 4, isLd, NewOpc2, OddReg, OddDeadKill, - OddUndef, BaseReg, BaseKill, BaseUndef, Pred, PredReg, TII); + OddUndef, BaseReg, BaseKill, BaseUndef, Pred, PredReg, TII, + MI); } if (isLd) ++NumLDRD2LDR; @@ -2048,6 +2075,11 @@ char ARMPreAllocLoadStoreOpt::ID = 0; INITIALIZE_PASS(ARMPreAllocLoadStoreOpt, "arm-prera-ldst-opt", ARM_PREALLOC_LOAD_STORE_OPT_NAME, false, false) +// Limit the number of instructions to be rescheduled. +// FIXME: tune this limit, and/or come up with some better heuristics. +static cl::opt<unsigned> InstReorderLimit("arm-prera-ldst-opt-reorder-limit", + cl::init(8), cl::Hidden); + bool ARMPreAllocLoadStoreOpt::runOnMachineFunction(MachineFunction &Fn) { if (AssumeMisalignedLoadStores || skipFunction(Fn.getFunction())) return false; @@ -2140,7 +2172,8 @@ ARMPreAllocLoadStoreOpt::CanFormLdStDWord(MachineInstr *Op0, MachineInstr *Op1, // At the moment, we ignore the memoryoperand's value. // If we want to use AliasAnalysis, we should check it accordingly. if (!Op0->hasOneMemOperand() || - (*Op0->memoperands_begin())->isVolatile()) + (*Op0->memoperands_begin())->isVolatile() || + (*Op0->memoperands_begin())->isAtomic()) return false; unsigned Align = (*Op0->memoperands_begin())->getAlignment(); @@ -2223,7 +2256,7 @@ bool ARMPreAllocLoadStoreOpt::RescheduleOps(MachineBasicBlock *MBB, } // Don't try to reschedule too many instructions. - if (NumMove == 8) // FIXME: Tune this limit. + if (NumMove == InstReorderLimit) break; // Found a mergable instruction; save information about it. @@ -2351,10 +2384,13 @@ ARMPreAllocLoadStoreOpt::RescheduleLoadStoreInstrs(MachineBasicBlock *MBB) { bool RetVal = false; DenseMap<MachineInstr*, unsigned> MI2LocMap; - DenseMap<unsigned, SmallVector<MachineInstr *, 4>> Base2LdsMap; - DenseMap<unsigned, SmallVector<MachineInstr *, 4>> Base2StsMap; - SmallVector<unsigned, 4> LdBases; - SmallVector<unsigned, 4> StBases; + using MapIt = DenseMap<unsigned, SmallVector<MachineInstr *, 4>>::iterator; + using Base2InstMap = DenseMap<unsigned, SmallVector<MachineInstr *, 4>>; + using BaseVec = SmallVector<unsigned, 4>; + Base2InstMap Base2LdsMap; + Base2InstMap Base2StsMap; + BaseVec LdBases; + BaseVec StBases; unsigned Loc = 0; MachineBasicBlock::iterator MBBI = MBB->begin(); @@ -2381,41 +2417,28 @@ ARMPreAllocLoadStoreOpt::RescheduleLoadStoreInstrs(MachineBasicBlock *MBB) { bool isLd = isLoadSingle(Opc); unsigned Base = MI.getOperand(1).getReg(); int Offset = getMemoryOpOffset(MI); - bool StopHere = false; - if (isLd) { - DenseMap<unsigned, SmallVector<MachineInstr *, 4>>::iterator BI = - Base2LdsMap.find(Base); - if (BI != Base2LdsMap.end()) { - for (unsigned i = 0, e = BI->second.size(); i != e; ++i) { - if (Offset == getMemoryOpOffset(*BI->second[i])) { - StopHere = true; - break; - } - } - if (!StopHere) - BI->second.push_back(&MI); - } else { - Base2LdsMap[Base].push_back(&MI); - LdBases.push_back(Base); + auto FindBases = [&] (Base2InstMap &Base2Ops, BaseVec &Bases) { + MapIt BI = Base2Ops.find(Base); + if (BI == Base2Ops.end()) { + Base2Ops[Base].push_back(&MI); + Bases.push_back(Base); + return; } - } else { - DenseMap<unsigned, SmallVector<MachineInstr *, 4>>::iterator BI = - Base2StsMap.find(Base); - if (BI != Base2StsMap.end()) { - for (unsigned i = 0, e = BI->second.size(); i != e; ++i) { - if (Offset == getMemoryOpOffset(*BI->second[i])) { - StopHere = true; - break; - } + for (unsigned i = 0, e = BI->second.size(); i != e; ++i) { + if (Offset == getMemoryOpOffset(*BI->second[i])) { + StopHere = true; + break; } - if (!StopHere) - BI->second.push_back(&MI); - } else { - Base2StsMap[Base].push_back(&MI); - StBases.push_back(Base); } - } + if (!StopHere) + BI->second.push_back(&MI); + }; + + if (isLd) + FindBases(Base2LdsMap, LdBases); + else + FindBases(Base2StsMap, StBases); if (StopHere) { // Found a duplicate (a base+offset combination that's seen earlier). diff --git a/lib/Target/ARM/ARMLowOverheadLoops.cpp b/lib/Target/ARM/ARMLowOverheadLoops.cpp new file mode 100644 index 000000000000..cedf3bd3c74e --- /dev/null +++ b/lib/Target/ARM/ARMLowOverheadLoops.cpp @@ -0,0 +1,384 @@ +//===-- ARMLowOverheadLoops.cpp - CodeGen Low-overhead Loops ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// Finalize v8.1-m low-overhead loops by converting the associated pseudo +/// instructions into machine operations. +/// The expectation is that the loop contains three pseudo instructions: +/// - t2*LoopStart - placed in the preheader or pre-preheader. The do-loop +/// form should be in the preheader, whereas the while form should be in the +/// preheaders only predecessor. TODO: Could DoLoopStart get moved into the +/// pre-preheader? +/// - t2LoopDec - placed within in the loop body. +/// - t2LoopEnd - the loop latch terminator. +/// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMBaseInstrInfo.h" +#include "ARMBaseRegisterInfo.h" +#include "ARMBasicBlockInfo.h" +#include "ARMSubtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +using namespace llvm; + +#define DEBUG_TYPE "arm-low-overhead-loops" +#define ARM_LOW_OVERHEAD_LOOPS_NAME "ARM Low Overhead Loops pass" + +namespace { + + class ARMLowOverheadLoops : public MachineFunctionPass { + const ARMBaseInstrInfo *TII = nullptr; + MachineRegisterInfo *MRI = nullptr; + std::unique_ptr<ARMBasicBlockUtils> BBUtils = nullptr; + + public: + static char ID; + + ARMLowOverheadLoops() : MachineFunctionPass(ID) { } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired<MachineLoopInfo>(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + bool ProcessLoop(MachineLoop *ML); + + void RevertWhile(MachineInstr *MI) const; + + void RevertLoopDec(MachineInstr *MI) const; + + void RevertLoopEnd(MachineInstr *MI) const; + + void Expand(MachineLoop *ML, MachineInstr *Start, + MachineInstr *Dec, MachineInstr *End, bool Revert); + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return ARM_LOW_OVERHEAD_LOOPS_NAME; + } + }; +} + +char ARMLowOverheadLoops::ID = 0; + +INITIALIZE_PASS(ARMLowOverheadLoops, DEBUG_TYPE, ARM_LOW_OVERHEAD_LOOPS_NAME, + false, false) + +bool ARMLowOverheadLoops::runOnMachineFunction(MachineFunction &MF) { + if (!static_cast<const ARMSubtarget&>(MF.getSubtarget()).hasLOB()) + return false; + + LLVM_DEBUG(dbgs() << "ARM Loops on " << MF.getName() << " ------------- \n"); + + auto &MLI = getAnalysis<MachineLoopInfo>(); + MRI = &MF.getRegInfo(); + TII = static_cast<const ARMBaseInstrInfo*>( + MF.getSubtarget().getInstrInfo()); + BBUtils = std::unique_ptr<ARMBasicBlockUtils>(new ARMBasicBlockUtils(MF)); + BBUtils->computeAllBlockSizes(); + BBUtils->adjustBBOffsetsAfter(&MF.front()); + + bool Changed = false; + for (auto ML : MLI) { + if (!ML->getParentLoop()) + Changed |= ProcessLoop(ML); + } + return Changed; +} + +bool ARMLowOverheadLoops::ProcessLoop(MachineLoop *ML) { + + bool Changed = false; + + // Process inner loops first. + for (auto I = ML->begin(), E = ML->end(); I != E; ++I) + Changed |= ProcessLoop(*I); + + LLVM_DEBUG(dbgs() << "ARM Loops: Processing " << *ML); + + auto IsLoopStart = [](MachineInstr &MI) { + return MI.getOpcode() == ARM::t2DoLoopStart || + MI.getOpcode() == ARM::t2WhileLoopStart; + }; + + // Search the given block for a loop start instruction. If one isn't found, + // and there's only one predecessor block, search that one too. + std::function<MachineInstr*(MachineBasicBlock*)> SearchForStart = + [&IsLoopStart, &SearchForStart](MachineBasicBlock *MBB) -> MachineInstr* { + for (auto &MI : *MBB) { + if (IsLoopStart(MI)) + return &MI; + } + if (MBB->pred_size() == 1) + return SearchForStart(*MBB->pred_begin()); + return nullptr; + }; + + MachineInstr *Start = nullptr; + MachineInstr *Dec = nullptr; + MachineInstr *End = nullptr; + bool Revert = false; + + // Search the preheader for the start intrinsic, or look through the + // predecessors of the header to find exactly one set.iterations intrinsic. + // FIXME: I don't see why we shouldn't be supporting multiple predecessors + // with potentially multiple set.loop.iterations, so we need to enable this. + if (auto *Preheader = ML->getLoopPreheader()) { + Start = SearchForStart(Preheader); + } else { + LLVM_DEBUG(dbgs() << "ARM Loops: Failed to find loop preheader!\n" + << " - Performing manual predecessor search.\n"); + MachineBasicBlock *Pred = nullptr; + for (auto *MBB : ML->getHeader()->predecessors()) { + if (!ML->contains(MBB)) { + if (Pred) { + LLVM_DEBUG(dbgs() << " - Found multiple out-of-loop preds.\n"); + Start = nullptr; + break; + } + Pred = MBB; + Start = SearchForStart(MBB); + } + } + } + + // Find the low-overhead loop components and decide whether or not to fall + // back to a normal loop. + for (auto *MBB : reverse(ML->getBlocks())) { + for (auto &MI : *MBB) { + if (MI.getOpcode() == ARM::t2LoopDec) + Dec = &MI; + else if (MI.getOpcode() == ARM::t2LoopEnd) + End = &MI; + else if (MI.getDesc().isCall()) + // TODO: Though the call will require LE to execute again, does this + // mean we should revert? Always executing LE hopefully should be + // faster than performing a sub,cmp,br or even subs,br. + Revert = true; + + if (!Dec) + continue; + + // If we find that we load/store LR between LoopDec and LoopEnd, expect + // that the decremented value has been spilled to the stack. Because + // this value isn't actually going to be produced until the latch, by LE, + // we would need to generate a real sub. The value is also likely to be + // reloaded for use of LoopEnd - in which in case we'd need to perform + // an add because it gets negated again by LE! The other option is to + // then generate the other form of LE which doesn't perform the sub. + if (MI.mayLoad() || MI.mayStore()) + Revert = + MI.getOperand(0).isReg() && MI.getOperand(0).getReg() == ARM::LR; + } + + if (Dec && End && Revert) + break; + } + + if (!Start && !Dec && !End) { + LLVM_DEBUG(dbgs() << "ARM Loops: Not a low-overhead loop.\n"); + return Changed; + } if (!(Start && Dec && End)) { + report_fatal_error("Failed to find all loop components"); + } + + if (!End->getOperand(1).isMBB() || + End->getOperand(1).getMBB() != ML->getHeader()) + report_fatal_error("Expected LoopEnd to target Loop Header"); + + // The WLS and LE instructions have 12-bits for the label offset. WLS + // requires a positive offset, while LE uses negative. + if (BBUtils->getOffsetOf(End) < BBUtils->getOffsetOf(ML->getHeader()) || + !BBUtils->isBBInRange(End, ML->getHeader(), 4094)) { + LLVM_DEBUG(dbgs() << "ARM Loops: LE offset is out-of-range\n"); + Revert = true; + } + if (Start->getOpcode() == ARM::t2WhileLoopStart && + (BBUtils->getOffsetOf(Start) > + BBUtils->getOffsetOf(Start->getOperand(1).getMBB()) || + !BBUtils->isBBInRange(Start, Start->getOperand(1).getMBB(), 4094))) { + LLVM_DEBUG(dbgs() << "ARM Loops: WLS offset is out-of-range!\n"); + Revert = true; + } + + LLVM_DEBUG(dbgs() << "ARM Loops:\n - Found Loop Start: " << *Start + << " - Found Loop Dec: " << *Dec + << " - Found Loop End: " << *End); + + Expand(ML, Start, Dec, End, Revert); + return true; +} + +// WhileLoopStart holds the exit block, so produce a cmp lr, 0 and then a +// beq that branches to the exit branch. +// FIXME: Need to check that we're not trashing the CPSR when generating the +// cmp. We could also try to generate a cbz if the value in LR is also in +// another low register. +void ARMLowOverheadLoops::RevertWhile(MachineInstr *MI) const { + LLVM_DEBUG(dbgs() << "ARM Loops: Reverting to cmp: " << *MI); + MachineBasicBlock *MBB = MI->getParent(); + MachineInstrBuilder MIB = BuildMI(*MBB, MI, MI->getDebugLoc(), + TII->get(ARM::t2CMPri)); + MIB.addReg(ARM::LR); + MIB.addImm(0); + MIB.addImm(ARMCC::AL); + MIB.addReg(ARM::CPSR); + + // TODO: Try to use tBcc instead + MIB = BuildMI(*MBB, MI, MI->getDebugLoc(), TII->get(ARM::t2Bcc)); + MIB.add(MI->getOperand(1)); // branch target + MIB.addImm(ARMCC::EQ); // condition code + MIB.addReg(ARM::CPSR); + MI->eraseFromParent(); +} + +// TODO: Check flags so that we can possibly generate a tSubs or tSub. +void ARMLowOverheadLoops::RevertLoopDec(MachineInstr *MI) const { + LLVM_DEBUG(dbgs() << "ARM Loops: Reverting to sub: " << *MI); + MachineBasicBlock *MBB = MI->getParent(); + MachineInstrBuilder MIB = BuildMI(*MBB, MI, MI->getDebugLoc(), + TII->get(ARM::t2SUBri)); + MIB.addDef(ARM::LR); + MIB.add(MI->getOperand(1)); + MIB.add(MI->getOperand(2)); + MIB.addImm(ARMCC::AL); + MIB.addReg(0); + MIB.addReg(0); + MI->eraseFromParent(); +} + +// Generate a subs, or sub and cmp, and a branch instead of an LE. +// FIXME: Need to check that we're not trashing the CPSR when generating +// the cmp. +void ARMLowOverheadLoops::RevertLoopEnd(MachineInstr *MI) const { + LLVM_DEBUG(dbgs() << "ARM Loops: Reverting to cmp, br: " << *MI); + + // Create cmp + MachineBasicBlock *MBB = MI->getParent(); + MachineInstrBuilder MIB = BuildMI(*MBB, MI, MI->getDebugLoc(), + TII->get(ARM::t2CMPri)); + MIB.addReg(ARM::LR); + MIB.addImm(0); + MIB.addImm(ARMCC::AL); + MIB.addReg(ARM::CPSR); + + // TODO Try to use tBcc instead. + // Create bne + MIB = BuildMI(*MBB, MI, MI->getDebugLoc(), TII->get(ARM::t2Bcc)); + MIB.add(MI->getOperand(1)); // branch target + MIB.addImm(ARMCC::NE); // condition code + MIB.addReg(ARM::CPSR); + MI->eraseFromParent(); +} + +void ARMLowOverheadLoops::Expand(MachineLoop *ML, MachineInstr *Start, + MachineInstr *Dec, MachineInstr *End, + bool Revert) { + + auto ExpandLoopStart = [this](MachineLoop *ML, MachineInstr *Start) { + // The trip count should already been held in LR since the instructions + // within the loop can only read and write to LR. So, there should be a + // mov to setup the count. WLS/DLS perform this move, so find the original + // and delete it - inserting WLS/DLS in its place. + MachineBasicBlock *MBB = Start->getParent(); + MachineInstr *InsertPt = Start; + for (auto &I : MRI->def_instructions(ARM::LR)) { + if (I.getParent() != MBB) + continue; + + // Always execute. + if (!I.getOperand(2).isImm() || I.getOperand(2).getImm() != ARMCC::AL) + continue; + + // Only handle move reg, if the trip count it will need moving into a reg + // before the setup instruction anyway. + if (!I.getDesc().isMoveReg() || + !I.getOperand(1).isIdenticalTo(Start->getOperand(0))) + continue; + InsertPt = &I; + break; + } + + unsigned Opc = Start->getOpcode() == ARM::t2DoLoopStart ? + ARM::t2DLS : ARM::t2WLS; + MachineInstrBuilder MIB = + BuildMI(*MBB, InsertPt, InsertPt->getDebugLoc(), TII->get(Opc)); + + MIB.addDef(ARM::LR); + MIB.add(Start->getOperand(0)); + if (Opc == ARM::t2WLS) + MIB.add(Start->getOperand(1)); + + if (InsertPt != Start) + InsertPt->eraseFromParent(); + Start->eraseFromParent(); + LLVM_DEBUG(dbgs() << "ARM Loops: Inserted start: " << *MIB); + return &*MIB; + }; + + // Combine the LoopDec and LoopEnd instructions into LE(TP). + auto ExpandLoopEnd = [this](MachineLoop *ML, MachineInstr *Dec, + MachineInstr *End) { + MachineBasicBlock *MBB = End->getParent(); + MachineInstrBuilder MIB = BuildMI(*MBB, End, End->getDebugLoc(), + TII->get(ARM::t2LEUpdate)); + MIB.addDef(ARM::LR); + MIB.add(End->getOperand(0)); + MIB.add(End->getOperand(1)); + LLVM_DEBUG(dbgs() << "ARM Loops: Inserted LE: " << *MIB); + + End->eraseFromParent(); + Dec->eraseFromParent(); + return &*MIB; + }; + + // TODO: We should be able to automatically remove these branches before we + // get here - probably by teaching analyzeBranch about the pseudo + // instructions. + // If there is an unconditional branch, after I, that just branches to the + // next block, remove it. + auto RemoveDeadBranch = [](MachineInstr *I) { + MachineBasicBlock *BB = I->getParent(); + MachineInstr *Terminator = &BB->instr_back(); + if (Terminator->isUnconditionalBranch() && I != Terminator) { + MachineBasicBlock *Succ = Terminator->getOperand(0).getMBB(); + if (BB->isLayoutSuccessor(Succ)) { + LLVM_DEBUG(dbgs() << "ARM Loops: Removing branch: " << *Terminator); + Terminator->eraseFromParent(); + } + } + }; + + if (Revert) { + if (Start->getOpcode() == ARM::t2WhileLoopStart) + RevertWhile(Start); + else + Start->eraseFromParent(); + RevertLoopDec(Dec); + RevertLoopEnd(End); + } else { + Start = ExpandLoopStart(ML, Start); + RemoveDeadBranch(Start); + End = ExpandLoopEnd(ML, Dec, End); + RemoveDeadBranch(End); + } +} + +FunctionPass *llvm::createARMLowOverheadLoopsPass() { + return new ARMLowOverheadLoops(); +} diff --git a/lib/Target/ARM/ARMMCInstLower.cpp b/lib/Target/ARM/ARMMCInstLower.cpp index 48b02d40b246..90c5ad025e56 100644 --- a/lib/Target/ARM/ARMMCInstLower.cpp +++ b/lib/Target/ARM/ARMMCInstLower.cpp @@ -1,9 +1,8 @@ //===-- ARMMCInstLower.cpp - Convert ARM MachineInstr to an MCInst --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMMachineFunctionInfo.cpp b/lib/Target/ARM/ARMMachineFunctionInfo.cpp index e25d36b57616..3b676ca4c883 100644 --- a/lib/Target/ARM/ARMMachineFunctionInfo.cpp +++ b/lib/Target/ARM/ARMMachineFunctionInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMMachineFunctionInfo.cpp - ARM machine function info ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMMachineFunctionInfo.h b/lib/Target/ARM/ARMMachineFunctionInfo.h index 91310e81e398..90d794cd27b1 100644 --- a/lib/Target/ARM/ARMMachineFunctionInfo.h +++ b/lib/Target/ARM/ARMMachineFunctionInfo.h @@ -1,9 +1,8 @@ //===-- ARMMachineFunctionInfo.h - ARM machine function info ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -62,6 +61,10 @@ class ARMFunctionInfo : public MachineFunctionInfo { /// enable far jump. bool LRSpilledForFarJump = false; + /// LRSpilled - True if the LR register has been for spilled for + /// any reason, so it's legal to emit an ARM::tBfar (i.e. "bl"). + bool LRSpilled = false; + /// FramePtrSpillOffset - If HasStackFrame, this records the frame pointer /// spill stack offset. unsigned FramePtrSpillOffset = 0; @@ -151,6 +154,9 @@ public: bool shouldRestoreSPFromFP() const { return RestoreSPFromFP; } void setShouldRestoreSPFromFP(bool s) { RestoreSPFromFP = s; } + bool isLRSpilled() const { return LRSpilled; } + void setLRIsSpilled(bool s) { LRSpilled = s; } + bool isLRSpilledForFarJump() const { return LRSpilledForFarJump; } void setLRIsSpilledForFarJump(bool s) { LRSpilledForFarJump = s; } @@ -239,6 +245,8 @@ public: void setPromotedConstpoolIncrease(int Sz) { PromotedGlobalsIncrease = Sz; } + + DenseMap<unsigned, unsigned> EHPrologueRemappedRegs; }; } // end namespace llvm diff --git a/lib/Target/ARM/ARMMacroFusion.cpp b/lib/Target/ARM/ARMMacroFusion.cpp index df1da9d8e474..38bf28ba8219 100644 --- a/lib/Target/ARM/ARMMacroFusion.cpp +++ b/lib/Target/ARM/ARMMacroFusion.cpp @@ -1,9 +1,8 @@ //===- ARMMacroFusion.cpp - ARM Macro Fusion ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMMacroFusion.h b/lib/Target/ARM/ARMMacroFusion.h index b3abd7b593a1..4896a4a2544d 100644 --- a/lib/Target/ARM/ARMMacroFusion.h +++ b/lib/Target/ARM/ARMMacroFusion.h @@ -1,9 +1,8 @@ //===- ARMMacroFusion.h - ARM Macro Fusion ------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMOptimizeBarriersPass.cpp b/lib/Target/ARM/ARMOptimizeBarriersPass.cpp index cff4a256100d..348895da713f 100644 --- a/lib/Target/ARM/ARMOptimizeBarriersPass.cpp +++ b/lib/Target/ARM/ARMOptimizeBarriersPass.cpp @@ -1,10 +1,9 @@ //===-- ARMOptimizeBarriersPass - two DMBs without a memory access in between, //removed one -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===------------------------------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMParallelDSP.cpp b/lib/Target/ARM/ARMParallelDSP.cpp index fc3258914f92..5389d09bf7d7 100644 --- a/lib/Target/ARM/ARMParallelDSP.cpp +++ b/lib/Target/ARM/ARMParallelDSP.cpp @@ -1,9 +1,8 @@ //===- ParallelDSP.cpp - Parallel DSP Pass --------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -49,12 +48,12 @@ DisableParallelDSP("disable-arm-parallel-dsp", cl::Hidden, cl::init(false), namespace { struct OpChain; struct BinOpChain; - struct Reduction; + class Reduction; using OpChainList = SmallVector<std::unique_ptr<OpChain>, 8>; using ReductionList = SmallVector<Reduction, 8>; using ValueList = SmallVector<Value*, 8>; - using MemInstList = SmallVector<Instruction*, 8>; + using MemInstList = SmallVector<LoadInst*, 8>; using PMACPair = std::pair<BinOpChain*,BinOpChain*>; using PMACPairList = SmallVector<PMACPair, 8>; using Instructions = SmallVector<Instruction*,16>; @@ -64,31 +63,24 @@ namespace { Instruction *Root; ValueList AllValues; MemInstList VecLd; // List of all load instructions. - MemLocList MemLocs; // All memory locations read by this tree. + MemInstList Loads; bool ReadOnly = true; OpChain(Instruction *I, ValueList &vl) : Root(I), AllValues(vl) { } virtual ~OpChain() = default; - void SetMemoryLocations() { - const auto Size = LocationSize::unknown(); + void PopulateLoads() { for (auto *V : AllValues) { - if (auto *I = dyn_cast<Instruction>(V)) { - if (I->mayWriteToMemory()) - ReadOnly = false; - if (auto *Ld = dyn_cast<LoadInst>(V)) - MemLocs.push_back(MemoryLocation(Ld->getPointerOperand(), Size)); - } + if (auto *Ld = dyn_cast<LoadInst>(V)) + Loads.push_back(Ld); } } unsigned size() const { return AllValues.size(); } }; - // 'BinOpChain' and 'Reduction' are just some bookkeeping data structures. - // 'Reduction' contains the phi-node and accumulator statement from where we - // start pattern matching, and 'BinOpChain' the multiplication - // instructions that are candidates for parallel execution. + // 'BinOpChain' holds the multiplication instructions that are candidates + // for parallel execution. struct BinOpChain : public OpChain { ValueList LHS; // List of all (narrow) left hand operands. ValueList RHS; // List of all (narrow) right hand operands. @@ -103,15 +95,85 @@ namespace { bool AreSymmetrical(BinOpChain *Other); }; - struct Reduction { - PHINode *Phi; // The Phi-node from where we start - // pattern matching. - Instruction *AccIntAdd; // The accumulating integer add statement, - // i.e, the reduction statement. - OpChainList MACCandidates; // The MAC candidates associated with - // this reduction statement. - PMACPairList PMACPairs; - Reduction (PHINode *P, Instruction *Acc) : Phi(P), AccIntAdd(Acc) { }; + /// Represent a sequence of multiply-accumulate operations with the aim to + /// perform the multiplications in parallel. + class Reduction { + Instruction *Root = nullptr; + Value *Acc = nullptr; + OpChainList Muls; + PMACPairList MulPairs; + SmallPtrSet<Instruction*, 4> Adds; + + public: + Reduction() = delete; + + Reduction (Instruction *Add) : Root(Add) { } + + /// Record an Add instruction that is a part of the this reduction. + void InsertAdd(Instruction *I) { Adds.insert(I); } + + /// Record a BinOpChain, rooted at a Mul instruction, that is a part of + /// this reduction. + void InsertMul(Instruction *I, ValueList &LHS, ValueList &RHS) { + Muls.push_back(make_unique<BinOpChain>(I, LHS, RHS)); + } + + /// Add the incoming accumulator value, returns true if a value had not + /// already been added. Returning false signals to the user that this + /// reduction already has a value to initialise the accumulator. + bool InsertAcc(Value *V) { + if (Acc) + return false; + Acc = V; + return true; + } + + /// Set two BinOpChains, rooted at muls, that can be executed as a single + /// parallel operation. + void AddMulPair(BinOpChain *Mul0, BinOpChain *Mul1) { + MulPairs.push_back(std::make_pair(Mul0, Mul1)); + } + + /// Return true if enough mul operations are found that can be executed in + /// parallel. + bool CreateParallelPairs(); + + /// Return the add instruction which is the root of the reduction. + Instruction *getRoot() { return Root; } + + /// Return the incoming value to be accumulated. This maybe null. + Value *getAccumulator() { return Acc; } + + /// Return the set of adds that comprise the reduction. + SmallPtrSetImpl<Instruction*> &getAdds() { return Adds; } + + /// Return the BinOpChain, rooted at mul instruction, that comprise the + /// the reduction. + OpChainList &getMuls() { return Muls; } + + /// Return the BinOpChain, rooted at mul instructions, that have been + /// paired for parallel execution. + PMACPairList &getMulPairs() { return MulPairs; } + + /// To finalise, replace the uses of the root with the intrinsic call. + void UpdateRoot(Instruction *SMLAD) { + Root->replaceAllUsesWith(SMLAD); + } + }; + + class WidenedLoad { + LoadInst *NewLd = nullptr; + SmallVector<LoadInst*, 4> Loads; + + public: + WidenedLoad(SmallVectorImpl<LoadInst*> &Lds, LoadInst *Wide) + : NewLd(Wide) { + for (auto *I : Lds) + Loads.push_back(I); + } + LoadInst *getLoad() { + return NewLd; + } }; class ARMParallelDSP : public LoopPass { @@ -124,28 +186,37 @@ namespace { const DataLayout *DL; Module *M; std::map<LoadInst*, LoadInst*> LoadPairs; - std::map<LoadInst*, SmallVector<LoadInst*, 4>> SequentialLoads; + SmallPtrSet<LoadInst*, 4> OffsetLoads; + std::map<LoadInst*, std::unique_ptr<WidenedLoad>> WideLoads; - bool RecordSequentialLoads(BasicBlock *Header); - bool InsertParallelMACs(Reduction &Reduction); + template<unsigned> + bool IsNarrowSequence(Value *V, ValueList &VL); + + bool RecordMemoryOps(BasicBlock *BB); + void InsertParallelMACs(Reduction &Reduction); bool AreSequentialLoads(LoadInst *Ld0, LoadInst *Ld1, MemInstList &VecMem); - void CreateParallelMACPairs(Reduction &R); - Instruction *CreateSMLADCall(LoadInst *VecLd0, LoadInst *VecLd1, - Instruction *Acc, bool Exchange, - Instruction *InsertAfter); + LoadInst* CreateWideLoad(SmallVectorImpl<LoadInst*> &Loads, + IntegerType *LoadTy); + bool CreateParallelPairs(Reduction &R); /// Try to match and generate: SMLAD, SMLADX - Signed Multiply Accumulate /// Dual performs two signed 16x16-bit multiplications. It adds the /// products to a 32-bit accumulate operand. Optionally, the instruction can /// exchange the halfwords of the second operand before performing the /// arithmetic. - bool MatchSMLAD(Function &F); + bool MatchSMLAD(Loop *L); public: static char ID; ARMParallelDSP() : LoopPass(ID) { } + bool doInitialization(Loop *L, LPPassManager &LPM) override { + LoadPairs.clear(); + WideLoads.clear(); + return true; + } + void getAnalysisUsage(AnalysisUsage &AU) const override { LoopPass::getAnalysisUsage(AU); AU.addRequired<AssumptionCacheTracker>(); @@ -183,6 +254,9 @@ namespace { return false; } + if (!TheLoop->getLoopPreheader()) + InsertPreheaderForLoop(L, DT, LI, nullptr, true); + Function &F = *Header->getParent(); M = F.getParent(); DL = &M->getDataLayout(); @@ -202,31 +276,62 @@ namespace { return false; } + if (!ST->isLittle()) { + LLVM_DEBUG(dbgs() << "Only supporting little endian: not running pass " + << "ARMParallelDSP\n"); + return false; + } + LoopAccessInfo LAI(L, SE, TLI, AA, DT, LI); - bool Changes = false; LLVM_DEBUG(dbgs() << "\n== Parallel DSP pass ==\n"); LLVM_DEBUG(dbgs() << " - " << F.getName() << "\n\n"); - if (!RecordSequentialLoads(Header)) { + if (!RecordMemoryOps(Header)) { LLVM_DEBUG(dbgs() << " - No sequential loads found.\n"); return false; } - Changes = MatchSMLAD(F); + bool Changes = MatchSMLAD(L); return Changes; } }; } +template<typename MemInst> +static bool AreSequentialAccesses(MemInst *MemOp0, MemInst *MemOp1, + const DataLayout &DL, ScalarEvolution &SE) { + if (isConsecutiveAccess(MemOp0, MemOp1, DL, SE)) + return true; + return false; +} + +bool ARMParallelDSP::AreSequentialLoads(LoadInst *Ld0, LoadInst *Ld1, + MemInstList &VecMem) { + if (!Ld0 || !Ld1) + return false; + + if (!LoadPairs.count(Ld0) || LoadPairs[Ld0] != Ld1) + return false; + + LLVM_DEBUG(dbgs() << "Loads are sequential and valid:\n"; + dbgs() << "Ld0:"; Ld0->dump(); + dbgs() << "Ld1:"; Ld1->dump(); + ); + + VecMem.clear(); + VecMem.push_back(Ld0); + VecMem.push_back(Ld1); + return true; +} + // MaxBitwidth: the maximum supported bitwidth of the elements in the DSP // instructions, which is set to 16. So here we should collect all i8 and i16 // narrow operations. // TODO: we currently only collect i16, and will support i8 later, so that's // why we check that types are equal to MaxBitWidth, and not <= MaxBitWidth. template<unsigned MaxBitWidth> -static bool IsNarrowSequence(Value *V, ValueList &VL) { - LLVM_DEBUG(dbgs() << "Is narrow sequence? "; V->dump()); +bool ARMParallelDSP::IsNarrowSequence(Value *V, ValueList &VL) { ConstantInt *CInt; if (match(V, m_ConstantInt(CInt))) { @@ -236,7 +341,7 @@ static bool IsNarrowSequence(Value *V, ValueList &VL) { auto *I = dyn_cast<Instruction>(V); if (!I) - return false; + return false; Value *Val, *LHS, *RHS; if (match(V, m_Trunc(m_Value(Val)))) { @@ -245,108 +350,253 @@ static bool IsNarrowSequence(Value *V, ValueList &VL) { } else if (match(V, m_Add(m_Value(LHS), m_Value(RHS)))) { // TODO: we need to implement sadd16/sadd8 for this, which enables to // also do the rewrite for smlad8.ll, but it is unsupported for now. - LLVM_DEBUG(dbgs() << "No, unsupported Op:\t"; I->dump()); return false; } else if (match(V, m_ZExtOrSExt(m_Value(Val)))) { - if (cast<CastInst>(I)->getSrcTy()->getIntegerBitWidth() != MaxBitWidth) { - LLVM_DEBUG(dbgs() << "No, wrong SrcTy size: " << - cast<CastInst>(I)->getSrcTy()->getIntegerBitWidth() << "\n"); + if (cast<CastInst>(I)->getSrcTy()->getIntegerBitWidth() != MaxBitWidth) return false; - } if (match(Val, m_Load(m_Value()))) { - LLVM_DEBUG(dbgs() << "Yes, found narrow Load:\t"; Val->dump()); + auto *Ld = cast<LoadInst>(Val); + + // Check that these load could be paired. + if (!LoadPairs.count(Ld) && !OffsetLoads.count(Ld)) + return false; + VL.push_back(Val); VL.push_back(I); return true; } } - LLVM_DEBUG(dbgs() << "No, unsupported Op:\t"; I->dump()); return false; } -template<typename MemInst> -static bool AreSequentialAccesses(MemInst *MemOp0, MemInst *MemOp1, - const DataLayout &DL, ScalarEvolution &SE) { - if (!MemOp0->isSimple() || !MemOp1->isSimple()) { - LLVM_DEBUG(dbgs() << "No, not touching volatile access\n"); - return false; - } - if (isConsecutiveAccess(MemOp0, MemOp1, DL, SE)) { - LLVM_DEBUG(dbgs() << "OK: accesses are consecutive.\n"); - return true; +/// Iterate through the block and record base, offset pairs of loads which can +/// be widened into a single load. +bool ARMParallelDSP::RecordMemoryOps(BasicBlock *BB) { + SmallVector<LoadInst*, 8> Loads; + SmallVector<Instruction*, 8> Writes; + + // Collect loads and instruction that may write to memory. For now we only + // record loads which are simple, sign-extended and have a single user. + // TODO: Allow zero-extended loads. + for (auto &I : *BB) { + if (I.mayWriteToMemory()) + Writes.push_back(&I); + auto *Ld = dyn_cast<LoadInst>(&I); + if (!Ld || !Ld->isSimple() || + !Ld->hasOneUse() || !isa<SExtInst>(Ld->user_back())) + continue; + Loads.push_back(Ld); } - LLVM_DEBUG(dbgs() << "No, accesses aren't consecutive.\n"); - return false; -} -bool ARMParallelDSP::AreSequentialLoads(LoadInst *Ld0, LoadInst *Ld1, - MemInstList &VecMem) { - if (!Ld0 || !Ld1) - return false; + using InstSet = std::set<Instruction*>; + using DepMap = std::map<Instruction*, InstSet>; + DepMap RAWDeps; - LLVM_DEBUG(dbgs() << "Are consecutive loads:\n"; - dbgs() << "Ld0:"; Ld0->dump(); - dbgs() << "Ld1:"; Ld1->dump(); - ); + // Record any writes that may alias a load. + const auto Size = LocationSize::unknown(); + for (auto Read : Loads) { + for (auto Write : Writes) { + MemoryLocation ReadLoc = + MemoryLocation(Read->getPointerOperand(), Size); - if (!Ld0->hasOneUse() || !Ld1->hasOneUse()) { - LLVM_DEBUG(dbgs() << "No, load has more than one use.\n"); - return false; + if (!isModOrRefSet(intersectModRef(AA->getModRefInfo(Write, ReadLoc), + ModRefInfo::ModRef))) + continue; + if (DT->dominates(Write, Read)) + RAWDeps[Read].insert(Write); + } } - if (!LoadPairs.count(Ld0) || LoadPairs[Ld0] != Ld1) - return false; + // Check whether there's not a write between the two loads which would + // prevent them from being safely merged. + auto SafeToPair = [&](LoadInst *Base, LoadInst *Offset) { + LoadInst *Dominator = DT->dominates(Base, Offset) ? Base : Offset; + LoadInst *Dominated = DT->dominates(Base, Offset) ? Offset : Base; - VecMem.clear(); - VecMem.push_back(Ld0); - VecMem.push_back(Ld1); - return true; -} + if (RAWDeps.count(Dominated)) { + InstSet &WritesBefore = RAWDeps[Dominated]; -/// Iterate through the block and record base, offset pairs of loads as well as -/// maximal sequences of sequential loads. -bool ARMParallelDSP::RecordSequentialLoads(BasicBlock *Header) { - SmallVector<LoadInst*, 8> Loads; - for (auto &I : *Header) { - auto *Ld = dyn_cast<LoadInst>(&I); - if (!Ld) - continue; - Loads.push_back(Ld); - } + for (auto Before : WritesBefore) { - std::map<LoadInst*, LoadInst*> BaseLoads; + // We can't move the second load backward, past a write, to merge + // with the first load. + if (DT->dominates(Dominator, Before)) + return false; + } + } + return true; + }; - for (auto *Ld0 : Loads) { - for (auto *Ld1 : Loads) { - if (Ld0 == Ld1) + // Record base, offset load pairs. + for (auto *Base : Loads) { + for (auto *Offset : Loads) { + if (Base == Offset) continue; - if (AreSequentialAccesses<LoadInst>(Ld0, Ld1, *DL, *SE)) { - LoadPairs[Ld0] = Ld1; - if (BaseLoads.count(Ld0)) { - LoadInst *Base = BaseLoads[Ld0]; - BaseLoads[Ld1] = Base; - SequentialLoads[Base].push_back(Ld1); - } else { - BaseLoads[Ld1] = Ld0; - SequentialLoads[Ld0].push_back(Ld1); - } + if (AreSequentialAccesses<LoadInst>(Base, Offset, *DL, *SE) && + SafeToPair(Base, Offset)) { + LoadPairs[Base] = Offset; + OffsetLoads.insert(Offset); + break; } } } + + LLVM_DEBUG(if (!LoadPairs.empty()) { + dbgs() << "Consecutive load pairs:\n"; + for (auto &MapIt : LoadPairs) { + LLVM_DEBUG(dbgs() << *MapIt.first << ", " + << *MapIt.second << "\n"); + } + }); return LoadPairs.size() > 1; } -void ARMParallelDSP::CreateParallelMACPairs(Reduction &R) { - OpChainList &Candidates = R.MACCandidates; - PMACPairList &PMACPairs = R.PMACPairs; - const unsigned Elems = Candidates.size(); +// Loop Pass that needs to identify integer add/sub reductions of 16-bit vector +// multiplications. +// To use SMLAD: +// 1) we first need to find integer add then look for this pattern: +// +// acc0 = ... +// ld0 = load i16 +// sext0 = sext i16 %ld0 to i32 +// ld1 = load i16 +// sext1 = sext i16 %ld1 to i32 +// mul0 = mul %sext0, %sext1 +// ld2 = load i16 +// sext2 = sext i16 %ld2 to i32 +// ld3 = load i16 +// sext3 = sext i16 %ld3 to i32 +// mul1 = mul i32 %sext2, %sext3 +// add0 = add i32 %mul0, %acc0 +// acc1 = add i32 %add0, %mul1 +// +// Which can be selected to: +// +// ldr r0 +// ldr r1 +// smlad r2, r0, r1, r2 +// +// If constants are used instead of loads, these will need to be hoisted +// out and into a register. +// +// If loop invariants are used instead of loads, these need to be packed +// before the loop begins. +// +bool ARMParallelDSP::MatchSMLAD(Loop *L) { + // Search recursively back through the operands to find a tree of values that + // form a multiply-accumulate chain. The search records the Add and Mul + // instructions that form the reduction and allows us to find a single value + // to be used as the initial input to the accumlator. + std::function<bool(Value*, Reduction&)> Search = [&] + (Value *V, Reduction &R) -> bool { - if (Elems < 2) - return; + // If we find a non-instruction, try to use it as the initial accumulator + // value. This may have already been found during the search in which case + // this function will return false, signaling a search fail. + auto *I = dyn_cast<Instruction>(V); + if (!I) + return R.InsertAcc(V); - auto CanPair = [&](BinOpChain *PMul0, BinOpChain *PMul1) { + switch (I->getOpcode()) { + default: + break; + case Instruction::PHI: + // Could be the accumulator value. + return R.InsertAcc(V); + case Instruction::Add: { + // Adds should be adding together two muls, or another add and a mul to + // be within the mac chain. One of the operands may also be the + // accumulator value at which point we should stop searching. + bool ValidLHS = Search(I->getOperand(0), R); + bool ValidRHS = Search(I->getOperand(1), R); + if (!ValidLHS && !ValidLHS) + return false; + else if (ValidLHS && ValidRHS) { + R.InsertAdd(I); + return true; + } else { + R.InsertAdd(I); + return R.InsertAcc(I); + } + } + case Instruction::Mul: { + Value *MulOp0 = I->getOperand(0); + Value *MulOp1 = I->getOperand(1); + if (isa<SExtInst>(MulOp0) && isa<SExtInst>(MulOp1)) { + ValueList LHS; + ValueList RHS; + if (IsNarrowSequence<16>(MulOp0, LHS) && + IsNarrowSequence<16>(MulOp1, RHS)) { + R.InsertMul(I, LHS, RHS); + return true; + } + } + return false; + } + case Instruction::SExt: + return Search(I->getOperand(0), R); + } + return false; + }; + + bool Changed = false; + SmallPtrSet<Instruction*, 4> AllAdds; + BasicBlock *Latch = L->getLoopLatch(); + + for (Instruction &I : reverse(*Latch)) { + if (I.getOpcode() != Instruction::Add) + continue; + + if (AllAdds.count(&I)) + continue; + + const auto *Ty = I.getType(); + if (!Ty->isIntegerTy(32) && !Ty->isIntegerTy(64)) + continue; + + Reduction R(&I); + if (!Search(&I, R)) + continue; + + if (!CreateParallelPairs(R)) + continue; + + InsertParallelMACs(R); + Changed = true; + AllAdds.insert(R.getAdds().begin(), R.getAdds().end()); + } + + return Changed; +} + +bool ARMParallelDSP::CreateParallelPairs(Reduction &R) { + + // Not enough mul operations to make a pair. + if (R.getMuls().size() < 2) + return false; + + // Check that the muls operate directly upon sign extended loads. + for (auto &MulChain : R.getMuls()) { + // A mul has 2 operands, and a narrow op consist of sext and a load; thus + // we expect at least 4 items in this operand value list. + if (MulChain->size() < 4) { + LLVM_DEBUG(dbgs() << "Operand list too short.\n"); + return false; + } + MulChain->PopulateLoads(); + ValueList &LHS = static_cast<BinOpChain*>(MulChain.get())->LHS; + ValueList &RHS = static_cast<BinOpChain*>(MulChain.get())->RHS; + + // Use +=2 to skip over the expected extend instructions. + for (unsigned i = 0, e = LHS.size(); i < e; i += 2) { + if (!isa<LoadInst>(LHS[i]) || !isa<LoadInst>(RHS[i])) + return false; + } + } + + auto CanPair = [&](Reduction &R, BinOpChain *PMul0, BinOpChain *PMul1) { if (!PMul0->AreSymmetrical(PMul1)) return false; @@ -363,23 +613,22 @@ void ARMParallelDSP::CreateParallelMACPairs(Reduction &R) { if (!Ld0 || !Ld1 || !Ld2 || !Ld3) return false; - LLVM_DEBUG(dbgs() << "Looking at operands " << x << ":\n" - << "\t Ld0: " << *Ld0 << "\n" - << "\t Ld1: " << *Ld1 << "\n" - << "and operands " << x + 2 << ":\n" - << "\t Ld2: " << *Ld2 << "\n" - << "\t Ld3: " << *Ld3 << "\n"); + LLVM_DEBUG(dbgs() << "Loads:\n" + << " - " << *Ld0 << "\n" + << " - " << *Ld1 << "\n" + << " - " << *Ld2 << "\n" + << " - " << *Ld3 << "\n"); if (AreSequentialLoads(Ld0, Ld1, PMul0->VecLd)) { if (AreSequentialLoads(Ld2, Ld3, PMul1->VecLd)) { LLVM_DEBUG(dbgs() << "OK: found two pairs of parallel loads!\n"); - PMACPairs.push_back(std::make_pair(PMul0, PMul1)); + R.AddMulPair(PMul0, PMul1); return true; } else if (AreSequentialLoads(Ld3, Ld2, PMul1->VecLd)) { LLVM_DEBUG(dbgs() << "OK: found two pairs of parallel loads!\n"); LLVM_DEBUG(dbgs() << " exchanging Ld2 and Ld3\n"); PMul1->Exchange = true; - PMACPairs.push_back(std::make_pair(PMul0, PMul1)); + R.AddMulPair(PMul0, PMul1); return true; } } else if (AreSequentialLoads(Ld1, Ld0, PMul0->VecLd) && @@ -389,16 +638,18 @@ void ARMParallelDSP::CreateParallelMACPairs(Reduction &R) { LLVM_DEBUG(dbgs() << " and swapping muls\n"); PMul0->Exchange = true; // Only the second operand can be exchanged, so swap the muls. - PMACPairs.push_back(std::make_pair(PMul1, PMul0)); + R.AddMulPair(PMul1, PMul0); return true; } } return false; }; + OpChainList &Muls = R.getMuls(); + const unsigned Elems = Muls.size(); SmallPtrSet<const Instruction*, 4> Paired; for (unsigned i = 0; i < Elems; ++i) { - BinOpChain *PMul0 = static_cast<BinOpChain*>(Candidates[i].get()); + BinOpChain *PMul0 = static_cast<BinOpChain*>(Muls[i].get()); if (Paired.count(PMul0->Root)) continue; @@ -406,7 +657,7 @@ void ARMParallelDSP::CreateParallelMACPairs(Reduction &R) { if (i == j) continue; - BinOpChain *PMul1 = static_cast<BinOpChain*>(Candidates[j].get()); + BinOpChain *PMul1 = static_cast<BinOpChain*>(Muls[j].get()); if (Paired.count(PMul1->Root)) continue; @@ -417,315 +668,133 @@ void ARMParallelDSP::CreateParallelMACPairs(Reduction &R) { assert(PMul0 != PMul1 && "expected different chains"); - LLVM_DEBUG(dbgs() << "\nCheck parallel muls:\n"; - dbgs() << "- "; Mul0->dump(); - dbgs() << "- "; Mul1->dump()); - - LLVM_DEBUG(dbgs() << "OK: mul operands list match:\n"); - if (CanPair(PMul0, PMul1)) { + if (CanPair(R, PMul0, PMul1)) { Paired.insert(Mul0); Paired.insert(Mul1); break; } } } + return !R.getMulPairs().empty(); } -bool ARMParallelDSP::InsertParallelMACs(Reduction &Reduction) { - Instruction *Acc = Reduction.Phi; - Instruction *InsertAfter = Reduction.AccIntAdd; - - for (auto &Pair : Reduction.PMACPairs) { - BinOpChain *PMul0 = Pair.first; - BinOpChain *PMul1 = Pair.second; - LLVM_DEBUG(dbgs() << "Found parallel MACs!!\n"; - dbgs() << "- "; PMul0->Root->dump(); - dbgs() << "- "; PMul1->Root->dump()); - - auto *VecLd0 = cast<LoadInst>(PMul0->VecLd[0]); - auto *VecLd1 = cast<LoadInst>(PMul1->VecLd[0]); - Acc = CreateSMLADCall(VecLd0, VecLd1, Acc, PMul1->Exchange, InsertAfter); - InsertAfter = Acc; - } - - if (Acc != Reduction.Phi) { - LLVM_DEBUG(dbgs() << "Replace Accumulate: "; Acc->dump()); - Reduction.AccIntAdd->replaceAllUsesWith(Acc); - return true; - } - return false; -} - -static void MatchReductions(Function &F, Loop *TheLoop, BasicBlock *Header, - ReductionList &Reductions) { - RecurrenceDescriptor RecDesc; - const bool HasFnNoNaNAttr = - F.getFnAttribute("no-nans-fp-math").getValueAsString() == "true"; - const BasicBlock *Latch = TheLoop->getLoopLatch(); - - // We need a preheader as getIncomingValueForBlock assumes there is one. - if (!TheLoop->getLoopPreheader()) { - LLVM_DEBUG(dbgs() << "No preheader found, bailing out\n"); - return; - } - - for (PHINode &Phi : Header->phis()) { - const auto *Ty = Phi.getType(); - if (!Ty->isIntegerTy(32) && !Ty->isIntegerTy(64)) - continue; - - const bool IsReduction = - RecurrenceDescriptor::AddReductionVar(&Phi, - RecurrenceDescriptor::RK_IntegerAdd, - TheLoop, HasFnNoNaNAttr, RecDesc); - if (!IsReduction) - continue; - - Instruction *Acc = dyn_cast<Instruction>(Phi.getIncomingValueForBlock(Latch)); - if (!Acc) - continue; - - Reductions.push_back(Reduction(&Phi, Acc)); - } - - LLVM_DEBUG( - dbgs() << "\nAccumulating integer additions (reductions) found:\n"; - for (auto &R : Reductions) { - dbgs() << "- "; R.Phi->dump(); - dbgs() << "-> "; R.AccIntAdd->dump(); - } - ); -} - -static void AddMACCandidate(OpChainList &Candidates, - Instruction *Mul, - Value *MulOp0, Value *MulOp1) { - LLVM_DEBUG(dbgs() << "OK, found acc mul:\t"; Mul->dump()); - assert(Mul->getOpcode() == Instruction::Mul && - "expected mul instruction"); - ValueList LHS; - ValueList RHS; - if (IsNarrowSequence<16>(MulOp0, LHS) && - IsNarrowSequence<16>(MulOp1, RHS)) { - LLVM_DEBUG(dbgs() << "OK, found narrow mul: "; Mul->dump()); - Candidates.push_back(make_unique<BinOpChain>(Mul, LHS, RHS)); - } -} -static void MatchParallelMACSequences(Reduction &R, - OpChainList &Candidates) { - Instruction *Acc = R.AccIntAdd; - LLVM_DEBUG(dbgs() << "\n- Analysing:\t" << *Acc); +void ARMParallelDSP::InsertParallelMACs(Reduction &R) { - // Returns false to signal the search should be stopped. - std::function<bool(Value*)> Match = - [&Candidates, &Match](Value *V) -> bool { + auto CreateSMLADCall = [&](SmallVectorImpl<LoadInst*> &VecLd0, + SmallVectorImpl<LoadInst*> &VecLd1, + Value *Acc, bool Exchange, + Instruction *InsertAfter) { + // Replace the reduction chain with an intrinsic call + IntegerType *Ty = IntegerType::get(M->getContext(), 32); + LoadInst *WideLd0 = WideLoads.count(VecLd0[0]) ? + WideLoads[VecLd0[0]]->getLoad() : CreateWideLoad(VecLd0, Ty); + LoadInst *WideLd1 = WideLoads.count(VecLd1[0]) ? + WideLoads[VecLd1[0]]->getLoad() : CreateWideLoad(VecLd1, Ty); - auto *I = dyn_cast<Instruction>(V); - if (!I) - return false; + Value* Args[] = { WideLd0, WideLd1, Acc }; + Function *SMLAD = nullptr; + if (Exchange) + SMLAD = Acc->getType()->isIntegerTy(32) ? + Intrinsic::getDeclaration(M, Intrinsic::arm_smladx) : + Intrinsic::getDeclaration(M, Intrinsic::arm_smlaldx); + else + SMLAD = Acc->getType()->isIntegerTy(32) ? + Intrinsic::getDeclaration(M, Intrinsic::arm_smlad) : + Intrinsic::getDeclaration(M, Intrinsic::arm_smlald); - switch (I->getOpcode()) { - case Instruction::Add: - if (Match(I->getOperand(0)) || (Match(I->getOperand(1)))) - return true; - break; - case Instruction::Mul: { - Value *MulOp0 = I->getOperand(0); - Value *MulOp1 = I->getOperand(1); - if (isa<SExtInst>(MulOp0) && isa<SExtInst>(MulOp1)) - AddMACCandidate(Candidates, I, MulOp0, MulOp1); - return false; - } - case Instruction::SExt: - return Match(I->getOperand(0)); - } - return false; + IRBuilder<NoFolder> Builder(InsertAfter->getParent(), + ++BasicBlock::iterator(InsertAfter)); + Instruction *Call = Builder.CreateCall(SMLAD, Args); + NumSMLAD++; + return Call; }; - while (Match (Acc)); - LLVM_DEBUG(dbgs() << "Finished matching MAC sequences, found " - << Candidates.size() << " candidates.\n"); -} - -// Collects all instructions that are not part of the MAC chains, which is the -// set of instructions that can potentially alias with the MAC operands. -static void AliasCandidates(BasicBlock *Header, Instructions &Reads, - Instructions &Writes) { - for (auto &I : *Header) { - if (I.mayReadFromMemory()) - Reads.push_back(&I); - if (I.mayWriteToMemory()) - Writes.push_back(&I); - } -} - -// Check whether statements in the basic block that write to memory alias with -// the memory locations accessed by the MAC-chains. -// TODO: we need the read statements when we accept more complicated chains. -static bool AreAliased(AliasAnalysis *AA, Instructions &Reads, - Instructions &Writes, OpChainList &MACCandidates) { - LLVM_DEBUG(dbgs() << "Alias checks:\n"); - for (auto &MAC : MACCandidates) { - LLVM_DEBUG(dbgs() << "mul: "; MAC->Root->dump()); - - // At the moment, we allow only simple chains that only consist of reads, - // accumulate their result with an integer add, and thus that don't write - // memory, and simply bail if they do. - if (!MAC->ReadOnly) - return true; - - // Now for all writes in the basic block, check that they don't alias with - // the memory locations accessed by our MAC-chain: - for (auto *I : Writes) { - LLVM_DEBUG(dbgs() << "- "; I->dump()); - assert(MAC->MemLocs.size() >= 2 && "expecting at least 2 memlocs"); - for (auto &MemLoc : MAC->MemLocs) { - if (isModOrRefSet(intersectModRef(AA->getModRefInfo(I, MemLoc), - ModRefInfo::ModRef))) { - LLVM_DEBUG(dbgs() << "Yes, aliases found\n"); - return true; - } - } - } - } - - LLVM_DEBUG(dbgs() << "OK: no aliases found!\n"); - return false; -} + Instruction *InsertAfter = R.getRoot(); + Value *Acc = R.getAccumulator(); + if (!Acc) + Acc = ConstantInt::get(IntegerType::get(M->getContext(), 32), 0); -static bool CheckMACMemory(OpChainList &Candidates) { - for (auto &C : Candidates) { - // A mul has 2 operands, and a narrow op consist of sext and a load; thus - // we expect at least 4 items in this operand value list. - if (C->size() < 4) { - LLVM_DEBUG(dbgs() << "Operand list too short.\n"); - return false; - } - C->SetMemoryLocations(); - ValueList &LHS = static_cast<BinOpChain*>(C.get())->LHS; - ValueList &RHS = static_cast<BinOpChain*>(C.get())->RHS; + LLVM_DEBUG(dbgs() << "Root: " << *InsertAfter << "\n" + << "Acc: " << *Acc << "\n"); + for (auto &Pair : R.getMulPairs()) { + BinOpChain *PMul0 = Pair.first; + BinOpChain *PMul1 = Pair.second; + LLVM_DEBUG(dbgs() << "Muls:\n" + << "- " << *PMul0->Root << "\n" + << "- " << *PMul1->Root << "\n"); - // Use +=2 to skip over the expected extend instructions. - for (unsigned i = 0, e = LHS.size(); i < e; i += 2) { - if (!isa<LoadInst>(LHS[i]) || !isa<LoadInst>(RHS[i])) - return false; - } + Acc = CreateSMLADCall(PMul0->VecLd, PMul1->VecLd, Acc, PMul1->Exchange, + InsertAfter); + InsertAfter = cast<Instruction>(Acc); } - return true; + R.UpdateRoot(cast<Instruction>(Acc)); } -// Loop Pass that needs to identify integer add/sub reductions of 16-bit vector -// multiplications. -// To use SMLAD: -// 1) we first need to find integer add reduction PHIs, -// 2) then from the PHI, look for this pattern: -// -// acc0 = phi i32 [0, %entry], [%acc1, %loop.body] -// ld0 = load i16 -// sext0 = sext i16 %ld0 to i32 -// ld1 = load i16 -// sext1 = sext i16 %ld1 to i32 -// mul0 = mul %sext0, %sext1 -// ld2 = load i16 -// sext2 = sext i16 %ld2 to i32 -// ld3 = load i16 -// sext3 = sext i16 %ld3 to i32 -// mul1 = mul i32 %sext2, %sext3 -// add0 = add i32 %mul0, %acc0 -// acc1 = add i32 %add0, %mul1 -// -// Which can be selected to: -// -// ldr.h r0 -// ldr.h r1 -// smlad r2, r0, r1, r2 -// -// If constants are used instead of loads, these will need to be hoisted -// out and into a register. -// -// If loop invariants are used instead of loads, these need to be packed -// before the loop begins. -// -bool ARMParallelDSP::MatchSMLAD(Function &F) { - BasicBlock *Header = L->getHeader(); - LLVM_DEBUG(dbgs() << "= Matching SMLAD =\n"; - dbgs() << "Header block:\n"; Header->dump(); - dbgs() << "Loop info:\n\n"; L->dump()); +LoadInst* ARMParallelDSP::CreateWideLoad(SmallVectorImpl<LoadInst*> &Loads, + IntegerType *LoadTy) { + assert(Loads.size() == 2 && "currently only support widening two loads"); - bool Changed = false; - ReductionList Reductions; - MatchReductions(F, L, Header, Reductions); + LoadInst *Base = Loads[0]; + LoadInst *Offset = Loads[1]; - for (auto &R : Reductions) { - OpChainList MACCandidates; - MatchParallelMACSequences(R, MACCandidates); - if (!CheckMACMemory(MACCandidates)) - continue; + Instruction *BaseSExt = dyn_cast<SExtInst>(Base->user_back()); + Instruction *OffsetSExt = dyn_cast<SExtInst>(Offset->user_back()); - R.MACCandidates = std::move(MACCandidates); + assert((BaseSExt && OffsetSExt) + && "Loads should have a single, extending, user"); - LLVM_DEBUG(dbgs() << "MAC candidates:\n"; - for (auto &M : R.MACCandidates) - M->Root->dump(); - dbgs() << "\n";); - } + std::function<void(Value*, Value*)> MoveBefore = + [&](Value *A, Value *B) -> void { + if (!isa<Instruction>(A) || !isa<Instruction>(B)) + return; - // Collect all instructions that may read or write memory. Our alias - // analysis checks bail out if any of these instructions aliases with an - // instruction from the MAC-chain. - Instructions Reads, Writes; - AliasCandidates(Header, Reads, Writes); + auto *Source = cast<Instruction>(A); + auto *Sink = cast<Instruction>(B); - for (auto &R : Reductions) { - if (AreAliased(AA, Reads, Writes, R.MACCandidates)) - return false; - CreateParallelMACPairs(R); - Changed |= InsertParallelMACs(R); - } + if (DT->dominates(Source, Sink) || + Source->getParent() != Sink->getParent() || + isa<PHINode>(Source) || isa<PHINode>(Sink)) + return; - LLVM_DEBUG(if (Changed) dbgs() << "Header block:\n"; Header->dump();); - return Changed; -} + Source->moveBefore(Sink); + for (auto &U : Source->uses()) + MoveBefore(Source, U.getUser()); + }; -static LoadInst *CreateLoadIns(IRBuilder<NoFolder> &IRB, LoadInst &BaseLoad, - const Type *LoadTy) { - const unsigned AddrSpace = BaseLoad.getPointerAddressSpace(); + // Insert the load at the point of the original dominating load. + LoadInst *DomLoad = DT->dominates(Base, Offset) ? Base : Offset; + IRBuilder<NoFolder> IRB(DomLoad->getParent(), + ++BasicBlock::iterator(DomLoad)); - Value *VecPtr = IRB.CreateBitCast(BaseLoad.getPointerOperand(), + // Bitcast the pointer to a wider type and create the wide load, while making + // sure to maintain the original alignment as this prevents ldrd from being + // generated when it could be illegal due to memory alignment. + const unsigned AddrSpace = DomLoad->getPointerAddressSpace(); + Value *VecPtr = IRB.CreateBitCast(Base->getPointerOperand(), LoadTy->getPointerTo(AddrSpace)); - return IRB.CreateAlignedLoad(VecPtr, BaseLoad.getAlignment()); -} + LoadInst *WideLoad = IRB.CreateAlignedLoad(LoadTy, VecPtr, + Base->getAlignment()); + + // Make sure everything is in the correct order in the basic block. + MoveBefore(Base->getPointerOperand(), VecPtr); + MoveBefore(VecPtr, WideLoad); -Instruction *ARMParallelDSP::CreateSMLADCall(LoadInst *VecLd0, LoadInst *VecLd1, - Instruction *Acc, bool Exchange, - Instruction *InsertAfter) { - LLVM_DEBUG(dbgs() << "Create SMLAD intrinsic using:\n" - << "- " << *VecLd0 << "\n" - << "- " << *VecLd1 << "\n" - << "- " << *Acc << "\n" - << "Exchange: " << Exchange << "\n"); + // From the wide load, create two values that equal the original two loads. + // Loads[0] needs trunc while Loads[1] needs a lshr and trunc. + // TODO: Support big-endian as well. + Value *Bottom = IRB.CreateTrunc(WideLoad, Base->getType()); + BaseSExt->setOperand(0, Bottom); - IRBuilder<NoFolder> Builder(InsertAfter->getParent(), - ++BasicBlock::iterator(InsertAfter)); + IntegerType *OffsetTy = cast<IntegerType>(Offset->getType()); + Value *ShiftVal = ConstantInt::get(LoadTy, OffsetTy->getBitWidth()); + Value *Top = IRB.CreateLShr(WideLoad, ShiftVal); + Value *Trunc = IRB.CreateTrunc(Top, OffsetTy); + OffsetSExt->setOperand(0, Trunc); - // Replace the reduction chain with an intrinsic call - const Type *Ty = IntegerType::get(M->getContext(), 32); - LoadInst *NewLd0 = CreateLoadIns(Builder, VecLd0[0], Ty); - LoadInst *NewLd1 = CreateLoadIns(Builder, VecLd1[0], Ty); - Value* Args[] = { NewLd0, NewLd1, Acc }; - Function *SMLAD = nullptr; - if (Exchange) - SMLAD = Acc->getType()->isIntegerTy(32) ? - Intrinsic::getDeclaration(M, Intrinsic::arm_smladx) : - Intrinsic::getDeclaration(M, Intrinsic::arm_smlaldx); - else - SMLAD = Acc->getType()->isIntegerTy(32) ? - Intrinsic::getDeclaration(M, Intrinsic::arm_smlad) : - Intrinsic::getDeclaration(M, Intrinsic::arm_smlald); - CallInst *Call = Builder.CreateCall(SMLAD, Args); - NumSMLAD++; - return Call; + WideLoads.emplace(std::make_pair(Base, + make_unique<WidenedLoad>(Loads, WideLoad))); + return WideLoad; } // Compare the value lists in Other to this chain. @@ -741,7 +810,6 @@ bool BinOpChain::AreSymmetrical(BinOpChain *Other) { } const unsigned Pairs = VL0.size(); - LLVM_DEBUG(dbgs() << "Number of operand pairs: " << Pairs << "\n"); for (unsigned i = 0; i < Pairs; ++i) { const Value *V0 = VL0[i]; @@ -749,24 +817,17 @@ bool BinOpChain::AreSymmetrical(BinOpChain *Other) { const auto *Inst0 = dyn_cast<Instruction>(V0); const auto *Inst1 = dyn_cast<Instruction>(V1); - LLVM_DEBUG(dbgs() << "Pair " << i << ":\n"; - dbgs() << "mul1: "; V0->dump(); - dbgs() << "mul2: "; V1->dump()); - if (!Inst0 || !Inst1) return false; - if (Inst0->isSameOperationAs(Inst1)) { - LLVM_DEBUG(dbgs() << "OK: same operation found!\n"); + if (Inst0->isSameOperationAs(Inst1)) continue; - } const APInt *C0, *C1; if (!(match(V0, m_APInt(C0)) && match(V1, m_APInt(C1)) && C0 == C1)) return false; } - LLVM_DEBUG(dbgs() << "OK: found symmetrical operand lists.\n"); return true; }; diff --git a/lib/Target/ARM/ARMPerfectShuffle.h b/lib/Target/ARM/ARMPerfectShuffle.h index 3ff0bee7e5bf..d519490c9c57 100644 --- a/lib/Target/ARM/ARMPerfectShuffle.h +++ b/lib/Target/ARM/ARMPerfectShuffle.h @@ -1,9 +1,8 @@ //===-- ARMPerfectShuffle.h - NEON Perfect Shuffle Table --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMPredicates.td b/lib/Target/ARM/ARMPredicates.td new file mode 100644 index 000000000000..0b6b40de80dd --- /dev/null +++ b/lib/Target/ARM/ARMPredicates.td @@ -0,0 +1,211 @@ +//===-- ARMPredicates.td - ARM Instruction Predicates ------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +def HasV4T : Predicate<"Subtarget->hasV4TOps()">, + AssemblerPredicate<"HasV4TOps", "armv4t">; +def NoV4T : Predicate<"!Subtarget->hasV4TOps()">; +def HasV5T : Predicate<"Subtarget->hasV5TOps()">, + AssemblerPredicate<"HasV5TOps", "armv5t">; +def NoV5T : Predicate<"!Subtarget->hasV5TOps()">; +def HasV5TE : Predicate<"Subtarget->hasV5TEOps()">, + AssemblerPredicate<"HasV5TEOps", "armv5te">; +def HasV6 : Predicate<"Subtarget->hasV6Ops()">, + AssemblerPredicate<"HasV6Ops", "armv6">; +def NoV6 : Predicate<"!Subtarget->hasV6Ops()">; +def HasV6M : Predicate<"Subtarget->hasV6MOps()">, + AssemblerPredicate<"HasV6MOps", + "armv6m or armv6t2">; +def HasV8MBaseline : Predicate<"Subtarget->hasV8MBaselineOps()">, + AssemblerPredicate<"HasV8MBaselineOps", + "armv8m.base">; +def HasV8MMainline : Predicate<"Subtarget->hasV8MMainlineOps()">, + AssemblerPredicate<"HasV8MMainlineOps", + "armv8m.main">; +def HasV8_1MMainline : Predicate<"Subtarget->hasV8_1MMainlineOps()">, + AssemblerPredicate<"HasV8_1MMainlineOps", + "armv8.1m.main">; +def HasMVEInt : Predicate<"Subtarget->hasMVEIntegerOps()">, + AssemblerPredicate<"HasMVEIntegerOps", + "mve">; +def HasMVEFloat : Predicate<"Subtarget->hasMVEFloatOps()">, + AssemblerPredicate<"HasMVEFloatOps", + "mve.fp">; +def HasFPRegs : Predicate<"Subtarget->hasFPRegs()">, + AssemblerPredicate<"FeatureFPRegs", + "fp registers">; +def HasFPRegs16 : Predicate<"Subtarget->hasFPRegs16()">, + AssemblerPredicate<"FeatureFPRegs16", + "16-bit fp registers">; +def HasFPRegs64 : Predicate<"Subtarget->hasFPRegs64()">, + AssemblerPredicate<"FeatureFPRegs64", + "64-bit fp registers">; +def HasFPRegsV8_1M : Predicate<"Subtarget->hasFPRegs() && Subtarget->hasV8_1MMainlineOps()">, + AssemblerPredicate<"FeatureFPRegs,HasV8_1MMainlineOps", + "armv8.1m.main with FP or MVE">; +def HasV6T2 : Predicate<"Subtarget->hasV6T2Ops()">, + AssemblerPredicate<"HasV6T2Ops", "armv6t2">; +def NoV6T2 : Predicate<"!Subtarget->hasV6T2Ops()">; +def HasV6K : Predicate<"Subtarget->hasV6KOps()">, + AssemblerPredicate<"HasV6KOps", "armv6k">; +def NoV6K : Predicate<"!Subtarget->hasV6KOps()">; +def HasV7 : Predicate<"Subtarget->hasV7Ops()">, + AssemblerPredicate<"HasV7Ops", "armv7">; +def HasV8 : Predicate<"Subtarget->hasV8Ops()">, + AssemblerPredicate<"HasV8Ops", "armv8">; +def PreV8 : Predicate<"!Subtarget->hasV8Ops()">, + AssemblerPredicate<"!HasV8Ops", "armv7 or earlier">; +def HasV8_1a : Predicate<"Subtarget->hasV8_1aOps()">, + AssemblerPredicate<"HasV8_1aOps", "armv8.1a">; +def HasV8_2a : Predicate<"Subtarget->hasV8_2aOps()">, + AssemblerPredicate<"HasV8_2aOps", "armv8.2a">; +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 NoVFP : Predicate<"!Subtarget->hasVFP2Base()">; +def HasVFP2 : Predicate<"Subtarget->hasVFP2Base()">, + AssemblerPredicate<"FeatureVFP2_D16_SP", "VFP2">; +def HasVFP3 : Predicate<"Subtarget->hasVFP3Base()">, + AssemblerPredicate<"FeatureVFP3_D16_SP", "VFP3">; +def HasVFP4 : Predicate<"Subtarget->hasVFP4Base()">, + AssemblerPredicate<"FeatureVFP4_D16_SP", "VFP4">; +def HasDPVFP : Predicate<"Subtarget->hasFP64()">, + AssemblerPredicate<"FeatureFP64", + "double precision VFP">; +def HasFPARMv8 : Predicate<"Subtarget->hasFPARMv8Base()">, + AssemblerPredicate<"FeatureFPARMv8_D16_SP", "FPARMv8">; +def HasNEON : Predicate<"Subtarget->hasNEON()">, + AssemblerPredicate<"FeatureNEON", "NEON">; +def HasSHA2 : Predicate<"Subtarget->hasSHA2()">, + AssemblerPredicate<"FeatureSHA2", "sha2">; +def HasAES : Predicate<"Subtarget->hasAES()">, + AssemblerPredicate<"FeatureAES", "aes">; +def HasCrypto : Predicate<"Subtarget->hasCrypto()">, + AssemblerPredicate<"FeatureCrypto", "crypto">; +def HasDotProd : Predicate<"Subtarget->hasDotProd()">, + AssemblerPredicate<"FeatureDotProd", "dotprod">; +def HasCRC : Predicate<"Subtarget->hasCRC()">, + AssemblerPredicate<"FeatureCRC", "crc">; +def HasRAS : Predicate<"Subtarget->hasRAS()">, + AssemblerPredicate<"FeatureRAS", "ras">; +def HasLOB : Predicate<"Subtarget->hasLOB()">, + AssemblerPredicate<"FeatureLOB", "lob">; +def HasFP16 : Predicate<"Subtarget->hasFP16()">, + AssemblerPredicate<"FeatureFP16","half-float conversions">; +def HasFullFP16 : Predicate<"Subtarget->hasFullFP16()">, + AssemblerPredicate<"FeatureFullFP16","full half-float">; +def HasFP16FML : Predicate<"Subtarget->hasFP16FML()">, + AssemblerPredicate<"FeatureFP16FML","full half-float fml">; +def HasDivideInThumb : Predicate<"Subtarget->hasDivideInThumbMode()">, + AssemblerPredicate<"FeatureHWDivThumb", "divide in THUMB">; +def HasDivideInARM : Predicate<"Subtarget->hasDivideInARMMode()">, + AssemblerPredicate<"FeatureHWDivARM", "divide in ARM">; +def HasDSP : Predicate<"Subtarget->hasDSP()">, + AssemblerPredicate<"FeatureDSP", "dsp">; +def HasDB : Predicate<"Subtarget->hasDataBarrier()">, + AssemblerPredicate<"FeatureDB", + "data-barriers">; +def HasDFB : Predicate<"Subtarget->hasFullDataBarrier()">, + AssemblerPredicate<"FeatureDFB", + "full-data-barrier">; +def HasV7Clrex : Predicate<"Subtarget->hasV7Clrex()">, + AssemblerPredicate<"FeatureV7Clrex", + "v7 clrex">; +def HasAcquireRelease : Predicate<"Subtarget->hasAcquireRelease()">, + AssemblerPredicate<"FeatureAcquireRelease", + "acquire/release">; +def HasMP : Predicate<"Subtarget->hasMPExtension()">, + AssemblerPredicate<"FeatureMP", + "mp-extensions">; +def HasVirtualization: Predicate<"false">, + AssemblerPredicate<"FeatureVirtualization", + "virtualization-extensions">; +def HasTrustZone : Predicate<"Subtarget->hasTrustZone()">, + AssemblerPredicate<"FeatureTrustZone", + "TrustZone">; +def Has8MSecExt : Predicate<"Subtarget->has8MSecExt()">, + AssemblerPredicate<"Feature8MSecExt", + "ARMv8-M Security Extensions">; +def HasZCZ : Predicate<"Subtarget->hasZeroCycleZeroing()">; +def UseNEONForFP : Predicate<"Subtarget->useNEONForSinglePrecisionFP()">; +def DontUseNEONForFP : Predicate<"!Subtarget->useNEONForSinglePrecisionFP()">; +def IsThumb : Predicate<"Subtarget->isThumb()">, + AssemblerPredicate<"ModeThumb", "thumb">; +def IsThumb1Only : Predicate<"Subtarget->isThumb1Only()">; +def IsThumb2 : Predicate<"Subtarget->isThumb2()">, + AssemblerPredicate<"ModeThumb,FeatureThumb2", + "thumb2">; +def IsMClass : Predicate<"Subtarget->isMClass()">, + AssemblerPredicate<"FeatureMClass", "armv*m">; +def IsNotMClass : Predicate<"!Subtarget->isMClass()">, + AssemblerPredicate<"!FeatureMClass", + "!armv*m">; +def IsARM : Predicate<"!Subtarget->isThumb()">, + AssemblerPredicate<"!ModeThumb", "arm-mode">; +def IsMachO : Predicate<"Subtarget->isTargetMachO()">; +def IsNotMachO : Predicate<"!Subtarget->isTargetMachO()">; +def IsNaCl : Predicate<"Subtarget->isTargetNaCl()">; +def IsWindows : Predicate<"Subtarget->isTargetWindows()">; +def IsNotWindows : Predicate<"!Subtarget->isTargetWindows()">; +def IsReadTPHard : Predicate<"Subtarget->isReadTPHard()">; +def IsReadTPSoft : Predicate<"!Subtarget->isReadTPHard()">; +def UseNaClTrap : Predicate<"Subtarget->useNaClTrap()">, + AssemblerPredicate<"FeatureNaClTrap", "NaCl">; +def DontUseNaClTrap : Predicate<"!Subtarget->useNaClTrap()">; + +def UseNegativeImmediates : + Predicate<"false">, + AssemblerPredicate<"!FeatureNoNegativeImmediates", + "NegativeImmediates">; + +// FIXME: Eventually this will be just "hasV6T2Ops". +let RecomputePerFunction = 1 in { + def UseMovt : Predicate<"Subtarget->useMovt()">; + def DontUseMovt : Predicate<"!Subtarget->useMovt()">; + def UseMovtInPic : Predicate<"Subtarget->useMovt() && Subtarget->allowPositionIndependentMovt()">; + def DontUseMovtInPic : Predicate<"!Subtarget->useMovt() || !Subtarget->allowPositionIndependentMovt()">; + + def UseFPVMLx: Predicate<"((Subtarget->useFPVMLx() &&" + " TM.Options.AllowFPOpFusion != FPOpFusion::Fast) ||" + "Subtarget->hasMinSize())">; +} +def UseMulOps : Predicate<"Subtarget->useMulOps()">; + +// Prefer fused MAC for fp mul + add over fp VMLA / VMLS if they are available. +// But only select them if more precision in FP computation is allowed, and when +// they are not slower than a mul + add sequence. +// Do not use them for Darwin platforms. +def UseFusedMAC : Predicate<"(TM.Options.AllowFPOpFusion ==" + " FPOpFusion::Fast && " + " Subtarget->hasVFP4Base()) && " + "!Subtarget->isTargetDarwin() &&" + "Subtarget->useFPVMLx()">; + +def HasFastVGETLNi32 : Predicate<"!Subtarget->hasSlowVGETLNi32()">; +def HasSlowVGETLNi32 : Predicate<"Subtarget->hasSlowVGETLNi32()">; + +def HasFastVDUP32 : Predicate<"!Subtarget->hasSlowVDUP32()">; +def HasSlowVDUP32 : Predicate<"Subtarget->hasSlowVDUP32()">; + +def UseVMOVSR : Predicate<"Subtarget->preferVMOVSR() ||" + "!Subtarget->useNEONForSinglePrecisionFP()">; +def DontUseVMOVSR : Predicate<"!Subtarget->preferVMOVSR() &&" + "Subtarget->useNEONForSinglePrecisionFP()">; + +let RecomputePerFunction = 1 in { + def IsLE : Predicate<"MF->getDataLayout().isLittleEndian()">; + def IsBE : Predicate<"MF->getDataLayout().isBigEndian()">; +} + +def GenExecuteOnly : Predicate<"Subtarget->genExecuteOnly()">; + +// Armv8.5-A extensions +def HasSB : Predicate<"Subtarget->hasSB()">, + AssemblerPredicate<"FeatureSB", "sb">; diff --git a/lib/Target/ARM/ARMRegisterBankInfo.cpp b/lib/Target/ARM/ARMRegisterBankInfo.cpp index 4f28f2dafc70..b100150175fc 100644 --- a/lib/Target/ARM/ARMRegisterBankInfo.cpp +++ b/lib/Target/ARM/ARMRegisterBankInfo.cpp @@ -1,9 +1,8 @@ //===- ARMRegisterBankInfo.cpp -----------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file @@ -161,6 +160,10 @@ ARMRegisterBankInfo::ARMRegisterBankInfo(const TargetRegisterInfo &TRI) "Subclass not added?"); assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPR_and_tcGPRRegClassID)) && "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPREven_and_tGPR_and_tcGPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPROdd_and_tcGPRRegClassID)) && + "Subclass not added?"); assert(RBGPR.getSize() == 32 && "GPRs should hold up to 32-bit"); #ifndef NDEBUG @@ -182,6 +185,13 @@ const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass( case tGPR_and_tcGPRRegClassID: case tcGPRRegClassID: case tGPRRegClassID: + case tGPREvenRegClassID: + case tGPROddRegClassID: + case tGPR_and_tGPREvenRegClassID: + case tGPR_and_tGPROddRegClassID: + case tGPREven_and_tcGPRRegClassID: + case tGPREven_and_tGPR_and_tcGPRRegClassID: + case tGPROdd_and_tcGPRRegClassID: return getRegBank(ARM::GPRRegBankID); case HPRRegClassID: case SPR_8RegClassID: @@ -218,7 +228,15 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { switch (Opc) { case G_ADD: - case G_SUB: + case G_SUB: { + // Integer operations where the source and destination are in the + // same register class. + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + OperandsMapping = Ty.getSizeInBits() == 64 + ? &ARM::ValueMappings[ARM::DPR3OpsIdx] + : &ARM::ValueMappings[ARM::GPR3OpsIdx]; + break; + } case G_MUL: case G_AND: case G_OR: @@ -337,6 +355,14 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { &ARM::ValueMappings[ARM::GPR3OpsIdx]}); break; } + case G_FCONSTANT: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + OperandsMapping = getOperandsMapping( + {Ty.getSizeInBits() == 64 ? &ARM::ValueMappings[ARM::DPR3OpsIdx] + : &ARM::ValueMappings[ARM::SPR3OpsIdx], + nullptr}); + break; + } case G_CONSTANT: case G_FRAME_INDEX: case G_GLOBAL_VALUE: @@ -424,6 +450,19 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { OperandsMapping = getOperandsMapping({&ARM::ValueMappings[ARM::GPR3OpsIdx], nullptr}); break; + case DBG_VALUE: { + SmallVector<const ValueMapping *, 4> OperandBanks(NumOperands); + const MachineOperand &MaybeReg = MI.getOperand(0); + if (MaybeReg.isReg() && MaybeReg.getReg()) { + unsigned Size = MRI.getType(MaybeReg.getReg()).getSizeInBits(); + if (Size > 32 && Size != 64) + return getInvalidInstructionMapping(); + OperandBanks[0] = Size == 64 ? &ARM::ValueMappings[ARM::DPR3OpsIdx] + : &ARM::ValueMappings[ARM::GPR3OpsIdx]; + } + OperandsMapping = getOperandsMapping(OperandBanks); + break; + } default: return getInvalidInstructionMapping(); } @@ -433,7 +472,7 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { for (const auto &Mapping : OperandsMapping[i]) { assert( (Mapping.RegBank->getID() != ARM::FPRRegBankID || - MF.getSubtarget<ARMSubtarget>().hasVFP2()) && + MF.getSubtarget<ARMSubtarget>().hasVFP2Base()) && "Trying to use floating point register bank on target without vfp"); } } diff --git a/lib/Target/ARM/ARMRegisterBankInfo.h b/lib/Target/ARM/ARMRegisterBankInfo.h index 9650b358f319..1961f7af49bb 100644 --- a/lib/Target/ARM/ARMRegisterBankInfo.h +++ b/lib/Target/ARM/ARMRegisterBankInfo.h @@ -1,9 +1,8 @@ //===- ARMRegisterBankInfo ---------------------------------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file diff --git a/lib/Target/ARM/ARMRegisterBanks.td b/lib/Target/ARM/ARMRegisterBanks.td index 6e3834da3bb5..e4ebf793f9b0 100644 --- a/lib/Target/ARM/ARMRegisterBanks.td +++ b/lib/Target/ARM/ARMRegisterBanks.td @@ -1,9 +1,8 @@ //=- ARMRegisterBank.td - Describe the AArch64 Banks ---------*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMRegisterInfo.cpp b/lib/Target/ARM/ARMRegisterInfo.cpp index e6e8cdf965e2..6649750bb388 100644 --- a/lib/Target/ARM/ARMRegisterInfo.cpp +++ b/lib/Target/ARM/ARMRegisterInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMRegisterInfo.cpp - ARM Register Information --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMRegisterInfo.h b/lib/Target/ARM/ARMRegisterInfo.h index e2e650e4af93..87c0f322d3b3 100644 --- a/lib/Target/ARM/ARMRegisterInfo.h +++ b/lib/Target/ARM/ARMRegisterInfo.h @@ -1,9 +1,8 @@ //===-- ARMRegisterInfo.h - ARM Register Information Impl -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMRegisterInfo.td b/lib/Target/ARM/ARMRegisterInfo.td index dc56186cb54a..92ae26b3729d 100644 --- a/lib/Target/ARM/ARMRegisterInfo.td +++ b/lib/Target/ARM/ARMRegisterInfo.td @@ -1,9 +1,8 @@ //===-- ARMRegisterInfo.td - ARM Register defs -------------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -14,7 +13,8 @@ include "ARMSystemRegister.td" //===----------------------------------------------------------------------===// // Registers are identified with 4-bit ID numbers. -class ARMReg<bits<16> Enc, string n, list<Register> subregs = []> : Register<n> { +class ARMReg<bits<16> Enc, string n, list<Register> subregs = [], + list<string> altNames = []> : Register<n, altNames> { let HWEncoding = Enc; let Namespace = "ARM"; let SubRegs = subregs; @@ -27,6 +27,11 @@ class ARMFReg<bits<16> Enc, string n> : Register<n> { let Namespace = "ARM"; } +let Namespace = "ARM", + FallbackRegAltNameIndex = NoRegAltName in { + def RegNamesRaw : RegAltNameIndex; +} + // Subregister indices. let Namespace = "ARM" in { def qqsub_0 : SubRegIndex<256>; @@ -84,9 +89,11 @@ def R9 : ARMReg< 9, "r9">, DwarfRegNum<[9]>; def R10 : ARMReg<10, "r10">, DwarfRegNum<[10]>; def R11 : ARMReg<11, "r11">, DwarfRegNum<[11]>; def R12 : ARMReg<12, "r12">, DwarfRegNum<[12]>; -def SP : ARMReg<13, "sp">, DwarfRegNum<[13]>; -def LR : ARMReg<14, "lr">, DwarfRegNum<[14]>; -def PC : ARMReg<15, "pc">, DwarfRegNum<[15]>; +let RegAltNameIndices = [RegNamesRaw] in { +def SP : ARMReg<13, "sp", [], ["r13"]>, DwarfRegNum<[13]>; +def LR : ARMReg<14, "lr", [], ["r14"]>, DwarfRegNum<[14]>; +def PC : ARMReg<15, "pc", [], ["r15"]>, DwarfRegNum<[15]>; +} } // Float registers @@ -190,6 +197,17 @@ def MVFR0 : ARMReg<7, "mvfr0">; def FPEXC : ARMReg<8, "fpexc">; def FPINST : ARMReg<9, "fpinst">; def FPINST2 : ARMReg<10, "fpinst2">; +// These encodings aren't actual instruction encodings, their encoding depends +// on the instruction they are used in and for VPR 32 was chosen such that it +// always comes last in spr_reglist_with_vpr. +def VPR : ARMReg<32, "vpr">; +def FPSCR_NZCVQC + : ARMReg<2, "fpscr_nzcvqc">; +def P0 : ARMReg<13, "p0">; +def FPCXTNS : ARMReg<14, "fpcxtns">; +def FPCXTS : ARMReg<15, "fpcxts">; + +def ZR : ARMReg<15, "zr">, DwarfRegNum<[15]>; // Register classes. // @@ -209,9 +227,10 @@ def GPR : RegisterClass<"ARM", [i32], 32, (add (sequence "R%u", 0, 12), // know how to spill them. If we make our prologue/epilogue code smarter at // some point, we can go back to using the above allocation orders for the // Thumb1 instructions that know how to use hi regs. - let AltOrders = [(add LR, GPR), (trunc GPR, 8)]; + let AltOrders = [(add LR, GPR), (trunc GPR, 8), + (add (trunc GPR, 8), R12, LR, (shl GPR, 8))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + return MF.getSubtarget<ARMSubtarget>().getGPRAllocationOrder(MF); }]; let DiagnosticString = "operand must be a register in range [r0, r15]"; } @@ -220,9 +239,10 @@ def GPR : RegisterClass<"ARM", [i32], 32, (add (sequence "R%u", 0, 12), // certain operand slots, particularly as the destination. Primarily // useful for disassembly. def GPRnopc : RegisterClass<"ARM", [i32], 32, (sub GPR, PC)> { - let AltOrders = [(add LR, GPRnopc), (trunc GPRnopc, 8)]; + let AltOrders = [(add LR, GPRnopc), (trunc GPRnopc, 8), + (add (trunc GPRnopc, 8), R12, LR, (shl GPRnopc, 8))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + return MF.getSubtarget<ARMSubtarget>().getGPRAllocationOrder(MF); }]; let DiagnosticString = "operand must be a register in range [r0, r14]"; } @@ -238,6 +258,27 @@ def GPRwithAPSR : RegisterClass<"ARM", [i32], 32, (add (sub GPR, PC), APSR_NZCV) let DiagnosticString = "operand must be a register in range [r0, r14] or apsr_nzcv"; } +// GPRs without the PC and SP registers but with APSR. Used by CLRM instruction. +def GPRwithAPSRnosp : RegisterClass<"ARM", [i32], 32, (add (sequence "R%u", 0, 12), LR, APSR)> { + let isAllocatable = 0; +} + +def GPRwithZR : RegisterClass<"ARM", [i32], 32, (add (sub GPR, PC), ZR)> { + let AltOrders = [(add LR, GPRwithZR), (trunc GPRwithZR, 8)]; + let AltOrderSelect = [{ + return 1 + MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + }]; + let DiagnosticString = "operand must be a register in range [r0, r14] or zr"; +} + +def GPRwithZRnosp : RegisterClass<"ARM", [i32], 32, (sub GPRwithZR, SP)> { + let AltOrders = [(add LR, GPRwithZRnosp), (trunc GPRwithZRnosp, 8)]; + let AltOrderSelect = [{ + return 1 + MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + }]; + let DiagnosticString = "operand must be a register in range [r0, r12] or r14 or zr"; +} + // GPRsp - Only the SP is legal. Used by Thumb1 instructions that want the // implied SP argument list. // FIXME: It would be better to not use this at all and refactor the @@ -247,14 +288,19 @@ def GPRsp : RegisterClass<"ARM", [i32], 32, (add SP)> { let DiagnosticString = "operand must be a register sp"; } +// GPRlr - Only LR is legal. Used by ARMv8.1-M Low Overhead Loop instructions +// where LR is the only legal loop counter register. +def GPRlr : RegisterClass<"ARM", [i32], 32, (add LR)>; + // restricted GPR register class. Many Thumb2 instructions allow the full // register range for operands, but have undefined behaviours when PC // or SP (R13 or R15) are used. The ARM ISA refers to these operands // via the BadReg() pseudo-code description. def rGPR : RegisterClass<"ARM", [i32], 32, (sub GPR, SP, PC)> { - let AltOrders = [(add LR, rGPR), (trunc rGPR, 8)]; + let AltOrders = [(add LR, rGPR), (trunc rGPR, 8), + (add (trunc rGPR, 8), R12, LR, (shl rGPR, 8))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + return MF.getSubtarget<ARMSubtarget>().getGPRAllocationOrder(MF); }]; let DiagnosticType = "rGPR"; } @@ -285,12 +331,38 @@ def tcGPR : RegisterClass<"ARM", [i32], 32, (add R0, R1, R2, R3, R12)> { }]; } +def tGPROdd : RegisterClass<"ARM", [i32], 32, (add R1, R3, R5, R7, R9, R11)> { + let AltOrders = [(and tGPROdd, tGPR)]; + let AltOrderSelect = [{ + return MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + }]; + let DiagnosticString = + "operand must be an odd-numbered register in range [r1,r11]"; +} + +def tGPREven : RegisterClass<"ARM", [i32], 32, (add R0, R2, R4, R6, R8, R10, R12, LR)> { + let AltOrders = [(and tGPREven, tGPR)]; + let AltOrderSelect = [{ + return MF.getSubtarget<ARMSubtarget>().isThumb1Only(); + }]; + let DiagnosticString = "operand must be an even-numbered register"; +} + // Condition code registers. def CCR : RegisterClass<"ARM", [i32], 32, (add CPSR)> { let CopyCost = -1; // Don't allow copying of status registers. let isAllocatable = 0; } +// MVE Condition code register. +def VCCR : RegisterClass<"ARM", [i32, v16i1, v8i1, v4i1], 32, (add VPR)> { +// let CopyCost = -1; // Don't allow copying of status registers. +} + +// FPSCR, when the flags at the top of it are used as the input or +// output to an instruction such as MVE VADC. +def cl_FPSCR_NZCV : RegisterClass<"ARM", [i32], 32, (add FPSCR_NZCV)>; + // Scalar single precision floating point register class.. // FIXME: Allocation order changed to s0, s2, ... or s0, s4, ... as a quick hack // to avoid partial-write dependencies on D or Q (depending on platform) @@ -302,7 +374,7 @@ def SPR : RegisterClass<"ARM", [f32], 32, (sequence "S%u", 0, 31)> { (decimate (rotl SPR, 1), 4), (decimate (rotl SPR, 1), 2))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(MF); + return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(); }]; let DiagnosticString = "operand must be a register in range [s0, s31]"; } @@ -314,7 +386,7 @@ def HPR : RegisterClass<"ARM", [f16], 32, (sequence "S%u", 0, 31)> { (decimate (rotl HPR, 1), 4), (decimate (rotl HPR, 1), 2))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(MF); + return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(); }]; let DiagnosticString = "operand must be a register in range [s0, s31]"; } @@ -336,11 +408,18 @@ def DPR : RegisterClass<"ARM", [f64, v8i8, v4i16, v2i32, v1i64, v2f32, v4f16], 6 let AltOrders = [(rotl DPR, 16), (add (decimate (rotl DPR, 16), 2), (rotl DPR, 16))]; let AltOrderSelect = [{ - return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(MF); + return 1 + MF.getSubtarget<ARMSubtarget>().useStride4VFPs(); }]; let DiagnosticType = "DPR"; } +// Scalar single and double precision floating point and VPR register class, +// this is only used for parsing, don't use it anywhere else as the size and +// types don't match! +def FPWithVPR : RegisterClass<"ARM", [f32], 32, (add SPR, DPR, VPR)> { + let isAllocatable = 0; +} + // Subset of DPR that are accessible with VFP2 (and so that also have // 32-bit SPR subregs). def DPR_VFP2 : RegisterClass<"ARM", [f64, v8i8, v4i16, v2i32, v1i64, v2f32, v4f16], 64, @@ -359,8 +438,10 @@ def DPR_8 : RegisterClass<"ARM", [f64, v8i8, v4i16, v2i32, v1i64, v2f32, v4f16], def QPR : RegisterClass<"ARM", [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64, v8f16], 128, (sequence "Q%u", 0, 15)> { // Allocate non-VFP2 aliases Q8-Q15 first. - let AltOrders = [(rotl QPR, 8)]; - let AltOrderSelect = [{ return 1; }]; + let AltOrders = [(rotl QPR, 8), (trunc QPR, 8)]; + let AltOrderSelect = [{ + return 1 + MF.getSubtarget<ARMSubtarget>().hasMVEIntegerOps(); + }]; let DiagnosticString = "operand must be a register in range [q0, q15]"; } @@ -376,6 +457,12 @@ def QPR_8 : RegisterClass<"ARM", [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], let DiagnosticString = "operand must be a register in range [q0, q3]"; } +// MVE 128-bit vector register class. This class is only really needed for +// parsing assembly, since we still have to truncate the register set in the QPR +// class anyway. +def MQPR : RegisterClass<"ARM", [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64, v8f16], + 128, (trunc QPR, 8)>; + // Pseudo-registers representing odd-even pairs of D registers. The even-odd // pairs are already represented by the Q registers. // These are needed by NEON instructions requiring two consecutive D registers. @@ -390,8 +477,11 @@ def DPair : RegisterClass<"ARM", [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], 128, (interleave QPR, TuplesOE2D)> { // Allocate starting at non-VFP2 registers D16-D31 first. // Prefer even-odd pairs as they are easier to copy. - let AltOrders = [(add (rotl QPR, 8), (rotl DPair, 16))]; - let AltOrderSelect = [{ return 1; }]; + let AltOrders = [(add (rotl QPR, 8), (rotl DPair, 16)), + (add (trunc QPR, 8), (trunc DPair, 16))]; + let AltOrderSelect = [{ + return 1 + MF.getSubtarget<ARMSubtarget>().hasMVEIntegerOps(); + }]; } // Pseudo-registers representing even-odd pairs of GPRs from R1 to R13/SP. diff --git a/lib/Target/ARM/ARMSchedule.td b/lib/Target/ARM/ARMSchedule.td index ed5a3a7bb696..ce74d325c4e5 100644 --- a/lib/Target/ARM/ARMSchedule.td +++ b/lib/Target/ARM/ARMSchedule.td @@ -1,9 +1,8 @@ //===-- ARMSchedule.td - ARM Scheduling Definitions --------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// @@ -425,4 +424,4 @@ include "ARMScheduleA9.td" include "ARMScheduleSwift.td" include "ARMScheduleR52.td" include "ARMScheduleA57.td" -include "ARMScheduleM3.td" +include "ARMScheduleM4.td" diff --git a/lib/Target/ARM/ARMScheduleA57.td b/lib/Target/ARM/ARMScheduleA57.td index 63f975ba6e39..a79f3348f338 100644 --- a/lib/Target/ARM/ARMScheduleA57.td +++ b/lib/Target/ARM/ARMScheduleA57.td @@ -1,9 +1,8 @@ //=- ARMScheduleA57.td - ARM Cortex-A57 Scheduling Defs -----*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -95,6 +94,9 @@ def CortexA57Model : SchedMachineModel { // FIXME: Remove when all errors have been fixed. let FullInstRWOverlapCheck = 0; + + let UnsupportedFeatures = [HasV8_1MMainline, HasMVEInt, HasMVEFloat, + HasFPRegsV8_1M]; } //===----------------------------------------------------------------------===// @@ -1175,7 +1177,8 @@ def : InstRW<[A57Write_8cyc_1V], (instregex // ASIMD FP max/min def : InstRW<[A57Write_5cyc_1V], (instregex - "(VMAX|VMIN)(fd|fq|hd|hq)", "(VPMAX|VPMIN)(f|h)", "VMAXNM", "VMINNM")>; + "(VMAX|VMIN)(fd|fq|hd|hq)", "(VPMAX|VPMIN)(f|h)", "(NEON|VFP)_VMAXNM", + "(NEON|VFP)_VMINNM")>; // ASIMD FP multiply def A57WriteVMUL_VecFP : SchedWriteRes<[A57UnitV]> { let Latency = 5; } diff --git a/lib/Target/ARM/ARMScheduleA57WriteRes.td b/lib/Target/ARM/ARMScheduleA57WriteRes.td index 670717dc7c13..5ba61503686e 100644 --- a/lib/Target/ARM/ARMScheduleA57WriteRes.td +++ b/lib/Target/ARM/ARMScheduleA57WriteRes.td @@ -1,9 +1,8 @@ //=- ARMScheduleA57WriteRes.td - ARM Cortex-A57 Write Res ---*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMScheduleA8.td b/lib/Target/ARM/ARMScheduleA8.td index ba380cba100f..1be0ee4334a8 100644 --- a/lib/Target/ARM/ARMScheduleA8.td +++ b/lib/Target/ARM/ARMScheduleA8.td @@ -1,9 +1,8 @@ //=- ARMScheduleA8.td - ARM Cortex-A8 Scheduling Definitions -*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMScheduleA9.td b/lib/Target/ARM/ARMScheduleA9.td index fc301c589269..21d32bde4710 100644 --- a/lib/Target/ARM/ARMScheduleA9.td +++ b/lib/Target/ARM/ARMScheduleA9.td @@ -1,9 +1,8 @@ //=- ARMScheduleA9.td - ARM Cortex-A9 Scheduling Definitions -*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMScheduleM3.td b/lib/Target/ARM/ARMScheduleM3.td deleted file mode 100644 index 93f8299f9bd0..000000000000 --- a/lib/Target/ARM/ARMScheduleM3.td +++ /dev/null @@ -1,21 +0,0 @@ -//=- ARMScheduleM3.td - ARM Cortex-M3 Scheduling Definitions -*- 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 ARM Cortex-M3 processor. -// -//===----------------------------------------------------------------------===// - -def CortexM3Model : SchedMachineModel { - let IssueWidth = 1; // Only IT can be dual-issued, so assume single-issue - let MicroOpBufferSize = 0; // In-order - let LoadLatency = 2; // Latency when not pipelined, not pc-relative - let MispredictPenalty = 2; // Best case branch taken cost - - let CompleteModel = 0; -} diff --git a/lib/Target/ARM/ARMScheduleM4.td b/lib/Target/ARM/ARMScheduleM4.td new file mode 100644 index 000000000000..38c8ea2b4f35 --- /dev/null +++ b/lib/Target/ARM/ARMScheduleM4.td @@ -0,0 +1,119 @@ +//==- ARMScheduleM4.td - Cortex-M4 Scheduling Definitions -*- tablegen -*-====// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the SchedRead/Write data for the ARM Cortex-M4 processor. +// +//===----------------------------------------------------------------------===// + +def CortexM4Model : SchedMachineModel { + let IssueWidth = 1; // Only IT can be dual-issued, so assume single-issue + let MicroOpBufferSize = 0; // In-order + let LoadLatency = 2; // Latency when not pipelined, not pc-relative + let MispredictPenalty = 2; // Best case branch taken cost + let PostRAScheduler = 1; + + let CompleteModel = 0; +} + + +// We model the entire cpu as a single pipeline with a BufferSize = 0 since +// Cortex-M4 is in-order. + +def M4Unit : ProcResource<1> { let BufferSize = 0; } + + +let SchedModel = CortexM4Model in { + +// Some definitions of latencies we apply to different instructions + +class M4UnitL1<SchedWrite write> : WriteRes<write, [M4Unit]> { let Latency = 1; } +class M4UnitL2<SchedWrite write> : WriteRes<write, [M4Unit]> { let Latency = 2; } +class M4UnitL3<SchedWrite write> : WriteRes<write, [M4Unit]> { let Latency = 3; } +class M4UnitL14<SchedWrite write> : WriteRes<write, [M4Unit]> { let Latency = 14; } +def M4UnitL1_wr : SchedWriteRes<[M4Unit]> { let Latency = 1; } +def M4UnitL2_wr : SchedWriteRes<[M4Unit]> { let Latency = 2; } +class M4UnitL1I<dag instr> : InstRW<[M4UnitL1_wr], instr>; +class M4UnitL2I<dag instr> : InstRW<[M4UnitL2_wr], instr>; + + +// Loads, MAC's and DIV all get a higher latency of 2 +def : M4UnitL2<WriteLd>; +def : M4UnitL2<WriteMAC32>; +def : M4UnitL2<WriteMAC64Hi>; +def : M4UnitL2<WriteMAC64Lo>; +def : M4UnitL2<WriteMAC16>; +def : M4UnitL2<WriteDIV>; + +def : M4UnitL2I<(instregex "(t|t2)LDM")>; + + +// Stores we use a latency of 1 as they have no outputs + +def : M4UnitL1<WriteST>; +def : M4UnitL1I<(instregex "(t|t2)STM")>; + + +// Everything else has a Latency of 1 + +def : M4UnitL1<WriteALU>; +def : M4UnitL1<WriteALUsi>; +def : M4UnitL1<WriteALUsr>; +def : M4UnitL1<WriteALUSsr>; +def : M4UnitL1<WriteBr>; +def : M4UnitL1<WriteBrL>; +def : M4UnitL1<WriteBrTbl>; +def : M4UnitL1<WriteCMPsi>; +def : M4UnitL1<WriteCMPsr>; +def : M4UnitL1<WriteCMP>; +def : M4UnitL1<WriteMUL32>; +def : M4UnitL1<WriteMUL64Hi>; +def : M4UnitL1<WriteMUL64Lo>; +def : M4UnitL1<WriteMUL16>; +def : M4UnitL1<WriteNoop>; +def : M4UnitL1<WritePreLd>; +def : M4UnitL1I<(instregex "(t|t2)MOV")>; +def : M4UnitL1I<(instrs COPY)>; +def : M4UnitL1I<(instregex "t2IT")>; +def : M4UnitL1I<(instregex "t2SEL", "t2USAD8", + "t2(S|Q|SH|U|UQ|UH)(ADD16|ASX|SAX|SUB16|ADD8|SUB8)", "t2USADA8", "(t|t2)REV")>; + +def : ReadAdvance<ReadALU, 0>; +def : ReadAdvance<ReadALUsr, 0>; +def : ReadAdvance<ReadMUL, 0>; +def : ReadAdvance<ReadMAC, 0>; + +// Most FP instructions are single-cycle latency, except MAC's, Div's and Sqrt's. +// Loads still take 2 cycles. + +def : M4UnitL1<WriteFPCVT>; +def : M4UnitL1<WriteFPMOV>; +def : M4UnitL1<WriteFPALU32>; +def : M4UnitL1<WriteFPALU64>; +def : M4UnitL1<WriteFPMUL32>; +def : M4UnitL1<WriteFPMUL64>; +def : M4UnitL2I<(instregex "VLD")>; +def : M4UnitL1I<(instregex "VST")>; +def : M4UnitL3<WriteFPMAC32>; +def : M4UnitL3<WriteFPMAC64>; +def : M4UnitL14<WriteFPDIV32>; +def : M4UnitL14<WriteFPDIV64>; +def : M4UnitL14<WriteFPSQRT32>; +def : M4UnitL14<WriteFPSQRT64>; +def : M4UnitL1<WriteVLD1>; +def : M4UnitL1<WriteVLD2>; +def : M4UnitL1<WriteVLD3>; +def : M4UnitL1<WriteVLD4>; +def : M4UnitL1<WriteVST1>; +def : M4UnitL1<WriteVST2>; +def : M4UnitL1<WriteVST3>; +def : M4UnitL1<WriteVST4>; + +def : ReadAdvance<ReadFPMUL, 0>; +def : ReadAdvance<ReadFPMAC, 0>; + +} diff --git a/lib/Target/ARM/ARMScheduleR52.td b/lib/Target/ARM/ARMScheduleR52.td index 11bce45161b3..d1cbf754b5a1 100644 --- a/lib/Target/ARM/ARMScheduleR52.td +++ b/lib/Target/ARM/ARMScheduleR52.td @@ -1,9 +1,8 @@ //==- ARMScheduleR52.td - Cortex-R52 Scheduling Definitions -*- tablegen -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMScheduleSwift.td b/lib/Target/ARM/ARMScheduleSwift.td index 87984648139b..00a44599b1b2 100644 --- a/lib/Target/ARM/ARMScheduleSwift.td +++ b/lib/Target/ARM/ARMScheduleSwift.td @@ -1,9 +1,8 @@ //=- ARMScheduleSwift.td - Swift Scheduling Definitions -*- tablegen -*----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMScheduleV6.td b/lib/Target/ARM/ARMScheduleV6.td index 57d0bfb65049..9b86097329c0 100644 --- a/lib/Target/ARM/ARMScheduleV6.td +++ b/lib/Target/ARM/ARMScheduleV6.td @@ -1,9 +1,8 @@ //===-- ARMScheduleV6.td - ARM v6 Scheduling Definitions ---*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMSelectionDAGInfo.cpp b/lib/Target/ARM/ARMSelectionDAGInfo.cpp index 4d685158e258..cade06e8c109 100644 --- a/lib/Target/ARM/ARMSelectionDAGInfo.cpp +++ b/lib/Target/ARM/ARMSelectionDAGInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMSelectionDAGInfo.cpp - ARM SelectionDAG Info -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -171,7 +170,7 @@ SDValue ARMSelectionDAGInfo::EmitTargetCodeForMemcpy( // Code size optimisation: do not inline memcpy if expansion results in // more instructions than the libary call. - if (NumMEMCPYs > 1 && DAG.getMachineFunction().getFunction().optForMinSize()) { + if (NumMEMCPYs > 1 && Subtarget.hasMinSize()) { return SDValue(); } diff --git a/lib/Target/ARM/ARMSelectionDAGInfo.h b/lib/Target/ARM/ARMSelectionDAGInfo.h index 2ddb42c95397..b8a86ae7310f 100644 --- a/lib/Target/ARM/ARMSelectionDAGInfo.h +++ b/lib/Target/ARM/ARMSelectionDAGInfo.h @@ -1,9 +1,8 @@ //===-- ARMSelectionDAGInfo.h - ARM SelectionDAG Info -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMSubtarget.cpp b/lib/Target/ARM/ARMSubtarget.cpp index b1d0761e3231..978faed776b0 100644 --- a/lib/Target/ARM/ARMSubtarget.cpp +++ b/lib/Target/ARM/ARMSubtarget.cpp @@ -1,9 +1,8 @@ //===-- ARMSubtarget.cpp - ARM Subtarget Information ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -93,10 +92,12 @@ ARMFrameLowering *ARMSubtarget::initializeFrameLowering(StringRef CPU, ARMSubtarget::ARMSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, - const ARMBaseTargetMachine &TM, bool IsLittle) + const ARMBaseTargetMachine &TM, bool IsLittle, + bool MinSize) : ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps), - CPUString(CPU), IsLittle(IsLittle), TargetTriple(TT), Options(TM.Options), - TM(TM), FrameLowering(initializeFrameLowering(CPU, FS)), + CPUString(CPU), OptMinSize(MinSize), IsLittle(IsLittle), + TargetTriple(TT), Options(TM.Options), TM(TM), + FrameLowering(initializeFrameLowering(CPU, FS)), // At this point initializeSubtargetDependencies has been called so // we can query directly. InstrInfo(isThumb1Only() @@ -283,6 +284,7 @@ void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) { case CortexA72: case CortexA73: case CortexA75: + case CortexA76: case CortexR4: case CortexR4F: case CortexR5: @@ -359,6 +361,13 @@ unsigned ARMSubtarget::getMispredictionPenalty() const { } bool ARMSubtarget::enableMachineScheduler() const { + // The MachineScheduler can increase register usage, so we use more high + // registers and end up with more T2 instructions that cannot be converted to + // T1 instructions. At least until we do better at converting to thumb1 + // instructions, on cortex-m at Oz where we are size-paranoid, don't use the + // Machine scheduler, relying on the DAG register pressure scheduler instead. + if (isMClass() && hasMinSize()) + return false; // Enable the MachineScheduler before register allocation for subtargets // with the use-misched feature. return useMachineScheduler(); @@ -374,20 +383,20 @@ bool ARMSubtarget::enablePostRAScheduler() const { bool ARMSubtarget::enableAtomicExpand() const { return hasAnyDataBarrier(); } -bool ARMSubtarget::useStride4VFPs(const MachineFunction &MF) const { +bool ARMSubtarget::useStride4VFPs() const { // For general targets, the prologue can grow when VFPs are allocated with // stride 4 (more vpush instructions). But WatchOS uses a compact unwind // format which it's more important to get right. return isTargetWatchABI() || - (useWideStrideVFP() && !MF.getFunction().optForMinSize()); + (useWideStrideVFP() && !OptMinSize); } -bool ARMSubtarget::useMovt(const MachineFunction &MF) const { +bool ARMSubtarget::useMovt() const { // NOTE Windows on ARM needs to use mov.w/mov.t pairs to materialise 32-bit // immediates as it is inherently position independent, and may be out of // range otherwise. return !NoMovt && hasV8MBaselineOps() && - (isTargetWindows() || !MF.getFunction().optForMinSize() || genExecuteOnly()); + (isTargetWindows() || !OptMinSize || genExecuteOnly()); } bool ARMSubtarget::useFastISel() const { @@ -404,3 +413,45 @@ bool ARMSubtarget::useFastISel() const { ((isTargetMachO() && !isThumb1Only()) || (isTargetLinux() && !isThumb()) || (isTargetNaCl() && !isThumb())); } + +unsigned ARMSubtarget::getGPRAllocationOrder(const MachineFunction &MF) const { + // The GPR register class has multiple possible allocation orders, with + // tradeoffs preferred by different sub-architectures and optimisation goals. + // The allocation orders are: + // 0: (the default tablegen order, not used) + // 1: r14, r0-r13 + // 2: r0-r7 + // 3: r0-r7, r12, lr, r8-r11 + // Note that the register allocator will change this order so that + // callee-saved registers are used later, as they require extra work in the + // prologue/epilogue (though we sometimes override that). + + // For thumb1-only targets, only the low registers are allocatable. + if (isThumb1Only()) + return 2; + + // Allocate low registers first, so we can select more 16-bit instructions. + // We also (in ignoreCSRForAllocationOrder) override the default behaviour + // with regards to callee-saved registers, because pushing extra registers is + // much cheaper (in terms of code size) than using high registers. After + // that, we allocate r12 (doesn't need to be saved), lr (saving it means we + // can return with the pop, don't need an extra "bx lr") and then the rest of + // the high registers. + if (isThumb2() && MF.getFunction().hasMinSize()) + return 3; + + // Otherwise, allocate in the default order, using LR first because saving it + // allows a shorter epilogue sequence. + return 1; +} + +bool ARMSubtarget::ignoreCSRForAllocationOrder(const MachineFunction &MF, + unsigned PhysReg) const { + // To minimize code size in Thumb2, we prefer the usage of low regs (lower + // cost per use) so we can use narrow encoding. By default, caller-saved + // registers (e.g. lr, r12) are always allocated first, regardless of + // their cost per use. When optForMinSize, we prefer the low regs even if + // they are CSR because usually push/pop can be folded into existing ones. + return isThumb2() && MF.getFunction().hasMinSize() && + ARM::GPRRegClass.contains(PhysReg); +} diff --git a/lib/Target/ARM/ARMSubtarget.h b/lib/Target/ARM/ARMSubtarget.h index 11841b4467a2..c2b0f052b843 100644 --- a/lib/Target/ARM/ARMSubtarget.h +++ b/lib/Target/ARM/ARMSubtarget.h @@ -1,9 +1,8 @@ //===-- ARMSubtarget.h - Define Subtarget for the ARM ----------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -60,6 +59,7 @@ protected: CortexA72, CortexA73, CortexA75, + CortexA76, CortexA8, CortexA9, CortexM3, @@ -110,7 +110,8 @@ protected: ARMv8a, ARMv8mBaseline, ARMv8mMainline, - ARMv8r + ARMv8r, + ARMv81mMainline, }; public: @@ -157,6 +158,9 @@ protected: bool HasV8_5aOps = false; bool HasV8MBaselineOps = false; bool HasV8MMainlineOps = false; + bool HasV8_1MMainlineOps = false; + bool HasMVEIntegerOps = false; + bool HasMVEFloatOps = false; /// HasVFPv2, HasVFPv3, HasVFPv4, HasFPARMv8, HasNEON - Specify what /// floating point ISAs are supported. @@ -165,6 +169,24 @@ protected: bool HasVFPv4 = false; bool HasFPARMv8 = false; bool HasNEON = false; + bool HasFPRegs = false; + bool HasFPRegs16 = false; + bool HasFPRegs64 = false; + + /// Versions of the VFP flags restricted to single precision, or to + /// 16 d-registers, or both. + bool HasVFPv2SP = false; + bool HasVFPv3SP = false; + bool HasVFPv4SP = false; + bool HasFPARMv8SP = false; + bool HasVFPv2D16 = false; + bool HasVFPv3D16 = false; + bool HasVFPv4D16 = false; + bool HasFPARMv8D16 = false; + bool HasVFPv2D16SP = false; + bool HasVFPv3D16SP = false; + bool HasVFPv4D16SP = false; + bool HasFPARMv8D16SP = false; /// HasDotProd - True if the ARMv8.2A dot product instructions are supported. bool HasDotProd = false; @@ -232,9 +254,9 @@ protected: /// HasFP16FML - True if subtarget supports half-precision FP fml operations bool HasFP16FML = false; - /// HasD16 - True if subtarget is limited to 16 double precision + /// HasD32 - True if subtarget has the full 32 double precision /// FP registers for VFPv3. - bool HasD16 = false; + bool HasD32 = false; /// HasHardwareDivide - True if subtarget supports [su]div in Thumb mode bool HasHardwareDivideInThumb = false; @@ -291,9 +313,9 @@ protected: /// extension. bool HasVirtualization = false; - /// FPOnlySP - If true, the floating point unit only supports single + /// HasFP64 - If true, the floating point unit supports double /// precision. - bool FPOnlySP = false; + bool HasFP64 = false; /// If true, the processor supports the Performance Monitor Extensions. These /// include a generic cycle-counter as well as more fine-grained (often @@ -321,6 +343,9 @@ protected: /// HasRAS - if true, the processor supports RAS extensions bool HasRAS = false; + /// HasLOB - if true, the processor supports the Low Overhead Branch extension + bool HasLOB = false; + /// If true, the instructions "vmov.i32 d0, #0" and "vmov.i32 q0, #0" are /// particularly effective at zeroing a VFP register. bool HasZeroCycleZeroing = false; @@ -446,6 +471,10 @@ protected: /// What alignment is preferred for loop bodies, in log2(bytes). unsigned PrefLoopAlignment = 0; + /// OptMinSize - True if we're optimising for minimum code size, equal to + /// the function attribute. + bool OptMinSize = false; + /// IsLittle - The target is Little Endian bool IsLittle; @@ -468,7 +497,8 @@ public: /// of the specified triple. /// ARMSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, - const ARMBaseTargetMachine &TM, bool IsLittle); + const ARMBaseTargetMachine &TM, bool IsLittle, + bool MinSize = false); /// getMaxInlineSizeThreshold - Returns the maximum memset / memcpy size /// that still makes it profitable to inline the call. @@ -546,6 +576,12 @@ public: bool hasV8_5aOps() const { return HasV8_5aOps; } bool hasV8MBaselineOps() const { return HasV8MBaselineOps; } bool hasV8MMainlineOps() const { return HasV8MMainlineOps; } + bool hasV8_1MMainlineOps() const { return HasV8_1MMainlineOps; } + bool hasMVEIntegerOps() const { return HasMVEIntegerOps; } + bool hasMVEFloatOps() const { return HasMVEFloatOps; } + bool hasFPRegs() const { return HasFPRegs; } + bool hasFPRegs16() const { return HasFPRegs16; } + bool hasFPRegs64() const { return HasFPRegs64; } /// @{ /// These functions are obsolete, please consider adding subtarget features @@ -564,10 +600,10 @@ public: bool hasARMOps() const { return !NoARM; } - bool hasVFP2() const { return HasVFPv2; } - bool hasVFP3() const { return HasVFPv3; } - bool hasVFP4() const { return HasVFPv4; } - bool hasFPARMv8() const { return HasFPARMv8; } + bool hasVFP2Base() const { return HasVFPv2D16SP; } + bool hasVFP3Base() const { return HasVFPv3D16SP; } + bool hasVFP4Base() const { return HasVFPv4D16SP; } + bool hasFPARMv8Base() const { return HasFPARMv8D16SP; } bool hasNEON() const { return HasNEON; } bool hasSHA2() const { return HasSHA2; } bool hasAES() const { return HasAES; } @@ -575,6 +611,7 @@ public: bool hasDotProd() const { return HasDotProd; } bool hasCRC() const { return HasCRC; } bool hasRAS() const { return HasRAS; } + bool hasLOB() const { return HasLOB; } bool hasVirtualization() const { return HasVirtualization; } bool useNEONForSinglePrecisionFP() const { @@ -596,7 +633,7 @@ public: bool useFPVMLx() const { return !SlowFPVMLx; } bool hasVMLxForwarding() const { return HasVMLxForwarding; } bool isFPBrccSlow() const { return SlowFPBrcc; } - bool isFPOnlySP() const { return FPOnlySP; } + bool hasFP64() const { return HasFP64; } bool hasPerfMon() const { return HasPerfMon; } bool hasTrustZone() const { return HasTrustZone; } bool has8MSecExt() const { return Has8MSecExt; } @@ -633,7 +670,7 @@ public: bool genExecuteOnly() const { return GenExecuteOnly; } bool hasFP16() const { return HasFP16; } - bool hasD16() const { return HasD16; } + bool hasD32() const { return HasD32; } bool hasFullFP16() const { return HasFullFP16; } bool hasFP16FML() const { return HasFP16FML; } @@ -710,6 +747,7 @@ public: bool disablePostRAScheduler() const { return DisablePostRAScheduler; } bool useSoftFloat() const { return UseSoftFloat; } bool isThumb() const { return InThumbMode; } + bool hasMinSize() const { return OptMinSize; } bool isThumb1Only() const { return InThumbMode && !HasThumb2; } bool isThumb2() const { return InThumbMode && HasThumb2; } bool hasThumb2() const { return HasThumb2; } @@ -736,9 +774,9 @@ public: isThumb1Only(); } - bool useStride4VFPs(const MachineFunction &MF) const; + bool useStride4VFPs() const; - bool useMovt(const MachineFunction &MF) const; + bool useMovt() const; bool supportsTailCall() const { return SupportsTailCall; } @@ -818,6 +856,10 @@ public: unsigned getPrefLoopAlignment() const { return PrefLoopAlignment; } + + bool ignoreCSRForAllocationOrder(const MachineFunction &MF, + unsigned PhysReg) const override; + unsigned getGPRAllocationOrder(const MachineFunction &MF) const; }; } // end namespace llvm diff --git a/lib/Target/ARM/ARMSystemRegister.td b/lib/Target/ARM/ARMSystemRegister.td index ad1d37168e08..f21c7f0246f9 100644 --- a/lib/Target/ARM/ARMSystemRegister.td +++ b/lib/Target/ARM/ARMSystemRegister.td @@ -1,9 +1,8 @@ //===-- ARMSystemRegister.td - ARM Register defs -------------*- tablegen -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMTargetMachine.cpp b/lib/Target/ARM/ARMTargetMachine.cpp index ec02c840d5e1..7f0aae1739b3 100644 --- a/lib/Target/ARM/ARMTargetMachine.cpp +++ b/lib/Target/ARM/ARMTargetMachine.cpp @@ -1,9 +1,8 @@ //===-- ARMTargetMachine.cpp - Define TargetMachine for ARM ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,6 +16,7 @@ #include "ARMTargetObjectFile.h" #include "ARMTargetTransformInfo.h" #include "MCTargetDesc/ARMMCTargetDesc.h" +#include "TargetInfo/ARMTargetInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -95,6 +95,8 @@ extern "C" void LLVMInitializeARMTarget() { initializeARMExecutionDomainFixPass(Registry); initializeARMExpandPseudoPass(Registry); initializeThumb2SizeReducePass(Registry); + initializeMVEVPTBlockPass(Registry); + initializeARMLowOverheadLoopsPass(Registry); } static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) { @@ -142,6 +144,10 @@ static std::string computeDataLayout(const Triple &TT, StringRef CPU, // Pointers are 32 bits and aligned to 32 bits. Ret += "-p:32:32"; + // Function pointers are aligned to 8 bits (because the LSB stores the + // ARM/Thumb state). + Ret += "-Fi8"; + // ABIs other than APCS have 64 bit integers with natural alignment. if (ABI != ARMBaseTargetMachine::ARM_ABI_APCS) Ret += "-i64:64"; @@ -264,13 +270,20 @@ ARMBaseTargetMachine::getSubtargetImpl(const Function &F) const { if (SoftFloat) FS += FS.empty() ? "+soft-float" : ",+soft-float"; - auto &I = SubtargetMap[CPU + FS]; + // Use the optminsize to identify the subtarget, but don't use it in the + // feature string. + std::string Key = CPU + FS; + if (F.hasMinSize()) + Key += "+minsize"; + + auto &I = SubtargetMap[Key]; if (!I) { // This needs to be done before we create a new subtarget since any // creation will depend on the TM and the code generation flags on the // function that reside in TargetOptions. resetTargetOptions(F); - I = llvm::make_unique<ARMSubtarget>(TargetTriple, CPU, FS, *this, isLittle); + I = llvm::make_unique<ARMSubtarget>(TargetTriple, CPU, FS, *this, isLittle, + F.hasMinSize()); if (!I->isThumb() && !I->hasARMOps()) F.getContext().emitError("Function '" + F.getName() + "' uses ARM " @@ -351,6 +364,8 @@ public: void addPreRegAlloc() override; void addPreSched2() override; void addPreEmitPass() override; + + std::unique_ptr<CSEConfigBase> getCSEConfig() const override; }; class ARMExecutionDomainFix : public ExecutionDomainFix { @@ -375,6 +390,10 @@ TargetPassConfig *ARMBaseTargetMachine::createPassConfig(PassManagerBase &PM) { return new ARMPassConfig(*this, PM); } +std::unique_ptr<CSEConfigBase> ARMPassConfig::getCSEConfig() const { + return getStandardCSEConfigForOpt(TM->getOptLevel()); +} + void ARMPassConfig::addIRPasses() { if (TM->Options.ThreadModel == ThreadModel::Single) addPass(createLowerAtomicPass()); @@ -393,6 +412,10 @@ void ARMPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); + // Run the parallel DSP pass. + if (getOptLevel() == CodeGenOpt::Aggressive) + addPass(createARMParallelDSPPass()); + // Match interleaved memory accesses to ldN/stN intrinsics. if (TM->getOptLevel() != CodeGenOpt::None) addPass(createInterleavedAccessPass()); @@ -405,9 +428,6 @@ void ARMPassConfig::addCodeGenPrepare() { } bool ARMPassConfig::addPreISel() { - if (getOptLevel() != CodeGenOpt::None) - addPass(createARMParallelDSPPass()); - if ((TM->getOptLevel() != CodeGenOpt::None && EnableGlobalMerge == cl::BOU_UNSET) || EnableGlobalMerge == cl::BOU_TRUE) { @@ -427,6 +447,9 @@ bool ARMPassConfig::addPreISel() { MergeExternalByDefault)); } + if (TM->getOptLevel() != CodeGenOpt::None) + addPass(createHardwareLoopsPass()); + return false; } @@ -490,6 +513,7 @@ void ARMPassConfig::addPreSched2() { return !MF.getSubtarget<ARMSubtarget>().isThumb1Only(); })); } + addPass(createMVEVPTBlockPass()); addPass(createThumb2ITBlockPass()); } @@ -506,4 +530,5 @@ void ARMPassConfig::addPreEmitPass() { addPass(createARMOptimizeBarriersPass()); addPass(createARMConstantIslandPass()); + addPass(createARMLowOverheadLoopsPass()); } diff --git a/lib/Target/ARM/ARMTargetMachine.h b/lib/Target/ARM/ARMTargetMachine.h index 2c791998e702..cb8650d8139b 100644 --- a/lib/Target/ARM/ARMTargetMachine.h +++ b/lib/Target/ARM/ARMTargetMachine.h @@ -1,9 +1,8 @@ //===-- ARMTargetMachine.h - Define TargetMachine for ARM -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/ARMTargetObjectFile.cpp b/lib/Target/ARM/ARMTargetObjectFile.cpp index 9c13359cba71..891329d3f297 100644 --- a/lib/Target/ARM/ARMTargetObjectFile.cpp +++ b/lib/Target/ARM/ARMTargetObjectFile.cpp @@ -1,9 +1,8 @@ //===-- llvm/Target/ARMTargetObjectFile.cpp - ARM Object Info Impl --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMTargetObjectFile.h b/lib/Target/ARM/ARMTargetObjectFile.h index 0dc0882809c0..7b15dcc61f56 100644 --- a/lib/Target/ARM/ARMTargetObjectFile.h +++ b/lib/Target/ARM/ARMTargetObjectFile.h @@ -1,9 +1,8 @@ //===-- llvm/Target/ARMTargetObjectFile.h - ARM Object Info -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMTargetTransformInfo.cpp b/lib/Target/ARM/ARMTargetTransformInfo.cpp index f72bb8632eb7..2a8ec734a05f 100644 --- a/lib/Target/ARM/ARMTargetTransformInfo.cpp +++ b/lib/Target/ARM/ARMTargetTransformInfo.cpp @@ -1,9 +1,8 @@ //===- ARMTargetTransformInfo.cpp - ARM specific TTI ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -22,6 +21,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Type.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Support/Casting.h" @@ -36,6 +36,10 @@ using namespace llvm; #define DEBUG_TYPE "armtti" +static cl::opt<bool> DisableLowOverheadLoops( + "disable-arm-loloops", cl::Hidden, cl::init(true), + cl::desc("Disable the generation of low-overhead loops")); + bool ARMTTIImpl::areInlineCompatible(const Function *Caller, const Function *Callee) const { const TargetMachine &TM = getTLI()->getTargetMachine(); @@ -107,9 +111,13 @@ int ARMTTIImpl::getIntImmCost(unsigned Opcode, unsigned Idx, const APInt &Imm, Idx == 1) return 0; - if (Opcode == Instruction::And) - // Conversion to BIC is free, and means we can use ~Imm instead. - return std::min(getIntImmCost(Imm, Ty), getIntImmCost(~Imm, Ty)); + if (Opcode == Instruction::And) { + // UXTB/UXTH + if (Imm == 255 || Imm == 65535) + return 0; + // Conversion to BIC is free, and means we can use ~Imm instead. + return std::min(getIntImmCost(Imm, Ty), getIntImmCost(~Imm, Ty)); + } if (Opcode == Instruction::Add) // Conversion to SUB is free, and means we can use -Imm instead. @@ -398,6 +406,40 @@ int ARMTTIImpl::getAddressComputationCost(Type *Ty, ScalarEvolution *SE, return 1; } +int ARMTTIImpl::getMemcpyCost(const Instruction *I) { + const MemCpyInst *MI = dyn_cast<MemCpyInst>(I); + assert(MI && "MemcpyInst expected"); + ConstantInt *C = dyn_cast<ConstantInt>(MI->getLength()); + + // To model the cost of a library call, we assume 1 for the call, and + // 3 for the argument setup. + const unsigned LibCallCost = 4; + + // If 'size' is not a constant, a library call will be generated. + if (!C) + return LibCallCost; + + const unsigned Size = C->getValue().getZExtValue(); + const unsigned DstAlign = MI->getDestAlignment(); + const unsigned SrcAlign = MI->getSourceAlignment(); + const Function *F = I->getParent()->getParent(); + const unsigned Limit = TLI->getMaxStoresPerMemmove(F->hasMinSize()); + std::vector<EVT> MemOps; + + // MemOps will be poplulated with a list of data types that needs to be + // loaded and stored. That's why we multiply the number of elements by 2 to + // get the cost for this memcpy. + if (getTLI()->findOptimalMemOpLowering( + MemOps, Limit, Size, DstAlign, SrcAlign, false /*IsMemset*/, + false /*ZeroMemset*/, false /*MemcpyStrSrc*/, false /*AllowOverlap*/, + MI->getDestAddressSpace(), MI->getSourceAddressSpace(), + F->getAttributes())) + return MemOps.size() * 2; + + // If we can't find an optimal memop lowering, return the default cost + return LibCallCost; +} + int ARMTTIImpl::getShuffleCost(TTI::ShuffleKind Kind, Type *Tp, int Index, Type *SubTp) { if (Kind == TTI::SK_Broadcast) { @@ -590,6 +632,222 @@ int ARMTTIImpl::getInterleavedMemoryOpCost(unsigned Opcode, Type *VecTy, UseMaskForCond, UseMaskForGaps); } +bool ARMTTIImpl::isLoweredToCall(const Function *F) { + if (!F->isIntrinsic()) + BaseT::isLoweredToCall(F); + + // Assume all Arm-specific intrinsics map to an instruction. + if (F->getName().startswith("llvm.arm")) + return false; + + switch (F->getIntrinsicID()) { + default: break; + case Intrinsic::powi: + case Intrinsic::sin: + case Intrinsic::cos: + case Intrinsic::pow: + case Intrinsic::log: + case Intrinsic::log10: + case Intrinsic::log2: + case Intrinsic::exp: + case Intrinsic::exp2: + return true; + case Intrinsic::sqrt: + case Intrinsic::fabs: + case Intrinsic::copysign: + case Intrinsic::floor: + case Intrinsic::ceil: + case Intrinsic::trunc: + case Intrinsic::rint: + case Intrinsic::nearbyint: + case Intrinsic::round: + case Intrinsic::canonicalize: + case Intrinsic::lround: + case Intrinsic::llround: + case Intrinsic::lrint: + case Intrinsic::llrint: + if (F->getReturnType()->isDoubleTy() && !ST->hasFP64()) + return true; + if (F->getReturnType()->isHalfTy() && !ST->hasFullFP16()) + return true; + // Some operations can be handled by vector instructions and assume + // unsupported vectors will be expanded into supported scalar ones. + // TODO Handle scalar operations properly. + return !ST->hasFPARMv8Base() && !ST->hasVFP2Base(); + case Intrinsic::masked_store: + case Intrinsic::masked_load: + case Intrinsic::masked_gather: + case Intrinsic::masked_scatter: + return !ST->hasMVEIntegerOps(); + case Intrinsic::sadd_with_overflow: + case Intrinsic::uadd_with_overflow: + case Intrinsic::ssub_with_overflow: + case Intrinsic::usub_with_overflow: + case Intrinsic::sadd_sat: + case Intrinsic::uadd_sat: + case Intrinsic::ssub_sat: + case Intrinsic::usub_sat: + return false; + } + + return BaseT::isLoweredToCall(F); +} + +bool ARMTTIImpl::isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, + TargetLibraryInfo *LibInfo, + HardwareLoopInfo &HWLoopInfo) { + // Low-overhead branches are only supported in the 'low-overhead branch' + // extension of v8.1-m. + if (!ST->hasLOB() || DisableLowOverheadLoops) + return false; + + if (!SE.hasLoopInvariantBackedgeTakenCount(L)) + return false; + + const SCEV *BackedgeTakenCount = SE.getBackedgeTakenCount(L); + if (isa<SCEVCouldNotCompute>(BackedgeTakenCount)) + return false; + + const SCEV *TripCountSCEV = + SE.getAddExpr(BackedgeTakenCount, + SE.getOne(BackedgeTakenCount->getType())); + + // We need to store the trip count in LR, a 32-bit register. + if (SE.getUnsignedRangeMax(TripCountSCEV).getBitWidth() > 32) + return false; + + // Making a call will trash LR and clear LO_BRANCH_INFO, so there's little + // point in generating a hardware loop if that's going to happen. + auto MaybeCall = [this](Instruction &I) { + const ARMTargetLowering *TLI = getTLI(); + unsigned ISD = TLI->InstructionOpcodeToISD(I.getOpcode()); + EVT VT = TLI->getValueType(DL, I.getType(), true); + if (TLI->getOperationAction(ISD, VT) == TargetLowering::LibCall) + return true; + + // Check if an intrinsic will be lowered to a call and assume that any + // other CallInst will generate a bl. + if (auto *Call = dyn_cast<CallInst>(&I)) { + if (isa<IntrinsicInst>(Call)) { + if (const Function *F = Call->getCalledFunction()) + return isLoweredToCall(F); + } + return true; + } + + // FPv5 provides conversions between integer, double-precision, + // single-precision, and half-precision formats. + switch (I.getOpcode()) { + default: + break; + case Instruction::FPToSI: + case Instruction::FPToUI: + case Instruction::SIToFP: + case Instruction::UIToFP: + case Instruction::FPTrunc: + case Instruction::FPExt: + return !ST->hasFPARMv8Base(); + } + + // FIXME: Unfortunately the approach of checking the Operation Action does + // not catch all cases of Legalization that use library calls. Our + // Legalization step categorizes some transformations into library calls as + // Custom, Expand or even Legal when doing type legalization. So for now + // we have to special case for instance the SDIV of 64bit integers and the + // use of floating point emulation. + if (VT.isInteger() && VT.getSizeInBits() >= 64) { + switch (ISD) { + default: + break; + case ISD::SDIV: + case ISD::UDIV: + case ISD::SREM: + case ISD::UREM: + case ISD::SDIVREM: + case ISD::UDIVREM: + return true; + } + } + + // Assume all other non-float operations are supported. + if (!VT.isFloatingPoint()) + return false; + + // We'll need a library call to handle most floats when using soft. + if (TLI->useSoftFloat()) { + switch (I.getOpcode()) { + default: + return true; + case Instruction::Alloca: + case Instruction::Load: + case Instruction::Store: + case Instruction::Select: + case Instruction::PHI: + return false; + } + } + + // We'll need a libcall to perform double precision operations on a single + // precision only FPU. + if (I.getType()->isDoubleTy() && !ST->hasFP64()) + return true; + + // Likewise for half precision arithmetic. + if (I.getType()->isHalfTy() && !ST->hasFullFP16()) + return true; + + return false; + }; + + auto IsHardwareLoopIntrinsic = [](Instruction &I) { + if (auto *Call = dyn_cast<IntrinsicInst>(&I)) { + switch (Call->getIntrinsicID()) { + default: + break; + case Intrinsic::set_loop_iterations: + case Intrinsic::test_set_loop_iterations: + case Intrinsic::loop_decrement: + case Intrinsic::loop_decrement_reg: + return true; + } + } + return false; + }; + + // Scan the instructions to see if there's any that we know will turn into a + // call or if this loop is already a low-overhead loop. + auto ScanLoop = [&](Loop *L) { + for (auto *BB : L->getBlocks()) { + for (auto &I : *BB) { + if (MaybeCall(I) || IsHardwareLoopIntrinsic(I)) + return false; + } + } + return true; + }; + + // Visit inner loops. + for (auto Inner : *L) + if (!ScanLoop(Inner)) + return false; + + if (!ScanLoop(L)) + return false; + + // TODO: Check whether the trip count calculation is expensive. If L is the + // inner loop but we know it has a low trip count, calculating that trip + // count (in the parent loop) may be detrimental. + + LLVMContext &C = L->getHeader()->getContext(); + HWLoopInfo.CounterInReg = true; + HWLoopInfo.IsNestingLegal = false; + HWLoopInfo.PerformEntryTest = true; + HWLoopInfo.CountType = Type::getInt32Ty(C); + HWLoopInfo.LoopDecrement = ConstantInt::get(HWLoopInfo.CountType, 1); + return true; +} + void ARMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP) { // Only currently enable these preferences for M-Class cores. @@ -599,7 +857,7 @@ void ARMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, // Disable loop unrolling for Oz and Os. UP.OptSizeThreshold = 0; UP.PartialOptSizeThreshold = 0; - if (L->getHeader()->getParent()->optForSize()) + if (L->getHeader()->getParent()->hasOptSize()) return; // Only enable on Thumb-2 targets. @@ -645,6 +903,7 @@ void ARMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, UP.Partial = true; UP.Runtime = true; + UP.UpperBound = true; UP.UnrollRemainder = true; UP.DefaultUnrollRuntimeCount = 4; UP.UnrollAndJam = true; diff --git a/lib/Target/ARM/ARMTargetTransformInfo.h b/lib/Target/ARM/ARMTargetTransformInfo.h index 2dd143d48a15..52f6ea4a6e2f 100644 --- a/lib/Target/ARM/ARMTargetTransformInfo.h +++ b/lib/Target/ARM/ARMTargetTransformInfo.h @@ -1,9 +1,8 @@ //===- ARMTargetTransformInfo.h - ARM specific TTI --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -49,7 +48,7 @@ class ARMTTIImpl : public BasicTTIImplBase<ARMTTIImpl> { const ARMTargetLowering *TLI; // Currently the following features are excluded from InlineFeatureWhitelist. - // ModeThumb, FeatureNoARM, ModeSoftFloat, FeatureVFPOnlySP, FeatureD16 + // ModeThumb, FeatureNoARM, ModeSoftFloat, FeatureFP64, FeatureD32 // Depending on whether they are set or unset, different // instructions/registers are available. For example, inlining a callee with // -thumb-mode in a caller with +thumb-mode, may cause the assembler to @@ -94,6 +93,12 @@ public: bool enableInterleavedAccessVectorization() { return true; } + bool shouldFavorBackedgeIndex(const Loop *L) const { + if (L->getHeader()->getParent()->hasOptSize()) + return false; + return ST->isMClass() && ST->isThumb2() && L->getNumBlocks() == 1; + } + /// Floating-point computation using ARMv8 AArch32 Advanced /// SIMD instructions remains unchanged from ARMv7. Only AArch64 SIMD /// is IEEE-754 compliant, but it's not covered in this target. @@ -143,6 +148,8 @@ public: return ST->getMaxInterleaveFactor(); } + int getMemcpyCost(const Instruction *I); + int getShuffleCost(TTI::ShuffleKind Kind, Type *Tp, int Index, Type *SubTp); int getCastInstrCost(unsigned Opcode, Type *Dst, Type *Src, @@ -173,6 +180,12 @@ public: bool UseMaskForCond = false, bool UseMaskForGaps = false); + bool isLoweredToCall(const Function *F); + bool isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, + TargetLibraryInfo *LibInfo, + HardwareLoopInfo &HWLoopInfo); + void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP); diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 3832b0112b87..1da9452f1d22 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1,19 +1,20 @@ //===- ARMAsmParser.cpp - Parse ARM assembly to MCInst instructions -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ARMFeatures.h" -#include "InstPrinter/ARMInstPrinter.h" +#include "ARMBaseInstrInfo.h" #include "Utils/ARMBaseInfo.h" #include "MCTargetDesc/ARMAddressingModes.h" #include "MCTargetDesc/ARMBaseInfo.h" +#include "MCTargetDesc/ARMInstPrinter.h" #include "MCTargetDesc/ARMMCExpr.h" #include "MCTargetDesc/ARMMCTargetDesc.h" +#include "TargetInfo/ARMTargetInfo.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/None.h" @@ -69,6 +70,10 @@ using namespace llvm; +namespace llvm { +extern const MCInstrDesc ARMInsts[]; +} // end namespace llvm + namespace { enum class ImplicitItModeTy { Always, Never, ARMOnly, ThumbOnly }; @@ -90,6 +95,16 @@ static cl::opt<bool> AddBuildAttributes("arm-add-build-attributes", enum VectorLaneTy { NoLanes, AllLanes, IndexedLane }; +static inline unsigned extractITMaskBit(unsigned Mask, unsigned Position) { + // Position==0 means we're not in an IT block at all. Position==1 + // means we want the first state bit, which is always 0 (Then). + // Position==2 means we want the second state bit, stored at bit 3 + // of Mask, and so on downwards. So (5 - Position) will shift the + // right bit down to bit 0, including the always-0 bit at bit 4 for + // the mandatory initial Then. + return (Mask >> (5 - Position) & 1); +} + class UnwindContext { using Locs = SmallVector<SMLoc, 4>; @@ -165,6 +180,7 @@ public: } }; + class ARMAsmParser : public MCTargetAsmParser { const MCRegisterInfo *MRI; UnwindContext UC; @@ -225,11 +241,10 @@ class ARMAsmParser : public MCTargetAsmParser { } // Emit the IT instruction - unsigned Mask = getITMaskEncoding(); MCInst ITInst; ITInst.setOpcode(ARM::t2IT); ITInst.addOperand(MCOperand::createImm(ITState.Cond)); - ITInst.addOperand(MCOperand::createImm(Mask)); + ITInst.addOperand(MCOperand::createImm(ITState.Mask)); Out.EmitInstruction(ITInst, getSTI()); // Emit the conditonal instructions @@ -287,27 +302,10 @@ class ARMAsmParser : public MCTargetAsmParser { return MRI->getSubReg(QReg, ARM::dsub_0); } - // Get the encoding of the IT mask, as it will appear in an IT instruction. - unsigned getITMaskEncoding() { - assert(inITBlock()); - unsigned Mask = ITState.Mask; - unsigned TZ = countTrailingZeros(Mask); - if ((ITState.Cond & 1) == 0) { - assert(Mask && TZ <= 3 && "illegal IT mask value!"); - Mask ^= (0xE << TZ) & 0xF; - } - return Mask; - } - // Get the condition code corresponding to the current IT block slot. ARMCC::CondCodes currentITCond() { - unsigned MaskBit; - if (ITState.CurPosition == 1) - MaskBit = 1; - else - MaskBit = (ITState.Mask >> (5 - ITState.CurPosition)) & 1; - - return MaskBit ? ITState.Cond : ARMCC::getOppositeCondition(ITState.Cond); + unsigned MaskBit = extractITMaskBit(ITState.Mask, ITState.CurPosition); + return MaskBit ? ARMCC::getOppositeCondition(ITState.Cond) : ITState.Cond; } // Invert the condition of the current IT block slot without changing any @@ -337,7 +335,7 @@ class ARMAsmParser : public MCTargetAsmParser { // Keep any existing condition bits. NewMask |= ITState.Mask & (0xE << TZ); // Insert the new condition bit. - NewMask |= (Cond == ITState.Cond) << TZ; + NewMask |= (Cond != ITState.Cond) << TZ; // Move the trailing 1 down one bit. NewMask |= 1 << (TZ - 1); ITState.Mask = NewMask; @@ -352,9 +350,10 @@ class ARMAsmParser : public MCTargetAsmParser { ITState.IsExplicit = false; } - // Create a new explicit IT block with the given condition and mask. The mask - // should be in the parsed format, with a 1 implying 't', regardless of the - // low bit of the condition. + // Create a new explicit IT block with the given condition and mask. + // The mask should be in the format used in ARMOperand and + // MCOperand, with a 1 implying 'e', regardless of the low bit of + // the condition. void startExplicitITBlock(ARMCC::CondCodes Cond, unsigned Mask) { assert(!inITBlock()); ITState.Cond = Cond; @@ -363,6 +362,18 @@ class ARMAsmParser : public MCTargetAsmParser { ITState.IsExplicit = true; } + struct { + unsigned Mask : 4; + unsigned CurPosition; + } VPTState; + bool inVPTBlock() { return VPTState.CurPosition != ~0U; } + void forwardVPTPosition() { + if (!inVPTBlock()) return; + unsigned TZ = countTrailingZeros(VPTState.Mask); + if (++VPTState.CurPosition == 5 - TZ) + VPTState.CurPosition = ~0U; + } + void Note(SMLoc L, const Twine &Msg, SMRange Range = None) { return getParser().Note(L, Msg, Range); } @@ -383,7 +394,7 @@ class ARMAsmParser : public MCTargetAsmParser { int tryParseRegister(); bool tryParseRegisterWithWriteBack(OperandVector &); int tryParseShiftRegister(OperandVector &); - bool parseRegisterList(OperandVector &); + bool parseRegisterList(OperandVector &, bool EnforceOrder = true); bool parseMemory(OperandVector &); bool parseOperand(OperandVector &, StringRef Mnemonic); bool parsePrefix(ARMMCExpr::VariantKind &RefKind); @@ -421,12 +432,15 @@ class ARMAsmParser : public MCTargetAsmParser { bool parseDirectiveAlign(SMLoc L); bool parseDirectiveThumbSet(SMLoc L); - StringRef splitMnemonic(StringRef Mnemonic, unsigned &PredicationCode, - bool &CarrySetting, unsigned &ProcessorIMod, - StringRef &ITMask); - void getMnemonicAcceptInfo(StringRef Mnemonic, StringRef FullInst, - bool &CanAcceptCarrySet, - bool &CanAcceptPredicationCode); + bool isMnemonicVPTPredicable(StringRef Mnemonic, StringRef ExtraToken); + StringRef splitMnemonic(StringRef Mnemonic, StringRef ExtraToken, + unsigned &PredicationCode, + unsigned &VPTPredicationCode, bool &CarrySetting, + unsigned &ProcessorIMod, StringRef &ITMask); + void getMnemonicAcceptInfo(StringRef Mnemonic, StringRef ExtraToken, + StringRef FullInst, bool &CanAcceptCarrySet, + bool &CanAcceptPredicationCode, + bool &CanAcceptVPTPredicationCode); void tryConvertingToTwoOperandForm(StringRef Mnemonic, bool CarrySetting, OperandVector &Operands); @@ -478,7 +492,15 @@ class ARMAsmParser : public MCTargetAsmParser { bool hasV8MMainline() const { return getSTI().getFeatureBits()[ARM::HasV8MMainlineOps]; } - + bool hasV8_1MMainline() const { + return getSTI().getFeatureBits()[ARM::HasV8_1MMainlineOps]; + } + bool hasMVE() const { + return getSTI().getFeatureBits()[ARM::HasMVEIntegerOps]; + } + bool hasMVEFloat() const { + return getSTI().getFeatureBits()[ARM::HasMVEFloatOps]; + } bool has8MSecExt() const { return getSTI().getFeatureBits()[ARM::Feature8MSecExt]; } @@ -491,8 +513,8 @@ class ARMAsmParser : public MCTargetAsmParser { return getSTI().getFeatureBits()[ARM::FeatureDSP]; } - bool hasD16() const { - return getSTI().getFeatureBits()[ARM::FeatureD16]; + bool hasD32() const { + return getSTI().getFeatureBits()[ARM::FeatureD32]; } bool hasV8_1aOps() const { @@ -505,7 +527,7 @@ class ARMAsmParser : public MCTargetAsmParser { void SwitchMode() { MCSubtargetInfo &STI = copySTI(); - uint64_t FB = ComputeAvailableFeatures(STI.ToggleFeature(ARM::ModeThumb)); + auto FB = ComputeAvailableFeatures(STI.ToggleFeature(ARM::ModeThumb)); setAvailableFeatures(FB); } @@ -556,11 +578,13 @@ class ARMAsmParser : public MCTargetAsmParser { // Asm Match Converter Methods void cvtThumbMultiply(MCInst &Inst, const OperandVector &); void cvtThumbBranches(MCInst &Inst, const OperandVector &); + void cvtMVEVMOVQtoDReg(MCInst &Inst, const OperandVector &); bool validateInstruction(MCInst &Inst, const OperandVector &Ops); bool processInstruction(MCInst &Inst, const OperandVector &Ops, MCStreamer &Out); bool shouldOmitCCOutOperand(StringRef Mnemonic, OperandVector &Operands); bool shouldOmitPredicateOperand(StringRef Mnemonic, OperandVector &Operands); + bool shouldOmitVectorPredicateOperand(StringRef Mnemonic, OperandVector &Operands); bool isITBlockTerminator(MCInst &Inst) const; void fixupGNULDRDAlias(StringRef Mnemonic, OperandVector &Operands); bool validateLDRDSTRD(MCInst &Inst, const OperandVector &Operands, @@ -597,6 +621,8 @@ public: // Not in an ITBlock to start with. ITState.CurPosition = ~0U; + VPTState.CurPosition = ~0U; + NextSymbolIsThumb = false; } @@ -642,6 +668,7 @@ public: class ARMOperand : public MCParsedAsmOperand { enum KindTy { k_CondCode, + k_VPTPred, k_CCOut, k_ITCondMask, k_CoprocNum, @@ -659,8 +686,11 @@ class ARMOperand : public MCParsedAsmOperand { k_VectorIndex, k_Register, k_RegisterList, + k_RegisterListWithAPSR, k_DPRRegisterList, k_SPRRegisterList, + k_FPSRegisterListWithVPR, + k_FPDRegisterListWithVPR, k_VectorList, k_VectorListAllLanes, k_VectorListIndexed, @@ -681,6 +711,10 @@ class ARMOperand : public MCParsedAsmOperand { ARMCC::CondCodes Val; }; + struct VCCOp { + ARMVCC::VPTCodes Val; + }; + struct CopOp { unsigned Val; }; @@ -797,6 +831,7 @@ class ARMOperand : public MCParsedAsmOperand { union { struct CCOp CC; + struct VCCOp VCC; struct CopOp Cop; struct CoprocOptionOp CoprocOption; struct MBOptOp MBOpt; @@ -845,6 +880,11 @@ public: return CC.Val; } + ARMVCC::VPTCodes getVPTPred() const { + assert(isVPTPred() && "Invalid access!"); + return VCC.Val; + } + unsigned getCoproc() const { assert((Kind == k_CoprocNum || Kind == k_CoprocReg) && "Invalid access!"); return Cop.Val; @@ -861,8 +901,11 @@ public: } const SmallVectorImpl<unsigned> &getRegList() const { - assert((Kind == k_RegisterList || Kind == k_DPRRegisterList || - Kind == k_SPRRegisterList) && "Invalid access!"); + assert((Kind == k_RegisterList || Kind == k_RegisterListWithAPSR || + Kind == k_DPRRegisterList || Kind == k_SPRRegisterList || + Kind == k_FPSRegisterListWithVPR || + Kind == k_FPDRegisterListWithVPR) && + "Invalid access!"); return Registers; } @@ -915,6 +958,7 @@ public: bool isCoprocReg() const { return Kind == k_CoprocReg; } bool isCoprocOption() const { return Kind == k_CoprocOption; } bool isCondCode() const { return Kind == k_CondCode; } + bool isVPTPred() const { return Kind == k_VPTPred; } bool isCCOut() const { return Kind == k_CCOut; } bool isITMask() const { return Kind == k_ITCondMask; } bool isITCondCode() const { return Kind == k_CondCode; } @@ -970,6 +1014,18 @@ public: return false; } + // checks whether this operand is an offset suitable for the LE / + // LETP instructions in Arm v8.1M + bool isLEOffset() const { + if (!isImm()) return false; + if (isa<MCSymbolRefExpr>(Imm.Val)) return true; + if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val)) { + int64_t Val = CE->getValue(); + return Val < 0 && Val >= -4094 && (Val & 1) == 0; + } + return false; + } + // checks whether this operand is a memory operand computed as an offset // applied to PC. the offset may have 8 bits of magnitude and is represented // with two bits of shift. textually it may be either [pc, #imm], #imm or @@ -982,7 +1038,7 @@ public: if (!CE) return false; Val = CE->getValue(); } - else if (isMem()) { + else if (isGPRMem()) { if(!Memory.OffsetImm || Memory.OffsetRegNum) return false; if(Memory.BaseRegNum != ARM::PC) return false; Val = Memory.OffsetImm->getValue(); @@ -1016,7 +1072,14 @@ public: int64_t Value = CE->getValue(); return ((Value & 3) == 0) && Value >= N && Value <= M; } - + template<int64_t N, int64_t M> + bool isImmediateS2() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + int64_t Value = CE->getValue(); + return ((Value & 1) == 0) && Value >= N && Value <= M; + } bool isFBits16() const { return isImmediate<0, 17>(); } @@ -1026,6 +1089,21 @@ public: bool isImm8s4() const { return isImmediateS4<-1020, 1020>(); } + bool isImm7s4() const { + return isImmediateS4<-508, 508>(); + } + bool isImm7Shift0() const { + return isImmediate<-127, 127>(); + } + bool isImm7Shift1() const { + return isImmediateS2<-255, 255>(); + } + bool isImm7Shift2() const { + return isImmediateS4<-511, 511>(); + } + bool isImm7() const { + return isImmediate<-127, 127>(); + } bool isImm0_1020s4() const { return isImmediateS4<0, 1020>(); } @@ -1098,6 +1176,34 @@ public: return isImmediate<1, 33>(); } + template<int shift> + bool isExpImmValue(uint64_t Value) const { + uint64_t mask = (1 << shift) - 1; + if ((Value & mask) != 0 || (Value >> shift) > 0xff) + return false; + return true; + } + + template<int shift> + bool isExpImm() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + + return isExpImmValue<shift>(CE->getValue()); + } + + template<int shift, int size> + bool isInvertedExpImm() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + + uint64_t OriginalValue = CE->getValue(); + uint64_t InvertedValue = OriginalValue ^ (((uint64_t)1 << size) - 1); + return isExpImmValue<shift>(InvertedValue); + } + bool isPKHLSLImm() const { return isImmediate<0, 32>(); } @@ -1167,13 +1273,34 @@ public: bool isReg() const override { return Kind == k_Register; } bool isRegList() const { return Kind == k_RegisterList; } + bool isRegListWithAPSR() const { + return Kind == k_RegisterListWithAPSR || Kind == k_RegisterList; + } bool isDPRRegList() const { return Kind == k_DPRRegisterList; } bool isSPRRegList() const { return Kind == k_SPRRegisterList; } + bool isFPSRegListWithVPR() const { return Kind == k_FPSRegisterListWithVPR; } + bool isFPDRegListWithVPR() const { return Kind == k_FPDRegisterListWithVPR; } bool isToken() const override { return Kind == k_Token; } bool isMemBarrierOpt() const { return Kind == k_MemBarrierOpt; } bool isInstSyncBarrierOpt() const { return Kind == k_InstSyncBarrierOpt; } bool isTraceSyncBarrierOpt() const { return Kind == k_TraceSyncBarrierOpt; } bool isMem() const override { + return isGPRMem() || isMVEMem(); + } + bool isMVEMem() const { + if (Kind != k_Memory) + return false; + if (Memory.BaseRegNum && + !ARMMCRegisterClasses[ARM::GPRRegClassID].contains(Memory.BaseRegNum) && + !ARMMCRegisterClasses[ARM::MQPRRegClassID].contains(Memory.BaseRegNum)) + return false; + if (Memory.OffsetRegNum && + !ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + Memory.OffsetRegNum)) + return false; + return true; + } + bool isGPRMem() const { if (Kind != k_Memory) return false; if (Memory.BaseRegNum && @@ -1198,6 +1325,16 @@ public: RegShiftedImm.SrcReg); } bool isRotImm() const { return Kind == k_RotateImmediate; } + + template<unsigned Min, unsigned Max> + bool isPowerTwoInRange() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + int64_t Value = CE->getValue(); + return Value > 0 && countPopulation((uint64_t)Value) == 1 && + Value >= Min && Value <= Max; + } bool isModImm() const { return Kind == k_ModifiedImmediate; } bool isModImmNot() const { @@ -1243,14 +1380,50 @@ public: return isPostIdxRegShifted() && PostIdxReg.ShiftTy == ARM_AM::no_shift; } bool isMemNoOffset(bool alignOK = false, unsigned Alignment = 0) const { - if (!isMem()) + if (!isGPRMem()) + return false; + // No offset of any kind. + return Memory.OffsetRegNum == 0 && Memory.OffsetImm == nullptr && + (alignOK || Memory.Alignment == Alignment); + } + bool isMemNoOffsetT2(bool alignOK = false, unsigned Alignment = 0) const { + if (!isGPRMem()) + return false; + + if (!ARMMCRegisterClasses[ARM::GPRnopcRegClassID].contains( + Memory.BaseRegNum)) + return false; + + // No offset of any kind. + return Memory.OffsetRegNum == 0 && Memory.OffsetImm == nullptr && + (alignOK || Memory.Alignment == Alignment); + } + bool isMemNoOffsetT2NoSp(bool alignOK = false, unsigned Alignment = 0) const { + if (!isGPRMem()) + return false; + + if (!ARMMCRegisterClasses[ARM::rGPRRegClassID].contains( + Memory.BaseRegNum)) return false; + + // No offset of any kind. + return Memory.OffsetRegNum == 0 && Memory.OffsetImm == nullptr && + (alignOK || Memory.Alignment == Alignment); + } + bool isMemNoOffsetT(bool alignOK = false, unsigned Alignment = 0) const { + if (!isGPRMem()) + return false; + + if (!ARMMCRegisterClasses[ARM::tGPRRegClassID].contains( + Memory.BaseRegNum)) + return false; + // No offset of any kind. return Memory.OffsetRegNum == 0 && Memory.OffsetImm == nullptr && (alignOK || Memory.Alignment == Alignment); } bool isMemPCRelImm12() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Base register must be PC. if (Memory.BaseRegNum != ARM::PC) @@ -1337,7 +1510,7 @@ public: } bool isAddrMode2() const { - if (!isMem() || Memory.Alignment != 0) return false; + if (!isGPRMem() || Memory.Alignment != 0) return false; // Check for register offset. if (Memory.OffsetRegNum) return true; // Immediate offset in range [-4095, 4095]. @@ -1362,7 +1535,7 @@ public: // and we reject it. if (isImm() && !isa<MCConstantExpr>(getImm())) return true; - if (!isMem() || Memory.Alignment != 0) return false; + if (!isGPRMem() || Memory.Alignment != 0) return false; // No shifts are legal for AM3. if (Memory.ShiftType != ARM_AM::no_shift) return false; // Check for register offset. @@ -1396,7 +1569,7 @@ public: // and we reject it. if (isImm() && !isa<MCConstantExpr>(getImm())) return true; - if (!isMem() || Memory.Alignment != 0) return false; + if (!isGPRMem() || Memory.Alignment != 0) return false; // Check for register offset. if (Memory.OffsetRegNum) return false; // Immediate offset in range [-1020, 1020] and a multiple of 4. @@ -1412,7 +1585,7 @@ public: // and we reject it. if (isImm() && !isa<MCConstantExpr>(getImm())) return true; - if (!isMem() || Memory.Alignment != 0) return false; + if (!isGPRMem() || Memory.Alignment != 0) return false; // Check for register offset. if (Memory.OffsetRegNum) return false; // Immediate offset in range [-510, 510] and a multiple of 2. @@ -1423,14 +1596,14 @@ public: } bool isMemTBB() const { - if (!isMem() || !Memory.OffsetRegNum || Memory.isNegative || + if (!isGPRMem() || !Memory.OffsetRegNum || Memory.isNegative || Memory.ShiftType != ARM_AM::no_shift || Memory.Alignment != 0) return false; return true; } bool isMemTBH() const { - if (!isMem() || !Memory.OffsetRegNum || Memory.isNegative || + if (!isGPRMem() || !Memory.OffsetRegNum || Memory.isNegative || Memory.ShiftType != ARM_AM::lsl || Memory.ShiftImm != 1 || Memory.Alignment != 0 ) return false; @@ -1438,13 +1611,13 @@ public: } bool isMemRegOffset() const { - if (!isMem() || !Memory.OffsetRegNum || Memory.Alignment != 0) + if (!isGPRMem() || !Memory.OffsetRegNum || Memory.Alignment != 0) return false; return true; } bool isT2MemRegOffset() const { - if (!isMem() || !Memory.OffsetRegNum || Memory.isNegative || + if (!isGPRMem() || !Memory.OffsetRegNum || Memory.isNegative || Memory.Alignment != 0 || Memory.BaseRegNum == ARM::PC) return false; // Only lsl #{0, 1, 2, 3} allowed. @@ -1458,7 +1631,7 @@ public: bool isMemThumbRR() const { // Thumb reg+reg addressing is simple. Just two registers, a base and // an offset. No shifts, negations or any other complicating factors. - if (!isMem() || !Memory.OffsetRegNum || Memory.isNegative || + if (!isGPRMem() || !Memory.OffsetRegNum || Memory.isNegative || Memory.ShiftType != ARM_AM::no_shift || Memory.Alignment != 0) return false; return isARMLowRegister(Memory.BaseRegNum) && @@ -1466,7 +1639,7 @@ public: } bool isMemThumbRIs4() const { - if (!isMem() || Memory.OffsetRegNum != 0 || + if (!isGPRMem() || Memory.OffsetRegNum != 0 || !isARMLowRegister(Memory.BaseRegNum) || Memory.Alignment != 0) return false; // Immediate offset, multiple of 4 in range [0, 124]. @@ -1476,7 +1649,7 @@ public: } bool isMemThumbRIs2() const { - if (!isMem() || Memory.OffsetRegNum != 0 || + if (!isGPRMem() || Memory.OffsetRegNum != 0 || !isARMLowRegister(Memory.BaseRegNum) || Memory.Alignment != 0) return false; // Immediate offset, multiple of 4 in range [0, 62]. @@ -1486,7 +1659,7 @@ public: } bool isMemThumbRIs1() const { - if (!isMem() || Memory.OffsetRegNum != 0 || + if (!isGPRMem() || Memory.OffsetRegNum != 0 || !isARMLowRegister(Memory.BaseRegNum) || Memory.Alignment != 0) return false; // Immediate offset in range [0, 31]. @@ -1496,7 +1669,7 @@ public: } bool isMemThumbSPI() const { - if (!isMem() || Memory.OffsetRegNum != 0 || + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.BaseRegNum != ARM::SP || Memory.Alignment != 0) return false; // Immediate offset, multiple of 4 in range [0, 1020]. @@ -1511,7 +1684,7 @@ public: // and we reject it. if (isImm() && !isa<MCConstantExpr>(getImm())) return true; - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Immediate offset a multiple of 4 in range [-1020, 1020]. if (!Memory.OffsetImm) return true; @@ -1520,9 +1693,24 @@ public: return (Val >= -1020 && Val <= 1020 && (Val & 3) == 0) || Val == std::numeric_limits<int32_t>::min(); } - + bool isMemImm7s4Offset() const { + // If we have an immediate that's not a constant, treat it as a label + // reference needing a fixup. If it is a constant, it's something else + // and we reject it. + if (isImm() && !isa<MCConstantExpr>(getImm())) + return true; + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0 || + !ARMMCRegisterClasses[ARM::GPRnopcRegClassID].contains( + Memory.BaseRegNum)) + return false; + // Immediate offset a multiple of 4 in range [-508, 508]. + if (!Memory.OffsetImm) return true; + int64_t Val = Memory.OffsetImm->getValue(); + // Special case, #-0 is INT32_MIN. + return (Val >= -508 && Val <= 508 && (Val & 3) == 0) || Val == INT32_MIN; + } bool isMemImm0_1020s4Offset() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Immediate offset a multiple of 4 in range [0, 1020]. if (!Memory.OffsetImm) return true; @@ -1531,7 +1719,7 @@ public: } bool isMemImm8Offset() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Base reg of PC isn't allowed for these encodings. if (Memory.BaseRegNum == ARM::PC) return false; @@ -1542,8 +1730,81 @@ public: (Val > -256 && Val < 256); } + template<unsigned Bits, unsigned RegClassID> + bool isMemImm7ShiftedOffset() const { + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0 || + !ARMMCRegisterClasses[RegClassID].contains(Memory.BaseRegNum)) + return false; + + // Expect an immediate offset equal to an element of the range + // [-127, 127], shifted left by Bits. + + if (!Memory.OffsetImm) return true; + int64_t Val = Memory.OffsetImm->getValue(); + + // INT32_MIN is a special-case value (indicating the encoding with + // zero offset and the subtract bit set) + if (Val == INT32_MIN) + return true; + + unsigned Divisor = 1U << Bits; + + // Check that the low bits are zero + if (Val % Divisor != 0) + return false; + + // Check that the remaining offset is within range. + Val /= Divisor; + return (Val >= -127 && Val <= 127); + } + + template <int shift> bool isMemRegRQOffset() const { + if (!isMVEMem() || Memory.OffsetImm != 0 || Memory.Alignment != 0) + return false; + + if (!ARMMCRegisterClasses[ARM::GPRnopcRegClassID].contains( + Memory.BaseRegNum)) + return false; + if (!ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + Memory.OffsetRegNum)) + return false; + + if (shift == 0 && Memory.ShiftType != ARM_AM::no_shift) + return false; + + if (shift > 0 && + (Memory.ShiftType != ARM_AM::uxtw || Memory.ShiftImm != shift)) + return false; + + return true; + } + + template <int shift> bool isMemRegQOffset() const { + if (!isMVEMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + return false; + + if (!ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + Memory.BaseRegNum)) + return false; + + if(!Memory.OffsetImm) return true; + static_assert(shift < 56, + "Such that we dont shift by a value higher than 62"); + int64_t Val = Memory.OffsetImm->getValue(); + + // The value must be a multiple of (1 << shift) + if ((Val & ((1U << shift) - 1)) != 0) + return false; + + // And be in the right range, depending on the amount that it is shifted + // by. Shift 0, is equal to 7 unsigned bits, the sign bit is set + // separately. + int64_t Range = (1U << (7+shift)) - 1; + return (Val == INT32_MIN) || (Val > -Range && Val < Range); + } + bool isMemPosImm8Offset() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Immediate offset in range [0, 255]. if (!Memory.OffsetImm) return true; @@ -1552,7 +1813,7 @@ public: } bool isMemNegImm8Offset() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Base reg of PC isn't allowed for these encodings. if (Memory.BaseRegNum == ARM::PC) return false; @@ -1564,7 +1825,7 @@ public: } bool isMemUImm12Offset() const { - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Immediate offset in range [0, 4095]. if (!Memory.OffsetImm) return true; @@ -1580,7 +1841,7 @@ public: if (isImm() && !isa<MCConstantExpr>(getImm())) return true; - if (!isMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) + if (!isGPRMem() || Memory.OffsetRegNum != 0 || Memory.Alignment != 0) return false; // Immediate offset in range [-4095, 4095]. if (!Memory.OffsetImm) return true; @@ -1631,6 +1892,12 @@ public: return VectorList.Count == 1; } + bool isVecListTwoMQ() const { + return isSingleSpacedVectorList() && VectorList.Count == 2 && + ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + VectorList.RegNum); + } + bool isVecListDPair() const { if (!isSingleSpacedVectorList()) return false; return (ARMMCRegisterClasses[ARM::DPairRegClassID] @@ -1664,6 +1931,12 @@ public: return VectorList.Count == 4; } + bool isVecListFourMQ() const { + return isSingleSpacedVectorList() && VectorList.Count == 4 && + ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + VectorList.RegNum); + } + bool isSingleSpacedVectorAllLanes() const { return Kind == k_VectorListAllLanes && !VectorList.isDoubleSpaced; } @@ -1806,23 +2079,24 @@ public: return VectorList.Count == 4 && VectorList.LaneIndex <= 1; } - bool isVectorIndex8() const { - if (Kind != k_VectorIndex) return false; - return VectorIndex.Val < 8; - } + bool isVectorIndex() const { return Kind == k_VectorIndex; } - bool isVectorIndex16() const { + template <unsigned NumLanes> + bool isVectorIndexInRange() const { if (Kind != k_VectorIndex) return false; - return VectorIndex.Val < 4; + return VectorIndex.Val < NumLanes; } - bool isVectorIndex32() const { - if (Kind != k_VectorIndex) return false; - return VectorIndex.Val < 2; - } - bool isVectorIndex64() const { + bool isVectorIndex8() const { return isVectorIndexInRange<8>(); } + bool isVectorIndex16() const { return isVectorIndexInRange<4>(); } + bool isVectorIndex32() const { return isVectorIndexInRange<2>(); } + bool isVectorIndex64() const { return isVectorIndexInRange<1>(); } + + template<int PermittedValue, int OtherPermittedValue> + bool isMVEPairVectorIndex() const { if (Kind != k_VectorIndex) return false; - return VectorIndex.Val < 1; + return VectorIndex.Val == PermittedValue || + VectorIndex.Val == OtherPermittedValue; } bool isNEONi8splat() const { @@ -1992,6 +2266,51 @@ public: return (Value % Angle == Remainder && Value <= 270); } + bool isMVELongShift() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + // Must be a constant. + if (!CE) return false; + uint64_t Value = CE->getValue(); + return Value >= 1 && Value <= 32; + } + + bool isITCondCodeNoAL() const { + if (!isITCondCode()) return false; + ARMCC::CondCodes CC = getCondCode(); + return CC != ARMCC::AL; + } + + bool isITCondCodeRestrictedI() const { + if (!isITCondCode()) + return false; + ARMCC::CondCodes CC = getCondCode(); + return CC == ARMCC::EQ || CC == ARMCC::NE; + } + + bool isITCondCodeRestrictedS() const { + if (!isITCondCode()) + return false; + ARMCC::CondCodes CC = getCondCode(); + return CC == ARMCC::LT || CC == ARMCC::GT || CC == ARMCC::LE || + CC == ARMCC::GE; + } + + bool isITCondCodeRestrictedU() const { + if (!isITCondCode()) + return false; + ARMCC::CondCodes CC = getCondCode(); + return CC == ARMCC::HS || CC == ARMCC::HI; + } + + bool isITCondCodeRestrictedFP() const { + if (!isITCondCode()) + return false; + ARMCC::CondCodes CC = getCondCode(); + return CC == ARMCC::EQ || CC == ARMCC::NE || CC == ARMCC::LT || + CC == ARMCC::GT || CC == ARMCC::LE || CC == ARMCC::GE; + } + void addExpr(MCInst &Inst, const MCExpr *Expr) const { // Add as immediates when possible. Null MCExpr = 0. if (!Expr) @@ -2019,6 +2338,30 @@ public: Inst.addOperand(MCOperand::createReg(RegNum)); } + void addVPTPredNOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(unsigned(getVPTPred()))); + unsigned RegNum = getVPTPred() == ARMVCC::None ? 0: ARM::P0; + Inst.addOperand(MCOperand::createReg(RegNum)); + } + + void addVPTPredROperands(MCInst &Inst, unsigned N) const { + assert(N == 3 && "Invalid number of operands!"); + addVPTPredNOperands(Inst, N-1); + unsigned RegNum; + if (getVPTPred() == ARMVCC::None) { + RegNum = 0; + } else { + unsigned NextOpIndex = Inst.getNumOperands(); + const MCInstrDesc &MCID = ARMInsts[Inst.getOpcode()]; + int TiedOp = MCID.getOperandConstraint(NextOpIndex, MCOI::TIED_TO); + assert(TiedOp >= 0 && + "Inactive register in vpred_r is not tied to an output!"); + RegNum = Inst.getOperand(TiedOp).getReg(); + } + Inst.addOperand(MCOperand::createReg(RegNum)); + } + void addCoprocNumOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(getCoproc())); @@ -2044,6 +2387,11 @@ public: Inst.addOperand(MCOperand::createImm(unsigned(getCondCode()))); } + void addITCondCodeInvOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(unsigned(ARMCC::getOppositeCondition(getCondCode())))); + } + void addCCOutOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createReg(getReg())); @@ -2089,6 +2437,14 @@ public: Inst.addOperand(MCOperand::createReg(*I)); } + void addRegListWithAPSROperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const SmallVectorImpl<unsigned> &RegList = getRegList(); + for (SmallVectorImpl<unsigned>::const_iterator + I = RegList.begin(), E = RegList.end(); I != E; ++I) + Inst.addOperand(MCOperand::createReg(*I)); + } + void addDPRRegListOperands(MCInst &Inst, unsigned N) const { addRegListOperands(Inst, N); } @@ -2097,6 +2453,14 @@ public: addRegListOperands(Inst, N); } + void addFPSRegListWithVPROperands(MCInst &Inst, unsigned N) const { + addRegListOperands(Inst, N); + } + + void addFPDRegListWithVPROperands(MCInst &Inst, unsigned N) const { + addRegListOperands(Inst, N); + } + void addRotImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // Encoded as val>>3. The printer handles display as 8, 16, 24. @@ -2184,6 +2548,42 @@ public: Inst.addOperand(MCOperand::createImm(CE->getValue())); } + void addImm7s4Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + // FIXME: We really want to scale the value here, but the VSTR/VLDR_VSYSR + // instruction don't encode operands that way yet. + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + + void addImm7Shift0Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + assert(CE != nullptr && "Invalid operand type!"); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + + void addImm7Shift1Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + assert(CE != nullptr && "Invalid operand type!"); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + + void addImm7Shift2Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + assert(CE != nullptr && "Invalid operand type!"); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + + void addImm7Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + assert(CE != nullptr && "Invalid operand type!"); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + void addImm0_1020s4Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate is scaled by four in the encoding and is stored @@ -2293,7 +2693,7 @@ public: return; } - assert(isMem() && "Unknown value type!"); + assert(isGPRMem() && "Unknown value type!"); assert(isa<MCConstantExpr>(Memory.OffsetImm) && "Unknown value type!"); Inst.addOperand(MCOperand::createImm(Memory.OffsetImm->getValue())); } @@ -2318,6 +2718,21 @@ public: Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); } + void addMemNoOffsetT2Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); + } + + void addMemNoOffsetT2NoSpOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); + } + + void addMemNoOffsetTOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); + } + void addMemPCRelImm12Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); int32_t Imm = Memory.OffsetImm->getValue(); @@ -2535,6 +2950,22 @@ public: Inst.addOperand(MCOperand::createImm(Val)); } + void addMemImm7s4OffsetOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + // If we have an immediate that's not a constant, treat it as a label + // reference needing a fixup. If it is a constant, it's something else + // and we reject it. + if (isImm()) { + Inst.addOperand(MCOperand::createExpr(getImm())); + Inst.addOperand(MCOperand::createImm(0)); + return; + } + + int64_t Val = Memory.OffsetImm ? Memory.OffsetImm->getValue() : 0; + Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); + Inst.addOperand(MCOperand::createImm(Val)); + } + void addMemImm0_1020s4OffsetOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); // The lower two bits are always zero and as such are not encoded. @@ -2543,19 +2974,17 @@ public: Inst.addOperand(MCOperand::createImm(Val)); } - void addMemImm8OffsetOperands(MCInst &Inst, unsigned N) const { + void addMemImmOffsetOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); int64_t Val = Memory.OffsetImm ? Memory.OffsetImm->getValue() : 0; Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); Inst.addOperand(MCOperand::createImm(Val)); } - void addMemPosImm8OffsetOperands(MCInst &Inst, unsigned N) const { - addMemImm8OffsetOperands(Inst, N); - } - - void addMemNegImm8OffsetOperands(MCInst &Inst, unsigned N) const { - addMemImm8OffsetOperands(Inst, N); + void addMemRegRQOffsetOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(Memory.BaseRegNum)); + Inst.addOperand(MCOperand::createReg(Memory.OffsetRegNum)); } void addMemUImm12OffsetOperands(MCInst &Inst, unsigned N) const { @@ -2699,6 +3128,12 @@ public: Inst.addOperand(MCOperand::createImm(Imm)); } + void addPowerTwoOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + Inst.addOperand(MCOperand::createImm(CE->getValue())); + } + void addMSRMaskOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(unsigned(getMSRMask()))); @@ -2719,6 +3154,37 @@ public: Inst.addOperand(MCOperand::createReg(VectorList.RegNum)); } + void addMVEVecListOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + + // When we come here, the VectorList field will identify a range + // of q-registers by its base register and length, and it will + // have already been error-checked to be the expected length of + // range and contain only q-regs in the range q0-q7. So we can + // count on the base register being in the range q0-q6 (for 2 + // regs) or q0-q4 (for 4) + // + // The MVE instructions taking a register range of this kind will + // need an operand in the QQPR or QQQQPR class, representing the + // entire range as a unit. So we must translate into that class, + // by finding the index of the base register in the MQPR reg + // class, and returning the super-register at the corresponding + // index in the target class. + + const MCRegisterClass *RC_in = &ARMMCRegisterClasses[ARM::MQPRRegClassID]; + const MCRegisterClass *RC_out = (VectorList.Count == 2) ? + &ARMMCRegisterClasses[ARM::QQPRRegClassID] : + &ARMMCRegisterClasses[ARM::QQQQPRRegClassID]; + + unsigned I, E = RC_out->getNumRegs(); + for (I = 0; I < E; I++) + if (RC_in->getRegister(I) == VectorList.RegNum) + break; + assert(I < E && "Invalid vector list start register!"); + + Inst.addOperand(MCOperand::createReg(RC_out->getRegister(I))); + } + void addVecListIndexedOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createReg(VectorList.RegNum)); @@ -2745,6 +3211,16 @@ public: Inst.addOperand(MCOperand::createImm(getVectorIndex())); } + void addMVEVectorIndexOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(getVectorIndex())); + } + + void addMVEPairVectorIndexOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(getVectorIndex())); + } + void addNEONi8splatOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate encodes the type of constant as well as the value. @@ -2913,6 +3389,15 @@ public: return Op; } + static std::unique_ptr<ARMOperand> CreateVPTPred(ARMVCC::VPTCodes CC, + SMLoc S) { + auto Op = make_unique<ARMOperand>(k_VPTPred); + Op->VCC.Val = CC; + Op->StartLoc = S; + Op->EndLoc = S; + return Op; + } + static std::unique_ptr<ARMOperand> CreateCoprocNum(unsigned CopVal, SMLoc S) { auto Op = make_unique<ARMOperand>(k_CoprocNum); Op->Cop.Val = CopVal; @@ -3044,19 +3529,31 @@ public: assert(Regs.size() > 0 && "RegList contains no registers?"); KindTy Kind = k_RegisterList; - if (ARMMCRegisterClasses[ARM::DPRRegClassID].contains(Regs.front().second)) - Kind = k_DPRRegisterList; - else if (ARMMCRegisterClasses[ARM::SPRRegClassID]. - contains(Regs.front().second)) - Kind = k_SPRRegisterList; + if (ARMMCRegisterClasses[ARM::DPRRegClassID].contains( + Regs.front().second)) { + if (Regs.back().second == ARM::VPR) + Kind = k_FPDRegisterListWithVPR; + else + Kind = k_DPRRegisterList; + } else if (ARMMCRegisterClasses[ARM::SPRRegClassID].contains( + Regs.front().second)) { + if (Regs.back().second == ARM::VPR) + Kind = k_FPSRegisterListWithVPR; + else + Kind = k_SPRRegisterList; + } // Sort based on the register encoding values. array_pod_sort(Regs.begin(), Regs.end()); + if (Kind == k_RegisterList && Regs.back().second == ARM::APSR) + Kind = k_RegisterListWithAPSR; + auto Op = make_unique<ARMOperand>(Kind); for (SmallVectorImpl<std::pair<unsigned, unsigned>>::const_iterator I = Regs.begin(), E = Regs.end(); I != E; ++I) Op->Registers.push_back(I->second); + Op->StartLoc = StartLoc; Op->EndLoc = EndLoc; return Op; @@ -3217,15 +3714,18 @@ void ARMOperand::print(raw_ostream &OS) const { case k_CondCode: OS << "<ARMCC::" << ARMCondCodeToString(getCondCode()) << ">"; break; + case k_VPTPred: + OS << "<ARMVCC::" << ARMVPTPredToString(getVPTPred()) << ">"; + break; case k_CCOut: OS << "<ccout " << RegName(getReg()) << ">"; break; case k_ITCondMask: { static const char *const MaskStr[] = { - "(invalid)", "(teee)", "(tee)", "(teet)", - "(te)", "(tete)", "(tet)", "(tett)", - "(t)", "(ttee)", "(tte)", "(ttet)", - "(tt)", "(ttte)", "(ttt)", "(tttt)" + "(invalid)", "(tttt)", "(ttt)", "(ttte)", + "(tt)", "(ttet)", "(tte)", "(ttee)", + "(t)", "(tett)", "(tet)", "(tete)", + "(te)", "(teet)", "(tee)", "(teee)", }; assert((ITMask.Mask & 0xf) == ITMask.Mask); OS << "<it-mask " << MaskStr[ITMask.Mask] << ">"; @@ -3324,8 +3824,11 @@ void ARMOperand::print(raw_ostream &OS) const { << ", width: " << Bitfield.Width << ">"; break; case k_RegisterList: + case k_RegisterListWithAPSR: case k_DPRRegisterList: - case k_SPRRegisterList: { + case k_SPRRegisterList: + case k_FPSRegisterListWithVPR: + case k_FPDRegisterListWithVPR: { OS << "<register_list "; const SmallVectorImpl<unsigned> &RegList = getRegList(); @@ -3423,7 +3926,7 @@ int ARMAsmParser::tryParseRegister() { } // Some FPUs only have 16 D registers, so D16-D31 are invalid - if (hasD16() && RegNum >= ARM::D16 && RegNum <= ARM::D31) + if (!hasD32() && RegNum >= ARM::D16 && RegNum <= ARM::D31) return -1; Parser.Lex(); // Eat identifier token. @@ -3662,11 +4165,10 @@ ARMAsmParser::parseCoprocNumOperand(OperandVector &Operands) { if (Tok.isNot(AsmToken::Identifier)) return MatchOperand_NoMatch; - int Num = MatchCoprocessorOperandName(Tok.getString(), 'p'); + int Num = MatchCoprocessorOperandName(Tok.getString().lower(), 'p'); if (Num == -1) return MatchOperand_NoMatch; - // ARMv7 and v8 don't allow cp10/cp11 due to VFP/NEON specific instructions - if ((hasV7Ops() || hasV8Ops()) && (Num == 10 || Num == 11)) + if (!isValidCoprocessorNumber(Num, getSTI().getFeatureBits())) return MatchOperand_NoMatch; Parser.Lex(); // Eat identifier token. @@ -3685,7 +4187,7 @@ ARMAsmParser::parseCoprocRegOperand(OperandVector &Operands) { if (Tok.isNot(AsmToken::Identifier)) return MatchOperand_NoMatch; - int Reg = MatchCoprocessorOperandName(Tok.getString(), 'c'); + int Reg = MatchCoprocessorOperandName(Tok.getString().lower(), 'c'); if (Reg == -1) return MatchOperand_NoMatch; @@ -3752,7 +4254,8 @@ static unsigned getNextRegister(unsigned Reg) { } /// Parse a register list. -bool ARMAsmParser::parseRegisterList(OperandVector &Operands) { +bool ARMAsmParser::parseRegisterList(OperandVector &Operands, + bool EnforceOrder) { MCAsmParser &Parser = getParser(); if (Parser.getTok().isNot(AsmToken::LCurly)) return TokError("Token is not a Left Curly Brace"); @@ -3785,6 +4288,8 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands) { RC = &ARMMCRegisterClasses[ARM::DPRRegClassID]; else if (ARMMCRegisterClasses[ARM::SPRRegClassID].contains(Reg)) RC = &ARMMCRegisterClasses[ARM::SPRRegClassID]; + else if (ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains(Reg)) + RC = &ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID]; else return Error(RegLoc, "invalid register in register list"); @@ -3838,14 +4343,32 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands) { Reg = getDRegFromQReg(Reg); isQReg = true; } + if (!RC->contains(Reg) && + RC->getID() == ARMMCRegisterClasses[ARM::GPRRegClassID].getID() && + ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains(Reg)) { + // switch the register classes, as GPRwithAPSRnospRegClassID is a partial + // subset of GPRRegClassId except it contains APSR as well. + RC = &ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID]; + } + if (Reg == ARM::VPR && (RC == &ARMMCRegisterClasses[ARM::SPRRegClassID] || + RC == &ARMMCRegisterClasses[ARM::DPRRegClassID])) { + RC = &ARMMCRegisterClasses[ARM::FPWithVPRRegClassID]; + EReg = MRI->getEncodingValue(Reg); + Registers.push_back(std::pair<unsigned, unsigned>(EReg, Reg)); + continue; + } // The register must be in the same register class as the first. if (!RC->contains(Reg)) return Error(RegLoc, "invalid register in register list"); - // List must be monotonically increasing. - if (MRI->getEncodingValue(Reg) < MRI->getEncodingValue(OldReg)) { + // In most cases, the list must be monotonically increasing. An + // exception is CLRM, which is order-independent anyway, so + // there's no potential for confusion if you write clrm {r2,r1} + // instead of clrm {r1,r2}. + if (EnforceOrder && + MRI->getEncodingValue(Reg) < MRI->getEncodingValue(OldReg)) { if (ARMMCRegisterClasses[ARM::GPRRegClassID].contains(Reg)) Warning(RegLoc, "register list not in ascending order"); - else + else if (!ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains(Reg)) return Error(RegLoc, "register list not in ascending order"); } if (MRI->getEncodingValue(Reg) == MRI->getEncodingValue(OldReg)) { @@ -3855,6 +4378,7 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands) { } // VFP register lists must also be contiguous. if (RC != &ARMMCRegisterClasses[ARM::GPRRegClassID] && + RC != &ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID] && Reg != OldReg + 1) return Error(RegLoc, "non-contiguous register range"); EReg = MRI->getEncodingValue(Reg); @@ -3944,7 +4468,7 @@ ARMAsmParser::parseVectorList(OperandVector &Operands) { // As an extension (to match gas), support a plain D register or Q register // (without encosing curly braces) as a single or double entry list, // respectively. - if (Parser.getTok().is(AsmToken::Identifier)) { + if (!hasMVE() && Parser.getTok().is(AsmToken::Identifier)) { SMLoc E = Parser.getTok().getEndLoc(); int Reg = tryParseRegister(); if (Reg == -1) @@ -4012,9 +4536,14 @@ ARMAsmParser::parseVectorList(OperandVector &Operands) { unsigned Count = 1; int Spacing = 0; unsigned FirstReg = Reg; + + if (hasMVE() && !ARMMCRegisterClasses[ARM::MQPRRegClassID].contains(Reg)) { + Error(Parser.getTok().getLoc(), "vector register in range Q0-Q7 expected"); + return MatchOperand_ParseFail; + } // The list is of D registers, but we also allow Q regs and just interpret // them as the two D sub-registers. - if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { + else if (!hasMVE() && ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { FirstReg = Reg = getDRegFromQReg(Reg); Spacing = 1; // double-spacing requires explicit D registers, otherwise // it's ambiguous with four-register single spaced. @@ -4044,14 +4573,17 @@ ARMAsmParser::parseVectorList(OperandVector &Operands) { return MatchOperand_ParseFail; } // Allow Q regs and just interpret them as the two D sub-registers. - if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(EndReg)) + if (!hasMVE() && ARMMCRegisterClasses[ARM::QPRRegClassID].contains(EndReg)) EndReg = getDRegFromQReg(EndReg) + 1; // If the register is the same as the start reg, there's nothing // more to do. if (Reg == EndReg) continue; // The register must be in the same register class as the first. - if (!ARMMCRegisterClasses[ARM::DPRRegClassID].contains(EndReg)) { + if ((hasMVE() && + !ARMMCRegisterClasses[ARM::MQPRRegClassID].contains(EndReg)) || + (!hasMVE() && + !ARMMCRegisterClasses[ARM::DPRRegClassID].contains(EndReg))) { Error(AfterMinusLoc, "invalid register in register list"); return MatchOperand_ParseFail; } @@ -4084,13 +4616,21 @@ ARMAsmParser::parseVectorList(OperandVector &Operands) { Error(RegLoc, "register expected"); return MatchOperand_ParseFail; } + + if (hasMVE()) { + if (!ARMMCRegisterClasses[ARM::MQPRRegClassID].contains(Reg)) { + Error(RegLoc, "vector register in range Q0-Q7 expected"); + return MatchOperand_ParseFail; + } + Spacing = 1; + } // vector register lists must be contiguous. // It's OK to use the enumeration values directly here rather, as the // VFP register classes have the enum sorted properly. // // The list is of D registers, but we also allow Q regs and just interpret // them as the two D sub-registers. - if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { + else if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { if (!Spacing) Spacing = 1; // Register range implies a single spaced list. else if (Spacing == 2) { @@ -4151,30 +4691,20 @@ ARMAsmParser::parseVectorList(OperandVector &Operands) { switch (LaneKind) { case NoLanes: + case AllLanes: { // Two-register operands have been converted to the // composite register classes. - if (Count == 2) { - const MCRegisterClass *RC = (Spacing == 1) ? - &ARMMCRegisterClasses[ARM::DPairRegClassID] : - &ARMMCRegisterClasses[ARM::DPairSpcRegClassID]; - FirstReg = MRI->getMatchingSuperReg(FirstReg, ARM::dsub_0, RC); - } - Operands.push_back(ARMOperand::CreateVectorList(FirstReg, Count, - (Spacing == 2), S, E)); - break; - case AllLanes: - // Two-register operands have been converted to the - // composite register classes. - if (Count == 2) { + if (Count == 2 && !hasMVE()) { const MCRegisterClass *RC = (Spacing == 1) ? &ARMMCRegisterClasses[ARM::DPairRegClassID] : &ARMMCRegisterClasses[ARM::DPairSpcRegClassID]; FirstReg = MRI->getMatchingSuperReg(FirstReg, ARM::dsub_0, RC); } - Operands.push_back(ARMOperand::CreateVectorListAllLanes(FirstReg, Count, - (Spacing == 2), - S, E)); + auto Create = (LaneKind == NoLanes ? ARMOperand::CreateVectorList : + ARMOperand::CreateVectorListAllLanes); + Operands.push_back(Create(FirstReg, Count, (Spacing == 2), S, E)); break; + } case IndexedLane: Operands.push_back(ARMOperand::CreateVectorListIndexed(FirstReg, Count, LaneIndex, @@ -5061,6 +5591,21 @@ void ARMAsmParser::cvtThumbBranches(MCInst &Inst, ((ARMOperand &)*Operands[CondOp]).addCondCodeOperands(Inst, 2); } +void ARMAsmParser::cvtMVEVMOVQtoDReg( + MCInst &Inst, const OperandVector &Operands) { + + // mnemonic, condition code, Rt, Rt2, Qd, idx, Qd again, idx2 + assert(Operands.size() == 8); + + ((ARMOperand &)*Operands[2]).addRegOperands(Inst, 1); // Rt + ((ARMOperand &)*Operands[3]).addRegOperands(Inst, 1); // Rt2 + ((ARMOperand &)*Operands[4]).addRegOperands(Inst, 1); // Qd + ((ARMOperand &)*Operands[5]).addMVEPairVectorIndexOperands(Inst, 1); // idx + // skip second copy of Qd in Operands[6] + ((ARMOperand &)*Operands[7]).addMVEPairVectorIndexOperands(Inst, 1); // idx2 + ((ARMOperand &)*Operands[1]).addCondCodeOperands(Inst, 2); // condition code +} + /// Parse an ARM memory expression, return false if successful else return true /// or an error. The first token must be a '[' when called. bool ARMAsmParser::parseMemory(OperandVector &Operands) { @@ -5275,6 +5820,8 @@ bool ARMAsmParser::parseMemRegOffsetShift(ARM_AM::ShiftOpc &St, St = ARM_AM::ror; else if (ShiftName == "rrx" || ShiftName == "RRX") St = ARM_AM::rrx; + else if (ShiftName == "uxtw" || ShiftName == "UXTW") + St = ARM_AM::uxtw; else return Error(Loc, "illegal shift operator"); Parser.Lex(); // Eat shift type token. @@ -5463,7 +6010,7 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { case AsmToken::LBrac: return parseMemory(Operands); case AsmToken::LCurly: - return parseRegisterList(Operands); + return parseRegisterList(Operands, !Mnemonic.startswith("clr")); case AsmToken::Dollar: case AsmToken::Hash: // #42 -> immediate. @@ -5595,6 +6142,9 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) { case MCObjectFileInfo::IsWasm: CurrentFormat = WASM; break; + case MCObjectFileInfo::IsXCOFF: + llvm_unreachable("unexpected object format"); + break; } if (~Prefix->SupportedFormats & CurrentFormat) { @@ -5621,11 +6171,14 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) { // FIXME: Would be nice to autogen this. // FIXME: This is a bit of a maze of special cases. StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, + StringRef ExtraToken, unsigned &PredicationCode, + unsigned &VPTPredicationCode, bool &CarrySetting, unsigned &ProcessorIMod, StringRef &ITMask) { PredicationCode = ARMCC::AL; + VPTPredicationCode = ARMVCC::None; CarrySetting = false; ProcessorIMod = 0; @@ -5649,7 +6202,12 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, Mnemonic == "bxns" || Mnemonic == "blxns" || Mnemonic == "vudot" || Mnemonic == "vsdot" || Mnemonic == "vcmla" || Mnemonic == "vcadd" || - Mnemonic == "vfmal" || Mnemonic == "vfmsl") + Mnemonic == "vfmal" || Mnemonic == "vfmsl" || + Mnemonic == "wls" || Mnemonic == "le" || Mnemonic == "dls" || + Mnemonic == "csel" || Mnemonic == "csinc" || + Mnemonic == "csinv" || Mnemonic == "csneg" || Mnemonic == "cinc" || + Mnemonic == "cinv" || Mnemonic == "cneg" || Mnemonic == "cset" || + Mnemonic == "csetm") return Mnemonic; // First, split out any predication code. Ignore mnemonics we know aren't @@ -5657,7 +6215,18 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, if (Mnemonic != "adcs" && Mnemonic != "bics" && Mnemonic != "movs" && Mnemonic != "muls" && Mnemonic != "smlals" && Mnemonic != "smulls" && Mnemonic != "umlals" && Mnemonic != "umulls" && Mnemonic != "lsls" && - Mnemonic != "sbcs" && Mnemonic != "rscs") { + Mnemonic != "sbcs" && Mnemonic != "rscs" && + !(hasMVE() && + (Mnemonic == "vmine" || + Mnemonic == "vshle" || Mnemonic == "vshlt" || Mnemonic == "vshllt" || + Mnemonic == "vrshle" || Mnemonic == "vrshlt" || + Mnemonic == "vmvne" || Mnemonic == "vorne" || + Mnemonic == "vnege" || Mnemonic == "vnegt" || + Mnemonic == "vmule" || Mnemonic == "vmult" || + Mnemonic == "vrintne" || + Mnemonic == "vcmult" || Mnemonic == "vcmule" || + Mnemonic == "vpsele" || Mnemonic == "vpselt" || + Mnemonic.startswith("vq")))) { unsigned CC = ARMCondCodeFromString(Mnemonic.substr(Mnemonic.size()-2)); if (CC != ~0U) { Mnemonic = Mnemonic.slice(0, Mnemonic.size() - 2); @@ -5677,7 +6246,8 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, Mnemonic == "fsts" || Mnemonic == "fcpys" || Mnemonic == "fdivs" || Mnemonic == "fmuls" || Mnemonic == "fcmps" || Mnemonic == "fcmpzs" || Mnemonic == "vfms" || Mnemonic == "vfnms" || Mnemonic == "fconsts" || - Mnemonic == "bxns" || Mnemonic == "blxns" || + Mnemonic == "bxns" || Mnemonic == "blxns" || Mnemonic == "vfmas" || + Mnemonic == "vmlas" || (Mnemonic == "movs" && isThumb()))) { Mnemonic = Mnemonic.slice(0, Mnemonic.size() - 1); CarrySetting = true; @@ -5698,12 +6268,36 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, } } + if (isMnemonicVPTPredicable(Mnemonic, ExtraToken) && Mnemonic != "vmovlt" && + Mnemonic != "vshllt" && Mnemonic != "vrshrnt" && Mnemonic != "vshrnt" && + Mnemonic != "vqrshrunt" && Mnemonic != "vqshrunt" && + Mnemonic != "vqrshrnt" && Mnemonic != "vqshrnt" && Mnemonic != "vmullt" && + Mnemonic != "vqmovnt" && Mnemonic != "vqmovunt" && + Mnemonic != "vqmovnt" && Mnemonic != "vmovnt" && Mnemonic != "vqdmullt" && + Mnemonic != "vpnot" && Mnemonic != "vcvtt" && Mnemonic != "vcvt") { + unsigned CC = ARMVectorCondCodeFromString(Mnemonic.substr(Mnemonic.size()-1)); + if (CC != ~0U) { + Mnemonic = Mnemonic.slice(0, Mnemonic.size()-1); + VPTPredicationCode = CC; + } + return Mnemonic; + } + // The "it" instruction has the condition mask on the end of the mnemonic. if (Mnemonic.startswith("it")) { ITMask = Mnemonic.slice(2, Mnemonic.size()); Mnemonic = Mnemonic.slice(0, 2); } + if (Mnemonic.startswith("vpst")) { + ITMask = Mnemonic.slice(4, Mnemonic.size()); + Mnemonic = Mnemonic.slice(0, 4); + } + else if (Mnemonic.startswith("vpt")) { + ITMask = Mnemonic.slice(3, Mnemonic.size()); + Mnemonic = Mnemonic.slice(0, 3); + } + return Mnemonic; } @@ -5711,9 +6305,14 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, /// inclusion of carry set or predication code operands. // // FIXME: It would be nice to autogen this. -void ARMAsmParser::getMnemonicAcceptInfo(StringRef Mnemonic, StringRef FullInst, +void ARMAsmParser::getMnemonicAcceptInfo(StringRef Mnemonic, + StringRef ExtraToken, + StringRef FullInst, bool &CanAcceptCarrySet, - bool &CanAcceptPredicationCode) { + bool &CanAcceptPredicationCode, + bool &CanAcceptVPTPredicationCode) { + CanAcceptVPTPredicationCode = isMnemonicVPTPredicable(Mnemonic, ExtraToken); + CanAcceptCarrySet = Mnemonic == "and" || Mnemonic == "lsl" || Mnemonic == "lsr" || Mnemonic == "rrx" || Mnemonic == "ror" || Mnemonic == "sub" || @@ -5742,7 +6341,18 @@ void ARMAsmParser::getMnemonicAcceptInfo(StringRef Mnemonic, StringRef FullInst, Mnemonic == "vcmla" || Mnemonic == "vcadd" || Mnemonic == "vfmal" || Mnemonic == "vfmsl" || Mnemonic == "sb" || Mnemonic == "ssbb" || - Mnemonic == "pssbb") { + Mnemonic == "pssbb" || + Mnemonic == "bfcsel" || Mnemonic == "wls" || + Mnemonic == "dls" || Mnemonic == "le" || Mnemonic == "csel" || + Mnemonic == "csinc" || Mnemonic == "csinv" || Mnemonic == "csneg" || + Mnemonic == "cinc" || Mnemonic == "cinv" || Mnemonic == "cneg" || + Mnemonic == "cset" || Mnemonic == "csetm" || + Mnemonic.startswith("vpt") || Mnemonic.startswith("vpst") || + (hasMVE() && + (Mnemonic.startswith("vst2") || Mnemonic.startswith("vld2") || + Mnemonic.startswith("vst4") || Mnemonic.startswith("vld4") || + Mnemonic.startswith("wlstp") || Mnemonic.startswith("dlstp") || + Mnemonic.startswith("letp")))) { // These mnemonics are never predicable CanAcceptPredicationCode = false; } else if (!isThumb()) { @@ -5976,7 +6586,8 @@ bool ARMAsmParser::shouldOmitPredicateOperand(StringRef Mnemonic, OperandVector &Operands) { // VRINT{Z, X} have a predicate operand in VFP, but not in NEON unsigned RegIdx = 3; - if ((Mnemonic == "vrintz" || Mnemonic == "vrintx") && + if ((((Mnemonic == "vrintz" || Mnemonic == "vrintx") && !hasMVE()) || + Mnemonic == "vrintr") && (static_cast<ARMOperand &>(*Operands[2]).getToken() == ".f32" || static_cast<ARMOperand &>(*Operands[2]).getToken() == ".f16")) { if (static_cast<ARMOperand &>(*Operands[3]).isToken() && @@ -5994,6 +6605,47 @@ bool ARMAsmParser::shouldOmitPredicateOperand(StringRef Mnemonic, return false; } +bool ARMAsmParser::shouldOmitVectorPredicateOperand(StringRef Mnemonic, + OperandVector &Operands) { + if (!hasMVE() || Operands.size() < 3) + return true; + + if (Mnemonic.startswith("vld2") || Mnemonic.startswith("vld4") || + Mnemonic.startswith("vst2") || Mnemonic.startswith("vst4")) + return true; + + if (Mnemonic.startswith("vctp") || Mnemonic.startswith("vpnot")) + return false; + + if (Mnemonic.startswith("vmov") && + !(Mnemonic.startswith("vmovl") || Mnemonic.startswith("vmovn") || + Mnemonic.startswith("vmovx"))) { + for (auto &Operand : Operands) { + if (static_cast<ARMOperand &>(*Operand).isVectorIndex() || + ((*Operand).isReg() && + (ARMMCRegisterClasses[ARM::SPRRegClassID].contains( + (*Operand).getReg()) || + ARMMCRegisterClasses[ARM::DPRRegClassID].contains( + (*Operand).getReg())))) { + return true; + } + } + return false; + } else { + for (auto &Operand : Operands) { + // We check the larger class QPR instead of just the legal class + // MQPR, to more accurately report errors when using Q registers + // outside of the allowed range. + if (static_cast<ARMOperand &>(*Operand).isVectorIndex() || + (Operand->isReg() && + (ARMMCRegisterClasses[ARM::QPRRegClassID].contains( + Operand->getReg())))) + return false; + } + return true; + } +} + static bool isDataTypeToken(StringRef Tok) { return Tok == ".8" || Tok == ".16" || Tok == ".32" || Tok == ".64" || Tok == ".i8" || Tok == ".i16" || Tok == ".i32" || Tok == ".i64" || @@ -6010,7 +6662,8 @@ static bool doesIgnoreDataTypeSuffix(StringRef Mnemonic, StringRef DT) { return Mnemonic.startswith("vldm") || Mnemonic.startswith("vstm"); } -static void applyMnemonicAliases(StringRef &Mnemonic, uint64_t Features, +static void applyMnemonicAliases(StringRef &Mnemonic, + const FeatureBitset &Features, unsigned VariantID); // The GNU assembler has aliases of ldrd and strd with the second register @@ -6033,7 +6686,7 @@ void ARMAsmParser::fixupGNULDRDAlias(StringRef Mnemonic, if (!Op2.isReg()) return; - if (!Op3.isMem()) + if (!Op3.isGPRMem()) return; const MCRegisterClass &GPR = MRI->getRegClass(ARM::GPRRegClassID); @@ -6068,7 +6721,7 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // The generic tblgen'erated code does this later, at the start of // MatchInstructionImpl(), but that's too late for aliases that include // any sort of suffix. - uint64_t AvailableFeatures = getAvailableFeatures(); + const FeatureBitset &AvailableFeatures = getAvailableFeatures(); unsigned AssemblerDialect = getParser().getAssemblerDialect(); applyMnemonicAliases(Name, AvailableFeatures, AssemblerDialect); @@ -6084,14 +6737,16 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // Create the leading tokens for the mnemonic, split by '.' characters. size_t Start = 0, Next = Name.find('.'); StringRef Mnemonic = Name.slice(Start, Next); + StringRef ExtraToken = Name.slice(Next, Name.find(' ', Next + 1)); // Split out the predication code and carry setting flag from the mnemonic. unsigned PredicationCode; + unsigned VPTPredicationCode; unsigned ProcessorIMod; bool CarrySetting; StringRef ITMask; - Mnemonic = splitMnemonic(Mnemonic, PredicationCode, CarrySetting, - ProcessorIMod, ITMask); + Mnemonic = splitMnemonic(Mnemonic, ExtraToken, PredicationCode, VPTPredicationCode, + CarrySetting, ProcessorIMod, ITMask); // In Thumb1, only the branch (B) instruction can be predicated. if (isThumbOne() && PredicationCode != ARMCC::AL && Mnemonic != "b") { @@ -6100,15 +6755,24 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, Operands.push_back(ARMOperand::CreateToken(Mnemonic, NameLoc)); - // Handle the IT instruction ITMask. Convert it to a bitmask. This - // is the mask as it will be for the IT encoding if the conditional - // encoding has a '1' as it's bit0 (i.e. 't' ==> '1'). In the case - // where the conditional bit0 is zero, the instruction post-processing - // will adjust the mask accordingly. - if (Mnemonic == "it") { - SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + 2); + // Handle the mask for IT and VPT instructions. In ARMOperand and + // MCOperand, this is stored in a format independent of the + // condition code: the lowest set bit indicates the end of the + // encoding, and above that, a 1 bit indicates 'else', and an 0 + // indicates 'then'. E.g. + // IT -> 1000 + // ITx -> x100 (ITT -> 0100, ITE -> 1100) + // ITxy -> xy10 (e.g. ITET -> 1010) + // ITxyz -> xyz1 (e.g. ITEET -> 1101) + if (Mnemonic == "it" || Mnemonic.startswith("vpt") || + Mnemonic.startswith("vpst")) { + SMLoc Loc = Mnemonic == "it" ? SMLoc::getFromPointer(NameLoc.getPointer() + 2) : + Mnemonic == "vpt" ? SMLoc::getFromPointer(NameLoc.getPointer() + 3) : + SMLoc::getFromPointer(NameLoc.getPointer() + 4); if (ITMask.size() > 3) { - return Error(Loc, "too many conditions on IT instruction"); + if (Mnemonic == "it") + return Error(Loc, "too many conditions on IT instruction"); + return Error(Loc, "too many conditions on VPT instruction"); } unsigned Mask = 8; for (unsigned i = ITMask.size(); i != 0; --i) { @@ -6117,7 +6781,7 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, return Error(Loc, "illegal IT block condition mask '" + ITMask + "'"); } Mask >>= 1; - if (ITMask[i - 1] == 't') + if (ITMask[i - 1] == 'e') Mask |= 8; } Operands.push_back(ARMOperand::CreateITMask(Mask, Loc)); @@ -6133,8 +6797,9 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // ConditionCode operands to match the mnemonic "as written" and then we let // the matcher deal with finding the right instruction or generating an // appropriate error. - bool CanAcceptCarrySet, CanAcceptPredicationCode; - getMnemonicAcceptInfo(Mnemonic, Name, CanAcceptCarrySet, CanAcceptPredicationCode); + bool CanAcceptCarrySet, CanAcceptPredicationCode, CanAcceptVPTPredicationCode; + getMnemonicAcceptInfo(Mnemonic, ExtraToken, Name, CanAcceptCarrySet, + CanAcceptPredicationCode, CanAcceptVPTPredicationCode); // If we had a carry-set on an instruction that can't do that, issue an // error. @@ -6149,6 +6814,13 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, "' is not predicable, but condition code specified"); } + // If we had a VPT predication code on an instruction that can't do that, issue an + // error. + if (!CanAcceptVPTPredicationCode && VPTPredicationCode != ARMVCC::None) { + return Error(NameLoc, "instruction '" + Mnemonic + + "' is not VPT predicable, but VPT code T/E is specified"); + } + // Add the carry setting operand, if necessary. if (CanAcceptCarrySet) { SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size()); @@ -6161,7 +6833,24 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size() + CarrySetting); Operands.push_back(ARMOperand::CreateCondCode( - ARMCC::CondCodes(PredicationCode), Loc)); + ARMCC::CondCodes(PredicationCode), Loc)); + } + + // Add the VPT predication code operand, if necessary. + // FIXME: We don't add them for the instructions filtered below as these can + // have custom operands which need special parsing. This parsing requires + // the operand to be in the same place in the OperandVector as their + // definition in tblgen. Since these instructions may also have the + // scalar predication operand we do not add the vector one and leave until + // now to fix it up. + if (CanAcceptVPTPredicationCode && Mnemonic != "vmov" && + !Mnemonic.startswith("vcmp") && + !(Mnemonic.startswith("vcvt") && Mnemonic != "vcvta" && + Mnemonic != "vcvtn" && Mnemonic != "vcvtp" && Mnemonic != "vcvtm")) { + SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size() + + CarrySetting); + Operands.push_back(ARMOperand::CreateVPTPred( + ARMVCC::VPTCodes(VPTPredicationCode), Loc)); } // Add the processor imod operand, if necessary. @@ -6177,7 +6866,7 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, while (Next != StringRef::npos) { Start = Next; Next = Name.find('.', Start + 1); - StringRef ExtraToken = Name.slice(Start, Next); + ExtraToken = Name.slice(Start, Next); // Some NEON instructions have an optional datatype suffix that is // completely ignored. Check for that. @@ -6233,57 +6922,173 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // Some instructions have the same mnemonic, but don't always // have a predicate. Distinguish them here and delete the - // predicate if needed. + // appropriate predicate if needed. This could be either the scalar + // predication code or the vector predication code. if (PredicationCode == ARMCC::AL && shouldOmitPredicateOperand(Mnemonic, Operands)) Operands.erase(Operands.begin() + 1); - // ARM mode 'blx' need special handling, as the register operand version - // is predicable, but the label operand version is not. So, we can't rely - // on the Mnemonic based checking to correctly figure out when to put - // a k_CondCode operand in the list. If we're trying to match the label - // version, remove the k_CondCode operand here. - if (!isThumb() && Mnemonic == "blx" && Operands.size() == 3 && - static_cast<ARMOperand &>(*Operands[2]).isImm()) - Operands.erase(Operands.begin() + 1); - - // Adjust operands of ldrexd/strexd to MCK_GPRPair. - // ldrexd/strexd require even/odd GPR pair. To enforce this constraint, - // a single GPRPair reg operand is used in the .td file to replace the two - // GPRs. However, when parsing from asm, the two GRPs cannot be automatically - // expressed as a GPRPair, so we have to manually merge them. - // FIXME: We would really like to be able to tablegen'erate this. - if (!isThumb() && Operands.size() > 4 && - (Mnemonic == "ldrexd" || Mnemonic == "strexd" || Mnemonic == "ldaexd" || - Mnemonic == "stlexd")) { - bool isLoad = (Mnemonic == "ldrexd" || Mnemonic == "ldaexd"); - unsigned Idx = isLoad ? 2 : 3; - ARMOperand &Op1 = static_cast<ARMOperand &>(*Operands[Idx]); - ARMOperand &Op2 = static_cast<ARMOperand &>(*Operands[Idx + 1]); - const MCRegisterClass& MRC = MRI->getRegClass(ARM::GPRRegClassID); - // Adjust only if Op1 and Op2 are GPRs. - if (Op1.isReg() && Op2.isReg() && MRC.contains(Op1.getReg()) && - MRC.contains(Op2.getReg())) { - unsigned Reg1 = Op1.getReg(); - unsigned Reg2 = Op2.getReg(); - unsigned Rt = MRI->getEncodingValue(Reg1); - unsigned Rt2 = MRI->getEncodingValue(Reg2); + if (hasMVE()) { + if (!shouldOmitVectorPredicateOperand(Mnemonic, Operands) && + Mnemonic == "vmov" && PredicationCode == ARMCC::LT) { + // Very nasty hack to deal with the vector predicated variant of vmovlt + // the scalar predicated vmov with condition 'lt'. We can not tell them + // apart until we have parsed their operands. + Operands.erase(Operands.begin() + 1); + Operands.erase(Operands.begin()); + SMLoc MLoc = SMLoc::getFromPointer(NameLoc.getPointer()); + SMLoc PLoc = SMLoc::getFromPointer(NameLoc.getPointer() + + Mnemonic.size() - 1 + CarrySetting); + Operands.insert(Operands.begin(), + ARMOperand::CreateVPTPred(ARMVCC::None, PLoc)); + Operands.insert(Operands.begin(), + ARMOperand::CreateToken(StringRef("vmovlt"), MLoc)); + } else if (Mnemonic == "vcvt" && PredicationCode == ARMCC::NE && + !shouldOmitVectorPredicateOperand(Mnemonic, Operands)) { + // Another nasty hack to deal with the ambiguity between vcvt with scalar + // predication 'ne' and vcvtn with vector predication 'e'. As above we + // can only distinguish between the two after we have parsed their + // operands. + Operands.erase(Operands.begin() + 1); + Operands.erase(Operands.begin()); + SMLoc MLoc = SMLoc::getFromPointer(NameLoc.getPointer()); + SMLoc PLoc = SMLoc::getFromPointer(NameLoc.getPointer() + + Mnemonic.size() - 1 + CarrySetting); + Operands.insert(Operands.begin(), + ARMOperand::CreateVPTPred(ARMVCC::Else, PLoc)); + Operands.insert(Operands.begin(), + ARMOperand::CreateToken(StringRef("vcvtn"), MLoc)); + } else if (Mnemonic == "vmul" && PredicationCode == ARMCC::LT && + !shouldOmitVectorPredicateOperand(Mnemonic, Operands)) { + // Another hack, this time to distinguish between scalar predicated vmul + // with 'lt' predication code and the vector instruction vmullt with + // vector predication code "none" + Operands.erase(Operands.begin() + 1); + Operands.erase(Operands.begin()); + SMLoc MLoc = SMLoc::getFromPointer(NameLoc.getPointer()); + Operands.insert(Operands.begin(), + ARMOperand::CreateToken(StringRef("vmullt"), MLoc)); + } + // For vmov and vcmp, as mentioned earlier, we did not add the vector + // predication code, since these may contain operands that require + // special parsing. So now we have to see if they require vector + // predication and replace the scalar one with the vector predication + // operand if that is the case. + else if (Mnemonic == "vmov" || Mnemonic.startswith("vcmp") || + (Mnemonic.startswith("vcvt") && !Mnemonic.startswith("vcvta") && + !Mnemonic.startswith("vcvtn") && !Mnemonic.startswith("vcvtp") && + !Mnemonic.startswith("vcvtm"))) { + if (!shouldOmitVectorPredicateOperand(Mnemonic, Operands)) { + // We could not split the vector predicate off vcvt because it might + // have been the scalar vcvtt instruction. Now we know its a vector + // instruction, we still need to check whether its the vector + // predicated vcvt with 'Then' predication or the vector vcvtt. We can + // distinguish the two based on the suffixes, if it is any of + // ".f16.f32", ".f32.f16", ".f16.f64" or ".f64.f16" then it is the vcvtt. + if (Mnemonic.startswith("vcvtt") && Operands.size() >= 4) { + auto Sz1 = static_cast<ARMOperand &>(*Operands[2]); + auto Sz2 = static_cast<ARMOperand &>(*Operands[3]); + if (!(Sz1.isToken() && Sz1.getToken().startswith(".f") && + Sz2.isToken() && Sz2.getToken().startswith(".f"))) { + Operands.erase(Operands.begin()); + SMLoc MLoc = SMLoc::getFromPointer(NameLoc.getPointer()); + VPTPredicationCode = ARMVCC::Then; - // Rt2 must be Rt + 1 and Rt must be even. - if (Rt + 1 != Rt2 || (Rt & 1)) { - return Error(Op2.getStartLoc(), - isLoad ? "destination operands must be sequential" - : "source operands must be sequential"); + Mnemonic = Mnemonic.substr(0, 4); + Operands.insert(Operands.begin(), + ARMOperand::CreateToken(Mnemonic, MLoc)); + } + } + Operands.erase(Operands.begin() + 1); + SMLoc PLoc = SMLoc::getFromPointer(NameLoc.getPointer() + + Mnemonic.size() + CarrySetting); + Operands.insert(Operands.begin() + 1, + ARMOperand::CreateVPTPred( + ARMVCC::VPTCodes(VPTPredicationCode), PLoc)); + } + } else if (CanAcceptVPTPredicationCode) { + // For all other instructions, make sure only one of the two + // predication operands is left behind, depending on whether we should + // use the vector predication. + if (shouldOmitVectorPredicateOperand(Mnemonic, Operands)) { + if (CanAcceptPredicationCode) + Operands.erase(Operands.begin() + 2); + else + Operands.erase(Operands.begin() + 1); + } else if (CanAcceptPredicationCode && PredicationCode == ARMCC::AL) { + Operands.erase(Operands.begin() + 1); } - unsigned NewReg = MRI->getMatchingSuperReg(Reg1, ARM::gsub_0, - &(MRI->getRegClass(ARM::GPRPairRegClassID))); - Operands[Idx] = - ARMOperand::CreateReg(NewReg, Op1.getStartLoc(), Op2.getEndLoc()); - Operands.erase(Operands.begin() + Idx + 1); } } + if (VPTPredicationCode != ARMVCC::None) { + bool usedVPTPredicationCode = false; + for (unsigned I = 1; I < Operands.size(); ++I) + if (static_cast<ARMOperand &>(*Operands[I]).isVPTPred()) + usedVPTPredicationCode = true; + if (!usedVPTPredicationCode) { + // If we have a VPT predication code and we haven't just turned it + // into an operand, then it was a mistake for splitMnemonic to + // separate it from the rest of the mnemonic in the first place, + // and this may lead to wrong disassembly (e.g. scalar floating + // point VCMPE is actually a different instruction from VCMP, so + // we mustn't treat them the same). In that situation, glue it + // back on. + Mnemonic = Name.slice(0, Mnemonic.size() + 1); + Operands.erase(Operands.begin()); + Operands.insert(Operands.begin(), + ARMOperand::CreateToken(Mnemonic, NameLoc)); + } + } + + // ARM mode 'blx' need special handling, as the register operand version + // is predicable, but the label operand version is not. So, we can't rely + // on the Mnemonic based checking to correctly figure out when to put + // a k_CondCode operand in the list. If we're trying to match the label + // version, remove the k_CondCode operand here. + if (!isThumb() && Mnemonic == "blx" && Operands.size() == 3 && + static_cast<ARMOperand &>(*Operands[2]).isImm()) + Operands.erase(Operands.begin() + 1); + + // Adjust operands of ldrexd/strexd to MCK_GPRPair. + // ldrexd/strexd require even/odd GPR pair. To enforce this constraint, + // a single GPRPair reg operand is used in the .td file to replace the two + // GPRs. However, when parsing from asm, the two GRPs cannot be + // automatically + // expressed as a GPRPair, so we have to manually merge them. + // FIXME: We would really like to be able to tablegen'erate this. + if (!isThumb() && Operands.size() > 4 && + (Mnemonic == "ldrexd" || Mnemonic == "strexd" || Mnemonic == "ldaexd" || + Mnemonic == "stlexd")) { + bool isLoad = (Mnemonic == "ldrexd" || Mnemonic == "ldaexd"); + unsigned Idx = isLoad ? 2 : 3; + ARMOperand &Op1 = static_cast<ARMOperand &>(*Operands[Idx]); + ARMOperand &Op2 = static_cast<ARMOperand &>(*Operands[Idx + 1]); + + const MCRegisterClass &MRC = MRI->getRegClass(ARM::GPRRegClassID); + // Adjust only if Op1 and Op2 are GPRs. + if (Op1.isReg() && Op2.isReg() && MRC.contains(Op1.getReg()) && + MRC.contains(Op2.getReg())) { + unsigned Reg1 = Op1.getReg(); + unsigned Reg2 = Op2.getReg(); + unsigned Rt = MRI->getEncodingValue(Reg1); + unsigned Rt2 = MRI->getEncodingValue(Reg2); + + // Rt2 must be Rt + 1 and Rt must be even. + if (Rt + 1 != Rt2 || (Rt & 1)) { + return Error(Op2.getStartLoc(), + isLoad ? "destination operands must be sequential" + : "source operands must be sequential"); + } + unsigned NewReg = MRI->getMatchingSuperReg( + Reg1, ARM::gsub_0, &(MRI->getRegClass(ARM::GPRPairRegClassID))); + Operands[Idx] = + ARMOperand::CreateReg(NewReg, Op1.getStartLoc(), Op2.getEndLoc()); + Operands.erase(Operands.begin() + Idx + 1); + } + } + // GNU Assembler extension (compatibility). fixupGNULDRDAlias(Mnemonic, Operands); @@ -6442,6 +7247,17 @@ bool ARMAsmParser::validateLDRDSTRD(MCInst &Inst, return false; } +static int findFirstVectorPredOperandIdx(const MCInstrDesc &MCID) { + for (unsigned i = 0; i < MCID.NumOperands; ++i) { + if (ARM::isVpred(MCID.OpInfo[i].OperandType)) + return i; + } + return -1; +} + +static bool isVectorPredicable(const MCInstrDesc &MCID) { + return findFirstVectorPredOperandIdx(MCID) != -1; +} // FIXME: We would really like to be able to tablegen'erate this. bool ARMAsmParser::validateInstruction(MCInst &Inst, @@ -6473,12 +7289,25 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, } else if (isThumbTwo() && MCID.isPredicable() && Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() != ARMCC::AL && Inst.getOpcode() != ARM::tBcc && - Inst.getOpcode() != ARM::t2Bcc) { + Inst.getOpcode() != ARM::t2Bcc && + Inst.getOpcode() != ARM::t2BFic) { return Error(Loc, "predicated instructions must be in IT block"); } else if (!isThumb() && !useImplicitITARM() && MCID.isPredicable() && Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() != ARMCC::AL) { return Warning(Loc, "predicated instructions should be in IT block"); + } else if (!MCID.isPredicable()) { + // Check the instruction doesn't have a predicate operand anyway + // that it's not allowed to use. Sometimes this happens in order + // to keep instructions the same shape even though one cannot + // legally be predicated, e.g. vmul.f16 vs vmul.f32. + for (unsigned i = 0, e = MCID.getNumOperands(); i != e; ++i) { + if (MCID.OpInfo[i].isPredicate()) { + if (Inst.getOperand(i).getImm() != ARMCC::AL) + return Error(Loc, "instruction is not predicable"); + break; + } + } } // PC-setting instructions in an IT block, but not the last instruction of @@ -6487,6 +7316,28 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, return Error(Loc, "instruction must be outside of IT block or the last instruction in an IT block"); } + if (inVPTBlock() && !instIsBreakpoint(Inst)) { + unsigned Bit = extractITMaskBit(VPTState.Mask, VPTState.CurPosition); + if (!isVectorPredicable(MCID)) + return Error(Loc, "instruction in VPT block must be predicable"); + unsigned Pred = Inst.getOperand(findFirstVectorPredOperandIdx(MCID)).getImm(); + unsigned VPTPred = Bit ? ARMVCC::Else : ARMVCC::Then; + if (Pred != VPTPred) { + SMLoc PredLoc; + for (unsigned I = 1; I < Operands.size(); ++I) + if (static_cast<ARMOperand &>(*Operands[I]).isVPTPred()) + PredLoc = Operands[I]->getStartLoc(); + return Error(PredLoc, "incorrect predication in VPT block; got '" + + StringRef(ARMVPTPredToString(ARMVCC::VPTCodes(Pred))) + + "', but expected '" + + ARMVPTPredToString(ARMVCC::VPTCodes(VPTPred)) + "'"); + } + } + else if (isVectorPredicable(MCID) && + Inst.getOperand(findFirstVectorPredOperandIdx(MCID)).getImm() != + ARMVCC::None) + return Error(Loc, "VPT predicated instructions must be in VPT block"); + const unsigned Opcode = Inst.getOpcode(); switch (Opcode) { case ARM::t2IT: { @@ -6496,11 +7347,10 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, unsigned Cond = Inst.getOperand(0).getImm(); unsigned Mask = Inst.getOperand(1).getImm(); - // Mask hasn't been modified to the IT instruction encoding yet so - // conditions only allowing a 't' are a block of 1s starting at bit 3 - // followed by all 0s. Easiest way is to just list the 4 possibilities. - if (Cond == ARMCC::AL && Mask != 8 && Mask != 12 && Mask != 14 && - Mask != 15) + // Conditions only allowing a 't' are those with no set bit except + // the lowest-order one that indicates the end of the sequence. In + // other words, powers of 2. + if (Cond == ARMCC::AL && countPopulation(Mask) != 1) return Error(Loc, "unpredictable IT predicate sequence"); break; } @@ -6609,6 +7459,54 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, "destination register and base register can't be identical"); return false; } + + case ARM::MVE_VLDRBU8_rq: + case ARM::MVE_VLDRBU16_rq: + case ARM::MVE_VLDRBS16_rq: + case ARM::MVE_VLDRBU32_rq: + case ARM::MVE_VLDRBS32_rq: + case ARM::MVE_VLDRHU16_rq: + case ARM::MVE_VLDRHU16_rq_u: + case ARM::MVE_VLDRHU32_rq: + case ARM::MVE_VLDRHU32_rq_u: + case ARM::MVE_VLDRHS32_rq: + case ARM::MVE_VLDRHS32_rq_u: + case ARM::MVE_VLDRWU32_rq: + case ARM::MVE_VLDRWU32_rq_u: + case ARM::MVE_VLDRDU64_rq: + case ARM::MVE_VLDRDU64_rq_u: + case ARM::MVE_VLDRWU32_qi: + case ARM::MVE_VLDRWU32_qi_pre: + case ARM::MVE_VLDRDU64_qi: + case ARM::MVE_VLDRDU64_qi_pre: { + // Qd must be different from Qm. + unsigned QdIdx = 0, QmIdx = 2; + bool QmIsPointer = false; + switch (Opcode) { + case ARM::MVE_VLDRWU32_qi: + case ARM::MVE_VLDRDU64_qi: + QmIdx = 1; + QmIsPointer = true; + break; + case ARM::MVE_VLDRWU32_qi_pre: + case ARM::MVE_VLDRDU64_qi_pre: + QdIdx = 1; + QmIsPointer = true; + break; + } + + const unsigned Qd = MRI->getEncodingValue(Inst.getOperand(QdIdx).getReg()); + const unsigned Qm = MRI->getEncodingValue(Inst.getOperand(QmIdx).getReg()); + + if (Qd == Qm) { + return Error(Operands[3]->getStartLoc(), + Twine("destination vector register and vector ") + + (QmIsPointer ? "pointer" : "offset") + + " register can't be identical"); + } + return false; + } + case ARM::SBFX: case ARM::t2SBFX: case ARM::UBFX: @@ -6776,6 +7674,20 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, } break; + case ARM::t2ADDri: + case ARM::t2ADDri12: + case ARM::t2ADDrr: + case ARM::t2ADDrs: + case ARM::t2SUBri: + case ARM::t2SUBri12: + case ARM::t2SUBrr: + case ARM::t2SUBrs: + if (Inst.getOperand(0).getReg() == ARM::SP && + Inst.getOperand(1).getReg() != ARM::SP) + return Error(Operands[4]->getStartLoc(), + "source register must be sp if destination is sp"); + break; + // Final range checking for Thumb unconditional branch instructions. case ARM::tB: if (!(static_cast<ARMOperand &>(*Operands[2])).isSignedOffset<11, 1>()) @@ -6845,6 +7757,61 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, "code specified"); break; } + case ARM::t2BFi: + case ARM::t2BFr: + case ARM::t2BFLi: + case ARM::t2BFLr: { + if (!static_cast<ARMOperand &>(*Operands[2]).isUnsignedOffset<4, 1>() || + (Inst.getOperand(0).isImm() && Inst.getOperand(0).getImm() == 0)) + return Error(Operands[2]->getStartLoc(), + "branch location out of range or not a multiple of 2"); + + if (Opcode == ARM::t2BFi) { + if (!static_cast<ARMOperand &>(*Operands[3]).isSignedOffset<16, 1>()) + return Error(Operands[3]->getStartLoc(), + "branch target out of range or not a multiple of 2"); + } else if (Opcode == ARM::t2BFLi) { + if (!static_cast<ARMOperand &>(*Operands[3]).isSignedOffset<18, 1>()) + return Error(Operands[3]->getStartLoc(), + "branch target out of range or not a multiple of 2"); + } + break; + } + case ARM::t2BFic: { + if (!static_cast<ARMOperand &>(*Operands[1]).isUnsignedOffset<4, 1>() || + (Inst.getOperand(0).isImm() && Inst.getOperand(0).getImm() == 0)) + return Error(Operands[1]->getStartLoc(), + "branch location out of range or not a multiple of 2"); + + if (!static_cast<ARMOperand &>(*Operands[2]).isSignedOffset<16, 1>()) + return Error(Operands[2]->getStartLoc(), + "branch target out of range or not a multiple of 2"); + + assert(Inst.getOperand(0).isImm() == Inst.getOperand(2).isImm() && + "branch location and else branch target should either both be " + "immediates or both labels"); + + if (Inst.getOperand(0).isImm() && Inst.getOperand(2).isImm()) { + int Diff = Inst.getOperand(2).getImm() - Inst.getOperand(0).getImm(); + if (Diff != 4 && Diff != 2) + return Error( + Operands[3]->getStartLoc(), + "else branch target must be 2 or 4 greater than the branch location"); + } + break; + } + case ARM::t2CLRM: { + for (unsigned i = 2; i < Inst.getNumOperands(); i++) { + if (Inst.getOperand(i).isReg() && + !ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains( + Inst.getOperand(i).getReg())) { + return Error(Operands[2]->getStartLoc(), + "invalid register in register list. Valid registers are " + "r0-r12, lr/r14 and APSR."); + } + } + break; + } case ARM::DSB: case ARM::t2DSB: { @@ -6892,6 +7859,39 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, "list of registers must be at least 1 and at most 16"); break; } + case ARM::MVE_VQDMULLs32bh: + case ARM::MVE_VQDMULLs32th: + case ARM::MVE_VCMULf32: + case ARM::MVE_VMULLs32bh: + case ARM::MVE_VMULLs32th: + case ARM::MVE_VMULLu32bh: + case ARM::MVE_VMULLu32th: { + if (Operands[3]->getReg() == Operands[4]->getReg()) { + return Error (Operands[3]->getStartLoc(), + "Qd register and Qn register can't be identical"); + } + if (Operands[3]->getReg() == Operands[5]->getReg()) { + return Error (Operands[3]->getStartLoc(), + "Qd register and Qm register can't be identical"); + } + break; + } + case ARM::MVE_VMOV_rr_q: { + if (Operands[4]->getReg() != Operands[6]->getReg()) + return Error (Operands[4]->getStartLoc(), "Q-registers must be the same"); + if (static_cast<ARMOperand &>(*Operands[5]).getVectorIndex() != + static_cast<ARMOperand &>(*Operands[7]).getVectorIndex() + 2) + return Error (Operands[5]->getStartLoc(), "Q-register indexes must be 2 and 0 or 3 and 1"); + break; + } + case ARM::MVE_VMOV_q_rr: { + if (Operands[2]->getReg() != Operands[4]->getReg()) + return Error (Operands[2]->getStartLoc(), "Q-registers must be the same"); + if (static_cast<ARMOperand &>(*Operands[3]).getVectorIndex() != + static_cast<ARMOperand &>(*Operands[5]).getVectorIndex() + 2) + return Error (Operands[3]->getStartLoc(), "Q-register indexes must be 2 and 0 or 3 and 1"); + break; + } } return false; @@ -7168,6 +8168,50 @@ bool ARMAsmParser::processInstruction(MCInst &Inst, } switch (Inst.getOpcode()) { + case ARM::MVE_VORNIZ0v4i32: + case ARM::MVE_VORNIZ0v8i16: + case ARM::MVE_VORNIZ8v4i32: + case ARM::MVE_VORNIZ8v8i16: + case ARM::MVE_VORNIZ16v4i32: + case ARM::MVE_VORNIZ24v4i32: + case ARM::MVE_VANDIZ0v4i32: + case ARM::MVE_VANDIZ0v8i16: + case ARM::MVE_VANDIZ8v4i32: + case ARM::MVE_VANDIZ8v8i16: + case ARM::MVE_VANDIZ16v4i32: + case ARM::MVE_VANDIZ24v4i32: { + unsigned Opcode; + bool imm16 = false; + switch(Inst.getOpcode()) { + case ARM::MVE_VORNIZ0v4i32: Opcode = ARM::MVE_VORRIZ0v4i32; break; + case ARM::MVE_VORNIZ0v8i16: Opcode = ARM::MVE_VORRIZ0v8i16; imm16 = true; break; + case ARM::MVE_VORNIZ8v4i32: Opcode = ARM::MVE_VORRIZ8v4i32; break; + case ARM::MVE_VORNIZ8v8i16: Opcode = ARM::MVE_VORRIZ8v8i16; imm16 = true; break; + case ARM::MVE_VORNIZ16v4i32: Opcode = ARM::MVE_VORRIZ16v4i32; break; + case ARM::MVE_VORNIZ24v4i32: Opcode = ARM::MVE_VORRIZ24v4i32; break; + case ARM::MVE_VANDIZ0v4i32: Opcode = ARM::MVE_VBICIZ0v4i32; break; + case ARM::MVE_VANDIZ0v8i16: Opcode = ARM::MVE_VBICIZ0v8i16; imm16 = true; break; + case ARM::MVE_VANDIZ8v4i32: Opcode = ARM::MVE_VBICIZ8v4i32; break; + case ARM::MVE_VANDIZ8v8i16: Opcode = ARM::MVE_VBICIZ8v8i16; imm16 = true; break; + case ARM::MVE_VANDIZ16v4i32: Opcode = ARM::MVE_VBICIZ16v4i32; break; + case ARM::MVE_VANDIZ24v4i32: Opcode = ARM::MVE_VBICIZ24v4i32; break; + default: llvm_unreachable("unexpected opcode"); + } + + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(Inst.getOperand(0)); + TmpInst.addOperand(Inst.getOperand(1)); + + // invert immediate + unsigned imm = ~Inst.getOperand(2).getImm() & (imm16 ? 0xffff : 0xffffffff); + TmpInst.addOperand(MCOperand::createImm(imm)); + + TmpInst.addOperand(Inst.getOperand(3)); + TmpInst.addOperand(Inst.getOperand(4)); + Inst = TmpInst; + return true; + } // Alias for alternate form of 'ldr{,b}t Rt, [Rn], #imm' instruction. case ARM::LDRT_POST: case ARM::LDRBT_POST: { @@ -8990,15 +10034,11 @@ bool ARMAsmParser::processInstruction(MCInst &Inst, } case ARM::ITasm: case ARM::t2IT: { - MCOperand &MO = Inst.getOperand(1); - unsigned Mask = MO.getImm(); - ARMCC::CondCodes Cond = ARMCC::CondCodes(Inst.getOperand(0).getImm()); - // Set up the IT block state according to the IT instruction we just // matched. assert(!inITBlock() && "nested IT blocks?!"); - startExplicitITBlock(Cond, Mask); - MO.setImm(getITMaskEncoding()); + startExplicitITBlock(ARMCC::CondCodes(Inst.getOperand(0).getImm()), + Inst.getOperand(1).getImm()); break; } case ARM::t2LSLrr: @@ -9074,6 +10114,35 @@ bool ARMAsmParser::processInstruction(MCInst &Inst, return true; } return false; + case ARM::MVE_VPST: + case ARM::MVE_VPTv16i8: + case ARM::MVE_VPTv8i16: + case ARM::MVE_VPTv4i32: + case ARM::MVE_VPTv16u8: + case ARM::MVE_VPTv8u16: + case ARM::MVE_VPTv4u32: + case ARM::MVE_VPTv16s8: + case ARM::MVE_VPTv8s16: + case ARM::MVE_VPTv4s32: + case ARM::MVE_VPTv4f32: + case ARM::MVE_VPTv8f16: + case ARM::MVE_VPTv16i8r: + case ARM::MVE_VPTv8i16r: + case ARM::MVE_VPTv4i32r: + case ARM::MVE_VPTv16u8r: + case ARM::MVE_VPTv8u16r: + case ARM::MVE_VPTv4u32r: + case ARM::MVE_VPTv16s8r: + case ARM::MVE_VPTv8s16r: + case ARM::MVE_VPTv4s32r: + case ARM::MVE_VPTv4f32r: + case ARM::MVE_VPTv8f16r: { + assert(!inVPTBlock() && "Nested VPT blocks are not allowed"); + MCOperand &MO = Inst.getOperand(0); + VPTState.Mask = MO.getImm(); + VPTState.CurPosition = 0; + break; + } } return false; } @@ -9138,18 +10207,50 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) { return Match_RequiresV8; } - // Use of SP for VMRS/VMSR is only allowed in ARM mode with the exception of - // ARMv8-A. - if ((Inst.getOpcode() == ARM::VMRS || Inst.getOpcode() == ARM::VMSR) && - Inst.getOperand(0).getReg() == ARM::SP && (isThumb() && !hasV8Ops())) - return Match_InvalidOperand; + switch (Inst.getOpcode()) { + case ARM::VMRS: + case ARM::VMSR: + case ARM::VMRS_FPCXTS: + case ARM::VMRS_FPCXTNS: + case ARM::VMSR_FPCXTS: + case ARM::VMSR_FPCXTNS: + case ARM::VMRS_FPSCR_NZCVQC: + case ARM::VMSR_FPSCR_NZCVQC: + case ARM::FMSTAT: + case ARM::VMRS_VPR: + case ARM::VMRS_P0: + case ARM::VMSR_VPR: + case ARM::VMSR_P0: + // Use of SP for VMRS/VMSR is only allowed in ARM mode with the exception of + // ARMv8-A. + if (Inst.getOperand(0).isReg() && Inst.getOperand(0).getReg() == ARM::SP && + (isThumb() && !hasV8Ops())) + return Match_InvalidOperand; + break; + default: + break; + } for (unsigned I = 0; I < MCID.NumOperands; ++I) if (MCID.OpInfo[I].RegClass == ARM::rGPRRegClassID) { // rGPRRegClass excludes PC, and also excluded SP before ARMv8 - if ((Inst.getOperand(I).getReg() == ARM::SP) && !hasV8Ops()) + const auto &Op = Inst.getOperand(I); + if (!Op.isReg()) { + // This can happen in awkward cases with tied operands, e.g. a + // writeback load/store with a complex addressing mode in + // which there's an output operand corresponding to the + // updated written-back base register: the Tablegen-generated + // AsmMatcher will have written a placeholder operand to that + // slot in the form of an immediate 0, because it can't + // generate the register part of the complex addressing-mode + // operand ahead of time. + continue; + } + + unsigned Reg = Op.getReg(); + if ((Reg == ARM::SP) && !hasV8Ops()) return Match_RequiresV8; - else if (Inst.getOperand(I).getReg() == ARM::PC) + else if (Reg == ARM::PC) return Match_InvalidOperand; } @@ -9268,7 +10369,7 @@ unsigned ARMAsmParser::MatchInstruction(OperandVector &Operands, MCInst &Inst, return PlainMatchResult; } -static std::string ARMMnemonicSpellCheck(StringRef S, uint64_t FBS, +static std::string ARMMnemonicSpellCheck(StringRef S, const FeatureBitset &FBS, unsigned VariantID = 0); static const char *getSubtargetFeatureName(uint64_t Val); @@ -9296,6 +10397,7 @@ bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, // Still progress the IT block, otherwise one wrong condition causes // nasty cascading errors. forwardITPosition(); + forwardVPTPosition(); return true; } @@ -9322,6 +10424,7 @@ bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, // and process gets a consistent answer about whether we're in an IT // block. forwardITPosition(); + forwardVPTPosition(); // ITasm is an ARM mode pseudo-instruction that just sets the ITblock and // doesn't actually encode. @@ -9341,7 +10444,7 @@ bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, ReportNearMisses(NearMisses, IDLoc, Operands); return true; case Match_MnemonicFail: { - uint64_t FBS = ComputeAvailableFeatures(getSTI().getFeatureBits()); + FeatureBitset FBS = ComputeAvailableFeatures(getSTI().getFeatureBits()); std::string Suggestion = ARMMnemonicSpellCheck( ((ARMOperand &)*Operands[0]).getToken(), FBS); return Error(IDLoc, "invalid instruction" + Suggestion, @@ -10384,11 +11487,11 @@ ARMAsmParser::getCustomOperandDiag(ARMMatchResultTy MatchError) { : "operand must be a register in range [r0, r12] or r14"; // DPR contains 16 registers for some FPUs, and 32 for others. case Match_DPR: - return hasD16() ? "operand must be a register in range [d0, d15]" - : "operand must be a register in range [d0, d31]"; + return hasD32() ? "operand must be a register in range [d0, d31]" + : "operand must be a register in range [d0, d15]"; case Match_DPR_RegList: - return hasD16() ? "operand must be a list of registers in range [d0, d15]" - : "operand must be a list of registers in range [d0, d31]"; + return hasD32() ? "operand must be a list of registers in range [d0, d31]" + : "operand must be a list of registers in range [d0, d15]"; // For all other diags, use the static string from tablegen. default: @@ -10416,7 +11519,7 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn, // variants of an instruction that take 8- and 16-bit immediates, we want // to only report the widest one. std::multimap<unsigned, unsigned> OperandMissesSeen; - SmallSet<uint64_t, 4> FeatureMissesSeen; + SmallSet<FeatureBitset, 4> FeatureMissesSeen; bool ReportedTooFewOperands = false; // Process the near-misses in reverse order, so that we see more general ones @@ -10467,7 +11570,7 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn, break; } case NearMissInfo::NearMissFeature: { - uint64_t MissingFeatures = I.getFeatures(); + const FeatureBitset &MissingFeatures = I.getFeatures(); // Don't report the same set of features twice. if (FeatureMissesSeen.count(MissingFeatures)) break; @@ -10475,20 +11578,21 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn, // Special case: don't report a feature set which includes arm-mode for // targets that don't have ARM mode. - if ((MissingFeatures & Feature_IsARM) && !hasARM()) + if (MissingFeatures.test(Feature_IsARMBit) && !hasARM()) break; // Don't report any near-misses that both require switching instruction // set, and adding other subtarget features. - if (isThumb() && (MissingFeatures & Feature_IsARM) && - (MissingFeatures & ~Feature_IsARM)) + if (isThumb() && MissingFeatures.test(Feature_IsARMBit) && + MissingFeatures.count() > 1) break; - if (!isThumb() && (MissingFeatures & Feature_IsThumb) && - (MissingFeatures & ~Feature_IsThumb)) + if (!isThumb() && MissingFeatures.test(Feature_IsThumbBit) && + MissingFeatures.count() > 1) break; - if (!isThumb() && (MissingFeatures & Feature_IsThumb2) && - (MissingFeatures & ~(Feature_IsThumb2 | Feature_IsThumb))) + if (!isThumb() && MissingFeatures.test(Feature_IsThumb2Bit) && + (MissingFeatures & ~FeatureBitset({Feature_IsThumb2Bit, + Feature_IsThumbBit})).any()) break; - if (isMClass() && (MissingFeatures & Feature_HasNEON)) + if (isMClass() && MissingFeatures.test(Feature_HasNEONBit)) break; NearMissMessage Message; @@ -10496,14 +11600,10 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn, raw_svector_ostream OS(Message.Message); OS << "instruction requires:"; - uint64_t Mask = 1; - for (unsigned MaskPos = 0; MaskPos < (sizeof(MissingFeatures) * 8 - 1); - ++MaskPos) { - if (MissingFeatures & Mask) { - OS << " " << getSubtargetFeatureName(MissingFeatures & Mask); - } - Mask <<= 1; - } + for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) + if (MissingFeatures.test(i)) + OS << ' ' << getSubtargetFeatureName(i); + NearMissesOut.emplace_back(Message); break; @@ -10579,38 +11679,44 @@ void ARMAsmParser::ReportNearMisses(SmallVectorImpl<NearMissInfo> &NearMisses, } } -// FIXME: This structure should be moved inside ARMTargetParser -// when we start to table-generate them, and we can use the ARM -// flags below, that were generated by table-gen. -static const struct { - const unsigned Kind; - const uint64_t ArchCheck; - const FeatureBitset Features; -} Extensions[] = { - { ARM::AEK_CRC, Feature_HasV8, {ARM::FeatureCRC} }, - { ARM::AEK_CRYPTO, Feature_HasV8, - {ARM::FeatureCrypto, ARM::FeatureNEON, ARM::FeatureFPARMv8} }, - { ARM::AEK_FP, Feature_HasV8, {ARM::FeatureFPARMv8} }, - { (ARM::AEK_HWDIVTHUMB | ARM::AEK_HWDIVARM), Feature_HasV7 | Feature_IsNotMClass, - {ARM::FeatureHWDivThumb, ARM::FeatureHWDivARM} }, - { ARM::AEK_MP, Feature_HasV7 | Feature_IsNotMClass, {ARM::FeatureMP} }, - { ARM::AEK_SIMD, Feature_HasV8, {ARM::FeatureNEON, ARM::FeatureFPARMv8} }, - { ARM::AEK_SEC, Feature_HasV6K, {ARM::FeatureTrustZone} }, - // FIXME: Only available in A-class, isel not predicated - { ARM::AEK_VIRT, Feature_HasV7, {ARM::FeatureVirtualization} }, - { ARM::AEK_FP16, Feature_HasV8_2a, {ARM::FeatureFPARMv8, ARM::FeatureFullFP16} }, - { ARM::AEK_RAS, Feature_HasV8, {ARM::FeatureRAS} }, - // FIXME: Unsupported extensions. - { ARM::AEK_OS, Feature_None, {} }, - { ARM::AEK_IWMMXT, Feature_None, {} }, - { ARM::AEK_IWMMXT2, Feature_None, {} }, - { ARM::AEK_MAVERICK, Feature_None, {} }, - { ARM::AEK_XSCALE, Feature_None, {} }, -}; - /// parseDirectiveArchExtension /// ::= .arch_extension [no]feature bool ARMAsmParser::parseDirectiveArchExtension(SMLoc L) { + // FIXME: This structure should be moved inside ARMTargetParser + // when we start to table-generate them, and we can use the ARM + // flags below, that were generated by table-gen. + static const struct { + const unsigned Kind; + const FeatureBitset ArchCheck; + const FeatureBitset Features; + } Extensions[] = { + { ARM::AEK_CRC, {Feature_HasV8Bit}, {ARM::FeatureCRC} }, + { ARM::AEK_CRYPTO, {Feature_HasV8Bit}, + {ARM::FeatureCrypto, ARM::FeatureNEON, ARM::FeatureFPARMv8} }, + { ARM::AEK_FP, {Feature_HasV8Bit}, + {ARM::FeatureVFP2_D16_SP, ARM::FeatureFPARMv8} }, + { (ARM::AEK_HWDIVTHUMB | ARM::AEK_HWDIVARM), + {Feature_HasV7Bit, Feature_IsNotMClassBit}, + {ARM::FeatureHWDivThumb, ARM::FeatureHWDivARM} }, + { ARM::AEK_MP, {Feature_HasV7Bit, Feature_IsNotMClassBit}, + {ARM::FeatureMP} }, + { ARM::AEK_SIMD, {Feature_HasV8Bit}, + {ARM::FeatureNEON, ARM::FeatureVFP2_D16_SP, ARM::FeatureFPARMv8} }, + { ARM::AEK_SEC, {Feature_HasV6KBit}, {ARM::FeatureTrustZone} }, + // FIXME: Only available in A-class, isel not predicated + { ARM::AEK_VIRT, {Feature_HasV7Bit}, {ARM::FeatureVirtualization} }, + { ARM::AEK_FP16, {Feature_HasV8_2aBit}, + {ARM::FeatureFPARMv8, ARM::FeatureFullFP16} }, + { ARM::AEK_RAS, {Feature_HasV8Bit}, {ARM::FeatureRAS} }, + { ARM::AEK_LOB, {Feature_HasV8_1MMainlineBit}, {ARM::FeatureLOB} }, + // FIXME: Unsupported extensions. + { ARM::AEK_OS, {}, {} }, + { ARM::AEK_IWMMXT, {}, {} }, + { ARM::AEK_IWMMXT2, {}, {} }, + { ARM::AEK_MAVERICK, {}, {} }, + { ARM::AEK_XSCALE, {}, {} }, + }; + MCAsmParser &Parser = getParser(); if (getLexer().isNot(AsmToken::Identifier)) @@ -10646,12 +11752,12 @@ bool ARMAsmParser::parseDirectiveArchExtension(SMLoc L) { "allowed for the current base architecture"); MCSubtargetInfo &STI = copySTI(); - FeatureBitset ToggleFeatures = EnableFeature - ? (~STI.getFeatureBits() & Extension.Features) - : ( STI.getFeatureBits() & Extension.Features); - - uint64_t Features = - ComputeAvailableFeatures(STI.ToggleFeature(ToggleFeatures)); + if (EnableFeature) { + STI.SetFeatureBitsTransitively(Extension.Features); + } else { + STI.ClearFeatureBitsTransitively(Extension.Features); + } + FeatureBitset Features = ComputeAvailableFeatures(STI.getFeatureBits()); setAvailableFeatures(Features); return false; } @@ -10675,6 +11781,18 @@ unsigned ARMAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, if (CE->getValue() == 0) return Match_Success; break; + case MCK__35_8: + if (Op.isImm()) + if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm())) + if (CE->getValue() == 8) + return Match_Success; + break; + case MCK__35_16: + if (Op.isImm()) + if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm())) + if (CE->getValue() == 16) + return Match_Success; + break; case MCK_ModImm: if (Op.isImm()) { const MCExpr *SOExpr = Op.getImm(); @@ -10698,3 +11816,76 @@ unsigned ARMAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, } return Match_InvalidOperand; } + +bool ARMAsmParser::isMnemonicVPTPredicable(StringRef Mnemonic, + StringRef ExtraToken) { + if (!hasMVE()) + return false; + + return Mnemonic.startswith("vabav") || Mnemonic.startswith("vaddv") || + Mnemonic.startswith("vaddlv") || Mnemonic.startswith("vminnmv") || + Mnemonic.startswith("vminnmav") || Mnemonic.startswith("vminv") || + Mnemonic.startswith("vminav") || Mnemonic.startswith("vmaxnmv") || + Mnemonic.startswith("vmaxnmav") || Mnemonic.startswith("vmaxv") || + Mnemonic.startswith("vmaxav") || Mnemonic.startswith("vmladav") || + Mnemonic.startswith("vrmlaldavh") || Mnemonic.startswith("vrmlalvh") || + Mnemonic.startswith("vmlsdav") || Mnemonic.startswith("vmlav") || + Mnemonic.startswith("vmlaldav") || Mnemonic.startswith("vmlalv") || + Mnemonic.startswith("vmaxnm") || Mnemonic.startswith("vminnm") || + Mnemonic.startswith("vmax") || Mnemonic.startswith("vmin") || + Mnemonic.startswith("vshlc") || Mnemonic.startswith("vmovlt") || + Mnemonic.startswith("vmovlb") || Mnemonic.startswith("vshll") || + Mnemonic.startswith("vrshrn") || Mnemonic.startswith("vshrn") || + Mnemonic.startswith("vqrshrun") || Mnemonic.startswith("vqshrun") || + Mnemonic.startswith("vqrshrn") || Mnemonic.startswith("vqshrn") || + Mnemonic.startswith("vbic") || Mnemonic.startswith("vrev64") || + Mnemonic.startswith("vrev32") || Mnemonic.startswith("vrev16") || + Mnemonic.startswith("vmvn") || Mnemonic.startswith("veor") || + Mnemonic.startswith("vorn") || Mnemonic.startswith("vorr") || + Mnemonic.startswith("vand") || Mnemonic.startswith("vmul") || + Mnemonic.startswith("vqrdmulh") || Mnemonic.startswith("vqdmulh") || + Mnemonic.startswith("vsub") || Mnemonic.startswith("vadd") || + Mnemonic.startswith("vqsub") || Mnemonic.startswith("vqadd") || + Mnemonic.startswith("vabd") || Mnemonic.startswith("vrhadd") || + Mnemonic.startswith("vhsub") || Mnemonic.startswith("vhadd") || + Mnemonic.startswith("vdup") || Mnemonic.startswith("vcls") || + Mnemonic.startswith("vclz") || Mnemonic.startswith("vneg") || + Mnemonic.startswith("vabs") || Mnemonic.startswith("vqneg") || + Mnemonic.startswith("vqabs") || + (Mnemonic.startswith("vrint") && Mnemonic != "vrintr") || + Mnemonic.startswith("vcmla") || Mnemonic.startswith("vfma") || + Mnemonic.startswith("vfms") || Mnemonic.startswith("vcadd") || + Mnemonic.startswith("vadd") || Mnemonic.startswith("vsub") || + Mnemonic.startswith("vshl") || Mnemonic.startswith("vqshl") || + Mnemonic.startswith("vqrshl") || Mnemonic.startswith("vrshl") || + Mnemonic.startswith("vsri") || Mnemonic.startswith("vsli") || + Mnemonic.startswith("vrshr") || Mnemonic.startswith("vshr") || + Mnemonic.startswith("vpsel") || Mnemonic.startswith("vcmp") || + Mnemonic.startswith("vqdmladh") || Mnemonic.startswith("vqrdmladh") || + Mnemonic.startswith("vqdmlsdh") || Mnemonic.startswith("vqrdmlsdh") || + Mnemonic.startswith("vcmul") || Mnemonic.startswith("vrmulh") || + Mnemonic.startswith("vqmovn") || Mnemonic.startswith("vqmovun") || + Mnemonic.startswith("vmovnt") || Mnemonic.startswith("vmovnb") || + Mnemonic.startswith("vmaxa") || Mnemonic.startswith("vmaxnma") || + Mnemonic.startswith("vhcadd") || Mnemonic.startswith("vadc") || + Mnemonic.startswith("vsbc") || Mnemonic.startswith("vrshr") || + Mnemonic.startswith("vshr") || Mnemonic.startswith("vstrb") || + Mnemonic.startswith("vldrb") || + (Mnemonic.startswith("vstrh") && Mnemonic != "vstrhi") || + (Mnemonic.startswith("vldrh") && Mnemonic != "vldrhi") || + Mnemonic.startswith("vstrw") || Mnemonic.startswith("vldrw") || + Mnemonic.startswith("vldrd") || Mnemonic.startswith("vstrd") || + Mnemonic.startswith("vqdmull") || Mnemonic.startswith("vbrsr") || + Mnemonic.startswith("vfmas") || Mnemonic.startswith("vmlas") || + Mnemonic.startswith("vmla") || Mnemonic.startswith("vqdmlash") || + Mnemonic.startswith("vqdmlah") || Mnemonic.startswith("vqrdmlash") || + Mnemonic.startswith("vqrdmlah") || Mnemonic.startswith("viwdup") || + Mnemonic.startswith("vdwdup") || Mnemonic.startswith("vidup") || + Mnemonic.startswith("vddup") || Mnemonic.startswith("vctp") || + Mnemonic.startswith("vpnot") || Mnemonic.startswith("vbic") || + Mnemonic.startswith("vrmlsldavh") || Mnemonic.startswith("vmlsldav") || + Mnemonic.startswith("vcvt") || + (Mnemonic.startswith("vmov") && + !(ExtraToken == ".f16" || ExtraToken == ".32" || + ExtraToken == ".16" || ExtraToken == ".8")); +} diff --git a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index 61bec04678dd..673691ebd93e 100644 --- a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -1,15 +1,16 @@ //===- ARMDisassembler.cpp - Disassembler for ARM/Thumb ISA ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "ARMBaseInstrInfo.h" #include "MCTargetDesc/ARMAddressingModes.h" #include "MCTargetDesc/ARMBaseInfo.h" #include "MCTargetDesc/ARMMCTargetDesc.h" +#include "TargetInfo/ARMTargetInfo.h" #include "Utils/ARMBaseInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -63,22 +64,19 @@ namespace { return ITStates.size() == 1; } - // Called when decoding an IT instruction. Sets the IT state for the following - // instructions that for the IT block. Firstcond and Mask correspond to the - // fields in the IT instruction encoding. + // Called when decoding an IT instruction. Sets the IT state for + // the following instructions that for the IT block. Firstcond + // corresponds to the field in the IT instruction encoding; Mask + // is in the MCOperand format in which 1 means 'else' and 0 'then'. void setITState(char Firstcond, char Mask) { // (3 - the number of trailing zeros) is the number of then / else. - unsigned CondBit0 = Firstcond & 1; unsigned NumTZ = countTrailingZeros<uint8_t>(Mask); unsigned char CCBits = static_cast<unsigned char>(Firstcond & 0xf); assert(NumTZ <= 3 && "Invalid IT mask!"); // push condition codes onto the stack the correct order for the pops for (unsigned Pos = NumTZ+1; Pos <= 3; ++Pos) { - bool T = ((Mask >> Pos) & 1) == CondBit0; - if (T) - ITStates.push_back(CCBits); - else - ITStates.push_back(CCBits ^ 1); + unsigned Else = (Mask >> Pos) & 1; + ITStates.push_back(CCBits ^ Else); } ITStates.push_back(CCBits); } @@ -87,6 +85,47 @@ namespace { std::vector<unsigned char> ITStates; }; + class VPTStatus + { + public: + unsigned getVPTPred() { + unsigned Pred = ARMVCC::None; + if (instrInVPTBlock()) + Pred = VPTStates.back(); + return Pred; + } + + void advanceVPTState() { + VPTStates.pop_back(); + } + + bool instrInVPTBlock() { + return !VPTStates.empty(); + } + + bool instrLastInVPTBlock() { + return VPTStates.size() == 1; + } + + void setVPTState(char Mask) { + // (3 - the number of trailing zeros) is the number of then / else. + unsigned NumTZ = countTrailingZeros<uint8_t>(Mask); + assert(NumTZ <= 3 && "Invalid VPT mask!"); + // push predicates onto the stack the correct order for the pops + for (unsigned Pos = NumTZ+1; Pos <= 3; ++Pos) { + bool T = ((Mask >> Pos) & 1) == 0; + if (T) + VPTStates.push_back(ARMVCC::Then); + else + VPTStates.push_back(ARMVCC::Else); + } + VPTStates.push_back(ARMVCC::Then); + } + + private: + SmallVector<unsigned char, 4> VPTStates; + }; + /// ARM disassembler for all ARM platforms. class ARMDisassembler : public MCDisassembler { public: @@ -100,27 +139,23 @@ public: ArrayRef<uint8_t> Bytes, uint64_t Address, raw_ostream &VStream, raw_ostream &CStream) const override; -}; -/// Thumb disassembler for all Thumb platforms. -class ThumbDisassembler : public MCDisassembler { -public: - ThumbDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) : - MCDisassembler(STI, Ctx) { - } - - ~ThumbDisassembler() override = default; +private: + DecodeStatus getARMInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef<uint8_t> Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const; - DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, - ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &VStream, - raw_ostream &CStream) const override; + DecodeStatus getThumbInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef<uint8_t> Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const; -private: mutable ITStatus ITBlock; + mutable VPTStatus VPTBlock; DecodeStatus AddThumbPredicate(MCInst&) const; - void UpdateThumbVFPPredicate(MCInst&) const; + void UpdateThumbVFPPredicate(DecodeStatus &, MCInst&) const; }; } // end anonymous namespace @@ -144,12 +179,23 @@ static bool Check(DecodeStatus &Out, DecodeStatus In) { // Definitions are further down. static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeCLRMGPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodetGPROddRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodetGPREvenRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPRnopcRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPRwithAPSRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeGPRwithZRRegisterClass(MCInst &Inst, + unsigned RegNo, uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeGPRwithZRnospRegisterClass( + MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodetGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodetcGPRRegisterClass(MCInst &Inst, unsigned RegNo, @@ -166,12 +212,20 @@ static DecodeStatus DecodeDPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeDPR_8RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeSPR_8RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeDPR_VFP2RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeQPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeQQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeQQQQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeDPairRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeDPairSpacedRegisterClass(MCInst &Inst, @@ -262,6 +316,10 @@ static DecodeStatus DecodeVLD4DupInstruction(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); static DecodeStatus DecodeNEONModImmInstruction(MCInst &Inst,unsigned Val, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMVEModImmInstruction(MCInst &Inst,unsigned Val, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMVEVADCInstruction(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeVSHLMaxInstruction(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); static DecodeStatus DecodeShiftRight8Imm(MCInst &Inst, unsigned Val, @@ -276,6 +334,11 @@ static DecodeStatus DecodeTBLInstruction(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodePostIdxReg(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMveAddrModeRQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); +template<int shift> +static DecodeStatus DecodeMveAddrModeQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeCoprocessor(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemBarrierOption(MCInst &Inst, unsigned Insn, @@ -324,6 +387,8 @@ static DecodeStatus DecodeVCVTD(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeVCVTQ(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeVCVTImmOperand(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeNEONComplexLane64Instruction(MCInst &Inst, unsigned Val, uint64_t Address, @@ -359,14 +424,28 @@ static DecodeStatus DecodeT2LoadLabel(MCInst &Inst, unsigned Insn, uint64_t Address, const void* Decoder); static DecodeStatus DecodeT2Imm8S4(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeT2Imm7S4(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeT2AddrModeImm8s4(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeT2AddrModeImm7s4(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); static DecodeStatus DecodeT2AddrModeImm0_1020s4(MCInst &Inst,unsigned Val, uint64_t Address, const void *Decoder); static DecodeStatus DecodeT2Imm8(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +template<int shift> +static DecodeStatus DecodeT2Imm7(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeT2AddrModeImm8(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +template<int shift> +static DecodeStatus DecodeTAddrModeImm7(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +template<int shift, int WriteBack> +static DecodeStatus DecodeT2AddrModeImm7(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); static DecodeStatus DecodeThumbAddSPImm(MCInst &Inst, uint16_t Val, uint64_t Address, const void *Decoder); static DecodeStatus DecodeThumbAddSPReg(MCInst &Inst, uint16_t Insn, @@ -409,6 +488,82 @@ static DecodeStatus DecoderForMRRC2AndMCRR2(MCInst &Inst, unsigned Val, static DecodeStatus DecodeForVMRSandVMSR(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +template <bool isSigned, bool isNeg, bool zeroPermitted, int size> +static DecodeStatus DecodeBFLabelOperand(MCInst &Inst, unsigned val, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeBFAfterTargetOperand(MCInst &Inst, unsigned val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodePredNoALOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeLOLoop(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeLongShiftOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeVSCCLRM(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeVPTMaskOperand(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeVpredROperand(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeRestrictedIPredicateOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeRestrictedSPredicateOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeRestrictedUPredicateOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeRestrictedFPPredicateOperand(MCInst &Inst, + unsigned Val, + uint64_t Address, + const void *Decoder); +template<bool Writeback> +static DecodeStatus DecodeVSTRVLDR_SYSREG(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); +template<int shift> +static DecodeStatus DecodeMVE_MEM_1_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +template<int shift> +static DecodeStatus DecodeMVE_MEM_2_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +template<int shift> +static DecodeStatus DecodeMVE_MEM_3_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +template<unsigned MinLog, unsigned MaxLog> +static DecodeStatus DecodePowerTwoOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +template <int shift> +static DecodeStatus DecodeExpandedImmOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +template<unsigned start> +static DecodeStatus DecodeMVEPairVectorIndexOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeMVEVMOVQtoDReg(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeMVEVMOVDRegtoQ(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeMVEVCVTt1fp(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); +typedef DecodeStatus OperandDecoder(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder); +template<bool scalar, OperandDecoder predicate_decoder> +static DecodeStatus DecodeMVEVCMP(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMveVCTP(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMVEOverlappingLongShift(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); #include "ARMGenDisassemblerTables.inc" static MCDisassembler *createARMDisassembler(const Target &T, @@ -417,12 +572,6 @@ static MCDisassembler *createARMDisassembler(const Target &T, return new ARMDisassembler(STI, Ctx); } -static MCDisassembler *createThumbDisassembler(const Target &T, - const MCSubtargetInfo &STI, - MCContext &Ctx) { - return new ThumbDisassembler(STI, Ctx); -} - // Post-decoding checks static DecodeStatus checkDecodedInstruction(MCInst &MI, uint64_t &Size, uint64_t Address, raw_ostream &OS, @@ -440,6 +589,18 @@ static DecodeStatus checkDecodedInstruction(MCInst &MI, uint64_t &Size, return MCDisassembler::SoftFail; return Result; } + case ARM::t2ADDri: + case ARM::t2ADDri12: + case ARM::t2ADDrr: + case ARM::t2ADDrs: + case ARM::t2SUBri: + case ARM::t2SUBri12: + case ARM::t2SUBrr: + case ARM::t2SUBrs: + if (MI.getOperand(0).getReg() == ARM::SP && + MI.getOperand(1).getReg() != ARM::SP) + return MCDisassembler::SoftFail; + return Result; default: return Result; } } @@ -448,6 +609,16 @@ DecodeStatus ARMDisassembler::getInstruction(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address, raw_ostream &OS, raw_ostream &CS) const { + if (STI.getFeatureBits()[ARM::ModeThumb]) + return getThumbInstruction(MI, Size, Bytes, Address, OS, CS); + return getARMInstruction(MI, Size, Bytes, Address, OS, CS); +} + +DecodeStatus ARMDisassembler::getARMInstruction(MCInst &MI, uint64_t &Size, + ArrayRef<uint8_t> Bytes, + uint64_t Address, + raw_ostream &OS, + raw_ostream &CS) const { CommentStream = &CS; assert(!STI.getFeatureBits()[ARM::ModeThumb] && @@ -569,12 +740,22 @@ static void AddThumb1SBit(MCInst &MI, bool InITBlock) { MI.insert(I, MCOperand::createReg(InITBlock ? 0 : ARM::CPSR)); } +static bool isVectorPredicable(unsigned Opcode) { + const MCOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + unsigned short NumOps = ARMInsts[Opcode].NumOperands; + for (unsigned i = 0; i < NumOps; ++i) { + if (ARM::isVpred(OpInfo[i].OperandType)) + return true; + } + return false; +} + // Most Thumb instructions don't have explicit predicates in the // encoding, but rather get their predicates from IT context. We need // to fix up the predicate operands using this context information as a // post-pass. MCDisassembler::DecodeStatus -ThumbDisassembler::AddThumbPredicate(MCInst &MI) const { +ARMDisassembler::AddThumbPredicate(MCInst &MI) const { MCDisassembler::DecodeStatus S = Success; const FeatureBitset &FeatureBits = getSubtargetInfo().getFeatureBits(); @@ -590,6 +771,10 @@ ThumbDisassembler::AddThumbPredicate(MCInst &MI) const { case ARM::t2CPS3p: case ARM::t2CPS2p: case ARM::t2CPS1p: + case ARM::t2CSEL: + case ARM::t2CSINC: + case ARM::t2CSINV: + case ARM::t2CSNEG: case ARM::tMOVSr: case ARM::tSETEND: // Some instructions (mostly conditional branches) are not @@ -616,37 +801,66 @@ ThumbDisassembler::AddThumbPredicate(MCInst &MI) const { break; } - // If we're in an IT block, base the predicate on that. Otherwise, + // Warn on non-VPT predicable instruction in a VPT block and a VPT + // predicable instruction in an IT block + if ((!isVectorPredicable(MI.getOpcode()) && VPTBlock.instrInVPTBlock()) || + (isVectorPredicable(MI.getOpcode()) && ITBlock.instrInITBlock())) + S = SoftFail; + + // If we're in an IT/VPT block, base the predicate on that. Otherwise, // assume a predicate of AL. - unsigned CC; - CC = ITBlock.getITCC(); - if (CC == 0xF) - CC = ARMCC::AL; - if (ITBlock.instrInITBlock()) + unsigned CC = ARMCC::AL; + unsigned VCC = ARMVCC::None; + if (ITBlock.instrInITBlock()) { + CC = ITBlock.getITCC(); ITBlock.advanceITState(); + } else if (VPTBlock.instrInVPTBlock()) { + VCC = VPTBlock.getVPTPred(); + VPTBlock.advanceVPTState(); + } const MCOperandInfo *OpInfo = ARMInsts[MI.getOpcode()].OpInfo; unsigned short NumOps = ARMInsts[MI.getOpcode()].NumOperands; - MCInst::iterator I = MI.begin(); - for (unsigned i = 0; i < NumOps; ++i, ++I) { - if (I == MI.end()) break; - if (OpInfo[i].isPredicate()) { - I = MI.insert(I, MCOperand::createImm(CC)); - ++I; - if (CC == ARMCC::AL) - MI.insert(I, MCOperand::createReg(0)); - else - MI.insert(I, MCOperand::createReg(ARM::CPSR)); - return S; - } + + MCInst::iterator CCI = MI.begin(); + for (unsigned i = 0; i < NumOps; ++i, ++CCI) { + if (OpInfo[i].isPredicate() || CCI == MI.end()) break; } - I = MI.insert(I, MCOperand::createImm(CC)); - ++I; - if (CC == ARMCC::AL) - MI.insert(I, MCOperand::createReg(0)); - else - MI.insert(I, MCOperand::createReg(ARM::CPSR)); + if (ARMInsts[MI.getOpcode()].isPredicable()) { + CCI = MI.insert(CCI, MCOperand::createImm(CC)); + ++CCI; + if (CC == ARMCC::AL) + MI.insert(CCI, MCOperand::createReg(0)); + else + MI.insert(CCI, MCOperand::createReg(ARM::CPSR)); + } else if (CC != ARMCC::AL) { + Check(S, SoftFail); + } + + MCInst::iterator VCCI = MI.begin(); + unsigned VCCPos; + for (VCCPos = 0; VCCPos < NumOps; ++VCCPos, ++VCCI) { + if (ARM::isVpred(OpInfo[VCCPos].OperandType) || VCCI == MI.end()) break; + } + + if (isVectorPredicable(MI.getOpcode())) { + VCCI = MI.insert(VCCI, MCOperand::createImm(VCC)); + ++VCCI; + if (VCC == ARMVCC::None) + MI.insert(VCCI, MCOperand::createReg(0)); + else + MI.insert(VCCI, MCOperand::createReg(ARM::P0)); + if (OpInfo[VCCPos].OperandType == ARM::OPERAND_VPRED_R) { + int TiedOp = ARMInsts[MI.getOpcode()].getOperandConstraint( + VCCPos + 2, MCOI::TIED_TO); + assert(TiedOp >= 0 && + "Inactive register in vpred_r is not tied to an output!"); + MI.insert(VCCI, MI.getOperand(TiedOp)); + } + } else if (VCC != ARMVCC::None) { + Check(S, SoftFail); + } return S; } @@ -656,19 +870,26 @@ ThumbDisassembler::AddThumbPredicate(MCInst &MI) const { // mode, the auto-generated decoder will give them an (incorrect) // predicate operand. We need to rewrite these operands based on the IT // context as a post-pass. -void ThumbDisassembler::UpdateThumbVFPPredicate(MCInst &MI) const { +void ARMDisassembler::UpdateThumbVFPPredicate( + DecodeStatus &S, MCInst &MI) const { unsigned CC; CC = ITBlock.getITCC(); if (CC == 0xF) CC = ARMCC::AL; if (ITBlock.instrInITBlock()) ITBlock.advanceITState(); + else if (VPTBlock.instrInVPTBlock()) { + CC = VPTBlock.getVPTPred(); + VPTBlock.advanceVPTState(); + } const MCOperandInfo *OpInfo = ARMInsts[MI.getOpcode()].OpInfo; MCInst::iterator I = MI.begin(); unsigned short NumOps = ARMInsts[MI.getOpcode()].NumOperands; for (unsigned i = 0; i < NumOps; ++i, ++I) { if (OpInfo[i].isPredicate() ) { + if (CC != ARMCC::AL && !ARMInsts[MI.getOpcode()].isPredicable()) + Check(S, SoftFail); I->setImm(CC); ++I; if (CC == ARMCC::AL) @@ -680,11 +901,11 @@ void ThumbDisassembler::UpdateThumbVFPPredicate(MCInst &MI) const { } } -DecodeStatus ThumbDisassembler::getInstruction(MCInst &MI, uint64_t &Size, - ArrayRef<uint8_t> Bytes, - uint64_t Address, - raw_ostream &OS, - raw_ostream &CS) const { +DecodeStatus ARMDisassembler::getThumbInstruction(MCInst &MI, uint64_t &Size, + ArrayRef<uint8_t> Bytes, + uint64_t Address, + raw_ostream &OS, + raw_ostream &CS) const { CommentStream = &CS; assert(STI.getFeatureBits()[ARM::ModeThumb] && @@ -751,6 +972,27 @@ DecodeStatus ThumbDisassembler::getInstruction(MCInst &MI, uint64_t &Size, uint32_t Insn32 = (Bytes[3] << 8) | (Bytes[2] << 0) | (Bytes[1] << 24) | (Bytes[0] << 16); + + Result = + decodeInstruction(DecoderTableMVE32, MI, Insn32, Address, this, STI); + if (Result != MCDisassembler::Fail) { + Size = 4; + + // Nested VPT blocks are UNPREDICTABLE. Must be checked before we add + // the VPT predicate. + if (isVPTOpcode(MI.getOpcode()) && VPTBlock.instrInVPTBlock()) + Result = MCDisassembler::SoftFail; + + Check(Result, AddThumbPredicate(MI)); + + if (isVPTOpcode(MI.getOpcode())) { + unsigned Mask = MI.getOperand(0).getImm(); + VPTBlock.setVPTState(Mask); + } + + return Result; + } + Result = decodeInstruction(DecoderTableThumb32, MI, Insn32, Address, this, STI); if (Result != MCDisassembler::Fail) { @@ -766,7 +1008,7 @@ DecodeStatus ThumbDisassembler::getInstruction(MCInst &MI, uint64_t &Size, if (Result != MCDisassembler::Fail) { Size = 4; Check(Result, AddThumbPredicate(MI)); - return Result; + return checkDecodedInstruction(MI, Size, Address, OS, CS, Insn32, Result); } if (fieldFromInstruction(Insn32, 28, 4) == 0xE) { @@ -774,7 +1016,7 @@ DecodeStatus ThumbDisassembler::getInstruction(MCInst &MI, uint64_t &Size, decodeInstruction(DecoderTableVFP32, MI, Insn32, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 4; - UpdateThumbVFPPredicate(MI); + UpdateThumbVFPPredicate(Result, MI); return Result; } } @@ -861,9 +1103,9 @@ extern "C" void LLVMInitializeARMDisassembler() { TargetRegistry::RegisterMCDisassembler(getTheARMBETarget(), createARMDisassembler); TargetRegistry::RegisterMCDisassembler(getTheThumbLETarget(), - createThumbDisassembler); + createARMDisassembler); TargetRegistry::RegisterMCDisassembler(getTheThumbBETarget(), - createThumbDisassembler); + createARMDisassembler); } static const uint16_t GPRDecoderTable[] = { @@ -873,6 +1115,13 @@ static const uint16_t GPRDecoderTable[] = { ARM::R12, ARM::SP, ARM::LR, ARM::PC }; +static const uint16_t CLRMGPRDecoderTable[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3, + ARM::R4, ARM::R5, ARM::R6, ARM::R7, + ARM::R8, ARM::R9, ARM::R10, ARM::R11, + ARM::R12, 0, ARM::LR, ARM::APSR +}; + static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 15) @@ -883,6 +1132,20 @@ static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo, return MCDisassembler::Success; } +static DecodeStatus DecodeCLRMGPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 15) + return MCDisassembler::Fail; + + unsigned Register = CLRMGPRDecoderTable[RegNo]; + if (Register == 0) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + static DecodeStatus DecodeGPRnopcRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { @@ -911,6 +1174,34 @@ DecodeGPRwithAPSRRegisterClass(MCInst &Inst, unsigned RegNo, return S; } +static DecodeStatus +DecodeGPRwithZRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + if (RegNo == 15) + { + Inst.addOperand(MCOperand::createReg(ARM::ZR)); + return MCDisassembler::Success; + } + + if (RegNo == 13) + Check(S, MCDisassembler::SoftFail); + + Check(S, DecodeGPRRegisterClass(Inst, RegNo, Address, Decoder)); + return S; +} + +static DecodeStatus +DecodeGPRwithZRnospRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + if (RegNo == 13) + return MCDisassembler::Fail; + Check(S, DecodeGPRwithZRRegisterClass(Inst, RegNo, Address, Decoder)); + return S; +} + static DecodeStatus DecodetGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) @@ -1024,9 +1315,9 @@ static DecodeStatus DecodeDPRRegisterClass(MCInst &Inst, unsigned RegNo, const FeatureBitset &featureBits = ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); - bool hasD16 = featureBits[ARM::FeatureD16]; + bool hasD32 = featureBits[ARM::FeatureD32]; - if (RegNo > 31 || (hasD16 && RegNo > 15)) + if (RegNo > 31 || (!hasD32 && RegNo > 15)) return MCDisassembler::Fail; unsigned Register = DPRDecoderTable[RegNo]; @@ -1041,6 +1332,13 @@ static DecodeStatus DecodeDPR_8RegisterClass(MCInst &Inst, unsigned RegNo, return DecodeDPRRegisterClass(Inst, RegNo, Address, Decoder); } +static DecodeStatus DecodeSPR_8RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + if (RegNo > 15) + return MCDisassembler::Fail; + return DecodeSPRRegisterClass(Inst, RegNo, Address, Decoder); +} + static DecodeStatus DecodeDPR_VFP2RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { @@ -1111,16 +1409,19 @@ static DecodeStatus DecodeDPairSpacedRegisterClass(MCInst &Inst, static DecodeStatus DecodePredicateOperand(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; if (Val == 0xF) return MCDisassembler::Fail; // AL predicate is not allowed on Thumb1 branches. if (Inst.getOpcode() == ARM::tBcc && Val == 0xE) return MCDisassembler::Fail; + if (Val != ARMCC::AL && !ARMInsts[Inst.getOpcode()].isPredicable()) + Check(S, MCDisassembler::SoftFail); Inst.addOperand(MCOperand::createImm(Val)); if (Val == ARMCC::AL) { Inst.addOperand(MCOperand::createReg(0)); } else Inst.addOperand(MCOperand::createReg(ARM::CPSR)); - return MCDisassembler::Success; + return S; } static DecodeStatus DecodeCCOutOperand(MCInst &Inst, unsigned Val, @@ -1210,6 +1511,7 @@ static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Val, bool NeedDisjointWriteback = false; unsigned WritebackReg = 0; + bool CLRM = false; switch (Inst.getOpcode()) { default: break; @@ -1224,17 +1526,26 @@ static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Val, NeedDisjointWriteback = true; WritebackReg = Inst.getOperand(0).getReg(); break; + case ARM::t2CLRM: + CLRM = true; + break; } // Empty register lists are not allowed. if (Val == 0) return MCDisassembler::Fail; for (unsigned i = 0; i < 16; ++i) { if (Val & (1 << i)) { - if (!Check(S, DecodeGPRRegisterClass(Inst, i, Address, Decoder))) - return MCDisassembler::Fail; - // Writeback not allowed if Rn is in the target list. - if (NeedDisjointWriteback && WritebackReg == Inst.end()[-1].getReg()) - Check(S, MCDisassembler::SoftFail); + if (CLRM) { + if (!Check(S, DecodeCLRMGPRRegisterClass(Inst, i, Address, Decoder))) { + return MCDisassembler::Fail; + } + } else { + if (!Check(S, DecodeGPRRegisterClass(Inst, i, Address, Decoder))) + return MCDisassembler::Fail; + // Writeback not allowed if Rn is in the target list. + if (NeedDisjointWriteback && WritebackReg == Inst.end()[-1].getReg()) + Check(S, MCDisassembler::SoftFail); + } } } @@ -1327,6 +1638,8 @@ static DecodeStatus DecodeCopMemInstruction(MCInst &Inst, unsigned Insn, unsigned imm = fieldFromInstruction(Insn, 0, 8); unsigned Rn = fieldFromInstruction(Insn, 16, 4); unsigned U = fieldFromInstruction(Insn, 23, 1); + const FeatureBitset &featureBits = + ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); switch (Inst.getOpcode()) { case ARM::LDC_OFFSET: @@ -1361,15 +1674,42 @@ static DecodeStatus DecodeCopMemInstruction(MCInst &Inst, unsigned Insn, case ARM::t2STCL_PRE: case ARM::t2STCL_POST: case ARM::t2STCL_OPTION: - if (coproc == 0xA || coproc == 0xB) + case ARM::t2LDC2_OFFSET: + case ARM::t2LDC2L_OFFSET: + case ARM::t2LDC2_PRE: + case ARM::t2LDC2L_PRE: + case ARM::t2STC2_OFFSET: + case ARM::t2STC2L_OFFSET: + case ARM::t2STC2_PRE: + case ARM::t2STC2L_PRE: + case ARM::LDC2_OFFSET: + case ARM::LDC2L_OFFSET: + case ARM::LDC2_PRE: + case ARM::LDC2L_PRE: + case ARM::STC2_OFFSET: + case ARM::STC2L_OFFSET: + case ARM::STC2_PRE: + case ARM::STC2L_PRE: + case ARM::t2LDC2_OPTION: + case ARM::t2STC2_OPTION: + case ARM::t2LDC2_POST: + case ARM::t2LDC2L_POST: + case ARM::t2STC2_POST: + case ARM::t2STC2L_POST: + case ARM::LDC2_POST: + case ARM::LDC2L_POST: + case ARM::STC2_POST: + case ARM::STC2L_POST: + if (coproc == 0xA || coproc == 0xB || + (featureBits[ARM::HasV8_1MMainlineOps] && + (coproc == 0x8 || coproc == 0x9 || coproc == 0xA || coproc == 0xB || + coproc == 0xE || coproc == 0xF))) return MCDisassembler::Fail; break; default: break; } - const FeatureBitset &featureBits = - ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); if (featureBits[ARM::HasV8Ops] && (coproc != 14)) return MCDisassembler::Fail; @@ -3150,6 +3490,60 @@ DecodeNEONModImmInstruction(MCInst &Inst, unsigned Insn, return S; } +static DecodeStatus +DecodeMVEModImmInstruction(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned cmode = fieldFromInstruction(Insn, 8, 4); + unsigned imm = fieldFromInstruction(Insn, 0, 4); + imm |= fieldFromInstruction(Insn, 16, 3) << 4; + imm |= fieldFromInstruction(Insn, 28, 1) << 7; + imm |= cmode << 8; + imm |= fieldFromInstruction(Insn, 5, 1) << 12; + + if (cmode == 0xF && Inst.getOpcode() == ARM::MVE_VMVNimmi32) + return MCDisassembler::Fail; + + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createImm(imm)); + + Inst.addOperand(MCOperand::createImm(ARMVCC::None)); + Inst.addOperand(MCOperand::createReg(0)); + Inst.addOperand(MCOperand::createImm(0)); + + return S; +} + +static DecodeStatus DecodeMVEVADCInstruction(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Qd = fieldFromInstruction(Insn, 13, 3); + Qd |= fieldFromInstruction(Insn, 22, 1) << 3; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + Inst.addOperand(MCOperand::createReg(ARM::FPSCR_NZCV)); + + unsigned Qn = fieldFromInstruction(Insn, 17, 3); + Qn |= fieldFromInstruction(Insn, 7, 1) << 3; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qn, Address, Decoder))) + return MCDisassembler::Fail; + unsigned Qm = fieldFromInstruction(Insn, 1, 3); + Qm |= fieldFromInstruction(Insn, 5, 1) << 3; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qm, Address, Decoder))) + return MCDisassembler::Fail; + if (!fieldFromInstruction(Insn, 12, 1)) // I bit clear => need input FPSCR + Inst.addOperand(MCOperand::createReg(ARM::FPSCR_NZCV)); + Inst.addOperand(MCOperand::createImm(Qd)); + + return S; +} + static DecodeStatus DecodeVSHLMaxInstruction(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; @@ -3706,6 +4100,21 @@ static DecodeStatus DecodeT2Imm8S4(MCInst &Inst, unsigned Val, return MCDisassembler::Success; } +static DecodeStatus DecodeT2Imm7S4(MCInst &Inst, unsigned Val, uint64_t Address, + const void *Decoder) { + if (Val == 0) + Inst.addOperand(MCOperand::createImm(INT32_MIN)); + else { + int imm = Val & 0x7F; + + if (!(Val & 0x80)) + imm *= -1; + Inst.addOperand(MCOperand::createImm(imm * 4)); + } + + return MCDisassembler::Success; +} + static DecodeStatus DecodeT2AddrModeImm8s4(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; @@ -3721,6 +4130,22 @@ static DecodeStatus DecodeT2AddrModeImm8s4(MCInst &Inst, unsigned Val, return S; } +static DecodeStatus DecodeT2AddrModeImm7s4(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Rn = fieldFromInstruction(Val, 8, 4); + unsigned imm = fieldFromInstruction(Val, 0, 8); + + if (!Check(S, DecodeGPRnopcRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeT2Imm7S4(Inst, imm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + static DecodeStatus DecodeT2AddrModeImm0_1020s4(MCInst &Inst,unsigned Val, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; @@ -3748,6 +4173,21 @@ static DecodeStatus DecodeT2Imm8(MCInst &Inst, unsigned Val, return MCDisassembler::Success; } +template<int shift> +static DecodeStatus DecodeT2Imm7(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder) { + int imm = Val & 0x7F; + if (Val == 0) + imm = INT32_MIN; + else if (!(Val & 0x80)) + imm *= -1; + if (imm != INT32_MIN) + imm *= (1U << shift); + Inst.addOperand(MCOperand::createImm(imm)); + + return MCDisassembler::Success; +} + static DecodeStatus DecodeT2AddrModeImm8(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; @@ -3794,6 +4234,42 @@ static DecodeStatus DecodeT2AddrModeImm8(MCInst &Inst, unsigned Val, return S; } +template<int shift> +static DecodeStatus DecodeTAddrModeImm7(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Rn = fieldFromInstruction(Val, 8, 3); + unsigned imm = fieldFromInstruction(Val, 0, 8); + + if (!Check(S, DecodetGPRRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeT2Imm7<shift>(Inst, imm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +template<int shift, int WriteBack> +static DecodeStatus DecodeT2AddrModeImm7(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Rn = fieldFromInstruction(Val, 8, 4); + unsigned imm = fieldFromInstruction(Val, 0, 8); + if (WriteBack) { + if (!Check(S, DecoderGPRRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + } else if (!Check(S, DecodeGPRnopcRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeT2Imm7<shift>(Inst, imm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + static DecodeStatus DecodeT2LdStPre(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; @@ -3941,6 +4417,43 @@ static DecodeStatus DecodePostIdxReg(MCInst &Inst, unsigned Insn, return S; } +static DecodeStatus DecodeMveAddrModeRQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Rn = fieldFromInstruction(Insn, 3, 4); + unsigned Qm = fieldFromInstruction(Insn, 0, 3); + + if (!Check(S, DecodeGPRnopcRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +template<int shift> +static DecodeStatus DecodeMveAddrModeQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Qm = fieldFromInstruction(Insn, 8, 3); + int imm = fieldFromInstruction(Insn, 0, 7); + + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qm, Address, Decoder))) + return MCDisassembler::Fail; + + if(!fieldFromInstruction(Insn, 7, 1)) { + if (imm == 0) + imm = INT32_MIN; // indicate -0 + else + imm *= -1; + } + if (imm != INT32_MIN) + imm *= (1U << shift); + Inst.addOperand(MCOperand::createImm(imm)); + + return S; +} + static DecodeStatus DecodeThumbBLXOffset(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder) { // Val is passed in as S:J1:J2:imm10H:imm10L:'0' @@ -3973,7 +4486,7 @@ static DecodeStatus DecodeCoprocessor(MCInst &Inst, unsigned Val, const FeatureBitset &featureBits = ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); - if (featureBits[ARM::HasV8Ops] && !(Val == 14 || Val == 15)) + if (!isValidCoprocessorNumber(Val, featureBits)) return MCDisassembler::Fail; Inst.addOperand(MCOperand::createImm(Val)); @@ -4981,6 +5494,16 @@ static DecodeStatus DecodeIT(MCInst &Inst, unsigned Insn, if (mask == 0x0) return MCDisassembler::Fail; + // IT masks are encoded as a sequence of replacement low-order bits + // for the condition code. So if the low bit of the starting + // condition code is 1, then we have to flip all the bits above the + // terminating bit (which is the lowest 1 bit). + if (pred & 1) { + unsigned LowBit = mask & -mask; + unsigned BitsAboveLowBit = 0xF & (-LowBit << 1); + mask ^= BitsAboveLowBit; + } + Inst.addOperand(MCOperand::createImm(pred)); Inst.addOperand(MCOperand::createImm(mask)); return S; @@ -5341,14 +5864,37 @@ static DecodeStatus DecodeForVMRSandVMSR(MCInst &Inst, unsigned Val, ((const MCDisassembler *)Decoder)->getSubtargetInfo().getFeatureBits(); DecodeStatus S = MCDisassembler::Success; - unsigned Rt = fieldFromInstruction(Val, 12, 4); + // Add explicit operand for the destination sysreg, for cases where + // we have to model it for code generation purposes. + switch (Inst.getOpcode()) { + case ARM::VMSR_FPSCR_NZCVQC: + Inst.addOperand(MCOperand::createReg(ARM::FPSCR_NZCV)); + break; + case ARM::VMSR_P0: + Inst.addOperand(MCOperand::createReg(ARM::VPR)); + break; + } - if (featureBits[ARM::ModeThumb] && !featureBits[ARM::HasV8Ops]) { - if (Rt == 13 || Rt == 15) - S = MCDisassembler::SoftFail; - Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder)); - } else - Check(S, DecodeGPRnopcRegisterClass(Inst, Rt, Address, Decoder)); + if (Inst.getOpcode() != ARM::FMSTAT) { + unsigned Rt = fieldFromInstruction(Val, 12, 4); + + if (featureBits[ARM::ModeThumb] && !featureBits[ARM::HasV8Ops]) { + if (Rt == 13 || Rt == 15) + S = MCDisassembler::SoftFail; + Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder)); + } else + Check(S, DecodeGPRnopcRegisterClass(Inst, Rt, Address, Decoder)); + } + + // Add explicit operand for the source sysreg, similarly to above. + switch (Inst.getOpcode()) { + case ARM::VMRS_FPSCR_NZCVQC: + Inst.addOperand(MCOperand::createReg(ARM::FPSCR_NZCV)); + break; + case ARM::VMRS_P0: + Inst.addOperand(MCOperand::createReg(ARM::VPR)); + break; + } if (featureBits[ARM::ModeThumb]) { Inst.addOperand(MCOperand::createImm(ARMCC::AL)); @@ -5361,3 +5907,668 @@ static DecodeStatus DecodeForVMRSandVMSR(MCInst &Inst, unsigned Val, return S; } + +template <bool isSigned, bool isNeg, bool zeroPermitted, int size> +static DecodeStatus DecodeBFLabelOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + if (Val == 0 && !zeroPermitted) + S = MCDisassembler::Fail; + + uint64_t DecVal; + if (isSigned) + DecVal = SignExtend32<size + 1>(Val << 1); + else + DecVal = (Val << 1); + + if (!tryAddingSymbolicOperand(Address, Address + DecVal + 4, true, 4, Inst, + Decoder)) + Inst.addOperand(MCOperand::createImm(isNeg ? -DecVal : DecVal)); + return S; +} + +static DecodeStatus DecodeBFAfterTargetOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + + uint64_t LocImm = Inst.getOperand(0).getImm(); + Val = LocImm + (2 << Val); + if (!tryAddingSymbolicOperand(Address, Address + Val + 4, true, 4, Inst, + Decoder)) + Inst.addOperand(MCOperand::createImm(Val)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodePredNoALOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + if (Val >= ARMCC::AL) // also exclude the non-condition NV + return MCDisassembler::Fail; + Inst.addOperand(MCOperand::createImm(Val)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLOLoop(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + if (Inst.getOpcode() == ARM::MVE_LCTP) + return S; + + unsigned Imm = fieldFromInstruction(Insn, 11, 1) | + fieldFromInstruction(Insn, 1, 10) << 1; + switch (Inst.getOpcode()) { + case ARM::t2LEUpdate: + case ARM::MVE_LETP: + Inst.addOperand(MCOperand::createReg(ARM::LR)); + Inst.addOperand(MCOperand::createReg(ARM::LR)); + LLVM_FALLTHROUGH; + case ARM::t2LE: + if (!Check(S, DecodeBFLabelOperand<false, true, true, 11>( + Inst, Imm, Address, Decoder))) + return MCDisassembler::Fail; + break; + case ARM::t2WLS: + case ARM::MVE_WLSTP_8: + case ARM::MVE_WLSTP_16: + case ARM::MVE_WLSTP_32: + case ARM::MVE_WLSTP_64: + Inst.addOperand(MCOperand::createReg(ARM::LR)); + if (!Check(S, + DecoderGPRRegisterClass(Inst, fieldFromInstruction(Insn, 16, 4), + Address, Decoder)) || + !Check(S, DecodeBFLabelOperand<false, false, true, 11>( + Inst, Imm, Address, Decoder))) + return MCDisassembler::Fail; + break; + case ARM::t2DLS: + case ARM::MVE_DLSTP_8: + case ARM::MVE_DLSTP_16: + case ARM::MVE_DLSTP_32: + case ARM::MVE_DLSTP_64: + unsigned Rn = fieldFromInstruction(Insn, 16, 4); + if (Rn == 0xF) { + // Enforce all the rest of the instruction bits in LCTP, which + // won't have been reliably checked based on LCTP's own tablegen + // record, because we came to this decode by a roundabout route. + uint32_t CanonicalLCTP = 0xF00FE001, SBZMask = 0x00300FFE; + if ((Insn & ~SBZMask) != CanonicalLCTP) + return MCDisassembler::Fail; // a mandatory bit is wrong: hard fail + if (Insn != CanonicalLCTP) + Check(S, MCDisassembler::SoftFail); // an SBZ bit is wrong: soft fail + + Inst.setOpcode(ARM::MVE_LCTP); + } else { + Inst.addOperand(MCOperand::createReg(ARM::LR)); + if (!Check(S, DecoderGPRRegisterClass(Inst, + fieldFromInstruction(Insn, 16, 4), + Address, Decoder))) + return MCDisassembler::Fail; + } + break; + } + return S; +} + +static DecodeStatus DecodeLongShiftOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + if (Val == 0) + Val = 32; + + Inst.addOperand(MCOperand::createImm(Val)); + + return S; +} + +static DecodeStatus DecodetGPROddRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + if ((RegNo) + 1 > 11) + return MCDisassembler::Fail; + + unsigned Register = GPRDecoderTable[(RegNo) + 1]; + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodetGPREvenRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + if ((RegNo) > 14) + return MCDisassembler::Fail; + + unsigned Register = GPRDecoderTable[(RegNo)]; + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeVSCCLRM(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + Inst.addOperand(MCOperand::createImm(ARMCC::AL)); + Inst.addOperand(MCOperand::createReg(0)); + if (Inst.getOpcode() == ARM::VSCCLRMD) { + unsigned reglist = (fieldFromInstruction(Insn, 1, 7) << 1) | + (fieldFromInstruction(Insn, 12, 4) << 8) | + (fieldFromInstruction(Insn, 22, 1) << 12); + if (!Check(S, DecodeDPRRegListOperand(Inst, reglist, Address, Decoder))) { + return MCDisassembler::Fail; + } + } else { + unsigned reglist = fieldFromInstruction(Insn, 0, 8) | + (fieldFromInstruction(Insn, 22, 1) << 8) | + (fieldFromInstruction(Insn, 12, 4) << 9); + if (!Check(S, DecodeSPRRegListOperand(Inst, reglist, Address, Decoder))) { + return MCDisassembler::Fail; + } + } + Inst.addOperand(MCOperand::createReg(ARM::VPR)); + + return S; +} + +static DecodeStatus DecodeMQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + + unsigned Register = QPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + +static const uint16_t QQPRDecoderTable[] = { + ARM::Q0_Q1, ARM::Q1_Q2, ARM::Q2_Q3, ARM::Q3_Q4, + ARM::Q4_Q5, ARM::Q5_Q6, ARM::Q6_Q7 +}; + +static DecodeStatus DecodeQQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 6) + return MCDisassembler::Fail; + + unsigned Register = QQPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + +static const uint16_t QQQQPRDecoderTable[] = { + ARM::Q0_Q1_Q2_Q3, ARM::Q1_Q2_Q3_Q4, ARM::Q2_Q3_Q4_Q5, + ARM::Q3_Q4_Q5_Q6, ARM::Q4_Q5_Q6_Q7 +}; + +static DecodeStatus DecodeQQQQPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 4) + return MCDisassembler::Fail; + + unsigned Register = QQQQPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Register)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeVPTMaskOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + // Parse VPT mask and encode it in the MCInst as an immediate with the same + // format as the it_mask. That is, from the second 'e|t' encode 'e' as 1 and + // 't' as 0 and finish with a 1. + unsigned Imm = 0; + // We always start with a 't'. + unsigned CurBit = 0; + for (int i = 3; i >= 0; --i) { + // If the bit we are looking at is not the same as last one, invert the + // CurBit, if it is the same leave it as is. + CurBit ^= (Val >> i) & 1U; + + // Encode the CurBit at the right place in the immediate. + Imm |= (CurBit << i); + + // If we are done, finish the encoding with a 1. + if ((Val & ~(~0U << i)) == 0) { + Imm |= 1U << i; + break; + } + } + + Inst.addOperand(MCOperand::createImm(Imm)); + + return S; +} + +static DecodeStatus DecodeVpredROperand(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + // The vpred_r operand type includes an MQPR register field derived + // from the encoding. But we don't actually want to add an operand + // to the MCInst at this stage, because AddThumbPredicate will do it + // later, and will infer the register number from the TIED_TO + // constraint. So this is a deliberately empty decoder method that + // will inhibit the auto-generated disassembly code from adding an + // operand at all. + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRestrictedIPredicateOperand(MCInst &Inst, + unsigned Val, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm((Val & 0x1) == 0 ? ARMCC::EQ : ARMCC::NE)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRestrictedSPredicateOperand(MCInst &Inst, + unsigned Val, + uint64_t Address, + const void *Decoder) { + unsigned Code; + switch (Val & 0x3) { + case 0: + Code = ARMCC::GE; + break; + case 1: + Code = ARMCC::LT; + break; + case 2: + Code = ARMCC::GT; + break; + case 3: + Code = ARMCC::LE; + break; + } + Inst.addOperand(MCOperand::createImm(Code)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRestrictedUPredicateOperand(MCInst &Inst, + unsigned Val, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm((Val & 0x1) == 0 ? ARMCC::HS : ARMCC::HI)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRestrictedFPPredicateOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + unsigned Code; + switch (Val) { + default: + return MCDisassembler::Fail; + case 0: + Code = ARMCC::EQ; + break; + case 1: + Code = ARMCC::NE; + break; + case 4: + Code = ARMCC::GE; + break; + case 5: + Code = ARMCC::LT; + break; + case 6: + Code = ARMCC::GT; + break; + case 7: + Code = ARMCC::LE; + break; + } + + Inst.addOperand(MCOperand::createImm(Code)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeVCVTImmOperand(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned DecodedVal = 64 - Val; + + switch (Inst.getOpcode()) { + case ARM::MVE_VCVTf16s16_fix: + case ARM::MVE_VCVTs16f16_fix: + case ARM::MVE_VCVTf16u16_fix: + case ARM::MVE_VCVTu16f16_fix: + if (DecodedVal > 16) + return MCDisassembler::Fail; + break; + case ARM::MVE_VCVTf32s32_fix: + case ARM::MVE_VCVTs32f32_fix: + case ARM::MVE_VCVTf32u32_fix: + case ARM::MVE_VCVTu32f32_fix: + if (DecodedVal > 32) + return MCDisassembler::Fail; + break; + } + + Inst.addOperand(MCOperand::createImm(64 - Val)); + + return S; +} + +static unsigned FixedRegForVSTRVLDR_SYSREG(unsigned Opcode) { + switch (Opcode) { + case ARM::VSTR_P0_off: + case ARM::VSTR_P0_pre: + case ARM::VSTR_P0_post: + case ARM::VLDR_P0_off: + case ARM::VLDR_P0_pre: + case ARM::VLDR_P0_post: + return ARM::P0; + default: + return 0; + } +} + +template<bool Writeback> +static DecodeStatus DecodeVSTRVLDR_SYSREG(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + switch (Inst.getOpcode()) { + case ARM::VSTR_FPSCR_pre: + case ARM::VSTR_FPSCR_NZCVQC_pre: + case ARM::VLDR_FPSCR_pre: + case ARM::VLDR_FPSCR_NZCVQC_pre: + case ARM::VSTR_FPSCR_off: + case ARM::VSTR_FPSCR_NZCVQC_off: + case ARM::VLDR_FPSCR_off: + case ARM::VLDR_FPSCR_NZCVQC_off: + case ARM::VSTR_FPSCR_post: + case ARM::VSTR_FPSCR_NZCVQC_post: + case ARM::VLDR_FPSCR_post: + case ARM::VLDR_FPSCR_NZCVQC_post: + const FeatureBitset &featureBits = + ((const MCDisassembler *)Decoder)->getSubtargetInfo().getFeatureBits(); + + if (!featureBits[ARM::HasMVEIntegerOps] && !featureBits[ARM::FeatureVFP2]) + return MCDisassembler::Fail; + } + + DecodeStatus S = MCDisassembler::Success; + if (unsigned Sysreg = FixedRegForVSTRVLDR_SYSREG(Inst.getOpcode())) + Inst.addOperand(MCOperand::createReg(Sysreg)); + unsigned Rn = fieldFromInstruction(Val, 16, 4); + unsigned addr = fieldFromInstruction(Val, 0, 7) | + (fieldFromInstruction(Val, 23, 1) << 7) | (Rn << 8); + + if (Writeback) { + if (!Check(S, DecodeGPRnopcRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + } + if (!Check(S, DecodeT2AddrModeImm7s4(Inst, addr, Address, Decoder))) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createImm(ARMCC::AL)); + Inst.addOperand(MCOperand::createReg(0)); + + return S; +} + +static inline DecodeStatus DecodeMVE_MEM_pre( + MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder, + unsigned Rn, OperandDecoder RnDecoder, OperandDecoder AddrDecoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Qd = fieldFromInstruction(Val, 13, 3); + unsigned addr = fieldFromInstruction(Val, 0, 7) | + (fieldFromInstruction(Val, 23, 1) << 7) | (Rn << 8); + + if (!Check(S, RnDecoder(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, AddrDecoder(Inst, addr, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +template <int shift> +static DecodeStatus DecodeMVE_MEM_1_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder) { + return DecodeMVE_MEM_pre(Inst, Val, Address, Decoder, + fieldFromInstruction(Val, 16, 3), + DecodetGPRRegisterClass, + DecodeTAddrModeImm7<shift>); +} + +template <int shift> +static DecodeStatus DecodeMVE_MEM_2_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder) { + return DecodeMVE_MEM_pre(Inst, Val, Address, Decoder, + fieldFromInstruction(Val, 16, 4), + DecoderGPRRegisterClass, + DecodeT2AddrModeImm7<shift,1>); +} + +template <int shift> +static DecodeStatus DecodeMVE_MEM_3_pre(MCInst &Inst, unsigned Val, + uint64_t Address, const void *Decoder) { + return DecodeMVE_MEM_pre(Inst, Val, Address, Decoder, + fieldFromInstruction(Val, 17, 3), + DecodeMQPRRegisterClass, + DecodeMveAddrModeQ<shift>); +} + +template<unsigned MinLog, unsigned MaxLog> +static DecodeStatus DecodePowerTwoOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + if (Val < MinLog || Val > MaxLog) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createImm(1LL << Val)); + return S; +} + +template <int shift> +static DecodeStatus DecodeExpandedImmOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + Val <<= shift; + + Inst.addOperand(MCOperand::createImm(Val)); + return MCDisassembler::Success; +} + +template<unsigned start> +static DecodeStatus DecodeMVEPairVectorIndexOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + Inst.addOperand(MCOperand::createImm(start + Val)); + + return S; +} + +static DecodeStatus DecodeMVEVMOVQtoDReg(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Rt = fieldFromInstruction(Insn, 0, 4); + unsigned Rt2 = fieldFromInstruction(Insn, 16, 4); + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned index = fieldFromInstruction(Insn, 4, 1); + + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<2>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<0>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +static DecodeStatus DecodeMVEVMOVDRegtoQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Rt = fieldFromInstruction(Insn, 0, 4); + unsigned Rt2 = fieldFromInstruction(Insn, 16, 4); + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned index = fieldFromInstruction(Insn, 4, 1); + + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<2>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<0>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +static DecodeStatus DecodeMVEOverlappingLongShift( + MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned RdaLo = fieldFromInstruction(Insn, 17, 3) << 1; + unsigned RdaHi = fieldFromInstruction(Insn, 9, 3) << 1; + unsigned Rm = fieldFromInstruction(Insn, 12, 4); + + if (RdaHi == 14) { + // This value of RdaHi (really indicating pc, because RdaHi has to + // be an odd-numbered register, so the low bit will be set by the + // decode function below) indicates that we must decode as SQRSHR + // or UQRSHL, which both have a single Rda register field with all + // four bits. + unsigned Rda = fieldFromInstruction(Insn, 16, 4); + + switch (Inst.getOpcode()) { + case ARM::MVE_ASRLr: + case ARM::MVE_SQRSHRL: + Inst.setOpcode(ARM::MVE_SQRSHR); + break; + case ARM::MVE_LSLLr: + case ARM::MVE_UQRSHLL: + Inst.setOpcode(ARM::MVE_UQRSHL); + break; + default: + llvm_unreachable("Unexpected starting opcode!"); + } + + // Rda as output parameter + if (!Check(S, DecoderGPRRegisterClass(Inst, Rda, Address, Decoder))) + return MCDisassembler::Fail; + + // Rda again as input parameter + if (!Check(S, DecoderGPRRegisterClass(Inst, Rda, Address, Decoder))) + return MCDisassembler::Fail; + + // Rm, the amount to shift by + if (!Check(S, DecoderGPRRegisterClass(Inst, Rm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; + } + + // Otherwise, we decode as whichever opcode our caller has already + // put into Inst. Those all look the same: + + // RdaLo,RdaHi as output parameters + if (!Check(S, DecodetGPREvenRegisterClass(Inst, RdaLo, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodetGPROddRegisterClass(Inst, RdaHi, Address, Decoder))) + return MCDisassembler::Fail; + + // RdaLo,RdaHi again as input parameters + if (!Check(S, DecodetGPREvenRegisterClass(Inst, RdaLo, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodetGPROddRegisterClass(Inst, RdaHi, Address, Decoder))) + return MCDisassembler::Fail; + + // Rm, the amount to shift by + if (!Check(S, DecoderGPRRegisterClass(Inst, Rm, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +static DecodeStatus DecodeMVEVCVTt1fp(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned Qm = ((fieldFromInstruction(Insn, 5, 1) << 3) | + fieldFromInstruction(Insn, 1, 3)); + unsigned imm6 = fieldFromInstruction(Insn, 16, 6); + + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qm, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeVCVTImmOperand(Inst, imm6, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +template<bool scalar, OperandDecoder predicate_decoder> +static DecodeStatus DecodeMVEVCMP(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + Inst.addOperand(MCOperand::createReg(ARM::VPR)); + unsigned Qn = fieldFromInstruction(Insn, 17, 3); + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qn, Address, Decoder))) + return MCDisassembler::Fail; + + unsigned fc; + + if (scalar) { + fc = fieldFromInstruction(Insn, 12, 1) << 2 | + fieldFromInstruction(Insn, 7, 1) | + fieldFromInstruction(Insn, 5, 1) << 1; + unsigned Rm = fieldFromInstruction(Insn, 0, 4); + if (!Check(S, DecodeGPRwithZRRegisterClass(Inst, Rm, Address, Decoder))) + return MCDisassembler::Fail; + } else { + fc = fieldFromInstruction(Insn, 12, 1) << 2 | + fieldFromInstruction(Insn, 7, 1) | + fieldFromInstruction(Insn, 0, 1) << 1; + unsigned Qm = fieldFromInstruction(Insn, 5, 1) << 4 | + fieldFromInstruction(Insn, 1, 3); + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qm, Address, Decoder))) + return MCDisassembler::Fail; + } + + if (!Check(S, predicate_decoder(Inst, fc, Address, Decoder))) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createImm(ARMVCC::None)); + Inst.addOperand(MCOperand::createReg(0)); + Inst.addOperand(MCOperand::createImm(0)); + + return S; +} + +static DecodeStatus DecodeMveVCTP(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + Inst.addOperand(MCOperand::createReg(ARM::VPR)); + unsigned Rn = fieldFromInstruction(Insn, 16, 4); + if (!Check(S, DecoderGPRRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + return S; +} diff --git a/lib/Target/ARM/LICENSE.TXT b/lib/Target/ARM/LICENSE.TXT deleted file mode 100755 index 68afea12ed44..000000000000 --- a/lib/Target/ARM/LICENSE.TXT +++ /dev/null @@ -1,47 +0,0 @@ -ARM Limited - -Software Grant License Agreement ("Agreement") - -Except for the license granted herein to you, ARM Limited ("ARM") reserves all -right, title, and interest in and to the Software (defined below). - -Definition - -"Software" means the code and documentation as well as any original work of -authorship, including any modifications or additions to an existing work, that -is intentionally submitted by ARM to llvm.org (http://llvm.org) ("LLVM") for -inclusion in, or documentation of, any of the products owned or managed by LLVM -(the "Work"). For the purposes of this definition, "submitted" means any form of -electronic, verbal, or written communication sent to LLVM or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems that are -managed by, or on behalf of, LLVM for the purpose of discussing and improving -the Work, but excluding communication that is conspicuously marked otherwise. - -1. Grant of Copyright License. Subject to the terms and conditions of this - Agreement, ARM hereby grants to you and to recipients of the Software - distributed by LLVM a perpetual, worldwide, non-exclusive, no-charge, - royalty-free, irrevocable copyright license to reproduce, prepare derivative - works of, publicly display, publicly perform, sublicense, and distribute the - Software and such derivative works. - -2. Grant of Patent License. Subject to the terms and conditions of this - Agreement, ARM hereby grants you and to recipients of the Software - distributed by LLVM a perpetual, worldwide, non-exclusive, no-charge, - royalty-free, irrevocable (except as stated in this section) patent license - to make, have made, use, offer to sell, sell, import, and otherwise transfer - the Work, where such license applies only to those patent claims licensable - by ARM that are necessarily infringed by ARM's Software alone or by - combination of the Software with the Work to which such Software was - submitted. If any entity institutes patent litigation against ARM or any - other entity (including a cross-claim or counterclaim in a lawsuit) alleging - that ARM's Software, or the Work to which ARM has contributed constitutes - direct or contributory patent infringement, then any patent licenses granted - to that entity under this Agreement for the Software or Work shall terminate - as of the date such litigation is filed. - -Unless required by applicable law or agreed to in writing, the software is -provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied, including, without limitation, any warranties or -conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. diff --git a/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h b/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h index e1ea5964cf67..7732a6485a85 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h +++ b/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h @@ -1,9 +1,8 @@ //===-- ARMAddressingModes.h - ARM Addressing Modes -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -31,7 +30,8 @@ namespace ARM_AM { lsl, lsr, ror, - rrx + rrx, + uxtw }; enum AddrOpc { @@ -49,6 +49,7 @@ namespace ARM_AM { case ARM_AM::lsr: return "lsr"; case ARM_AM::ror: return "ror"; case ARM_AM::rrx: return "rrx"; + case ARM_AM::uxtw: return "uxtw"; } } diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp index c2a07d4ddcef..aeab5be78ab4 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp @@ -1,9 +1,8 @@ //===-- ARMAsmBackend.cpp - ARM Assembler Backend -------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -30,6 +29,7 @@ #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCValue.h" +#include "llvm/MC/MCAsmLayout.h" #include "llvm/Support/Debug.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorHandling.h" @@ -47,6 +47,13 @@ public: }; } // end anonymous namespace +Optional<MCFixupKind> ARMAsmBackend::getFixupKind(StringRef Name) const { + if (STI.getTargetTriple().isOSBinFormatELF() && Name == "R_ARM_NONE") + return FK_NONE; + + return MCAsmBackend::getFixupKind(Name); +} + const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { const static MCFixupKindInfo InfosLE[ARM::NumTargetFixupKinds] = { // This table *must* be in the order that the fixup_* kinds are defined in @@ -98,6 +105,13 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { {"fixup_t2_movw_lo16", 0, 20, 0}, {"fixup_arm_mod_imm", 0, 12, 0}, {"fixup_t2_so_imm", 0, 26, 0}, + {"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bf_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfl_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfc_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfcsel_else_target", 0, 32, 0}, + {"fixup_wls", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_le", 0, 32, MCFixupKindInfo::FKF_IsPCRel} }; const static MCFixupKindInfo InfosBE[ARM::NumTargetFixupKinds] = { // This table *must* be in the order that the fixup_* kinds are defined in @@ -149,6 +163,13 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { {"fixup_t2_movw_lo16", 12, 20, 0}, {"fixup_arm_mod_imm", 20, 12, 0}, {"fixup_t2_so_imm", 26, 6, 0}, + {"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bf_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfl_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfc_target", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_bfcsel_else_target", 0, 32, 0}, + {"fixup_wls", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_le", 0, 32, MCFixupKindInfo::FKF_IsPCRel} }; if (Kind < FirstTargetFixupKind) @@ -203,6 +224,13 @@ bool ARMAsmBackend::mayNeedRelaxation(const MCInst &Inst, return false; } +static const char *checkPCRelOffset(uint64_t Value, int64_t Min, int64_t Max) { + int64_t Offset = int64_t(Value) - 4; + if (Offset < Min || Offset > Max) + return "out of range pc-relative fixup value"; + return nullptr; +} + const char *ARMAsmBackend::reasonForFixupRelaxation(const MCFixup &Fixup, uint64_t Value) const { switch ((unsigned)Fixup.getKind()) { @@ -250,6 +278,32 @@ const char *ARMAsmBackend::reasonForFixupRelaxation(const MCFixup &Fixup, return "will be converted to nop"; break; } + case ARM::fixup_bf_branch: + return checkPCRelOffset(Value, 0, 30); + case ARM::fixup_bf_target: + return checkPCRelOffset(Value, -0x10000, +0xfffe); + case ARM::fixup_bfl_target: + return checkPCRelOffset(Value, -0x40000, +0x3fffe); + case ARM::fixup_bfc_target: + return checkPCRelOffset(Value, -0x1000, +0xffe); + case ARM::fixup_wls: + return checkPCRelOffset(Value, 0, +0xffe); + case ARM::fixup_le: + // The offset field in the LE and LETP instructions is an 11-bit + // value shifted left by 2 (i.e. 0,2,4,...,4094), and it is + // interpreted as a negative offset from the value read from pc, + // i.e. from instruction_address+4. + // + // So an LE instruction can in principle address the instruction + // immediately after itself, or (not very usefully) the address + // half way through the 4-byte LE. + return checkPCRelOffset(Value, -0xffe, 0); + case ARM::fixup_bfcsel_else_target: { + if (Value != 2 && Value != 4) + return "out of range label-relative fixup value"; + break; + } + default: llvm_unreachable("Unexpected fixup kind in reasonForFixupRelaxation()!"); } @@ -384,6 +438,7 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm, default: Ctx.reportError(Fixup.getLoc(), "bad relocation fixup type"); return 0; + case FK_NONE: case FK_Data_1: case FK_Data_2: case FK_Data_4: @@ -753,6 +808,60 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm, EncValue |= (Value & 0xff); return swapHalfWords(EncValue, Endian == support::little); } + case ARM::fixup_bf_branch: { + const char *FixupDiagnostic = reasonForFixupRelaxation(Fixup, Value); + if (FixupDiagnostic) { + Ctx.reportError(Fixup.getLoc(), FixupDiagnostic); + return 0; + } + uint32_t out = (((Value - 4) >> 1) & 0xf) << 23; + return swapHalfWords(out, Endian == support::little); + } + case ARM::fixup_bf_target: + case ARM::fixup_bfl_target: + case ARM::fixup_bfc_target: { + const char *FixupDiagnostic = reasonForFixupRelaxation(Fixup, Value); + if (FixupDiagnostic) { + Ctx.reportError(Fixup.getLoc(), FixupDiagnostic); + return 0; + } + uint32_t out = 0; + uint32_t HighBitMask = (Kind == ARM::fixup_bf_target ? 0xf800 : + Kind == ARM::fixup_bfl_target ? 0x3f800 : 0x800); + out |= (((Value - 4) >> 1) & 0x1) << 11; + out |= (((Value - 4) >> 1) & 0x7fe); + out |= (((Value - 4) >> 1) & HighBitMask) << 5; + return swapHalfWords(out, Endian == support::little); + } + case ARM::fixup_bfcsel_else_target: { + // If this is a fixup of a branch future's else target then it should be a + // constant MCExpr representing the distance between the branch targetted + // and the instruction after that same branch. + Value = Target.getConstant(); + + const char *FixupDiagnostic = reasonForFixupRelaxation(Fixup, Value); + if (FixupDiagnostic) { + Ctx.reportError(Fixup.getLoc(), FixupDiagnostic); + return 0; + } + uint32_t out = ((Value >> 2) & 1) << 17; + return swapHalfWords(out, Endian == support::little); + } + case ARM::fixup_wls: + case ARM::fixup_le: { + const char *FixupDiagnostic = reasonForFixupRelaxation(Fixup, Value); + if (FixupDiagnostic) { + Ctx.reportError(Fixup.getLoc(), FixupDiagnostic); + return 0; + } + uint64_t real_value = Value - 4; + uint32_t out = 0; + if (Kind == ARM::fixup_le) + real_value = -real_value; + out |= ((real_value >> 1) & 0x1) << 11; + out |= ((real_value >> 1) & 0x7fe); + return swapHalfWords(out, Endian == support::little); + } } } @@ -762,7 +871,9 @@ bool ARMAsmBackend::shouldForceRelocation(const MCAssembler &Asm, const MCSymbolRefExpr *A = Target.getSymA(); const MCSymbol *Sym = A ? &A->getSymbol() : nullptr; const unsigned FixupKind = Fixup.getKind() ; - if ((unsigned)Fixup.getKind() == ARM::fixup_arm_thumb_bl) { + if (FixupKind == FK_NONE) + return true; + if (FixupKind == ARM::fixup_arm_thumb_bl) { assert(Sym && "How did we resolve this?"); // If the symbol is external the linker will handle it. @@ -804,6 +915,9 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { default: llvm_unreachable("Unknown fixup kind!"); + case FK_NONE: + return 0; + case FK_Data_1: case ARM::fixup_arm_thumb_bcc: case ARM::fixup_arm_thumb_cp: @@ -842,6 +956,13 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { case ARM::fixup_t2_movt_hi16: case ARM::fixup_t2_movw_lo16: case ARM::fixup_t2_so_imm: + case ARM::fixup_bf_branch: + case ARM::fixup_bf_target: + case ARM::fixup_bfl_target: + case ARM::fixup_bfc_target: + case ARM::fixup_bfcsel_else_target: + case ARM::fixup_wls: + case ARM::fixup_le: return 4; case FK_SecRel_2: @@ -858,6 +979,9 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) { default: llvm_unreachable("Unknown fixup kind!"); + case FK_NONE: + return 0; + case FK_Data_1: return 1; case FK_Data_2: @@ -876,6 +1000,7 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) { case ARM::fixup_arm_pcrel_10_unscaled: case ARM::fixup_arm_ldst_pcrel_12: case ARM::fixup_arm_pcrel_10: + case ARM::fixup_arm_pcrel_9: case ARM::fixup_arm_adr_pcrel_12: case ARM::fixup_arm_uncondbl: case ARM::fixup_arm_condbl: @@ -895,6 +1020,13 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) { case ARM::fixup_t2_movw_lo16: case ARM::fixup_arm_mod_imm: case ARM::fixup_t2_so_imm: + case ARM::fixup_bf_branch: + case ARM::fixup_bf_target: + case ARM::fixup_bfl_target: + case ARM::fixup_bfc_target: + case ARM::fixup_bfcsel_else_target: + case ARM::fixup_wls: + case ARM::fixup_le: // Instruction size is 4 bytes. return 4; } diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h index 88c476bf65f4..67722a5e5b64 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h +++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h @@ -1,9 +1,8 @@ //===-- ARMAsmBackend.h - ARM Assembler Backend -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -38,6 +37,8 @@ public: // different. bool hasNOP() const { return STI.getFeatureBits()[ARM::HasV6T2Ops]; } + Optional<MCFixupKind> getFixupKind(StringRef Name) const override; + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendDarwin.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendDarwin.h index de1bfaf203e4..87e56940f46d 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendDarwin.h +++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendDarwin.h @@ -1,9 +1,8 @@ //===-- ARMAsmBackendDarwin.h ARM Asm Backend Darwin ----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendELF.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendELF.h index 86a583b19cf7..5d735114d441 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendELF.h +++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendELF.h @@ -1,9 +1,8 @@ //===-- ARMAsmBackendELF.h ARM Asm Backend ELF -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendWinCOFF.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendWinCOFF.h index 553922d20f43..8cd7a4a00ead 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackendWinCOFF.h +++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackendWinCOFF.h @@ -1,9 +1,8 @@ //===-- ARMAsmBackendWinCOFF.h - ARM Asm Backend WinCOFF --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMBaseInfo.h b/lib/Target/ARM/MCTargetDesc/ARMBaseInfo.h index 33c32d5464af..c4daafe8ee97 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMBaseInfo.h +++ b/lib/Target/ARM/MCTargetDesc/ARMBaseInfo.h @@ -1,9 +1,8 @@ //===-- ARMBaseInfo.h - Top level definitions for ARM -------- --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -203,6 +202,9 @@ namespace ARMII { AddrMode_i12 = 16, AddrMode5FP16 = 17, // i8 * 2 AddrModeT2_ldrex = 18, // i8 * 4, with unscaled offset in MCInst + AddrModeT2_i7s4 = 19, // i7 * 4 + AddrModeT2_i7s2 = 20, // i7 * 2 + AddrModeT2_i7 = 21, // i7 * 1 }; inline static const char *AddrModeToString(AddrMode addrmode) { @@ -226,6 +228,9 @@ namespace ARMII { case AddrModeT2_i8s4: return "AddrModeT2_i8s4"; case AddrMode_i12: return "AddrMode_i12"; case AddrModeT2_ldrex:return "AddrModeT2_ldrex"; + case AddrModeT2_i7s4: return "AddrModeT2_i7s4"; + case AddrModeT2_i7s2: return "AddrModeT2_i7s2"; + case AddrModeT2_i7: return "AddrModeT2_i7"; } } @@ -386,16 +391,17 @@ namespace ARMII { // instruction. Used by the parser to determine whether to require the 'S' // suffix on the mnemonic (when not in an IT block) or preclude it (when // in an IT block). - ThumbArithFlagSetting = 1 << 18, + ThumbArithFlagSetting = 1 << 19, //===------------------------------------------------------------------===// // Code domain. DomainShift = 15, - DomainMask = 7 << DomainShift, + DomainMask = 15 << DomainShift, DomainGeneral = 0 << DomainShift, DomainVFP = 1 << DomainShift, DomainNEON = 2 << DomainShift, DomainNEONA8 = 4 << DomainShift, + DomainMVE = 8 << DomainShift, //===------------------------------------------------------------------===// // Field shifts - such shifts are used to set field while generating diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp index b8ba7584911b..fda19eea1de6 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp @@ -1,9 +1,8 @@ //===-- ARMELFObjectWriter.cpp - ARM ELF Writer ---------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -138,12 +137,20 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target, default: return ELF::R_ARM_THM_CALL; } + case ARM::fixup_bf_target: + return ELF::R_ARM_THM_BF16; + case ARM::fixup_bfc_target: + return ELF::R_ARM_THM_BF12; + case ARM::fixup_bfl_target: + return ELF::R_ARM_THM_BF18; } } switch ((unsigned)Fixup.getKind()) { default: Ctx.reportFatalError(Fixup.getLoc(), "unsupported relocation on symbol"); return ELF::R_ARM_NONE; + case FK_NONE: + return ELF::R_ARM_NONE; case FK_Data_1: switch (Modifier) { default: diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp index d3744fffac32..f51fbdcd84da 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -1,9 +1,8 @@ //===- lib/MC/ARMELFStreamer.cpp - ELF Object Output for ARM --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -485,8 +484,8 @@ public: /// This function is the one used to emit instruction data into the ELF /// streamer. We override it to add the appropriate mapping symbol if /// necessary. - void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, - bool) override { + void EmitInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI) override { if (IsThumb) EmitThumbMappingSymbol(); else diff --git a/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h b/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h index 831589ba0581..bdf04a208b24 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h +++ b/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h @@ -1,9 +1,8 @@ //===-- ARMFixupKinds.h - ARM Specific Fixup Entries ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -104,6 +103,15 @@ enum Fixups { // Fixup for Thumb2 8-bit rotated operand fixup_t2_so_imm, + // Fixups for Branch Future. + fixup_bf_branch, + fixup_bf_target, + fixup_bfl_target, + fixup_bfc_target, + fixup_bfcsel_else_target, + fixup_wls, + fixup_le, + // Marker LastTargetFixupKind, NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind diff --git a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp index 2f84719c4c4f..45be1ee96342 100644 --- a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp @@ -1,9 +1,8 @@ //===-- ARMInstPrinter.cpp - Convert ARM MCInst to assembly syntax --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -73,8 +72,20 @@ ARMInstPrinter::ARMInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, const MCRegisterInfo &MRI) : MCInstPrinter(MAI, MII, MRI) {} +bool ARMInstPrinter::applyTargetSpecificCLOption(StringRef Opt) { + if (Opt == "reg-names-std") { + DefaultAltIdx = ARM::NoRegAltName; + return true; + } + if (Opt == "reg-names-raw") { + DefaultAltIdx = ARM::RegNamesRaw; + return true; + } + return false; +} + void ARMInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { - OS << markup("<reg:") << getRegisterName(RegNo) << markup(">"); + OS << markup("<reg:") << getRegisterName(RegNo, DefaultAltIdx) << markup(">"); } void ARMInstPrinter::printInst(const MCInst *MI, raw_ostream &O, @@ -592,6 +603,40 @@ void ARMInstPrinter::printPostIdxImm8s4Operand(const MCInst *MI, unsigned OpNum, << markup(">"); } +template<int shift> +void ARMInstPrinter::printMveAddrModeRQOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + const MCOperand &MO1 = MI->getOperand(OpNum); + const MCOperand &MO2 = MI->getOperand(OpNum + 1); + + O << markup("<mem:") << "["; + printRegName(O, MO1.getReg()); + O << ", "; + printRegName(O, MO2.getReg()); + + if (shift > 0) + printRegImmShift(O, ARM_AM::uxtw, shift, UseMarkup); + + O << "]" << markup(">"); +} + +void ARMInstPrinter::printMveAddrModeQOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + const MCOperand &MO1 = MI->getOperand(OpNum); + const MCOperand &MO2 = MI->getOperand(OpNum + 1); + + O << markup("<mem:") << "["; + printRegName(O, MO1.getReg()); + + int64_t Imm = MO2.getImm(); + if (Imm != 0) + O << ", " << markup("<imm:") << '#' << Imm << markup(">"); + + O << "]" << markup(">"); +} + void ARMInstPrinter::printLdStmModeOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O) { @@ -760,11 +805,13 @@ void ARMInstPrinter::printPKHASRShiftImm(const MCInst *MI, unsigned OpNum, void ARMInstPrinter::printRegisterList(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O) { - assert(std::is_sorted(MI->begin() + OpNum, MI->end(), - [&](const MCOperand &LHS, const MCOperand &RHS) { - return MRI.getEncodingValue(LHS.getReg()) < - MRI.getEncodingValue(RHS.getReg()); - })); + if (MI->getOpcode() != ARM::t2CLRM) { + assert(std::is_sorted(MI->begin() + OpNum, MI->end(), + [&](const MCOperand &LHS, const MCOperand &RHS) { + return MRI.getEncodingValue(LHS.getReg()) < + MRI.getEncodingValue(RHS.getReg()); + })); + } O << "{"; for (unsigned i = OpNum, e = MI->getNumOperands(); i != e; ++i) { @@ -919,6 +966,15 @@ void ARMInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNum, O << ARMCondCodeToString(CC); } +void ARMInstPrinter::printMandatoryRestrictedPredicateOperand( + const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, + raw_ostream &O) { + if ((ARMCC::CondCodes)MI->getOperand(OpNum).getImm() == ARMCC::HS) + O << "cs"; + else + printMandatoryPredicateOperand(MI, OpNum, STI, O); +} + void ARMInstPrinter::printMandatoryPredicateOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, @@ -927,6 +983,14 @@ void ARMInstPrinter::printMandatoryPredicateOperand(const MCInst *MI, O << ARMCondCodeToString(CC); } +void ARMInstPrinter::printMandatoryInvertedPredicateOperand(const MCInst *MI, + unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm(); + O << ARMCondCodeToString(ARMCC::getOppositeCondition(CC)); +} + void ARMInstPrinter::printSBitModifierOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O) { @@ -1009,16 +1073,13 @@ void ARMInstPrinter::printThumbITMask(const MCInst *MI, unsigned OpNum, raw_ostream &O) { // (3 - the number of trailing zeros) is the number of then / else. unsigned Mask = MI->getOperand(OpNum).getImm(); - unsigned Firstcond = MI->getOperand(OpNum - 1).getImm(); - unsigned CondBit0 = Firstcond & 1; unsigned NumTZ = countTrailingZeros(Mask); assert(NumTZ <= 3 && "Invalid IT mask!"); for (unsigned Pos = 3, e = NumTZ; Pos > e; --Pos) { - bool T = ((Mask >> Pos) & 1) == CondBit0; - if (T) - O << 't'; - else + if ((Mask >> Pos) & 1) O << 'e'; + else + O << 't'; } } @@ -1561,6 +1622,20 @@ void ARMInstPrinter::printVectorListFourSpaced(const MCInst *MI, unsigned OpNum, O << "}"; } +template<unsigned NumRegs> +void ARMInstPrinter::printMVEVectorList(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + unsigned Reg = MI->getOperand(OpNum).getReg(); + const char *Prefix = "{"; + for (unsigned i = 0; i < NumRegs; i++) { + O << Prefix; + printRegName(O, MRI.getSubReg(Reg, ARM::qsub_0 + i)); + Prefix = ", "; + } + O << "}"; +} + template<int64_t Angle, int64_t Remainder> void ARMInstPrinter::printComplexRotationOp(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, @@ -1569,3 +1644,35 @@ void ARMInstPrinter::printComplexRotationOp(const MCInst *MI, unsigned OpNo, O << "#" << (Val * Angle) + Remainder; } +void ARMInstPrinter::printVPTPredicateOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + ARMVCC::VPTCodes CC = (ARMVCC::VPTCodes)MI->getOperand(OpNum).getImm(); + if (CC != ARMVCC::None) + O << ARMVPTPredToString(CC); +} + +void ARMInstPrinter::printVPTMask(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + // (3 - the number of trailing zeroes) is the number of them / else. + unsigned Mask = MI->getOperand(OpNum).getImm(); + unsigned NumTZ = countTrailingZeros(Mask); + assert(NumTZ <= 3 && "Invalid VPT mask!"); + for (unsigned Pos = 3, e = NumTZ; Pos > e; --Pos) { + bool T = ((Mask >> Pos) & 1) == 0; + if (T) + O << 't'; + else + O << 'e'; + } +} + +void ARMInstPrinter::printExpandedImmOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + uint32_t Val = MI->getOperand(OpNum).getImm(); + O << markup("<imm:") << "#0x"; + O.write_hex(Val); + O << markup(">"); +} diff --git a/lib/Target/ARM/InstPrinter/ARMInstPrinter.h b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h index afc8515136bc..69026956b60e 100644 --- a/lib/Target/ARM/InstPrinter/ARMInstPrinter.h +++ b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h @@ -1,9 +1,8 @@ //===- ARMInstPrinter.h - Convert ARM MCInst to assembly syntax -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -11,9 +10,10 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIB_TARGET_ARM_INSTPRINTER_ARMINSTPRINTER_H -#define LLVM_LIB_TARGET_ARM_INSTPRINTER_ARMINSTPRINTER_H +#ifndef LLVM_LIB_TARGET_ARM_MCTARGETDESC_ARMINSTPRINTER_H +#define LLVM_LIB_TARGET_ARM_MCTARGETDESC_ARMINSTPRINTER_H +#include "MCTargetDesc/ARMMCTargetDesc.h" #include "llvm/MC/MCInstPrinter.h" namespace llvm { @@ -23,6 +23,8 @@ public: ARMInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, const MCRegisterInfo &MRI); + bool applyTargetSpecificCLOption(StringRef Opt) override; + void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, const MCSubtargetInfo &STI) override; void printRegName(raw_ostream &OS, unsigned RegNo) const override; @@ -36,7 +38,8 @@ public: unsigned PrintMethodIdx, const MCSubtargetInfo &STI, raw_ostream &O); - static const char *getRegisterName(unsigned RegNo); + static const char *getRegisterName(unsigned RegNo, + unsigned AltIdx = ARM::NoRegAltName); void printOperand(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O); @@ -167,6 +170,13 @@ public: void printMandatoryPredicateOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); + void printMandatoryRestrictedPredicateOperand(const MCInst *MI, + unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O); + void printMandatoryInvertedPredicateOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O); void printSBitModifierOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); void printRegisterList(const MCInst *MI, unsigned OpNum, @@ -233,11 +243,30 @@ public: const MCSubtargetInfo &STI, raw_ostream &O); void printVectorListFourSpaced(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); + template<unsigned NumRegs> + void printMVEVectorList(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); template<int64_t Angle, int64_t Remainder> void printComplexRotationOp(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); + // MVE + void printVPTPredicateOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O); + void printVPTMask(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); + template<int shift> + void printMveAddrModeRQOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); + void printMveAddrModeQOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); + void printExpandedImmOperand(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); + +private: + unsigned DefaultAltIdx = ARM::NoRegAltName; }; } // end namespace llvm -#endif // LLVM_LIB_TARGET_ARM_INSTPRINTER_ARMINSTPRINTER_H +#endif // LLVM_LIB_TARGET_ARM_MCTARGETDESC_ARMINSTPRINTER_H diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp index 3ee63ac374b3..d30d15df3d00 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMMCAsmInfo.cpp - ARM asm properties -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.h b/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.h index 5e548162bec6..55d7b299674d 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.h +++ b/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.h @@ -1,9 +1,8 @@ //===-- ARMMCAsmInfo.h - ARM asm properties --------------------*- C++ -*--===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index b37b8073548f..dca6fe37d49a 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -1,9 +1,8 @@ //===-- ARM/ARMMCCodeEmitter.cpp - Convert ARM code to machine code -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -50,7 +49,7 @@ namespace { class ARMMCCodeEmitter : public MCCodeEmitter { const MCInstrInfo &MCII; - const MCContext &CTX; + MCContext &CTX; bool IsLittleEndian; public: @@ -163,6 +162,15 @@ public: SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + uint32_t getITMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// getMVEShiftImmOpValue - Return encoding info for the 'sz:imm5' + /// operand. + uint32_t getMVEShiftImmOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; /// getAddrModeImm12OpValue - Return encoding info for 'reg +/- imm12' /// operand. @@ -181,18 +189,37 @@ public: SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + /// getT2AddrModeImm7s4OpValue - Return encoding info for 'reg +/- imm7<<2' + /// operand. + uint32_t getT2AddrModeImm7s4OpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + /// getT2AddrModeImm0_1020s4OpValue - Return encoding info for 'reg + imm8<<2' /// operand. uint32_t getT2AddrModeImm0_1020s4OpValue(const MCInst &MI, unsigned OpIdx, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; - /// getT2Imm8s4OpValue - Return encoding info for '+/- imm8<<2' + /// getT2ScaledImmOpValue - Return encoding info for '+/- immX<<Y' /// operand. - uint32_t getT2Imm8s4OpValue(const MCInst &MI, unsigned OpIdx, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const; + template<unsigned Bits, unsigned Shift> + uint32_t getT2ScaledImmOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + /// getMveAddrModeRQOpValue - Return encoding info for 'reg, vreg' + /// operand. + uint32_t getMveAddrModeRQOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// getMveAddrModeQOpValue - Return encoding info for 'reg +/- imm7<<{shift}' + /// operand. + template<int shift> + uint32_t getMveAddrModeQOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; /// getLdStSORegOpValue - Return encoding info for 'reg +/- reg shop imm' /// operand as needed by load/store instructions. @@ -224,8 +251,9 @@ public: case ARM_AM::asr: return 2; case ARM_AM::ror: case ARM_AM::rrx: return 3; + default: + llvm_unreachable("Invalid ShiftOpc!"); } - llvm_unreachable("Invalid ShiftOpc!"); } /// getAddrMode2OffsetOpValue - Return encoding for am2offset operands. @@ -283,40 +311,6 @@ public: return MI.getOperand(Op).getReg() == ARM::CPSR; } - /// getSOImmOpValue - Return an encoded 12-bit shifted-immediate value. - unsigned getSOImmOpValue(const MCInst &MI, unsigned Op, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const { - const MCOperand &MO = MI.getOperand(Op); - - // We expect MO to be an immediate or an expression, - // if it is an immediate - that's fine, just encode the value. - // Otherwise - create a Fixup. - if (MO.isExpr()) { - const MCExpr *Expr = MO.getExpr(); - // In instruction code this value always encoded as lowest 12 bits, - // so we don't have to perform any specific adjustments. - // Due to requirements of relocatable records we have to use FK_Data_4. - // See ARMELFObjectWriter::ExplicitRelSym and - // ARMELFObjectWriter::GetRelocTypeInner for more details. - MCFixupKind Kind = MCFixupKind(FK_Data_4); - Fixups.push_back(MCFixup::create(0, Expr, Kind, MI.getLoc())); - return 0; - } - - unsigned SoImm = MO.getImm(); - int SoImmVal = ARM_AM::getSOImmVal(SoImm); - assert(SoImmVal != -1 && "Not a valid so_imm value!"); - - // Encode rotate_imm. - unsigned Binary = (ARM_AM::getSOImmValRot((unsigned)SoImmVal) >> 1) - << ARMII::SoRotImmShift; - - // Encode immed_8. - Binary |= ARM_AM::getSOImmValImm((unsigned)SoImmVal); - return Binary; - } - unsigned getModImmOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &ST) const { @@ -358,7 +352,8 @@ public: unsigned getT2AddrModeSORegOpValue(const MCInst &MI, unsigned OpNum, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; - unsigned getT2AddrModeImm8OpValue(const MCInst &MI, unsigned OpNum, + template<unsigned Bits, unsigned Shift> + unsigned getT2AddrModeImmOpValue(const MCInst &MI, unsigned OpNum, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; unsigned getT2AddrModeImm8OffsetOpValue(const MCInst &MI, unsigned OpNum, @@ -418,6 +413,14 @@ public: unsigned getThumbSRImmOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + template <uint8_t shift, bool invert> + unsigned getExpandedImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + static_assert(shift <= 32, "Shift count must be less than or equal to 32."); + const MCOperand MO = MI.getOperand(Op); + return (invert ? (MO.getImm() ^ 0xff) : MO.getImm()) >> shift; + } unsigned NEONThumb2DataIPostEncoder(const MCInst &MI, unsigned EncodedValue, @@ -436,6 +439,10 @@ public: unsigned EncodedValue, const MCSubtargetInfo &STI) const; + uint32_t getPowerTwoOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + void EmitByte(unsigned char C, raw_ostream &OS) const { OS << (char)C; } @@ -451,6 +458,26 @@ public: void encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const override; + + template <bool isNeg, ARM::Fixups fixup> + uint32_t getBFTargetOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getBFAfterTargetOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getVPTMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + uint32_t getRestrictedCondCodeOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + template <unsigned size> + uint32_t getMVEPairVectorIndexOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; }; } // end anonymous namespace @@ -537,7 +564,15 @@ getMachineOpValue(const MCInst &MI, const MCOperand &MO, unsigned Reg = MO.getReg(); unsigned RegNo = CTX.getRegisterInfo()->getEncodingValue(Reg); - // Q registers are encoded as 2x their register number. + // In NEON, Q registers are encoded as 2x their register number, + // because they're using the same indices as the D registers they + // overlap. In MVE, there are no 64-bit vector instructions, so + // the encodings all refer to Q-registers by their literal + // register number. + + if (STI.getFeatureBits()[ARM::HasMVEIntegerOps]) + return RegNo; + switch (Reg) { default: return RegNo; @@ -849,6 +884,33 @@ getT2AdrLabelOpValue(const MCInst &MI, unsigned OpIdx, return Val; } +/// getITMaskOpValue - Return the architectural encoding of an IT +/// predication mask, given the MCOperand format. +uint32_t ARMMCCodeEmitter:: +getITMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MaskMO = MI.getOperand(OpIdx); + assert(MaskMO.isImm() && "Unexpected operand type!"); + + unsigned Mask = MaskMO.getImm(); + + // IT masks are encoded as a sequence of replacement low-order bits + // for the condition code. So if the low bit of the starting + // condition code is 1, then we have to flip all the bits above the + // terminating bit (which is the lowest 1 bit). + assert(OpIdx > 0 && "IT mask appears first!"); + const MCOperand CondMO = MI.getOperand(OpIdx-1); + assert(CondMO.isImm() && "Unexpected operand type!"); + if (CondMO.getImm() & 1) { + unsigned LowBit = Mask & -Mask; + unsigned BitsAboveLowBit = 0xF & (-LowBit << 1); + Mask ^= BitsAboveLowBit; + } + + return Mask; +} + /// getThumbAdrLabelOpValue - Return encoding info for 8-bit immediate ADR label /// target. uint32_t ARMMCCodeEmitter:: @@ -878,6 +940,41 @@ getThumbAddrModeRegRegOpValue(const MCInst &MI, unsigned OpIdx, return (Rm << 3) | Rn; } +/// getMVEShiftImmOpValue - Return encoding info for the 'sz:imm5' +/// operand. +uint32_t +ARMMCCodeEmitter::getMVEShiftImmOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + // {4-0} = szimm5 + // The value we are trying to encode is an immediate between either the + // range of [1-7] or [1-15] depending on whether we are dealing with the + // u8/s8 or the u16/s16 variants respectively. + // This value is encoded as follows, if ShiftImm is the value within those + // ranges then the encoding szimm5 = ShiftImm + size, where size is either 8 + // or 16. + + unsigned Size, ShiftImm; + switch(MI.getOpcode()) { + case ARM::MVE_VSHLL_imms16bh: + case ARM::MVE_VSHLL_imms16th: + case ARM::MVE_VSHLL_immu16bh: + case ARM::MVE_VSHLL_immu16th: + Size = 16; + break; + case ARM::MVE_VSHLL_imms8bh: + case ARM::MVE_VSHLL_imms8th: + case ARM::MVE_VSHLL_immu8bh: + case ARM::MVE_VSHLL_immu8th: + Size = 8; + break; + default: + llvm_unreachable("Use of operand not supported by this instruction"); + } + ShiftImm = MI.getOperand(OpIdx).getImm(); + return Size + ShiftImm; +} + /// getAddrModeImm12OpValue - Return encoding info for 'reg +/- imm12' operand. uint32_t ARMMCCodeEmitter:: getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx, @@ -929,12 +1026,11 @@ getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx, return Binary; } -/// getT2Imm8s4OpValue - Return encoding info for -/// '+/- imm8<<2' operand. +template<unsigned Bits, unsigned Shift> uint32_t ARMMCCodeEmitter:: -getT2Imm8s4OpValue(const MCInst &MI, unsigned OpIdx, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const { +getT2ScaledImmOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { // FIXME: The immediate operand should have already been encoded like this // before ever getting here. The encoder method should just need to combine // the MI operands for the register and the offset into a single @@ -942,25 +1038,75 @@ getT2Imm8s4OpValue(const MCInst &MI, unsigned OpIdx, // style, unfortunately. As-is, we can't represent the distinct encoding // for #-0. - // {8} = (U)nsigned (add == '1', sub == '0') - // {7-0} = imm8 - int32_t Imm8 = MI.getOperand(OpIdx).getImm(); - bool isAdd = Imm8 >= 0; + // {Bits} = (U)nsigned (add == '1', sub == '0') + // {(Bits-1)-0} = immediate + int32_t Imm = MI.getOperand(OpIdx).getImm(); + bool isAdd = Imm >= 0; // Immediate is always encoded as positive. The 'U' bit controls add vs sub. - if (Imm8 < 0) - Imm8 = -(uint32_t)Imm8; + if (Imm < 0) + Imm = -(uint32_t)Imm; - // Scaled by 4. - Imm8 /= 4; + Imm >>= Shift; - uint32_t Binary = Imm8 & 0xff; + uint32_t Binary = Imm & ((1U << Bits) - 1); // Immediate is always encoded as positive. The 'U' bit controls add vs sub. if (isAdd) - Binary |= (1 << 8); + Binary |= (1U << Bits); return Binary; } +/// getMveAddrModeRQOpValue - Return encoding info for 'reg, vreg' +/// operand. +uint32_t ARMMCCodeEmitter:: +getMveAddrModeRQOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + // {6-3} Rn + // {2-0} Qm + const MCOperand &M0 = MI.getOperand(OpIdx); + const MCOperand &M1 = MI.getOperand(OpIdx + 1); + + unsigned Rn = CTX.getRegisterInfo()->getEncodingValue(M0.getReg()); + unsigned Qm = CTX.getRegisterInfo()->getEncodingValue(M1.getReg()); + + assert(Qm < 8 && "Qm is supposed to be encodable in 3 bits"); + + return (Rn << 3) | Qm; +} + +/// getMveAddrModeRQOpValue - Return encoding info for 'reg, vreg' +/// operand. +template<int shift> +uint32_t ARMMCCodeEmitter:: +getMveAddrModeQOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + // {10-8} Qm + // {7-0} Imm + const MCOperand &M0 = MI.getOperand(OpIdx); + const MCOperand &M1 = MI.getOperand(OpIdx + 1); + + unsigned Qm = CTX.getRegisterInfo()->getEncodingValue(M0.getReg()); + int32_t Imm = M1.getImm(); + + bool isAdd = Imm >= 0; + + Imm >>= shift; + + if (!isAdd) + Imm = -(uint32_t)Imm; + + Imm &= 0x7f; + + if (isAdd) + Imm |= 0x80; + + assert(Qm < 8 && "Qm is supposed to be encodable in 3 bits"); + + return (Qm << 8) | Imm; +} + /// getT2AddrModeImm8s4OpValue - Return encoding info for /// 'reg +/- imm8<<2' operand. uint32_t ARMMCCodeEmitter:: @@ -1002,6 +1148,33 @@ getT2AddrModeImm8s4OpValue(const MCInst &MI, unsigned OpIdx, return Binary; } +/// getT2AddrModeImm7s4OpValue - Return encoding info for +/// 'reg +/- imm7<<2' operand. +uint32_t +ARMMCCodeEmitter::getT2AddrModeImm7s4OpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + // {11-8} = reg + // {7} = (A)dd (add == '1', sub == '0') + // {6-0} = imm7 + unsigned Reg, Imm7; + // If The first operand isn't a register, we have a label reference. + bool isAdd = EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm7, Fixups, STI); + + // FIXME: The immediate operand should have already been encoded like this + // before ever getting here. The encoder method should just need to combine + // the MI operands for the register and the offset into a single + // representation for the complex operand in the .td file. This isn't just + // style, unfortunately. As-is, we can't represent the distinct encoding + // for #-0. + uint32_t Binary = (Imm7 >> 2) & 0xff; + // Immediate is always encoded as positive. The 'A' bit controls add vs sub. + if (isAdd) + Binary |= (1 << 7); + Binary |= (Reg << 8); + return Binary; +} + /// getT2AddrModeImm0_1020s4OpValue - Return encoding info for /// 'reg + imm8<<2' operand. uint32_t ARMMCCodeEmitter:: @@ -1434,25 +1607,29 @@ getT2AddrModeSORegOpValue(const MCInst &MI, unsigned OpNum, return Value; } +template<unsigned Bits, unsigned Shift> unsigned ARMMCCodeEmitter:: -getT2AddrModeImm8OpValue(const MCInst &MI, unsigned OpNum, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const { +getT2AddrModeImmOpValue(const MCInst &MI, unsigned OpNum, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { const MCOperand &MO1 = MI.getOperand(OpNum); const MCOperand &MO2 = MI.getOperand(OpNum+1); // FIXME: Needs fixup support. unsigned Value = CTX.getRegisterInfo()->getEncodingValue(MO1.getReg()); - // Even though the immediate is 8 bits long, we need 9 bits in order + // If the immediate is B bits long, we need B+1 bits in order // to represent the (inverse of the) sign bit. - Value <<= 9; + Value <<= (Bits + 1); int32_t tmp = (int32_t)MO2.getImm(); - if (tmp < 0) + if (tmp == INT32_MIN) { // represents subtracting zero rather than adding it + tmp = 0; + } else if (tmp < 0) { tmp = abs(tmp); - else - Value |= 256; // Set the ADD bit - Value |= tmp & 255; + } else { + Value |= (1U << Bits); // Set the ADD bit + } + Value |= (tmp >> Shift) & ((1U << Bits) - 1); return Value; } @@ -1534,7 +1711,7 @@ unsigned ARMMCCodeEmitter:: getRegisterListOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { - // VLDM/VSTM: + // VLDM/VSTM/VSCCLRM: // {12-8} = Vd // {7-0} = Number of registers // @@ -1543,28 +1720,40 @@ getRegisterListOpValue(const MCInst &MI, unsigned Op, unsigned Reg = MI.getOperand(Op).getReg(); bool SPRRegs = ARMMCRegisterClasses[ARM::SPRRegClassID].contains(Reg); bool DPRRegs = ARMMCRegisterClasses[ARM::DPRRegClassID].contains(Reg); + bool CLRMRegs = MI.getOpcode() == ARM::t2CLRM; unsigned Binary = 0; if (SPRRegs || DPRRegs) { - // VLDM/VSTM + // VLDM/VSTM/VSCCLRM unsigned RegNo = CTX.getRegisterInfo()->getEncodingValue(Reg); unsigned NumRegs = (MI.getNumOperands() - Op) & 0xff; Binary |= (RegNo & 0x1f) << 8; + + // Ignore VPR + if (MI.getOpcode() == ARM::VSCCLRMD || MI.getOpcode() == ARM::VSCCLRMS) + --NumRegs; if (SPRRegs) Binary |= NumRegs; else Binary |= NumRegs * 2; } else { const MCRegisterInfo &MRI = *CTX.getRegisterInfo(); - assert(std::is_sorted(MI.begin() + Op, MI.end(), - [&](const MCOperand &LHS, const MCOperand &RHS) { - return MRI.getEncodingValue(LHS.getReg()) < - MRI.getEncodingValue(RHS.getReg()); - })); + if (!CLRMRegs) { + assert(std::is_sorted(MI.begin() + Op, MI.end(), + [&](const MCOperand &LHS, const MCOperand &RHS) { + return MRI.getEncodingValue(LHS.getReg()) < + MRI.getEncodingValue(RHS.getReg()); + })); + } for (unsigned I = Op, E = MI.getNumOperands(); I < E; ++I) { - unsigned RegNo = MRI.getEncodingValue(MI.getOperand(I).getReg()); + unsigned RegNo; + if (CLRMRegs && MI.getOperand(I).getReg() == ARM::APSR) { + RegNo = 15; + } else { + RegNo = MRI.getEncodingValue(MI.getOperand(I).getReg()); + } Binary |= 1 << RegNo; } } @@ -1710,6 +1899,120 @@ encodeInstruction(const MCInst &MI, raw_ostream &OS, ++MCNumEmitted; // Keep track of the # of mi's emitted. } +template <bool isNeg, ARM::Fixups fixup> +uint32_t +ARMMCCodeEmitter::getBFTargetOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MO = MI.getOperand(OpIdx); + if (MO.isExpr()) + return ::getBranchTargetOpValue(MI, OpIdx, fixup, Fixups, STI); + return isNeg ? -(MO.getImm() >> 1) : (MO.getImm() >> 1); +} + +uint32_t +ARMMCCodeEmitter::getBFAfterTargetOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MO = MI.getOperand(OpIdx); + const MCOperand BranchMO = MI.getOperand(0); + + if (MO.isExpr()) { + assert(BranchMO.isExpr()); + const MCExpr *DiffExpr = MCBinaryExpr::createSub( + MO.getExpr(), BranchMO.getExpr(), CTX); + MCFixupKind Kind = MCFixupKind(ARM::fixup_bfcsel_else_target); + Fixups.push_back(llvm::MCFixup::create(0, DiffExpr, Kind, MI.getLoc())); + return 0; + } + + assert(MO.isImm() && BranchMO.isImm()); + int Diff = MO.getImm() - BranchMO.getImm(); + assert(Diff == 4 || Diff == 2); + + return Diff == 4; +} + +uint32_t ARMMCCodeEmitter::getVPTMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI)const { + const MCOperand MO = MI.getOperand(OpIdx); + assert(MO.isImm() && "Unexpected operand type!"); + + int Value = MO.getImm(); + int Imm = 0; + + // VPT Masks are actually encoded as a series of invert/don't invert bits, + // rather than true/false bits. + unsigned PrevBit = 0; + for (int i = 3; i >= 0; --i) { + unsigned Bit = (Value >> i) & 1; + + // Check if we are at the end of the mask. + if ((Value & ~(~0U << i)) == 0) { + Imm |= (1 << i); + break; + } + + // Convert the bit in the mask based on the previous bit. + if (Bit != PrevBit) + Imm |= (1 << i); + + PrevBit = Bit; + } + + return Imm; +} + +uint32_t ARMMCCodeEmitter::getRestrictedCondCodeOpValue( + const MCInst &MI, unsigned OpIdx, SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + + const MCOperand MO = MI.getOperand(OpIdx); + assert(MO.isImm() && "Unexpected operand type!"); + + switch (MO.getImm()) { + default: + assert(0 && "Unexpected Condition!"); + return 0; + case ARMCC::HS: + case ARMCC::EQ: + return 0; + case ARMCC::HI: + case ARMCC::NE: + return 1; + case ARMCC::GE: + return 4; + case ARMCC::LT: + return 5; + case ARMCC::GT: + return 6; + case ARMCC::LE: + return 7; + } +} + +uint32_t ARMMCCodeEmitter:: +getPowerTwoOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpIdx); + assert(MO.isImm() && "Unexpected operand type!"); + return countTrailingZeros((uint64_t)MO.getImm()); +} + +template <unsigned start> +uint32_t ARMMCCodeEmitter:: +getMVEPairVectorIndexOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MO = MI.getOperand(OpIdx); + assert(MO.isImm() && "Unexpected operand type!"); + + int Value = MO.getImm(); + return Value - start; +} + #include "ARMGenMCCodeEmitter.inc" MCCodeEmitter *llvm::createARMLEMCCodeEmitter(const MCInstrInfo &MCII, diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp index 306f068312f5..fbad05fb1759 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp @@ -1,9 +1,8 @@ //===-- ARMMCExpr.cpp - ARM specific MC expression classes ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h b/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h index 75dde8008fca..033a43288f3e 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h +++ b/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h @@ -1,9 +1,8 @@ //===-- ARMMCExpr.h - ARM specific MC expression classes --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp index 46434007a854..90022a8d88a6 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp @@ -1,9 +1,8 @@ //===-- ARMMCTargetDesc.cpp - ARM Target Descriptions ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -13,8 +12,9 @@ #include "ARMMCTargetDesc.h" #include "ARMBaseInfo.h" +#include "ARMInstPrinter.h" #include "ARMMCAsmInfo.h" -#include "InstPrinter/ARMInstPrinter.h" +#include "TargetInfo/ARMTargetInfo.h" #include "llvm/ADT/Triple.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCCodeEmitter.h" @@ -277,14 +277,29 @@ class ThumbMCInstrAnalysis : public ARMMCInstrAnalysis { public: ThumbMCInstrAnalysis(const MCInstrInfo *Info) : ARMMCInstrAnalysis(Info) {} - bool evaluateBranch(const MCInst &Inst, uint64_t Addr, - uint64_t Size, uint64_t &Target) const override { + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target) const override { + unsigned OpId; + switch (Inst.getOpcode()) { + default: + OpId = 0; + break; + case ARM::t2WLS: + case ARM::t2LEUpdate: + OpId = 2; + break; + case ARM::t2LE: + OpId = 1; + break; + } + // We only handle PCRel branches for now. - if (Info->get(Inst.getOpcode()).OpInfo[0].OperandType!=MCOI::OPERAND_PCREL) + if (Info->get(Inst.getOpcode()).OpInfo[OpId].OperandType != + MCOI::OPERAND_PCREL) return false; - int64_t Imm = Inst.getOperand(0).getImm(); - Target = Addr+Imm+4; // In Thumb mode the PC is always off by 4 bytes. + // In Thumb mode the PC is always off by 4 bytes. + Target = Addr + Inst.getOperand(OpId).getImm() + 4; return true; } }; diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h b/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h index 3ee004592ac6..9cbbd56225ef 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h +++ b/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h @@ -1,9 +1,8 @@ //===-- ARMMCTargetDesc.h - ARM Target Descriptions -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -15,6 +14,7 @@ #define LLVM_LIB_TARGET_ARM_MCTARGETDESC_ARMMCTARGETDESC_H #include "llvm/Support/DataTypes.h" +#include "llvm/MC/MCInstrDesc.h" #include <memory> #include <string> @@ -39,11 +39,6 @@ class Triple; class raw_ostream; class raw_pwrite_stream; -Target &getTheARMLETarget(); -Target &getTheThumbLETarget(); -Target &getTheARMBETarget(); -Target &getTheThumbBETarget(); - namespace ARM_MC { std::string ParseARMTriple(const Triple &TT, StringRef CPU); @@ -100,6 +95,20 @@ createARMWinCOFFObjectWriter(bool Is64Bit); /// Construct ARM Mach-O relocation info. MCRelocationInfo *createARMMachORelocationInfo(MCContext &Ctx); + +namespace ARM { +enum OperandType { + OPERAND_VPRED_R = MCOI::OPERAND_FIRST_TARGET, + OPERAND_VPRED_N, +}; +inline bool isVpred(OperandType op) { + return op == OPERAND_VPRED_R || op == OPERAND_VPRED_N; +} +inline bool isVpred(uint8_t op) { + return isVpred(static_cast<OperandType>(op)); +} +} // end namespace ARM + } // End llvm namespace // Defines symbolic names for ARM registers. This defines a mapping from diff --git a/lib/Target/ARM/MCTargetDesc/ARMMachORelocationInfo.cpp b/lib/Target/ARM/MCTargetDesc/ARMMachORelocationInfo.cpp index 6259c98321f4..886b7e7bc84e 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMachORelocationInfo.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMachORelocationInfo.cpp @@ -1,9 +1,8 @@ //===- ARMMachORelocationInfo.cpp -----------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp index 0ced8195790d..c49885023cb2 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMachObjectWriter.cpp @@ -1,9 +1,8 @@ //===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp index 91836cff95c8..b863517c0cca 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp @@ -1,9 +1,8 @@ //===- ARMTargetStreamer.cpp - ARMTargetStreamer class --*- C++ -*---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -125,7 +124,9 @@ static ARMBuildAttrs::CPUArch getArchForCPU(const MCSubtargetInfo &STI) { if (STI.hasFeature(ARM::FeatureRClass)) return ARMBuildAttrs::v8_R; return ARMBuildAttrs::v8_A; - } else if (STI.hasFeature(ARM::HasV8MMainlineOps)) + } else if (STI.hasFeature(ARM::HasV8_1MMainlineOps)) + return ARMBuildAttrs::v8_1_M_Main; + else if (STI.hasFeature(ARM::HasV8MMainlineOps)) return ARMBuildAttrs::v8_M_Main; else if (STI.hasFeature(ARM::HasV7Ops)) { if (STI.hasFeature(ARM::FeatureMClass) && STI.hasFeature(ARM::FeatureDSP)) @@ -223,37 +224,37 @@ void ARMTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) { ? ARMBuildAttrs::AllowNeonARMv8_1a : ARMBuildAttrs::AllowNeonARMv8); } else { - if (STI.hasFeature(ARM::FeatureFPARMv8)) + if (STI.hasFeature(ARM::FeatureFPARMv8_D16_SP)) // FPv5 and FP-ARMv8 have the same instructions, so are modeled as one // FPU, but there are two different names for it depending on the CPU. - emitFPU(STI.hasFeature(ARM::FeatureD16) - ? (STI.hasFeature(ARM::FeatureVFPOnlySP) ? ARM::FK_FPV5_SP_D16 - : ARM::FK_FPV5_D16) - : ARM::FK_FP_ARMV8); - else if (STI.hasFeature(ARM::FeatureVFP4)) - emitFPU(STI.hasFeature(ARM::FeatureD16) - ? (STI.hasFeature(ARM::FeatureVFPOnlySP) ? ARM::FK_FPV4_SP_D16 - : ARM::FK_VFPV4_D16) - : ARM::FK_VFPV4); - else if (STI.hasFeature(ARM::FeatureVFP3)) + emitFPU(STI.hasFeature(ARM::FeatureD32) + ? ARM::FK_FP_ARMV8 + : (STI.hasFeature(ARM::FeatureFP64) ? ARM::FK_FPV5_D16 + : ARM::FK_FPV5_SP_D16)); + else if (STI.hasFeature(ARM::FeatureVFP4_D16_SP)) + emitFPU(STI.hasFeature(ARM::FeatureD32) + ? ARM::FK_VFPV4 + : (STI.hasFeature(ARM::FeatureFP64) ? ARM::FK_VFPV4_D16 + : ARM::FK_FPV4_SP_D16)); + else if (STI.hasFeature(ARM::FeatureVFP3_D16_SP)) emitFPU( - STI.hasFeature(ARM::FeatureD16) - // +d16 - ? (STI.hasFeature(ARM::FeatureVFPOnlySP) - ? (STI.hasFeature(ARM::FeatureFP16) ? ARM::FK_VFPV3XD_FP16 - : ARM::FK_VFPV3XD) - : (STI.hasFeature(ARM::FeatureFP16) + STI.hasFeature(ARM::FeatureD32) + // +d32 + ? (STI.hasFeature(ARM::FeatureFP16) ? ARM::FK_VFPV3_FP16 + : ARM::FK_VFPV3) + // -d32 + : (STI.hasFeature(ARM::FeatureFP64) + ? (STI.hasFeature(ARM::FeatureFP16) ? ARM::FK_VFPV3_D16_FP16 - : ARM::FK_VFPV3_D16)) - // -d16 - : (STI.hasFeature(ARM::FeatureFP16) ? ARM::FK_VFPV3_FP16 - : ARM::FK_VFPV3)); - else if (STI.hasFeature(ARM::FeatureVFP2)) + : ARM::FK_VFPV3_D16) + : (STI.hasFeature(ARM::FeatureFP16) ? ARM::FK_VFPV3XD_FP16 + : ARM::FK_VFPV3XD))); + else if (STI.hasFeature(ARM::FeatureVFP2_D16_SP)) emitFPU(ARM::FK_VFPV2); } // ABI_HardFP_use attribute to indicate single precision FP. - if (STI.hasFeature(ARM::FeatureVFPOnlySP)) + if (STI.hasFeature(ARM::FeatureVFP2_D16_SP) && !STI.hasFeature(ARM::FeatureFP64)) emitAttribute(ARMBuildAttrs::ABI_HardFP_use, ARMBuildAttrs::HardFPSinglePrecision); @@ -263,6 +264,11 @@ void ARMTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) { if (STI.hasFeature(ARM::FeatureMP)) emitAttribute(ARMBuildAttrs::MPextension_use, ARMBuildAttrs::AllowMP); + if (STI.hasFeature(ARM::HasMVEFloatOps)) + emitAttribute(ARMBuildAttrs::MVE_arch, ARMBuildAttrs::AllowMVEIntegerAndFloat); + else if (STI.hasFeature(ARM::HasMVEIntegerOps)) + emitAttribute(ARMBuildAttrs::MVE_arch, ARMBuildAttrs::AllowMVEInteger); + // Hardware divide in ARM mode is part of base arch, starting from ARMv8. // If only Thumb hwdiv is present, it must also be in base arch (ARMv7-R/M). // It is not possible to produce DisallowDIV: if hwdiv is present in the base diff --git a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.cpp b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.cpp index d3ab83bbccbc..38667d686b85 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.cpp @@ -1,9 +1,8 @@ //===-- ARMUnwindOpAsm.cpp - ARM Unwind Opcodes Assembler -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h index a7bfbdf4938e..c3134c04b33a 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h +++ b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h @@ -1,9 +1,8 @@ //===-- ARMUnwindOpAsm.h - ARM Unwind Opcodes Assembler ---------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp index 30cbde1ca71f..054a95dd1e12 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp @@ -1,9 +1,8 @@ //===-- ARMWinCOFFObjectWriter.cpp - ARM Windows COFF Object Writer -- C++ -==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp index 32cb3dcdcad8..2e816bea5e91 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp @@ -1,9 +1,8 @@ //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/MLxExpansionPass.cpp b/lib/Target/ARM/MLxExpansionPass.cpp index 7f03e1463c1d..4b25986b90a7 100644 --- a/lib/Target/ARM/MLxExpansionPass.cpp +++ b/lib/Target/ARM/MLxExpansionPass.cpp @@ -1,9 +1,8 @@ //===-- MLxExpansionPass.cpp - Expand MLx instrs to avoid hazards ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/TargetInfo/ARMTargetInfo.cpp b/lib/Target/ARM/TargetInfo/ARMTargetInfo.cpp index b0491a4108a6..86cb907abfa3 100644 --- a/lib/Target/ARM/TargetInfo/ARMTargetInfo.cpp +++ b/lib/Target/ARM/TargetInfo/ARMTargetInfo.cpp @@ -1,13 +1,12 @@ //===-- ARMTargetInfo.cpp - ARM Target Implementation ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -#include "MCTargetDesc/ARMMCTargetDesc.h" +#include "TargetInfo/ARMTargetInfo.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; diff --git a/lib/Target/ARM/TargetInfo/ARMTargetInfo.h b/lib/Target/ARM/TargetInfo/ARMTargetInfo.h new file mode 100644 index 000000000000..c217dd5c4612 --- /dev/null +++ b/lib/Target/ARM/TargetInfo/ARMTargetInfo.h @@ -0,0 +1,23 @@ +//===-- ARMTargetInfo.h - ARM Target Implementation -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARM_TARGETINFO_ARMTARGETINFO_H +#define LLVM_LIB_TARGET_ARM_TARGETINFO_ARMTARGETINFO_H + +namespace llvm { + +class Target; + +Target &getTheARMLETarget(); +Target &getTheARMBETarget(); +Target &getTheThumbLETarget(); +Target &getTheThumbBETarget(); + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_ARM_TARGETINFO_ARMTARGETINFO_H diff --git a/lib/Target/ARM/Thumb1FrameLowering.cpp b/lib/Target/ARM/Thumb1FrameLowering.cpp index 5c745e112b2e..426e9a0ed9b8 100644 --- a/lib/Target/ARM/Thumb1FrameLowering.cpp +++ b/lib/Target/ARM/Thumb1FrameLowering.cpp @@ -1,9 +1,8 @@ //===- Thumb1FrameLowering.cpp - Thumb1 Frame Information -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -64,15 +63,52 @@ bool Thumb1FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const{ return !MFI.hasVarSizedObjects(); } -static void emitSPUpdate(MachineBasicBlock &MBB, - MachineBasicBlock::iterator &MBBI, - const TargetInstrInfo &TII, const DebugLoc &dl, - const ThumbRegisterInfo &MRI, int NumBytes, - unsigned MIFlags = MachineInstr::NoFlags) { +static void +emitPrologueEpilogueSPUpdate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + const TargetInstrInfo &TII, const DebugLoc &dl, + const ThumbRegisterInfo &MRI, int NumBytes, + unsigned ScratchReg, unsigned MIFlags) { + // If it would take more than three instructions to adjust the stack pointer + // using tADDspi/tSUBspi, load an immediate instead. + if (std::abs(NumBytes) > 508 * 3) { + // We use a different codepath here from the normal + // emitThumbRegPlusImmediate so we don't have to deal with register + // scavenging. (Scavenging could try to use the emergency spill slot + // before we've actually finished setting up the stack.) + if (ScratchReg == ARM::NoRegister) + report_fatal_error("Failed to emit Thumb1 stack adjustment"); + MachineFunction &MF = *MBB.getParent(); + const ARMSubtarget &ST = MF.getSubtarget<ARMSubtarget>(); + if (ST.genExecuteOnly()) { + BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm), ScratchReg) + .addImm(NumBytes).setMIFlags(MIFlags); + } else { + MRI.emitLoadConstPool(MBB, MBBI, dl, ScratchReg, 0, NumBytes, ARMCC::AL, + 0, MIFlags); + } + BuildMI(MBB, MBBI, dl, TII.get(ARM::tADDhirr), ARM::SP) + .addReg(ARM::SP).addReg(ScratchReg, RegState::Kill) + .add(predOps(ARMCC::AL)); + return; + } + // FIXME: This is assuming the heuristics in emitThumbRegPlusImmediate + // won't change. emitThumbRegPlusImmediate(MBB, MBBI, dl, ARM::SP, ARM::SP, NumBytes, TII, MRI, MIFlags); + } +static void emitCallSPUpdate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + const TargetInstrInfo &TII, const DebugLoc &dl, + const ThumbRegisterInfo &MRI, int NumBytes, + unsigned MIFlags = MachineInstr::NoFlags) { + emitThumbRegPlusImmediate(MBB, MBBI, dl, ARM::SP, ARM::SP, NumBytes, TII, + MRI, MIFlags); +} + + MachineBasicBlock::iterator Thumb1FrameLowering:: eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { @@ -96,10 +132,10 @@ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, // Replace the pseudo instruction with a new instruction... unsigned Opc = Old.getOpcode(); if (Opc == ARM::ADJCALLSTACKDOWN || Opc == ARM::tADJCALLSTACKDOWN) { - emitSPUpdate(MBB, I, TII, dl, *RegInfo, -Amount); + emitCallSPUpdate(MBB, I, TII, dl, *RegInfo, -Amount); } else { assert(Opc == ARM::ADJCALLSTACKUP || Opc == ARM::tADJCALLSTACKUP); - emitSPUpdate(MBB, I, TII, dl, *RegInfo, Amount); + emitCallSPUpdate(MBB, I, TII, dl, *RegInfo, Amount); } } } @@ -142,8 +178,8 @@ void Thumb1FrameLowering::emitPrologue(MachineFunction &MF, int FramePtrSpillFI = 0; if (ArgRegsSaveSize) { - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -ArgRegsSaveSize, - MachineInstr::FrameSetup); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -ArgRegsSaveSize, + ARM::NoRegister, MachineInstr::FrameSetup); CFAOffset -= ArgRegsSaveSize; unsigned CFIIndex = MF.addFrameInst( MCCFIInstruction::createDefCfaOffset(nullptr, CFAOffset)); @@ -154,8 +190,9 @@ void Thumb1FrameLowering::emitPrologue(MachineFunction &MF, if (!AFI->hasStackFrame()) { if (NumBytes - ArgRegsSaveSize != 0) { - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -(NumBytes - ArgRegsSaveSize), - MachineInstr::FrameSetup); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, + -(NumBytes - ArgRegsSaveSize), + ARM::NoRegister, MachineInstr::FrameSetup); CFAOffset -= NumBytes - ArgRegsSaveSize; unsigned CFIIndex = MF.addFrameInst( MCCFIInstruction::createDefCfaOffset(nullptr, CFAOffset)); @@ -332,8 +369,20 @@ void Thumb1FrameLowering::emitPrologue(MachineFunction &MF, if (NumBytes) { // Insert it after all the callee-save spills. - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -NumBytes, - MachineInstr::FrameSetup); + // + // For a large stack frame, we might need a scratch register to store + // the size of the frame. We know all callee-save registers are free + // at this point in the prologue, so pick one. + unsigned ScratchRegister = ARM::NoRegister; + for (auto &I : CSI) { + unsigned Reg = I.getReg(); + if (isARMLowRegister(Reg) && !(HasFP && Reg == FramePtr)) { + ScratchRegister = Reg; + break; + } + } + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, -NumBytes, + ScratchRegister, MachineInstr::FrameSetup); if (!HasFP) { CFAOffset -= NumBytes; unsigned CFIIndex = MF.addFrameInst( @@ -438,7 +487,9 @@ void Thumb1FrameLowering::emitEpilogue(MachineFunction &MF, if (!AFI->hasStackFrame()) { if (NumBytes - ArgRegsSaveSize != 0) - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, NumBytes - ArgRegsSaveSize); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, + NumBytes - ArgRegsSaveSize, ARM::NoRegister, + MachineInstr::NoFlags); } else { // Unwind MBBI to point to first LDR / VLDRD. if (MBBI != MBB.begin()) { @@ -473,13 +524,27 @@ void Thumb1FrameLowering::emitEpilogue(MachineFunction &MF, .addReg(FramePtr) .add(predOps(ARMCC::AL)); } else { + // For a large stack frame, we might need a scratch register to store + // the size of the frame. We know all callee-save registers are free + // at this point in the epilogue, so pick one. + unsigned ScratchRegister = ARM::NoRegister; + bool HasFP = hasFP(MF); + for (auto &I : MFI.getCalleeSavedInfo()) { + unsigned Reg = I.getReg(); + if (isARMLowRegister(Reg) && !(HasFP && Reg == FramePtr)) { + ScratchRegister = Reg; + break; + } + } if (MBBI != MBB.end() && MBBI->getOpcode() == ARM::tBX_RET && &MBB.front() != &*MBBI && std::prev(MBBI)->getOpcode() == ARM::tPOP) { MachineBasicBlock::iterator PMBBI = std::prev(MBBI); if (!tryFoldSPUpdateIntoPushPop(STI, MF, &*PMBBI, NumBytes)) - emitSPUpdate(MBB, PMBBI, TII, dl, *RegInfo, NumBytes); + emitPrologueEpilogueSPUpdate(MBB, PMBBI, TII, dl, *RegInfo, NumBytes, + ScratchRegister, MachineInstr::NoFlags); } else if (!tryFoldSPUpdateIntoPushPop(STI, MF, &*MBBI, NumBytes)) - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, NumBytes); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, NumBytes, + ScratchRegister, MachineInstr::NoFlags); } } @@ -666,7 +731,9 @@ bool Thumb1FrameLowering::emitPopSpecialFixUp(MachineBasicBlock &MBB, // Advance past the pop instruction. MBBI++; // Increment the SP. - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, ArgRegsSaveSize + 4); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, + ArgRegsSaveSize + 4, ARM::NoRegister, + MachineInstr::NoFlags); return true; } @@ -707,7 +774,8 @@ bool Thumb1FrameLowering::emitPopSpecialFixUp(MachineBasicBlock &MBB, .add(predOps(ARMCC::AL)) .addReg(PopReg, RegState::Define); - emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, ArgRegsSaveSize); + emitPrologueEpilogueSPUpdate(MBB, MBBI, TII, dl, *RegInfo, ArgRegsSaveSize, + ARM::NoRegister, MachineInstr::NoFlags); BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVr)) .addReg(ARM::LR, RegState::Define) @@ -821,8 +889,9 @@ spillCalleeSavedRegisters(MachineBasicBlock &MBB, findNextOrderedReg(std::begin(AllCopyRegs), CopyRegs, AllCopyRegsEnd); // Create the PUSH, but don't insert it yet (the MOVs need to come first). - MachineInstrBuilder PushMIB = - BuildMI(MF, DL, TII.get(ARM::tPUSH)).add(predOps(ARMCC::AL)); + MachineInstrBuilder PushMIB = BuildMI(MF, DL, TII.get(ARM::tPUSH)) + .add(predOps(ARMCC::AL)) + .setMIFlags(MachineInstr::FrameSetup); SmallVector<unsigned, 4> RegsToPush; while (HiRegToSave != AllHighRegsEnd && CopyReg != AllCopyRegsEnd) { @@ -835,7 +904,8 @@ spillCalleeSavedRegisters(MachineBasicBlock &MBB, BuildMI(MBB, MI, DL, TII.get(ARM::tMOVr)) .addReg(*CopyReg, RegState::Define) .addReg(*HiRegToSave, getKillRegState(isKill)) - .add(predOps(ARMCC::AL)); + .add(predOps(ARMCC::AL)) + .setMIFlags(MachineInstr::FrameSetup); // Record the register that must be added to the PUSH. RegsToPush.push_back(*CopyReg); diff --git a/lib/Target/ARM/Thumb1FrameLowering.h b/lib/Target/ARM/Thumb1FrameLowering.h index a4d6451ccf12..61af48712b6c 100644 --- a/lib/Target/ARM/Thumb1FrameLowering.h +++ b/lib/Target/ARM/Thumb1FrameLowering.h @@ -1,9 +1,8 @@ //===- Thumb1FrameLowering.h - Thumb1-specific frame info stuff ---*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/Thumb1InstrInfo.cpp b/lib/Target/ARM/Thumb1InstrInfo.cpp index 11aa285fc939..f57d93a2e83d 100644 --- a/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -1,9 +1,8 @@ //===-- Thumb1InstrInfo.cpp - Thumb-1 Instruction Information -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/Thumb1InstrInfo.h b/lib/Target/ARM/Thumb1InstrInfo.h index 9f04a3ed262f..bc433e7a7a93 100644 --- a/lib/Target/ARM/Thumb1InstrInfo.h +++ b/lib/Target/ARM/Thumb1InstrInfo.h @@ -1,9 +1,8 @@ //===-- Thumb1InstrInfo.h - Thumb-1 Instruction Information -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/Thumb2ITBlockPass.cpp b/lib/Target/ARM/Thumb2ITBlockPass.cpp index e0a5f7f04fa9..3143eb9840ed 100644 --- a/lib/Target/ARM/Thumb2ITBlockPass.cpp +++ b/lib/Target/ARM/Thumb2ITBlockPass.cpp @@ -1,9 +1,8 @@ //===-- Thumb2ITBlockPass.cpp - Insert Thumb-2 IT blocks ------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -32,13 +31,16 @@ using namespace llvm; #define DEBUG_TYPE "thumb2-it" +#define PASS_NAME "Thumb IT blocks insertion pass" STATISTIC(NumITs, "Number of IT blocks inserted"); STATISTIC(NumMovedInsts, "Number of predicated instructions moved"); +using RegisterSet = SmallSet<unsigned, 4>; + namespace { - class Thumb2ITBlockPass : public MachineFunctionPass { + class Thumb2ITBlock : public MachineFunctionPass { public: static char ID; @@ -47,7 +49,7 @@ namespace { const TargetRegisterInfo *TRI; ARMFunctionInfo *AFI; - Thumb2ITBlockPass() : MachineFunctionPass(ID) {} + Thumb2ITBlock() : MachineFunctionPass(ID) {} bool runOnMachineFunction(MachineFunction &Fn) override; @@ -57,33 +59,32 @@ namespace { } StringRef getPassName() const override { - return "Thumb IT blocks insertion pass"; + return PASS_NAME; } private: bool MoveCopyOutOfITBlock(MachineInstr *MI, ARMCC::CondCodes CC, ARMCC::CondCodes OCC, - SmallSet<unsigned, 4> &Defs, - SmallSet<unsigned, 4> &Uses); - bool InsertITInstructions(MachineBasicBlock &MBB); + RegisterSet &Defs, RegisterSet &Uses); + bool InsertITInstructions(MachineBasicBlock &Block); }; - char Thumb2ITBlockPass::ID = 0; + char Thumb2ITBlock::ID = 0; } // end anonymous namespace +INITIALIZE_PASS(Thumb2ITBlock, DEBUG_TYPE, PASS_NAME, false, false) + /// TrackDefUses - Tracking what registers are being defined and used by /// instructions in the IT block. This also tracks "dependencies", i.e. uses /// in the IT block that are defined before the IT instruction. -static void TrackDefUses(MachineInstr *MI, - SmallSet<unsigned, 4> &Defs, - SmallSet<unsigned, 4> &Uses, +static void TrackDefUses(MachineInstr *MI, RegisterSet &Defs, RegisterSet &Uses, const TargetRegisterInfo *TRI) { - SmallVector<unsigned, 4> LocalDefs; - SmallVector<unsigned, 4> LocalUses; + using RegList = SmallVector<unsigned, 4>; + RegList LocalDefs; + RegList LocalUses; - for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { - MachineOperand &MO = MI->getOperand(i); + for (auto &MO : MI->operands()) { if (!MO.isReg()) continue; unsigned Reg = MO.getReg(); @@ -95,27 +96,21 @@ static void TrackDefUses(MachineInstr *MI, LocalDefs.push_back(Reg); } - for (unsigned i = 0, e = LocalUses.size(); i != e; ++i) { - unsigned Reg = LocalUses[i]; - for (MCSubRegIterator Subreg(Reg, TRI, /*IncludeSelf=*/true); - Subreg.isValid(); ++Subreg) - Uses.insert(*Subreg); - } + auto InsertUsesDefs = [&](RegList &Regs, RegisterSet &UsesDefs) { + for (unsigned Reg : Regs) + for (MCSubRegIterator Subreg(Reg, TRI, /*IncludeSelf=*/true); + Subreg.isValid(); ++Subreg) + UsesDefs.insert(*Subreg); + }; - for (unsigned i = 0, e = LocalDefs.size(); i != e; ++i) { - unsigned Reg = LocalDefs[i]; - for (MCSubRegIterator Subreg(Reg, TRI, /*IncludeSelf=*/true); - Subreg.isValid(); ++Subreg) - Defs.insert(*Subreg); - if (Reg == ARM::CPSR) - continue; - } + InsertUsesDefs(LocalDefs, Defs); + InsertUsesDefs(LocalUses, Uses); } /// Clear kill flags for any uses in the given set. This will likely /// conservatively remove more kill flags than are necessary, but removing them /// is safer than incorrect kill flags remaining on instructions. -static void ClearKillFlags(MachineInstr *MI, SmallSet<unsigned, 4> &Uses) { +static void ClearKillFlags(MachineInstr *MI, RegisterSet &Uses) { for (MachineOperand &MO : MI->operands()) { if (!MO.isReg() || MO.isDef() || !MO.isKill()) continue; @@ -138,10 +133,9 @@ static bool isCopy(MachineInstr *MI) { } bool -Thumb2ITBlockPass::MoveCopyOutOfITBlock(MachineInstr *MI, - ARMCC::CondCodes CC, ARMCC::CondCodes OCC, - SmallSet<unsigned, 4> &Defs, - SmallSet<unsigned, 4> &Uses) { +Thumb2ITBlock::MoveCopyOutOfITBlock(MachineInstr *MI, + ARMCC::CondCodes CC, ARMCC::CondCodes OCC, + RegisterSet &Defs, RegisterSet &Uses) { if (!isCopy(MI)) return false; // llvm models select's as two-address instructions. That means a copy @@ -181,10 +175,13 @@ Thumb2ITBlockPass::MoveCopyOutOfITBlock(MachineInstr *MI, // Then peek at the next instruction to see if it's predicated on CC or OCC. // If not, then there is nothing to be gained by moving the copy. - MachineBasicBlock::iterator I = MI; ++I; + MachineBasicBlock::iterator I = MI; + ++I; MachineBasicBlock::iterator E = MI->getParent()->end(); + while (I != E && I->isDebugInstr()) ++I; + if (I != E) { unsigned NPredReg = 0; ARMCC::CondCodes NCC = getITInstrPredicate(*I, NPredReg); @@ -194,12 +191,11 @@ Thumb2ITBlockPass::MoveCopyOutOfITBlock(MachineInstr *MI, return false; } -bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { +bool Thumb2ITBlock::InsertITInstructions(MachineBasicBlock &MBB) { bool Modified = false; - - SmallSet<unsigned, 4> Defs; - SmallSet<unsigned, 4> Uses; + RegisterSet Defs, Uses; MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { MachineInstr *MI = &*MBBI; DebugLoc dl = MI->getDebugLoc(); @@ -246,7 +242,7 @@ bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { unsigned NPredReg = 0; ARMCC::CondCodes NCC = getITInstrPredicate(*NMI, NPredReg); if (NCC == CC || NCC == OCC) { - Mask |= (NCC & 1) << Pos; + Mask |= ((NCC ^ CC) & 1) << Pos; // Add implicit use of ITSTATE. NMI->addOperand(MachineOperand::CreateReg(ARM::ITSTATE, false/*ifDef*/, true/*isImp*/, false/*isKill*/)); @@ -270,8 +266,6 @@ bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { // Finalize IT mask. Mask |= (1 << Pos); - // Tag along (firstcond[0] << 4) with the mask. - Mask |= (CC & 1) << 4; MIB.addImm(Mask); // Last instruction in IT block kills ITSTATE. @@ -288,7 +282,7 @@ bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { return Modified; } -bool Thumb2ITBlockPass::runOnMachineFunction(MachineFunction &Fn) { +bool Thumb2ITBlock::runOnMachineFunction(MachineFunction &Fn) { const ARMSubtarget &STI = static_cast<const ARMSubtarget &>(Fn.getSubtarget()); if (!STI.isThumb2()) @@ -302,11 +296,8 @@ bool Thumb2ITBlockPass::runOnMachineFunction(MachineFunction &Fn) { return false; bool Modified = false; - for (MachineFunction::iterator MFI = Fn.begin(), E = Fn.end(); MFI != E; ) { - MachineBasicBlock &MBB = *MFI; - ++MFI; + for (auto &MBB : Fn ) Modified |= InsertITInstructions(MBB); - } if (Modified) AFI->setHasITBlocks(true); @@ -316,6 +307,132 @@ bool Thumb2ITBlockPass::runOnMachineFunction(MachineFunction &Fn) { /// createThumb2ITBlockPass - Returns an instance of the Thumb2 IT blocks /// insertion pass. -FunctionPass *llvm::createThumb2ITBlockPass() { - return new Thumb2ITBlockPass(); +FunctionPass *llvm::createThumb2ITBlockPass() { return new Thumb2ITBlock(); } + +#undef DEBUG_TYPE +#define DEBUG_TYPE "arm-mve-vpt" + +namespace { + class MVEVPTBlock : public MachineFunctionPass { + public: + static char ID; + const Thumb2InstrInfo *TII; + const TargetRegisterInfo *TRI; + + MVEVPTBlock() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &Fn) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return "MVE VPT block insertion pass"; + } + + private: + bool InsertVPTBlocks(MachineBasicBlock &MBB); + }; + + char MVEVPTBlock::ID = 0; + +} // end anonymous namespace + +INITIALIZE_PASS(MVEVPTBlock, DEBUG_TYPE, "ARM MVE VPT block pass", false, false) + +enum VPTMaskValue { + T = 8, // 0b1000 + TT = 4, // 0b0100 + TE = 12, // 0b1100 + TTT = 2, // 0b0010 + TTE = 6, // 0b0110 + TEE = 10, // 0b1010 + TET = 14, // 0b1110 + TTTT = 1, // 0b0001 + TTTE = 3, // 0b0011 + TTEE = 5, // 0b0101 + TTET = 7, // 0b0111 + TEEE = 9, // 0b1001 + TEET = 11, // 0b1011 + TETT = 13, // 0b1101 + TETE = 15 // 0b1111 +}; + +bool MVEVPTBlock::InsertVPTBlocks(MachineBasicBlock &Block) { + bool Modified = false; + MachineBasicBlock::iterator MBIter = Block.begin(); + MachineBasicBlock::iterator EndIter = Block.end(); + + while (MBIter != EndIter) { + MachineInstr *MI = &*MBIter; + unsigned PredReg = 0; + DebugLoc dl = MI->getDebugLoc(); + + ARMVCC::VPTCodes Pred = getVPTInstrPredicate(*MI, PredReg); + + // The idea of the predicate is that None, Then and Else are for use when + // handling assembly language: they correspond to the three possible + // suffixes "", "t" and "e" on the mnemonic. So when instructions are read + // from assembly source or disassembled from object code, you expect to see + // a mixture whenever there's a long VPT block. But in code generation, we + // hope we'll never generate an Else as input to this pass. + + assert(Pred != ARMVCC::Else && "VPT block pass does not expect Else preds"); + + if (Pred == ARMVCC::None) { + ++MBIter; + continue; + } + + MachineInstrBuilder MIBuilder = + BuildMI(Block, MBIter, dl, TII->get(ARM::MVE_VPST)); + // The mask value for the VPST instruction is T = 0b1000 = 8 + MIBuilder.addImm(VPTMaskValue::T); + + MachineBasicBlock::iterator VPSTInsertPos = MIBuilder.getInstr(); + int VPTInstCnt = 1; + ARMVCC::VPTCodes NextPred; + + do { + ++MBIter; + NextPred = getVPTInstrPredicate(*MBIter, PredReg); + } while (NextPred != ARMVCC::None && NextPred == Pred && ++VPTInstCnt < 4); + + MachineInstr *LastMI = &*MBIter; + finalizeBundle(Block, VPSTInsertPos.getInstrIterator(), + ++LastMI->getIterator()); + + Modified = true; + LLVM_DEBUG(dbgs() << "VPT block created for: "; MI->dump();); + + ++MBIter; + } + return Modified; +} + +bool MVEVPTBlock::runOnMachineFunction(MachineFunction &Fn) { + const ARMSubtarget &STI = + static_cast<const ARMSubtarget &>(Fn.getSubtarget()); + + if (!STI.isThumb2() || !STI.hasMVEIntegerOps()) + return false; + + TII = static_cast<const Thumb2InstrInfo *>(STI.getInstrInfo()); + TRI = STI.getRegisterInfo(); + + LLVM_DEBUG(dbgs() << "********** ARM MVE VPT BLOCKS **********\n" + << "********** Function: " << Fn.getName() << '\n'); + + bool Modified = false; + for (MachineBasicBlock &MBB : Fn) + Modified |= InsertVPTBlocks(MBB); + + LLVM_DEBUG(dbgs() << "**************************************\n"); + return Modified; } + +/// createMVEVPTBlock - Returns an instance of the MVE VPT block +/// insertion pass. +FunctionPass *llvm::createMVEVPTBlockPass() { return new MVEVPTBlock(); } diff --git a/lib/Target/ARM/Thumb2InstrInfo.cpp b/lib/Target/ARM/Thumb2InstrInfo.cpp index d567d3339049..5a965f7a6b9b 100644 --- a/lib/Target/ARM/Thumb2InstrInfo.cpp +++ b/lib/Target/ARM/Thumb2InstrInfo.cpp @@ -1,9 +1,8 @@ //===- Thumb2InstrInfo.cpp - Thumb-2 Instruction Information --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -162,7 +161,7 @@ storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, // otherwise). if (TargetRegisterInfo::isVirtualRegister(SrcReg)) { MachineRegisterInfo *MRI = &MF.getRegInfo(); - MRI->constrainRegClass(SrcReg, &ARM::GPRPair_with_gsub_1_in_rGPRRegClass); + MRI->constrainRegClass(SrcReg, &ARM::GPRPair_with_gsub_1_in_GPRwithAPSRnospRegClass); } MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(ARM::t2STRDi8)); @@ -204,7 +203,7 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, if (TargetRegisterInfo::isVirtualRegister(DestReg)) { MachineRegisterInfo *MRI = &MF.getRegInfo(); MRI->constrainRegClass(DestReg, - &ARM::GPRPair_with_gsub_1_in_rGPRRegClass); + &ARM::GPRPair_with_gsub_1_in_GPRwithAPSRnospRegClass); } MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(ARM::t2LDRDi8)); @@ -478,7 +477,7 @@ bool llvm::rewriteT2FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, bool isSub = false; // Memory operands in inline assembly always use AddrModeT2_i12. - if (Opcode == ARM::INLINEASM) + if (Opcode == ARM::INLINEASM || Opcode == ARM::INLINEASM_BR) AddrMode = ARMII::AddrModeT2_i12; // FIXME. mode for thumb2? if (Opcode == ARM::t2ADDri || Opcode == ARM::t2ADDri12) { @@ -611,9 +610,23 @@ bool llvm::rewriteT2FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, Offset = -Offset; isSub = true; } + } else if (AddrMode == ARMII::AddrModeT2_i7s4 || + AddrMode == ARMII::AddrModeT2_i7s2 || + AddrMode == ARMII::AddrModeT2_i7) { + Offset += MI.getOperand(FrameRegIdx + 1).getImm(); + unsigned OffsetMask; + switch (AddrMode) { + case ARMII::AddrModeT2_i7s4: NumBits = 9; OffsetMask = 0x3; break; + case ARMII::AddrModeT2_i7s2: NumBits = 8; OffsetMask = 0x1; break; + default: NumBits = 7; OffsetMask = 0x0; break; + } + // MCInst operand expects already scaled value. + Scale = 1; + assert((Offset & OffsetMask) == 0 && "Can't encode this offset!"); + (void)OffsetMask; // squash unused-variable warning at -NDEBUG } else if (AddrMode == ARMII::AddrModeT2_i8s4) { Offset += MI.getOperand(FrameRegIdx + 1).getImm() * 4; - NumBits = 10; // 8 bits scaled by 4 + NumBits = 8 + 2; // MCInst operand expects already scaled value. Scale = 1; assert((Offset & 3) == 0 && "Can't encode this offset!"); @@ -639,7 +652,7 @@ bool llvm::rewriteT2FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, // Replace the FrameIndex with fp/sp MI.getOperand(FrameRegIdx).ChangeToRegister(FrameReg, false); if (isSub) { - if (AddrMode == ARMII::AddrMode5) + if (AddrMode == ARMII::AddrMode5 || AddrMode == ARMII::AddrMode5FP16) // FIXME: Not consistent. ImmedOffset |= 1 << NumBits; else @@ -653,7 +666,7 @@ bool llvm::rewriteT2FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, // Otherwise, offset doesn't fit. Pull in what we can to simplify ImmedOffset = ImmedOffset & Mask; if (isSub) { - if (AddrMode == ARMII::AddrMode5) + if (AddrMode == ARMII::AddrMode5 || AddrMode == ARMII::AddrMode5FP16) // FIXME: Not consistent. ImmedOffset |= 1 << NumBits; else { @@ -678,3 +691,28 @@ ARMCC::CondCodes llvm::getITInstrPredicate(const MachineInstr &MI, return ARMCC::AL; return getInstrPredicate(MI, PredReg); } + +int llvm::findFirstVPTPredOperandIdx(const MachineInstr &MI) { + const MCInstrDesc &MCID = MI.getDesc(); + + if (!MCID.OpInfo) + return -1; + + for (unsigned i = 0, e = MCID.getNumOperands(); i != e; ++i) + if (ARM::isVpred(MCID.OpInfo[i].OperandType)) + return i; + + return -1; +} + +ARMVCC::VPTCodes llvm::getVPTInstrPredicate(const MachineInstr &MI, + unsigned &PredReg) { + int PIdx = findFirstVPTPredOperandIdx(MI); + if (PIdx == -1) { + PredReg = 0; + return ARMVCC::None; + } + + PredReg = MI.getOperand(PIdx+1).getReg(); + return (ARMVCC::VPTCodes)MI.getOperand(PIdx).getImm(); +} diff --git a/lib/Target/ARM/Thumb2InstrInfo.h b/lib/Target/ARM/Thumb2InstrInfo.h index c834ba73bfea..a6712d5a0e72 100644 --- a/lib/Target/ARM/Thumb2InstrInfo.h +++ b/lib/Target/ARM/Thumb2InstrInfo.h @@ -1,9 +1,8 @@ //===-- Thumb2InstrInfo.h - Thumb-2 Instruction Information -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -69,6 +68,12 @@ private: /// to llvm::getInstrPredicate except it returns AL for conditional branch /// instructions which are "predicated", but are not in IT blocks. ARMCC::CondCodes getITInstrPredicate(const MachineInstr &MI, unsigned &PredReg); + +// getVPTInstrPredicate: VPT analogue of that, plus a helper function +// corresponding to MachineInstr::findFirstPredOperandIdx. +int findFirstVPTPredOperandIdx(const MachineInstr &MI); +ARMVCC::VPTCodes getVPTInstrPredicate(const MachineInstr &MI, + unsigned &PredReg); } #endif diff --git a/lib/Target/ARM/Thumb2SizeReduction.cpp b/lib/Target/ARM/Thumb2SizeReduction.cpp index 65889fc4e28b..37a85fa38417 100644 --- a/lib/Target/ARM/Thumb2SizeReduction.cpp +++ b/lib/Target/ARM/Thumb2SizeReduction.cpp @@ -1,9 +1,8 @@ //===-- Thumb2SizeReduction.cpp - Thumb2 code size reduction pass -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -454,7 +453,7 @@ Thumb2SizeReduce::ReduceLoadStore(MachineBasicBlock &MBB, MachineInstr *MI, break; case ARM::t2LDR_POST: case ARM::t2STR_POST: { - if (!MBB.getParent()->getFunction().optForMinSize()) + if (!MinimizeSize) return false; if (!MI->hasOneMemOperand() || @@ -1128,8 +1127,8 @@ bool Thumb2SizeReduce::runOnMachineFunction(MachineFunction &MF) { TII = static_cast<const Thumb2InstrInfo *>(STI->getInstrInfo()); // Optimizing / minimizing size? Minimizing size implies optimizing for size. - OptimizeSize = MF.getFunction().optForSize(); - MinimizeSize = MF.getFunction().optForMinSize(); + OptimizeSize = MF.getFunction().hasOptSize(); + MinimizeSize = STI->hasMinSize(); BlockInfo.clear(); BlockInfo.resize(MF.getNumBlockIDs()); diff --git a/lib/Target/ARM/ThumbRegisterInfo.cpp b/lib/Target/ARM/ThumbRegisterInfo.cpp index e4bdd40fb743..a96417ffce4d 100644 --- a/lib/Target/ARM/ThumbRegisterInfo.cpp +++ b/lib/Target/ARM/ThumbRegisterInfo.cpp @@ -1,9 +1,8 @@ //===-- ThumbRegisterInfo.cpp - Thumb-1 Register Information -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -447,63 +446,6 @@ void ThumbRegisterInfo::resolveFrameIndex(MachineInstr &MI, unsigned BaseReg, (void)Done; } -/// saveScavengerRegister - Spill the register so it can be used by the -/// register scavenger. Return true. -bool ThumbRegisterInfo::saveScavengerRegister( - MachineBasicBlock &MBB, MachineBasicBlock::iterator I, - MachineBasicBlock::iterator &UseMI, const TargetRegisterClass *RC, - unsigned Reg) const { - - const ARMSubtarget &STI = MBB.getParent()->getSubtarget<ARMSubtarget>(); - if (!STI.isThumb1Only()) - return ARMBaseRegisterInfo::saveScavengerRegister(MBB, I, UseMI, RC, Reg); - - // Thumb1 can't use the emergency spill slot on the stack because - // ldr/str immediate offsets must be positive, and if we're referencing - // off the frame pointer (if, for example, there are alloca() calls in - // the function, the offset will be negative. Use R12 instead since that's - // a call clobbered register that we know won't be used in Thumb1 mode. - const TargetInstrInfo &TII = *STI.getInstrInfo(); - DebugLoc DL; - BuildMI(MBB, I, DL, TII.get(ARM::tMOVr)) - .addReg(ARM::R12, RegState::Define) - .addReg(Reg, RegState::Kill) - .add(predOps(ARMCC::AL)); - - // The UseMI is where we would like to restore the register. If there's - // interference with R12 before then, however, we'll need to restore it - // before that instead and adjust the UseMI. - bool done = false; - for (MachineBasicBlock::iterator II = I; !done && II != UseMI ; ++II) { - if (II->isDebugInstr()) - continue; - // If this instruction affects R12, adjust our restore point. - for (unsigned i = 0, e = II->getNumOperands(); i != e; ++i) { - const MachineOperand &MO = II->getOperand(i); - if (MO.isRegMask() && MO.clobbersPhysReg(ARM::R12)) { - UseMI = II; - done = true; - break; - } - if (!MO.isReg() || MO.isUndef() || !MO.getReg() || - TargetRegisterInfo::isVirtualRegister(MO.getReg())) - continue; - if (MO.getReg() == ARM::R12) { - UseMI = II; - done = true; - break; - } - } - } - // Restore the register from R12 - BuildMI(MBB, UseMI, DL, TII.get(ARM::tMOVr)) - .addReg(Reg, RegState::Define) - .addReg(ARM::R12, RegState::Kill) - .add(predOps(ARMCC::AL)); - - return true; -} - void ThumbRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, RegScavenger *RS) const { @@ -619,3 +561,14 @@ void ThumbRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, if (MI.isPredicable()) MIB.add(predOps(ARMCC::AL)); } + +bool +ThumbRegisterInfo::useFPForScavengingIndex(const MachineFunction &MF) const { + if (MF.getSubtarget<ARMSubtarget>().isThumb1Only()) { + // For Thumb1, the emergency spill slot must be some small positive + // offset from the base/stack pointer. + return false; + } + // For Thumb2, put the emergency spill slot next to FP. + return true; +} diff --git a/lib/Target/ARM/ThumbRegisterInfo.h b/lib/Target/ARM/ThumbRegisterInfo.h index 75c3fe9ae8ad..08cf67284d4c 100644 --- a/lib/Target/ARM/ThumbRegisterInfo.h +++ b/lib/Target/ARM/ThumbRegisterInfo.h @@ -1,9 +1,8 @@ //===- ThumbRegisterInfo.h - Thumb Register Information Impl -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -52,14 +51,10 @@ public: const ARMBaseInstrInfo &TII) const; void resolveFrameIndex(MachineInstr &MI, unsigned BaseReg, int64_t Offset) const override; - bool saveScavengerRegister(MachineBasicBlock &MBB, - MachineBasicBlock::iterator I, - MachineBasicBlock::iterator &UseMI, - const TargetRegisterClass *RC, - unsigned Reg) const override; void eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, RegScavenger *RS = nullptr) const override; + bool useFPForScavengingIndex(const MachineFunction &MF) const override; }; } diff --git a/lib/Target/ARM/Utils/ARMBaseInfo.cpp b/lib/Target/ARM/Utils/ARMBaseInfo.cpp index 534f78c6d4d2..4ace61cccd0f 100644 --- a/lib/Target/ARM/Utils/ARMBaseInfo.cpp +++ b/lib/Target/ARM/Utils/ARMBaseInfo.cpp @@ -1,9 +1,8 @@ //===-- ARMBaseInfo.cpp - ARM Base encoding information------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/Target/ARM/Utils/ARMBaseInfo.h b/lib/Target/ARM/Utils/ARMBaseInfo.h index f32d8223f53c..aa3aca359cb8 100644 --- a/lib/Target/ARM/Utils/ARMBaseInfo.h +++ b/lib/Target/ARM/Utils/ARMBaseInfo.h @@ -1,9 +1,8 @@ //===-- ARMBaseInfo.h - Top level definitions for ARM ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -67,6 +66,30 @@ inline static CondCodes getOppositeCondition(CondCodes CC) { } } // end namespace ARMCC +namespace ARMVCC { + enum VPTCodes { + None = 0, + Then, + Else + }; +} + +inline static const char *ARMVPTPredToString(ARMVCC::VPTCodes CC) { + switch (CC) { + case ARMVCC::None: return "none"; + case ARMVCC::Then: return "t"; + case ARMVCC::Else: return "e"; + } + llvm_unreachable("Unknown VPT code"); +} + +inline static unsigned ARMVectorCondCodeFromString(StringRef CC) { + return StringSwitch<unsigned>(CC.lower()) + .Case("t", ARMVCC::Then) + .Case("e", ARMVCC::Else) + .Default(~0U); +} + inline static const char *ARMCondCodeToString(ARMCC::CondCodes CC) { switch (CC) { case ARMCC::EQ: return "eq"; |
