xref: /openbsd-src/gnu/llvm/llvm/lib/Target/AArch64/AArch64LowerHomogeneousPrologEpilog.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
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