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
17using namespace llvm;
18
19#define RISCV_PUSH_POP_OPT_NAME "RISC-V Zcmp Push/Pop optimization pass"
20
21namespace {
22struct RISCVPushPopOpt : public MachineFunctionPass {
23 static char ID;
24
25 RISCVPushPopOpt() : MachineFunctionPass(ID) {}
26
27 const RISCVInstrInfo *TII;
28 const TargetRegisterInfo *TRI;
29
30 // Track which register units have been modified and used.
31 LiveRegUnits ModifiedRegUnits, UsedRegUnits;
32
33 bool usePopRet(MachineBasicBlock::iterator &MBBI,
34 MachineBasicBlock::iterator &NextI, bool IsReturnZero);
35 bool adjustRetVal(MachineBasicBlock::iterator &MBBI);
36 bool runOnMachineFunction(MachineFunction &Fn) override;
37
38 StringRef getPassName() const override { return RISCV_PUSH_POP_OPT_NAME; }
39};
40
41char RISCVPushPopOpt::ID = 0;
42
43} // end of anonymous namespace
44
45INITIALIZE_PASS(RISCVPushPopOpt, "riscv-push-pop-opt", RISCV_PUSH_POP_OPT_NAME,
46 false, false)
47
48// Check if POP instruction was inserted into the MBB and return iterator to it.
49static MachineBasicBlock::iterator containsPop(MachineBasicBlock &MBB) {
50 for (MachineBasicBlock::iterator MBBI = MBB.begin(); MBBI != MBB.end();
51 MBBI = next_nodbg(It: MBBI, End: MBB.end()))
52 if (MBBI->getOpcode() == RISCV::CM_POP)
53 return MBBI;
54
55 return MBB.end();
56}
57
58bool RISCVPushPopOpt::usePopRet(MachineBasicBlock::iterator &MBBI,
59 MachineBasicBlock::iterator &NextI,
60 bool IsReturnZero) {
61 // Since Pseudo instruction lowering happen later in the pipeline,
62 // this will detect all ret instruction.
63 DebugLoc DL = NextI->getDebugLoc();
64 unsigned Opc = IsReturnZero ? RISCV::CM_POPRETZ : RISCV::CM_POPRET;
65 MachineInstrBuilder PopRetBuilder =
66 BuildMI(BB&: *NextI->getParent(), I: NextI, MIMD: DL, MCID: TII->get(Opcode: Opc))
67 .add(MO: MBBI->getOperand(i: 0))
68 .add(MO: MBBI->getOperand(i: 1));
69
70 // Copy over the variable implicit uses and defs from the CM_POP. They depend
71 // on what register list has been picked during frame lowering.
72 const MCInstrDesc &PopDesc = MBBI->getDesc();
73 unsigned FirstNonDeclaredOp = PopDesc.getNumOperands() +
74 PopDesc.NumImplicitUses +
75 PopDesc.NumImplicitDefs;
76 for (unsigned i = FirstNonDeclaredOp; i < MBBI->getNumOperands(); ++i)
77 PopRetBuilder.add(MO: MBBI->getOperand(i));
78
79 MBBI->eraseFromParent();
80 NextI->eraseFromParent();
81 return true;
82}
83
84// Search for last assignment to a0 and if possible use ret_val slot of POP to
85// store return value.
86bool RISCVPushPopOpt::adjustRetVal(MachineBasicBlock::iterator &MBBI) {
87 MachineBasicBlock::reverse_iterator RE = MBBI->getParent()->rend();
88 // Track which register units have been modified and used between the POP
89 // insn and the last assignment to register a0.
90 ModifiedRegUnits.clear();
91 UsedRegUnits.clear();
92 // Since POP instruction is in Epilogue no normal instructions will follow
93 // after it. Therefore search only previous ones to find the return value.
94 for (MachineBasicBlock::reverse_iterator I =
95 next_nodbg(It: MBBI.getReverse(), End: RE);
96 I != RE; I = next_nodbg(It: I, End: RE)) {
97 MachineInstr &MI = *I;
98 if (auto OperandPair = TII->isCopyInstrImpl(MI)) {
99 Register DestReg = OperandPair->Destination->getReg();
100 Register Source = OperandPair->Source->getReg();
101 if (DestReg == RISCV::X10 && Source == RISCV::X0) {
102 MI.removeFromParent();
103 return true;
104 }
105 }
106 // Update modified / used register units.
107 LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
108 // If a0 was modified or used, there is no possibility
109 // of using ret_val slot of POP instruction.
110 if (!ModifiedRegUnits.available(Reg: RISCV::X10) ||
111 !UsedRegUnits.available(Reg: RISCV::X10))
112 return false;
113 }
114 return false;
115}
116
117bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) {
118 if (skipFunction(F: Fn.getFunction()))
119 return false;
120
121 // If Zcmp extension is not supported, abort.
122 const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>();
123 if (!Subtarget->hasStdExtZcmp())
124 return false;
125
126 // If frame pointer elimination has been disabled, abort to avoid breaking the
127 // ABI.
128 if (Fn.getTarget().Options.DisableFramePointerElim(MF: Fn))
129 return false;
130
131 TII = Subtarget->getInstrInfo();
132 TRI = Subtarget->getRegisterInfo();
133 // Resize the modified and used register unit trackers. We do this once
134 // per function and then clear the register units each time we determine
135 // correct return value for the POP.
136 ModifiedRegUnits.init(TRI: *TRI);
137 UsedRegUnits.init(TRI: *TRI);
138 bool Modified = false;
139 for (auto &MBB : Fn) {
140 MachineBasicBlock::iterator MBBI = containsPop(MBB);
141 MachineBasicBlock::iterator NextI = next_nodbg(It: MBBI, End: MBB.end());
142 if (MBBI != MBB.end() && NextI != MBB.end() &&
143 NextI->getOpcode() == RISCV::PseudoRET)
144 Modified |= usePopRet(MBBI, NextI, IsReturnZero: adjustRetVal(MBBI));
145 }
146 return Modified;
147}
148
149/// createRISCVPushPopOptimizationPass - returns an instance of the
150/// Push/Pop optimization pass.
151FunctionPass *llvm::createRISCVPushPopOptimizationPass() {
152 return new RISCVPushPopOpt();
153}
154