xref: /freebsd/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVPushPopOptimizer.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
1 //===------- RISCVPushPopOptimizer.cpp - RISC-V 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 replaces Zcmp POP instructions with
10 // POPRET[Z] where possible.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "RISCVInstrInfo.h"
15 #include "RISCVMachineFunctionInfo.h"
16 #include "llvm/CodeGen/MachineInstr.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 
28   const RISCVInstrInfo *TII;
29   const TargetRegisterInfo *TRI;
30 
31   // Track which register units have been modified and used.
32   LiveRegUnits ModifiedRegUnits, UsedRegUnits;
33 
34   bool usePopRet(MachineBasicBlock::iterator &MBBI,
35                  MachineBasicBlock::iterator &NextI, bool IsReturnZero);
36   bool adjustRetVal(MachineBasicBlock::iterator &MBBI);
37   bool runOnMachineFunction(MachineFunction &Fn) override;
38 
39   StringRef getPassName() const override { return RISCV_PUSH_POP_OPT_NAME; }
40 };
41 
42 char RISCVPushPopOpt::ID = 0;
43 
44 } // end of anonymous namespace
45 
46 INITIALIZE_PASS(RISCVPushPopOpt, "riscv-push-pop-opt", RISCV_PUSH_POP_OPT_NAME,
47                 false, false)
48 
49 static bool isPop(unsigned Opcode) {
50   switch (Opcode) {
51   case RISCV::CM_POP:
52   case RISCV::QC_CM_POP:
53     return true;
54   default:
55     return false;
56   }
57 }
58 
59 static unsigned getPopRetOpcode(unsigned PopOpcode, bool IsReturnZero) {
60   assert(isPop(PopOpcode) && "Unexpected Pop Opcode");
61 
62   switch (PopOpcode) {
63   case RISCV::CM_POP:
64     return IsReturnZero ? RISCV::CM_POPRETZ : RISCV::CM_POPRET;
65   case RISCV::QC_CM_POP:
66     return IsReturnZero ? RISCV::QC_CM_POPRETZ : RISCV::QC_CM_POPRET;
67   default:
68     llvm_unreachable("Unhandled Pop Opcode");
69   }
70 }
71 
72 bool RISCVPushPopOpt::usePopRet(MachineBasicBlock::iterator &MBBI,
73                                 MachineBasicBlock::iterator &NextI,
74                                 bool IsReturnZero) {
75   // Since Pseudo instruction lowering happen later in the pipeline,
76   // this will detect all ret instruction.
77   DebugLoc DL = NextI->getDebugLoc();
78   unsigned Opc = getPopRetOpcode(MBBI->getOpcode(), IsReturnZero);
79   MachineInstrBuilder PopRetBuilder =
80       BuildMI(*NextI->getParent(), NextI, DL, TII->get(Opc))
81           .add(MBBI->getOperand(0))
82           .add(MBBI->getOperand(1))
83           .setMIFlag(MachineInstr::FrameDestroy);
84 
85   // Copy over the variable implicit uses and defs from the CM_POP. They depend
86   // on what register list has been picked during frame lowering.
87   const MCInstrDesc &PopDesc = MBBI->getDesc();
88   unsigned FirstNonDeclaredOp = PopDesc.getNumOperands() +
89                                 PopDesc.NumImplicitUses +
90                                 PopDesc.NumImplicitDefs;
91   for (unsigned i = FirstNonDeclaredOp; i < MBBI->getNumOperands(); ++i)
92     PopRetBuilder.add(MBBI->getOperand(i));
93 
94   MBBI->eraseFromParent();
95   NextI->eraseFromParent();
96   return true;
97 }
98 
99 // Search for last assignment to a0 and if possible use ret_val slot of POP to
100 // store return value.
101 bool RISCVPushPopOpt::adjustRetVal(MachineBasicBlock::iterator &MBBI) {
102   MachineBasicBlock::reverse_iterator RE = MBBI->getParent()->rend();
103   // Track which register units have been modified and used between the POP
104   // insn and the last assignment to register a0.
105   ModifiedRegUnits.clear();
106   UsedRegUnits.clear();
107   // Since POP instruction is in Epilogue no normal instructions will follow
108   // after it. Therefore search only previous ones to find the return value.
109   for (MachineBasicBlock::reverse_iterator I =
110            next_nodbg(MBBI.getReverse(), RE);
111        I != RE; I = next_nodbg(I, RE)) {
112     MachineInstr &MI = *I;
113     if (auto OperandPair = TII->isCopyInstrImpl(MI)) {
114       Register DestReg = OperandPair->Destination->getReg();
115       Register Source = OperandPair->Source->getReg();
116       if (DestReg == RISCV::X10 && Source == RISCV::X0) {
117         MI.removeFromParent();
118         return true;
119       }
120     }
121     // Update modified / used register units.
122     LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
123     // If a0 was modified or used, there is no possibility
124     // of using ret_val slot of POP instruction.
125     if (!ModifiedRegUnits.available(RISCV::X10) ||
126         !UsedRegUnits.available(RISCV::X10))
127       return false;
128   }
129   return false;
130 }
131 
132 bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) {
133   if (skipFunction(Fn.getFunction()))
134     return false;
135 
136   // If Zcmp extension is not supported, abort.
137   const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>();
138   if (!Subtarget->hasStdExtZcmp() && !Subtarget->hasVendorXqccmp())
139     return false;
140 
141   TII = Subtarget->getInstrInfo();
142   TRI = Subtarget->getRegisterInfo();
143 
144   // Resize the modified and used register unit trackers.  We do this once
145   // per function and then clear the register units each time we determine
146   // correct return value for the POP.
147   ModifiedRegUnits.init(*TRI);
148   UsedRegUnits.init(*TRI);
149 
150   bool Modified = false;
151   for (auto &MBB : Fn) {
152     // RET should be the only terminator.
153     auto RetMBBI = MBB.getFirstTerminator();
154     if (RetMBBI == MBB.end() || RetMBBI->getOpcode() != RISCV::PseudoRET ||
155         RetMBBI == MBB.begin())
156       continue;
157 
158     // The previous instruction should be a POP.
159     auto PopMBBI = prev_nodbg(RetMBBI, MBB.begin());
160     if (isPop(PopMBBI->getOpcode()) &&
161         PopMBBI->getFlag(MachineInstr::FrameDestroy))
162       Modified |= usePopRet(PopMBBI, RetMBBI, adjustRetVal(PopMBBI));
163   }
164 
165   return Modified;
166 }
167 
168 /// createRISCVPushPopOptimizationPass - returns an instance of the
169 /// Push/Pop optimization pass.
170 FunctionPass *llvm::createRISCVPushPopOptimizationPass() {
171   return new RISCVPushPopOpt();
172 }
173