1//===-- RISCVQCRelaxMarking.cpp - Mark Instructions for QC Relaxations ----===//
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 pass adds access tags to some instructions which are used by the
10// assembler to emit marker relocations, which enable some code-size relaxations
11// for Xqcilo/Xqcili.
12//
13// The pass is looking for the following sequences:
14//
15// $dst1 = QC_E_LI sym
16// $dst2 = Load killed $dst1, 0
17//
18// $dst1 = QC_E_LI sym
19// Store $dst2, killed $dst1, 0
20//
21// In either case, the Load/Store is modified to become a
22// PseudoQCAccess<Load/Store>, with an additional operand that represents the
23// accessed symbolic address, which will become the contents of a
24// `R_RISCV_QC_ACCESS_*` relocation on the emitted instruction.
25//
26// FIXME: The intention is this pass does not change the size of any
27// instructions, but right now it has to do instruction compression as the
28// CompressPat infrastructure cannot handle compressing the `%qc.access(...)`
29// operand. Symbolic operands are not usually compressible, but this one is as
30// we have relocations for both 32-bit and 16-bit instructions (and the
31// relocation does not care about the fields of the instruction).
32
33#include "RISCV.h"
34#include "RISCVSubtarget.h"
35#include "llvm/ADT/STLExtras.h"
36#include "llvm/ADT/Statistic.h"
37#include "llvm/CodeGen/Passes.h"
38#include "llvm/CodeGen/RegisterScavenging.h"
39#include "llvm/MC/TargetRegistry.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/ErrorHandling.h"
42
43using namespace llvm;
44
45#define DEBUG_TYPE "riscv-qc-relax-marking"
46#define RISCV_QC_RELAX_MARKING_NAME "RISC-V QC Relaxation Marking"
47
48STATISTIC(NumMarked, "Number of Loads/Stores Marked");
49
50namespace {
51
52struct RISCVQCRelaxMarking : public MachineFunctionPass {
53 static char ID;
54
55 bool runOnMachineFunction(MachineFunction &) override;
56
57 RISCVQCRelaxMarking() : MachineFunctionPass(ID) {}
58
59 StringRef getPassName() const override { return RISCV_QC_RELAX_MARKING_NAME; }
60};
61
62} // end namespace
63
64char RISCVQCRelaxMarking::ID = 0;
65
66INITIALIZE_PASS(RISCVQCRelaxMarking, DEBUG_TYPE, RISCV_QC_RELAX_MARKING_NAME,
67 false, false)
68
69/// Returns an instance of the Make Compressible Optimization pass.
70FunctionPass *llvm::createRISCVQCRelaxMarkingPass() {
71 return new RISCVQCRelaxMarking();
72}
73
74static bool isUImm7LSB000(const MachineOperand &MO) {
75 return MO.isImm() && isShiftedUInt<4, 3>(x: MO.getImm());
76}
77
78static bool isUImm2LSB0(const MachineOperand &MO) {
79 return MO.isImm() && isShiftedUInt<1, 1>(x: MO.getImm());
80}
81
82static bool isUImm2(const MachineOperand &MO) {
83 return MO.isImm() && isUInt<2>(x: MO.getImm());
84}
85
86static bool isGPRC(const MachineOperand &MO) {
87 return RISCV::GPRCRegClass.contains(Reg: MO.getReg());
88}
89
90static unsigned getQCMarkedOpcode(const MachineInstr &MI,
91 const RISCVSubtarget &STI) {
92 switch (MI.getOpcode()) {
93 case RISCV::LB:
94 // No c.lb
95 return RISCV::PseudoQCAccessLB;
96 case RISCV::LBU:
97 if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) &&
98 isGPRC(MO: MI.getOperand(i: 1)) && isUImm2(MO: MI.getOperand(i: 2)))
99 return RISCV::PseudoQCAccessC_LBU;
100 return RISCV::PseudoQCAccessLBU;
101 case RISCV::LH:
102 if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) &&
103 isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2)))
104 return RISCV::PseudoQCAccessC_LH;
105 return RISCV::PseudoQCAccessLH;
106 case RISCV::LHU:
107 if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) &&
108 isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2)))
109 return RISCV::PseudoQCAccessC_LHU;
110 return RISCV::PseudoQCAccessLHU;
111 case RISCV::LW:
112 if (STI.hasStdExtZca() && isGPRC(MO: MI.getOperand(i: 0)) &&
113 isGPRC(MO: MI.getOperand(i: 1)) && isUImm7LSB000(MO: MI.getOperand(i: 2)))
114 return RISCV::PseudoQCAccessC_LW;
115 return RISCV::PseudoQCAccessLW;
116 case RISCV::SB:
117 if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) &&
118 isGPRC(MO: MI.getOperand(i: 1)) && isUImm2(MO: MI.getOperand(i: 2)))
119 return RISCV::PseudoQCAccessC_SB;
120 return RISCV::PseudoQCAccessSB;
121 case RISCV::SH:
122 if (STI.hasStdExtZcb() && isGPRC(MO: MI.getOperand(i: 0)) &&
123 isGPRC(MO: MI.getOperand(i: 1)) && isUImm2LSB0(MO: MI.getOperand(i: 2)))
124 return RISCV::PseudoQCAccessC_SH;
125 return RISCV::PseudoQCAccessSH;
126 case RISCV::SW:
127 if (STI.hasStdExtZca() && isGPRC(MO: MI.getOperand(i: 0)) &&
128 isGPRC(MO: MI.getOperand(i: 1)) && isUImm7LSB000(MO: MI.getOperand(i: 2)))
129 return RISCV::PseudoQCAccessC_SW;
130 return RISCV::PseudoQCAccessSW;
131 default:
132 reportFatalInternalError(
133 reason: "Unhandled Opcode: No Corresponding Marked Opcode");
134 }
135}
136
137bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) {
138 if (skipFunction(F: MF.getFunction()))
139 return false;
140
141 // This is only relevant for QC.E.LI with a symbol, which we only use in the
142 // small code model.
143 if (MF.getTarget().getCodeModel() != CodeModel::Small)
144 return false;
145
146 auto &STI = MF.getSubtarget<RISCVSubtarget>();
147 // We need QC.E.LI instructions to perform this optimisation, which needs
148 // 32-bit and Xqcili. The markers are only needed when linker relaxations are
149 // enabled.
150 if (STI.is64Bit() || !STI.hasVendorXqcili() || !STI.enableLinkerRelax())
151 return false;
152
153 const RISCVInstrInfo *TII = STI.getInstrInfo();
154
155 bool Changed = false;
156 for (MachineBasicBlock &MBB : MF) {
157 for (auto MI = MBB.begin(), E = MBB.end(); MI != E; MI++) {
158 auto NextMI = std::next(x: MI);
159 if (NextMI == E)
160 break;
161
162 // Looking for QC.E.LI followed by a load or store
163 if (MI->getOpcode() != RISCV::QC_E_LI ||
164 !(RISCVInstrInfo::isBaseLoad(MI: *NextMI) ||
165 RISCVInstrInfo::isBaseStore(MI: *NextMI)))
166 continue;
167
168 LLVM_DEBUG(dbgs() << "Found QC_E_LI " << *MI);
169 LLVM_DEBUG(dbgs() << "Followed by Load/Store " << *NextMI);
170
171 if (MI->getOperand(i: 0).getReg() != NextMI->getOperand(i: 1).getReg())
172 continue;
173 if (!NextMI->getOperand(i: 1).isKill())
174 continue;
175
176 // This is unsafe for stores where the access address is being stored.
177 if (RISCVInstrInfo::isBaseStore(MI: *NextMI) &&
178 MI->getOperand(i: 0).getReg() == NextMI->getOperand(i: 0).getReg())
179 continue;
180
181 MachineOperand &SymOp = MI->getOperand(i: 1);
182 if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() &&
183 !SymOp.isCPI())
184 continue;
185
186 unsigned NewOpc = getQCMarkedOpcode(MI: *NextMI, STI);
187 LLVM_DEBUG(dbgs() << "Load/Store " << TII->getName(NextMI->getOpcode())
188 << " will become " << TII->getName(NewOpc) << "\n");
189 MachineInstrBuilder MIB =
190 BuildMI(BB&: MBB, I: NextMI, MIMD: NextMI->getDebugLoc(), MCID: TII->get(Opcode: NewOpc))
191 .add(MO: NextMI->getOperand(i: 0))
192 .add(MO: NextMI->getOperand(i: 1))
193 .add(MO: NextMI->getOperand(i: 2))
194 .cloneMemRefs(OtherMI: *NextMI);
195
196 if (SymOp.isSymbol()) {
197 MIB.addExternalSymbol(FnName: SymOp.getSymbolName(), TargetFlags: RISCVII::MO_QC_ACCESS);
198 } else if (SymOp.isGlobal()) {
199 MIB.addGlobalAddress(GV: SymOp.getGlobal(), Offset: SymOp.getOffset(),
200 TargetFlags: RISCVII::MO_QC_ACCESS);
201 } else if (SymOp.isMCSymbol()) {
202 MachineOperand MO = MachineOperand::CreateMCSymbol(
203 Sym: SymOp.getMCSymbol(), TargetFlags: RISCVII::MO_QC_ACCESS);
204 MO.setOffset(SymOp.getOffset());
205 MIB.add(MO);
206 } else if (SymOp.isCPI()) {
207 MIB.addConstantPoolIndex(Idx: SymOp.getIndex(), Offset: SymOp.getOffset(),
208 TargetFlags: RISCVII::MO_QC_ACCESS);
209 } else {
210 reportFatalInternalError(reason: "Unhandled SymOp Kind");
211 }
212
213 NextMI->removeFromParent();
214 NumMarked++;
215 Changed |= true;
216 }
217 }
218
219 return Changed;
220}
221