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