mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
a84c0ba160
These currently rely on the IRBuilder.h include in TargetLowering.h. Make them explicit.
615 lines
21 KiB
C++
615 lines
21 KiB
C++
//===- AArch64LowerHomogeneousPrologEpilog.cpp ----------------------------===//
|
|
//
|
|
// 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 a pass that lowers homogeneous prolog/epilog instructions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AArch64InstrInfo.h"
|
|
#include "AArch64Subtarget.h"
|
|
#include "MCTargetDesc/AArch64InstPrinter.h"
|
|
#include "Utils/AArch64BaseInfo.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineModuleInfo.h"
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
|
#include "llvm/IR/DebugLoc.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <sstream>
|
|
|
|
using namespace llvm;
|
|
|
|
#define AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME \
|
|
"AArch64 homogeneous prolog/epilog lowering pass"
|
|
|
|
cl::opt<int> FrameHelperSizeThreshold(
|
|
"frame-helper-size-threshold", cl::init(2), cl::Hidden,
|
|
cl::desc("The minimum number of instructions that are outlined in a frame "
|
|
"helper (default = 2)"));
|
|
|
|
namespace {
|
|
|
|
class AArch64LowerHomogeneousPE {
|
|
public:
|
|
const AArch64InstrInfo *TII;
|
|
|
|
AArch64LowerHomogeneousPE(Module *M, MachineModuleInfo *MMI)
|
|
: M(M), MMI(MMI) {}
|
|
|
|
bool run();
|
|
bool runOnMachineFunction(MachineFunction &Fn);
|
|
|
|
private:
|
|
Module *M;
|
|
MachineModuleInfo *MMI;
|
|
|
|
bool runOnMBB(MachineBasicBlock &MBB);
|
|
bool runOnMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI);
|
|
|
|
/// Lower a HOM_Prolog pseudo instruction into a helper call
|
|
/// or a sequence of homogeneous stores.
|
|
/// When a a fp setup follows, it can be optimized.
|
|
bool lowerProlog(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI);
|
|
/// Lower a HOM_Epilog pseudo instruction into a helper call
|
|
/// or a sequence of homogeneous loads.
|
|
/// When a return follow, it can be optimized.
|
|
bool lowerEpilog(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI);
|
|
};
|
|
|
|
class AArch64LowerHomogeneousPrologEpilog : public ModulePass {
|
|
public:
|
|
static char ID;
|
|
|
|
AArch64LowerHomogeneousPrologEpilog() : ModulePass(ID) {
|
|
initializeAArch64LowerHomogeneousPrologEpilogPass(
|
|
*PassRegistry::getPassRegistry());
|
|
}
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.addRequired<MachineModuleInfoWrapperPass>();
|
|
AU.addPreserved<MachineModuleInfoWrapperPass>();
|
|
AU.setPreservesAll();
|
|
ModulePass::getAnalysisUsage(AU);
|
|
}
|
|
bool runOnModule(Module &M) override;
|
|
|
|
StringRef getPassName() const override {
|
|
return AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
char AArch64LowerHomogeneousPrologEpilog::ID = 0;
|
|
|
|
INITIALIZE_PASS(AArch64LowerHomogeneousPrologEpilog,
|
|
"aarch64-lower-homogeneous-prolog-epilog",
|
|
AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME, false, false)
|
|
|
|
bool AArch64LowerHomogeneousPrologEpilog::runOnModule(Module &M) {
|
|
if (skipModule(M))
|
|
return false;
|
|
|
|
MachineModuleInfo *MMI =
|
|
&getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
|
|
return AArch64LowerHomogeneousPE(&M, MMI).run();
|
|
}
|
|
|
|
bool AArch64LowerHomogeneousPE::run() {
|
|
bool Changed = false;
|
|
for (auto &F : *M) {
|
|
if (F.empty())
|
|
continue;
|
|
|
|
MachineFunction *MF = MMI->getMachineFunction(F);
|
|
if (!MF)
|
|
continue;
|
|
Changed |= runOnMachineFunction(*MF);
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
enum FrameHelperType { Prolog, PrologFrame, Epilog, EpilogTail };
|
|
|
|
/// Return a frame helper name with the given CSRs and the helper type.
|
|
/// For instance, a prolog helper that saves x19 and x20 is named as
|
|
/// OUTLINED_FUNCTION_PROLOG_x19x20.
|
|
static std::string getFrameHelperName(SmallVectorImpl<unsigned> &Regs,
|
|
FrameHelperType Type, unsigned FpOffset) {
|
|
std::ostringstream RegStream;
|
|
switch (Type) {
|
|
case FrameHelperType::Prolog:
|
|
RegStream << "OUTLINED_FUNCTION_PROLOG_";
|
|
break;
|
|
case FrameHelperType::PrologFrame:
|
|
RegStream << "OUTLINED_FUNCTION_PROLOG_FRAME" << FpOffset << "_";
|
|
break;
|
|
case FrameHelperType::Epilog:
|
|
RegStream << "OUTLINED_FUNCTION_EPILOG_";
|
|
break;
|
|
case FrameHelperType::EpilogTail:
|
|
RegStream << "OUTLINED_FUNCTION_EPILOG_TAIL_";
|
|
break;
|
|
}
|
|
|
|
for (auto Reg : Regs)
|
|
RegStream << AArch64InstPrinter::getRegisterName(Reg);
|
|
|
|
return RegStream.str();
|
|
}
|
|
|
|
/// Create a Function for the unique frame helper with the given name.
|
|
/// Return a newly created MachineFunction with an empty MachineBasicBlock.
|
|
static MachineFunction &createFrameHelperMachineFunction(Module *M,
|
|
MachineModuleInfo *MMI,
|
|
StringRef Name) {
|
|
LLVMContext &C = M->getContext();
|
|
Function *F = M->getFunction(Name);
|
|
assert(F == nullptr && "Function has been created before");
|
|
F = Function::Create(FunctionType::get(Type::getVoidTy(C), false),
|
|
Function::ExternalLinkage, Name, M);
|
|
assert(F && "Function was null!");
|
|
|
|
// Use ODR linkage to avoid duplication.
|
|
F->setLinkage(GlobalValue::LinkOnceODRLinkage);
|
|
F->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
|
|
|
|
// Set no-opt/minsize, so we don't insert padding between outlined
|
|
// functions.
|
|
F->addFnAttr(Attribute::OptimizeNone);
|
|
F->addFnAttr(Attribute::NoInline);
|
|
F->addFnAttr(Attribute::MinSize);
|
|
F->addFnAttr(Attribute::Naked);
|
|
|
|
MachineFunction &MF = MMI->getOrCreateMachineFunction(*F);
|
|
// Remove unnecessary register liveness and set NoVRegs.
|
|
MF.getProperties().reset(MachineFunctionProperties::Property::TracksLiveness);
|
|
MF.getProperties().reset(MachineFunctionProperties::Property::IsSSA);
|
|
MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs);
|
|
MF.getRegInfo().freezeReservedRegs(MF);
|
|
|
|
// Create entry block.
|
|
BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
|
|
IRBuilder<> Builder(EntryBB);
|
|
Builder.CreateRetVoid();
|
|
|
|
// Insert the new block into the function.
|
|
MachineBasicBlock *MBB = MF.CreateMachineBasicBlock();
|
|
MF.insert(MF.begin(), MBB);
|
|
|
|
return MF;
|
|
}
|
|
|
|
/// Emit a store-pair instruction for frame-setup.
|
|
static void emitStore(MachineFunction &MF, MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator Pos,
|
|
const TargetInstrInfo &TII, unsigned Reg1, unsigned Reg2,
|
|
int Offset, bool IsPreDec) {
|
|
bool IsFloat = AArch64::FPR64RegClass.contains(Reg1);
|
|
assert(!(IsFloat ^ AArch64::FPR64RegClass.contains(Reg2)));
|
|
unsigned Opc;
|
|
if (IsPreDec)
|
|
Opc = IsFloat ? AArch64::STPDpre : AArch64::STPXpre;
|
|
else
|
|
Opc = IsFloat ? AArch64::STPDi : AArch64::STPXi;
|
|
|
|
MachineInstrBuilder MIB = BuildMI(MBB, Pos, DebugLoc(), TII.get(Opc));
|
|
if (IsPreDec)
|
|
MIB.addDef(AArch64::SP);
|
|
MIB.addReg(Reg2)
|
|
.addReg(Reg1)
|
|
.addReg(AArch64::SP)
|
|
.addImm(Offset)
|
|
.setMIFlag(MachineInstr::FrameSetup);
|
|
}
|
|
|
|
/// Emit a load-pair instruction for frame-destroy.
|
|
static void emitLoad(MachineFunction &MF, MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator Pos,
|
|
const TargetInstrInfo &TII, unsigned Reg1, unsigned Reg2,
|
|
int Offset, bool IsPostDec) {
|
|
bool IsFloat = AArch64::FPR64RegClass.contains(Reg1);
|
|
assert(!(IsFloat ^ AArch64::FPR64RegClass.contains(Reg2)));
|
|
unsigned Opc;
|
|
if (IsPostDec)
|
|
Opc = IsFloat ? AArch64::LDPDpost : AArch64::LDPXpost;
|
|
else
|
|
Opc = IsFloat ? AArch64::LDPDi : AArch64::LDPXi;
|
|
|
|
MachineInstrBuilder MIB = BuildMI(MBB, Pos, DebugLoc(), TII.get(Opc));
|
|
if (IsPostDec)
|
|
MIB.addDef(AArch64::SP);
|
|
MIB.addReg(Reg2, getDefRegState(true))
|
|
.addReg(Reg1, getDefRegState(true))
|
|
.addReg(AArch64::SP)
|
|
.addImm(Offset)
|
|
.setMIFlag(MachineInstr::FrameDestroy);
|
|
}
|
|
|
|
/// Return a unique function if a helper can be formed with the given Regs
|
|
/// and frame type.
|
|
/// 1) _OUTLINED_FUNCTION_PROLOG_x30x29x19x20x21x22:
|
|
/// stp x22, x21, [sp, #-32]! ; x29/x30 has been stored at the caller
|
|
/// stp x20, x19, [sp, #16]
|
|
/// ret
|
|
///
|
|
/// 2) _OUTLINED_FUNCTION_PROLOG_FRAME32_x30x29x19x20x21x22:
|
|
/// stp x22, x21, [sp, #-32]! ; x29/x30 has been stored at the caller
|
|
/// stp x20, x19, [sp, #16]
|
|
/// add fp, sp, #32
|
|
/// ret
|
|
///
|
|
/// 3) _OUTLINED_FUNCTION_EPILOG_x30x29x19x20x21x22:
|
|
/// mov x16, x30
|
|
/// ldp x29, x30, [sp, #32]
|
|
/// ldp x20, x19, [sp, #16]
|
|
/// ldp x22, x21, [sp], #48
|
|
/// ret x16
|
|
///
|
|
/// 4) _OUTLINED_FUNCTION_EPILOG_TAIL_x30x29x19x20x21x22:
|
|
/// ldp x29, x30, [sp, #32]
|
|
/// ldp x20, x19, [sp, #16]
|
|
/// ldp x22, x21, [sp], #48
|
|
/// ret
|
|
/// @param M module
|
|
/// @param MMI machine module info
|
|
/// @param Regs callee save regs that the helper will handle
|
|
/// @param Type frame helper type
|
|
/// @return a helper function
|
|
static Function *getOrCreateFrameHelper(Module *M, MachineModuleInfo *MMI,
|
|
SmallVectorImpl<unsigned> &Regs,
|
|
FrameHelperType Type,
|
|
unsigned FpOffset = 0) {
|
|
assert(Regs.size() >= 2);
|
|
auto Name = getFrameHelperName(Regs, Type, FpOffset);
|
|
auto *F = M->getFunction(Name);
|
|
if (F)
|
|
return F;
|
|
|
|
auto &MF = createFrameHelperMachineFunction(M, MMI, Name);
|
|
MachineBasicBlock &MBB = *MF.begin();
|
|
const TargetSubtargetInfo &STI = MF.getSubtarget();
|
|
const TargetInstrInfo &TII = *STI.getInstrInfo();
|
|
|
|
int Size = (int)Regs.size();
|
|
switch (Type) {
|
|
case FrameHelperType::Prolog:
|
|
case FrameHelperType::PrologFrame: {
|
|
// Compute the remaining SP adjust beyond FP/LR.
|
|
auto LRIdx = std::distance(
|
|
Regs.begin(), std::find(Regs.begin(), Regs.end(), AArch64::LR));
|
|
|
|
// If the register stored to the lowest address is not LR, we must subtract
|
|
// more from SP here.
|
|
if (LRIdx != Size - 2) {
|
|
assert(Regs[Size - 2] != AArch64::LR);
|
|
emitStore(MF, MBB, MBB.end(), TII, Regs[Size - 2], Regs[Size - 1],
|
|
LRIdx - Size + 2, true);
|
|
}
|
|
|
|
// Store CSRs in the reverse order.
|
|
for (int I = Size - 3; I >= 0; I -= 2) {
|
|
// FP/LR has been stored at call-site.
|
|
if (Regs[I - 1] == AArch64::LR)
|
|
continue;
|
|
emitStore(MF, MBB, MBB.end(), TII, Regs[I - 1], Regs[I], Size - I - 1,
|
|
false);
|
|
}
|
|
if (Type == FrameHelperType::PrologFrame)
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::ADDXri))
|
|
.addDef(AArch64::FP)
|
|
.addUse(AArch64::SP)
|
|
.addImm(FpOffset)
|
|
.addImm(0)
|
|
.setMIFlag(MachineInstr::FrameSetup);
|
|
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::RET))
|
|
.addReg(AArch64::LR);
|
|
break;
|
|
}
|
|
case FrameHelperType::Epilog:
|
|
case FrameHelperType::EpilogTail:
|
|
if (Type == FrameHelperType::Epilog)
|
|
// Stash LR to X16
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::ORRXrs))
|
|
.addDef(AArch64::X16)
|
|
.addReg(AArch64::XZR)
|
|
.addUse(AArch64::LR)
|
|
.addImm(0);
|
|
|
|
for (int I = 0; I < Size - 2; I += 2)
|
|
emitLoad(MF, MBB, MBB.end(), TII, Regs[I], Regs[I + 1], Size - I - 2,
|
|
false);
|
|
// Restore the last CSR with post-increment of SP.
|
|
emitLoad(MF, MBB, MBB.end(), TII, Regs[Size - 2], Regs[Size - 1], Size,
|
|
true);
|
|
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::RET))
|
|
.addReg(Type == FrameHelperType::Epilog ? AArch64::X16 : AArch64::LR);
|
|
break;
|
|
}
|
|
|
|
return M->getFunction(Name);
|
|
}
|
|
|
|
/// This function checks if a frame helper should be used for
|
|
/// HOM_Prolog/HOM_Epilog pseudo instruction expansion.
|
|
/// @param MBB machine basic block
|
|
/// @param NextMBBI next instruction following HOM_Prolog/HOM_Epilog
|
|
/// @param Regs callee save registers that are saved or restored.
|
|
/// @param Type frame helper type
|
|
/// @return True if a use of helper is qualified.
|
|
static bool shouldUseFrameHelper(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator &NextMBBI,
|
|
SmallVectorImpl<unsigned> &Regs,
|
|
FrameHelperType Type) {
|
|
const auto *TRI = MBB.getParent()->getSubtarget().getRegisterInfo();
|
|
auto RegCount = Regs.size();
|
|
assert(RegCount > 0 && (RegCount % 2 == 0));
|
|
// # of instructions that will be outlined.
|
|
int InstCount = RegCount / 2;
|
|
|
|
// Do not use a helper call when not saving LR.
|
|
if (std::find(Regs.begin(), Regs.end(), AArch64::LR) == Regs.end())
|
|
return false;
|
|
|
|
switch (Type) {
|
|
case FrameHelperType::Prolog:
|
|
// Prolog helper cannot save FP/LR.
|
|
InstCount--;
|
|
break;
|
|
case FrameHelperType::PrologFrame: {
|
|
// Effecitvely no change in InstCount since FpAdjusment is included.
|
|
break;
|
|
}
|
|
case FrameHelperType::Epilog:
|
|
// Bail-out if X16 is live across the epilog helper because it is used in
|
|
// the helper to handle X30.
|
|
for (auto NextMI = NextMBBI; NextMI != MBB.end(); NextMI++) {
|
|
if (NextMI->readsRegister(AArch64::W16, TRI))
|
|
return false;
|
|
}
|
|
// Epilog may not be in the last block. Check the liveness in successors.
|
|
for (const MachineBasicBlock *SuccMBB : MBB.successors()) {
|
|
if (SuccMBB->isLiveIn(AArch64::W16) || SuccMBB->isLiveIn(AArch64::X16))
|
|
return false;
|
|
}
|
|
// No change in InstCount for the regular epilog case.
|
|
break;
|
|
case FrameHelperType::EpilogTail: {
|
|
// EpilogTail helper includes the caller's return.
|
|
if (NextMBBI == MBB.end())
|
|
return false;
|
|
if (NextMBBI->getOpcode() != AArch64::RET_ReallyLR)
|
|
return false;
|
|
InstCount++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return InstCount >= FrameHelperSizeThreshold;
|
|
}
|
|
|
|
/// Lower a HOM_Epilog pseudo instruction into a helper call while
|
|
/// creating the helper on demand. Or emit a sequence of loads in place when not
|
|
/// using a helper call.
|
|
///
|
|
/// 1. With a helper including ret
|
|
/// HOM_Epilog x30, x29, x19, x20, x21, x22 ; MBBI
|
|
/// ret ; NextMBBI
|
|
/// =>
|
|
/// b _OUTLINED_FUNCTION_EPILOG_TAIL_x30x29x19x20x21x22
|
|
/// ... ; NextMBBI
|
|
///
|
|
/// 2. With a helper
|
|
/// HOM_Epilog x30, x29, x19, x20, x21, x22
|
|
/// =>
|
|
/// bl _OUTLINED_FUNCTION_EPILOG_x30x29x19x20x21x22
|
|
///
|
|
/// 3. Without a helper
|
|
/// HOM_Epilog x30, x29, x19, x20, x21, x22
|
|
/// =>
|
|
/// ldp x29, x30, [sp, #32]
|
|
/// ldp x20, x19, [sp, #16]
|
|
/// ldp x22, x21, [sp], #48
|
|
bool AArch64LowerHomogeneousPE::lowerEpilog(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI) {
|
|
auto &MF = *MBB.getParent();
|
|
MachineInstr &MI = *MBBI;
|
|
|
|
DebugLoc DL = MI.getDebugLoc();
|
|
SmallVector<unsigned, 8> Regs;
|
|
for (auto &MO : MI.operands())
|
|
if (MO.isReg())
|
|
Regs.push_back(MO.getReg());
|
|
int Size = (int)Regs.size();
|
|
if (Size == 0)
|
|
return false;
|
|
// Registers are in pair.
|
|
assert(Size % 2 == 0);
|
|
assert(MI.getOpcode() == AArch64::HOM_Epilog);
|
|
|
|
auto Return = NextMBBI;
|
|
if (shouldUseFrameHelper(MBB, NextMBBI, Regs, FrameHelperType::EpilogTail)) {
|
|
// When MBB ends with a return, emit a tail-call to the epilog helper
|
|
auto *EpilogTailHelper =
|
|
getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::EpilogTail);
|
|
BuildMI(MBB, MBBI, DL, TII->get(AArch64::TCRETURNdi))
|
|
.addGlobalAddress(EpilogTailHelper)
|
|
.addImm(0)
|
|
.setMIFlag(MachineInstr::FrameDestroy)
|
|
.copyImplicitOps(MI)
|
|
.copyImplicitOps(*Return);
|
|
NextMBBI = std::next(Return);
|
|
Return->removeFromParent();
|
|
} else if (shouldUseFrameHelper(MBB, NextMBBI, Regs,
|
|
FrameHelperType::Epilog)) {
|
|
// The default epilog helper case.
|
|
auto *EpilogHelper =
|
|
getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::Epilog);
|
|
BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
|
|
.addGlobalAddress(EpilogHelper)
|
|
.setMIFlag(MachineInstr::FrameDestroy)
|
|
.copyImplicitOps(MI);
|
|
} else {
|
|
// Fall back to no-helper.
|
|
for (int I = 0; I < Size - 2; I += 2)
|
|
emitLoad(MF, MBB, MBBI, *TII, Regs[I], Regs[I + 1], Size - I - 2, false);
|
|
// Restore the last CSR with post-increment of SP.
|
|
emitLoad(MF, MBB, MBBI, *TII, Regs[Size - 2], Regs[Size - 1], Size, true);
|
|
}
|
|
|
|
MBBI->removeFromParent();
|
|
return true;
|
|
}
|
|
|
|
/// Lower a HOM_Prolog pseudo instruction into a helper call while
|
|
/// creating the helper on demand. Or emit a sequence of stores in place when
|
|
/// not using a helper call.
|
|
///
|
|
/// 1. With a helper including frame-setup
|
|
/// HOM_Prolog x30, x29, x19, x20, x21, x22, 32
|
|
/// =>
|
|
/// stp x29, x30, [sp, #-16]!
|
|
/// bl _OUTLINED_FUNCTION_PROLOG_FRAME32_x30x29x19x20x21x22
|
|
///
|
|
/// 2. With a helper
|
|
/// HOM_Prolog x30, x29, x19, x20, x21, x22
|
|
/// =>
|
|
/// stp x29, x30, [sp, #-16]!
|
|
/// bl _OUTLINED_FUNCTION_PROLOG_x30x29x19x20x21x22
|
|
///
|
|
/// 3. Without a helper
|
|
/// HOM_Prolog x30, x29, x19, x20, x21, x22
|
|
/// =>
|
|
/// stp x22, x21, [sp, #-48]!
|
|
/// stp x20, x19, [sp, #16]
|
|
/// stp x29, x30, [sp, #32]
|
|
bool AArch64LowerHomogeneousPE::lowerProlog(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI) {
|
|
auto &MF = *MBB.getParent();
|
|
MachineInstr &MI = *MBBI;
|
|
|
|
DebugLoc DL = MI.getDebugLoc();
|
|
SmallVector<unsigned, 8> Regs;
|
|
int LRIdx = 0;
|
|
Optional<int> FpOffset;
|
|
for (auto &MO : MI.operands()) {
|
|
if (MO.isReg()) {
|
|
if (MO.getReg() == AArch64::LR)
|
|
LRIdx = Regs.size();
|
|
Regs.push_back(MO.getReg());
|
|
} else if (MO.isImm()) {
|
|
FpOffset = MO.getImm();
|
|
}
|
|
}
|
|
int Size = (int)Regs.size();
|
|
if (Size == 0)
|
|
return false;
|
|
// Allow compact unwind case only for oww.
|
|
assert(Size % 2 == 0);
|
|
assert(MI.getOpcode() == AArch64::HOM_Prolog);
|
|
|
|
if (FpOffset &&
|
|
shouldUseFrameHelper(MBB, NextMBBI, Regs, FrameHelperType::PrologFrame)) {
|
|
// FP/LR is stored at the top of stack before the prolog helper call.
|
|
emitStore(MF, MBB, MBBI, *TII, AArch64::LR, AArch64::FP, -LRIdx - 2, true);
|
|
auto *PrologFrameHelper = getOrCreateFrameHelper(
|
|
M, MMI, Regs, FrameHelperType::PrologFrame, *FpOffset);
|
|
BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
|
|
.addGlobalAddress(PrologFrameHelper)
|
|
.setMIFlag(MachineInstr::FrameSetup)
|
|
.copyImplicitOps(MI)
|
|
.addReg(AArch64::FP, RegState::Implicit | RegState::Define)
|
|
.addReg(AArch64::SP, RegState::Implicit);
|
|
} else if (!FpOffset && shouldUseFrameHelper(MBB, NextMBBI, Regs,
|
|
FrameHelperType::Prolog)) {
|
|
// FP/LR is stored at the top of stack before the prolog helper call.
|
|
emitStore(MF, MBB, MBBI, *TII, AArch64::LR, AArch64::FP, -LRIdx - 2, true);
|
|
auto *PrologHelper =
|
|
getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::Prolog);
|
|
BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
|
|
.addGlobalAddress(PrologHelper)
|
|
.setMIFlag(MachineInstr::FrameSetup)
|
|
.copyImplicitOps(MI);
|
|
} else {
|
|
// Fall back to no-helper.
|
|
emitStore(MF, MBB, MBBI, *TII, Regs[Size - 2], Regs[Size - 1], -Size, true);
|
|
for (int I = Size - 3; I >= 0; I -= 2)
|
|
emitStore(MF, MBB, MBBI, *TII, Regs[I - 1], Regs[I], Size - I - 1, false);
|
|
if (FpOffset) {
|
|
BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri))
|
|
.addDef(AArch64::FP)
|
|
.addUse(AArch64::SP)
|
|
.addImm(*FpOffset)
|
|
.addImm(0)
|
|
.setMIFlag(MachineInstr::FrameSetup);
|
|
}
|
|
}
|
|
|
|
MBBI->removeFromParent();
|
|
return true;
|
|
}
|
|
|
|
/// Process each machine instruction
|
|
/// @param MBB machine basic block
|
|
/// @param MBBI current instruction iterator
|
|
/// @param NextMBBI next instruction iterator which can be updated
|
|
/// @return True when IR is changed.
|
|
bool AArch64LowerHomogeneousPE::runOnMI(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI,
|
|
MachineBasicBlock::iterator &NextMBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
unsigned Opcode = MI.getOpcode();
|
|
switch (Opcode) {
|
|
default:
|
|
break;
|
|
case AArch64::HOM_Prolog:
|
|
return lowerProlog(MBB, MBBI, NextMBBI);
|
|
case AArch64::HOM_Epilog:
|
|
return lowerEpilog(MBB, MBBI, NextMBBI);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AArch64LowerHomogeneousPE::runOnMBB(MachineBasicBlock &MBB) {
|
|
bool Modified = false;
|
|
|
|
MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
|
|
while (MBBI != E) {
|
|
MachineBasicBlock::iterator NMBBI = std::next(MBBI);
|
|
Modified |= runOnMI(MBB, MBBI, NMBBI);
|
|
MBBI = NMBBI;
|
|
}
|
|
|
|
return Modified;
|
|
}
|
|
|
|
bool AArch64LowerHomogeneousPE::runOnMachineFunction(MachineFunction &MF) {
|
|
TII = static_cast<const AArch64InstrInfo *>(MF.getSubtarget().getInstrInfo());
|
|
|
|
bool Modified = false;
|
|
for (auto &MBB : MF)
|
|
Modified |= runOnMBB(MBB);
|
|
return Modified;
|
|
}
|
|
|
|
ModulePass *llvm::createAArch64LowerHomogeneousPrologEpilogPass() {
|
|
return new AArch64LowerHomogeneousPrologEpilog();
|
|
}
|