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