aboutsummaryrefslogtreecommitdiff
path: root/llvm
diff options
context:
space:
mode:
authorKoakuma <koachan@protonmail.com>2024-02-11 14:04:18 +0700
committerTom Stellard <tstellar@redhat.com>2024-02-13 11:42:37 -0800
commit50f8284ceadb56d8bb08d989b4563b9443e45b5f (patch)
tree8582cc6033d72e68825a369e54f9a18112c7fdc7 /llvm
parent076953d250624b8fdf094f8bce260380e28595ad (diff)
downloadllvm-50f8284ceadb56d8bb08d989b4563b9443e45b5f.zip
llvm-50f8284ceadb56d8bb08d989b4563b9443e45b5f.tar.gz
llvm-50f8284ceadb56d8bb08d989b4563b9443e45b5f.tar.bz2
[SPARC] Support reserving arbitrary general purpose registers (#74927)
This adds support for marking arbitrary general purpose registers - except for those with special purpose (G0, I6-I7, O6-O7) - as reserved, as needed by some software like the Linux kernel. (cherry picked from commit c2f9885a8aa3a820eefdacccf3fcc6b9d87e3284)
Diffstat (limited to 'llvm')
-rw-r--r--llvm/lib/Target/Sparc/Sparc.td14
-rw-r--r--llvm/lib/Target/Sparc/SparcISelLowering.cpp43
-rw-r--r--llvm/lib/Target/Sparc/SparcRegisterInfo.cpp14
-rw-r--r--llvm/lib/Target/Sparc/SparcRegisterInfo.h1
-rw-r--r--llvm/lib/Target/Sparc/SparcRegisterInfo.td4
-rw-r--r--llvm/lib/Target/Sparc/SparcSubtarget.cpp1
-rw-r--r--llvm/lib/Target/Sparc/SparcSubtarget.h10
-rw-r--r--llvm/test/CodeGen/SPARC/reserved-arg-regs.ll25
-rw-r--r--llvm/test/CodeGen/SPARC/reserved-regs-named.ll13
-rw-r--r--llvm/test/CodeGen/SPARC/reserved-regs-unavailable.ll14
-rw-r--r--llvm/test/CodeGen/SPARC/reserved-regs.ll17
11 files changed, 154 insertions, 2 deletions
diff --git a/llvm/lib/Target/Sparc/Sparc.td b/llvm/lib/Target/Sparc/Sparc.td
index 7b10339..38a59e6 100644
--- a/llvm/lib/Target/Sparc/Sparc.td
+++ b/llvm/lib/Target/Sparc/Sparc.td
@@ -72,6 +72,20 @@ def TuneSlowRDPC : SubtargetFeature<"slow-rdpc", "HasSlowRDPC", "true",
//==== Features added predmoninantly for LEON subtarget support
include "LeonFeatures.td"
+//==== Register allocation tweaks needed by some low-level software
+foreach i = 1 ... 7 in
+ def FeatureReserveG#i : SubtargetFeature<"reserve-g"#i, "ReserveRegister["#i#" + SP::G0]", "true",
+ "Reserve G"#i#", making it unavailable as a GPR">;
+foreach i = 0 ... 5 in
+ def FeatureReserveO#i : SubtargetFeature<"reserve-o"#i, "ReserveRegister["#i#" + SP::O0]", "true",
+ "Reserve O"#i#", making it unavailable as a GPR">;
+foreach i = 0 ... 7 in
+ def FeatureReserveL#i : SubtargetFeature<"reserve-l"#i, "ReserveRegister["#i#" + SP::L0]", "true",
+ "Reserve L"#i#", making it unavailable as a GPR">;
+foreach i = 0 ... 5 in
+ def FeatureReserveI#i : SubtargetFeature<"reserve-i"#i, "ReserveRegister["#i#" + SP::I0]", "true",
+ "Reserve I"#i#", making it unavailable as a GPR">;
+
//===----------------------------------------------------------------------===//
// Register File, Calling Conv, Instruction Descriptions
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.cpp b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
index 78bdf3a..bdefb08 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.cpp
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
@@ -13,6 +13,7 @@
#include "SparcISelLowering.h"
#include "MCTargetDesc/SparcMCExpr.h"
+#include "MCTargetDesc/SparcMCTargetDesc.h"
#include "SparcMachineFunctionInfo.h"
#include "SparcRegisterInfo.h"
#include "SparcTargetMachine.h"
@@ -28,6 +29,7 @@
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ErrorHandling.h"
@@ -729,6 +731,30 @@ SDValue SparcTargetLowering::LowerFormalArguments_64(
return Chain;
}
+// Check whether any of the argument registers are reserved
+static bool isAnyArgRegReserved(const SparcRegisterInfo *TRI,
+ const MachineFunction &MF) {
+ // The register window design means that outgoing parameters at O*
+ // will appear in the callee as I*.
+ // Be conservative and check both sides of the register names.
+ bool Outgoing =
+ llvm::any_of(SP::GPROutgoingArgRegClass, [TRI, &MF](MCPhysReg r) {
+ return TRI->isReservedReg(MF, r);
+ });
+ bool Incoming =
+ llvm::any_of(SP::GPRIncomingArgRegClass, [TRI, &MF](MCPhysReg r) {
+ return TRI->isReservedReg(MF, r);
+ });
+ return Outgoing || Incoming;
+}
+
+static void emitReservedArgRegCallError(const MachineFunction &MF) {
+ const Function &F = MF.getFunction();
+ F.getContext().diagnose(DiagnosticInfoUnsupported{
+ F, ("SPARC doesn't support"
+ " function calls if any of the argument registers is reserved.")});
+}
+
SDValue
SparcTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
SmallVectorImpl<SDValue> &InVals) const {
@@ -805,6 +831,7 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
bool &isTailCall = CLI.IsTailCall;
CallingConv::ID CallConv = CLI.CallConv;
bool isVarArg = CLI.IsVarArg;
+ MachineFunction &MF = DAG.getMachineFunction();
// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
@@ -1055,6 +1082,10 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
((hasReturnsTwice)
? TRI->getRTCallPreservedMask(CallConv)
: TRI->getCallPreservedMask(DAG.getMachineFunction(), CallConv));
+
+ if (isAnyArgRegReserved(TRI, MF))
+ emitReservedArgRegCallError(MF);
+
assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask));
@@ -1125,6 +1156,13 @@ Register SparcTargetLowering::getRegisterByName(const char* RegName, LLT VT,
.Case("g4", SP::G4).Case("g5", SP::G5).Case("g6", SP::G6).Case("g7", SP::G7)
.Default(0);
+ // If we're directly referencing register names
+ // (e.g in GCC C extension `register int r asm("g1");`),
+ // make sure that said register is in the reserve list.
+ const SparcRegisterInfo *TRI = Subtarget->getRegisterInfo();
+ if (!TRI->isReservedReg(MF, Reg))
+ Reg = 0;
+
if (Reg)
return Reg;
@@ -1189,6 +1227,7 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
SDLoc DL = CLI.DL;
SDValue Chain = CLI.Chain;
auto PtrVT = getPointerTy(DAG.getDataLayout());
+ MachineFunction &MF = DAG.getMachineFunction();
// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
@@ -1372,6 +1411,10 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
((hasReturnsTwice) ? TRI->getRTCallPreservedMask(CLI.CallConv)
: TRI->getCallPreservedMask(DAG.getMachineFunction(),
CLI.CallConv));
+
+ if (isAnyArgRegReserved(TRI, MF))
+ emitReservedArgRegCallError(MF);
+
assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask));
diff --git a/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp b/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
index f97bf57..71a27f7 100644
--- a/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
@@ -12,10 +12,8 @@
#include "SparcRegisterInfo.h"
#include "Sparc.h"
-#include "SparcMachineFunctionInfo.h"
#include "SparcSubtarget.h"
#include "llvm/ADT/BitVector.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -98,9 +96,21 @@ BitVector SparcRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
for (unsigned n = 0; n < 31; n++)
Reserved.set(SP::ASR1 + n);
+ for (TargetRegisterClass::iterator i = SP::IntRegsRegClass.begin();
+ i != SP::IntRegsRegClass.end(); ++i) {
+ if (MF.getSubtarget<SparcSubtarget>().isRegisterReserved(*i))
+ markSuperRegs(Reserved, *i);
+ }
+
+ assert(checkAllSuperRegsMarked(Reserved));
return Reserved;
}
+bool SparcRegisterInfo::isReservedReg(const MachineFunction &MF,
+ MCRegister Reg) const {
+ return getReservedRegs(MF)[Reg];
+}
+
const TargetRegisterClass*
SparcRegisterInfo::getPointerRegClass(const MachineFunction &MF,
unsigned Kind) const {
diff --git a/llvm/lib/Target/Sparc/SparcRegisterInfo.h b/llvm/lib/Target/Sparc/SparcRegisterInfo.h
index 5b3c1a7..58c85f3 100644
--- a/llvm/lib/Target/Sparc/SparcRegisterInfo.h
+++ b/llvm/lib/Target/Sparc/SparcRegisterInfo.h
@@ -30,6 +30,7 @@ struct SparcRegisterInfo : public SparcGenRegisterInfo {
const uint32_t* getRTCallPreservedMask(CallingConv::ID CC) const;
BitVector getReservedRegs(const MachineFunction &MF) const override;
+ bool isReservedReg(const MachineFunction &MF, MCRegister Reg) const;
const TargetRegisterClass *getPointerRegClass(const MachineFunction &MF,
unsigned Kind) const override;
diff --git a/llvm/lib/Target/Sparc/SparcRegisterInfo.td b/llvm/lib/Target/Sparc/SparcRegisterInfo.td
index d5ba746..d8319a8 100644
--- a/llvm/lib/Target/Sparc/SparcRegisterInfo.td
+++ b/llvm/lib/Target/Sparc/SparcRegisterInfo.td
@@ -370,6 +370,10 @@ def LowQFPRegs : RegisterClass<"SP", [f128], 128, (sequence "Q%u", 0, 7)>;
// Floating point control register classes.
def FCCRegs : RegisterClass<"SP", [i1], 1, (sequence "FCC%u", 0, 3)>;
+// GPR argument registers.
+def GPROutgoingArg : RegisterClass<"SP", [i32, i64], 32, (sequence "O%u", 0, 5)>;
+def GPRIncomingArg : RegisterClass<"SP", [i32, i64], 32, (sequence "I%u", 0, 5)>;
+
let isAllocatable = 0 in {
// Ancillary state registers
// FIXME: TICK is special-cased here as it can be accessed
diff --git a/llvm/lib/Target/Sparc/SparcSubtarget.cpp b/llvm/lib/Target/Sparc/SparcSubtarget.cpp
index 6b09904..5b65e34 100644
--- a/llvm/lib/Target/Sparc/SparcSubtarget.cpp
+++ b/llvm/lib/Target/Sparc/SparcSubtarget.cpp
@@ -50,6 +50,7 @@ SparcSubtarget::SparcSubtarget(const StringRef &CPU, const StringRef &TuneCPU,
const StringRef &FS, const TargetMachine &TM,
bool is64Bit)
: SparcGenSubtargetInfo(TM.getTargetTriple(), CPU, TuneCPU, FS),
+ ReserveRegister(TM.getMCRegisterInfo()->getNumRegs()),
TargetTriple(TM.getTargetTriple()), Is64Bit(is64Bit),
InstrInfo(initializeSubtargetDependencies(CPU, TuneCPU, FS)),
TLInfo(TM, *this), FrameLowering(*this) {}
diff --git a/llvm/lib/Target/Sparc/SparcSubtarget.h b/llvm/lib/Target/Sparc/SparcSubtarget.h
index cdb210f..fe4aca5 100644
--- a/llvm/lib/Target/Sparc/SparcSubtarget.h
+++ b/llvm/lib/Target/Sparc/SparcSubtarget.h
@@ -13,12 +13,14 @@
#ifndef LLVM_LIB_TARGET_SPARC_SPARCSUBTARGET_H
#define LLVM_LIB_TARGET_SPARC_SPARCSUBTARGET_H
+#include "MCTargetDesc/SparcMCTargetDesc.h"
#include "SparcFrameLowering.h"
#include "SparcISelLowering.h"
#include "SparcInstrInfo.h"
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DataLayout.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/TargetParser/Triple.h"
#include <string>
@@ -29,6 +31,10 @@ namespace llvm {
class StringRef;
class SparcSubtarget : public SparcGenSubtargetInfo {
+ // ReserveRegister[i] - Register #i is not available as a general purpose
+ // register.
+ BitVector ReserveRegister;
+
Triple TargetTriple;
virtual void anchor();
@@ -82,6 +88,10 @@ public:
return is64Bit() ? 2047 : 0;
}
+ bool isRegisterReserved(MCPhysReg PhysReg) const {
+ return ReserveRegister[PhysReg];
+ }
+
/// Given a actual stack size as determined by FrameInfo, this function
/// returns adjusted framesize which includes space for register window
/// spills and arguments.
diff --git a/llvm/test/CodeGen/SPARC/reserved-arg-regs.ll b/llvm/test/CodeGen/SPARC/reserved-arg-regs.ll
new file mode 100644
index 0000000..3587ecb
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/reserved-arg-regs.ll
@@ -0,0 +1,25 @@
+;; Test reserving argument registers.
+; RUN: not llc < %s -mtriple=sparc-linux-gnu -mattr=+reserve-o0 2>&1 | FileCheck %s --check-prefixes=CHECK-RESERVED-O0
+; RUN: not llc < %s -mtriple=sparc64-linux-gnu -mattr=+reserve-o0 2>&1 | FileCheck %s --check-prefixes=CHECK-RESERVED-O0
+; RUN: not llc < %s -mtriple=sparc-linux-gnu -mattr=+reserve-i0 2>&1 | FileCheck %s --check-prefixes=CHECK-RESERVED-I0
+; RUN: not llc < %s -mtriple=sparc64-linux-gnu -mattr=+reserve-i0 2>&1 | FileCheck %s --check-prefixes=CHECK-RESERVED-I0
+
+; CHECK-RESERVED-O0: error:
+; CHECK-RESERVED-O0-SAME: SPARC doesn't support function calls if any of the argument registers is reserved.
+; CHECK-RESERVED-I0: error:
+; CHECK-RESERVED-I0-SAME: SPARC doesn't support function calls if any of the argument registers is reserved.
+define void @call_function() {
+ call void @foo()
+ ret void
+}
+declare void @foo()
+
+; CHECK-RESERVED-O0: error:
+; CHECK-RESERVED-O0-SAME: SPARC doesn't support function calls if any of the argument registers is reserved.
+; CHECK-RESERVED-I0: error:
+; CHECK-RESERVED-I0-SAME: SPARC doesn't support function calls if any of the argument registers is reserved.
+define void @call_function_with_arg(i8 %in) {
+ call void @bar(i8 %in)
+ ret void
+}
+declare void @bar(i8)
diff --git a/llvm/test/CodeGen/SPARC/reserved-regs-named.ll b/llvm/test/CodeGen/SPARC/reserved-regs-named.ll
new file mode 100644
index 0000000..91808be
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/reserved-regs-named.ll
@@ -0,0 +1,13 @@
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L0
+
+;; Ensure explicit register references are catched as well.
+
+; CHECK-RESERVED-L0: %l0
+define void @set_reg(i32 zeroext %x) {
+entry:
+ tail call void @llvm.write_register.i32(metadata !0, i32 %x)
+ ret void
+}
+
+declare void @llvm.write_register.i32(metadata, i32)
+!0 = !{!"l0"}
diff --git a/llvm/test/CodeGen/SPARC/reserved-regs-unavailable.ll b/llvm/test/CodeGen/SPARC/reserved-regs-unavailable.ll
new file mode 100644
index 0000000..53ca045
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/reserved-regs-unavailable.ll
@@ -0,0 +1,14 @@
+; RUN: not --crash llc -mtriple=sparc64-linux-gnu -o - %s 2>&1 | FileCheck %s --check-prefixes=CHECK-RESERVED-L0
+
+;; Ensure explicit register references for non-reserved registers
+;; are caught properly.
+
+; CHECK-RESERVED-L0: LLVM ERROR: Invalid register name global variable
+define void @set_reg(i32 zeroext %x) {
+entry:
+ tail call void @llvm.write_register.i32(metadata !0, i32 %x)
+ ret void
+}
+
+declare void @llvm.write_register.i32(metadata, i32)
+!0 = !{!"l0"}
diff --git a/llvm/test/CodeGen/SPARC/reserved-regs.ll b/llvm/test/CodeGen/SPARC/reserved-regs.ll
index ec62905..7dea1f3 100644
--- a/llvm/test/CodeGen/SPARC/reserved-regs.ll
+++ b/llvm/test/CodeGen/SPARC/reserved-regs.ll
@@ -1,5 +1,14 @@
; RUN: llc -march=sparc -verify-machineinstrs < %s | FileCheck %s
+;; Test reserve-* options.
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-g1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-o1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-O1
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L1
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-I1
+
+;; Test multiple reserve-* options together.
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-g1 -mattr=+reserve-o1 -mattr=+reserve-l1 -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1,CHECK-RESERVED-O1,CHECK-RESERVED-L1,CHECK-RESERVED-I1
+
@g = common global [32 x i32] zeroinitializer, align 16
@h = common global [16 x i64] zeroinitializer, align 16
@@ -16,6 +25,10 @@
; CHECK-NOT: %o6
; CHECK-NOT: %i6
; CHECK-NOT: %i7
+; CHECK-RESERVED-G1-NOT: %g1
+; CHECK-RESERVED-O1-NOT: %o1
+; CHECK-RESERVED-L1-NOT: %l1
+; CHECK-RESERVED-I1-NOT: %i1
; CHECK: ret
define void @use_all_i32_regs() {
entry:
@@ -100,6 +113,10 @@ entry:
; CHECK-NOT: %o7
; CHECK-NOT: %i6
; CHECK-NOT: %i7
+; CHECK-RESERVED-G1-NOT: %g1
+; CHECK-RESERVED-O1-NOT: %o1
+; CHECK-RESERVED-L1-NOT: %l1
+; CHECK-RESERVED-I1-NOT: %i1
; CHECK: ret
define void @use_all_i64_regs() {
entry: