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
391std::pair<unsigned, bool> decodeVLMUL(VLMUL VLMul) {
392 switch (VLMul) {
393 default:
394 llvm_unreachable("Unexpected LMUL value!");
395 case LMUL_1:
396 case LMUL_2:
397 case LMUL_4:
398 case LMUL_8:
399 return std::make_pair(x: 1 << static_cast<unsigned>(VLMul), y: false);
400 case LMUL_F2:
401 case LMUL_F4:
402 case LMUL_F8:
403 return std::make_pair(x: 1 << (8 - static_cast<unsigned>(VLMul)), y: true);
404 }
405}
406
407void printVType(unsigned VType, raw_ostream &OS) {
408 unsigned Sew = getSEW(VType);
409 OS << "e" << Sew;
410
411 bool AltFmt = RISCVVType::isAltFmt(VType);
412 if (AltFmt)
413 OS << "alt";
414
415 unsigned LMul;
416 bool Fractional;
417 std::tie(args&: LMul, args&: Fractional) = decodeVLMUL(VLMul: getVLMUL(VType));
418
419 if (Fractional)
420 OS << ", mf";
421 else
422 OS << ", m";
423 OS << LMul;
424
425 if (isTailAgnostic(VType))
426 OS << ", ta";
427 else
428 OS << ", tu";
429
430 if (isMaskAgnostic(VType))
431 OS << ", ma";
432 else
433 OS << ", mu";
434}
435
436void printXSfmmVType(unsigned VType, raw_ostream &OS) {
437 OS << "e" << getSEW(VType) << ", w" << getXSfmmWiden(VType);
438}
439
440unsigned getSEWLMULRatio(unsigned SEW, VLMUL VLMul) {
441 unsigned LMul;
442 bool Fractional;
443 std::tie(args&: LMul, args&: Fractional) = decodeVLMUL(VLMul);
444
445 // Convert LMul to a fixed point value with 3 fractional bits.
446 LMul = Fractional ? (8 / LMul) : (LMul * 8);
447
448 assert(SEW >= 8 && "Unexpected SEW value");
449 return (SEW * 8) / LMul;
450}
451
452std::optional<VLMUL> getSameRatioLMUL(unsigned Ratio, unsigned EEW) {
453 unsigned EMULFixedPoint = (EEW * 8) / Ratio;
454 bool Fractional = EMULFixedPoint < 8;
455 unsigned EMUL = Fractional ? 8 / EMULFixedPoint : EMULFixedPoint / 8;
456 if (!isValidLMUL(LMUL: EMUL, Fractional))
457 return std::nullopt;
458 return RISCVVType::encodeLMUL(LMUL: EMUL, Fractional);
459}
460
461} // namespace RISCVVType
462
463} // namespace llvm
464