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
23namespace llvm {
24namespace mca {
25
26const llvm::StringRef RISCVLMULInstrument::DESC_NAME = "RISCV-LMUL";
27
28bool 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
35uint8_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
51const llvm::StringRef RISCVSEWInstrument::DESC_NAME = "RISCV-SEW";
52
53bool 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
60uint8_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
72bool RISCVInstrumentManager::supportsInstrumentType(
73 llvm::StringRef Type) const {
74 return Type == RISCVLMULInstrument::DESC_NAME ||
75 Type == RISCVSEWInstrument::DESC_NAME;
76}
77
78UniqueInstrument
79RISCVInstrumentManager::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
103SmallVector<UniqueInstrument>
104RISCVInstrumentManager::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
168static std::pair<uint8_t, uint8_t>
169getEEWAndEMUL(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
208bool 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
220unsigned 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
287using namespace llvm;
288using namespace mca;
289
290static InstrumentManager *
291createRISCVInstrumentManager(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
297extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTargetMCA() {
298 TargetRegistry::RegisterInstrumentManager(T&: getTheRISCV32Target(),
299 Fn: createRISCVInstrumentManager);
300 TargetRegistry::RegisterInstrumentManager(T&: getTheRISCV64Target(),
301 Fn: createRISCVInstrumentManager);
302}
303