xref: /freebsd/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1*06c3fb27SDimitry Andric //===------- RISCVPushPopOptimizer.cpp - RISCV Push/Pop opt. pass ---------===//
2*06c3fb27SDimitry Andric //
3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*06c3fb27SDimitry Andric //
7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
8*06c3fb27SDimitry Andric //
9*06c3fb27SDimitry Andric // This file contains a pass that modifies PUSH/POP instructions from Zca
10*06c3fb27SDimitry Andric // standard to use their non prolog/epilog related functionalities
11*06c3fb27SDimitry Andric // and generates POPRET instruction.
12*06c3fb27SDimitry Andric //
13*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
14*06c3fb27SDimitry Andric 
15*06c3fb27SDimitry Andric #include "RISCVInstrInfo.h"
16*06c3fb27SDimitry Andric #include "RISCVMachineFunctionInfo.h"
17*06c3fb27SDimitry Andric 
18*06c3fb27SDimitry Andric using namespace llvm;
19*06c3fb27SDimitry Andric 
20*06c3fb27SDimitry Andric #define RISCV_PUSH_POP_OPT_NAME "RISC-V Zcmp Push/Pop optimization pass"
21*06c3fb27SDimitry Andric 
22*06c3fb27SDimitry Andric namespace {
23*06c3fb27SDimitry Andric struct RISCVPushPopOpt : public MachineFunctionPass {
24*06c3fb27SDimitry Andric   static char ID;
25*06c3fb27SDimitry Andric 
26*06c3fb27SDimitry Andric   RISCVPushPopOpt() : MachineFunctionPass(ID) {
27*06c3fb27SDimitry Andric     initializeRISCVPushPopOptPass(*PassRegistry::getPassRegistry());
28*06c3fb27SDimitry Andric   }
29*06c3fb27SDimitry Andric 
30*06c3fb27SDimitry Andric   const RISCVInstrInfo *TII;
31*06c3fb27SDimitry Andric   const TargetRegisterInfo *TRI;
32*06c3fb27SDimitry Andric 
33*06c3fb27SDimitry Andric   // Track which register units have been modified and used.
34*06c3fb27SDimitry Andric   LiveRegUnits ModifiedRegUnits, UsedRegUnits;
35*06c3fb27SDimitry Andric 
36*06c3fb27SDimitry Andric   bool usePopRet(MachineBasicBlock::iterator &MBBI,
37*06c3fb27SDimitry Andric                  MachineBasicBlock::iterator &NextI, bool IsReturnZero);
38*06c3fb27SDimitry Andric   bool adjustRetVal(MachineBasicBlock::iterator &MBBI);
39*06c3fb27SDimitry Andric   bool runOnMachineFunction(MachineFunction &Fn) override;
40*06c3fb27SDimitry Andric 
41*06c3fb27SDimitry Andric   StringRef getPassName() const override { return RISCV_PUSH_POP_OPT_NAME; }
42*06c3fb27SDimitry Andric };
43*06c3fb27SDimitry Andric 
44*06c3fb27SDimitry Andric char RISCVPushPopOpt::ID = 0;
45*06c3fb27SDimitry Andric 
46*06c3fb27SDimitry Andric } // end of anonymous namespace
47*06c3fb27SDimitry Andric 
48*06c3fb27SDimitry Andric INITIALIZE_PASS(RISCVPushPopOpt, "riscv-push-pop-opt", RISCV_PUSH_POP_OPT_NAME,
49*06c3fb27SDimitry Andric                 false, false)
50*06c3fb27SDimitry Andric 
51*06c3fb27SDimitry Andric // Check if POP instruction was inserted into the MBB and return iterator to it.
52*06c3fb27SDimitry Andric static MachineBasicBlock::iterator containsPop(MachineBasicBlock &MBB) {
53*06c3fb27SDimitry Andric   for (MachineBasicBlock::iterator MBBI = MBB.begin(); MBBI != MBB.end();
54*06c3fb27SDimitry Andric        MBBI = next_nodbg(MBBI, MBB.end()))
55*06c3fb27SDimitry Andric     if (MBBI->getOpcode() == RISCV::CM_POP)
56*06c3fb27SDimitry Andric       return MBBI;
57*06c3fb27SDimitry Andric 
58*06c3fb27SDimitry Andric   return MBB.end();
59*06c3fb27SDimitry Andric }
60*06c3fb27SDimitry Andric 
61*06c3fb27SDimitry Andric bool RISCVPushPopOpt::usePopRet(MachineBasicBlock::iterator &MBBI,
62*06c3fb27SDimitry Andric                                 MachineBasicBlock::iterator &NextI,
63*06c3fb27SDimitry Andric                                 bool IsReturnZero) {
64*06c3fb27SDimitry Andric   // Since Pseudo instruction lowering happen later in the pipeline,
65*06c3fb27SDimitry Andric   // this will detect all ret instruction.
66*06c3fb27SDimitry Andric   DebugLoc DL = NextI->getDebugLoc();
67*06c3fb27SDimitry Andric   unsigned Opc = IsReturnZero ? RISCV::CM_POPRETZ : RISCV::CM_POPRET;
68*06c3fb27SDimitry Andric   BuildMI(*NextI->getParent(), NextI, DL, TII->get(Opc))
69*06c3fb27SDimitry Andric       .add(MBBI->getOperand(0))
70*06c3fb27SDimitry Andric       .add(MBBI->getOperand(1));
71*06c3fb27SDimitry Andric 
72*06c3fb27SDimitry Andric   MBBI->eraseFromParent();
73*06c3fb27SDimitry Andric   NextI->eraseFromParent();
74*06c3fb27SDimitry Andric   return true;
75*06c3fb27SDimitry Andric }
76*06c3fb27SDimitry Andric 
77*06c3fb27SDimitry Andric // Search for last assignment to a0 and if possible use ret_val slot of POP to
78*06c3fb27SDimitry Andric // store return value.
79*06c3fb27SDimitry Andric bool RISCVPushPopOpt::adjustRetVal(MachineBasicBlock::iterator &MBBI) {
80*06c3fb27SDimitry Andric   MachineBasicBlock::reverse_iterator RE = MBBI->getParent()->rend();
81*06c3fb27SDimitry Andric   // Track which register units have been modified and used between the POP
82*06c3fb27SDimitry Andric   // insn and the last assignment to register a0.
83*06c3fb27SDimitry Andric   ModifiedRegUnits.clear();
84*06c3fb27SDimitry Andric   UsedRegUnits.clear();
85*06c3fb27SDimitry Andric   // Since POP instruction is in Epilogue no normal instructions will follow
86*06c3fb27SDimitry Andric   // after it. Therefore search only previous ones to find the return value.
87*06c3fb27SDimitry Andric   for (MachineBasicBlock::reverse_iterator I =
88*06c3fb27SDimitry Andric            next_nodbg(MBBI.getReverse(), RE);
89*06c3fb27SDimitry Andric        I != RE; I = next_nodbg(I, RE)) {
90*06c3fb27SDimitry Andric     MachineInstr &MI = *I;
91*06c3fb27SDimitry Andric     if (auto OperandPair = TII->isCopyInstrImpl(MI)) {
92*06c3fb27SDimitry Andric       Register DestReg = OperandPair->Destination->getReg();
93*06c3fb27SDimitry Andric       Register Source = OperandPair->Source->getReg();
94*06c3fb27SDimitry Andric       if (DestReg == RISCV::X10 && Source == RISCV::X0) {
95*06c3fb27SDimitry Andric         MI.removeFromParent();
96*06c3fb27SDimitry Andric         return true;
97*06c3fb27SDimitry Andric       }
98*06c3fb27SDimitry Andric     }
99*06c3fb27SDimitry Andric     // Update modified / used register units.
100*06c3fb27SDimitry Andric     LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
101*06c3fb27SDimitry Andric     // If a0 was modified or used, there is no possibility
102*06c3fb27SDimitry Andric     // of using ret_val slot of POP instruction.
103*06c3fb27SDimitry Andric     if (!ModifiedRegUnits.available(RISCV::X10) ||
104*06c3fb27SDimitry Andric         !UsedRegUnits.available(RISCV::X10))
105*06c3fb27SDimitry Andric       return false;
106*06c3fb27SDimitry Andric   }
107*06c3fb27SDimitry Andric   return false;
108*06c3fb27SDimitry Andric }
109*06c3fb27SDimitry Andric 
110*06c3fb27SDimitry Andric bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) {
111*06c3fb27SDimitry Andric   if (skipFunction(Fn.getFunction()))
112*06c3fb27SDimitry Andric     return false;
113*06c3fb27SDimitry Andric 
114*06c3fb27SDimitry Andric   // If Zcmp extension is not supported, abort.
115*06c3fb27SDimitry Andric   const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>();
116*06c3fb27SDimitry Andric   if (!Subtarget->hasStdExtZcmp())
117*06c3fb27SDimitry Andric     return false;
118*06c3fb27SDimitry Andric 
119*06c3fb27SDimitry Andric   // If frame pointer elimination has been disabled, abort to avoid breaking the
120*06c3fb27SDimitry Andric   // ABI.
121*06c3fb27SDimitry Andric   if (Fn.getTarget().Options.DisableFramePointerElim(Fn))
122*06c3fb27SDimitry Andric     return false;
123*06c3fb27SDimitry Andric 
124*06c3fb27SDimitry Andric   TII = Subtarget->getInstrInfo();
125*06c3fb27SDimitry Andric   TRI = Subtarget->getRegisterInfo();
126*06c3fb27SDimitry Andric   // Resize the modified and used register unit trackers.  We do this once
127*06c3fb27SDimitry Andric   // per function and then clear the register units each time we determine
128*06c3fb27SDimitry Andric   // correct return value for the POP.
129*06c3fb27SDimitry Andric   ModifiedRegUnits.init(*TRI);
130*06c3fb27SDimitry Andric   UsedRegUnits.init(*TRI);
131*06c3fb27SDimitry Andric   bool Modified = false;
132*06c3fb27SDimitry Andric   for (auto &MBB : Fn) {
133*06c3fb27SDimitry Andric     MachineBasicBlock::iterator MBBI = containsPop(MBB);
134*06c3fb27SDimitry Andric     MachineBasicBlock::iterator NextI = next_nodbg(MBBI, MBB.end());
135*06c3fb27SDimitry Andric     if (MBBI != MBB.end() && NextI->getOpcode() == RISCV::PseudoRET)
136*06c3fb27SDimitry Andric       Modified |= usePopRet(MBBI, NextI, adjustRetVal(MBBI));
137*06c3fb27SDimitry Andric   }
138*06c3fb27SDimitry Andric   return Modified;
139*06c3fb27SDimitry Andric }
140*06c3fb27SDimitry Andric 
141*06c3fb27SDimitry Andric /// createRISCVPushPopOptimizationPass - returns an instance of the
142*06c3fb27SDimitry Andric /// Push/Pop optimization pass.
143*06c3fb27SDimitry Andric FunctionPass *llvm::createRISCVPushPopOptimizationPass() {
144*06c3fb27SDimitry Andric   return new RISCVPushPopOpt();
145*06c3fb27SDimitry Andric }
146