| //===-- M68kFrameLowering.cpp - M68k Frame Information ----------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// This file contains the M68k implementation of TargetFrameLowering class. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "M68kFrameLowering.h" | |
| #include "M68kInstrBuilder.h" | |
| #include "M68kInstrInfo.h" | |
| #include "M68kMachineFunction.h" | |
| #include "M68kSubtarget.h" | |
| #include "llvm/ADT/SmallSet.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/CodeGen/MachineModuleInfo.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/Support/Alignment.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| #include "llvm/Target/TargetOptions.h" | |
| using namespace llvm; | |
| M68kFrameLowering::M68kFrameLowering(const M68kSubtarget &STI, Align Alignment) | |
| : TargetFrameLowering(StackGrowsDown, Alignment, -4), STI(STI), | |
| TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) { | |
| SlotSize = STI.getSlotSize(); | |
| StackPtr = TRI->getStackRegister(); | |
| } | |
| bool M68kFrameLowering::hasFP(const MachineFunction &MF) const { | |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| const TargetRegisterInfo *TRI = STI.getRegisterInfo(); | |
| return MF.getTarget().Options.DisableFramePointerElim(MF) || | |
| MFI.hasVarSizedObjects() || MFI.isFrameAddressTaken() || | |
| TRI->hasStackRealignment(MF); | |
| } | |
| // FIXME Make sure no other factors prevent us from reserving call frame | |
| bool M68kFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { | |
| return !MF.getFrameInfo().hasVarSizedObjects() && | |
| !MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences(); | |
| } | |
| bool M68kFrameLowering::canSimplifyCallFramePseudos( | |
| const MachineFunction &MF) const { | |
| return hasReservedCallFrame(MF) || | |
| (hasFP(MF) && !TRI->hasStackRealignment(MF)) || | |
| TRI->hasBasePointer(MF); | |
| } | |
| bool M68kFrameLowering::needsFrameIndexResolution( | |
| const MachineFunction &MF) const { | |
| return MF.getFrameInfo().hasStackObjects() || | |
| MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences(); | |
| } | |
| // NOTE: this only has a subset of the full frame index logic. In | |
| // particular, the FI < 0 and AfterFPPop logic is handled in | |
| // M68kRegisterInfo::eliminateFrameIndex, but not here. Possibly | |
| // (probably?) it should be moved into here. | |
| StackOffset | |
| M68kFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, | |
| Register &FrameReg) const { | |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| // We can't calculate offset from frame pointer if the stack is realigned, | |
| // so enforce usage of stack/base pointer. The base pointer is used when we | |
| // have dynamic allocas in addition to dynamic realignment. | |
| if (TRI->hasBasePointer(MF)) | |
| FrameReg = TRI->getBaseRegister(); | |
| else if (TRI->hasStackRealignment(MF)) | |
| FrameReg = TRI->getStackRegister(); | |
| else | |
| FrameReg = TRI->getFrameRegister(MF); | |
| // Offset will hold the offset from the stack pointer at function entry to the | |
| // object. | |
| // We need to factor in additional offsets applied during the prologue to the | |
| // frame, base, and stack pointer depending on which is used. | |
| int Offset = MFI.getObjectOffset(FI) - getOffsetOfLocalArea(); | |
| const M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>(); | |
| uint64_t StackSize = MFI.getStackSize(); | |
| bool HasFP = hasFP(MF); | |
| // TODO: Support tail calls | |
| if (TRI->hasBasePointer(MF)) { | |
| assert(HasFP && "VLAs and dynamic stack realign, but no FP?!"); | |
| if (FI < 0) { | |
| // Skip the saved FP. | |
| return StackOffset::getFixed(Offset + SlotSize); | |
| } | |
| assert((-(Offset + StackSize)) % MFI.getObjectAlign(FI).value() == 0); | |
| return StackOffset::getFixed(Offset + StackSize); | |
| } | |
| if (TRI->hasStackRealignment(MF)) { | |
| if (FI < 0) { | |
| // Skip the saved FP. | |
| return StackOffset::getFixed(Offset + SlotSize); | |
| } | |
| assert((-(Offset + StackSize)) % MFI.getObjectAlign(FI).value() == 0); | |
| return StackOffset::getFixed(Offset + StackSize); | |
| } | |
| if (!HasFP) | |
| return StackOffset::getFixed(Offset + StackSize); | |
| // Skip the saved FP. | |
| Offset += SlotSize; | |
| // Skip the RETADDR move area | |
| int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta(); | |
| if (TailCallReturnAddrDelta < 0) | |
| Offset -= TailCallReturnAddrDelta; | |
| return StackOffset::getFixed(Offset); | |
| } | |
| /// Return a caller-saved register that isn't live | |
| /// when it reaches the "return" instruction. We can then pop a stack object | |
| /// to this register without worry about clobbering it. | |
| static unsigned findDeadCallerSavedReg(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator &MBBI, | |
| const M68kRegisterInfo *TRI) { | |
| const MachineFunction *MF = MBB.getParent(); | |
| if (MF->callsEHReturn()) | |
| return 0; | |
| const TargetRegisterClass &AvailableRegs = *TRI->getRegsForTailCall(*MF); | |
| if (MBBI == MBB.end()) | |
| return 0; | |
| switch (MBBI->getOpcode()) { | |
| default: | |
| return 0; | |
| case TargetOpcode::PATCHABLE_RET: | |
| case M68k::RET: { | |
| SmallSet<uint16_t, 8> Uses; | |
| for (unsigned i = 0, e = MBBI->getNumOperands(); i != e; ++i) { | |
| MachineOperand &MO = MBBI->getOperand(i); | |
| if (!MO.isReg() || MO.isDef()) | |
| continue; | |
| Register Reg = MO.getReg(); | |
| if (!Reg) | |
| continue; | |
| for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI) | |
| Uses.insert(*AI); | |
| } | |
| for (auto CS : AvailableRegs) | |
| if (!Uses.count(CS)) | |
| return CS; | |
| } | |
| } | |
| return 0; | |
| } | |
| static bool isRegLiveIn(MachineBasicBlock &MBB, unsigned Reg) { | |
| return llvm::any_of(MBB.liveins(), | |
| [Reg](MachineBasicBlock::RegisterMaskPair RegMask) { | |
| return RegMask.PhysReg == Reg; | |
| }); | |
| } | |
| uint64_t | |
| M68kFrameLowering::calculateMaxStackAlign(const MachineFunction &MF) const { | |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| uint64_t MaxAlign = MFI.getMaxAlign().value(); // Desired stack alignment. | |
| unsigned StackAlign = getStackAlignment(); // ABI alignment | |
| if (MF.getFunction().hasFnAttribute("stackrealign")) { | |
| if (MFI.hasCalls()) | |
| MaxAlign = (StackAlign > MaxAlign) ? StackAlign : MaxAlign; | |
| else if (MaxAlign < SlotSize) | |
| MaxAlign = SlotSize; | |
| } | |
| return MaxAlign; | |
| } | |
| void M68kFrameLowering::BuildStackAlignAND(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator MBBI, | |
| const DebugLoc &DL, unsigned Reg, | |
| uint64_t MaxAlign) const { | |
| uint64_t Val = -MaxAlign; | |
| unsigned AndOp = M68k::AND32di; | |
| unsigned MovOp = M68k::MOV32rr; | |
| // This function is normally used with SP which is Address Register, but AND, | |
| // or any other logical instructions in M68k do not support ARs so we need | |
| // to use a temp Data Register to perform the op. | |
| unsigned Tmp = M68k::D0; | |
| BuildMI(MBB, MBBI, DL, TII.get(MovOp), Tmp) | |
| .addReg(Reg) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(AndOp), Tmp) | |
| .addReg(Tmp) | |
| .addImm(Val) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| // The CCR implicit def is dead. | |
| MI->getOperand(3).setIsDead(); | |
| BuildMI(MBB, MBBI, DL, TII.get(MovOp), Reg) | |
| .addReg(Tmp) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| } | |
| MachineBasicBlock::iterator M68kFrameLowering::eliminateCallFramePseudoInstr( | |
| MachineFunction &MF, MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator I) const { | |
| bool ReserveCallFrame = hasReservedCallFrame(MF); | |
| unsigned Opcode = I->getOpcode(); | |
| bool IsDestroy = Opcode == TII.getCallFrameDestroyOpcode(); | |
| DebugLoc DL = I->getDebugLoc(); | |
| uint64_t Amount = !ReserveCallFrame ? I->getOperand(0).getImm() : 0; | |
| uint64_t InternalAmt = (IsDestroy && Amount) ? I->getOperand(1).getImm() : 0; | |
| I = MBB.erase(I); | |
| if (!ReserveCallFrame) { | |
| // If the stack pointer can be changed after prologue, turn the | |
| // adjcallstackup instruction into a 'sub %SP, <amt>' and the | |
| // adjcallstackdown instruction into 'add %SP, <amt>' | |
| // We need to keep the stack aligned properly. To do this, we round the | |
| // amount of space needed for the outgoing arguments up to the next | |
| // alignment boundary. | |
| unsigned StackAlign = getStackAlignment(); | |
| Amount = alignTo(Amount, StackAlign); | |
| MachineModuleInfo &MMI = MF.getMMI(); | |
| const auto &Fn = MF.getFunction(); | |
| bool DwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry(); | |
| // If we have any exception handlers in this function, and we adjust | |
| // the SP before calls, we may need to indicate this to the unwinder | |
| // using GNU_ARGS_SIZE. Note that this may be necessary even when | |
| // Amount == 0, because the preceding function may have set a non-0 | |
| // GNU_ARGS_SIZE. | |
| // TODO: We don't need to reset this between subsequent functions, | |
| // if it didn't change. | |
| bool HasDwarfEHHandlers = !MF.getLandingPads().empty(); | |
| if (HasDwarfEHHandlers && !IsDestroy && | |
| MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences()) { | |
| BuildCFI(MBB, I, DL, | |
| MCCFIInstruction::createGnuArgsSize(nullptr, Amount)); | |
| } | |
| if (Amount == 0) | |
| return I; | |
| // Factor out the amount that gets handled inside the sequence | |
| // (Pushes of argument for frame setup, callee pops for frame destroy) | |
| Amount -= InternalAmt; | |
| // TODO: This is needed only if we require precise CFA. | |
| // If this is a callee-pop calling convention, emit a CFA adjust for | |
| // the amount the callee popped. | |
| if (IsDestroy && InternalAmt && DwarfCFI && !hasFP(MF)) | |
| BuildCFI(MBB, I, DL, | |
| MCCFIInstruction::createAdjustCfaOffset(nullptr, -InternalAmt)); | |
| // Add Amount to SP to destroy a frame, or subtract to setup. | |
| int64_t StackAdjustment = IsDestroy ? Amount : -Amount; | |
| int64_t CfaAdjustment = -StackAdjustment; | |
| if (StackAdjustment) { | |
| // Merge with any previous or following adjustment instruction. Note: the | |
| // instructions merged with here do not have CFI, so their stack | |
| // adjustments do not feed into CfaAdjustment. | |
| StackAdjustment += mergeSPUpdates(MBB, I, true); | |
| StackAdjustment += mergeSPUpdates(MBB, I, false); | |
| if (StackAdjustment) { | |
| BuildStackAdjustment(MBB, I, DL, StackAdjustment, false); | |
| } | |
| } | |
| if (DwarfCFI && !hasFP(MF)) { | |
| // If we don't have FP, but need to generate unwind information, | |
| // we need to set the correct CFA offset after the stack adjustment. | |
| // How much we adjust the CFA offset depends on whether we're emitting | |
| // CFI only for EH purposes or for debugging. EH only requires the CFA | |
| // offset to be correct at each call site, while for debugging we want | |
| // it to be more precise. | |
| // TODO: When not using precise CFA, we also need to adjust for the | |
| // InternalAmt here. | |
| if (CfaAdjustment) { | |
| BuildCFI( | |
| MBB, I, DL, | |
| MCCFIInstruction::createAdjustCfaOffset(nullptr, CfaAdjustment)); | |
| } | |
| } | |
| return I; | |
| } | |
| if (IsDestroy && InternalAmt) { | |
| // If we are performing frame pointer elimination and if the callee pops | |
| // something off the stack pointer, add it back. We do this until we have | |
| // more advanced stack pointer tracking ability. | |
| // We are not tracking the stack pointer adjustment by the callee, so make | |
| // sure we restore the stack pointer immediately after the call, there may | |
| // be spill code inserted between the CALL and ADJCALLSTACKUP instructions. | |
| MachineBasicBlock::iterator CI = I; | |
| MachineBasicBlock::iterator B = MBB.begin(); | |
| while (CI != B && !std::prev(CI)->isCall()) | |
| --CI; | |
| BuildStackAdjustment(MBB, CI, DL, -InternalAmt, /*InEpilogue=*/false); | |
| } | |
| return I; | |
| } | |
| /// Emit a series of instructions to increment / decrement the stack pointer by | |
| /// a constant value. | |
| void M68kFrameLowering::emitSPUpdate(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator &MBBI, | |
| int64_t NumBytes, bool InEpilogue) const { | |
| bool IsSub = NumBytes < 0; | |
| uint64_t Offset = IsSub ? -NumBytes : NumBytes; | |
| uint64_t Chunk = (1LL << 31) - 1; | |
| DebugLoc DL = MBB.findDebugLoc(MBBI); | |
| while (Offset) { | |
| if (Offset > Chunk) { | |
| // Rather than emit a long series of instructions for large offsets, | |
| // load the offset into a register and do one sub/add | |
| Register Reg; | |
| if (IsSub && !isRegLiveIn(MBB, M68k::D0)) | |
| Reg = M68k::D0; | |
| else | |
| Reg = findDeadCallerSavedReg(MBB, MBBI, TRI); | |
| if (Reg) { | |
| unsigned Opc = M68k::MOV32ri; | |
| BuildMI(MBB, MBBI, DL, TII.get(Opc), Reg).addImm(Offset); | |
| Opc = IsSub ? M68k::SUB32ar : M68k::ADD32ar; | |
| MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr) | |
| .addReg(StackPtr) | |
| .addReg(Reg); | |
| // ??? still no CCR | |
| MI->getOperand(3).setIsDead(); // The CCR implicit def is dead. | |
| Offset = 0; | |
| continue; | |
| } | |
| } | |
| uint64_t ThisVal = std::min(Offset, Chunk); | |
| MachineInstrBuilder MI = BuildStackAdjustment( | |
| MBB, MBBI, DL, IsSub ? -ThisVal : ThisVal, InEpilogue); | |
| if (IsSub) | |
| MI.setMIFlag(MachineInstr::FrameSetup); | |
| else | |
| MI.setMIFlag(MachineInstr::FrameDestroy); | |
| Offset -= ThisVal; | |
| } | |
| } | |
| int M68kFrameLowering::mergeSPUpdates(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator &MBBI, | |
| bool MergeWithPrevious) const { | |
| if ((MergeWithPrevious && MBBI == MBB.begin()) || | |
| (!MergeWithPrevious && MBBI == MBB.end())) | |
| return 0; | |
| MachineBasicBlock::iterator PI = MergeWithPrevious ? std::prev(MBBI) : MBBI; | |
| MachineBasicBlock::iterator NI = | |
| MergeWithPrevious ? nullptr : std::next(MBBI); | |
| unsigned Opc = PI->getOpcode(); | |
| int Offset = 0; | |
| if (!MergeWithPrevious && NI != MBB.end() && | |
| NI->getOpcode() == TargetOpcode::CFI_INSTRUCTION) { | |
| // Don't merge with the next instruction if it has CFI. | |
| return Offset; | |
| } | |
| if (Opc == M68k::ADD32ai && PI->getOperand(0).getReg() == StackPtr) { | |
| assert(PI->getOperand(1).getReg() == StackPtr); | |
| Offset += PI->getOperand(2).getImm(); | |
| MBB.erase(PI); | |
| if (!MergeWithPrevious) | |
| MBBI = NI; | |
| } else if (Opc == M68k::SUB32ai && PI->getOperand(0).getReg() == StackPtr) { | |
| assert(PI->getOperand(1).getReg() == StackPtr); | |
| Offset -= PI->getOperand(2).getImm(); | |
| MBB.erase(PI); | |
| if (!MergeWithPrevious) | |
| MBBI = NI; | |
| } | |
| return Offset; | |
| } | |
| MachineInstrBuilder M68kFrameLowering::BuildStackAdjustment( | |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, | |
| const DebugLoc &DL, int64_t Offset, bool InEpilogue) const { | |
| assert(Offset != 0 && "zero offset stack adjustment requested"); | |
| // TODO can `lea` be used to adjust stack? | |
| bool IsSub = Offset < 0; | |
| uint64_t AbsOffset = IsSub ? -Offset : Offset; | |
| unsigned Opc = IsSub ? M68k::SUB32ai : M68k::ADD32ai; | |
| MachineInstrBuilder MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr) | |
| .addReg(StackPtr) | |
| .addImm(AbsOffset); | |
| // FIXME Update CCR as well. For now we just | |
| // conservatively say CCR implicit def is dead | |
| MI->getOperand(3).setIsDead(); | |
| return MI; | |
| } | |
| void M68kFrameLowering::BuildCFI(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator MBBI, | |
| const DebugLoc &DL, | |
| const MCCFIInstruction &CFIInst) const { | |
| MachineFunction &MF = *MBB.getParent(); | |
| unsigned CFIIndex = MF.addFrameInst(CFIInst); | |
| BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION)) | |
| .addCFIIndex(CFIIndex); | |
| } | |
| void M68kFrameLowering::emitPrologueCalleeSavedFrameMoves( | |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, | |
| const DebugLoc &DL) const { | |
| MachineFunction &MF = *MBB.getParent(); | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| MachineModuleInfo &MMI = MF.getMMI(); | |
| const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); | |
| // Add callee saved registers to move list. | |
| const auto &CSI = MFI.getCalleeSavedInfo(); | |
| if (CSI.empty()) | |
| return; | |
| // Calculate offsets. | |
| for (const auto &I : CSI) { | |
| int64_t Offset = MFI.getObjectOffset(I.getFrameIdx()); | |
| Register Reg = I.getReg(); | |
| unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true); | |
| BuildCFI(MBB, MBBI, DL, | |
| MCCFIInstruction::createOffset(nullptr, DwarfReg, Offset)); | |
| } | |
| } | |
| void M68kFrameLowering::emitPrologue(MachineFunction &MF, | |
| MachineBasicBlock &MBB) const { | |
| assert(&STI == &MF.getSubtarget<M68kSubtarget>() && | |
| "MF used frame lowering for wrong subtarget"); | |
| MachineBasicBlock::iterator MBBI = MBB.begin(); | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| const auto &Fn = MF.getFunction(); | |
| MachineModuleInfo &MMI = MF.getMMI(); | |
| M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>(); | |
| uint64_t MaxAlign = calculateMaxStackAlign(MF); // Desired stack alignment. | |
| uint64_t StackSize = MFI.getStackSize(); // Number of bytes to allocate. | |
| bool HasFP = hasFP(MF); | |
| bool NeedsDwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry(); | |
| Register FramePtr = TRI->getFrameRegister(MF); | |
| const unsigned MachineFramePtr = FramePtr; | |
| unsigned BasePtr = TRI->getBaseRegister(); | |
| // Debug location must be unknown since the first debug location is used | |
| // to determine the end of the prologue. | |
| DebugLoc DL; | |
| // Add RETADDR move area to callee saved frame size. | |
| int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta(); | |
| if (TailCallReturnAddrDelta < 0) { | |
| MMFI->setCalleeSavedFrameSize(MMFI->getCalleeSavedFrameSize() - | |
| TailCallReturnAddrDelta); | |
| } | |
| // Insert stack pointer adjustment for later moving of return addr. Only | |
| // applies to tail call optimized functions where the callee argument stack | |
| // size is bigger than the callers. | |
| if (TailCallReturnAddrDelta < 0) { | |
| BuildStackAdjustment(MBB, MBBI, DL, TailCallReturnAddrDelta, | |
| /*InEpilogue=*/false) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| } | |
| // Mapping for machine moves: | |
| // | |
| // DST: VirtualFP AND | |
| // SRC: VirtualFP => DW_CFA_def_cfa_offset | |
| // ELSE => DW_CFA_def_cfa | |
| // | |
| // SRC: VirtualFP AND | |
| // DST: Register => DW_CFA_def_cfa_register | |
| // | |
| // ELSE | |
| // OFFSET < 0 => DW_CFA_offset_extended_sf | |
| // REG < 64 => DW_CFA_offset + Reg | |
| // ELSE => DW_CFA_offset_extended | |
| uint64_t NumBytes = 0; | |
| int stackGrowth = -SlotSize; | |
| if (HasFP) { | |
| // Calculate required stack adjustment. | |
| uint64_t FrameSize = StackSize - SlotSize; | |
| // If required, include space for extra hidden slot for stashing base | |
| // pointer. | |
| if (MMFI->getRestoreBasePointer()) | |
| FrameSize += SlotSize; | |
| NumBytes = FrameSize - MMFI->getCalleeSavedFrameSize(); | |
| // Callee-saved registers are pushed on stack before the stack is realigned. | |
| if (TRI->hasStackRealignment(MF)) | |
| NumBytes = alignTo(NumBytes, MaxAlign); | |
| // Get the offset of the stack slot for the FP register, which is | |
| // guaranteed to be the last slot by processFunctionBeforeFrameFinalized. | |
| // Update the frame offset adjustment. | |
| MFI.setOffsetAdjustment(-NumBytes); | |
| BuildMI(MBB, MBBI, DL, TII.get(M68k::LINK16)) | |
| .addReg(M68k::WA6, RegState::Kill) | |
| .addImm(-NumBytes) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| if (NeedsDwarfCFI) { | |
| // Mark the place where FP was saved. | |
| // Define the current CFA rule to use the provided offset. | |
| assert(StackSize); | |
| BuildCFI(MBB, MBBI, DL, | |
| MCCFIInstruction::cfiDefCfaOffset(nullptr, 2 * stackGrowth)); | |
| // Change the rule for the FramePtr to be an "offset" rule. | |
| int DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true); | |
| assert(DwarfFramePtr > 0); | |
| BuildCFI(MBB, MBBI, DL, | |
| MCCFIInstruction::createOffset(nullptr, DwarfFramePtr, | |
| 2 * stackGrowth)); | |
| } | |
| if (NeedsDwarfCFI) { | |
| // Mark effective beginning of when frame pointer becomes valid. | |
| // Define the current CFA to use the FP register. | |
| unsigned DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true); | |
| BuildCFI(MBB, MBBI, DL, | |
| MCCFIInstruction::createDefCfaRegister(nullptr, DwarfFramePtr)); | |
| } | |
| // Mark the FramePtr as live-in in every block. Don't do this again for | |
| // funclet prologues. | |
| for (MachineBasicBlock &EveryMBB : MF) | |
| EveryMBB.addLiveIn(MachineFramePtr); | |
| } else { | |
| NumBytes = StackSize - MMFI->getCalleeSavedFrameSize(); | |
| } | |
| // Skip the callee-saved push instructions. | |
| bool PushedRegs = false; | |
| int StackOffset = 2 * stackGrowth; | |
| while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup) && | |
| MBBI->getOpcode() == M68k::PUSH32r) { | |
| PushedRegs = true; | |
| ++MBBI; | |
| if (!HasFP && NeedsDwarfCFI) { | |
| // Mark callee-saved push instruction. | |
| // Define the current CFA rule to use the provided offset. | |
| assert(StackSize); | |
| BuildCFI(MBB, MBBI, DL, | |
| MCCFIInstruction::cfiDefCfaOffset(nullptr, StackOffset)); | |
| StackOffset += stackGrowth; | |
| } | |
| } | |
| // Realign stack after we pushed callee-saved registers (so that we'll be | |
| // able to calculate their offsets from the frame pointer). | |
| if (TRI->hasStackRealignment(MF)) { | |
| assert(HasFP && "There should be a frame pointer if stack is realigned."); | |
| BuildStackAlignAND(MBB, MBBI, DL, StackPtr, MaxAlign); | |
| } | |
| // If there is an SUB32ri of SP immediately before this instruction, merge | |
| // the two. This can be the case when tail call elimination is enabled and | |
| // the callee has more arguments then the caller. | |
| NumBytes -= mergeSPUpdates(MBB, MBBI, true); | |
| // Adjust stack pointer: ESP -= numbytes. | |
| if (!HasFP) | |
| emitSPUpdate(MBB, MBBI, -(int64_t)NumBytes, /*InEpilogue=*/false); | |
| unsigned SPOrEstablisher = StackPtr; | |
| // If we need a base pointer, set it up here. It's whatever the value | |
| // of the stack pointer is at this point. Any variable size objects | |
| // will be allocated after this, so we can still use the base pointer | |
| // to reference locals. | |
| if (TRI->hasBasePointer(MF)) { | |
| // Update the base pointer with the current stack pointer. | |
| BuildMI(MBB, MBBI, DL, TII.get(M68k::MOV32aa), BasePtr) | |
| .addReg(SPOrEstablisher) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| if (MMFI->getRestoreBasePointer()) { | |
| // Stash value of base pointer. Saving SP instead of FP shortens | |
| // dependence chain. Used by SjLj EH. | |
| unsigned Opm = M68k::MOV32ja; | |
| M68k::addRegIndirectWithDisp(BuildMI(MBB, MBBI, DL, TII.get(Opm)), | |
| FramePtr, true, | |
| MMFI->getRestoreBasePointerOffset()) | |
| .addReg(SPOrEstablisher) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| } | |
| } | |
| if (((!HasFP && NumBytes) || PushedRegs) && NeedsDwarfCFI) { | |
| // Mark end of stack pointer adjustment. | |
| if (!HasFP && NumBytes) { | |
| // Define the current CFA rule to use the provided offset. | |
| assert(StackSize); | |
| BuildCFI( | |
| MBB, MBBI, DL, | |
| MCCFIInstruction::cfiDefCfaOffset(nullptr, -StackSize + stackGrowth)); | |
| } | |
| // Emit DWARF info specifying the offsets of the callee-saved registers. | |
| if (PushedRegs) | |
| emitPrologueCalleeSavedFrameMoves(MBB, MBBI, DL); | |
| } | |
| // TODO Interrupt handlers | |
| // M68k Interrupt handling function cannot assume anything about the | |
| // direction flag (DF in CCR register). Clear this flag by creating "cld" | |
| // instruction in each prologue of interrupt handler function. The "cld" | |
| // instruction should only in these cases: | |
| // 1. The interrupt handling function uses any of the "rep" instructions. | |
| // 2. Interrupt handling function calls another function. | |
| } | |
| static bool isTailCallOpcode(unsigned Opc) { | |
| return Opc == M68k::TCRETURNj || Opc == M68k::TCRETURNq; | |
| } | |
| void M68kFrameLowering::emitEpilogue(MachineFunction &MF, | |
| MachineBasicBlock &MBB) const { | |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>(); | |
| MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); | |
| std::optional<unsigned> RetOpcode; | |
| if (MBBI != MBB.end()) | |
| RetOpcode = MBBI->getOpcode(); | |
| DebugLoc DL; | |
| if (MBBI != MBB.end()) | |
| DL = MBBI->getDebugLoc(); | |
| Register FramePtr = TRI->getFrameRegister(MF); | |
| unsigned MachineFramePtr = FramePtr; | |
| // Get the number of bytes to allocate from the FrameInfo. | |
| uint64_t StackSize = MFI.getStackSize(); | |
| uint64_t MaxAlign = calculateMaxStackAlign(MF); | |
| unsigned CSSize = MMFI->getCalleeSavedFrameSize(); | |
| uint64_t NumBytes = 0; | |
| if (hasFP(MF)) { | |
| // Calculate required stack adjustment. | |
| uint64_t FrameSize = StackSize - SlotSize; | |
| NumBytes = FrameSize - CSSize; | |
| // Callee-saved registers were pushed on stack before the stack was | |
| // realigned. | |
| if (TRI->hasStackRealignment(MF)) | |
| NumBytes = alignTo(FrameSize, MaxAlign); | |
| } else { | |
| NumBytes = StackSize - CSSize; | |
| } | |
| // Skip the callee-saved pop instructions. | |
| while (MBBI != MBB.begin()) { | |
| MachineBasicBlock::iterator PI = std::prev(MBBI); | |
| unsigned Opc = PI->getOpcode(); | |
| if ((Opc != M68k::POP32r || !PI->getFlag(MachineInstr::FrameDestroy)) && | |
| Opc != M68k::DBG_VALUE && !PI->isTerminator()) | |
| break; | |
| --MBBI; | |
| } | |
| MachineBasicBlock::iterator FirstCSPop = MBBI; | |
| if (MBBI != MBB.end()) | |
| DL = MBBI->getDebugLoc(); | |
| // If there is an ADD32ri or SUB32ri of SP immediately before this | |
| // instruction, merge the two instructions. | |
| if (NumBytes || MFI.hasVarSizedObjects()) | |
| NumBytes += mergeSPUpdates(MBB, MBBI, true); | |
| // If dynamic alloca is used, then reset SP to point to the last callee-saved | |
| // slot before popping them off! Same applies for the case, when stack was | |
| // realigned. Don't do this if this was a funclet epilogue, since the funclets | |
| // will not do realignment or dynamic stack allocation. | |
| if ((TRI->hasStackRealignment(MF) || MFI.hasVarSizedObjects())) { | |
| if (TRI->hasStackRealignment(MF)) | |
| MBBI = FirstCSPop; | |
| uint64_t LEAAmount = -CSSize; | |
| // 'move %FramePtr, SP' will not be recognized as an epilogue sequence. | |
| // However, we may use this sequence if we have a frame pointer because the | |
| // effects of the prologue can safely be undone. | |
| if (LEAAmount != 0) { | |
| unsigned Opc = M68k::LEA32p; | |
| M68k::addRegIndirectWithDisp( | |
| BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr), FramePtr, false, | |
| LEAAmount); | |
| --MBBI; | |
| } else { | |
| BuildMI(MBB, MBBI, DL, TII.get(M68k::UNLK)) | |
| .addReg(MachineFramePtr, RegState::Kill) | |
| .setMIFlag(MachineInstr::FrameDestroy); | |
| --MBBI; | |
| } | |
| } else if (hasFP(MF)) { | |
| BuildMI(MBB, MBBI, DL, TII.get(M68k::UNLK)) | |
| .addReg(MachineFramePtr, RegState::Kill) | |
| .setMIFlag(MachineInstr::FrameDestroy); | |
| } else if (NumBytes) { | |
| // Adjust stack pointer back: SP += numbytes. | |
| emitSPUpdate(MBB, MBBI, NumBytes, /*InEpilogue=*/true); | |
| --MBBI; | |
| } | |
| if (!RetOpcode || !isTailCallOpcode(*RetOpcode)) { | |
| // Add the return addr area delta back since we are not tail calling. | |
| int Offset = -1 * MMFI->getTCReturnAddrDelta(); | |
| assert(Offset >= 0 && "TCDelta should never be positive"); | |
| if (Offset) { | |
| MBBI = MBB.getFirstTerminator(); | |
| // Check for possible merge with preceding ADD instruction. | |
| Offset += mergeSPUpdates(MBB, MBBI, true); | |
| emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true); | |
| } | |
| } | |
| } | |
| void M68kFrameLowering::determineCalleeSaves(MachineFunction &MF, | |
| BitVector &SavedRegs, | |
| RegScavenger *RS) const { | |
| TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| M68kMachineFunctionInfo *M68kFI = MF.getInfo<M68kMachineFunctionInfo>(); | |
| int64_t TailCallReturnAddrDelta = M68kFI->getTCReturnAddrDelta(); | |
| if (TailCallReturnAddrDelta < 0) { | |
| // create RETURNADDR area | |
| // arg | |
| // arg | |
| // RETADDR | |
| // { ... | |
| // RETADDR area | |
| // ... | |
| // } | |
| // [FP] | |
| MFI.CreateFixedObject(-TailCallReturnAddrDelta, | |
| TailCallReturnAddrDelta - SlotSize, true); | |
| } | |
| // Spill the BasePtr if it's used. | |
| if (TRI->hasBasePointer(MF)) { | |
| SavedRegs.set(TRI->getBaseRegister()); | |
| } | |
| } | |
| bool M68kFrameLowering::assignCalleeSavedSpillSlots( | |
| MachineFunction &MF, const TargetRegisterInfo *TRI, | |
| std::vector<CalleeSavedInfo> &CSI) const { | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| M68kMachineFunctionInfo *M68kFI = MF.getInfo<M68kMachineFunctionInfo>(); | |
| int SpillSlotOffset = getOffsetOfLocalArea() + M68kFI->getTCReturnAddrDelta(); | |
| if (hasFP(MF)) { | |
| // emitPrologue always spills frame register the first thing. | |
| SpillSlotOffset -= SlotSize; | |
| MFI.CreateFixedSpillStackObject(SlotSize, SpillSlotOffset); | |
| // Since emitPrologue and emitEpilogue will handle spilling and restoring of | |
| // the frame register, we can delete it from CSI list and not have to worry | |
| // about avoiding it later. | |
| Register FPReg = TRI->getFrameRegister(MF); | |
| for (unsigned i = 0, e = CSI.size(); i < e; ++i) { | |
| if (TRI->regsOverlap(CSI[i].getReg(), FPReg)) { | |
| CSI.erase(CSI.begin() + i); | |
| break; | |
| } | |
| } | |
| } | |
| // The rest is fine | |
| return false; | |
| } | |
| bool M68kFrameLowering::spillCalleeSavedRegisters( | |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, | |
| ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const { | |
| auto &MRI = *static_cast<const M68kRegisterInfo *>(TRI); | |
| auto DL = MBB.findDebugLoc(MI); | |
| int FI = 0; | |
| unsigned Mask = 0; | |
| for (const auto &Info : CSI) { | |
| FI = std::max(FI, Info.getFrameIdx()); | |
| Register Reg = Info.getReg(); | |
| unsigned Shift = MRI.getSpillRegisterOrder(Reg); | |
| Mask |= 1 << Shift; | |
| } | |
| auto I = | |
| M68k::addFrameReference(BuildMI(MBB, MI, DL, TII.get(M68k::MOVM32pm)), FI) | |
| .addImm(Mask) | |
| .setMIFlag(MachineInstr::FrameSetup); | |
| // Append implicit registers and mem locations | |
| const MachineFunction &MF = *MBB.getParent(); | |
| const MachineRegisterInfo &RI = MF.getRegInfo(); | |
| for (const auto &Info : CSI) { | |
| Register Reg = Info.getReg(); | |
| bool IsLiveIn = RI.isLiveIn(Reg); | |
| if (!IsLiveIn) | |
| MBB.addLiveIn(Reg); | |
| I.addReg(Reg, IsLiveIn ? RegState::Implicit : RegState::ImplicitKill); | |
| M68k::addMemOperand(I, Info.getFrameIdx(), 0); | |
| } | |
| return true; | |
| } | |
| bool M68kFrameLowering::restoreCalleeSavedRegisters( | |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, | |
| MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const { | |
| auto &MRI = *static_cast<const M68kRegisterInfo *>(TRI); | |
| auto DL = MBB.findDebugLoc(MI); | |
| int FI = 0; | |
| unsigned Mask = 0; | |
| for (const auto &Info : CSI) { | |
| FI = std::max(FI, Info.getFrameIdx()); | |
| Register Reg = Info.getReg(); | |
| unsigned Shift = MRI.getSpillRegisterOrder(Reg); | |
| Mask |= 1 << Shift; | |
| } | |
| auto I = M68k::addFrameReference( | |
| BuildMI(MBB, MI, DL, TII.get(M68k::MOVM32mp)).addImm(Mask), FI) | |
| .setMIFlag(MachineInstr::FrameDestroy); | |
| // Append implicit registers and mem locations | |
| for (const auto &Info : CSI) { | |
| I.addReg(Info.getReg(), RegState::ImplicitDefine); | |
| M68k::addMemOperand(I, Info.getFrameIdx(), 0); | |
| } | |
| return true; | |
| } |