173471bf0Spatrick //===- AArch64LowerHomogeneousPrologEpilog.cpp ----------------------------===//
273471bf0Spatrick //
373471bf0Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
473471bf0Spatrick // See https://llvm.org/LICENSE.txt for license information.
573471bf0Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
673471bf0Spatrick //
773471bf0Spatrick //===----------------------------------------------------------------------===//
873471bf0Spatrick //
973471bf0Spatrick // This file contains a pass that lowers homogeneous prolog/epilog instructions.
1073471bf0Spatrick //
1173471bf0Spatrick //===----------------------------------------------------------------------===//
1273471bf0Spatrick
1373471bf0Spatrick #include "AArch64InstrInfo.h"
1473471bf0Spatrick #include "AArch64Subtarget.h"
1573471bf0Spatrick #include "MCTargetDesc/AArch64InstPrinter.h"
1673471bf0Spatrick #include "Utils/AArch64BaseInfo.h"
1773471bf0Spatrick #include "llvm/CodeGen/MachineBasicBlock.h"
1873471bf0Spatrick #include "llvm/CodeGen/MachineFunction.h"
1973471bf0Spatrick #include "llvm/CodeGen/MachineFunctionPass.h"
2073471bf0Spatrick #include "llvm/CodeGen/MachineInstr.h"
2173471bf0Spatrick #include "llvm/CodeGen/MachineInstrBuilder.h"
2273471bf0Spatrick #include "llvm/CodeGen/MachineModuleInfo.h"
2373471bf0Spatrick #include "llvm/CodeGen/MachineOperand.h"
2473471bf0Spatrick #include "llvm/CodeGen/TargetSubtargetInfo.h"
2573471bf0Spatrick #include "llvm/IR/DebugLoc.h"
2673471bf0Spatrick #include "llvm/IR/IRBuilder.h"
2773471bf0Spatrick #include "llvm/Pass.h"
2873471bf0Spatrick #include "llvm/Support/raw_ostream.h"
29*d415bd75Srobert #include <optional>
3073471bf0Spatrick #include <sstream>
3173471bf0Spatrick
3273471bf0Spatrick using namespace llvm;
3373471bf0Spatrick
3473471bf0Spatrick #define AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME \
3573471bf0Spatrick "AArch64 homogeneous prolog/epilog lowering pass"
3673471bf0Spatrick
3773471bf0Spatrick cl::opt<int> FrameHelperSizeThreshold(
3873471bf0Spatrick "frame-helper-size-threshold", cl::init(2), cl::Hidden,
3973471bf0Spatrick cl::desc("The minimum number of instructions that are outlined in a frame "
4073471bf0Spatrick "helper (default = 2)"));
4173471bf0Spatrick
4273471bf0Spatrick namespace {
4373471bf0Spatrick
4473471bf0Spatrick class AArch64LowerHomogeneousPE {
4573471bf0Spatrick public:
4673471bf0Spatrick const AArch64InstrInfo *TII;
4773471bf0Spatrick
AArch64LowerHomogeneousPE(Module * M,MachineModuleInfo * MMI)4873471bf0Spatrick AArch64LowerHomogeneousPE(Module *M, MachineModuleInfo *MMI)
4973471bf0Spatrick : M(M), MMI(MMI) {}
5073471bf0Spatrick
5173471bf0Spatrick bool run();
5273471bf0Spatrick bool runOnMachineFunction(MachineFunction &Fn);
5373471bf0Spatrick
5473471bf0Spatrick private:
5573471bf0Spatrick Module *M;
5673471bf0Spatrick MachineModuleInfo *MMI;
5773471bf0Spatrick
5873471bf0Spatrick bool runOnMBB(MachineBasicBlock &MBB);
5973471bf0Spatrick bool runOnMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
6073471bf0Spatrick MachineBasicBlock::iterator &NextMBBI);
6173471bf0Spatrick
6273471bf0Spatrick /// Lower a HOM_Prolog pseudo instruction into a helper call
6373471bf0Spatrick /// or a sequence of homogeneous stores.
6473471bf0Spatrick /// When a a fp setup follows, it can be optimized.
6573471bf0Spatrick bool lowerProlog(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
6673471bf0Spatrick MachineBasicBlock::iterator &NextMBBI);
6773471bf0Spatrick /// Lower a HOM_Epilog pseudo instruction into a helper call
6873471bf0Spatrick /// or a sequence of homogeneous loads.
6973471bf0Spatrick /// When a return follow, it can be optimized.
7073471bf0Spatrick bool lowerEpilog(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
7173471bf0Spatrick MachineBasicBlock::iterator &NextMBBI);
7273471bf0Spatrick };
7373471bf0Spatrick
7473471bf0Spatrick class AArch64LowerHomogeneousPrologEpilog : public ModulePass {
7573471bf0Spatrick public:
7673471bf0Spatrick static char ID;
7773471bf0Spatrick
AArch64LowerHomogeneousPrologEpilog()7873471bf0Spatrick AArch64LowerHomogeneousPrologEpilog() : ModulePass(ID) {
7973471bf0Spatrick initializeAArch64LowerHomogeneousPrologEpilogPass(
8073471bf0Spatrick *PassRegistry::getPassRegistry());
8173471bf0Spatrick }
getAnalysisUsage(AnalysisUsage & AU) const8273471bf0Spatrick void getAnalysisUsage(AnalysisUsage &AU) const override {
8373471bf0Spatrick AU.addRequired<MachineModuleInfoWrapperPass>();
8473471bf0Spatrick AU.addPreserved<MachineModuleInfoWrapperPass>();
8573471bf0Spatrick AU.setPreservesAll();
8673471bf0Spatrick ModulePass::getAnalysisUsage(AU);
8773471bf0Spatrick }
8873471bf0Spatrick bool runOnModule(Module &M) override;
8973471bf0Spatrick
getPassName() const9073471bf0Spatrick StringRef getPassName() const override {
9173471bf0Spatrick return AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME;
9273471bf0Spatrick }
9373471bf0Spatrick };
9473471bf0Spatrick
9573471bf0Spatrick } // end anonymous namespace
9673471bf0Spatrick
9773471bf0Spatrick char AArch64LowerHomogeneousPrologEpilog::ID = 0;
9873471bf0Spatrick
9973471bf0Spatrick INITIALIZE_PASS(AArch64LowerHomogeneousPrologEpilog,
10073471bf0Spatrick "aarch64-lower-homogeneous-prolog-epilog",
10173471bf0Spatrick AARCH64_LOWER_HOMOGENEOUS_PROLOG_EPILOG_NAME, false, false)
10273471bf0Spatrick
runOnModule(Module & M)10373471bf0Spatrick bool AArch64LowerHomogeneousPrologEpilog::runOnModule(Module &M) {
10473471bf0Spatrick if (skipModule(M))
10573471bf0Spatrick return false;
10673471bf0Spatrick
10773471bf0Spatrick MachineModuleInfo *MMI =
10873471bf0Spatrick &getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
10973471bf0Spatrick return AArch64LowerHomogeneousPE(&M, MMI).run();
11073471bf0Spatrick }
11173471bf0Spatrick
run()11273471bf0Spatrick bool AArch64LowerHomogeneousPE::run() {
11373471bf0Spatrick bool Changed = false;
11473471bf0Spatrick for (auto &F : *M) {
11573471bf0Spatrick if (F.empty())
11673471bf0Spatrick continue;
11773471bf0Spatrick
11873471bf0Spatrick MachineFunction *MF = MMI->getMachineFunction(F);
11973471bf0Spatrick if (!MF)
12073471bf0Spatrick continue;
12173471bf0Spatrick Changed |= runOnMachineFunction(*MF);
12273471bf0Spatrick }
12373471bf0Spatrick
12473471bf0Spatrick return Changed;
12573471bf0Spatrick }
12673471bf0Spatrick enum FrameHelperType { Prolog, PrologFrame, Epilog, EpilogTail };
12773471bf0Spatrick
12873471bf0Spatrick /// Return a frame helper name with the given CSRs and the helper type.
12973471bf0Spatrick /// For instance, a prolog helper that saves x19 and x20 is named as
13073471bf0Spatrick /// OUTLINED_FUNCTION_PROLOG_x19x20.
getFrameHelperName(SmallVectorImpl<unsigned> & Regs,FrameHelperType Type,unsigned FpOffset)13173471bf0Spatrick static std::string getFrameHelperName(SmallVectorImpl<unsigned> &Regs,
13273471bf0Spatrick FrameHelperType Type, unsigned FpOffset) {
13373471bf0Spatrick std::ostringstream RegStream;
13473471bf0Spatrick switch (Type) {
13573471bf0Spatrick case FrameHelperType::Prolog:
13673471bf0Spatrick RegStream << "OUTLINED_FUNCTION_PROLOG_";
13773471bf0Spatrick break;
13873471bf0Spatrick case FrameHelperType::PrologFrame:
13973471bf0Spatrick RegStream << "OUTLINED_FUNCTION_PROLOG_FRAME" << FpOffset << "_";
14073471bf0Spatrick break;
14173471bf0Spatrick case FrameHelperType::Epilog:
14273471bf0Spatrick RegStream << "OUTLINED_FUNCTION_EPILOG_";
14373471bf0Spatrick break;
14473471bf0Spatrick case FrameHelperType::EpilogTail:
14573471bf0Spatrick RegStream << "OUTLINED_FUNCTION_EPILOG_TAIL_";
14673471bf0Spatrick break;
14773471bf0Spatrick }
14873471bf0Spatrick
14973471bf0Spatrick for (auto Reg : Regs)
15073471bf0Spatrick RegStream << AArch64InstPrinter::getRegisterName(Reg);
15173471bf0Spatrick
15273471bf0Spatrick return RegStream.str();
15373471bf0Spatrick }
15473471bf0Spatrick
15573471bf0Spatrick /// Create a Function for the unique frame helper with the given name.
15673471bf0Spatrick /// Return a newly created MachineFunction with an empty MachineBasicBlock.
createFrameHelperMachineFunction(Module * M,MachineModuleInfo * MMI,StringRef Name)15773471bf0Spatrick static MachineFunction &createFrameHelperMachineFunction(Module *M,
15873471bf0Spatrick MachineModuleInfo *MMI,
15973471bf0Spatrick StringRef Name) {
16073471bf0Spatrick LLVMContext &C = M->getContext();
16173471bf0Spatrick Function *F = M->getFunction(Name);
16273471bf0Spatrick assert(F == nullptr && "Function has been created before");
16373471bf0Spatrick F = Function::Create(FunctionType::get(Type::getVoidTy(C), false),
16473471bf0Spatrick Function::ExternalLinkage, Name, M);
16573471bf0Spatrick assert(F && "Function was null!");
16673471bf0Spatrick
16773471bf0Spatrick // Use ODR linkage to avoid duplication.
16873471bf0Spatrick F->setLinkage(GlobalValue::LinkOnceODRLinkage);
16973471bf0Spatrick F->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
17073471bf0Spatrick
17173471bf0Spatrick // Set no-opt/minsize, so we don't insert padding between outlined
17273471bf0Spatrick // functions.
17373471bf0Spatrick F->addFnAttr(Attribute::OptimizeNone);
17473471bf0Spatrick F->addFnAttr(Attribute::NoInline);
17573471bf0Spatrick F->addFnAttr(Attribute::MinSize);
17673471bf0Spatrick F->addFnAttr(Attribute::Naked);
17773471bf0Spatrick
17873471bf0Spatrick MachineFunction &MF = MMI->getOrCreateMachineFunction(*F);
17973471bf0Spatrick // Remove unnecessary register liveness and set NoVRegs.
18073471bf0Spatrick MF.getProperties().reset(MachineFunctionProperties::Property::TracksLiveness);
18173471bf0Spatrick MF.getProperties().reset(MachineFunctionProperties::Property::IsSSA);
18273471bf0Spatrick MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs);
18373471bf0Spatrick MF.getRegInfo().freezeReservedRegs(MF);
18473471bf0Spatrick
18573471bf0Spatrick // Create entry block.
18673471bf0Spatrick BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
18773471bf0Spatrick IRBuilder<> Builder(EntryBB);
18873471bf0Spatrick Builder.CreateRetVoid();
18973471bf0Spatrick
19073471bf0Spatrick // Insert the new block into the function.
19173471bf0Spatrick MachineBasicBlock *MBB = MF.CreateMachineBasicBlock();
19273471bf0Spatrick MF.insert(MF.begin(), MBB);
19373471bf0Spatrick
19473471bf0Spatrick return MF;
19573471bf0Spatrick }
19673471bf0Spatrick
19773471bf0Spatrick /// Emit a store-pair instruction for frame-setup.
emitStore(MachineFunction & MF,MachineBasicBlock & MBB,MachineBasicBlock::iterator Pos,const TargetInstrInfo & TII,unsigned Reg1,unsigned Reg2,int Offset,bool IsPreDec)19873471bf0Spatrick static void emitStore(MachineFunction &MF, MachineBasicBlock &MBB,
19973471bf0Spatrick MachineBasicBlock::iterator Pos,
20073471bf0Spatrick const TargetInstrInfo &TII, unsigned Reg1, unsigned Reg2,
20173471bf0Spatrick int Offset, bool IsPreDec) {
20273471bf0Spatrick bool IsFloat = AArch64::FPR64RegClass.contains(Reg1);
20373471bf0Spatrick assert(!(IsFloat ^ AArch64::FPR64RegClass.contains(Reg2)));
20473471bf0Spatrick unsigned Opc;
20573471bf0Spatrick if (IsPreDec)
20673471bf0Spatrick Opc = IsFloat ? AArch64::STPDpre : AArch64::STPXpre;
20773471bf0Spatrick else
20873471bf0Spatrick Opc = IsFloat ? AArch64::STPDi : AArch64::STPXi;
20973471bf0Spatrick
21073471bf0Spatrick MachineInstrBuilder MIB = BuildMI(MBB, Pos, DebugLoc(), TII.get(Opc));
21173471bf0Spatrick if (IsPreDec)
21273471bf0Spatrick MIB.addDef(AArch64::SP);
21373471bf0Spatrick MIB.addReg(Reg2)
21473471bf0Spatrick .addReg(Reg1)
21573471bf0Spatrick .addReg(AArch64::SP)
21673471bf0Spatrick .addImm(Offset)
21773471bf0Spatrick .setMIFlag(MachineInstr::FrameSetup);
21873471bf0Spatrick }
21973471bf0Spatrick
22073471bf0Spatrick /// Emit a load-pair instruction for frame-destroy.
emitLoad(MachineFunction & MF,MachineBasicBlock & MBB,MachineBasicBlock::iterator Pos,const TargetInstrInfo & TII,unsigned Reg1,unsigned Reg2,int Offset,bool IsPostDec)22173471bf0Spatrick static void emitLoad(MachineFunction &MF, MachineBasicBlock &MBB,
22273471bf0Spatrick MachineBasicBlock::iterator Pos,
22373471bf0Spatrick const TargetInstrInfo &TII, unsigned Reg1, unsigned Reg2,
22473471bf0Spatrick int Offset, bool IsPostDec) {
22573471bf0Spatrick bool IsFloat = AArch64::FPR64RegClass.contains(Reg1);
22673471bf0Spatrick assert(!(IsFloat ^ AArch64::FPR64RegClass.contains(Reg2)));
22773471bf0Spatrick unsigned Opc;
22873471bf0Spatrick if (IsPostDec)
22973471bf0Spatrick Opc = IsFloat ? AArch64::LDPDpost : AArch64::LDPXpost;
23073471bf0Spatrick else
23173471bf0Spatrick Opc = IsFloat ? AArch64::LDPDi : AArch64::LDPXi;
23273471bf0Spatrick
23373471bf0Spatrick MachineInstrBuilder MIB = BuildMI(MBB, Pos, DebugLoc(), TII.get(Opc));
23473471bf0Spatrick if (IsPostDec)
23573471bf0Spatrick MIB.addDef(AArch64::SP);
23673471bf0Spatrick MIB.addReg(Reg2, getDefRegState(true))
23773471bf0Spatrick .addReg(Reg1, getDefRegState(true))
23873471bf0Spatrick .addReg(AArch64::SP)
23973471bf0Spatrick .addImm(Offset)
24073471bf0Spatrick .setMIFlag(MachineInstr::FrameDestroy);
24173471bf0Spatrick }
24273471bf0Spatrick
24373471bf0Spatrick /// Return a unique function if a helper can be formed with the given Regs
24473471bf0Spatrick /// and frame type.
24573471bf0Spatrick /// 1) _OUTLINED_FUNCTION_PROLOG_x30x29x19x20x21x22:
24673471bf0Spatrick /// stp x22, x21, [sp, #-32]! ; x29/x30 has been stored at the caller
24773471bf0Spatrick /// stp x20, x19, [sp, #16]
24873471bf0Spatrick /// ret
24973471bf0Spatrick ///
25073471bf0Spatrick /// 2) _OUTLINED_FUNCTION_PROLOG_FRAME32_x30x29x19x20x21x22:
25173471bf0Spatrick /// stp x22, x21, [sp, #-32]! ; x29/x30 has been stored at the caller
25273471bf0Spatrick /// stp x20, x19, [sp, #16]
25373471bf0Spatrick /// add fp, sp, #32
25473471bf0Spatrick /// ret
25573471bf0Spatrick ///
25673471bf0Spatrick /// 3) _OUTLINED_FUNCTION_EPILOG_x30x29x19x20x21x22:
25773471bf0Spatrick /// mov x16, x30
25873471bf0Spatrick /// ldp x29, x30, [sp, #32]
25973471bf0Spatrick /// ldp x20, x19, [sp, #16]
26073471bf0Spatrick /// ldp x22, x21, [sp], #48
26173471bf0Spatrick /// ret x16
26273471bf0Spatrick ///
26373471bf0Spatrick /// 4) _OUTLINED_FUNCTION_EPILOG_TAIL_x30x29x19x20x21x22:
26473471bf0Spatrick /// ldp x29, x30, [sp, #32]
26573471bf0Spatrick /// ldp x20, x19, [sp, #16]
26673471bf0Spatrick /// ldp x22, x21, [sp], #48
26773471bf0Spatrick /// ret
26873471bf0Spatrick /// @param M module
26973471bf0Spatrick /// @param MMI machine module info
27073471bf0Spatrick /// @param Regs callee save regs that the helper will handle
27173471bf0Spatrick /// @param Type frame helper type
27273471bf0Spatrick /// @return a helper function
getOrCreateFrameHelper(Module * M,MachineModuleInfo * MMI,SmallVectorImpl<unsigned> & Regs,FrameHelperType Type,unsigned FpOffset=0)27373471bf0Spatrick static Function *getOrCreateFrameHelper(Module *M, MachineModuleInfo *MMI,
27473471bf0Spatrick SmallVectorImpl<unsigned> &Regs,
27573471bf0Spatrick FrameHelperType Type,
27673471bf0Spatrick unsigned FpOffset = 0) {
27773471bf0Spatrick assert(Regs.size() >= 2);
27873471bf0Spatrick auto Name = getFrameHelperName(Regs, Type, FpOffset);
27973471bf0Spatrick auto *F = M->getFunction(Name);
28073471bf0Spatrick if (F)
28173471bf0Spatrick return F;
28273471bf0Spatrick
28373471bf0Spatrick auto &MF = createFrameHelperMachineFunction(M, MMI, Name);
28473471bf0Spatrick MachineBasicBlock &MBB = *MF.begin();
28573471bf0Spatrick const TargetSubtargetInfo &STI = MF.getSubtarget();
28673471bf0Spatrick const TargetInstrInfo &TII = *STI.getInstrInfo();
28773471bf0Spatrick
28873471bf0Spatrick int Size = (int)Regs.size();
28973471bf0Spatrick switch (Type) {
29073471bf0Spatrick case FrameHelperType::Prolog:
29173471bf0Spatrick case FrameHelperType::PrologFrame: {
29273471bf0Spatrick // Compute the remaining SP adjust beyond FP/LR.
293*d415bd75Srobert auto LRIdx = std::distance(Regs.begin(), llvm::find(Regs, AArch64::LR));
29473471bf0Spatrick
29573471bf0Spatrick // If the register stored to the lowest address is not LR, we must subtract
29673471bf0Spatrick // more from SP here.
29773471bf0Spatrick if (LRIdx != Size - 2) {
29873471bf0Spatrick assert(Regs[Size - 2] != AArch64::LR);
29973471bf0Spatrick emitStore(MF, MBB, MBB.end(), TII, Regs[Size - 2], Regs[Size - 1],
30073471bf0Spatrick LRIdx - Size + 2, true);
30173471bf0Spatrick }
30273471bf0Spatrick
30373471bf0Spatrick // Store CSRs in the reverse order.
30473471bf0Spatrick for (int I = Size - 3; I >= 0; I -= 2) {
30573471bf0Spatrick // FP/LR has been stored at call-site.
30673471bf0Spatrick if (Regs[I - 1] == AArch64::LR)
30773471bf0Spatrick continue;
30873471bf0Spatrick emitStore(MF, MBB, MBB.end(), TII, Regs[I - 1], Regs[I], Size - I - 1,
30973471bf0Spatrick false);
31073471bf0Spatrick }
31173471bf0Spatrick if (Type == FrameHelperType::PrologFrame)
31273471bf0Spatrick BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::ADDXri))
31373471bf0Spatrick .addDef(AArch64::FP)
31473471bf0Spatrick .addUse(AArch64::SP)
31573471bf0Spatrick .addImm(FpOffset)
31673471bf0Spatrick .addImm(0)
31773471bf0Spatrick .setMIFlag(MachineInstr::FrameSetup);
31873471bf0Spatrick
31973471bf0Spatrick BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::RET))
32073471bf0Spatrick .addReg(AArch64::LR);
32173471bf0Spatrick break;
32273471bf0Spatrick }
32373471bf0Spatrick case FrameHelperType::Epilog:
32473471bf0Spatrick case FrameHelperType::EpilogTail:
32573471bf0Spatrick if (Type == FrameHelperType::Epilog)
32673471bf0Spatrick // Stash LR to X16
32773471bf0Spatrick BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::ORRXrs))
32873471bf0Spatrick .addDef(AArch64::X16)
32973471bf0Spatrick .addReg(AArch64::XZR)
33073471bf0Spatrick .addUse(AArch64::LR)
33173471bf0Spatrick .addImm(0);
33273471bf0Spatrick
33373471bf0Spatrick for (int I = 0; I < Size - 2; I += 2)
33473471bf0Spatrick emitLoad(MF, MBB, MBB.end(), TII, Regs[I], Regs[I + 1], Size - I - 2,
33573471bf0Spatrick false);
33673471bf0Spatrick // Restore the last CSR with post-increment of SP.
33773471bf0Spatrick emitLoad(MF, MBB, MBB.end(), TII, Regs[Size - 2], Regs[Size - 1], Size,
33873471bf0Spatrick true);
33973471bf0Spatrick
34073471bf0Spatrick BuildMI(MBB, MBB.end(), DebugLoc(), TII.get(AArch64::RET))
34173471bf0Spatrick .addReg(Type == FrameHelperType::Epilog ? AArch64::X16 : AArch64::LR);
34273471bf0Spatrick break;
34373471bf0Spatrick }
34473471bf0Spatrick
34573471bf0Spatrick return M->getFunction(Name);
34673471bf0Spatrick }
34773471bf0Spatrick
34873471bf0Spatrick /// This function checks if a frame helper should be used for
34973471bf0Spatrick /// HOM_Prolog/HOM_Epilog pseudo instruction expansion.
35073471bf0Spatrick /// @param MBB machine basic block
35173471bf0Spatrick /// @param NextMBBI next instruction following HOM_Prolog/HOM_Epilog
35273471bf0Spatrick /// @param Regs callee save registers that are saved or restored.
35373471bf0Spatrick /// @param Type frame helper type
35473471bf0Spatrick /// @return True if a use of helper is qualified.
shouldUseFrameHelper(MachineBasicBlock & MBB,MachineBasicBlock::iterator & NextMBBI,SmallVectorImpl<unsigned> & Regs,FrameHelperType Type)35573471bf0Spatrick static bool shouldUseFrameHelper(MachineBasicBlock &MBB,
35673471bf0Spatrick MachineBasicBlock::iterator &NextMBBI,
35773471bf0Spatrick SmallVectorImpl<unsigned> &Regs,
35873471bf0Spatrick FrameHelperType Type) {
35973471bf0Spatrick const auto *TRI = MBB.getParent()->getSubtarget().getRegisterInfo();
36073471bf0Spatrick auto RegCount = Regs.size();
36173471bf0Spatrick assert(RegCount > 0 && (RegCount % 2 == 0));
36273471bf0Spatrick // # of instructions that will be outlined.
36373471bf0Spatrick int InstCount = RegCount / 2;
36473471bf0Spatrick
36573471bf0Spatrick // Do not use a helper call when not saving LR.
366*d415bd75Srobert if (!llvm::is_contained(Regs, AArch64::LR))
36773471bf0Spatrick return false;
36873471bf0Spatrick
36973471bf0Spatrick switch (Type) {
37073471bf0Spatrick case FrameHelperType::Prolog:
37173471bf0Spatrick // Prolog helper cannot save FP/LR.
37273471bf0Spatrick InstCount--;
37373471bf0Spatrick break;
37473471bf0Spatrick case FrameHelperType::PrologFrame: {
37573471bf0Spatrick // Effecitvely no change in InstCount since FpAdjusment is included.
37673471bf0Spatrick break;
37773471bf0Spatrick }
37873471bf0Spatrick case FrameHelperType::Epilog:
37973471bf0Spatrick // Bail-out if X16 is live across the epilog helper because it is used in
38073471bf0Spatrick // the helper to handle X30.
38173471bf0Spatrick for (auto NextMI = NextMBBI; NextMI != MBB.end(); NextMI++) {
38273471bf0Spatrick if (NextMI->readsRegister(AArch64::W16, TRI))
38373471bf0Spatrick return false;
38473471bf0Spatrick }
38573471bf0Spatrick // Epilog may not be in the last block. Check the liveness in successors.
38673471bf0Spatrick for (const MachineBasicBlock *SuccMBB : MBB.successors()) {
38773471bf0Spatrick if (SuccMBB->isLiveIn(AArch64::W16) || SuccMBB->isLiveIn(AArch64::X16))
38873471bf0Spatrick return false;
38973471bf0Spatrick }
39073471bf0Spatrick // No change in InstCount for the regular epilog case.
39173471bf0Spatrick break;
39273471bf0Spatrick case FrameHelperType::EpilogTail: {
39373471bf0Spatrick // EpilogTail helper includes the caller's return.
39473471bf0Spatrick if (NextMBBI == MBB.end())
39573471bf0Spatrick return false;
39673471bf0Spatrick if (NextMBBI->getOpcode() != AArch64::RET_ReallyLR)
39773471bf0Spatrick return false;
39873471bf0Spatrick InstCount++;
39973471bf0Spatrick break;
40073471bf0Spatrick }
40173471bf0Spatrick }
40273471bf0Spatrick
40373471bf0Spatrick return InstCount >= FrameHelperSizeThreshold;
40473471bf0Spatrick }
40573471bf0Spatrick
40673471bf0Spatrick /// Lower a HOM_Epilog pseudo instruction into a helper call while
40773471bf0Spatrick /// creating the helper on demand. Or emit a sequence of loads in place when not
40873471bf0Spatrick /// using a helper call.
40973471bf0Spatrick ///
41073471bf0Spatrick /// 1. With a helper including ret
41173471bf0Spatrick /// HOM_Epilog x30, x29, x19, x20, x21, x22 ; MBBI
41273471bf0Spatrick /// ret ; NextMBBI
41373471bf0Spatrick /// =>
41473471bf0Spatrick /// b _OUTLINED_FUNCTION_EPILOG_TAIL_x30x29x19x20x21x22
41573471bf0Spatrick /// ... ; NextMBBI
41673471bf0Spatrick ///
41773471bf0Spatrick /// 2. With a helper
41873471bf0Spatrick /// HOM_Epilog x30, x29, x19, x20, x21, x22
41973471bf0Spatrick /// =>
42073471bf0Spatrick /// bl _OUTLINED_FUNCTION_EPILOG_x30x29x19x20x21x22
42173471bf0Spatrick ///
42273471bf0Spatrick /// 3. Without a helper
42373471bf0Spatrick /// HOM_Epilog x30, x29, x19, x20, x21, x22
42473471bf0Spatrick /// =>
42573471bf0Spatrick /// ldp x29, x30, [sp, #32]
42673471bf0Spatrick /// ldp x20, x19, [sp, #16]
42773471bf0Spatrick /// ldp x22, x21, [sp], #48
lowerEpilog(MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,MachineBasicBlock::iterator & NextMBBI)42873471bf0Spatrick bool AArch64LowerHomogeneousPE::lowerEpilog(
42973471bf0Spatrick MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
43073471bf0Spatrick MachineBasicBlock::iterator &NextMBBI) {
43173471bf0Spatrick auto &MF = *MBB.getParent();
43273471bf0Spatrick MachineInstr &MI = *MBBI;
43373471bf0Spatrick
43473471bf0Spatrick DebugLoc DL = MI.getDebugLoc();
43573471bf0Spatrick SmallVector<unsigned, 8> Regs;
43673471bf0Spatrick for (auto &MO : MI.operands())
43773471bf0Spatrick if (MO.isReg())
43873471bf0Spatrick Regs.push_back(MO.getReg());
43973471bf0Spatrick int Size = (int)Regs.size();
44073471bf0Spatrick if (Size == 0)
44173471bf0Spatrick return false;
44273471bf0Spatrick // Registers are in pair.
44373471bf0Spatrick assert(Size % 2 == 0);
44473471bf0Spatrick assert(MI.getOpcode() == AArch64::HOM_Epilog);
44573471bf0Spatrick
44673471bf0Spatrick auto Return = NextMBBI;
44773471bf0Spatrick if (shouldUseFrameHelper(MBB, NextMBBI, Regs, FrameHelperType::EpilogTail)) {
44873471bf0Spatrick // When MBB ends with a return, emit a tail-call to the epilog helper
44973471bf0Spatrick auto *EpilogTailHelper =
45073471bf0Spatrick getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::EpilogTail);
45173471bf0Spatrick BuildMI(MBB, MBBI, DL, TII->get(AArch64::TCRETURNdi))
45273471bf0Spatrick .addGlobalAddress(EpilogTailHelper)
45373471bf0Spatrick .addImm(0)
45473471bf0Spatrick .setMIFlag(MachineInstr::FrameDestroy)
45573471bf0Spatrick .copyImplicitOps(MI)
45673471bf0Spatrick .copyImplicitOps(*Return);
45773471bf0Spatrick NextMBBI = std::next(Return);
45873471bf0Spatrick Return->removeFromParent();
45973471bf0Spatrick } else if (shouldUseFrameHelper(MBB, NextMBBI, Regs,
46073471bf0Spatrick FrameHelperType::Epilog)) {
46173471bf0Spatrick // The default epilog helper case.
46273471bf0Spatrick auto *EpilogHelper =
46373471bf0Spatrick getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::Epilog);
46473471bf0Spatrick BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
46573471bf0Spatrick .addGlobalAddress(EpilogHelper)
46673471bf0Spatrick .setMIFlag(MachineInstr::FrameDestroy)
46773471bf0Spatrick .copyImplicitOps(MI);
46873471bf0Spatrick } else {
46973471bf0Spatrick // Fall back to no-helper.
47073471bf0Spatrick for (int I = 0; I < Size - 2; I += 2)
47173471bf0Spatrick emitLoad(MF, MBB, MBBI, *TII, Regs[I], Regs[I + 1], Size - I - 2, false);
47273471bf0Spatrick // Restore the last CSR with post-increment of SP.
47373471bf0Spatrick emitLoad(MF, MBB, MBBI, *TII, Regs[Size - 2], Regs[Size - 1], Size, true);
47473471bf0Spatrick }
47573471bf0Spatrick
47673471bf0Spatrick MBBI->removeFromParent();
47773471bf0Spatrick return true;
47873471bf0Spatrick }
47973471bf0Spatrick
48073471bf0Spatrick /// Lower a HOM_Prolog pseudo instruction into a helper call while
48173471bf0Spatrick /// creating the helper on demand. Or emit a sequence of stores in place when
48273471bf0Spatrick /// not using a helper call.
48373471bf0Spatrick ///
48473471bf0Spatrick /// 1. With a helper including frame-setup
48573471bf0Spatrick /// HOM_Prolog x30, x29, x19, x20, x21, x22, 32
48673471bf0Spatrick /// =>
48773471bf0Spatrick /// stp x29, x30, [sp, #-16]!
48873471bf0Spatrick /// bl _OUTLINED_FUNCTION_PROLOG_FRAME32_x30x29x19x20x21x22
48973471bf0Spatrick ///
49073471bf0Spatrick /// 2. With a helper
49173471bf0Spatrick /// HOM_Prolog x30, x29, x19, x20, x21, x22
49273471bf0Spatrick /// =>
49373471bf0Spatrick /// stp x29, x30, [sp, #-16]!
49473471bf0Spatrick /// bl _OUTLINED_FUNCTION_PROLOG_x30x29x19x20x21x22
49573471bf0Spatrick ///
49673471bf0Spatrick /// 3. Without a helper
49773471bf0Spatrick /// HOM_Prolog x30, x29, x19, x20, x21, x22
49873471bf0Spatrick /// =>
49973471bf0Spatrick /// stp x22, x21, [sp, #-48]!
50073471bf0Spatrick /// stp x20, x19, [sp, #16]
50173471bf0Spatrick /// stp x29, x30, [sp, #32]
lowerProlog(MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,MachineBasicBlock::iterator & NextMBBI)50273471bf0Spatrick bool AArch64LowerHomogeneousPE::lowerProlog(
50373471bf0Spatrick MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
50473471bf0Spatrick MachineBasicBlock::iterator &NextMBBI) {
50573471bf0Spatrick auto &MF = *MBB.getParent();
50673471bf0Spatrick MachineInstr &MI = *MBBI;
50773471bf0Spatrick
50873471bf0Spatrick DebugLoc DL = MI.getDebugLoc();
50973471bf0Spatrick SmallVector<unsigned, 8> Regs;
51073471bf0Spatrick int LRIdx = 0;
511*d415bd75Srobert std::optional<int> FpOffset;
51273471bf0Spatrick for (auto &MO : MI.operands()) {
51373471bf0Spatrick if (MO.isReg()) {
51473471bf0Spatrick if (MO.getReg() == AArch64::LR)
51573471bf0Spatrick LRIdx = Regs.size();
51673471bf0Spatrick Regs.push_back(MO.getReg());
51773471bf0Spatrick } else if (MO.isImm()) {
51873471bf0Spatrick FpOffset = MO.getImm();
51973471bf0Spatrick }
52073471bf0Spatrick }
52173471bf0Spatrick int Size = (int)Regs.size();
52273471bf0Spatrick if (Size == 0)
52373471bf0Spatrick return false;
52473471bf0Spatrick // Allow compact unwind case only for oww.
52573471bf0Spatrick assert(Size % 2 == 0);
52673471bf0Spatrick assert(MI.getOpcode() == AArch64::HOM_Prolog);
52773471bf0Spatrick
52873471bf0Spatrick if (FpOffset &&
52973471bf0Spatrick shouldUseFrameHelper(MBB, NextMBBI, Regs, FrameHelperType::PrologFrame)) {
53073471bf0Spatrick // FP/LR is stored at the top of stack before the prolog helper call.
53173471bf0Spatrick emitStore(MF, MBB, MBBI, *TII, AArch64::LR, AArch64::FP, -LRIdx - 2, true);
53273471bf0Spatrick auto *PrologFrameHelper = getOrCreateFrameHelper(
53373471bf0Spatrick M, MMI, Regs, FrameHelperType::PrologFrame, *FpOffset);
53473471bf0Spatrick BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
53573471bf0Spatrick .addGlobalAddress(PrologFrameHelper)
53673471bf0Spatrick .setMIFlag(MachineInstr::FrameSetup)
53773471bf0Spatrick .copyImplicitOps(MI)
53873471bf0Spatrick .addReg(AArch64::FP, RegState::Implicit | RegState::Define)
53973471bf0Spatrick .addReg(AArch64::SP, RegState::Implicit);
54073471bf0Spatrick } else if (!FpOffset && shouldUseFrameHelper(MBB, NextMBBI, Regs,
54173471bf0Spatrick FrameHelperType::Prolog)) {
54273471bf0Spatrick // FP/LR is stored at the top of stack before the prolog helper call.
54373471bf0Spatrick emitStore(MF, MBB, MBBI, *TII, AArch64::LR, AArch64::FP, -LRIdx - 2, true);
54473471bf0Spatrick auto *PrologHelper =
54573471bf0Spatrick getOrCreateFrameHelper(M, MMI, Regs, FrameHelperType::Prolog);
54673471bf0Spatrick BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL))
54773471bf0Spatrick .addGlobalAddress(PrologHelper)
54873471bf0Spatrick .setMIFlag(MachineInstr::FrameSetup)
54973471bf0Spatrick .copyImplicitOps(MI);
55073471bf0Spatrick } else {
55173471bf0Spatrick // Fall back to no-helper.
55273471bf0Spatrick emitStore(MF, MBB, MBBI, *TII, Regs[Size - 2], Regs[Size - 1], -Size, true);
55373471bf0Spatrick for (int I = Size - 3; I >= 0; I -= 2)
55473471bf0Spatrick emitStore(MF, MBB, MBBI, *TII, Regs[I - 1], Regs[I], Size - I - 1, false);
55573471bf0Spatrick if (FpOffset) {
55673471bf0Spatrick BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri))
55773471bf0Spatrick .addDef(AArch64::FP)
55873471bf0Spatrick .addUse(AArch64::SP)
55973471bf0Spatrick .addImm(*FpOffset)
56073471bf0Spatrick .addImm(0)
56173471bf0Spatrick .setMIFlag(MachineInstr::FrameSetup);
56273471bf0Spatrick }
56373471bf0Spatrick }
56473471bf0Spatrick
56573471bf0Spatrick MBBI->removeFromParent();
56673471bf0Spatrick return true;
56773471bf0Spatrick }
56873471bf0Spatrick
56973471bf0Spatrick /// Process each machine instruction
57073471bf0Spatrick /// @param MBB machine basic block
57173471bf0Spatrick /// @param MBBI current instruction iterator
57273471bf0Spatrick /// @param NextMBBI next instruction iterator which can be updated
57373471bf0Spatrick /// @return True when IR is changed.
runOnMI(MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,MachineBasicBlock::iterator & NextMBBI)57473471bf0Spatrick bool AArch64LowerHomogeneousPE::runOnMI(MachineBasicBlock &MBB,
57573471bf0Spatrick MachineBasicBlock::iterator MBBI,
57673471bf0Spatrick MachineBasicBlock::iterator &NextMBBI) {
57773471bf0Spatrick MachineInstr &MI = *MBBI;
57873471bf0Spatrick unsigned Opcode = MI.getOpcode();
57973471bf0Spatrick switch (Opcode) {
58073471bf0Spatrick default:
58173471bf0Spatrick break;
58273471bf0Spatrick case AArch64::HOM_Prolog:
58373471bf0Spatrick return lowerProlog(MBB, MBBI, NextMBBI);
58473471bf0Spatrick case AArch64::HOM_Epilog:
58573471bf0Spatrick return lowerEpilog(MBB, MBBI, NextMBBI);
58673471bf0Spatrick }
58773471bf0Spatrick return false;
58873471bf0Spatrick }
58973471bf0Spatrick
runOnMBB(MachineBasicBlock & MBB)59073471bf0Spatrick bool AArch64LowerHomogeneousPE::runOnMBB(MachineBasicBlock &MBB) {
59173471bf0Spatrick bool Modified = false;
59273471bf0Spatrick
59373471bf0Spatrick MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
59473471bf0Spatrick while (MBBI != E) {
59573471bf0Spatrick MachineBasicBlock::iterator NMBBI = std::next(MBBI);
59673471bf0Spatrick Modified |= runOnMI(MBB, MBBI, NMBBI);
59773471bf0Spatrick MBBI = NMBBI;
59873471bf0Spatrick }
59973471bf0Spatrick
60073471bf0Spatrick return Modified;
60173471bf0Spatrick }
60273471bf0Spatrick
runOnMachineFunction(MachineFunction & MF)60373471bf0Spatrick bool AArch64LowerHomogeneousPE::runOnMachineFunction(MachineFunction &MF) {
60473471bf0Spatrick TII = static_cast<const AArch64InstrInfo *>(MF.getSubtarget().getInstrInfo());
60573471bf0Spatrick
60673471bf0Spatrick bool Modified = false;
60773471bf0Spatrick for (auto &MBB : MF)
60873471bf0Spatrick Modified |= runOnMBB(MBB);
60973471bf0Spatrick return Modified;
61073471bf0Spatrick }
61173471bf0Spatrick
createAArch64LowerHomogeneousPrologEpilogPass()61273471bf0Spatrick ModulePass *llvm::createAArch64LowerHomogeneousPrologEpilogPass() {
61373471bf0Spatrick return new AArch64LowerHomogeneousPrologEpilog();
61473471bf0Spatrick }
615