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