| 1 | //==-- AArch64FrameLowering.h - TargetFrameLowering for AArch64 --*- C++ -*-==// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H |
| 14 | #define LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H |
| 15 | |
| 16 | #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" |
| 17 | #include "llvm/CodeGen/TargetFrameLowering.h" |
| 18 | #include "llvm/Support/TypeSize.h" |
| 19 | |
| 20 | namespace llvm { |
| 21 | |
| 22 | class TargetLowering; |
| 23 | class AArch64FunctionInfo; |
| 24 | class AArch64InstrInfo; |
| 25 | class AArch64PrologueEmitter; |
| 26 | class AArch64EpilogueEmitter; |
| 27 | |
| 28 | struct SVEStackSizes { |
| 29 | uint64_t ZPRStackSize{0}; |
| 30 | uint64_t PPRStackSize{0}; |
| 31 | }; |
| 32 | |
| 33 | class AArch64FrameLowering : public TargetFrameLowering { |
| 34 | public: |
| 35 | explicit AArch64FrameLowering() |
| 36 | : TargetFrameLowering(StackGrowsDown, Align(16), 0, Align(16), |
| 37 | true /*StackRealignable*/) {} |
| 38 | |
| 39 | void resetCFIToInitialState(MachineBasicBlock &MBB) const override; |
| 40 | |
| 41 | MachineBasicBlock::iterator |
| 42 | eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, |
| 43 | MachineBasicBlock::iterator I) const override; |
| 44 | |
| 45 | /// emitProlog/emitEpilog - These methods insert prolog and epilog code into |
| 46 | /// the function. |
| 47 | void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; |
| 48 | void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; |
| 49 | |
| 50 | /// Harden the entire function with pac-ret. |
| 51 | /// |
| 52 | /// If pac-ret+leaf is requested, we want to harden as much code as possible. |
| 53 | /// This function inserts pac-ret hardening at the points where prologue and |
| 54 | /// epilogue are traditionally inserted, ignoring possible shrink-wrapping |
| 55 | /// optimization. |
| 56 | void emitPacRetPlusLeafHardening(MachineFunction &MF) const; |
| 57 | |
| 58 | bool enableCFIFixup(const MachineFunction &MF) const override; |
| 59 | |
| 60 | bool enableFullCFIFixup(const MachineFunction &MF) const override; |
| 61 | |
| 62 | bool canUseAsPrologue(const MachineBasicBlock &MBB) const override; |
| 63 | |
| 64 | StackOffset getFrameIndexReference(const MachineFunction &MF, int FI, |
| 65 | Register &FrameReg) const override; |
| 66 | StackOffset getFrameIndexReferenceFromSP(const MachineFunction &MF, |
| 67 | int FI) const override; |
| 68 | StackOffset resolveFrameIndexReference(const MachineFunction &MF, int FI, |
| 69 | Register &FrameReg, bool PreferFP, |
| 70 | bool ForSimm) const; |
| 71 | StackOffset resolveFrameOffsetReference(const MachineFunction &MF, |
| 72 | int64_t ObjectOffset, bool isFixed, |
| 73 | TargetStackID::Value StackID, |
| 74 | Register &FrameReg, bool PreferFP, |
| 75 | bool ForSimm) const; |
| 76 | bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, |
| 77 | MachineBasicBlock::iterator MI, |
| 78 | ArrayRef<CalleeSavedInfo> CSI, |
| 79 | const TargetRegisterInfo *TRI) const override; |
| 80 | |
| 81 | bool |
| 82 | restoreCalleeSavedRegisters(MachineBasicBlock &MBB, |
| 83 | MachineBasicBlock::iterator MI, |
| 84 | MutableArrayRef<CalleeSavedInfo> CSI, |
| 85 | const TargetRegisterInfo *TRI) const override; |
| 86 | |
| 87 | /// Can this function use the red zone for local allocations. |
| 88 | bool canUseRedZone(const MachineFunction &MF) const; |
| 89 | |
| 90 | bool hasReservedCallFrame(const MachineFunction &MF) const override; |
| 91 | |
| 92 | bool |
| 93 | assignCalleeSavedSpillSlots(MachineFunction &MF, |
| 94 | const TargetRegisterInfo *TRI, |
| 95 | std::vector<CalleeSavedInfo> &CSI) const override; |
| 96 | |
| 97 | void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, |
| 98 | RegScavenger *RS) const override; |
| 99 | |
| 100 | /// Returns true if the target will correctly handle shrink wrapping. |
| 101 | bool enableShrinkWrapping(const MachineFunction &MF) const override { |
| 102 | return true; |
| 103 | } |
| 104 | |
| 105 | bool enableStackSlotScavenging(const MachineFunction &MF) const override; |
| 106 | TargetStackID::Value getStackIDForScalableVectors() const override; |
| 107 | |
| 108 | void processFunctionBeforeFrameFinalized(MachineFunction &MF, |
| 109 | RegScavenger *RS) const override; |
| 110 | |
| 111 | void |
| 112 | processFunctionBeforeFrameIndicesReplaced(MachineFunction &MF, |
| 113 | RegScavenger *RS) const override; |
| 114 | |
| 115 | unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const override; |
| 116 | |
| 117 | unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; |
| 118 | |
| 119 | StackOffset |
| 120 | getFrameIndexReferencePreferSP(const MachineFunction &MF, int FI, |
| 121 | Register &FrameReg, |
| 122 | bool IgnoreSPUpdates) const override; |
| 123 | StackOffset getNonLocalFrameIndexReference(const MachineFunction &MF, |
| 124 | int FI) const override; |
| 125 | int getSEHFrameIndexOffset(const MachineFunction &MF, int FI) const; |
| 126 | |
| 127 | bool isSupportedStackID(TargetStackID::Value ID) const override { |
| 128 | switch (ID) { |
| 129 | default: |
| 130 | return false; |
| 131 | case TargetStackID::Default: |
| 132 | case TargetStackID::ScalableVector: |
| 133 | case TargetStackID::ScalablePredicateVector: |
| 134 | case TargetStackID::NoAlloc: |
| 135 | return true; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | bool isStackIdSafeForLocalArea(unsigned StackId) const override { |
| 140 | // We don't support putting SVE objects into the pre-allocated local |
| 141 | // frame block at the moment. |
| 142 | return (StackId != TargetStackID::ScalableVector && |
| 143 | StackId != TargetStackID::ScalablePredicateVector); |
| 144 | } |
| 145 | |
| 146 | void |
| 147 | orderFrameObjects(const MachineFunction &MF, |
| 148 | SmallVectorImpl<int> &ObjectsToAllocate) const override; |
| 149 | |
| 150 | bool isFPReserved(const MachineFunction &MF) const; |
| 151 | |
| 152 | bool needsWinCFI(const MachineFunction &MF) const; |
| 153 | |
| 154 | bool requiresSaveVG(const MachineFunction &MF) const; |
| 155 | |
| 156 | /// Returns the size of the entire ZPR stackframe (calleesaves + spills). |
| 157 | StackOffset getZPRStackSize(const MachineFunction &MF) const; |
| 158 | |
| 159 | /// Returns the size of the entire PPR stackframe (calleesaves + spills + |
| 160 | /// hazard padding). |
| 161 | StackOffset getPPRStackSize(const MachineFunction &MF) const; |
| 162 | |
| 163 | /// Returns the size of the entire SVE stackframe (PPRs + ZPRs). |
| 164 | StackOffset getSVEStackSize(const MachineFunction &MF) const { |
| 165 | return getZPRStackSize(MF) + getPPRStackSize(MF); |
| 166 | } |
| 167 | |
| 168 | friend class AArch64PrologueEpilogueCommon; |
| 169 | friend class AArch64PrologueEmitter; |
| 170 | friend class AArch64EpilogueEmitter; |
| 171 | |
| 172 | // Windows unwind can't represent the required stack adjustments if we have |
| 173 | // both SVE callee-saves and dynamic stack allocations, and the frame |
| 174 | // pointer is before the SVE spills. The allocation of the frame pointer |
| 175 | // must be the last instruction in the prologue so the unwinder can restore |
| 176 | // the stack pointer correctly. (And there isn't any unwind opcode for |
| 177 | // `addvl sp, x29, -17`.) |
| 178 | // |
| 179 | // Because of this, we do spills in the opposite order on Windows: first SVE, |
| 180 | // then GPRs. The main side-effect of this is that it makes accessing |
| 181 | // parameters passed on the stack more expensive. |
| 182 | // |
| 183 | // We could consider rearranging the spills for simpler cases. |
| 184 | bool hasSVECalleeSavesAboveFrameRecord(const MachineFunction &MF) const; |
| 185 | |
| 186 | protected: |
| 187 | bool hasFPImpl(const MachineFunction &MF) const override; |
| 188 | |
| 189 | private: |
| 190 | /// Returns true if a homogeneous prolog or epilog code can be emitted |
| 191 | /// for the size optimization. If so, HOM_Prolog/HOM_Epilog pseudo |
| 192 | /// instructions are emitted in place. When Exit block is given, this check is |
| 193 | /// for epilog. |
| 194 | bool homogeneousPrologEpilog(MachineFunction &MF, |
| 195 | MachineBasicBlock *Exit = nullptr) const; |
| 196 | |
| 197 | /// Returns true if CSRs should be paired. |
| 198 | bool producePairRegisters(MachineFunction &MF) const; |
| 199 | |
| 200 | /// Make a determination whether a Hazard slot is used and create it if |
| 201 | /// needed. |
| 202 | void determineStackHazardSlot(MachineFunction &MF, |
| 203 | BitVector &SavedRegs) const; |
| 204 | |
| 205 | /// Emit target zero call-used regs. |
| 206 | void emitZeroCallUsedRegs(BitVector RegsToZero, |
| 207 | MachineBasicBlock &MBB) const override; |
| 208 | |
| 209 | /// Replace a StackProbe stub (if any) with the actual probe code inline |
| 210 | void inlineStackProbe(MachineFunction &MF, |
| 211 | MachineBasicBlock &PrologueMBB) const override; |
| 212 | |
| 213 | void inlineStackProbeFixed(MachineBasicBlock::iterator MBBI, |
| 214 | Register ScratchReg, int64_t FrameSize, |
| 215 | StackOffset CFAOffset) const; |
| 216 | |
| 217 | MachineBasicBlock::iterator |
| 218 | inlineStackProbeLoopExactMultiple(MachineBasicBlock::iterator MBBI, |
| 219 | int64_t NegProbeSize, |
| 220 | Register TargetReg) const; |
| 221 | |
| 222 | void (const MachineFunction &MF, |
| 223 | MachineOptimizationRemarkEmitter *ORE) const override; |
| 224 | |
| 225 | bool windowsRequiresStackProbe(const MachineFunction &MF, |
| 226 | uint64_t StackSizeInBytes) const; |
| 227 | |
| 228 | bool shouldSignReturnAddressEverywhere(const MachineFunction &MF) const; |
| 229 | |
| 230 | StackOffset getFPOffset(const MachineFunction &MF, |
| 231 | int64_t ObjectOffset) const; |
| 232 | |
| 233 | StackOffset getStackOffset(const MachineFunction &MF, |
| 234 | int64_t ObjectOffset) const; |
| 235 | |
| 236 | // Given a load or a store instruction, generate an appropriate unwinding SEH |
| 237 | // code on Windows. |
| 238 | MachineBasicBlock::iterator insertSEH(MachineBasicBlock::iterator MBBI, |
| 239 | const AArch64InstrInfo &TII, |
| 240 | MachineInstr::MIFlag Flag) const; |
| 241 | |
| 242 | /// Returns how much of the incoming argument stack area (in bytes) we should |
| 243 | /// clean up in an epilogue. For the C calling convention this will be 0, for |
| 244 | /// guaranteed tail call conventions it can be positive (a normal return or a |
| 245 | /// tail call to a function that uses less stack space for arguments) or |
| 246 | /// negative (for a tail call to a function that needs more stack space than |
| 247 | /// us for arguments). |
| 248 | int64_t getArgumentStackToRestore(MachineFunction &MF, |
| 249 | MachineBasicBlock &MBB) const; |
| 250 | |
| 251 | // Find a scratch register that we can use at the start of the prologue to |
| 252 | // re-align the stack pointer. We avoid using callee-save registers since |
| 253 | // they may appear to be free when this is called from canUseAsPrologue |
| 254 | // (during shrink wrapping), but then no longer be free when this is called |
| 255 | // from emitPrologue. |
| 256 | // |
| 257 | // FIXME: This is a bit conservative, since in the above case we could use one |
| 258 | // of the callee-save registers as a scratch temp to re-align the stack |
| 259 | // pointer, but we would then have to make sure that we were in fact saving at |
| 260 | // least one callee-save register in the prologue, which is additional |
| 261 | // complexity that doesn't seem worth the benefit. |
| 262 | Register findScratchNonCalleeSaveRegister(MachineBasicBlock *MBB, |
| 263 | bool HasCall = false) const; |
| 264 | |
| 265 | /// Returns the size of the fixed object area (allocated next to sp on entry) |
| 266 | /// On Win64 this may include a var args area and an UnwindHelp object for EH. |
| 267 | unsigned getFixedObjectSize(const MachineFunction &MF, |
| 268 | const AArch64FunctionInfo *AFI, bool IsWin64, |
| 269 | bool IsFunclet) const; |
| 270 | }; |
| 271 | |
| 272 | } // End llvm namespace |
| 273 | |
| 274 | #endif |
| 275 | |