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