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