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