llvm-project/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
Craig Topper e246d1615e
[RISCV] Force a frame pointer when the max reserved call frame exceeds simm12. (#182124)
We need to be able to address emergency spill slots without requiring a
register scavenging. This requires the emergency spill slot to be near
the SP or the FP to keep the offset small enough. If there is a large
reserved call frame, we can't keep the emergency spill slot near SP. But
we might not have a frame pointer.

This patch forces the use of a frame pointer when the max reserved call
frame is large so we can keep the emergency spill slot near it. This
idea is borrowed from AArch64.

Multiple MIR tests had to be updated to set the max call frame size as
the reserved registers are frozen before mirFileLoaded is called. I
copied mirFileLoaded from AArch64, but it appears the register freezing
moved after the AArch64 code was written.

Fixes #180199.
2026-02-18 16:42:40 -08:00

299 lines
11 KiB
C++

//===-- 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/RISCVLegalizerInfo.h"
#include "RISCV.h"
#include "RISCVFrameLowering.h"
#include "RISCVSelectionDAGInfo.h"
#include "RISCVTargetMachine.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
#define DEBUG_TYPE "riscv-subtarget"
#define GET_SUBTARGETINFO_TARGET_DESC
#define GET_SUBTARGETINFO_CTOR
#include "RISCVGenSubtargetInfo.inc"
#define GET_RISCV_MACRO_FUSION_PRED_IMPL
#include "RISCVGenMacroFusion.inc"
namespace llvm::RISCVTuneInfoTable {
#define GET_RISCVTuneInfoTable_IMPL
#include "RISCVGenSearchableTables.inc"
} // namespace llvm::RISCVTuneInfoTable
static cl::opt<unsigned> RVVVectorLMULMax(
"riscv-v-fixed-length-vector-lmul-max",
cl::desc("The maximum LMUL value to use for fixed length vectors. "
"Fractional LMUL values are not supported."),
cl::init(8), cl::Hidden);
static cl::opt<bool> RISCVDisableUsingConstantPoolForLargeInts(
"riscv-disable-using-constant-pool-for-large-ints",
cl::desc("Disable using constant pool for large integers."),
cl::init(false), cl::Hidden);
static cl::opt<unsigned> RISCVMaxBuildIntsCost(
"riscv-max-build-ints-cost",
cl::desc("The maximum cost used for building integers."), cl::init(0),
cl::Hidden);
static cl::opt<bool> UseAA("riscv-use-aa", cl::init(true),
cl::desc("Enable the use of AA during codegen."));
static cl::opt<unsigned> RISCVMinimumJumpTableEntries(
"riscv-min-jump-table-entries", cl::Hidden,
cl::desc("Set minimum number of entries to use a jump table on RISCV"));
static cl::opt<bool> UseMIPSLoadStorePairsOpt(
"use-riscv-mips-load-store-pairs",
cl::desc("Enable the load/store pair optimization pass"), cl::init(false),
cl::Hidden);
static cl::opt<bool> UseMIPSCCMovInsn("use-riscv-mips-ccmov",
cl::desc("Use 'mips.ccmov' instruction"),
cl::init(true), cl::Hidden);
static cl::opt<bool> EnablePExtSIMDCodeGen(
"riscv-enable-p-ext-simd-codegen",
cl::desc("Turn on P Extension SIMD codegen(This is a temporary switch "
"where only partial codegen is currently supported)"),
cl::init(false), cl::Hidden);
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);
TargetABI = RISCVABI::computeTargetABI(TT, getFeatureBits(), ABIName);
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),
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 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 !RISCVDisableUsingConstantPoolForLargeInts;
}
bool RISCVSubtarget::enablePExtSIMDCodeGen() const {
return HasStdExtP && EnablePExtSIMDCodeGen;
}
// Returns true if VT is a P extension packed SIMD type that fits in XLen.
bool RISCVSubtarget::isPExtPackedType(MVT VT) const {
if (!enablePExtSIMDCodeGen())
return false;
if (is64Bit())
return VT == MVT::v8i8 || VT == MVT::v4i16 || VT == MVT::v2i32;
return VT == MVT::v4i8 || VT == MVT::v2i16;
}
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 RISCVMaxBuildIntsCost == 0
? getSchedModel().LoadLatency + 1
: std::max<unsigned>(2, RISCVMaxBuildIntsCost);
}
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;
}
unsigned RISCVSubtarget::getMaxLMULForFixedLengthVectors() const {
assert(hasVInstructions() &&
"Tried to get vector length without Zve or V extension support!");
assert(RVVVectorLMULMax <= 8 &&
llvm::has_single_bit<uint32_t>(RVVVectorLMULMax) &&
"V extension requires a LMUL to be at most 8 and a power of 2!");
return llvm::bit_floor(std::clamp<unsigned>(RVVVectorLMULMax, 1, 8));
}
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 UseAA; }
unsigned RISCVSubtarget::getMinimumJumpTableEntries() const {
return RISCVMinimumJumpTableEntries.getNumOccurrences() > 0
? RISCVMinimumJumpTableEntries
: 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 UseMIPSLoadStorePairsOpt && HasVendorXMIPSLSP;
}
bool RISCVSubtarget::useMIPSCCMovInsn() const {
return UseMIPSCCMovInsn && HasVendorXMIPSCMov;
}