diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1185,7 +1185,26 @@ // For funclets the FP belongs to the containing function. if (!IsFunclet && HasFP) { // Only set up FP if we actually need to. - int64_t FPOffset = isTargetDarwin(MF) ? (AFI->getCalleeSavedStackSize() - 16) : 0; + int64_t FPOffset; + + // The frame pointer needs to point to the location of the frame record + // (x28 and x29) within the callee saved register space. + if (isTargetDarwin(MF)) { + // On Darwin, these are located at the top of the CSR space. + FPOffset = (AFI->getCalleeSavedStackSize() - 16); + } else { + // On other systems, these are located in the middle of the CSR space, + // after the other GPRs and before the FPRs. + assert(MFI.isCalleeSavedInfoValid() && "CalleeSavedInfo not calculated"); + if (MFI.getCalleeSavedInfo().empty()) { + FPOffset = 0; + } else { + FPOffset = AFI->getCalleeSavedStackSize(MFI, [](unsigned Reg) { + return AArch64::FPR64RegClass.contains(Reg) || + AArch64::FPR128RegClass.contains(Reg); + }); + } + } if (CombineSPBump) FPOffset += AFI->getLocalStackSize(); @@ -1843,8 +1862,16 @@ unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet=*/false); - unsigned FPAdjust = isTargetDarwin(MF) - ? 16 : AFI->getCalleeSavedStackSize(MF.getFrameInfo()); + + // Compensate for the position of the frame record within the callee-saved + // register space. On Darwin, this is a fixed offset. On other systems, + // this is determined by the number of callee-saved GPRs, excluding FPRs. + unsigned FPAdjust = + isTargetDarwin(MF) + ? 16 + : AFI->getCalleeSavedStackSize(MF.getFrameInfo(), [](unsigned Reg) { + return AArch64::GPR64RegClass.contains(Reg); + }); return {ObjectOffset + FixedObject + FPAdjust, MVT::i8}; } diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h --- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -194,9 +194,15 @@ // When CalleeSavedStackSize has not been set (for example when // some MachineIR pass is run in isolation), then recalculate // the CalleeSavedStackSize directly from the CalleeSavedInfo. + // RegisterFilter is a predicate to calculate the stack size for + // subsets of the callee-saved registers. It should return true + // for registers that should be included in the size calculation, + // and false otherwise. // Note: This information can only be recalculated after PEI // has assigned offsets to the callee save objects. - unsigned getCalleeSavedStackSize(const MachineFrameInfo &MFI) const { + unsigned getCalleeSavedStackSize( + const MachineFrameInfo &MFI, + llvm::function_ref RegisterFilter = nullptr) const { bool ValidateCalleeSavedStackSize = false; #ifndef NDEBUG @@ -206,14 +212,24 @@ ValidateCalleeSavedStackSize = HasCalleeSavedStackSize; #endif - if (!HasCalleeSavedStackSize || ValidateCalleeSavedStackSize) { + if (RegisterFilter || !HasCalleeSavedStackSize || + ValidateCalleeSavedStackSize) { assert(MFI.isCalleeSavedInfoValid() && "CalleeSavedInfo not calculated"); if (MFI.getCalleeSavedInfo().empty()) return 0; int64_t MinOffset = std::numeric_limits::max(); int64_t MaxOffset = std::numeric_limits::min(); + + bool AnyRegistersCounted = false; for (const auto &Info : MFI.getCalleeSavedInfo()) { + if (RegisterFilter) { + unsigned Reg = Info.getReg(); + if (!RegisterFilter(Reg)) + continue; + } + + AnyRegistersCounted = true; int FrameIdx = Info.getFrameIdx(); if (MFI.getStackID(FrameIdx) != TargetStackID::Default) continue; @@ -221,10 +237,15 @@ int64_t ObjSize = MFI.getObjectSize(FrameIdx); MinOffset = std::min(Offset, MinOffset); MaxOffset = std::max(Offset + ObjSize, MaxOffset); + AnyRegistersCounted = true; } + if (!AnyRegistersCounted) + return 0; + unsigned Size = alignTo(MaxOffset - MinOffset, 16); - assert((!HasCalleeSavedStackSize || getCalleeSavedStackSize() == Size) && + assert((RegisterFilter || !HasCalleeSavedStackSize || + getCalleeSavedStackSize() == Size) && "Invalid size calculated for callee saves"); return Size; } diff --git a/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll b/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll @@ -0,0 +1,22 @@ +; RUN: llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -disable-post-ra --frame-pointer=all < %s | FileCheck %s + +; The purpose of this test is to verify that frame pointer (x29) +; is correctly setup in the presence of callee-saved floating +; point registers. The frame pointer should point to the frame +; record, which is located 16 bytes above the end of the CSR +; space when a single FP CSR is in use. +define void @test1(i32) #26 { +entry: + call void asm sideeffect "nop", "~{d8}"() #26 + ret void +} +; CHECK-LABEL: test1: +; CHECK: str d8, [sp, #-32]! +; CHECK-NEXT: stp x29, x30, [sp, #16] +; CHECK-NEXT: add x29, sp, #16 +; CHECK: nop +; CHECK: ldp x29, x30, [sp, #16] +; CHECK-NEXT: ldr d8, [sp], #32 +; CHECK-NEXT: ret + +attributes #26 = { nounwind }