| //===-- RISCVSubtarget.cpp - RISC-V Subtarget Information -----------------===// | |
| // | |
| // 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 implements the RISC-V specific subclass of TargetSubtargetInfo. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "RISCVSubtarget.h" | |
| #include "GISel/RISCVCallLowering.h" | |
| #include "GISel/RISCVInlineAsmLowering.h" | |
| #include "GISel/RISCVLegalizerInfo.h" | |
| #include "RISCV.h" | |
| #include "RISCVFrameLowering.h" | |
| #include "RISCVSelectionDAGInfo.h" | |
| #include "RISCVTargetMachine.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| #include "llvm/CodeGen/MachineTraceMetrics.h" | |
| #include "llvm/MC/MCSchedule.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Option/LibraryOptions.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "riscv-macro-fusion" | |
| #define GET_RISCV_MACRO_FUSION_PRED_IMPL | |
| #include "RISCVGenMacroFusion.inc" | |
| #undef DEBUG_TYPE | |
| #define DEBUG_TYPE "riscv-subtarget" | |
| #define GET_SUBTARGETINFO_TARGET_DESC | |
| #define GET_SUBTARGETINFO_CTOR | |
| #include "RISCVGenSubtargetInfo.inc" | |
| #define OPTIONS_STRUCT_DEFS | |
| #include "RISCVOptions.inc" | |
| namespace llvm::RISCVTuneInfoTable { | |
| #define GET_RISCVTuneInfoTable_IMPL | |
| #include "RISCVGenSearchableTables.inc" | |
| } // namespace llvm::RISCVTuneInfoTable | |
| void RISCVSubtarget::anchor() {} | |
| RISCVSubtarget & | |
| RISCVSubtarget::initializeSubtargetDependencies(const Triple &TT, StringRef CPU, | |
| StringRef TuneCPU, StringRef FS, | |
| StringRef ABIName) { | |
| // Determine default and user-specified characteristics | |
| bool Is64Bit = TT.isArch64Bit(); | |
| if (CPU.empty() || CPU == "generic") | |
| CPU = Is64Bit ? "generic-rv64" : "generic-rv32"; | |
| if (TuneCPU.empty()) | |
| TuneCPU = CPU; | |
| if (TuneCPU == "generic") | |
| TuneCPU = Is64Bit ? "generic-rv64" : "generic-rv32"; | |
| TuneInfo = RISCVTuneInfoTable::getRISCVTuneInfo(TuneCPU); | |
| // If there is no TuneInfo for this CPU, we fail back to generic. | |
| if (!TuneInfo) | |
| TuneInfo = RISCVTuneInfoTable::getRISCVTuneInfo("generic"); | |
| assert(TuneInfo && "TuneInfo shouldn't be nullptr!"); | |
| ParseSubtargetFeatures(CPU, TuneCPU, FS); | |
| RISCV::updateCZceFeatureImplications(*this); | |
| // Re-sync the flags. | |
| HasStdExtZcd = hasFeature(RISCV::FeatureStdExtZcd); | |
| HasStdExtZcf = hasFeature(RISCV::FeatureStdExtZcf); | |
| HasStdExtC = hasFeature(RISCV::FeatureStdExtC); | |
| HasStdExtZce = hasFeature(RISCV::FeatureStdExtZce); | |
| // Can't be fatal: per-function subtargets mean this one may just be the | |
| // module-level default with no matching function, e.g. -target-abi ilp32f | |
| // with no global -mattr=+f but all functions have their own "+f" attribute. | |
| if (auto ABIOrErr = RISCVABI::computeTargetABI(*this, ABIName)) { | |
| TargetABI = *ABIOrErr; | |
| } else { | |
| errs() << "note: " << toString(ABIOrErr.takeError()) | |
| << " (ignoring target-abi)\n"; | |
| TargetABI = cantFail(RISCVABI::computeTargetABI(*this, "")); | |
| } | |
| RISCVFeatures::validate(TT, getFeatureBits()); | |
| return *this; | |
| } | |
| RISCVSubtarget::RISCVSubtarget(const Triple &TT, StringRef CPU, | |
| StringRef TuneCPU, StringRef FS, | |
| StringRef ABIName, unsigned RVVVectorBitsMin, | |
| unsigned RVVVectorBitsMax, | |
| const TargetMachine &TM) | |
| : RISCVGenSubtargetInfo(TT, CPU, TuneCPU, FS), | |
| CLOpts(static_cast<const RISCVTargetMachine &>(TM).getCLOpts()), | |
| IsLittleEndian(TT.isLittleEndian()), RVVVectorBitsMin(RVVVectorBitsMin), | |
| RVVVectorBitsMax(RVVVectorBitsMax), | |
| FrameLowering( | |
| initializeSubtargetDependencies(TT, CPU, TuneCPU, FS, ABIName)), | |
| InstrInfo(*this), TLInfo(TM, *this) { | |
| TSInfo = std::make_unique<RISCVSelectionDAGInfo>(); | |
| } | |
| RISCVSubtarget::~RISCVSubtarget() = default; | |
| const SelectionDAGTargetInfo *RISCVSubtarget::getSelectionDAGInfo() const { | |
| return TSInfo.get(); | |
| } | |
| const InlineAsmLowering *RISCVSubtarget::getInlineAsmLowering() const { | |
| if (!InlineAsmLoweringInfo) | |
| InlineAsmLoweringInfo.reset( | |
| new RISCVInlineAsmLowering(getTargetLowering())); | |
| return InlineAsmLoweringInfo.get(); | |
| } | |
| const CallLowering *RISCVSubtarget::getCallLowering() const { | |
| if (!CallLoweringInfo) | |
| CallLoweringInfo.reset(new RISCVCallLowering(*getTargetLowering())); | |
| return CallLoweringInfo.get(); | |
| } | |
| InstructionSelector *RISCVSubtarget::getInstructionSelector() const { | |
| if (!InstSelector) { | |
| InstSelector.reset(createRISCVInstructionSelector( | |
| *static_cast<const RISCVTargetMachine *>(&TLInfo.getTargetMachine()), | |
| *this, *getRegBankInfo())); | |
| } | |
| return InstSelector.get(); | |
| } | |
| const LegalizerInfo *RISCVSubtarget::getLegalizerInfo() const { | |
| if (!Legalizer) | |
| Legalizer.reset(new RISCVLegalizerInfo(*this)); | |
| return Legalizer.get(); | |
| } | |
| const RISCVRegisterBankInfo *RISCVSubtarget::getRegBankInfo() const { | |
| if (!RegBankInfo) | |
| RegBankInfo.reset(new RISCVRegisterBankInfo(getHwMode())); | |
| return RegBankInfo.get(); | |
| } | |
| bool RISCVSubtarget::useConstantPoolForLargeInts() const { | |
| return CLOpts.constant_pool_for_large_ints; | |
| } | |
| // Returns true if VT is a P extension packed SIMD type. | |
| bool RISCVSubtarget::isPExtPackedType(MVT VT) const { | |
| if (!HasStdExtP) | |
| return false; | |
| // RV32 supports 32-bit and 64-bit vectors. RV64 only support 64-bit vectors. | |
| if (!is64Bit() && (VT == MVT::v4i8 || VT == MVT::v2i16)) | |
| return true; | |
| return VT == MVT::v8i8 || VT == MVT::v4i16 || VT == MVT::v2i32; | |
| } | |
| // Returns true if VT is a P extension packed double-wide SIMD type. | |
| bool RISCVSubtarget::isPExtPackedDoubleType(MVT VT) const { | |
| if (!HasStdExtP || is64Bit()) | |
| return false; | |
| return VT == MVT::v8i8 || VT == MVT::v4i16 || VT == MVT::v2i32; | |
| } | |
| unsigned RISCVSubtarget::getMaxBuildIntsCost() const { | |
| // Loading integer from constant pool needs two instructions (the reason why | |
| // the minimum cost is 2): an address calculation instruction and a load | |
| // instruction. Usually, address calculation and instructions used for | |
| // building integers (addi, slli, etc.) can be done in one cycle, so here we | |
| // set the default cost to (LoadLatency + 1) if no threshold is provided. | |
| return CLOpts.max_build_ints_cost == 0 | |
| ? getLoadLatency() + 1 | |
| : std::max<unsigned>(2, CLOpts.max_build_ints_cost); | |
| } | |
| unsigned RISCVSubtarget::getMispredictionPenalty() const { | |
| return CLOpts.sched_mispredict_penalty.value_or( | |
| getSchedModel().MispredictPenalty); | |
| } | |
| unsigned RISCVSubtarget::getLoadLatency() const { | |
| return CLOpts.sched_load_latency.value_or(getSchedModel().LoadLatency); | |
| } | |
| unsigned RISCVSubtarget::getMaxRVVVectorSizeInBits() const { | |
| assert(hasVInstructions() && | |
| "Tried to get vector length without Zve or V extension support!"); | |
| // ZvlLen specifies the minimum required vlen. The upper bound provided by | |
| // riscv-v-vector-bits-max should be no less than it. | |
| if (RVVVectorBitsMax != 0 && RVVVectorBitsMax < ZvlLen) | |
| report_fatal_error("riscv-v-vector-bits-max specified is lower " | |
| "than the Zvl*b limitation"); | |
| return RVVVectorBitsMax; | |
| } | |
| unsigned RISCVSubtarget::getMinRVVVectorSizeInBits() const { | |
| assert(hasVInstructions() && | |
| "Tried to get vector length without Zve or V extension support!"); | |
| if (RVVVectorBitsMin == -1U) | |
| return ZvlLen; | |
| // ZvlLen specifies the minimum required vlen. The lower bound provided by | |
| // riscv-v-vector-bits-min should be no less than it. | |
| if (RVVVectorBitsMin != 0 && RVVVectorBitsMin < ZvlLen) | |
| report_fatal_error("riscv-v-vector-bits-min specified is lower " | |
| "than the Zvl*b limitation"); | |
| return RVVVectorBitsMin; | |
| } | |
| bool RISCVSubtarget::useRVVForFixedLengthVectors() const { | |
| return hasVInstructions() && | |
| getMinRVVVectorSizeInBits() >= RISCV::RVVBitsPerBlock; | |
| } | |
| bool RISCVSubtarget::enableSubRegLiveness() const { return true; } | |
| bool RISCVSubtarget::enableMachinePipeliner() const { | |
| return getSchedModel().hasInstrSchedModel(); | |
| } | |
| void RISCVSubtarget::mirFileLoaded(MachineFunction &MF) const { | |
| // We usually compute max call frame size after ISel. Do the computation now | |
| // if the .mir file didn't specify it. Note that this will probably give you | |
| // bogus values after PEI has eliminated the callframe setup/destroy pseudo | |
| // instructions, specify explicitly if you need it to be correct. | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| if (!MFI.isMaxCallFrameSizeComputed()) | |
| MFI.computeMaxCallFrameSize(MF); | |
| } | |
| /// Enable use of alias analysis during code generation (during MI | |
| /// scheduling, DAGCombine, etc.). | |
| bool RISCVSubtarget::useAA() const { return CLOpts.use_aa; } | |
| unsigned RISCVSubtarget::getMinimumJumpTableEntries() const { | |
| return CLOpts.min_jump_table_entries.value_or( | |
| TuneInfo->MinimumJumpTableEntries); | |
| } | |
| void RISCVSubtarget::overrideSchedPolicy(MachineSchedPolicy &Policy, | |
| const SchedRegion &Region) const { | |
| // Do bidirectional scheduling since it provides a more balanced scheduling | |
| // leading to better performance. This will increase compile time. | |
| Policy.OnlyTopDown = false; | |
| Policy.OnlyBottomUp = false; | |
| // Disabling the latency heuristic can reduce the number of spills/reloads but | |
| // will cause some regressions on some cores. | |
| Policy.DisableLatencyHeuristic = DisableLatencySchedHeuristic; | |
| // Spilling is generally expensive on all RISC-V cores, so always enable | |
| // register-pressure tracking. This will increase compile time. | |
| Policy.ShouldTrackPressure = true; | |
| } | |
| void RISCVSubtarget::overridePostRASchedPolicy( | |
| MachineSchedPolicy &Policy, const SchedRegion &Region) const { | |
| MISched::Direction PostRASchedDirection = getPostRASchedDirection(); | |
| if (PostRASchedDirection == MISched::TopDown) { | |
| Policy.OnlyTopDown = true; | |
| Policy.OnlyBottomUp = false; | |
| } else if (PostRASchedDirection == MISched::BottomUp) { | |
| Policy.OnlyTopDown = false; | |
| Policy.OnlyBottomUp = true; | |
| } else if (PostRASchedDirection == MISched::Bidirectional) { | |
| Policy.OnlyTopDown = false; | |
| Policy.OnlyBottomUp = false; | |
| } | |
| } | |
| bool RISCVSubtarget::useMIPSLoadStorePairs() const { | |
| return CLOpts.mips_load_store_pairs && HasVendorXMIPSLSP; | |
| } | |
| bool RISCVSubtarget::useMIPSCCMovInsn() const { | |
| return CLOpts.mips_ccmov && HasVendorXMIPSCMov; | |
| } |