: CalleeSavedRegs<(add (sequence "X%u", 0, 15),
(sequence "X%u", 18, 28), FP, SP,
(sequence "Q%u", 0, 31))>;
+
+// Variants of the standard calling conventions for shadow call stack.
+// These all preserve x18 in addition to any other registers.
+def CSR_AArch64_NoRegs_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_NoRegs, X18)>;
+def CSR_AArch64_AllRegs_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_AllRegs, X18)>;
+def CSR_AArch64_CXX_TLS_Darwin_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_CXX_TLS_Darwin, X18)>;
+def CSR_AArch64_AAPCS_SwiftError_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_AAPCS_SwiftError, X18)>;
+def CSR_AArch64_RT_MostRegs_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_RT_MostRegs, X18)>;
+def CSR_AArch64_AAPCS_SCS
+ : CalleeSavedRegs<(add CSR_AArch64_AAPCS, X18)>;
static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, const TargetInstrInfo *TII, int CSStackSizeInc) {
+ // Ignore instructions that do not operate on SP, i.e. shadow call stack
+ // instructions.
+ while (MBBI->getOpcode() == AArch64::STRXpost ||
+ MBBI->getOpcode() == AArch64::LDRXpre) {
+ assert(MBBI->getOperand(0).getReg() != AArch64::SP);
+ ++MBBI;
+ }
+
unsigned NewOpc;
bool NewIsUnscaled = false;
switch (MBBI->getOpcode()) {
static void fixupCalleeSaveRestoreStackOffset(MachineInstr &MI,
unsigned LocalStackSize) {
unsigned Opc = MI.getOpcode();
+
+ // Ignore instructions that do not operate on SP, i.e. shadow call stack
+ // instructions.
+ if (Opc == AArch64::STRXpost || Opc == AArch64::LDRXpre) {
+ assert(MI.getOperand(0).getReg() != AArch64::SP);
+ return;
+ }
+
(void)Opc;
assert((Opc == AArch64::STPXi || Opc == AArch64::STPDi ||
Opc == AArch64::STRXui || Opc == AArch64::STRDui ||
// assumes the SP is at the same location as it was after the callee-save save
// code in the prologue.
if (AfterCSRPopSize) {
+ // Find an insertion point for the first ldp so that it goes before the
+ // shadow call stack epilog instruction. This ensures that the restore of
+ // lr from x18 is placed after the restore from sp.
+ auto FirstSPPopI = MBB.getFirstTerminator();
+ while (FirstSPPopI != Begin) {
+ auto Prev = std::prev(FirstSPPopI);
+ if (Prev->getOpcode() != AArch64::LDRXpre ||
+ Prev->getOperand(0).getReg() == AArch64::SP)
+ break;
+ FirstSPPopI = Prev;
+ }
+
// Sometimes (when we restore in the same order as we save), we can end up
// with code like this:
//
// a post-index ldp.
// If we managed to grab the first pop instruction, move it to the end.
if (LastPopI != Begin)
- MBB.splice(MBB.getFirstTerminator(), &MBB, LastPopI);
+ MBB.splice(FirstSPPopI, &MBB, LastPopI);
// We should end up with something like this now:
//
// ldp x24, x23, [sp, #16]
//
// ldp x26, x25, [sp], #64
//
- emitFrameOffset(MBB, MBB.getFirstTerminator(), DL, AArch64::SP, AArch64::SP,
+ emitFrameOffset(MBB, FirstSPPopI, DL, AArch64::SP, AArch64::SP,
AfterCSRPopSize, TII, MachineInstr::FrameDestroy);
}
}
static void computeCalleeSaveRegisterPairs(
MachineFunction &MF, const std::vector<CalleeSavedInfo> &CSI,
- const TargetRegisterInfo *TRI, SmallVectorImpl<RegPairInfo> &RegPairs) {
+ const TargetRegisterInfo *TRI, SmallVectorImpl<RegPairInfo> &RegPairs,
+ bool &NeedShadowCallStackProlog) {
if (CSI.empty())
return;
RPI.Reg2 = NextReg;
}
+ // If either of the registers to be saved is the lr register, it means that
+ // 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())
+ report_fatal_error("Must reserve x18 to use shadow call stack");
+ NeedShadowCallStackProlog = true;
+ }
+
// GPRs and FPRs are saved in pairs of 64-bit regs. We expect the CSI
// list to come in sorted by frame index so that we can issue the store
// pair instructions directly. Assert if we see anything otherwise.
DebugLoc DL;
SmallVector<RegPairInfo, 8> RegPairs;
- computeCalleeSaveRegisterPairs(MF, CSI, TRI, RegPairs);
+ bool NeedShadowCallStackProlog = false;
+ computeCalleeSaveRegisterPairs(MF, CSI, TRI, RegPairs,
+ NeedShadowCallStackProlog);
const MachineRegisterInfo &MRI = MF.getRegInfo();
+ if (NeedShadowCallStackProlog) {
+ // Shadow call stack prolog: str x30, [x18], #8
+ BuildMI(MBB, MI, DL, TII.get(AArch64::STRXpost))
+ .addReg(AArch64::X18, RegState::Define)
+ .addReg(AArch64::LR)
+ .addReg(AArch64::X18)
+ .addImm(8)
+ .setMIFlag(MachineInstr::FrameSetup);
+
+ // This instruction also makes x18 live-in to the entry block.
+ MBB.addLiveIn(AArch64::X18);
+ }
+
for (auto RPII = RegPairs.rbegin(), RPIE = RegPairs.rend(); RPII != RPIE;
++RPII) {
RegPairInfo RPI = *RPII;
if (MI != MBB.end())
DL = MI->getDebugLoc();
- computeCalleeSaveRegisterPairs(MF, CSI, TRI, RegPairs);
+ bool NeedShadowCallStackProlog = false;
+ computeCalleeSaveRegisterPairs(MF, CSI, TRI, RegPairs,
+ NeedShadowCallStackProlog);
auto EmitMI = [&](const RegPairInfo &RPI) {
unsigned Reg1 = RPI.Reg1;
else
for (const RegPairInfo &RPI : RegPairs)
EmitMI(RPI);
+
+ if (NeedShadowCallStackProlog) {
+ // Shadow call stack epilog: ldr x30, [x18, #-8]!
+ BuildMI(MBB, MI, DL, TII.get(AArch64::LDRXpre))
+ .addReg(AArch64::X18, RegState::Define)
+ .addReg(AArch64::LR, RegState::Define)
+ .addReg(AArch64::X18)
+ .addImm(-8)
+ .setMIFlag(MachineInstr::FrameDestroy);
+ }
+
return true;
}
const uint32_t *
AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const {
+ bool SCS = MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack);
if (CC == CallingConv::GHC)
// This is academic because all GHC calls are (supposed to be) tail calls
- return CSR_AArch64_NoRegs_RegMask;
+ return SCS ? CSR_AArch64_NoRegs_SCS_RegMask : CSR_AArch64_NoRegs_RegMask;
if (CC == CallingConv::AnyReg)
- return CSR_AArch64_AllRegs_RegMask;
+ return SCS ? CSR_AArch64_AllRegs_SCS_RegMask : CSR_AArch64_AllRegs_RegMask;
if (CC == CallingConv::CXX_FAST_TLS)
- return CSR_AArch64_CXX_TLS_Darwin_RegMask;
+ return SCS ? CSR_AArch64_CXX_TLS_Darwin_SCS_RegMask
+ : CSR_AArch64_CXX_TLS_Darwin_RegMask;
if (MF.getSubtarget<AArch64Subtarget>().getTargetLowering()
->supportSwiftError() &&
MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError))
- return CSR_AArch64_AAPCS_SwiftError_RegMask;
+ return SCS ? CSR_AArch64_AAPCS_SwiftError_SCS_RegMask
+ : CSR_AArch64_AAPCS_SwiftError_RegMask;
if (CC == CallingConv::PreserveMost)
- return CSR_AArch64_RT_MostRegs_RegMask;
+ return SCS ? CSR_AArch64_RT_MostRegs_SCS_RegMask
+ : CSR_AArch64_RT_MostRegs_RegMask;
else
- return CSR_AArch64_AAPCS_RegMask;
+ return SCS ? CSR_AArch64_AAPCS_SCS_RegMask : CSR_AArch64_AAPCS_RegMask;
}
const uint32_t *AArch64RegisterInfo::getTLSCallPreservedMask() const {
--- /dev/null
+; RUN: llc -verify-machineinstrs -o - %s -mtriple=aarch64-linux-gnu -mattr=+reserve-x18 | FileCheck %s
+
+define void @f1() shadowcallstack {
+ ; CHECK: f1:
+ ; CHECK-NOT: x18
+ ; CHECK: ret
+ ret void
+}
+
+declare void @foo()
+
+define void @f2() shadowcallstack {
+ ; CHECK: f2:
+ ; CHECK-NOT: x18
+ ; CHECK: b foo
+ tail call void @foo()
+ ret void
+}
+
+declare i32 @bar()
+
+define i32 @f3() shadowcallstack {
+ ; CHECK: f3:
+ ; CHECK: str x30, [x18], #8
+ ; CHECK: str x30, [sp, #-16]!
+ %res = call i32 @bar()
+ %res1 = add i32 %res, 1
+ ; CHECK: ldr x30, [sp], #16
+ ; CHECK: ldr x30, [x18, #-8]!
+ ; CHECK: ret
+ ret i32 %res
+}
+
+define i32 @f4() shadowcallstack {
+ ; CHECK: f4:
+ %res1 = call i32 @bar()
+ %res2 = call i32 @bar()
+ %res3 = call i32 @bar()
+ %res4 = call i32 @bar()
+ %res12 = add i32 %res1, %res2
+ %res34 = add i32 %res3, %res4
+ %res1234 = add i32 %res12, %res34
+ ; CHECK: ldp {{.*}}x30, [sp
+ ; CHECK: ldr x30, [x18, #-8]!
+ ; CHECK: ret
+ ret i32 %res1234
+}