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