[RISCV] Add support for -ffixed-xX flags
This adds support for reserving GPRs such that the compiler will not
choose a register for register allocation. The implementation follows
the same design as for AArch64; each reserved register becomes a target
feature and used for getting the reserved registers for a given
MachineFunction. The backend checks that it does not need to write to
any reserved register; if it does a relevant error is generated.
Differential Revision: https://reviews.llvm.org/D67185
diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td
index 46530a8f..70d87d6 100644
--- a/llvm/lib/Target/RISCV/RISCV.td
+++ b/llvm/lib/Target/RISCV/RISCV.td
@@ -69,6 +69,11 @@
: SubtargetFeature<"relax", "EnableLinkerRelax", "true",
"Enable Linker relaxation.">;
+foreach i = {1-31} in
+ def FeatureReserveX#i :
+ SubtargetFeature<"reserve-x"#i, "UserReservedRegister[RISCV::X"#i#"]",
+ "true", "Reserve X"#i>;
+
//===----------------------------------------------------------------------===//
// Named operands for CSR instructions.
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 6b6f62e..daa7b7a 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -18,6 +18,7 @@
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/MC/MCDwarf.h"
using namespace llvm;
@@ -131,6 +132,12 @@
if (StackSize == 0 && !MFI.adjustsStack())
return;
+ // If the stack pointer has been marked as reserved, then produce an error if
+ // the frame requires stack allocation
+ if (STI.isRegisterReservedByUser(SPReg))
+ MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(), "Stack pointer required, but has been reserved."});
+
uint64_t FirstSPAdjustAmount = getFirstSPAdjustAmount(MF);
// Split the SP adjustment to reduce the offsets of callee saved spill.
if (FirstSPAdjustAmount)
@@ -167,6 +174,10 @@
// Generate new FP.
if (hasFP(MF)) {
+ if (STI.isRegisterReservedByUser(FPReg))
+ MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(), "Frame pointer required, but has been reserved."});
+
adjustReg(MBB, MBBI, DL, FPReg, SPReg,
StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup);
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index dc829fc..b56d951 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -2247,6 +2247,16 @@
Glue = Chain.getValue(1);
}
+ // Validate that none of the argument registers have been marked as
+ // reserved, if so report an error. Do the same for the return address if this
+ // is not a tailcall.
+ validateCCReservedRegs(RegsToPass, MF);
+ if (!IsTailCall &&
+ MF.getSubtarget<RISCVSubtarget>().isRegisterReservedByUser(RISCV::X1))
+ MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(),
+ "Return address register required, but has been reserved."});
+
// If the callee is a GlobalAddress/ExternalSymbol node, turn it into a
// TargetGlobalAddress/TargetExternalSymbol node so that legalize won't
// split it and then direct call can be matched by PseudoCALL.
@@ -2362,6 +2372,9 @@
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
+ const MachineFunction &MF = DAG.getMachineFunction();
+ const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>();
+
// Stores the assignment of the return value to a location.
SmallVector<CCValAssign, 16> RVLocs;
@@ -2391,6 +2404,13 @@
Register RegLo = VA.getLocReg();
assert(RegLo < RISCV::X31 && "Invalid register pair");
Register RegHi = RegLo + 1;
+
+ if (STI.isRegisterReservedByUser(RegLo) ||
+ STI.isRegisterReservedByUser(RegHi))
+ MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(),
+ "Return value register required, but has been reserved."});
+
Chain = DAG.getCopyToReg(Chain, DL, RegLo, Lo, Glue);
Glue = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(RegLo, MVT::i32));
@@ -2402,6 +2422,11 @@
Val = convertValVTToLocVT(DAG, Val, VA, DL);
Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Glue);
+ if (STI.isRegisterReservedByUser(VA.getLocReg()))
+ MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(),
+ "Return value register required, but has been reserved."});
+
// Guarantee that all emitted copies are stuck together.
Glue = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
@@ -2440,6 +2465,19 @@
return DAG.getNode(RISCVISD::RET_FLAG, DL, MVT::Other, RetOps);
}
+void RISCVTargetLowering::validateCCReservedRegs(
+ const SmallVectorImpl<std::pair<llvm::Register, llvm::SDValue>> &Regs,
+ MachineFunction &MF) const {
+ const Function &F = MF.getFunction();
+ const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>();
+
+ if (std::any_of(std::begin(Regs), std::end(Regs), [&STI](auto Reg) {
+ return STI.isRegisterReservedByUser(Reg.first);
+ }))
+ F.getContext().diagnose(DiagnosticInfoUnsupported{
+ F, "Argument register required, but has been reserved."});
+}
+
const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
switch ((RISCVISD::NodeType)Opcode) {
case RISCVISD::FIRST_NUMBER:
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 18fc735..0d2d14e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -210,6 +210,12 @@
Value *AlignedAddr, Value *CmpVal,
Value *NewVal, Value *Mask,
AtomicOrdering Ord) const override;
+
+ /// Generate error diagnostics if any register used by CC has been marked
+ /// reserved.
+ void validateCCReservedRegs(
+ const SmallVectorImpl<std::pair<llvm::Register, llvm::SDValue>> &Regs,
+ MachineFunction &MF) const;
};
}
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 6655768..65a7e41 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -69,6 +69,12 @@
const TargetFrameLowering *TFI = getFrameLowering(MF);
BitVector Reserved(getNumRegs());
+ // Mark any registers requested to be reserved as such
+ for (size_t Reg = 0; Reg < getNumRegs(); Reg++) {
+ if (MF.getSubtarget<RISCVSubtarget>().isRegisterReservedByUser(Reg))
+ markSuperRegs(Reserved, Reg);
+ }
+
// Use markSuperRegs to ensure any register aliases are also reserved
markSuperRegs(Reserved, RISCV::X0); // zero
markSuperRegs(Reserved, RISCV::X1); // ra
@@ -81,6 +87,11 @@
return Reserved;
}
+bool RISCVRegisterInfo::isAsmClobberable(const MachineFunction &MF,
+ unsigned PhysReg) const {
+ return !MF.getSubtarget<RISCVSubtarget>().isRegisterReservedByUser(PhysReg);
+}
+
bool RISCVRegisterInfo::isConstantPhysReg(unsigned PhysReg) const {
return PhysReg == RISCV::X0;
}
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
index 56a50fe..30b6395 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
@@ -30,6 +30,8 @@
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
BitVector getReservedRegs(const MachineFunction &MF) const override;
+ bool isAsmClobberable(const MachineFunction &MF,
+ unsigned PhysReg) const override;
bool isConstantPhysReg(unsigned PhysReg) const override;
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index f114c6a..83e7e2d 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -50,6 +50,7 @@
RISCVSubtarget::RISCVSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
StringRef ABIName, const TargetMachine &TM)
: RISCVGenSubtargetInfo(TT, CPU, FS),
+ UserReservedRegister(RISCV::NUM_TARGET_REGS),
FrameLowering(initializeSubtargetDependencies(TT, CPU, FS, ABIName)),
InstrInfo(*this), RegInfo(getHwMode()), TLInfo(TM, *this) {
CallLoweringInfo.reset(new RISCVCallLowering(*getTargetLowering()));
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index 7d0373a..605d4ab 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -46,6 +46,7 @@
unsigned XLen = 32;
MVT XLenVT = MVT::i32;
RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown;
+ BitVector UserReservedRegister;
RISCVFrameLowering FrameLowering;
RISCVInstrInfo InstrInfo;
RISCVRegisterInfo RegInfo;
@@ -93,6 +94,10 @@
MVT getXLenVT() const { return XLenVT; }
unsigned getXLen() const { return XLen; }
RISCVABI::ABI getTargetABI() const { return TargetABI; }
+ bool isRegisterReservedByUser(Register i) const {
+ assert(i < RISCV::NUM_TARGET_REGS && "Register out of range");
+ return UserReservedRegister[i];
+ }
protected:
// GlobalISel related APIs.