1//===------------------- RISCVCustomBehaviour.cpp ---------------*-C++ -* -===//
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/// \file
9///
10/// This file implements methods from the RISCVCustomBehaviour class.
11///
12//===----------------------------------------------------------------------===//
13
14#include "RISCVCustomBehaviour.h"
15#include "MCTargetDesc/RISCVMCTargetDesc.h"
16#include "RISCV.h"
17#include "TargetInfo/RISCVTargetInfo.h"
18#include "llvm/MC/TargetRegistry.h"
19#include "llvm/Support/Compiler.h"
20#include "llvm/Support/Debug.h"
21#include "llvm/Support/DebugLog.h"
22
23#define DEBUG_TYPE "llvm-mca-riscv-custombehaviour"
24
25namespace llvm::RISCV {
26struct VXMemOpInfo {
27 unsigned Log2IdxEEW : 3;
28 unsigned IsOrdered : 1;
29 unsigned IsStore : 1;
30 unsigned NFields : 4;
31 unsigned BaseInstr;
32};
33
34#define GET_RISCVBaseVXMemOpTable_IMPL
35#include "RISCVGenSearchableTables.inc"
36} // namespace llvm::RISCV
37
38namespace llvm {
39namespace mca {
40
41const llvm::StringRef RISCVLMULInstrument::DESC_NAME = "RISCV-LMUL";
42
43bool RISCVLMULInstrument::isDataValid(llvm::StringRef Data) {
44 // Return true if not one of the valid LMUL strings
45 return StringSwitch<bool>(Data)
46 .Cases(CaseStrings: {"M1", "M2", "M4", "M8", "MF2", "MF4", "MF8"}, Value: true)
47 .Default(Value: false);
48}
49
50uint8_t RISCVLMULInstrument::getLMUL() const {
51 // assertion prevents us from needing llvm_unreachable in the StringSwitch
52 // below
53 assert(isDataValid(getData()) &&
54 "Cannot get LMUL because invalid Data value");
55 // These are the LMUL values that are used in RISC-V tablegen
56 return StringSwitch<uint8_t>(getData())
57 .Case(S: "M1", Value: 0b000)
58 .Case(S: "M2", Value: 0b001)
59 .Case(S: "M4", Value: 0b010)
60 .Case(S: "M8", Value: 0b011)
61 .Case(S: "MF2", Value: 0b111)
62 .Case(S: "MF4", Value: 0b110)
63 .Case(S: "MF8", Value: 0b101);
64}
65
66const llvm::StringRef RISCVSEWInstrument::DESC_NAME = "RISCV-SEW";
67
68bool RISCVSEWInstrument::isDataValid(llvm::StringRef Data) {
69 // Return true if not one of the valid SEW strings
70 return StringSwitch<bool>(Data)
71 .Cases(CaseStrings: {"E8", "E16", "E32", "E64"}, Value: true)
72 .Default(Value: false);
73}
74
75uint8_t RISCVSEWInstrument::getSEW() const {
76 // assertion prevents us from needing llvm_unreachable in the StringSwitch
77 // below
78 assert(isDataValid(getData()) && "Cannot get SEW because invalid Data value");
79 // These are the LMUL values that are used in RISC-V tablegen
80 return StringSwitch<uint8_t>(getData())
81 .Case(S: "E8", Value: 8)
82 .Case(S: "E16", Value: 16)
83 .Case(S: "E32", Value: 32)
84 .Case(S: "E64", Value: 64);
85}
86
87bool RISCVInstrumentManager::supportsInstrumentType(
88 llvm::StringRef Type) const {
89 return Type == RISCVLMULInstrument::DESC_NAME ||
90 Type == RISCVSEWInstrument::DESC_NAME ||
91 InstrumentManager::supportsInstrumentType(Type);
92}
93
94UniqueInstrument
95RISCVInstrumentManager::createInstrument(llvm::StringRef Desc,
96 llvm::StringRef Data) {
97 if (Desc == RISCVLMULInstrument::DESC_NAME) {
98 if (!RISCVLMULInstrument::isDataValid(Data)) {
99 LDBG() << "RVCB: Bad data for instrument kind " << Desc << ": " << Data
100 << '\n';
101 return nullptr;
102 }
103 return std::make_unique<RISCVLMULInstrument>(args&: Data);
104 }
105
106 if (Desc == RISCVSEWInstrument::DESC_NAME) {
107 if (!RISCVSEWInstrument::isDataValid(Data)) {
108 LDBG() << "RVCB: Bad data for instrument kind " << Desc << ": " << Data
109 << '\n';
110 return nullptr;
111 }
112 return std::make_unique<RISCVSEWInstrument>(args&: Data);
113 }
114
115 LDBG() << "RVCB: Creating default instrument for Desc: " << Desc << '\n';
116 return InstrumentManager::createInstrument(Desc, Data);
117}
118
119SmallVector<UniqueInstrument>
120RISCVInstrumentManager::createInstruments(const MCInst &Inst) {
121 if (Inst.getOpcode() == RISCV::VSETVLI ||
122 Inst.getOpcode() == RISCV::VSETIVLI) {
123 LDBG() << "RVCB: Found VSETVLI and creating instrument for it: " << Inst
124 << "\n";
125 unsigned VTypeI = Inst.getOperand(i: 2).getImm();
126 RISCVVType::VLMUL VLMUL = RISCVVType::getVLMUL(VType: VTypeI);
127
128 StringRef LMUL;
129 switch (VLMUL) {
130 case RISCVVType::LMUL_1:
131 LMUL = "M1";
132 break;
133 case RISCVVType::LMUL_2:
134 LMUL = "M2";
135 break;
136 case RISCVVType::LMUL_4:
137 LMUL = "M4";
138 break;
139 case RISCVVType::LMUL_8:
140 LMUL = "M8";
141 break;
142 case RISCVVType::LMUL_F2:
143 LMUL = "MF2";
144 break;
145 case RISCVVType::LMUL_F4:
146 LMUL = "MF4";
147 break;
148 case RISCVVType::LMUL_F8:
149 LMUL = "MF8";
150 break;
151 case RISCVVType::LMUL_RESERVED:
152 llvm_unreachable("Cannot create instrument for LMUL_RESERVED");
153 }
154 SmallVector<UniqueInstrument> Instruments;
155 Instruments.emplace_back(
156 Args: createInstrument(Desc: RISCVLMULInstrument::DESC_NAME, Data: LMUL));
157
158 unsigned SEW = RISCVVType::getSEW(VType: VTypeI);
159 StringRef SEWStr;
160 switch (SEW) {
161 case 8:
162 SEWStr = "E8";
163 break;
164 case 16:
165 SEWStr = "E16";
166 break;
167 case 32:
168 SEWStr = "E32";
169 break;
170 case 64:
171 SEWStr = "E64";
172 break;
173 default:
174 llvm_unreachable("Cannot create instrument for SEW");
175 }
176 Instruments.emplace_back(
177 Args: createInstrument(Desc: RISCVSEWInstrument::DESC_NAME, Data: SEWStr));
178
179 return Instruments;
180 }
181 return SmallVector<UniqueInstrument>();
182}
183
184static std::pair<uint8_t, uint8_t>
185getEEWAndEMUL(unsigned Opcode, RISCVVType::VLMUL LMUL, uint8_t SEW) {
186 uint8_t EEW;
187 switch (Opcode) {
188 case RISCV::VLM_V:
189 case RISCV::VSM_V:
190 case RISCV::VLE8_V:
191 case RISCV::VSE8_V:
192 case RISCV::VLSE8_V:
193 case RISCV::VSSE8_V:
194 EEW = 8;
195 break;
196 case RISCV::VLE16_V:
197 case RISCV::VSE16_V:
198 case RISCV::VLSE16_V:
199 case RISCV::VSSE16_V:
200 EEW = 16;
201 break;
202 case RISCV::VLE32_V:
203 case RISCV::VSE32_V:
204 case RISCV::VLSE32_V:
205 case RISCV::VSSE32_V:
206 EEW = 32;
207 break;
208 case RISCV::VLE64_V:
209 case RISCV::VSE64_V:
210 case RISCV::VLSE64_V:
211 case RISCV::VSSE64_V:
212 EEW = 64;
213 break;
214 default:
215 llvm_unreachable("Could not determine EEW from Opcode");
216 }
217
218 auto EMUL =
219 RISCVVType::getSameRatioLMUL(Ratio: RISCVVType::getSEWLMULRatio(SEW, VLMul: LMUL), EEW);
220 if (!EEW)
221 llvm_unreachable("Invalid SEW or LMUL for new ratio");
222 return std::make_pair(x&: EEW, y&: *EMUL);
223}
224
225static bool opcodeHasEEWAndEMULInfo(unsigned short Opcode) {
226 return Opcode == RISCV::VLM_V || Opcode == RISCV::VSM_V ||
227 Opcode == RISCV::VLE8_V || Opcode == RISCV::VSE8_V ||
228 Opcode == RISCV::VLE16_V || Opcode == RISCV::VSE16_V ||
229 Opcode == RISCV::VLE32_V || Opcode == RISCV::VSE32_V ||
230 Opcode == RISCV::VLE64_V || Opcode == RISCV::VSE64_V ||
231 Opcode == RISCV::VLSE8_V || Opcode == RISCV::VSSE8_V ||
232 Opcode == RISCV::VLSE16_V || Opcode == RISCV::VSSE16_V ||
233 Opcode == RISCV::VLSE32_V || Opcode == RISCV::VSSE32_V ||
234 Opcode == RISCV::VLSE64_V || Opcode == RISCV::VSSE64_V;
235}
236
237unsigned RISCVInstrumentManager::getSchedClassID(
238 const MCInstrInfo &MCII, const MCInst &MCI,
239 const llvm::SmallVector<Instrument *> &IVec) const {
240 unsigned short Opcode = MCI.getOpcode();
241 unsigned SchedClassID = MCII.get(Opcode).getSchedClass();
242
243 // Unpack all possible RISC-V instruments from IVec.
244 RISCVLMULInstrument *LI = nullptr;
245 RISCVSEWInstrument *SI = nullptr;
246 for (auto &I : IVec) {
247 if (I->getDesc() == RISCVLMULInstrument::DESC_NAME)
248 LI = static_cast<RISCVLMULInstrument *>(I);
249 else if (I->getDesc() == RISCVSEWInstrument::DESC_NAME)
250 SI = static_cast<RISCVSEWInstrument *>(I);
251 }
252
253 // Need LMUL or LMUL, SEW in order to override opcode. If no LMUL is provided,
254 // then no option to override.
255 if (!LI) {
256 LDBG() << "RVCB: Did not use instrumentation to override Opcode.\n";
257 return SchedClassID;
258 }
259 uint8_t LMUL = LI->getLMUL();
260
261 // getBaseInfo works with (Opcode, LMUL, 0) if no SEW instrument,
262 // or (Opcode, LMUL, SEW) if SEW instrument is active, and depends on LMUL
263 // and SEW, or (Opcode, LMUL, 0) if does not depend on SEW.
264 uint8_t SEW = SI ? SI->getSEW() : 0;
265
266 std::optional<unsigned> VPOpcode;
267 if (const auto *VXMO = RISCV::getVXMemOpInfo(BaseInstr: Opcode)) {
268 // Calculate the expected index EMUL. For indexed operations,
269 // the DataEEW and DataEMUL are equal to SEW and LMUL, respectively.
270 unsigned IndexEMUL = ((1 << VXMO->Log2IdxEEW) * LMUL) / SEW;
271
272 if (!VXMO->NFields) {
273 // Indexed Load / Store.
274 if (VXMO->IsStore) {
275 if (const auto *VXP = RISCV::getVSXPseudo(
276 /*Masked=*/0, Ordered: VXMO->IsOrdered, Log2SEW: VXMO->Log2IdxEEW, LMUL,
277 IndexLMUL: IndexEMUL))
278 VPOpcode = VXP->Pseudo;
279 } else {
280 if (const auto *VXP = RISCV::getVLXPseudo(
281 /*Masked=*/0, Ordered: VXMO->IsOrdered, Log2SEW: VXMO->Log2IdxEEW, LMUL,
282 IndexLMUL: IndexEMUL))
283 VPOpcode = VXP->Pseudo;
284 }
285 } else {
286 // Segmented Indexed Load / Store.
287 if (VXMO->IsStore) {
288 if (const auto *VXP = RISCV::getVSXSEGPseudo(
289 NF: VXMO->NFields, /*Masked=*/0, Ordered: VXMO->IsOrdered, Log2SEW: VXMO->Log2IdxEEW,
290 LMUL, IndexLMUL: IndexEMUL))
291 VPOpcode = VXP->Pseudo;
292 } else {
293 if (const auto *VXP = RISCV::getVLXSEGPseudo(
294 NF: VXMO->NFields, /*Masked=*/0, Ordered: VXMO->IsOrdered, Log2SEW: VXMO->Log2IdxEEW,
295 LMUL, IndexLMUL: IndexEMUL))
296 VPOpcode = VXP->Pseudo;
297 }
298 }
299 } else if (opcodeHasEEWAndEMULInfo(Opcode)) {
300 RISCVVType::VLMUL VLMUL = static_cast<RISCVVType::VLMUL>(LMUL);
301 auto [EEW, EMUL] = getEEWAndEMUL(Opcode, LMUL: VLMUL, SEW);
302 if (const auto *RVV =
303 RISCVVInversePseudosTable::getBaseInfo(BaseInstr: Opcode, VLMul: EMUL, SEW: EEW))
304 VPOpcode = RVV->Pseudo;
305 } else {
306 // Check if it depends on LMUL and SEW
307 const auto *RVV = RISCVVInversePseudosTable::getBaseInfo(BaseInstr: Opcode, VLMul: LMUL, SEW);
308 // Check if it depends only on LMUL
309 if (!RVV)
310 RVV = RISCVVInversePseudosTable::getBaseInfo(BaseInstr: Opcode, VLMul: LMUL, SEW: 0);
311
312 if (RVV)
313 VPOpcode = RVV->Pseudo;
314 }
315
316 // Not a RVV instr
317 if (!VPOpcode) {
318 LDBG() << "RVCB: Could not find PseudoInstruction for Opcode "
319 << MCII.getName(Opcode)
320 << ", LMUL=" << (LI ? LI->getData() : "Unspecified")
321 << ", SEW=" << (SI ? SI->getData() : "Unspecified")
322 << ". Ignoring instrumentation and using original SchedClassID="
323 << SchedClassID << '\n';
324 return SchedClassID;
325 }
326
327 // Override using pseudo
328 LDBG() << "RVCB: Found Pseudo Instruction for Opcode " << MCII.getName(Opcode)
329 << ", LMUL=" << LI->getData()
330 << ", SEW=" << (SI ? SI->getData() : "Unspecified")
331 << ". Overriding original SchedClassID=" << SchedClassID << " with "
332 << MCII.getName(Opcode: *VPOpcode) << '\n';
333 return MCII.get(Opcode: *VPOpcode).getSchedClass();
334}
335
336} // namespace mca
337} // namespace llvm
338
339using namespace llvm;
340using namespace mca;
341
342static InstrumentManager *
343createRISCVInstrumentManager(const MCSubtargetInfo &STI,
344 const MCInstrInfo &MCII) {
345 return new RISCVInstrumentManager(STI, MCII);
346}
347
348/// Extern function to initialize the targets for the RISC-V backend
349extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
350LLVMInitializeRISCVTargetMCA() {
351 TargetRegistry::RegisterInstrumentManager(T&: getTheRISCV32Target(),
352 Fn: createRISCVInstrumentManager);
353 TargetRegistry::RegisterInstrumentManager(T&: getTheRISCV64Target(),
354 Fn: createRISCVInstrumentManager);
355}
356