
Implement base windowed register call ABI. By defaullt use rotation window by 8 registers.
391 lines
14 KiB
C++
391 lines
14 KiB
C++
//===- XtensaFrameLowering.cpp - Xtensa Frame Information -----------------===//
|
|
//
|
|
// 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 Xtensa implementation of TargetFrameLowering class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "XtensaFrameLowering.h"
|
|
#include "XtensaInstrInfo.h"
|
|
#include "XtensaMachineFunctionInfo.h"
|
|
#include "XtensaSubtarget.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineModuleInfo.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/RegisterScavenging.h"
|
|
#include "llvm/IR/Function.h"
|
|
|
|
using namespace llvm;
|
|
|
|
// Minimum frame = reg save area (4 words) plus static chain (1 word)
|
|
// and the total number of words must be a multiple of 128 bits.
|
|
// Width of a word, in units (bytes).
|
|
#define UNITS_PER_WORD 4
|
|
#define MIN_FRAME_SIZE (8 * UNITS_PER_WORD)
|
|
|
|
XtensaFrameLowering::XtensaFrameLowering(const XtensaSubtarget &STI)
|
|
: TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(4), 0,
|
|
Align(4)),
|
|
STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) {}
|
|
|
|
bool XtensaFrameLowering::hasFPImpl(const MachineFunction &MF) const {
|
|
const MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
return MF.getTarget().Options.DisableFramePointerElim(MF) ||
|
|
MFI.hasVarSizedObjects();
|
|
}
|
|
|
|
void XtensaFrameLowering::emitPrologue(MachineFunction &MF,
|
|
MachineBasicBlock &MBB) const {
|
|
assert(&MBB == &MF.front() && "Shrink-wrapping not yet implemented");
|
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
MachineBasicBlock::iterator MBBI = MBB.begin();
|
|
DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
|
|
MCRegister SP = Xtensa::SP;
|
|
MCRegister FP = TRI->getFrameRegister(MF);
|
|
const MCRegisterInfo *MRI = MF.getContext().getRegisterInfo();
|
|
XtensaMachineFunctionInfo *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();
|
|
|
|
// First, compute final stack size.
|
|
uint64_t StackSize = MFI.getStackSize();
|
|
uint64_t PrevStackSize = StackSize;
|
|
|
|
// Round up StackSize to 16*N
|
|
StackSize += (16 - StackSize) & 0xf;
|
|
|
|
if (STI.isWindowedABI()) {
|
|
StackSize += 32;
|
|
uint64_t MaxAlignment = MFI.getMaxAlign().value();
|
|
if (MaxAlignment > 32)
|
|
StackSize += MaxAlignment;
|
|
|
|
if (StackSize <= 32760) {
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::ENTRY))
|
|
.addReg(SP)
|
|
.addImm(StackSize);
|
|
} else {
|
|
// Use a8 as a temporary since a0-a7 may be live.
|
|
MCRegister TmpReg = Xtensa::A8;
|
|
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::ENTRY))
|
|
.addReg(SP)
|
|
.addImm(MIN_FRAME_SIZE);
|
|
TII.loadImmediate(MBB, MBBI, &TmpReg, StackSize - MIN_FRAME_SIZE);
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::SUB), TmpReg)
|
|
.addReg(SP)
|
|
.addReg(TmpReg);
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::MOVSP), SP).addReg(TmpReg);
|
|
}
|
|
|
|
// Calculate how much is needed to have the correct alignment.
|
|
// Change offset to: alignment + difference.
|
|
// For example, in case of alignment of 128:
|
|
// diff_to_128_aligned_address = (128 - (SP & 127))
|
|
// new_offset = SP + diff_to_128_aligned_address
|
|
// This is safe to do because we increased the stack size by MaxAlignment.
|
|
MCRegister Reg, RegMisAlign;
|
|
if (MaxAlignment > 32) {
|
|
TII.loadImmediate(MBB, MBBI, &RegMisAlign, MaxAlignment - 1);
|
|
TII.loadImmediate(MBB, MBBI, &Reg, MaxAlignment);
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::AND))
|
|
.addReg(RegMisAlign, RegState::Define)
|
|
.addReg(FP)
|
|
.addReg(RegMisAlign);
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::SUB), RegMisAlign)
|
|
.addReg(Reg)
|
|
.addReg(RegMisAlign);
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::ADD), SP)
|
|
.addReg(SP)
|
|
.addReg(RegMisAlign, RegState::Kill);
|
|
}
|
|
|
|
// Store FP register in A8, because FP may be used to pass function
|
|
// arguments
|
|
if (XtensaFI->isSaveFrameRegister()) {
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::OR), Xtensa::A8)
|
|
.addReg(FP)
|
|
.addReg(FP);
|
|
}
|
|
|
|
// if framepointer enabled, set it to point to the stack pointer.
|
|
if (hasFP(MF)) {
|
|
// Insert instruction "move $fp, $sp" at this location.
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::OR), FP)
|
|
.addReg(SP)
|
|
.addReg(SP)
|
|
.setMIFlag(MachineInstr::FrameSetup);
|
|
|
|
MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa(
|
|
nullptr, MRI->getDwarfRegNum(FP, true), StackSize);
|
|
unsigned CFIIndex = MF.addFrameInst(Inst);
|
|
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
|
|
.addCFIIndex(CFIIndex);
|
|
} else {
|
|
// emit ".cfi_def_cfa_offset StackSize"
|
|
unsigned CFIIndex = MF.addFrameInst(
|
|
MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize));
|
|
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
|
|
.addCFIIndex(CFIIndex);
|
|
}
|
|
} else {
|
|
// No need to allocate space on the stack.
|
|
if (StackSize == 0 && !MFI.adjustsStack())
|
|
return;
|
|
|
|
// Adjust stack.
|
|
TII.adjustStackPtr(SP, -StackSize, MBB, MBBI);
|
|
|
|
// emit ".cfi_def_cfa_offset StackSize"
|
|
unsigned CFIIndex =
|
|
MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize));
|
|
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
|
|
.addCFIIndex(CFIIndex);
|
|
|
|
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
|
|
|
|
if (!CSI.empty()) {
|
|
// Find the instruction past the last instruction that saves a
|
|
// callee-saved register to the stack. The callee-saved store
|
|
// instructions are placed at the begin of basic block, so
|
|
// iterate over instruction sequence and check that
|
|
// save instructions are placed correctly.
|
|
for (unsigned i = 0, e = CSI.size(); i < e; ++i) {
|
|
#ifndef NDEBUG
|
|
const CalleeSavedInfo &Info = CSI[i];
|
|
int FI = Info.getFrameIdx();
|
|
int StoreFI = 0;
|
|
|
|
// Checking that the instruction is exactly as expected
|
|
bool IsStoreInst = false;
|
|
if (MBBI->getOpcode() == TargetOpcode::COPY && Info.isSpilledToReg()) {
|
|
Register DstReg = MBBI->getOperand(0).getReg();
|
|
Register Reg = MBBI->getOperand(1).getReg();
|
|
IsStoreInst = Info.getDstReg() == DstReg.asMCReg() &&
|
|
Info.getReg() == Reg.asMCReg();
|
|
} else {
|
|
Register Reg = TII.isStoreToStackSlot(*MBBI, StoreFI);
|
|
IsStoreInst = Reg.asMCReg() == Info.getReg() && StoreFI == FI;
|
|
}
|
|
assert(IsStoreInst &&
|
|
"Unexpected callee-saved register store instruction");
|
|
#endif
|
|
++MBBI;
|
|
}
|
|
|
|
// Iterate over list of callee-saved registers and emit .cfi_offset
|
|
// directives.
|
|
for (const auto &I : CSI) {
|
|
int64_t Offset = MFI.getObjectOffset(I.getFrameIdx());
|
|
MCRegister Reg = I.getReg();
|
|
|
|
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
|
|
nullptr, MRI->getDwarfRegNum(Reg, 1), Offset));
|
|
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
|
|
.addCFIIndex(CFIIndex);
|
|
}
|
|
}
|
|
|
|
// if framepointer enabled, set it to point to the stack pointer.
|
|
if (hasFP(MF)) {
|
|
// Insert instruction "move $fp, $sp" at this location.
|
|
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::OR), FP)
|
|
.addReg(SP)
|
|
.addReg(SP)
|
|
.setMIFlag(MachineInstr::FrameSetup);
|
|
|
|
// emit ".cfi_def_cfa_register $fp"
|
|
unsigned CFIIndex =
|
|
MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(
|
|
nullptr, MRI->getDwarfRegNum(FP, true)));
|
|
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
|
|
.addCFIIndex(CFIIndex);
|
|
}
|
|
}
|
|
|
|
if (StackSize != PrevStackSize) {
|
|
MFI.setStackSize(StackSize);
|
|
|
|
for (int i = MFI.getObjectIndexBegin(); i < MFI.getObjectIndexEnd(); i++) {
|
|
if (!MFI.isDeadObjectIndex(i)) {
|
|
int64_t SPOffset = MFI.getObjectOffset(i);
|
|
|
|
if (SPOffset < 0)
|
|
MFI.setObjectOffset(i, SPOffset - StackSize + PrevStackSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XtensaFrameLowering::emitEpilogue(MachineFunction &MF,
|
|
MachineBasicBlock &MBB) const {
|
|
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
|
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
DebugLoc DL = MBBI->getDebugLoc();
|
|
MCRegister SP = Xtensa::SP;
|
|
MCRegister FP = TRI->getFrameRegister(MF);
|
|
|
|
// if framepointer enabled, restore the stack pointer.
|
|
if (hasFP(MF)) {
|
|
// We should place restore stack pointer instruction just before
|
|
// sequence of instructions which restores callee-saved registers.
|
|
// This sequence is placed at the end of the basic block,
|
|
// so we should find first instruction of the sequence.
|
|
MachineBasicBlock::iterator I = MBBI;
|
|
|
|
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
|
|
|
|
// Find the first instruction at the end that restores a callee-saved
|
|
// register.
|
|
for (unsigned i = 0, e = CSI.size(); i < e; ++i) {
|
|
--I;
|
|
#ifndef NDEBUG
|
|
const CalleeSavedInfo &Info = CSI[i];
|
|
int FI = Info.getFrameIdx();
|
|
int LoadFI = 0;
|
|
|
|
// Checking that the instruction is exactly as expected
|
|
bool IsRestoreInst = false;
|
|
if (I->getOpcode() == TargetOpcode::COPY && Info.isSpilledToReg()) {
|
|
Register Reg = I->getOperand(0).getReg();
|
|
Register DstReg = I->getOperand(1).getReg();
|
|
IsRestoreInst = Info.getDstReg() == DstReg.asMCReg() &&
|
|
Info.getReg() == Reg.asMCReg();
|
|
} else {
|
|
Register Reg = TII.isLoadFromStackSlot(*I, LoadFI);
|
|
IsRestoreInst = Info.getReg() == Reg.asMCReg() && LoadFI == FI;
|
|
}
|
|
assert(IsRestoreInst &&
|
|
"Unexpected callee-saved register restore instruction");
|
|
#endif
|
|
}
|
|
if (STI.isWindowedABI()) {
|
|
// In most architectures, we need to explicitly restore the stack pointer
|
|
// before returning.
|
|
//
|
|
// For Xtensa Windowed Register option, it is not needed to explicitly
|
|
// restore the stack pointer. Reason being is that on function return,
|
|
// the window of the caller (including the old stack pointer) gets
|
|
// restored anyways.
|
|
} else {
|
|
BuildMI(MBB, I, DL, TII.get(Xtensa::OR), SP).addReg(FP).addReg(FP);
|
|
}
|
|
}
|
|
|
|
if (STI.isWindowedABI())
|
|
return;
|
|
|
|
// Get the number of bytes from FrameInfo
|
|
uint64_t StackSize = MFI.getStackSize();
|
|
|
|
if (!StackSize)
|
|
return;
|
|
|
|
// Adjust stack.
|
|
TII.adjustStackPtr(SP, StackSize, MBB, MBBI);
|
|
}
|
|
|
|
bool XtensaFrameLowering::spillCalleeSavedRegisters(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
|
|
ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
|
|
MachineFunction *MF = MBB.getParent();
|
|
MachineBasicBlock &EntryBlock = *(MF->begin());
|
|
|
|
if (STI.isWindowedABI())
|
|
return true;
|
|
|
|
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
|
|
// Add the callee-saved register as live-in. Do not add if the register is
|
|
// A0 and return address is taken, because it will be implemented in
|
|
// method XtensaTargetLowering::LowerRETURNADDR.
|
|
// It's killed at the spill, unless the register is RA and return address
|
|
// is taken.
|
|
MCRegister Reg = CSI[i].getReg();
|
|
bool IsA0AndRetAddrIsTaken =
|
|
(Reg == Xtensa::A0) && MF->getFrameInfo().isReturnAddressTaken();
|
|
if (!IsA0AndRetAddrIsTaken)
|
|
EntryBlock.addLiveIn(Reg);
|
|
|
|
// Insert the spill to the stack frame.
|
|
bool IsKill = !IsA0AndRetAddrIsTaken;
|
|
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
|
|
TII.storeRegToStackSlot(EntryBlock, MI, Reg, IsKill, CSI[i].getFrameIdx(),
|
|
RC, TRI, Register());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool XtensaFrameLowering::restoreCalleeSavedRegisters(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
|
|
MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
|
|
if (STI.isWindowedABI())
|
|
return true;
|
|
return TargetFrameLowering::restoreCalleeSavedRegisters(MBB, MI, CSI, TRI);
|
|
}
|
|
|
|
// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions
|
|
MachineBasicBlock::iterator XtensaFrameLowering::eliminateCallFramePseudoInstr(
|
|
MachineFunction &MF, MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator I) const {
|
|
if (!hasReservedCallFrame(MF)) {
|
|
int64_t Amount = I->getOperand(0).getImm();
|
|
|
|
if (I->getOpcode() == Xtensa::ADJCALLSTACKDOWN)
|
|
Amount = -Amount;
|
|
|
|
TII.adjustStackPtr(Xtensa::SP, Amount, MBB, I);
|
|
}
|
|
|
|
return MBB.erase(I);
|
|
}
|
|
|
|
void XtensaFrameLowering::determineCalleeSaves(MachineFunction &MF,
|
|
BitVector &SavedRegs,
|
|
RegScavenger *RS) const {
|
|
MCRegister FP = TRI->getFrameRegister(MF);
|
|
|
|
if (STI.isWindowedABI()) {
|
|
return;
|
|
}
|
|
|
|
TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);
|
|
|
|
// Mark $fp as used if function has dedicated frame pointer.
|
|
if (hasFP(MF))
|
|
SavedRegs.set(FP);
|
|
}
|
|
|
|
void XtensaFrameLowering::processFunctionBeforeFrameFinalized(
|
|
MachineFunction &MF, RegScavenger *RS) const {
|
|
// Set scavenging frame index if necessary.
|
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
uint64_t MaxSPOffset = MFI.estimateStackSize(MF);
|
|
auto *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();
|
|
unsigned ScavSlotsNum = 0;
|
|
|
|
if (!isInt<12>(MaxSPOffset))
|
|
ScavSlotsNum = 1;
|
|
|
|
// Far branches over 18-bit offset require a spill slot for scratch register.
|
|
bool IsLargeFunction = !isInt<18>(MF.estimateFunctionSizeInBytes());
|
|
if (IsLargeFunction)
|
|
ScavSlotsNum = std::max(ScavSlotsNum, 1u);
|
|
|
|
const TargetRegisterClass &RC = Xtensa::ARRegClass;
|
|
unsigned Size = TRI->getSpillSize(RC);
|
|
Align Alignment = TRI->getSpillAlign(RC);
|
|
for (unsigned I = 0; I < ScavSlotsNum; I++) {
|
|
int FI = MFI.CreateSpillStackObject(Size, Alignment);
|
|
RS->addScavengingFrameIndex(FI);
|
|
|
|
if (IsLargeFunction &&
|
|
XtensaFI->getBranchRelaxationScratchFrameIndex() == -1)
|
|
XtensaFI->setBranchRelaxationScratchFrameIndex(FI);
|
|
}
|
|
}
|