1//===-- RISCVTargetParser.cpp - Parser for target features ------*- 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//
9// This file implements a target parser to recognise hardware features
10// for RISC-V CPUs.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/TargetParser/RISCVTargetParser.h"
15#include "llvm/ADT/SetOperations.h"
16#include "llvm/ADT/SmallSet.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringSwitch.h"
20#include "llvm/ADT/StringTable.h"
21#include "llvm/TargetParser/RISCVISAInfo.h"
22
23namespace llvm {
24namespace RISCV {
25
26char ParserError::ID = 0;
27char ParserWarning::ID = 0;
28
29enum CPUKind : unsigned {
30#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
31 FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
32 CK_##ENUM,
33#define TUNE_PROC(ENUM, NAME) CK_##ENUM,
34#include "llvm/TargetParser/RISCVTargetParserDef.inc"
35};
36
37constexpr CPUInfo RISCVCPUInfo[] = {
38#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
39 FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
40 { \
41 NAME, \
42 DEFAULT_MARCH, \
43 FAST_SCALAR_UNALIGN, \
44 FAST_VECTOR_UNALIGN, \
45 {MVENDORID, MARCHID, MIMPID}, \
46 },
47#include "llvm/TargetParser/RISCVTargetParserDef.inc"
48};
49
50static const CPUInfo *getCPUInfoByName(StringRef CPU) {
51 for (auto &C : RISCVCPUInfo)
52 if (C.Name == CPU)
53 return &C;
54 return nullptr;
55}
56
57bool hasFastScalarUnalignedAccess(StringRef CPU) {
58 const CPUInfo *Info = getCPUInfoByName(CPU);
59 return Info && Info->FastScalarUnalignedAccess;
60}
61
62bool hasFastVectorUnalignedAccess(StringRef CPU) {
63 const CPUInfo *Info = getCPUInfoByName(CPU);
64 return Info && Info->FastVectorUnalignedAccess;
65}
66
67bool hasValidCPUModel(StringRef CPU) { return getCPUModel(CPU).isValid(); }
68
69CPUModel getCPUModel(StringRef CPU) {
70 const CPUInfo *Info = getCPUInfoByName(CPU);
71 if (!Info)
72 return {.MVendorID: 0, .MArchID: 0, .MImpID: 0};
73 return Info->Model;
74}
75
76StringRef getCPUNameFromCPUModel(const CPUModel &Model) {
77 if (!Model.isValid())
78 return "";
79
80 for (auto &C : RISCVCPUInfo)
81 if (C.Model == Model)
82 return C.Name;
83 return "";
84}
85
86bool parseCPU(StringRef CPU, bool IsRV64) {
87 const CPUInfo *Info = getCPUInfoByName(CPU);
88
89 if (!Info)
90 return false;
91 return Info->is64Bit() == IsRV64;
92}
93
94bool parseTuneCPU(StringRef TuneCPU, bool IsRV64) {
95 std::optional<CPUKind> Kind =
96 llvm::StringSwitch<std::optional<CPUKind>>(TuneCPU)
97#define TUNE_PROC(ENUM, NAME) .Case(NAME, CK_##ENUM)
98 #include "llvm/TargetParser/RISCVTargetParserDef.inc"
99 .Default(Value: std::nullopt);
100
101 if (Kind.has_value())
102 return true;
103
104 // Fallback to parsing as a CPU.
105 return parseCPU(CPU: TuneCPU, IsRV64);
106}
107
108StringRef getMArchFromMcpu(StringRef CPU) {
109 const CPUInfo *Info = getCPUInfoByName(CPU);
110 if (!Info)
111 return "";
112 return Info->DefaultMarch;
113}
114
115void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
116 for (const auto &C : RISCVCPUInfo) {
117 if (IsRV64 == C.is64Bit())
118 Values.emplace_back(Args: C.Name);
119 }
120}
121
122void fillValidTuneCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
123 for (const auto &C : RISCVCPUInfo) {
124 if (IsRV64 == C.is64Bit())
125 Values.emplace_back(Args: C.Name);
126 }
127#define TUNE_PROC(ENUM, NAME) Values.emplace_back(StringRef(NAME));
128#include "llvm/TargetParser/RISCVTargetParserDef.inc"
129}
130
131// This function is currently used by IREE, so it's not dead code.
132void getFeaturesForCPU(StringRef CPU,
133 SmallVectorImpl<std::string> &EnabledFeatures,
134 bool NeedPlus) {
135 StringRef MarchFromCPU = llvm::RISCV::getMArchFromMcpu(CPU);
136 if (MarchFromCPU == "")
137 return;
138
139 EnabledFeatures.clear();
140 auto RII = RISCVISAInfo::parseArchString(
141 Arch: MarchFromCPU, /* EnableExperimentalExtension */ true);
142
143 if (llvm::errorToBool(Err: RII.takeError()))
144 return;
145
146 std::vector<std::string> FeatStrings =
147 (*RII)->toFeatures(/* AddAllExtensions */ false);
148 for (const auto &F : FeatStrings)
149 if (NeedPlus)
150 EnabledFeatures.push_back(Elt: F);
151 else
152 EnabledFeatures.push_back(Elt: F.substr(pos: 1));
153}
154
155namespace {
156class RISCVTuneFeatureLookupTable {
157 struct RISCVTuneFeature {
158 unsigned PosIdx;
159 unsigned NegIdx;
160 unsigned FeatureIdx;
161 };
162
163 struct RISCVImpliedTuneFeature {
164 unsigned FeatureIdx;
165 unsigned ImpliedFeatureIdx;
166 };
167
168 struct RISCVConfigurableTuneFeatures {
169 StringRef Processor;
170 unsigned DirectiveIdx;
171
172 bool operator<(const RISCVConfigurableTuneFeatures &RHS) const {
173 return Processor < RHS.Processor;
174 }
175 };
176
177#define GET_TUNE_FEATURES
178#define GET_CONFIGURABLE_TUNE_FEATURES
179#include "llvm/TargetParser/RISCVTargetParserDef.inc"
180
181 // Positive directive name -> Feature name
182 StringMap<StringRef> PositiveMap;
183 // Negative directive name -> Feature name
184 StringMap<StringRef> NegativeMap;
185
186 StringMap<SmallVector<StringRef>> ImpliedFeatureMap;
187 StringMap<SmallVector<StringRef>> InvImpliedFeatureMap;
188
189public:
190 using SmallStringSet = SmallSet<StringRef, 4>;
191
192 static void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
193 for (const auto &TuneFeature : TuneFeatures)
194 Features.push_back(Elt: TuneFeatureStrings[TuneFeature.FeatureIdx]);
195 }
196
197 static void getConfigurableFeatures(StringRef ProcName,
198 SmallStringSet &Directives) {
199 // Entries for the same processor are always put together.
200 auto [ItFirst, ItEnd] =
201 std::equal_range(first: std::begin(arr: ConfigurableTuneFeatures),
202 last: std::end(arr: ConfigurableTuneFeatures),
203 val: RISCVConfigurableTuneFeatures{.Processor: ProcName, .DirectiveIdx: 0});
204 for (; ItFirst != ItEnd; ++ItFirst)
205 Directives.insert(V: TuneFeatureStrings[ItFirst->DirectiveIdx]);
206 }
207
208 RISCVTuneFeatureLookupTable() {
209 for (const auto &TuneFeature : TuneFeatures) {
210 StringRef PosDirective = TuneFeatureStrings[TuneFeature.PosIdx];
211 StringRef NegDirective = TuneFeatureStrings[TuneFeature.NegIdx];
212 StringRef FeatureName = TuneFeatureStrings[TuneFeature.FeatureIdx];
213 PositiveMap[PosDirective] = FeatureName;
214 NegativeMap[NegDirective] = FeatureName;
215 }
216
217 for (const auto &Imp : ImpliedTuneFeatures) {
218 StringRef Feature = TuneFeatureStrings[Imp.FeatureIdx];
219 StringRef ImpliedFeature = TuneFeatureStrings[Imp.ImpliedFeatureIdx];
220 ImpliedFeatureMap[Feature].push_back(Elt: ImpliedFeature);
221 InvImpliedFeatureMap[ImpliedFeature].push_back(Elt: Feature);
222 }
223 }
224
225 /// Returns {Feature name, Is positive or not}, or empty feature name
226 /// if not found.
227 std::pair<StringRef, bool> getFeature(StringRef DirectiveName) const {
228 auto It = PositiveMap.find(Key: DirectiveName);
229 if (It != PositiveMap.end())
230 return {It->getValue(), /*IsPositive=*/true};
231
232 return {NegativeMap.lookup(Key: DirectiveName), /*IsPositive=*/false};
233 }
234
235 /// Returns the implied features, or empty ArrayRef if not found. Note:
236 /// ImpliedFeatureMap / InvImpliedFeatureMap are the owners of these implied
237 /// feature lists, so we can just return the ArrayRef.
238 ArrayRef<StringRef> featureImplies(StringRef FeatureName,
239 bool Inverse = false) const {
240 const auto &Map = Inverse ? InvImpliedFeatureMap : ImpliedFeatureMap;
241 auto It = Map.find(Key: FeatureName);
242 if (It == Map.end())
243 return {};
244 return It->second;
245 }
246};
247} // namespace
248
249void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
250 RISCVTuneFeatureLookupTable::getAllTuneFeatures(Features);
251}
252
253Error parseTuneFeatureString(StringRef ProcName, StringRef TFString,
254 SmallVectorImpl<std::string> &ResFeatures) {
255 RISCVTuneFeatureLookupTable TFLookup;
256 using SmallStringSet = RISCVTuneFeatureLookupTable::SmallStringSet;
257
258 // Do not create ParserWarning right away. Instead, we store the warning
259 // message until the last moment.
260 std::string WarningMsg;
261
262 TFString = TFString.trim();
263 if (TFString.empty())
264 return Error::success();
265
266 // Note: StringSet is not really ergonomic to use in this case here.
267 SmallStringSet PositiveFeatures;
268 SmallStringSet NegativeFeatures;
269 SmallStringSet PerProcDirectives;
270 RISCVTuneFeatureLookupTable::getConfigurableFeatures(ProcName,
271 Directives&: PerProcDirectives);
272 if (PerProcDirectives.empty() && !ProcName.empty())
273 return make_error<ParserError>(Args: "Processor '" + Twine(ProcName) +
274 "' has no "
275 "configurable tuning features");
276
277 // Phase 1: Collect explicit features.
278 StringRef DirectiveStr;
279 do {
280 std::tie(args&: DirectiveStr, args&: TFString) = TFString.split(Separator: ",");
281 auto [FeatureName, IsPositive] = TFLookup.getFeature(DirectiveName: DirectiveStr);
282 if (FeatureName.empty()) {
283 raw_string_ostream SS(WarningMsg);
284 SS << "unrecognized tune feature directive '" << DirectiveStr << "'";
285 continue;
286 }
287
288 auto &Features = IsPositive ? PositiveFeatures : NegativeFeatures;
289 if (!Features.insert(V: FeatureName).second)
290 return make_error<ParserError>(
291 Args: "cannot specify more than one instance of '" + Twine(DirectiveStr) +
292 "'");
293
294 if (!PerProcDirectives.count(V: DirectiveStr) && !ProcName.empty())
295 return make_error<ParserError>(Args: "Directive '" + Twine(DirectiveStr) +
296 "' is not "
297 "allowed to be used with processor '" +
298 Twine(ProcName) + "'");
299 } while (!TFString.empty());
300
301 auto Intersection =
302 llvm::set_intersection(S1: PositiveFeatures, S2: NegativeFeatures);
303 if (!Intersection.empty()) {
304 std::string IntersectedStr = join(R&: Intersection, Separator: "', '");
305 return make_error<ParserError>(Args: "Feature(s) '" + Twine(IntersectedStr) +
306 "' cannot appear in both "
307 "positive and negative directives");
308 }
309
310 // Phase 2: Derive implied features.
311 SmallStringSet DerivedPosFeatures;
312 SmallStringSet DerivedNegFeatures;
313 for (StringRef PF : PositiveFeatures) {
314 if (auto FeatureList = TFLookup.featureImplies(FeatureName: PF); !FeatureList.empty())
315 DerivedPosFeatures.insert_range(R&: FeatureList);
316 }
317 for (StringRef NF : NegativeFeatures) {
318 if (auto FeatureList = TFLookup.featureImplies(FeatureName: NF, /*Inverse=*/true);
319 !FeatureList.empty())
320 DerivedNegFeatures.insert_range(R&: FeatureList);
321 }
322 PositiveFeatures.insert_range(R&: DerivedPosFeatures);
323 NegativeFeatures.insert_range(R&: DerivedNegFeatures);
324
325 Intersection = llvm::set_intersection(S1: PositiveFeatures, S2: NegativeFeatures);
326 if (!Intersection.empty()) {
327 std::string IntersectedStr = join(R&: Intersection, Separator: "', '");
328 return make_error<ParserError>(Args: "Feature(s) '" + Twine(IntersectedStr) +
329 "' were implied by both "
330 "positive and negative directives");
331 }
332
333 // Export the result.
334 const std::string PosPrefix("+");
335 const std::string NegPrefix("-");
336 for (StringRef PF : PositiveFeatures)
337 ResFeatures.emplace_back(Args: PosPrefix + PF.str());
338 for (StringRef NF : NegativeFeatures)
339 ResFeatures.emplace_back(Args: NegPrefix + NF.str());
340
341 if (WarningMsg.empty())
342 return Error::success();
343
344 return make_error<ParserWarning>(Args&: WarningMsg);
345}
346
347void getCPUConfigurableTuneFeatures(StringRef CPU,
348 SmallVectorImpl<StringRef> &Directives) {
349 RISCVTuneFeatureLookupTable::SmallStringSet DirectiveSet;
350 RISCVTuneFeatureLookupTable::getConfigurableFeatures(ProcName: CPU, Directives&: DirectiveSet);
351 Directives.assign(in_start: DirectiveSet.begin(), in_end: DirectiveSet.end());
352}
353} // namespace RISCV
354
355namespace RISCVVType {
356// Encode VTYPE into the binary format used by the the VSETVLI instruction which
357// is used by our MC layer representation.
358//
359// Bits | Name | Description
360// -----+------------+------------------------------------------------
361// 8 | altfmt | Alternative format for bf16/ofp8
362// 7 | vma | Vector mask agnostic
363// 6 | vta | Vector tail agnostic
364// 5:3 | vsew[2:0] | Standard element width (SEW) setting
365// 2:0 | vlmul[2:0] | Vector register group multiplier (LMUL) setting
366unsigned encodeVTYPE(VLMUL VLMul, unsigned SEW, bool TailAgnostic,
367 bool MaskAgnostic, bool AltFmt) {
368 assert(isValidSEW(SEW) && "Invalid SEW");
369 unsigned VLMulBits = static_cast<unsigned>(VLMul);
370 unsigned VSEWBits = encodeSEW(SEW);
371 unsigned VTypeI = (VSEWBits << 3) | (VLMulBits & 0x7);
372 if (TailAgnostic)
373 VTypeI |= 0x40;
374 if (MaskAgnostic)
375 VTypeI |= 0x80;
376 if (AltFmt)
377 VTypeI |= 0x100;
378
379 return VTypeI;
380}
381
382unsigned encodeXSfmmVType(unsigned SEW, unsigned Widen, bool AltFmt) {
383 assert(isValidSEW(SEW) && "Invalid SEW");
384 assert((Widen == 1 || Widen == 2 || Widen == 4) && "Invalid Widen");
385 unsigned VSEWBits = encodeSEW(SEW);
386 unsigned TWiden = Log2_32(Value: Widen) + 1;
387 unsigned VTypeI = (VSEWBits << 3) | AltFmt << 8 | TWiden << 9;
388 return VTypeI;
389}
390
391namespace IME {
392static void assertValidXLenForVType(unsigned XLen) {
393 assert((XLen == 32 || XLen == 64) && "Invalid XLEN");
394}
395
396static unsigned getLambdaShift(unsigned XLen) {
397 assertValidXLenForVType(XLen);
398 return XLen - 4;
399}
400
401static unsigned getAltFmtAShift(unsigned XLen) {
402 assertValidXLenForVType(XLen);
403 return XLen - 6;
404}
405
406static unsigned getAltFmtBShift(unsigned XLen) {
407 assertValidXLenForVType(XLen);
408 return XLen - 7;
409}
410
411static unsigned getBSShift(unsigned XLen) {
412 assertValidXLenForVType(XLen);
413 return XLen - 5;
414}
415
416unsigned encodeLambda(unsigned Lambda) {
417 assert(isValidLambda(Lambda) && "Invalid IME lambda");
418 if (Lambda == 0)
419 return 0;
420 return Log2_32(Value: Lambda) + 1;
421}
422
423std::optional<unsigned> decodeLambda(unsigned Encoding) {
424 assert(Encoding < 8 && "Invalid IME lambda encoding");
425 if (Encoding == 0)
426 return std::nullopt;
427 return 1U << (Encoding - 1);
428}
429
430uint64_t getVTypeFieldsMask(unsigned XLen) {
431 assertValidXLenForVType(XLen);
432 return (0x7ULL << getLambdaShift(XLen)) | (1ULL << getAltFmtAShift(XLen)) |
433 (1ULL << getAltFmtBShift(XLen)) | (1ULL << getBSShift(XLen));
434}
435
436uint64_t encodeVTypeFields(unsigned XLen, unsigned Lambda, bool AltFmtA,
437 bool AltFmtB, bool BlockSize16) {
438 assertValidXLenForVType(XLen);
439 uint64_t VType = uint64_t(encodeLambda(Lambda)) << getLambdaShift(XLen);
440 if (AltFmtA)
441 VType |= 1ULL << getAltFmtAShift(XLen);
442 if (AltFmtB)
443 VType |= 1ULL << getAltFmtBShift(XLen);
444 if (BlockSize16)
445 VType |= 1ULL << getBSShift(XLen);
446 return VType;
447}
448
449uint64_t addVTypeFields(uint64_t VType, unsigned XLen, unsigned Lambda,
450 bool AltFmtA, bool AltFmtB, bool BlockSize16) {
451 return (VType & ~getVTypeFieldsMask(XLen)) |
452 encodeVTypeFields(XLen, Lambda, AltFmtA, AltFmtB, BlockSize16);
453}
454
455unsigned getLambdaEncoding(uint64_t VType, unsigned XLen) {
456 assertValidXLenForVType(XLen);
457 return (VType >> getLambdaShift(XLen)) & 0x7;
458}
459
460std::optional<unsigned> getLambda(uint64_t VType, unsigned XLen) {
461 return decodeLambda(Encoding: getLambdaEncoding(VType, XLen));
462}
463
464bool isAltFmtA(uint64_t VType, unsigned XLen) {
465 assertValidXLenForVType(XLen);
466 return VType & (1ULL << getAltFmtAShift(XLen));
467}
468
469bool isAltFmtB(uint64_t VType, unsigned XLen) {
470 assertValidXLenForVType(XLen);
471 return VType & (1ULL << getAltFmtBShift(XLen));
472}
473
474bool isBlockSize16(uint64_t VType, unsigned XLen) {
475 assertValidXLenForVType(XLen);
476 return VType & (1ULL << getBSShift(XLen));
477}
478} // namespace IME
479
480std::pair<unsigned, bool> decodeVLMUL(VLMUL VLMul) {
481 switch (VLMul) {
482 default:
483 llvm_unreachable("Unexpected LMUL value!");
484 case LMUL_1:
485 case LMUL_2:
486 case LMUL_4:
487 case LMUL_8:
488 return std::make_pair(x: 1 << static_cast<unsigned>(VLMul), y: false);
489 case LMUL_F2:
490 case LMUL_F4:
491 case LMUL_F8:
492 return std::make_pair(x: 1 << (8 - static_cast<unsigned>(VLMul)), y: true);
493 }
494}
495
496void printVType(unsigned VType, raw_ostream &OS) {
497 unsigned Sew = getSEW(VType);
498 OS << "e" << Sew;
499
500 bool AltFmt = RISCVVType::isAltFmt(VType);
501 if (AltFmt)
502 OS << "alt";
503
504 unsigned LMul;
505 bool Fractional;
506 std::tie(args&: LMul, args&: Fractional) = decodeVLMUL(VLMul: getVLMUL(VType));
507
508 if (Fractional)
509 OS << ", mf";
510 else
511 OS << ", m";
512 OS << LMul;
513
514 if (isTailAgnostic(VType))
515 OS << ", ta";
516 else
517 OS << ", tu";
518
519 if (isMaskAgnostic(VType))
520 OS << ", ma";
521 else
522 OS << ", mu";
523}
524
525void printXSfmmVType(unsigned VType, raw_ostream &OS) {
526 OS << "e" << getSEW(VType) << ", w" << getXSfmmWiden(VType);
527}
528
529unsigned getSEWLMULRatio(unsigned SEW, VLMUL VLMul) {
530 unsigned LMul;
531 bool Fractional;
532 std::tie(args&: LMul, args&: Fractional) = decodeVLMUL(VLMul);
533
534 // Convert LMul to a fixed point value with 3 fractional bits.
535 LMul = Fractional ? (8 / LMul) : (LMul * 8);
536
537 assert(SEW >= 8 && "Unexpected SEW value");
538 return (SEW * 8) / LMul;
539}
540
541std::optional<VLMUL> getSameRatioLMUL(unsigned Ratio, unsigned EEW) {
542 unsigned EMULFixedPoint = (EEW * 8) / Ratio;
543 bool Fractional = EMULFixedPoint < 8;
544 unsigned EMUL = Fractional ? 8 / EMULFixedPoint : EMULFixedPoint / 8;
545 if (!isValidLMUL(LMUL: EMUL, Fractional))
546 return std::nullopt;
547 return RISCVVType::encodeLMUL(LMUL: EMUL, Fractional);
548}
549
550} // namespace RISCVVType
551
552} // namespace llvm
553