[AArch64] Support reserving x1-7 registers.

Summary:
Reserving registers x1-7 is used to support CONFIG_ARM64_LSE_ATOMICS in Linux kernel. This change adds support for reserving registers x1 through x7.

Reviewers: javed.absar, phosek, srhines, nickdesaulniers, efriedma

Reviewed By: nickdesaulniers, efriedma

Subscribers: niravd, jfb, manojgupta, nickdesaulniers, jyknight, efriedma, kristof.beyls, llvm-commits

Differential Revision: https://reviews.llvm.org/D48580

llvm-svn: 341706
This commit is contained in:
Nick Desaulniers 2018-09-07 20:58:57 +00:00
parent 5cbce81c91
commit 287a3be379
11 changed files with 155 additions and 52 deletions

View File

@ -99,12 +99,9 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align",
"Disallow all unaligned memory "
"access">;
def FeatureReserveX18 : SubtargetFeature<"reserve-x18", "ReserveX18", "true",
"Reserve X18, making it unavailable "
"as a GPR">;
def FeatureReserveX20 : SubtargetFeature<"reserve-x20", "ReserveX20", "true",
"Reserve X20, making it unavailable "
foreach i = {1-7,18,20} in
def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true",
"Reserve X"#i#", making it unavailable "
"as a GPR">;
def FeatureUseAA : SubtargetFeature<"use-aa", "UseAA", "true",

View File

@ -377,9 +377,12 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
MIB.add(Callee);
// Tell the call which registers are clobbered.
auto TRI = MF.getSubtarget().getRegisterInfo();
auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo();
MIB.addRegMask(TRI->getCallPreservedMask(MF, F.getCallingConv()));
if (TRI->isAnyArgRegReserved(MF))
TRI->emitReservedArgRegCallError(MF);
// Do the actual argument marshalling.
SmallVector<unsigned, 8> PhysRegs;
OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFnFixed,

View File

@ -3208,6 +3208,10 @@ bool AArch64FastISel::fastLowerCall(CallLoweringInfo &CLI) {
if (!processCallArgs(CLI, OutVTs, NumBytes))
return false;
const AArch64RegisterInfo *RegInfo = Subtarget->getRegisterInfo();
if (RegInfo->isAnyArgRegReserved(*MF))
RegInfo->emitReservedArgRegCallError(*MF);
// Issue the call.
MachineInstrBuilder MIB;
if (Subtarget->useSmallAddressing()) {

View File

@ -1253,7 +1253,7 @@ static void computeCalleeSaveRegisterPairs(
// we also need to save lr in the shadow call stack.
if ((RPI.Reg1 == AArch64::LR || RPI.Reg2 == AArch64::LR) &&
MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) {
if (!MF.getSubtarget<AArch64Subtarget>().isX18Reserved())
if (!MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(18))
report_fatal_error("Must reserve x18 to use shadow call stack");
NeedShadowCallStackProlog = true;
}

View File

@ -3727,6 +3727,9 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
} else
Mask = TRI->getCallPreservedMask(MF, CallConv);
if (TRI->isAnyArgRegReserved(MF))
TRI->emitReservedArgRegCallError(MF);
assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask));
@ -5037,15 +5040,43 @@ unsigned AArch64TargetLowering::getRegisterByName(const char* RegName, EVT VT,
SelectionDAG &DAG) const {
unsigned Reg = StringSwitch<unsigned>(RegName)
.Case("sp", AArch64::SP)
.Case("x1", AArch64::X1)
.Case("w1", AArch64::W1)
.Case("x2", AArch64::X2)
.Case("w2", AArch64::W2)
.Case("x3", AArch64::X3)
.Case("w3", AArch64::W3)
.Case("x4", AArch64::X4)
.Case("w4", AArch64::W4)
.Case("x5", AArch64::X5)
.Case("w5", AArch64::W5)
.Case("x6", AArch64::X6)
.Case("w6", AArch64::W6)
.Case("x7", AArch64::X7)
.Case("w7", AArch64::W7)
.Case("x18", AArch64::X18)
.Case("w18", AArch64::W18)
.Case("x20", AArch64::X20)
.Case("w20", AArch64::W20)
.Default(0);
if (((Reg == AArch64::X18 || Reg == AArch64::W18) &&
!Subtarget->isX18Reserved()) ||
if (((Reg == AArch64::X1 || Reg == AArch64::W1) &&
!Subtarget->isXRegisterReserved(1)) ||
((Reg == AArch64::X2 || Reg == AArch64::W2) &&
!Subtarget->isXRegisterReserved(2)) ||
((Reg == AArch64::X3 || Reg == AArch64::W3) &&
!Subtarget->isXRegisterReserved(3)) ||
((Reg == AArch64::X4 || Reg == AArch64::W4) &&
!Subtarget->isXRegisterReserved(4)) ||
((Reg == AArch64::X5 || Reg == AArch64::W5) &&
!Subtarget->isXRegisterReserved(5)) ||
((Reg == AArch64::X6 || Reg == AArch64::W6) &&
!Subtarget->isXRegisterReserved(6)) ||
((Reg == AArch64::X7 || Reg == AArch64::W7) &&
!Subtarget->isXRegisterReserved(7)) ||
((Reg == AArch64::X18 || Reg == AArch64::W18) &&
!Subtarget->isXRegisterReserved(18)) ||
((Reg == AArch64::X20 || Reg == AArch64::W20) &&
!Subtarget->isX20Reserved()))
!Subtarget->isXRegisterReserved(20)))
Reg = 0;
if (Reg)
return Reg;

