//===- ARCFrameLowering.cpp - ARC 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 // //===----------------------------------------------------------------------===// // // This file contains the ARC implementation of the TargetFrameLowering class. // //===----------------------------------------------------------------------===// #include "ARCFrameLowering.h" #include "ARCMachineFunctionInfo.h" #include "ARCSubtarget.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/RegisterScavenging.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/IR/Function.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "arc-frame-lowering" using namespace llvm; static cl::opt UseSaveRestoreFunclet("arc-save-restore-funclet", cl::Hidden, cl::desc("Use arc callee save/restore functions"), cl::init(true)); static const char *store_funclet_name[] = { "__st_r13_to_r15", "__st_r13_to_r16", "__st_r13_to_r17", "__st_r13_to_r18", "__st_r13_to_r19", "__st_r13_to_r20", "__st_r13_to_r21", "__st_r13_to_r22", "__st_r13_to_r23", "__st_r13_to_r24", "__st_r13_to_r25", }; static const char *load_funclet_name[] = { "__ld_r13_to_r15", "__ld_r13_to_r16", "__ld_r13_to_r17", "__ld_r13_to_r18", "__ld_r13_to_r19", "__ld_r13_to_r20", "__ld_r13_to_r21", "__ld_r13_to_r22", "__ld_r13_to_r23", "__ld_r13_to_r24", "__ld_r13_to_r25", }; static void generateStackAdjustment(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const ARCInstrInfo &TII, DebugLoc dl, int Amount, int StackPtr) { unsigned AdjOp; if (!Amount) return; bool Positive; unsigned AbsAmount; if (Amount < 0) { AbsAmount = -Amount; Positive = false; } else { AbsAmount = Amount; Positive = true; } LLVM_DEBUG(dbgs() << "Internal: adjust stack by: " << Amount << "," << AbsAmount << "\n"); assert((AbsAmount % 4 == 0) && "Stack adjustments must be 4-byte aligned."); if (isUInt<6>(AbsAmount)) AdjOp = Positive ? ARC::ADD_rru6 : ARC::SUB_rru6; else if (isInt<12>(AbsAmount)) AdjOp = Positive ? ARC::ADD_rrs12 : ARC::SUB_rrs12; else AdjOp = Positive ? ARC::ADD_rrlimm : ARC::SUB_rrlimm; BuildMI(MBB, MBBI, dl, TII.get(AdjOp), StackPtr) .addReg(StackPtr) .addImm(AbsAmount); } static unsigned determineLastCalleeSave(const std::vector &CSI) { unsigned Last = 0; for (auto Reg : CSI) { assert(Reg.getReg() >= ARC::R13 && Reg.getReg() <= ARC::R25 && "Unexpected callee saved reg."); if (Reg.getReg() > Last) Last = Reg.getReg(); } return Last; } void ARCFrameLowering::determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const { LLVM_DEBUG(dbgs() << "Determine Callee Saves: " << MF.getName() << "\n"); TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); SavedRegs.set(ARC::BLINK); } void ARCFrameLowering::adjustStackToMatchRecords( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, bool Allocate) const { MachineFunction &MF = *MBB.getParent(); int ScalarAlloc = MF.getFrameInfo().getStackSize(); if (Allocate) { // Allocate by adjusting by the negative of what the record holder tracked // it tracked a positive offset in a downward growing stack. ScalarAlloc = -ScalarAlloc; } generateStackAdjustment(MBB, MBBI, *ST.getInstrInfo(), DebugLoc(), ScalarAlloc, ARC::SP); } /// Insert prolog code into the function. /// For ARC, this inserts a call to a function that puts required callee saved /// registers onto the stack, when enough callee saved registers are required. void ARCFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { LLVM_DEBUG(dbgs() << "Emit Prologue: " << MF.getName() << "\n"); auto *AFI = MF.getInfo(); MachineModuleInfo &MMI = MF.getMMI(); MCContext &Context = MMI.getContext(); const MCRegisterInfo *MRI = Context.getRegisterInfo(); const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); MachineBasicBlock::iterator MBBI = MBB.begin(); // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc dl; MachineFrameInfo &MFI = MF.getFrameInfo(); const std::vector &CSI = MFI.getCalleeSavedInfo(); unsigned Last = determineLastCalleeSave(CSI); unsigned StackSlotsUsedByFunclet = 0; bool SavedBlink = false; unsigned AlreadyAdjusted = 0; if (MF.getFunction().isVarArg()) { // Add in the varargs area here first. LLVM_DEBUG(dbgs() << "Varargs\n"); unsigned VarArgsBytes = MFI.getObjectSize(AFI->getVarArgsFrameIndex()); unsigned Opc = ARC::SUB_rrlimm; if (isUInt<6>(VarArgsBytes)) Opc = ARC::SUB_rru6; else if (isInt<12>(VarArgsBytes)) Opc = ARC::SUB_rrs12; BuildMI(MBB, MBBI, dl, TII->get(Opc), ARC::SP) .addReg(ARC::SP) .addImm(VarArgsBytes); } if (hasFP(MF)) { LLVM_DEBUG(dbgs() << "Saving FP\n"); BuildMI(MBB, MBBI, dl, TII->get(ARC::ST_AW_rs9)) .addReg(ARC::SP, RegState::Define) .addReg(ARC::FP) .addReg(ARC::SP) .addImm(-4); AlreadyAdjusted += 4; } if (UseSaveRestoreFunclet && Last > ARC::R14) { LLVM_DEBUG(dbgs() << "Creating store funclet.\n"); // BL to __save_r13_to_getRegAsmName()> StackSlotsUsedByFunclet = Last - ARC::R12; BuildMI(MBB, MBBI, dl, TII->get(ARC::PUSH_S_BLINK)); BuildMI(MBB, MBBI, dl, TII->get(ARC::SUB_rru6)) .addReg(ARC::SP) .addReg(ARC::SP) .addImm(4 * StackSlotsUsedByFunclet); BuildMI(MBB, MBBI, dl, TII->get(ARC::BL)) .addExternalSymbol(store_funclet_name[Last - ARC::R15]) .addReg(ARC::BLINK, RegState::Implicit | RegState::Kill); AlreadyAdjusted += 4 * (StackSlotsUsedByFunclet + 1); SavedBlink = true; } // If we haven't saved BLINK, but we need to...do that now. if (MFI.hasCalls() && !SavedBlink) { LLVM_DEBUG(dbgs() << "Creating save blink.\n"); BuildMI(MBB, MBBI, dl, TII->get(ARC::PUSH_S_BLINK)); AlreadyAdjusted += 4; } if (AFI->MaxCallStackReq > 0) MFI.setStackSize(MFI.getStackSize() + AFI->MaxCallStackReq); // We have already saved some of the stack... LLVM_DEBUG(dbgs() << "Adjusting stack by: " << (MFI.getStackSize() - AlreadyAdjusted) << "\n"); generateStackAdjustment(MBB, MBBI, *ST.getInstrInfo(), dl, -(MFI.getStackSize() - AlreadyAdjusted), ARC::SP); if (hasFP(MF)) { LLVM_DEBUG(dbgs() << "Setting FP from SP.\n"); BuildMI(MBB, MBBI, dl, TII->get(isUInt<6>(MFI.getStackSize()) ? ARC::ADD_rru6 : ARC::ADD_rrlimm), ARC::FP) .addReg(ARC::SP) .addImm(MFI.getStackSize()); } // Emit CFI records: // .cfi_def_cfa_offset StackSize // .cfi_offset fp, -StackSize // .cfi_offset blink, -StackSize+4 unsigned CFIIndex = MF.addFrameInst( MCCFIInstruction::createDefCfaOffset(nullptr, -MFI.getStackSize())); BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex) .setMIFlags(MachineInstr::FrameSetup); int CurOffset = -4; if (hasFP(MF)) { CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( nullptr, MRI->getDwarfRegNum(ARC::FP, true), CurOffset)); BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex) .setMIFlags(MachineInstr::FrameSetup); CurOffset -= 4; } if (MFI.hasCalls()) { CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( nullptr, MRI->getDwarfRegNum(ARC::BLINK, true), CurOffset)); BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex) .setMIFlags(MachineInstr::FrameSetup); } // CFI for the rest of the registers. for (const auto &Entry : CSI) { unsigned Reg = Entry.getReg(); int FI = Entry.getFrameIdx(); // Skip BLINK and FP. if ((hasFP(MF) && Reg == ARC::FP) || (MFI.hasCalls() && Reg == ARC::BLINK)) continue; CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( nullptr, MRI->getDwarfRegNum(Reg, true), MFI.getObjectOffset(FI))); BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex) .setMIFlags(MachineInstr::FrameSetup); } } /// Insert epilog code into the function. /// For ARC, this inserts a call to a function that restores callee saved /// registers onto the stack, when enough callee saved registers are required. void ARCFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { LLVM_DEBUG(dbgs() << "Emit Epilogue: " << MF.getName() << "\n"); auto *AFI = MF.getInfo(); const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); MachineFrameInfo &MFI = MF.getFrameInfo(); uint64_t StackSize = MF.getFrameInfo().getStackSize(); bool SavedBlink = false; unsigned AmountAboveFunclet = 0; // If we have variable sized frame objects, then we have to move // the stack pointer to a known spot (fp - StackSize). // Then, replace the frame pointer by (new) [sp,StackSize-4]. // Then, move the stack pointer the rest of the way (sp = sp + StackSize). if (hasFP(MF)) { unsigned Opc = ARC::SUB_rrlimm; if (isUInt<6>(StackSize)) Opc = ARC::SUB_rru6; BuildMI(MBB, MBBI, DebugLoc(), TII->get(Opc), ARC::SP) .addReg(ARC::FP) .addImm(StackSize); AmountAboveFunclet += 4; } // Now, move the stack pointer to the bottom of the save area for the funclet. const std::vector &CSI = MFI.getCalleeSavedInfo(); unsigned Last = determineLastCalleeSave(CSI); unsigned StackSlotsUsedByFunclet = 0; // Now, restore the callee save registers. if (UseSaveRestoreFunclet && Last > ARC::R14) { // BL to __ld_r13_to_getRegAsmName()> StackSlotsUsedByFunclet = Last - ARC::R12; AmountAboveFunclet += 4 * (StackSlotsUsedByFunclet + 1); SavedBlink = true; } if (MFI.hasCalls() && !SavedBlink) { AmountAboveFunclet += 4; SavedBlink = true; } // Move the stack pointer up to the point of the funclet. if (unsigned MoveAmount = StackSize - AmountAboveFunclet) { unsigned Opc = ARC::ADD_rrlimm; if (isUInt<6>(MoveAmount)) Opc = ARC::ADD_rru6; else if (isInt<12>(MoveAmount)) Opc = ARC::ADD_rrs12; BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(Opc), ARC::SP) .addReg(ARC::SP) .addImm(StackSize - AmountAboveFunclet); } if (StackSlotsUsedByFunclet) { // This part of the adjustment will always be < 64 bytes. BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::BL)) .addExternalSymbol(load_funclet_name[Last - ARC::R15]) .addReg(ARC::BLINK, RegState::Implicit | RegState::Kill); unsigned Opc = ARC::ADD_rrlimm; if (isUInt<6>(4 * StackSlotsUsedByFunclet)) Opc = ARC::ADD_rru6; else if (isInt<12>(4 * StackSlotsUsedByFunclet)) Opc = ARC::ADD_rrs12; BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(Opc), ARC::SP) .addReg(ARC::SP) .addImm(4 * (StackSlotsUsedByFunclet)); } // Now, pop blink if necessary. if (SavedBlink) { BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::POP_S_BLINK)); } // Now, pop fp if necessary. if (hasFP(MF)) { BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::LD_AB_rs9)) .addReg(ARC::FP, RegState::Define) .addReg(ARC::SP, RegState::Define) .addReg(ARC::SP) .addImm(4); } // Relieve the varargs area if necessary. if (MF.getFunction().isVarArg()) { // Add in the varargs area here first. LLVM_DEBUG(dbgs() << "Varargs\n"); unsigned VarArgsBytes = MFI.getObjectSize(AFI->getVarArgsFrameIndex()); unsigned Opc = ARC::ADD_rrlimm; if (isUInt<6>(VarArgsBytes)) Opc = ARC::ADD_rru6; else if (isInt<12>(VarArgsBytes)) Opc = ARC::ADD_rrs12; BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(Opc)) .addReg(ARC::SP) .addReg(ARC::SP) .addImm(VarArgsBytes); } } static std::vector::iterator getSavedReg(std::vector &V, unsigned reg) { for (auto I = V.begin(), E = V.end(); I != E; ++I) { if (reg == I->getReg()) return I; } return V.end(); } bool ARCFrameLowering::assignCalleeSavedSpillSlots( MachineFunction &MF, const TargetRegisterInfo *TRI, std::vector &CSI) const { // Use this opportunity to assign the spill slots for all of the potential // callee save registers (blink, fp, r13->r25) that we care about the // placement for. We can calculate all of that data here. int CurOffset = -4; unsigned Last = determineLastCalleeSave(CSI); MachineFrameInfo &MFI = MF.getFrameInfo(); if (hasFP(MF)) { // Create a fixed slot at for FP int StackObj = MFI.CreateFixedSpillStackObject(4, CurOffset, true); LLVM_DEBUG(dbgs() << "Creating fixed object (" << StackObj << ") for FP at " << CurOffset << "\n"); (void)StackObj; CurOffset -= 4; } if (MFI.hasCalls() || (UseSaveRestoreFunclet && Last > ARC::R14)) { // Create a fixed slot for BLINK. int StackObj = MFI.CreateFixedSpillStackObject(4, CurOffset, true); LLVM_DEBUG(dbgs() << "Creating fixed object (" << StackObj << ") for BLINK at " << CurOffset << "\n"); (void)StackObj; CurOffset -= 4; } // Create slots for last down to r13. for (unsigned Which = Last; Which > ARC::R12; Which--) { auto RegI = getSavedReg(CSI, Which); if (RegI == CSI.end() || RegI->getFrameIdx() == 0) { // Always create the stack slot. If for some reason the register isn't in // the save list, then don't worry about it. int FI = MFI.CreateFixedSpillStackObject(4, CurOffset, true); if (RegI != CSI.end()) RegI->setFrameIdx(FI); } else MFI.setObjectOffset(RegI->getFrameIdx(), CurOffset); CurOffset -= 4; } for (auto &I : CSI) { if (I.getReg() > ARC::R12) continue; if (I.getFrameIdx() == 0) { I.setFrameIdx(MFI.CreateFixedSpillStackObject(4, CurOffset, true)); LLVM_DEBUG(dbgs() << "Creating fixed object (" << I.getFrameIdx() << ") for other register at " << CurOffset << "\n"); } else { MFI.setObjectOffset(I.getFrameIdx(), CurOffset); LLVM_DEBUG(dbgs() << "Updating fixed object (" << I.getFrameIdx() << ") for other register at " << CurOffset << "\n"); } CurOffset -= 4; } return true; } bool ARCFrameLowering::spillCalleeSavedRegisters( MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const std::vector &CSI, const TargetRegisterInfo *TRI) const { LLVM_DEBUG(dbgs() << "Spill callee saved registers: " << MBB.getParent()->getName() << "\n"); // There are routines for saving at least 3 registers (r13 to r15, etc.) unsigned Last = determineLastCalleeSave(CSI); if (UseSaveRestoreFunclet && Last > ARC::R14) { // Use setObjectOffset for these registers. // Needs to be in or before processFunctionBeforeFrameFinalized. // Or, do assignCalleeSaveSpillSlots? // Will be handled in prolog. return true; } return false; } bool ARCFrameLowering::restoreCalleeSavedRegisters( MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, std::vector &CSI, const TargetRegisterInfo *TRI) const { LLVM_DEBUG(dbgs() << "Restore callee saved registers: " << MBB.getParent()->getName() << "\n"); // There are routines for saving at least 3 registers (r13 to r15, etc.) unsigned Last = determineLastCalleeSave(CSI); if (UseSaveRestoreFunclet && Last > ARC::R14) { // Will be handled in epilog. return true; } return false; } // Adjust local variables that are 4-bytes or larger to 4-byte boundary void ARCFrameLowering::processFunctionBeforeFrameFinalized( MachineFunction &MF, RegScavenger *RS) const { const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); LLVM_DEBUG(dbgs() << "Process function before frame finalized: " << MF.getName() << "\n"); MachineFrameInfo &MFI = MF.getFrameInfo(); LLVM_DEBUG(dbgs() << "Current stack size: " << MFI.getStackSize() << "\n"); const TargetRegisterClass *RC = &ARC::GPR32RegClass; if (MFI.hasStackObjects()) { int RegScavFI = MFI.CreateStackObject( RegInfo->getSpillSize(*RC), RegInfo->getSpillAlignment(*RC), false); RS->addScavengingFrameIndex(RegScavFI); LLVM_DEBUG(dbgs() << "Created scavenging index RegScavFI=" << RegScavFI << "\n"); } } static void emitRegUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI, DebugLoc dl, unsigned Reg, int NumBytes, bool IsAdd, const ARCInstrInfo *TII) { unsigned Opc; if (isUInt<6>(NumBytes)) Opc = IsAdd ? ARC::ADD_rru6 : ARC::SUB_rru6; else if (isInt<12>(NumBytes)) Opc = IsAdd ? ARC::ADD_rrs12 : ARC::SUB_rrs12; else Opc = IsAdd ? ARC::ADD_rrlimm : ARC::SUB_rrlimm; BuildMI(MBB, MBBI, dl, TII->get(Opc), Reg) .addReg(Reg, RegState::Kill) .addImm(NumBytes); } MachineBasicBlock::iterator ARCFrameLowering::eliminateCallFramePseudoInstr( MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { LLVM_DEBUG(dbgs() << "EmitCallFramePseudo: " << MF.getName() << "\n"); const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); MachineInstr &Old = *I; DebugLoc dl = Old.getDebugLoc(); unsigned Amt = Old.getOperand(0).getImm(); auto *AFI = MF.getInfo(); if (!hasFP(MF)) { if (Amt > AFI->MaxCallStackReq && Old.getOpcode() == ARC::ADJCALLSTACKDOWN) AFI->MaxCallStackReq = Amt; } else { if (Amt != 0) { assert((Old.getOpcode() == ARC::ADJCALLSTACKDOWN || Old.getOpcode() == ARC::ADJCALLSTACKUP) && "Unknown Frame Pseudo."); bool IsAdd = (Old.getOpcode() == ARC::ADJCALLSTACKUP); emitRegUpdate(MBB, I, dl, ARC::SP, Amt, IsAdd, TII); } } return MBB.erase(I); } bool ARCFrameLowering::hasFP(const MachineFunction &MF) const { const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); bool HasFP = MF.getTarget().Options.DisableFramePointerElim(MF) || MF.getFrameInfo().hasVarSizedObjects() || MF.getFrameInfo().isFrameAddressTaken() || RegInfo->needsStackRealignment(MF); return HasFP; }