1 | //===-- RISCVISAInfo.cpp - RISC-V Arch String Parser ----------------------===// |
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 | #include "llvm/TargetParser/RISCVISAInfo.h" |
10 | #include "llvm/ADT/STLExtras.h" |
11 | #include "llvm/ADT/StringExtras.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | #include "llvm/Support/Errc.h" |
14 | #include "llvm/Support/Error.h" |
15 | #include "llvm/Support/raw_ostream.h" |
16 | |
17 | #include <array> |
18 | #include <atomic> |
19 | #include <optional> |
20 | #include <string> |
21 | #include <vector> |
22 | |
23 | using namespace llvm; |
24 | |
25 | namespace { |
26 | |
27 | struct RISCVSupportedExtension { |
28 | const char *Name; |
29 | /// Supported version. |
30 | RISCVISAUtils::ExtensionVersion Version; |
31 | |
32 | bool operator<(const RISCVSupportedExtension &RHS) const { |
33 | return StringRef(Name) < StringRef(RHS.Name); |
34 | } |
35 | }; |
36 | |
37 | struct RISCVProfile { |
38 | StringLiteral Name; |
39 | StringLiteral MArch; |
40 | |
41 | bool operator<(const RISCVProfile &RHS) const { |
42 | return StringRef(Name) < StringRef(RHS.Name); |
43 | } |
44 | }; |
45 | |
46 | } // end anonymous namespace |
47 | |
48 | static const char *RISCVGImplications[] = { |
49 | "i" , "m" , "a" , "f" , "d" , "zicsr" , "zifencei" |
50 | }; |
51 | |
52 | #define GET_SUPPORTED_EXTENSIONS |
53 | #include "llvm/TargetParser/RISCVTargetParserDef.inc" |
54 | |
55 | #define GET_SUPPORTED_PROFILES |
56 | #include "llvm/TargetParser/RISCVTargetParserDef.inc" |
57 | |
58 | static void verifyTables() { |
59 | #ifndef NDEBUG |
60 | static std::atomic<bool> TableChecked(false); |
61 | if (!TableChecked.load(std::memory_order_relaxed)) { |
62 | assert(llvm::is_sorted(SupportedExtensions) && |
63 | "Extensions are not sorted by name" ); |
64 | assert(llvm::is_sorted(SupportedExperimentalExtensions) && |
65 | "Experimental extensions are not sorted by name" ); |
66 | assert(llvm::is_sorted(SupportedProfiles) && |
67 | "Profiles are not sorted by name" ); |
68 | assert(llvm::is_sorted(SupportedExperimentalProfiles) && |
69 | "Experimental profiles are not sorted by name" ); |
70 | TableChecked.store(true, std::memory_order_relaxed); |
71 | } |
72 | #endif |
73 | } |
74 | |
75 | static void PrintExtension(StringRef Name, StringRef Version, |
76 | StringRef Description) { |
77 | outs().indent(NumSpaces: 4); |
78 | unsigned VersionWidth = Description.empty() ? 0 : 10; |
79 | outs() << left_justify(Str: Name, Width: 21) << left_justify(Str: Version, Width: VersionWidth) |
80 | << Description << "\n" ; |
81 | } |
82 | |
83 | void RISCVISAInfo::printSupportedExtensions(StringMap<StringRef> &DescMap) { |
84 | outs() << "All available -march extensions for RISC-V\n\n" ; |
85 | PrintExtension(Name: "Name" , Version: "Version" , Description: (DescMap.empty() ? "" : "Description" )); |
86 | |
87 | RISCVISAUtils::OrderedExtensionMap ExtMap; |
88 | for (const auto &E : SupportedExtensions) |
89 | ExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
90 | for (const auto &E : ExtMap) { |
91 | std::string Version = |
92 | std::to_string(val: E.second.Major) + "." + std::to_string(val: E.second.Minor); |
93 | PrintExtension(Name: E.first, Version, Description: DescMap[E.first]); |
94 | } |
95 | |
96 | outs() << "\nExperimental extensions\n" ; |
97 | ExtMap.clear(); |
98 | for (const auto &E : SupportedExperimentalExtensions) |
99 | ExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
100 | for (const auto &E : ExtMap) { |
101 | std::string Version = |
102 | std::to_string(val: E.second.Major) + "." + std::to_string(val: E.second.Minor); |
103 | PrintExtension(Name: E.first, Version, Description: DescMap["experimental-" + E.first]); |
104 | } |
105 | |
106 | outs() << "\nSupported Profiles\n" ; |
107 | for (const auto &P : SupportedProfiles) |
108 | outs().indent(NumSpaces: 4) << P.Name << "\n" ; |
109 | |
110 | outs() << "\nExperimental Profiles\n" ; |
111 | for (const auto &P : SupportedExperimentalProfiles) |
112 | outs().indent(NumSpaces: 4) << P.Name << "\n" ; |
113 | |
114 | outs() << "\nUse -march to specify the target's extension.\n" |
115 | "For example, clang -march=rv32i_v1p0\n" ; |
116 | } |
117 | |
118 | void RISCVISAInfo::printEnabledExtensions( |
119 | bool IsRV64, std::set<StringRef> &EnabledFeatureNames, |
120 | StringMap<StringRef> &DescMap) { |
121 | outs() << "Extensions enabled for the given RISC-V target\n\n" ; |
122 | PrintExtension(Name: "Name" , Version: "Version" , Description: (DescMap.empty() ? "" : "Description" )); |
123 | |
124 | RISCVISAUtils::OrderedExtensionMap FullExtMap; |
125 | RISCVISAUtils::OrderedExtensionMap ExtMap; |
126 | for (const auto &E : SupportedExtensions) |
127 | if (EnabledFeatureNames.count(x: E.Name) != 0) { |
128 | FullExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
129 | ExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
130 | } |
131 | for (const auto &E : ExtMap) { |
132 | std::string Version = |
133 | std::to_string(val: E.second.Major) + "." + std::to_string(val: E.second.Minor); |
134 | PrintExtension(Name: E.first, Version, Description: DescMap[E.first]); |
135 | } |
136 | |
137 | outs() << "\nExperimental extensions\n" ; |
138 | ExtMap.clear(); |
139 | for (const auto &E : SupportedExperimentalExtensions) { |
140 | StringRef Name(E.Name); |
141 | if (EnabledFeatureNames.count(x: "experimental-" + Name.str()) != 0) { |
142 | FullExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
143 | ExtMap[E.Name] = {.Major: E.Version.Major, .Minor: E.Version.Minor}; |
144 | } |
145 | } |
146 | for (const auto &E : ExtMap) { |
147 | std::string Version = |
148 | std::to_string(val: E.second.Major) + "." + std::to_string(val: E.second.Minor); |
149 | PrintExtension(Name: E.first, Version, Description: DescMap["experimental-" + E.first]); |
150 | } |
151 | |
152 | unsigned XLen = IsRV64 ? 64 : 32; |
153 | if (auto ISAString = RISCVISAInfo::createFromExtMap(XLen, Exts: FullExtMap)) |
154 | outs() << "\nISA String: " << ISAString.get()->toString() << "\n" ; |
155 | } |
156 | |
157 | static bool stripExperimentalPrefix(StringRef &Ext) { |
158 | return Ext.consume_front(Prefix: "experimental-" ); |
159 | } |
160 | |
161 | // This function finds the last character that doesn't belong to a version |
162 | // (e.g. zba1p0 is extension 'zba' of version '1p0'). So the function will |
163 | // consume [0-9]*p[0-9]* starting from the backward. An extension name will not |
164 | // end with a digit or the letter 'p', so this function will parse correctly. |
165 | // NOTE: This function is NOT able to take empty strings or strings that only |
166 | // have version numbers and no extension name. It assumes the extension name |
167 | // will be at least more than one character. |
168 | static size_t findLastNonVersionCharacter(StringRef Ext) { |
169 | assert(!Ext.empty() && |
170 | "Already guarded by if-statement in ::parseArchString" ); |
171 | |
172 | int Pos = Ext.size() - 1; |
173 | while (Pos > 0 && isDigit(C: Ext[Pos])) |
174 | Pos--; |
175 | if (Pos > 0 && Ext[Pos] == 'p' && isDigit(C: Ext[Pos - 1])) { |
176 | Pos--; |
177 | while (Pos > 0 && isDigit(C: Ext[Pos])) |
178 | Pos--; |
179 | } |
180 | return Pos; |
181 | } |
182 | |
183 | namespace { |
184 | struct LessExtName { |
185 | bool operator()(const RISCVSupportedExtension &LHS, StringRef RHS) { |
186 | return StringRef(LHS.Name) < RHS; |
187 | } |
188 | bool operator()(StringRef LHS, const RISCVSupportedExtension &RHS) { |
189 | return LHS < StringRef(RHS.Name); |
190 | } |
191 | }; |
192 | } // namespace |
193 | |
194 | static std::optional<RISCVISAUtils::ExtensionVersion> |
195 | findDefaultVersion(StringRef ExtName) { |
196 | // Find default version of an extension. |
197 | // TODO: We might set default version based on profile or ISA spec. |
198 | for (auto &ExtInfo : {ArrayRef(SupportedExtensions), |
199 | ArrayRef(SupportedExperimentalExtensions)}) { |
200 | auto I = llvm::lower_bound(Range: ExtInfo, Value&: ExtName, C: LessExtName()); |
201 | |
202 | if (I == ExtInfo.end() || I->Name != ExtName) |
203 | continue; |
204 | |
205 | return I->Version; |
206 | } |
207 | return std::nullopt; |
208 | } |
209 | |
210 | static StringRef getExtensionTypeDesc(StringRef Ext) { |
211 | if (Ext.starts_with(Prefix: 's')) |
212 | return "standard supervisor-level extension" ; |
213 | if (Ext.starts_with(Prefix: 'x')) |
214 | return "non-standard user-level extension" ; |
215 | if (Ext.starts_with(Prefix: 'z')) |
216 | return "standard user-level extension" ; |
217 | return StringRef(); |
218 | } |
219 | |
220 | static StringRef getExtensionType(StringRef Ext) { |
221 | if (Ext.starts_with(Prefix: 's')) |
222 | return "s" ; |
223 | if (Ext.starts_with(Prefix: 'x')) |
224 | return "x" ; |
225 | if (Ext.starts_with(Prefix: 'z')) |
226 | return "z" ; |
227 | return StringRef(); |
228 | } |
229 | |
230 | static std::optional<RISCVISAUtils::ExtensionVersion> |
231 | isExperimentalExtension(StringRef Ext) { |
232 | auto I = |
233 | llvm::lower_bound(Range: SupportedExperimentalExtensions, Value&: Ext, C: LessExtName()); |
234 | if (I == std::end(arr: SupportedExperimentalExtensions) || I->Name != Ext) |
235 | return std::nullopt; |
236 | |
237 | return I->Version; |
238 | } |
239 | |
240 | bool RISCVISAInfo::isSupportedExtensionFeature(StringRef Ext) { |
241 | bool IsExperimental = stripExperimentalPrefix(Ext); |
242 | |
243 | ArrayRef<RISCVSupportedExtension> ExtInfo = |
244 | IsExperimental ? ArrayRef(SupportedExperimentalExtensions) |
245 | : ArrayRef(SupportedExtensions); |
246 | |
247 | auto I = llvm::lower_bound(Range&: ExtInfo, Value&: Ext, C: LessExtName()); |
248 | return I != ExtInfo.end() && I->Name == Ext; |
249 | } |
250 | |
251 | bool RISCVISAInfo::isSupportedExtension(StringRef Ext) { |
252 | verifyTables(); |
253 | |
254 | for (auto ExtInfo : {ArrayRef(SupportedExtensions), |
255 | ArrayRef(SupportedExperimentalExtensions)}) { |
256 | auto I = llvm::lower_bound(Range&: ExtInfo, Value&: Ext, C: LessExtName()); |
257 | if (I != ExtInfo.end() && I->Name == Ext) |
258 | return true; |
259 | } |
260 | |
261 | return false; |
262 | } |
263 | |
264 | bool RISCVISAInfo::isSupportedExtension(StringRef Ext, unsigned MajorVersion, |
265 | unsigned MinorVersion) { |
266 | for (auto ExtInfo : {ArrayRef(SupportedExtensions), |
267 | ArrayRef(SupportedExperimentalExtensions)}) { |
268 | auto Range = |
269 | std::equal_range(first: ExtInfo.begin(), last: ExtInfo.end(), val: Ext, comp: LessExtName()); |
270 | for (auto I = Range.first, E = Range.second; I != E; ++I) |
271 | if (I->Version.Major == MajorVersion && I->Version.Minor == MinorVersion) |
272 | return true; |
273 | } |
274 | |
275 | return false; |
276 | } |
277 | |
278 | bool RISCVISAInfo::hasExtension(StringRef Ext) const { |
279 | stripExperimentalPrefix(Ext); |
280 | |
281 | if (!isSupportedExtension(Ext)) |
282 | return false; |
283 | |
284 | return Exts.count(x: Ext.str()) != 0; |
285 | } |
286 | |
287 | std::vector<std::string> RISCVISAInfo::toFeatures(bool AddAllExtensions, |
288 | bool IgnoreUnknown) const { |
289 | std::vector<std::string> Features; |
290 | for (const auto &[ExtName, _] : Exts) { |
291 | // i is a base instruction set, not an extension (see |
292 | // https://github.com/riscv/riscv-isa-manual/blob/main/src/naming.adoc#base-integer-isa) |
293 | // and is not recognized in clang -cc1 |
294 | if (ExtName == "i" ) |
295 | continue; |
296 | if (IgnoreUnknown && !isSupportedExtension(Ext: ExtName)) |
297 | continue; |
298 | |
299 | if (isExperimentalExtension(Ext: ExtName)) { |
300 | Features.push_back(x: (llvm::Twine("+experimental-" ) + ExtName).str()); |
301 | } else { |
302 | Features.push_back(x: (llvm::Twine("+" ) + ExtName).str()); |
303 | } |
304 | } |
305 | if (AddAllExtensions) { |
306 | for (const RISCVSupportedExtension &Ext : SupportedExtensions) { |
307 | if (Exts.count(x: Ext.Name)) |
308 | continue; |
309 | Features.push_back(x: (llvm::Twine("-" ) + Ext.Name).str()); |
310 | } |
311 | |
312 | for (const RISCVSupportedExtension &Ext : SupportedExperimentalExtensions) { |
313 | if (Exts.count(x: Ext.Name)) |
314 | continue; |
315 | Features.push_back(x: (llvm::Twine("-experimental-" ) + Ext.Name).str()); |
316 | } |
317 | } |
318 | return Features; |
319 | } |
320 | |
321 | static Error getError(const Twine &Message) { |
322 | return createStringError(EC: errc::invalid_argument, S: Message); |
323 | } |
324 | |
325 | static Error getErrorForInvalidExt(StringRef ExtName) { |
326 | if (ExtName.size() == 1) { |
327 | return getError(Message: "unsupported standard user-level extension '" + ExtName + |
328 | "'" ); |
329 | } |
330 | return getError(Message: "unsupported " + getExtensionTypeDesc(Ext: ExtName) + " '" + |
331 | ExtName + "'" ); |
332 | } |
333 | |
334 | // Extensions may have a version number, and may be separated by |
335 | // an underscore '_' e.g.: rv32i2_m2. |
336 | // Version number is divided into major and minor version numbers, |
337 | // separated by a 'p'. If the minor version is 0 then 'p0' can be |
338 | // omitted from the version string. E.g., rv32i2p0, rv32i2, rv32i2p1. |
339 | static Error getExtensionVersion(StringRef Ext, StringRef In, unsigned &Major, |
340 | unsigned &Minor, unsigned &ConsumeLength, |
341 | bool EnableExperimentalExtension, |
342 | bool ExperimentalExtensionVersionCheck) { |
343 | StringRef MajorStr, MinorStr; |
344 | Major = 0; |
345 | Minor = 0; |
346 | ConsumeLength = 0; |
347 | MajorStr = In.take_while(F: isDigit); |
348 | In = In.substr(Start: MajorStr.size()); |
349 | |
350 | if (!MajorStr.empty() && In.consume_front(Prefix: "p" )) { |
351 | MinorStr = In.take_while(F: isDigit); |
352 | In = In.substr(Start: MajorStr.size() + MinorStr.size() - 1); |
353 | |
354 | // Expected 'p' to be followed by minor version number. |
355 | if (MinorStr.empty()) { |
356 | return getError(Message: "minor version number missing after 'p' for extension '" + |
357 | Ext + "'" ); |
358 | } |
359 | } |
360 | |
361 | if (!MajorStr.empty() && MajorStr.getAsInteger(Radix: 10, Result&: Major)) |
362 | return getError(Message: "Failed to parse major version number for extension '" + |
363 | Ext + "'" ); |
364 | |
365 | if (!MinorStr.empty() && MinorStr.getAsInteger(Radix: 10, Result&: Minor)) |
366 | return getError(Message: "Failed to parse minor version number for extension '" + |
367 | Ext + "'" ); |
368 | |
369 | ConsumeLength = MajorStr.size(); |
370 | |
371 | if (!MinorStr.empty()) |
372 | ConsumeLength += MinorStr.size() + 1 /*'p'*/; |
373 | |
374 | // Expected multi-character extension with version number to have no |
375 | // subsequent characters (i.e. must either end string or be followed by |
376 | // an underscore). |
377 | if (Ext.size() > 1 && In.size()) |
378 | return getError( |
379 | Message: "multi-character extensions must be separated by underscores" ); |
380 | |
381 | // If experimental extension, require use of current version number |
382 | if (auto ExperimentalExtension = isExperimentalExtension(Ext)) { |
383 | if (!EnableExperimentalExtension) |
384 | return getError(Message: "requires '-menable-experimental-extensions' " |
385 | "for experimental extension '" + |
386 | Ext + "'" ); |
387 | |
388 | if (ExperimentalExtensionVersionCheck && |
389 | (MajorStr.empty() && MinorStr.empty())) |
390 | return getError( |
391 | Message: "experimental extension requires explicit version number `" + Ext + |
392 | "`" ); |
393 | |
394 | auto SupportedVers = *ExperimentalExtension; |
395 | if (ExperimentalExtensionVersionCheck && |
396 | (Major != SupportedVers.Major || Minor != SupportedVers.Minor)) { |
397 | std::string Error = "unsupported version number " + MajorStr.str(); |
398 | if (!MinorStr.empty()) |
399 | Error += "." + MinorStr.str(); |
400 | Error += " for experimental extension '" + Ext.str() + |
401 | "' (this compiler supports " + utostr(X: SupportedVers.Major) + |
402 | "." + utostr(X: SupportedVers.Minor) + ")" ; |
403 | return getError(Message: Error); |
404 | } |
405 | return Error::success(); |
406 | } |
407 | |
408 | // Exception rule for `g`, we don't have clear version scheme for that on |
409 | // ISA spec. |
410 | if (Ext == "g" ) |
411 | return Error::success(); |
412 | |
413 | if (MajorStr.empty() && MinorStr.empty()) { |
414 | if (auto DefaultVersion = findDefaultVersion(ExtName: Ext)) { |
415 | Major = DefaultVersion->Major; |
416 | Minor = DefaultVersion->Minor; |
417 | } |
418 | // No matter found or not, return success, assume other place will |
419 | // verify. |
420 | return Error::success(); |
421 | } |
422 | |
423 | if (RISCVISAInfo::isSupportedExtension(Ext, MajorVersion: Major, MinorVersion: Minor)) |
424 | return Error::success(); |
425 | |
426 | if (!RISCVISAInfo::isSupportedExtension(Ext)) |
427 | return getErrorForInvalidExt(ExtName: Ext); |
428 | |
429 | std::string Error = "unsupported version number " + MajorStr.str(); |
430 | if (!MinorStr.empty()) |
431 | Error += "." + MinorStr.str(); |
432 | Error += " for extension '" + Ext.str() + "'" ; |
433 | return getError(Message: Error); |
434 | } |
435 | |
436 | llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
437 | RISCVISAInfo::createFromExtMap(unsigned XLen, |
438 | const RISCVISAUtils::OrderedExtensionMap &Exts) { |
439 | assert(XLen == 32 || XLen == 64); |
440 | std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
441 | |
442 | ISAInfo->Exts = Exts; |
443 | |
444 | return RISCVISAInfo::postProcessAndChecking(ISAInfo: std::move(ISAInfo)); |
445 | } |
446 | |
447 | llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
448 | RISCVISAInfo::parseFeatures(unsigned XLen, |
449 | const std::vector<std::string> &Features) { |
450 | assert(XLen == 32 || XLen == 64); |
451 | std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
452 | |
453 | for (auto &Feature : Features) { |
454 | StringRef ExtName = Feature; |
455 | assert(ExtName.size() > 1 && (ExtName[0] == '+' || ExtName[0] == '-')); |
456 | bool Add = ExtName[0] == '+'; |
457 | ExtName = ExtName.drop_front(N: 1); // Drop '+' or '-' |
458 | bool Experimental = stripExperimentalPrefix(Ext&: ExtName); |
459 | auto ExtensionInfos = Experimental |
460 | ? ArrayRef(SupportedExperimentalExtensions) |
461 | : ArrayRef(SupportedExtensions); |
462 | auto ExtensionInfoIterator = |
463 | llvm::lower_bound(Range&: ExtensionInfos, Value&: ExtName, C: LessExtName()); |
464 | |
465 | // Not all features is related to ISA extension, like `relax` or |
466 | // `save-restore`, skip those feature. |
467 | if (ExtensionInfoIterator == ExtensionInfos.end() || |
468 | ExtensionInfoIterator->Name != ExtName) |
469 | continue; |
470 | |
471 | if (Add) |
472 | ISAInfo->Exts[ExtName.str()] = ExtensionInfoIterator->Version; |
473 | else |
474 | ISAInfo->Exts.erase(x: ExtName.str()); |
475 | } |
476 | |
477 | return RISCVISAInfo::postProcessAndChecking(ISAInfo: std::move(ISAInfo)); |
478 | } |
479 | |
480 | llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
481 | RISCVISAInfo::parseNormalizedArchString(StringRef Arch) { |
482 | // RISC-V ISA strings must be [a-z0-9_] |
483 | if (!llvm::all_of( |
484 | Range&: Arch, P: [](char C) { return isDigit(C) || isLower(C) || C == '_'; })) |
485 | return getError(Message: "string may only contain [a-z0-9_]" ); |
486 | |
487 | // Must start with a valid base ISA name. |
488 | unsigned XLen = 0; |
489 | if (Arch.consume_front(Prefix: "rv32" )) |
490 | XLen = 32; |
491 | else if (Arch.consume_front(Prefix: "rv64" )) |
492 | XLen = 64; |
493 | |
494 | if (XLen == 0 || Arch.empty() || (Arch[0] != 'i' && Arch[0] != 'e')) |
495 | return getError(Message: "arch string must begin with valid base ISA" ); |
496 | |
497 | std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
498 | |
499 | // Each extension is of the form ${name}${major_version}p${minor_version} |
500 | // and separated by _. Split by _ and then extract the name and version |
501 | // information for each extension. |
502 | while (!Arch.empty()) { |
503 | if (Arch[0] == '_') { |
504 | if (Arch.size() == 1 || Arch[1] == '_') |
505 | return getError(Message: "extension name missing after separator '_'" ); |
506 | Arch = Arch.drop_front(); |
507 | } |
508 | |
509 | size_t Idx = Arch.find(C: '_'); |
510 | StringRef Ext = Arch.slice(Start: 0, End: Idx); |
511 | Arch = Arch.slice(Start: Idx, End: StringRef::npos); |
512 | |
513 | StringRef Prefix, MinorVersionStr; |
514 | std::tie(args&: Prefix, args&: MinorVersionStr) = Ext.rsplit(Separator: 'p'); |
515 | if (MinorVersionStr.empty()) |
516 | return getError(Message: "extension lacks version in expected format" ); |
517 | unsigned MajorVersion, MinorVersion; |
518 | if (MinorVersionStr.getAsInteger(Radix: 10, Result&: MinorVersion)) |
519 | return getError(Message: "failed to parse minor version number" ); |
520 | |
521 | // Split Prefix into the extension name and the major version number |
522 | // (the trailing digits of Prefix). |
523 | size_t VersionStart = Prefix.size(); |
524 | while (VersionStart != 0) { |
525 | if (!isDigit(C: Prefix[VersionStart - 1])) |
526 | break; |
527 | --VersionStart; |
528 | } |
529 | if (VersionStart == Prefix.size()) |
530 | return getError(Message: "extension lacks version in expected format" ); |
531 | |
532 | if (VersionStart == 0) |
533 | return getError(Message: "missing extension name" ); |
534 | |
535 | StringRef ExtName = Prefix.slice(Start: 0, End: VersionStart); |
536 | StringRef MajorVersionStr = Prefix.slice(Start: VersionStart, End: StringRef::npos); |
537 | if (MajorVersionStr.getAsInteger(Radix: 10, Result&: MajorVersion)) |
538 | return getError(Message: "failed to parse major version number" ); |
539 | |
540 | if ((ExtName[0] == 'z' || ExtName[0] == 's' || ExtName[0] == 'x') && |
541 | (ExtName.size() == 1 || isDigit(C: ExtName[1]))) |
542 | return getError(Message: "'" + Twine(ExtName[0]) + |
543 | "' must be followed by a letter" ); |
544 | |
545 | if (!ISAInfo->Exts |
546 | .emplace( |
547 | args: ExtName.str(), |
548 | args: RISCVISAUtils::ExtensionVersion{.Major: MajorVersion, .Minor: MinorVersion}) |
549 | .second) |
550 | return getError(Message: "duplicate extension '" + ExtName + "'" ); |
551 | } |
552 | ISAInfo->updateImpliedLengths(); |
553 | return std::move(ISAInfo); |
554 | } |
555 | |
556 | llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
557 | RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, |
558 | bool ExperimentalExtensionVersionCheck) { |
559 | // RISC-V ISA strings must be [a-z0-9_] |
560 | if (!llvm::all_of( |
561 | Range&: Arch, P: [](char C) { return isDigit(C) || isLower(C) || C == '_'; })) |
562 | return getError(Message: "string may only contain [a-z0-9_]" ); |
563 | |
564 | // ISA string must begin with rv32, rv64, or a profile. |
565 | unsigned XLen = 0; |
566 | if (Arch.consume_front(Prefix: "rv32" )) { |
567 | XLen = 32; |
568 | } else if (Arch.consume_front(Prefix: "rv64" )) { |
569 | XLen = 64; |
570 | } else { |
571 | // Try parsing as a profile. |
572 | auto ProfileCmp = [](StringRef Arch, const RISCVProfile &Profile) { |
573 | return Arch < Profile.Name; |
574 | }; |
575 | auto I = llvm::upper_bound(Range: SupportedProfiles, Value&: Arch, C: ProfileCmp); |
576 | bool FoundProfile = I != std::begin(arr: SupportedProfiles) && |
577 | Arch.starts_with(Prefix: std::prev(x: I)->Name); |
578 | if (!FoundProfile) { |
579 | I = llvm::upper_bound(Range: SupportedExperimentalProfiles, Value&: Arch, C: ProfileCmp); |
580 | FoundProfile = (I != std::begin(arr: SupportedExperimentalProfiles) && |
581 | Arch.starts_with(Prefix: std::prev(x: I)->Name)); |
582 | if (FoundProfile && !EnableExperimentalExtension) { |
583 | return getError(Message: "requires '-menable-experimental-extensions' " |
584 | "for profile '" + |
585 | std::prev(x: I)->Name + "'" ); |
586 | } |
587 | } |
588 | if (FoundProfile) { |
589 | --I; |
590 | std::string NewArch = I->MArch.str(); |
591 | StringRef ArchWithoutProfile = Arch.drop_front(N: I->Name.size()); |
592 | if (!ArchWithoutProfile.empty()) { |
593 | if (ArchWithoutProfile.front() != '_') |
594 | return getError(Message: "additional extensions must be after separator '_'" ); |
595 | NewArch += ArchWithoutProfile.str(); |
596 | } |
597 | return parseArchString(Arch: NewArch, EnableExperimentalExtension, |
598 | ExperimentalExtensionVersionCheck); |
599 | } |
600 | } |
601 | |
602 | if (XLen == 0 || Arch.empty()) |
603 | return getError( |
604 | Message: "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported " |
605 | "profile name" ); |
606 | |
607 | std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
608 | |
609 | // The canonical order specified in ISA manual. |
610 | // Ref: Table 22.1 in RISC-V User-Level ISA V2.2 |
611 | char Baseline = Arch.front(); |
612 | // Skip the baseline. |
613 | Arch = Arch.drop_front(); |
614 | |
615 | unsigned Major, Minor, ConsumeLength; |
616 | |
617 | // First letter should be 'e', 'i' or 'g'. |
618 | switch (Baseline) { |
619 | default: |
620 | return getError(Message: "first letter after \'rv" + Twine(XLen) + |
621 | "\' should be 'e', 'i' or 'g'" ); |
622 | case 'e': |
623 | case 'i': |
624 | // Baseline is `i` or `e` |
625 | if (auto E = getExtensionVersion( |
626 | Ext: StringRef(&Baseline, 1), In: Arch, Major, Minor, ConsumeLength, |
627 | EnableExperimentalExtension, ExperimentalExtensionVersionCheck)) |
628 | return std::move(E); |
629 | |
630 | ISAInfo->Exts[std::string(1, Baseline)] = {.Major: Major, .Minor: Minor}; |
631 | break; |
632 | case 'g': |
633 | // g expands to extensions in RISCVGImplications. |
634 | if (!Arch.empty() && isDigit(C: Arch.front())) |
635 | return getError(Message: "version not supported for 'g'" ); |
636 | |
637 | // Versions for g are disallowed, and this was checked for previously. |
638 | ConsumeLength = 0; |
639 | |
640 | // No matter which version is given to `g`, we always set imafd to default |
641 | // version since the we don't have clear version scheme for that on |
642 | // ISA spec. |
643 | for (const char *Ext : RISCVGImplications) { |
644 | auto Version = findDefaultVersion(ExtName: Ext); |
645 | assert(Version && "Default extension version not found?" ); |
646 | // Postpone AddExtension until end of this function |
647 | ISAInfo->Exts[std::string(Ext)] = {.Major: Version->Major, .Minor: Version->Minor}; |
648 | } |
649 | break; |
650 | } |
651 | |
652 | // Consume the base ISA version number and any '_' between rvxxx and the |
653 | // first extension |
654 | Arch = Arch.drop_front(N: ConsumeLength); |
655 | |
656 | while (!Arch.empty()) { |
657 | if (Arch.front() == '_') { |
658 | if (Arch.size() == 1 || Arch[1] == '_') |
659 | return getError(Message: "extension name missing after separator '_'" ); |
660 | Arch = Arch.drop_front(); |
661 | } |
662 | |
663 | size_t Idx = Arch.find(C: '_'); |
664 | StringRef Ext = Arch.slice(Start: 0, End: Idx); |
665 | Arch = Arch.slice(Start: Idx, End: StringRef::npos); |
666 | |
667 | do { |
668 | StringRef Name, Vers, Desc; |
669 | if (RISCVISAUtils::AllStdExts.contains(C: Ext.front())) { |
670 | Name = Ext.take_front(N: 1); |
671 | Ext = Ext.drop_front(); |
672 | Vers = Ext; |
673 | Desc = "standard user-level extension" ; |
674 | } else if (Ext.front() == 'z' || Ext.front() == 's' || |
675 | Ext.front() == 'x') { |
676 | // Handle other types of extensions other than the standard |
677 | // general purpose and standard user-level extensions. |
678 | // Parse the ISA string containing non-standard user-level |
679 | // extensions, standard supervisor-level extensions and |
680 | // non-standard supervisor-level extensions. |
681 | // These extensions start with 'z', 's', 'x' prefixes, might have a |
682 | // version number (major, minor) and are separated by a single |
683 | // underscore '_'. We do not enforce a canonical order for them. |
684 | StringRef Type = getExtensionType(Ext); |
685 | Desc = getExtensionTypeDesc(Ext); |
686 | auto Pos = findLastNonVersionCharacter(Ext) + 1; |
687 | Name = Ext.substr(Start: 0, N: Pos); |
688 | Vers = Ext.substr(Start: Pos); |
689 | Ext = StringRef(); |
690 | |
691 | assert(!Type.empty() && "Empty type?" ); |
692 | if (Name.size() == Type.size()) |
693 | return getError(Message: Desc + " name missing after '" + Type + "'" ); |
694 | } else { |
695 | return getError(Message: "invalid standard user-level extension '" + |
696 | Twine(Ext.front()) + "'" ); |
697 | } |
698 | |
699 | unsigned Major, Minor, ConsumeLength; |
700 | if (auto E = getExtensionVersion(Ext: Name, In: Vers, Major, Minor, ConsumeLength, |
701 | EnableExperimentalExtension, |
702 | ExperimentalExtensionVersionCheck)) |
703 | return E; |
704 | |
705 | if (Name.size() == 1) |
706 | Ext = Ext.substr(Start: ConsumeLength); |
707 | |
708 | if (!RISCVISAInfo::isSupportedExtension(Ext: Name)) |
709 | return getErrorForInvalidExt(ExtName: Name); |
710 | |
711 | // Insert and error for duplicates. |
712 | if (!ISAInfo->Exts |
713 | .emplace(args: Name.str(), |
714 | args: RISCVISAUtils::ExtensionVersion{.Major: Major, .Minor: Minor}) |
715 | .second) |
716 | return getError(Message: "duplicated " + Desc + " '" + Name + "'" ); |
717 | |
718 | } while (!Ext.empty()); |
719 | } |
720 | |
721 | return RISCVISAInfo::postProcessAndChecking(ISAInfo: std::move(ISAInfo)); |
722 | } |
723 | |
724 | Error RISCVISAInfo::checkDependency() { |
725 | bool HasE = Exts.count(x: "e" ) != 0; |
726 | bool HasI = Exts.count(x: "i" ) != 0; |
727 | bool HasC = Exts.count(x: "c" ) != 0; |
728 | bool HasF = Exts.count(x: "f" ) != 0; |
729 | bool HasD = Exts.count(x: "d" ) != 0; |
730 | bool HasZfinx = Exts.count(x: "zfinx" ) != 0; |
731 | bool HasVector = Exts.count(x: "zve32x" ) != 0; |
732 | bool HasZvl = MinVLen != 0; |
733 | bool HasZcmt = Exts.count(x: "zcmt" ) != 0; |
734 | |
735 | if (HasI && HasE) |
736 | return getError(Message: "'I' and 'E' extensions are incompatible" ); |
737 | |
738 | if (HasF && HasZfinx) |
739 | return getError(Message: "'f' and 'zfinx' extensions are incompatible" ); |
740 | |
741 | if (HasZvl && !HasVector) |
742 | return getError( |
743 | Message: "'zvl*b' requires 'v' or 'zve*' extension to also be specified" ); |
744 | |
745 | if (Exts.count(x: "zvbb" ) && !HasVector) |
746 | return getError( |
747 | Message: "'zvbb' requires 'v' or 'zve*' extension to also be specified" ); |
748 | |
749 | if (Exts.count(x: "zvbc" ) && !Exts.count(x: "zve64x" )) |
750 | return getError( |
751 | Message: "'zvbc' requires 'v' or 'zve64*' extension to also be specified" ); |
752 | |
753 | if ((Exts.count(x: "zvkb" ) || Exts.count(x: "zvkg" ) || Exts.count(x: "zvkned" ) || |
754 | Exts.count(x: "zvknha" ) || Exts.count(x: "zvksed" ) || Exts.count(x: "zvksh" )) && |
755 | !HasVector) |
756 | return getError( |
757 | Message: "'zvk*' requires 'v' or 'zve*' extension to also be specified" ); |
758 | |
759 | if (Exts.count(x: "zvknhb" ) && !Exts.count(x: "zve64x" )) |
760 | return getError( |
761 | Message: "'zvknhb' requires 'v' or 'zve64*' extension to also be specified" ); |
762 | |
763 | if ((HasZcmt || Exts.count(x: "zcmp" )) && HasD && (HasC || Exts.count(x: "zcd" ))) |
764 | return getError(Message: Twine("'" ) + (HasZcmt ? "zcmt" : "zcmp" ) + |
765 | "' extension is incompatible with '" + |
766 | (HasC ? "c" : "zcd" ) + |
767 | "' extension when 'd' extension is enabled" ); |
768 | |
769 | if (XLen != 32 && Exts.count(x: "zcf" )) |
770 | return getError(Message: "'zcf' is only supported for 'rv32'" ); |
771 | |
772 | if (Exts.count(x: "zacas" ) && !(Exts.count(x: "a" ) || Exts.count(x: "zaamo" ))) |
773 | return getError( |
774 | Message: "'zacas' requires 'a' or 'zaamo' extension to also be specified" ); |
775 | |
776 | if (Exts.count(x: "zabha" ) && !(Exts.count(x: "a" ) || Exts.count(x: "zaamo" ))) |
777 | return getError( |
778 | Message: "'zabha' requires 'a' or 'zaamo' extension to also be specified" ); |
779 | |
780 | if (Exts.count(x: "xwchc" ) != 0) { |
781 | if (XLen != 32) |
782 | return getError(Message: "'Xwchc' is only supported for 'rv32'" ); |
783 | |
784 | if (HasD) |
785 | return getError(Message: "'D' and 'Xwchc' extensions are incompatible" ); |
786 | |
787 | if (Exts.count(x: "zcb" ) != 0) |
788 | return getError(Message: "'Xwchc' and 'Zcb' extensions are incompatible" ); |
789 | } |
790 | |
791 | return Error::success(); |
792 | } |
793 | |
794 | struct ImpliedExtsEntry { |
795 | StringLiteral Name; |
796 | const char *ImpliedExt; |
797 | |
798 | bool operator<(const ImpliedExtsEntry &Other) const { |
799 | return Name < Other.Name; |
800 | } |
801 | }; |
802 | |
803 | static bool operator<(const ImpliedExtsEntry &LHS, StringRef RHS) { |
804 | return LHS.Name < RHS; |
805 | } |
806 | |
807 | static bool operator<(StringRef LHS, const ImpliedExtsEntry &RHS) { |
808 | return LHS < RHS.Name; |
809 | } |
810 | |
811 | #define GET_IMPLIED_EXTENSIONS |
812 | #include "llvm/TargetParser/RISCVTargetParserDef.inc" |
813 | |
814 | void RISCVISAInfo::updateImplication() { |
815 | bool HasE = Exts.count(x: "e" ) != 0; |
816 | bool HasI = Exts.count(x: "i" ) != 0; |
817 | |
818 | // If not in e extension and i extension does not exist, i extension is |
819 | // implied |
820 | if (!HasE && !HasI) { |
821 | auto Version = findDefaultVersion(ExtName: "i" ); |
822 | Exts["i" ] = *Version; |
823 | } |
824 | |
825 | if (HasE && HasI) |
826 | Exts.erase(x: "i" ); |
827 | |
828 | assert(llvm::is_sorted(ImpliedExts) && "Table not sorted by Name" ); |
829 | |
830 | // This loop may execute over 1 iteration since implication can be layered |
831 | // Exits loop if no more implication is applied |
832 | SmallVector<StringRef, 16> WorkList; |
833 | for (auto const &Ext : Exts) |
834 | WorkList.push_back(Elt: Ext.first); |
835 | |
836 | while (!WorkList.empty()) { |
837 | StringRef ExtName = WorkList.pop_back_val(); |
838 | auto Range = std::equal_range(first: std::begin(arr: ImpliedExts), |
839 | last: std::end(arr: ImpliedExts), val: ExtName); |
840 | std::for_each(first: Range.first, last: Range.second, |
841 | f: [&](const ImpliedExtsEntry &Implied) { |
842 | const char *ImpliedExt = Implied.ImpliedExt; |
843 | if (Exts.count(x: ImpliedExt)) |
844 | return; |
845 | auto Version = findDefaultVersion(ExtName: ImpliedExt); |
846 | Exts[ImpliedExt] = *Version; |
847 | WorkList.push_back(Elt: ImpliedExt); |
848 | }); |
849 | } |
850 | |
851 | // Add Zcf if Zce and F are enabled on RV32. |
852 | if (XLen == 32 && Exts.count(x: "zce" ) && Exts.count(x: "f" ) && |
853 | !Exts.count(x: "zcf" )) { |
854 | auto Version = findDefaultVersion(ExtName: "zcf" ); |
855 | Exts["zcf" ] = *Version; |
856 | } |
857 | } |
858 | |
859 | static constexpr StringLiteral CombineIntoExts[] = { |
860 | {"zk" }, {"zkn" }, {"zks" }, {"zvkn" }, {"zvknc" }, |
861 | {"zvkng" }, {"zvks" }, {"zvksc" }, {"zvksg" }, |
862 | }; |
863 | |
864 | void RISCVISAInfo::updateCombination() { |
865 | bool MadeChange = false; |
866 | do { |
867 | MadeChange = false; |
868 | for (StringRef CombineExt : CombineIntoExts) { |
869 | if (Exts.count(x: CombineExt.str())) |
870 | continue; |
871 | |
872 | // Look up the extension in the ImpliesExt table to find everything it |
873 | // depends on. |
874 | auto Range = std::equal_range(first: std::begin(arr: ImpliedExts), |
875 | last: std::end(arr: ImpliedExts), val: CombineExt); |
876 | bool HasAllRequiredFeatures = std::all_of( |
877 | first: Range.first, last: Range.second, pred: [&](const ImpliedExtsEntry &Implied) { |
878 | return Exts.count(x: Implied.ImpliedExt); |
879 | }); |
880 | if (HasAllRequiredFeatures) { |
881 | auto Version = findDefaultVersion(ExtName: CombineExt); |
882 | Exts[CombineExt.str()] = *Version; |
883 | MadeChange = true; |
884 | } |
885 | } |
886 | } while (MadeChange); |
887 | } |
888 | |
889 | void RISCVISAInfo::updateImpliedLengths() { |
890 | assert(FLen == 0 && MaxELenFp == 0 && MaxELen == 0 && MinVLen == 0 && |
891 | "Expected lengths to be initialied to zero" ); |
892 | |
893 | // TODO: Handle q extension. |
894 | if (Exts.count(x: "d" )) |
895 | FLen = 64; |
896 | else if (Exts.count(x: "f" )) |
897 | FLen = 32; |
898 | |
899 | if (Exts.count(x: "v" )) { |
900 | MaxELenFp = std::max(a: MaxELenFp, b: 64u); |
901 | MaxELen = std::max(a: MaxELen, b: 64u); |
902 | } |
903 | |
904 | for (auto const &Ext : Exts) { |
905 | StringRef ExtName = Ext.first; |
906 | // Infer MaxELen and MaxELenFp from Zve(32/64)(x/f/d) |
907 | if (ExtName.consume_front(Prefix: "zve" )) { |
908 | unsigned ZveELen; |
909 | if (ExtName.consumeInteger(Radix: 10, Result&: ZveELen)) |
910 | continue; |
911 | |
912 | if (ExtName == "f" ) |
913 | MaxELenFp = std::max(a: MaxELenFp, b: 32u); |
914 | else if (ExtName == "d" ) |
915 | MaxELenFp = std::max(a: MaxELenFp, b: 64u); |
916 | else if (ExtName != "x" ) |
917 | continue; |
918 | |
919 | MaxELen = std::max(a: MaxELen, b: ZveELen); |
920 | continue; |
921 | } |
922 | |
923 | // Infer MinVLen from zvl*b. |
924 | if (ExtName.consume_front(Prefix: "zvl" )) { |
925 | unsigned ZvlLen; |
926 | if (ExtName.consumeInteger(Radix: 10, Result&: ZvlLen)) |
927 | continue; |
928 | |
929 | if (ExtName != "b" ) |
930 | continue; |
931 | |
932 | MinVLen = std::max(a: MinVLen, b: ZvlLen); |
933 | continue; |
934 | } |
935 | } |
936 | } |
937 | |
938 | std::string RISCVISAInfo::toString() const { |
939 | std::string Buffer; |
940 | raw_string_ostream Arch(Buffer); |
941 | |
942 | Arch << "rv" << XLen; |
943 | |
944 | ListSeparator LS("_" ); |
945 | for (auto const &Ext : Exts) { |
946 | StringRef ExtName = Ext.first; |
947 | auto ExtInfo = Ext.second; |
948 | Arch << LS << ExtName; |
949 | Arch << ExtInfo.Major << "p" << ExtInfo.Minor; |
950 | } |
951 | |
952 | return Arch.str(); |
953 | } |
954 | |
955 | llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
956 | RISCVISAInfo::postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo) { |
957 | ISAInfo->updateImplication(); |
958 | ISAInfo->updateCombination(); |
959 | ISAInfo->updateImpliedLengths(); |
960 | |
961 | if (Error Result = ISAInfo->checkDependency()) |
962 | return std::move(Result); |
963 | return std::move(ISAInfo); |
964 | } |
965 | |
966 | StringRef RISCVISAInfo::computeDefaultABI() const { |
967 | if (XLen == 32) { |
968 | if (Exts.count(x: "e" )) |
969 | return "ilp32e" ; |
970 | if (Exts.count(x: "d" )) |
971 | return "ilp32d" ; |
972 | if (Exts.count(x: "f" )) |
973 | return "ilp32f" ; |
974 | return "ilp32" ; |
975 | } else if (XLen == 64) { |
976 | if (Exts.count(x: "e" )) |
977 | return "lp64e" ; |
978 | if (Exts.count(x: "d" )) |
979 | return "lp64d" ; |
980 | if (Exts.count(x: "f" )) |
981 | return "lp64f" ; |
982 | return "lp64" ; |
983 | } |
984 | llvm_unreachable("Invalid XLEN" ); |
985 | } |
986 | |
987 | bool RISCVISAInfo::isSupportedExtensionWithVersion(StringRef Ext) { |
988 | if (Ext.empty()) |
989 | return false; |
990 | |
991 | auto Pos = findLastNonVersionCharacter(Ext) + 1; |
992 | StringRef Name = Ext.substr(Start: 0, N: Pos); |
993 | StringRef Vers = Ext.substr(Start: Pos); |
994 | if (Vers.empty()) |
995 | return false; |
996 | |
997 | unsigned Major, Minor, ConsumeLength; |
998 | if (auto E = getExtensionVersion(Ext: Name, In: Vers, Major, Minor, ConsumeLength, |
999 | EnableExperimentalExtension: true, ExperimentalExtensionVersionCheck: true)) { |
1000 | consumeError(Err: std::move(E)); |
1001 | return false; |
1002 | } |
1003 | |
1004 | return true; |
1005 | } |
1006 | |
1007 | std::string RISCVISAInfo::getTargetFeatureForExtension(StringRef Ext) { |
1008 | if (Ext.empty()) |
1009 | return std::string(); |
1010 | |
1011 | auto Pos = findLastNonVersionCharacter(Ext) + 1; |
1012 | StringRef Name = Ext.substr(Start: 0, N: Pos); |
1013 | |
1014 | if (Pos != Ext.size() && !isSupportedExtensionWithVersion(Ext)) |
1015 | return std::string(); |
1016 | |
1017 | if (!isSupportedExtension(Ext: Name)) |
1018 | return std::string(); |
1019 | |
1020 | return isExperimentalExtension(Ext: Name) ? "experimental-" + Name.str() |
1021 | : Name.str(); |
1022 | } |
1023 | |