View File

@ -25,6 +25,7 @@
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/Target/TargetOptions.h"
@ -147,11 +148,10 @@ AArch64RegisterInfo::getReservedRegs(const MachineFunction &MF) const {
if (TFI->hasFP(MF) || TT.isOSDarwin())
markSuperRegs(Reserved, AArch64::W29);
if (MF.getSubtarget<AArch64Subtarget>().isX18Reserved())
markSuperRegs(Reserved, AArch64::W18); // Platform register
if (MF.getSubtarget<AArch64Subtarget>().isX20Reserved())
markSuperRegs(Reserved, AArch64::W20); // Platform register
for (size_t i = 0; i < AArch64::GPR32commonRegClass.getNumRegs(); ++i) {
if (MF.getSubtarget<AArch64Subtarget>().isXRegisterReserved(i))
markSuperRegs(Reserved, AArch64::GPR32commonRegClass.getRegister(i));
}
if (hasBasePointer(MF))
markSuperRegs(Reserved, AArch64::W19);
@ -162,31 +162,23 @@ AArch64RegisterInfo::getReservedRegs(const MachineFunction &MF) const {
bool AArch64RegisterInfo::isReservedReg(const MachineFunction &MF,
unsigned Reg) const {
const AArch64FrameLowering *TFI = getFrameLowering(MF);
return getReservedRegs(MF)[Reg];
}
switch (Reg) {
default:
break;
case AArch64::SP:
case AArch64::XZR:
case AArch64::WSP:
case AArch64::WZR:
return true;
case AArch64::X18:
case AArch64::W18:
return MF.getSubtarget<AArch64Subtarget>().isX18Reserved();
case AArch64::X19:
case AArch64::W19:
return hasBasePointer(MF);
case AArch64::X20:
case AArch64::W20:
return MF.getSubtarget<AArch64Subtarget>().isX20Reserved();
case AArch64::FP:
case AArch64::W29:
return TFI->hasFP(MF) || TT.isOSDarwin();
}
bool AArch64RegisterInfo::isAnyArgRegReserved(const MachineFunction &MF) const {
// FIXME: Get the list of argument registers from TableGen.
static const MCPhysReg GPRArgRegs[] = { AArch64::X0, AArch64::X1, AArch64::X2,
AArch64::X3, AArch64::X4, AArch64::X5,
AArch64::X6, AArch64::X7 };
return std::any_of(std::begin(GPRArgRegs), std::end(GPRArgRegs),
[this, &MF](MCPhysReg r){return isReservedReg(MF, r);});
}
return false;
void AArch64RegisterInfo::emitReservedArgRegCallError(
const MachineFunction &MF) const {
const Function &F = MF.getFunction();
F.getContext().diagnose(DiagnosticInfoUnsupported{F, "AArch64 doesn't support"
" function calls if any of the argument registers is reserved."});
}
bool AArch64RegisterInfo::isAsmClobberable(const MachineFunction &MF,
@ -454,10 +446,7 @@ unsigned AArch64RegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC,
case AArch64::GPR64commonRegClassID:
return 32 - 1 // XZR/SP
- (TFI->hasFP(MF) || TT.isOSDarwin()) // FP
- MF.getSubtarget<AArch64Subtarget>()
.isX18Reserved() // X18 reserved as platform register
- MF.getSubtarget<AArch64Subtarget>()
.isX20Reserved() // X20 reserved as platform register
- MF.getSubtarget<AArch64Subtarget>().getNumXRegisterReserved()
- hasBasePointer(MF); // X19
case AArch64::FPR8RegClassID:
case AArch64::FPR16RegClassID:

View File

@ -31,6 +31,8 @@ public:
AArch64RegisterInfo(const Triple &TT);
bool isReservedReg(const MachineFunction &MF, unsigned Reg) const;
bool isAnyArgRegReserved(const MachineFunction &MF) const;
void emitReservedArgRegCallError(const MachineFunction &MF) const;
/// Code Generation virtual methods...
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;

View File

@ -152,10 +152,14 @@ AArch64Subtarget::AArch64Subtarget(const Triple &TT, const std::string &CPU,
const std::string &FS,
const TargetMachine &TM, bool LittleEndian)
: AArch64GenSubtargetInfo(TT, CPU, FS),
ReserveX18(AArch64::isX18ReservedByDefault(TT)), IsLittle(LittleEndian),
ReserveXRegister(AArch64::GPR64commonRegClass.getNumRegs()),
IsLittle(LittleEndian),
TargetTriple(TT), FrameLowering(),
InstrInfo(initializeSubtargetDependencies(FS, CPU)), TSInfo(),
TLInfo(TM, *this) {
if (AArch64::isX18ReservedByDefault(TT))
ReserveXRegister.set(18);
CallLoweringInfo.reset(new AArch64CallLowering(*getTargetLowering()));
Legalizer.reset(new AArch64LegalizerInfo(*this));

View File

@ -138,11 +138,8 @@ protected:
unsigned MaxJumpTableSize = 0;
unsigned WideningBaseCost = 0;
// ReserveX18 - X18 is not available as a general purpose register.
bool ReserveX18;
// ReserveX20 - X20 is not available as a general purpose register.
bool ReserveX20 = false;
// ReserveXRegister[i] - X#i is not available as a general purpose register.
BitVector ReserveXRegister;
bool IsLittle;
@ -229,8 +226,8 @@ public:
return MinVectorRegisterBitWidth;
}
bool isX18Reserved() const { return ReserveX18; }
bool isX20Reserved() const { return ReserveX20; }
bool isXRegisterReserved(size_t i) const { return ReserveXRegister[i]; }
unsigned getNumXRegisterReserved() const { return ReserveXRegister.count(); }
bool hasFPARMv8() const { return HasFPARMv8; }
bool hasNEON() const { return HasNEON; }
bool hasCrypto() const { return HasCrypto; }

View File

@ -7,6 +7,49 @@
; RUN: llc -mtriple=aarch64-fuchsia -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE --check-prefix=CHECK-RESERVE-X18
; RUN: llc -mtriple=aarch64-windows -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE --check-prefix=CHECK-RESERVE-X18
; Test reserve-x# options individually.
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X1
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x2 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X2
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x3 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X3
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x4 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X4
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x5 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X5
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x6 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X6
; RUN: llc -mtriple=arm64-linux-gnu -mattr=+reserve-x7 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVE,CHECK-RESERVE-X7
; Test multiple of reserve-x# options together.
; RUN: llc -mtriple=arm64-linux-gnu \
; RUN: -mattr=+reserve-x1 \
; RUN: -mattr=+reserve-x2 \
; RUN: -mattr=+reserve-x18 \
; RUN: -o - %s | FileCheck %s \
; RUN: --check-prefix=CHECK-RESERVE \
; RUN: --check-prefix=CHECK-RESERVE-X1 \
; RUN: --check-prefix=CHECK-RESERVE-X2 \
; RUN: --check-prefix=CHECK-RESERVE-X18
; Test all reserve-x# options together.
; RUN: llc -mtriple=arm64-linux-gnu \
; RUN: -mattr=+reserve-x1 \
; RUN: -mattr=+reserve-x2 \
; RUN: -mattr=+reserve-x3 \
; RUN: -mattr=+reserve-x4 \
; RUN: -mattr=+reserve-x5 \
; RUN: -mattr=+reserve-x6 \
; RUN: -mattr=+reserve-x7 \
; RUN: -mattr=+reserve-x18 \
; RUN: -mattr=+reserve-x20 \
; RUN: -o - %s | FileCheck %s \
; RUN: --check-prefix=CHECK-RESERVE \
; RUN: --check-prefix=CHECK-RESERVE-X1 \
; RUN: --check-prefix=CHECK-RESERVE-X2 \
; RUN: --check-prefix=CHECK-RESERVE-X3 \
; RUN: --check-prefix=CHECK-RESERVE-X4 \
; RUN: --check-prefix=CHECK-RESERVE-X5 \
; RUN: --check-prefix=CHECK-RESERVE-X6 \
; RUN: --check-prefix=CHECK-RESERVE-X7 \
; RUN: --check-prefix=CHECK-RESERVE-X18 \
; RUN: --check-prefix=CHECK-RESERVE-X20
; x18 is reserved as a platform register on Darwin but not on other
; systems. Create loads of register pressure and make sure this is respected.
@ -23,10 +66,24 @@ define void @keep_live() {
; CHECK: str x18
; CHECK-RESERVE-NOT: ldr fp
; CHECK-RESERVE-X1-NOT: ldr x1,
; CHECK-RESERVE-X2-NOT: ldr x2,
; CHECK-RESERVE-X3-NOT: ldr x3,
; CHECK-RESERVE-X4-NOT: ldr x4,
; CHECK-RESERVE-X5-NOT: ldr x5,
; CHECK-RESERVE-X6-NOT: ldr x6,
; CHECK-RESERVE-X7-NOT: ldr x7,
; CHECK-RESERVE-X18-NOT: ldr x18
; CHECK-RESERVE-X20-NOT: ldr x20
; CHECK-RESERVE: Spill
; CHECK-RESERVE-NOT: ldr fp
; CHECK-RESERVE-X1-NOT: ldr x1,
; CHECK-RESERVE-X2-NOT: ldr x2,
; CHECK-RESERVE-X3-NOT: ldr x3,
; CHECK-RESERVE-X4-NOT: ldr x4,
; CHECK-RESERVE-X5-NOT: ldr x5,
; CHECK-RESERVE-X6-NOT: ldr x6,
; CHECK-RESERVE-X7-NOT: ldr x7,
; CHECK-RESERVE-X18-NOT: ldr x18
; CHECK-RESERVE-X20-NOT: ldr x20
; CHECK-RESERVE: ret

View File

@ -0,0 +1,19 @@
; RUN: not llc < %s -mtriple=arm64-linux-gnu -mattr=+reserve-x1 2>&1 | FileCheck %s
; RUN: not llc < %s -mtriple=arm64-linux-gnu -mattr=+reserve-x1 -fast-isel 2>&1 | FileCheck %s
; RUN: not llc < %s -mtriple=arm64-linux-gnu -mattr=+reserve-x1 -global-isel 2>&1 | FileCheck %s
; CHECK: error:
; CHECK-SAME: AArch64 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: error:
; CHECK-SAME: AArch64 doesn't support function calls if any of the argument registers is reserved.
define void @call_memcpy(i8* %out, i8* %in) {
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %out, i8* %in, i64 800, i1 false)
ret void
}
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)