| //===-- RISCVISAInfo.cpp - RISCV Arch String Parser -------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Support/RISCVISAInfo.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <array> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| using namespace llvm; |
| |
| namespace { |
| /// Represents the major and version number components of a RISC-V extension |
| struct RISCVExtensionVersion { |
| unsigned Major; |
| unsigned Minor; |
| }; |
| |
| struct RISCVSupportedExtension { |
| const char *Name; |
| /// Supported version. |
| RISCVExtensionVersion Version; |
| }; |
| |
| } // end anonymous namespace |
| |
| static constexpr StringLiteral AllStdExts = "mafdqlcbkjtpvnh"; |
| |
| static const RISCVSupportedExtension SupportedExtensions[] = { |
| {"i", RISCVExtensionVersion{2, 0}}, |
| {"e", RISCVExtensionVersion{1, 9}}, |
| {"m", RISCVExtensionVersion{2, 0}}, |
| {"a", RISCVExtensionVersion{2, 0}}, |
| {"f", RISCVExtensionVersion{2, 0}}, |
| {"d", RISCVExtensionVersion{2, 0}}, |
| {"c", RISCVExtensionVersion{2, 0}}, |
| |
| {"h", RISCVExtensionVersion{1, 0}}, |
| |
| {"zihintpause", RISCVExtensionVersion{2, 0}}, |
| |
| {"zfhmin", RISCVExtensionVersion{1, 0}}, |
| {"zfh", RISCVExtensionVersion{1, 0}}, |
| |
| {"zfinx", RISCVExtensionVersion{1, 0}}, |
| {"zdinx", RISCVExtensionVersion{1, 0}}, |
| {"zhinxmin", RISCVExtensionVersion{1, 0}}, |
| {"zhinx", RISCVExtensionVersion{1, 0}}, |
| |
| {"zba", RISCVExtensionVersion{1, 0}}, |
| {"zbb", RISCVExtensionVersion{1, 0}}, |
| {"zbc", RISCVExtensionVersion{1, 0}}, |
| {"zbs", RISCVExtensionVersion{1, 0}}, |
| |
| {"zbkb", RISCVExtensionVersion{1, 0}}, |
| {"zbkc", RISCVExtensionVersion{1, 0}}, |
| {"zbkx", RISCVExtensionVersion{1, 0}}, |
| {"zknd", RISCVExtensionVersion{1, 0}}, |
| {"zkne", RISCVExtensionVersion{1, 0}}, |
| {"zknh", RISCVExtensionVersion{1, 0}}, |
| {"zksed", RISCVExtensionVersion{1, 0}}, |
| {"zksh", RISCVExtensionVersion{1, 0}}, |
| {"zkr", RISCVExtensionVersion{1, 0}}, |
| {"zkn", RISCVExtensionVersion{1, 0}}, |
| {"zks", RISCVExtensionVersion{1, 0}}, |
| {"zkt", RISCVExtensionVersion{1, 0}}, |
| {"zk", RISCVExtensionVersion{1, 0}}, |
| |
| {"zmmul", RISCVExtensionVersion{1, 0}}, |
| |
| {"v", RISCVExtensionVersion{1, 0}}, |
| {"zvl32b", RISCVExtensionVersion{1, 0}}, |
| {"zvl64b", RISCVExtensionVersion{1, 0}}, |
| {"zvl128b", RISCVExtensionVersion{1, 0}}, |
| {"zvl256b", RISCVExtensionVersion{1, 0}}, |
| {"zvl512b", RISCVExtensionVersion{1, 0}}, |
| {"zvl1024b", RISCVExtensionVersion{1, 0}}, |
| {"zvl2048b", RISCVExtensionVersion{1, 0}}, |
| {"zvl4096b", RISCVExtensionVersion{1, 0}}, |
| {"zvl8192b", RISCVExtensionVersion{1, 0}}, |
| {"zvl16384b", RISCVExtensionVersion{1, 0}}, |
| {"zvl32768b", RISCVExtensionVersion{1, 0}}, |
| {"zvl65536b", RISCVExtensionVersion{1, 0}}, |
| {"zve32x", RISCVExtensionVersion{1, 0}}, |
| {"zve32f", RISCVExtensionVersion{1, 0}}, |
| {"zve64x", RISCVExtensionVersion{1, 0}}, |
| {"zve64f", RISCVExtensionVersion{1, 0}}, |
| {"zve64d", RISCVExtensionVersion{1, 0}}, |
| |
| {"zicbom", RISCVExtensionVersion{1, 0}}, |
| {"zicboz", RISCVExtensionVersion{1, 0}}, |
| {"zicbop", RISCVExtensionVersion{1, 0}}, |
| |
| {"svnapot", RISCVExtensionVersion{1, 0}}, |
| {"svpbmt", RISCVExtensionVersion{1, 0}}, |
| {"svinval", RISCVExtensionVersion{1, 0}}, |
| {"xventanacondops", RISCVExtensionVersion{1, 0}}, |
| {"xtheadvdot", RISCVExtensionVersion{1, 0}}, |
| }; |
| |
| static const RISCVSupportedExtension SupportedExperimentalExtensions[] = { |
| {"zihintntl", RISCVExtensionVersion{0, 2}}, |
| |
| {"zca", RISCVExtensionVersion{0, 70}}, |
| {"zcd", RISCVExtensionVersion{0, 70}}, |
| {"zcf", RISCVExtensionVersion{0, 70}}, |
| {"zvfh", RISCVExtensionVersion{0, 1}}, |
| {"zawrs", RISCVExtensionVersion{1, 0}}, |
| {"ztso", RISCVExtensionVersion{0, 1}}, |
| }; |
| |
| static bool stripExperimentalPrefix(StringRef &Ext) { |
| return Ext.consume_front("experimental-"); |
| } |
| |
| // This function finds the first character that doesn't belong to a version |
| // (e.g. zba1p0 is extension 'zba' of version '1p0'). So the function will |
| // consume [0-9]*p[0-9]* starting from the backward. An extension name will not |
| // end with a digit or the letter 'p', so this function will parse correctly. |
| // NOTE: This function is NOT able to take empty strings or strings that only |
| // have version numbers and no extension name. It assumes the extension name |
| // will be at least more than one character. |
| static size_t findFirstNonVersionCharacter(StringRef Ext) { |
| assert(!Ext.empty() && |
| "Already guarded by if-statement in ::parseArchString"); |
| |
| int Pos = Ext.size() - 1; |
| while (Pos > 0 && isDigit(Ext[Pos])) |
| Pos--; |
| if (Pos > 0 && Ext[Pos] == 'p' && isDigit(Ext[Pos - 1])) { |
| Pos--; |
| while (Pos > 0 && isDigit(Ext[Pos])) |
| Pos--; |
| } |
| return Pos; |
| } |
| |
| namespace { |
| struct FindByName { |
| FindByName(StringRef Ext) : Ext(Ext){}; |
| StringRef Ext; |
| bool operator()(const RISCVSupportedExtension &ExtInfo) { |
| return ExtInfo.Name == Ext; |
| } |
| }; |
| } // namespace |
| |
| static std::optional<RISCVExtensionVersion> |
| findDefaultVersion(StringRef ExtName) { |
| // Find default version of an extension. |
| // TODO: We might set default version based on profile or ISA spec. |
| for (auto &ExtInfo : {ArrayRef(SupportedExtensions), |
| ArrayRef(SupportedExperimentalExtensions)}) { |
| auto ExtensionInfoIterator = llvm::find_if(ExtInfo, FindByName(ExtName)); |
| |
| if (ExtensionInfoIterator == ExtInfo.end()) { |
| continue; |
| } |
| return ExtensionInfoIterator->Version; |
| } |
| return std::nullopt; |
| } |
| |
| void RISCVISAInfo::addExtension(StringRef ExtName, unsigned MajorVersion, |
| unsigned MinorVersion) { |
| RISCVExtensionInfo Ext; |
| Ext.ExtName = ExtName.str(); |
| Ext.MajorVersion = MajorVersion; |
| Ext.MinorVersion = MinorVersion; |
| Exts[ExtName.str()] = Ext; |
| } |
| |
| static StringRef getExtensionTypeDesc(StringRef Ext) { |
| if (Ext.startswith("sx")) |
| return "non-standard supervisor-level extension"; |
| if (Ext.startswith("s")) |
| return "standard supervisor-level extension"; |
| if (Ext.startswith("x")) |
| return "non-standard user-level extension"; |
| if (Ext.startswith("z")) |
| return "standard user-level extension"; |
| return StringRef(); |
| } |
| |
| static StringRef getExtensionType(StringRef Ext) { |
| if (Ext.startswith("sx")) |
| return "sx"; |
| if (Ext.startswith("s")) |
| return "s"; |
| if (Ext.startswith("x")) |
| return "x"; |
| if (Ext.startswith("z")) |
| return "z"; |
| return StringRef(); |
| } |
| |
| static std::optional<RISCVExtensionVersion> |
| isExperimentalExtension(StringRef Ext) { |
| auto ExtIterator = |
| llvm::find_if(SupportedExperimentalExtensions, FindByName(Ext)); |
| if (ExtIterator == std::end(SupportedExperimentalExtensions)) |
| return std::nullopt; |
| |
| return ExtIterator->Version; |
| } |
| |
| bool RISCVISAInfo::isSupportedExtensionFeature(StringRef Ext) { |
| bool IsExperimental = stripExperimentalPrefix(Ext); |
| |
| if (IsExperimental) |
| return llvm::any_of(SupportedExperimentalExtensions, FindByName(Ext)); |
| else |
| return llvm::any_of(SupportedExtensions, FindByName(Ext)); |
| } |
| |
| bool RISCVISAInfo::isSupportedExtension(StringRef Ext) { |
| return llvm::any_of(SupportedExtensions, FindByName(Ext)) || |
| llvm::any_of(SupportedExperimentalExtensions, FindByName(Ext)); |
| } |
| |
| bool RISCVISAInfo::isSupportedExtension(StringRef Ext, unsigned MajorVersion, |
| unsigned MinorVersion) { |
| auto FindByNameAndVersion = [=](const RISCVSupportedExtension &ExtInfo) { |
| return ExtInfo.Name == Ext && (MajorVersion == ExtInfo.Version.Major) && |
| (MinorVersion == ExtInfo.Version.Minor); |
| }; |
| return llvm::any_of(SupportedExtensions, FindByNameAndVersion) || |
| llvm::any_of(SupportedExperimentalExtensions, FindByNameAndVersion); |
| } |
| |
| bool RISCVISAInfo::hasExtension(StringRef Ext) const { |
| stripExperimentalPrefix(Ext); |
| |
| if (!isSupportedExtension(Ext)) |
| return false; |
| |
| return Exts.count(Ext.str()) != 0; |
| } |
| |
| // Get the rank for single-letter extension, lower value meaning higher |
| // priority. |
| static int singleLetterExtensionRank(char Ext) { |
| switch (Ext) { |
| case 'i': |
| return -2; |
| case 'e': |
| return -1; |
| default: |
| break; |
| } |
| |
| size_t Pos = AllStdExts.find(Ext); |
| int Rank; |
| if (Pos == StringRef::npos) |
| // If we got an unknown extension letter, then give it an alphabetical |
| // order, but after all known standard extensions. |
| Rank = AllStdExts.size() + (Ext - 'a'); |
| else |
| Rank = Pos; |
| |
| return Rank; |
| } |
| |
| // Get the rank for multi-letter extension, lower value meaning higher |
| // priority/order in canonical order. |
| static int multiLetterExtensionRank(const std::string &ExtName) { |
| assert(ExtName.length() >= 2); |
| int HighOrder; |
| int LowOrder = 0; |
| // The order between multi-char extensions: s -> h -> z -> x. |
| char ExtClass = ExtName[0]; |
| switch (ExtClass) { |
| case 's': |
| HighOrder = 0; |
| break; |
| case 'z': |
| HighOrder = 1; |
| // `z` extension must be sorted by canonical order of second letter. |
| // e.g. zmx has higher rank than zax. |
| LowOrder = singleLetterExtensionRank(ExtName[1]); |
| break; |
| case 'x': |
| HighOrder = 2; |
| break; |
| default: |
| llvm_unreachable("Unknown prefix for multi-char extension"); |
| return -1; |
| } |
| |
| return (HighOrder << 8) + LowOrder; |
| } |
| |
| // Compare function for extension. |
| // Only compare the extension name, ignore version comparison. |
| bool RISCVISAInfo::compareExtension(const std::string &LHS, |
| const std::string &RHS) { |
| size_t LHSLen = LHS.length(); |
| size_t RHSLen = RHS.length(); |
| if (LHSLen == 1 && RHSLen != 1) |
| return true; |
| |
| if (LHSLen != 1 && RHSLen == 1) |
| return false; |
| |
| if (LHSLen == 1 && RHSLen == 1) |
| return singleLetterExtensionRank(LHS[0]) < |
| singleLetterExtensionRank(RHS[0]); |
| |
| // Both are multi-char ext here. |
| int LHSRank = multiLetterExtensionRank(LHS); |
| int RHSRank = multiLetterExtensionRank(RHS); |
| if (LHSRank != RHSRank) |
| return LHSRank < RHSRank; |
| |
| // If the rank is same, it must be sorted by lexicographic order. |
| return LHS < RHS; |
| } |
| |
| void RISCVISAInfo::toFeatures( |
| std::vector<StringRef> &Features, |
| llvm::function_ref<StringRef(const Twine &)> StrAlloc, |
| bool AddAllExtensions) const { |
| for (auto const &Ext : Exts) { |
| StringRef ExtName = Ext.first; |
| |
| if (ExtName == "i") |
| continue; |
| |
| if (isExperimentalExtension(ExtName)) { |
| Features.push_back(StrAlloc("+experimental-" + ExtName)); |
| } else { |
| Features.push_back(StrAlloc("+" + ExtName)); |
| } |
| } |
| if (AddAllExtensions) { |
| for (const RISCVSupportedExtension &Ext : SupportedExtensions) { |
| if (Exts.count(Ext.Name)) |
| continue; |
| Features.push_back(StrAlloc(Twine("-") + Ext.Name)); |
| } |
| |
| for (const RISCVSupportedExtension &Ext : SupportedExperimentalExtensions) { |
| if (Exts.count(Ext.Name)) |
| continue; |
| Features.push_back(StrAlloc(Twine("-experimental-") + Ext.Name)); |
| } |
| } |
| } |
| |
| // Extensions may have a version number, and may be separated by |
| // an underscore '_' e.g.: rv32i2_m2. |
| // Version number is divided into major and minor version numbers, |
| // separated by a 'p'. If the minor version is 0 then 'p0' can be |
| // omitted from the version string. E.g., rv32i2p0, rv32i2, rv32i2p1. |
| static Error getExtensionVersion(StringRef Ext, StringRef In, unsigned &Major, |
| unsigned &Minor, unsigned &ConsumeLength, |
| bool EnableExperimentalExtension, |
| bool ExperimentalExtensionVersionCheck) { |
| StringRef MajorStr, MinorStr; |
| Major = 0; |
| Minor = 0; |
| ConsumeLength = 0; |
| MajorStr = In.take_while(isDigit); |
| In = In.substr(MajorStr.size()); |
| |
| if (!MajorStr.empty() && In.consume_front("p")) { |
| MinorStr = In.take_while(isDigit); |
| In = In.substr(MajorStr.size() + MinorStr.size() - 1); |
| |
| // Expected 'p' to be followed by minor version number. |
| if (MinorStr.empty()) { |
| return createStringError( |
| errc::invalid_argument, |
| "minor version number missing after 'p' for extension '" + Ext + "'"); |
| } |
| } |
| |
| if (!MajorStr.empty() && MajorStr.getAsInteger(10, Major)) |
| return createStringError( |
| errc::invalid_argument, |
| "Failed to parse major version number for extension '" + Ext + "'"); |
| |
| if (!MinorStr.empty() && MinorStr.getAsInteger(10, Minor)) |
| return createStringError( |
| errc::invalid_argument, |
| "Failed to parse minor version number for extension '" + Ext + "'"); |
| |
| ConsumeLength = MajorStr.size(); |
| |
| if (!MinorStr.empty()) |
| ConsumeLength += MinorStr.size() + 1 /*'p'*/; |
| |
| // Expected multi-character extension with version number to have no |
| // subsequent characters (i.e. must either end string or be followed by |
| // an underscore). |
| if (Ext.size() > 1 && In.size()) { |
| std::string Error = |
| "multi-character extensions must be separated by underscores"; |
| return createStringError(errc::invalid_argument, Error); |
| } |
| |
| // If experimental extension, require use of current version number number |
| if (auto ExperimentalExtension = isExperimentalExtension(Ext)) { |
| if (!EnableExperimentalExtension) { |
| std::string Error = "requires '-menable-experimental-extensions' for " |
| "experimental extension '" + |
| Ext.str() + "'"; |
| return createStringError(errc::invalid_argument, Error); |
| } |
| |
| if (ExperimentalExtensionVersionCheck && |
| (MajorStr.empty() && MinorStr.empty())) { |
| std::string Error = |
| "experimental extension requires explicit version number `" + |
| Ext.str() + "`"; |
| return createStringError(errc::invalid_argument, Error); |
| } |
| |
| auto SupportedVers = *ExperimentalExtension; |
| if (ExperimentalExtensionVersionCheck && |
| (Major != SupportedVers.Major || Minor != SupportedVers.Minor)) { |
| std::string Error = "unsupported version number " + MajorStr.str(); |
| if (!MinorStr.empty()) |
| Error += "." + MinorStr.str(); |
| Error += " for experimental extension '" + Ext.str() + |
| "' (this compiler supports " + utostr(SupportedVers.Major) + |
| "." + utostr(SupportedVers.Minor) + ")"; |
| return createStringError(errc::invalid_argument, Error); |
| } |
| return Error::success(); |
| } |
| |
| // Exception rule for `g`, we don't have clear version scheme for that on |
| // ISA spec. |
| if (Ext == "g") |
| return Error::success(); |
| |
| if (MajorStr.empty() && MinorStr.empty()) { |
| if (auto DefaultVersion = findDefaultVersion(Ext)) { |
| Major = DefaultVersion->Major; |
| Minor = DefaultVersion->Minor; |
| } |
| // No matter found or not, return success, assume other place will |
| // verify. |
| return Error::success(); |
| } |
| |
| if (RISCVISAInfo::isSupportedExtension(Ext, Major, Minor)) |
| return Error::success(); |
| |
| std::string Error = "unsupported version number " + std::string(MajorStr); |
| if (!MinorStr.empty()) |
| Error += "." + MinorStr.str(); |
| Error += " for extension '" + Ext.str() + "'"; |
| return createStringError(errc::invalid_argument, Error); |
| } |
| |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
| RISCVISAInfo::parseFeatures(unsigned XLen, |
| const std::vector<std::string> &Features) { |
| assert(XLen == 32 || XLen == 64); |
| std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
| |
| for (auto &Feature : Features) { |
| StringRef ExtName = Feature; |
| bool Experimental = false; |
| assert(ExtName.size() > 1 && (ExtName[0] == '+' || ExtName[0] == '-')); |
| bool Add = ExtName[0] == '+'; |
| ExtName = ExtName.drop_front(1); // Drop '+' or '-' |
| Experimental = stripExperimentalPrefix(ExtName); |
| auto ExtensionInfos = Experimental |
| ? ArrayRef(SupportedExperimentalExtensions) |
| : ArrayRef(SupportedExtensions); |
| auto ExtensionInfoIterator = |
| llvm::find_if(ExtensionInfos, FindByName(ExtName)); |
| |
| // Not all features is related to ISA extension, like `relax` or |
| // `save-restore`, skip those feature. |
| if (ExtensionInfoIterator == ExtensionInfos.end()) |
| continue; |
| |
| if (Add) |
| ISAInfo->addExtension(ExtName, ExtensionInfoIterator->Version.Major, |
| ExtensionInfoIterator->Version.Minor); |
| else |
| ISAInfo->Exts.erase(ExtName.str()); |
| } |
| |
| return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); |
| } |
| |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
| RISCVISAInfo::parseNormalizedArchString(StringRef Arch) { |
| if (llvm::any_of(Arch, isupper)) { |
| return createStringError(errc::invalid_argument, |
| "string must be lowercase"); |
| } |
| // Must start with a valid base ISA name. |
| unsigned XLen; |
| if (Arch.startswith("rv32i") || Arch.startswith("rv32e")) |
| XLen = 32; |
| else if (Arch.startswith("rv64i") || Arch.startswith("rv64e")) |
| XLen = 64; |
| else |
| return createStringError(errc::invalid_argument, |
| "arch string must begin with valid base ISA"); |
| std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
| // Discard rv32/rv64 prefix. |
| Arch = Arch.substr(4); |
| |
| // Each extension is of the form ${name}${major_version}p${minor_version} |
| // and separated by _. Split by _ and then extract the name and version |
| // information for each extension. |
| SmallVector<StringRef, 8> Split; |
| Arch.split(Split, '_'); |
| for (StringRef Ext : Split) { |
| StringRef Prefix, MinorVersionStr; |
| std::tie(Prefix, MinorVersionStr) = Ext.rsplit('p'); |
| if (MinorVersionStr.empty()) |
| return createStringError(errc::invalid_argument, |
| "extension lacks version in expected format"); |
| unsigned MajorVersion, MinorVersion; |
| if (MinorVersionStr.getAsInteger(10, MinorVersion)) |
| return createStringError(errc::invalid_argument, |
| "failed to parse minor version number"); |
| |
| // Split Prefix into the extension name and the major version number |
| // (the trailing digits of Prefix). |
| int TrailingDigits = 0; |
| StringRef ExtName = Prefix; |
| while (!ExtName.empty()) { |
| if (!isDigit(ExtName.back())) |
| break; |
| ExtName = ExtName.drop_back(1); |
| TrailingDigits++; |
| } |
| if (!TrailingDigits) |
| return createStringError(errc::invalid_argument, |
| "extension lacks version in expected format"); |
| |
| StringRef MajorVersionStr = Prefix.take_back(TrailingDigits); |
| if (MajorVersionStr.getAsInteger(10, MajorVersion)) |
| return createStringError(errc::invalid_argument, |
| "failed to parse major version number"); |
| ISAInfo->addExtension(ExtName, MajorVersion, MinorVersion); |
| } |
| ISAInfo->updateFLen(); |
| ISAInfo->updateMinVLen(); |
| ISAInfo->updateMaxELen(); |
| return std::move(ISAInfo); |
| } |
| |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
| RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, |
| bool ExperimentalExtensionVersionCheck, |
| bool IgnoreUnknown) { |
| // RISC-V ISA strings must be lowercase. |
| if (llvm::any_of(Arch, isupper)) { |
| return createStringError(errc::invalid_argument, |
| "string must be lowercase"); |
| } |
| |
| bool HasRV64 = Arch.startswith("rv64"); |
| // ISA string must begin with rv32 or rv64. |
| if (!(Arch.startswith("rv32") || HasRV64) || (Arch.size() < 5)) { |
| return createStringError(errc::invalid_argument, |
| "string must begin with rv32{i,e,g} or rv64{i,g}"); |
| } |
| |
| unsigned XLen = HasRV64 ? 64 : 32; |
| std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); |
| |
| // The canonical order specified in ISA manual. |
| // Ref: Table 22.1 in RISC-V User-Level ISA V2.2 |
| StringRef StdExts = AllStdExts; |
| char Baseline = Arch[4]; |
| |
| // First letter should be 'e', 'i' or 'g'. |
| switch (Baseline) { |
| default: |
| return createStringError(errc::invalid_argument, |
| "first letter should be 'e', 'i' or 'g'"); |
| case 'e': { |
| // Extension 'e' is not allowed in rv64. |
| if (HasRV64) |
| return createStringError( |
| errc::invalid_argument, |
| "standard user-level extension 'e' requires 'rv32'"); |
| break; |
| } |
| case 'i': |
| break; |
| case 'g': |
| // g = imafd |
| StdExts = StdExts.drop_front(4); |
| break; |
| } |
| |
| // Skip rvxxx |
| StringRef Exts = Arch.substr(5); |
| |
| // Remove multi-letter standard extensions, non-standard extensions and |
| // supervisor-level extensions. They have 'z', 'x', 's', 'sx' prefixes. |
| // Parse them at the end. |
| // Find the very first occurrence of 's', 'x' or 'z'. |
| StringRef OtherExts; |
| size_t Pos = Exts.find_first_of("zsx"); |
| if (Pos != StringRef::npos) { |
| OtherExts = Exts.substr(Pos); |
| Exts = Exts.substr(0, Pos); |
| } |
| |
| unsigned Major, Minor, ConsumeLength; |
| if (auto E = getExtensionVersion(std::string(1, Baseline), Exts, Major, Minor, |
| ConsumeLength, EnableExperimentalExtension, |
| ExperimentalExtensionVersionCheck)) |
| return std::move(E); |
| |
| if (Baseline == 'g') { |
| // No matter which version is given to `g`, we always set imafd to default |
| // version since the we don't have clear version scheme for that on |
| // ISA spec. |
| for (const auto *Ext : {"i", "m", "a", "f", "d"}) |
| if (auto Version = findDefaultVersion(Ext)) |
| ISAInfo->addExtension(Ext, Version->Major, Version->Minor); |
| else |
| llvm_unreachable("Default extension version not found?"); |
| } else |
| // Baseline is `i` or `e` |
| ISAInfo->addExtension(std::string(1, Baseline), Major, Minor); |
| |
| // Consume the base ISA version number and any '_' between rvxxx and the |
| // first extension |
| Exts = Exts.drop_front(ConsumeLength); |
| Exts.consume_front("_"); |
| |
| // TODO: Use version number when setting target features |
| |
| auto StdExtsItr = StdExts.begin(); |
| auto StdExtsEnd = StdExts.end(); |
| auto GoToNextExt = [](StringRef::iterator &I, unsigned ConsumeLength) { |
| I += 1 + ConsumeLength; |
| if (*I == '_') |
| ++I; |
| }; |
| for (auto I = Exts.begin(), E = Exts.end(); I != E;) { |
| char C = *I; |
| |
| // Check ISA extensions are specified in the canonical order. |
| while (StdExtsItr != StdExtsEnd && *StdExtsItr != C) |
| ++StdExtsItr; |
| |
| if (StdExtsItr == StdExtsEnd) { |
| // Either c contains a valid extension but it was not given in |
| // canonical order or it is an invalid extension. |
| if (StdExts.contains(C)) { |
| return createStringError( |
| errc::invalid_argument, |
| "standard user-level extension not given in canonical order '%c'", |
| C); |
| } |
| |
| return createStringError(errc::invalid_argument, |
| "invalid standard user-level extension '%c'", C); |
| } |
| |
| // Move to next char to prevent repeated letter. |
| ++StdExtsItr; |
| |
| std::string Next; |
| unsigned Major, Minor, ConsumeLength; |
| if (std::next(I) != E) |
| Next = std::string(std::next(I), E); |
| if (auto E = getExtensionVersion(std::string(1, C), Next, Major, Minor, |
| ConsumeLength, EnableExperimentalExtension, |
| ExperimentalExtensionVersionCheck)) { |
| if (IgnoreUnknown) { |
| consumeError(std::move(E)); |
| GoToNextExt(I, ConsumeLength); |
| continue; |
| } |
| return std::move(E); |
| } |
| |
| // The order is OK, then push it into features. |
| // TODO: Use version number when setting target features |
| // Currently LLVM supports only "mafdcvh". |
| if (!isSupportedExtension(StringRef(&C, 1))) { |
| if (IgnoreUnknown) { |
| GoToNextExt(I, ConsumeLength); |
| continue; |
| } |
| return createStringError(errc::invalid_argument, |
| "unsupported standard user-level extension '%c'", |
| C); |
| } |
| ISAInfo->addExtension(std::string(1, C), Major, Minor); |
| |
| // Consume full extension name and version, including any optional '_' |
| // between this extension and the next |
| GoToNextExt(I, ConsumeLength); |
| } |
| |
| // Handle other types of extensions other than the standard |
| // general purpose and standard user-level extensions. |
| // Parse the ISA string containing non-standard user-level |
| // extensions, standard supervisor-level extensions and |
| // non-standard supervisor-level extensions. |
| // These extensions start with 'z', 'x', 's', 'sx' prefixes, follow a |
| // canonical order, might have a version number (major, minor) |
| // and are separated by a single underscore '_'. |
| // Set the hardware features for the extensions that are supported. |
| |
| // Multi-letter extensions are seperated by a single underscore |
| // as described in RISC-V User-Level ISA V2.2. |
| SmallVector<StringRef, 8> Split; |
| OtherExts.split(Split, '_'); |
| |
| SmallVector<StringRef, 8> AllExts; |
| std::array<StringRef, 4> Prefix{"z", "x", "s", "sx"}; |
| auto I = Prefix.begin(); |
| auto E = Prefix.end(); |
| if (Split.size() > 1 || Split[0] != "") { |
| for (StringRef Ext : Split) { |
| if (Ext.empty()) |
| return createStringError(errc::invalid_argument, |
| "extension name missing after separator '_'"); |
| |
| StringRef Type = getExtensionType(Ext); |
| StringRef Desc = getExtensionTypeDesc(Ext); |
| auto Pos = findFirstNonVersionCharacter(Ext) + 1; |
| StringRef Name(Ext.substr(0, Pos)); |
| StringRef Vers(Ext.substr(Pos)); |
| |
| if (Type.empty()) { |
| if (IgnoreUnknown) |
| continue; |
| return createStringError(errc::invalid_argument, |
| "invalid extension prefix '" + Ext + "'"); |
| } |
| |
| // Check ISA extensions are specified in the canonical order. |
| while (I != E && *I != Type) |
| ++I; |
| |
| if (I == E) { |
| if (IgnoreUnknown) |
| continue; |
| return createStringError(errc::invalid_argument, |
| "%s not given in canonical order '%s'", |
| Desc.str().c_str(), Ext.str().c_str()); |
| } |
| |
| if (!IgnoreUnknown && Name.size() == Type.size()) { |
| return createStringError(errc::invalid_argument, |
| "%s name missing after '%s'", |
| Desc.str().c_str(), Type.str().c_str()); |
| } |
| |
| unsigned Major, Minor, ConsumeLength; |
| if (auto E = getExtensionVersion(Name, Vers, Major, Minor, ConsumeLength, |
| EnableExperimentalExtension, |
| ExperimentalExtensionVersionCheck)) { |
| if (IgnoreUnknown) { |
| consumeError(std::move(E)); |
| continue; |
| } |
| return std::move(E); |
| } |
| |
| // Check if duplicated extension. |
| if (!IgnoreUnknown && llvm::is_contained(AllExts, Name)) { |
| return createStringError(errc::invalid_argument, "duplicated %s '%s'", |
| Desc.str().c_str(), Name.str().c_str()); |
| } |
| |
| ISAInfo->addExtension(Name, Major, Minor); |
| // Extension format is correct, keep parsing the extensions. |
| // TODO: Save Type, Name, Major, Minor to avoid parsing them later. |
| AllExts.push_back(Name); |
| } |
| } |
| |
| for (auto Ext : AllExts) { |
| if (!isSupportedExtension(Ext)) { |
| StringRef Desc = getExtensionTypeDesc(getExtensionType(Ext)); |
| return createStringError(errc::invalid_argument, "unsupported %s '%s'", |
| Desc.str().c_str(), Ext.str().c_str()); |
| } |
| } |
| |
| return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); |
| } |
| |
| Error RISCVISAInfo::checkDependency() { |
| bool IsRv32 = XLen == 32; |
| bool HasE = Exts.count("e") != 0; |
| bool HasD = Exts.count("d") != 0; |
| bool HasF = Exts.count("f") != 0; |
| bool HasZfinx = Exts.count("zfinx") != 0; |
| bool HasZdinx = Exts.count("zdinx") != 0; |
| bool HasVector = Exts.count("zve32x") != 0; |
| bool HasZve32f = Exts.count("zve32f") != 0; |
| bool HasZve64d = Exts.count("zve64d") != 0; |
| bool HasZvl = MinVLen != 0; |
| |
| if (HasE && !IsRv32) |
| return createStringError( |
| errc::invalid_argument, |
| "standard user-level extension 'e' requires 'rv32'"); |
| |
| // It's illegal to specify the 'd' (double-precision floating point) |
| // extension without also specifying the 'f' (single precision |
| // floating-point) extension. |
| // TODO: This has been removed in later specs, which specify that D implies F |
| if (HasD && !HasF) |
| return createStringError(errc::invalid_argument, |
| "d requires f extension to also be specified"); |
| |
| if (HasZve32f && !HasF && !HasZfinx) |
| return createStringError( |
| errc::invalid_argument, |
| "zve32f requires f or zfinx extension to also be specified"); |
| |
| if (HasZve64d && !HasD && !HasZdinx) |
| return createStringError( |
| errc::invalid_argument, |
| "zve64d requires d or zdinx extension to also be specified"); |
| |
| if (Exts.count("zvfh") && !Exts.count("zfh") && !Exts.count("zfhmin") && |
| !Exts.count("zhinx") && !Exts.count("zhinxmin")) |
| return createStringError( |
| errc::invalid_argument, |
| "zvfh requires zfh, zfhmin, zhinx or zhinxmin extension to also be " |
| "specified"); |
| |
| if (HasZvl && !HasVector) |
| return createStringError( |
| errc::invalid_argument, |
| "zvl*b requires v or zve* extension to also be specified"); |
| |
| // Additional dependency checks. |
| // TODO: The 'q' extension requires rv64. |
| // TODO: It is illegal to specify 'e' extensions with 'f' and 'd'. |
| |
| return Error::success(); |
| } |
| |
| static const char *ImpliedExtsV[] = {"zvl128b", "zve64d", "f", "d"}; |
| static const char *ImpliedExtsZfhmin[] = {"f"}; |
| static const char *ImpliedExtsZfh[] = {"f"}; |
| static const char *ImpliedExtsZdinx[] = {"zfinx"}; |
| static const char *ImpliedExtsZhinxmin[] = {"zfinx"}; |
| static const char *ImpliedExtsZhinx[] = {"zfinx"}; |
| static const char *ImpliedExtsZve64d[] = {"zve64f"}; |
| static const char *ImpliedExtsZve64f[] = {"zve64x", "zve32f"}; |
| static const char *ImpliedExtsZve64x[] = {"zve32x", "zvl64b"}; |
| static const char *ImpliedExtsZve32f[] = {"zve32x"}; |
| static const char *ImpliedExtsZve32x[] = {"zvl32b"}; |
| static const char *ImpliedExtsZvl65536b[] = {"zvl32768b"}; |
| static const char *ImpliedExtsZvl32768b[] = {"zvl16384b"}; |
| static const char *ImpliedExtsZvl16384b[] = {"zvl8192b"}; |
| static const char *ImpliedExtsZvl8192b[] = {"zvl4096b"}; |
| static const char *ImpliedExtsZvl4096b[] = {"zvl2048b"}; |
| static const char *ImpliedExtsZvl2048b[] = {"zvl1024b"}; |
| static const char *ImpliedExtsZvl1024b[] = {"zvl512b"}; |
| static const char *ImpliedExtsZvl512b[] = {"zvl256b"}; |
| static const char *ImpliedExtsZvl256b[] = {"zvl128b"}; |
| static const char *ImpliedExtsZvl128b[] = {"zvl64b"}; |
| static const char *ImpliedExtsZvl64b[] = {"zvl32b"}; |
| static const char *ImpliedExtsZk[] = {"zkn", "zkt", "zkr"}; |
| static const char *ImpliedExtsZkn[] = {"zbkb", "zbkc", "zbkx", |
| "zkne", "zknd", "zknh"}; |
| static const char *ImpliedExtsZks[] = {"zbkb", "zbkc", "zbkx", "zksed", "zksh"}; |
| static const char *ImpliedExtsZvfh[] = {"zve32f"}; |
| static const char *ImpliedExtsXTHeadVdot[] = {"v"}; |
| |
| struct ImpliedExtsEntry { |
| StringLiteral Name; |
| ArrayRef<const char *> Exts; |
| |
| bool operator<(const ImpliedExtsEntry &Other) const { |
| return Name < Other.Name; |
| } |
| |
| bool operator<(StringRef Other) const { return Name < Other; } |
| }; |
| |
| // Note: The table needs to be sorted by name. |
| static constexpr ImpliedExtsEntry ImpliedExts[] = { |
| {{"v"}, {ImpliedExtsV}}, |
| {{"xtheadvdot"}, {ImpliedExtsXTHeadVdot}}, |
| {{"zdinx"}, {ImpliedExtsZdinx}}, |
| {{"zfh"}, {ImpliedExtsZfh}}, |
| {{"zfhmin"}, {ImpliedExtsZfhmin}}, |
| {{"zhinx"}, {ImpliedExtsZhinx}}, |
| {{"zhinxmin"}, {ImpliedExtsZhinxmin}}, |
| {{"zk"}, {ImpliedExtsZk}}, |
| {{"zkn"}, {ImpliedExtsZkn}}, |
| {{"zks"}, {ImpliedExtsZks}}, |
| {{"zve32f"}, {ImpliedExtsZve32f}}, |
| {{"zve32x"}, {ImpliedExtsZve32x}}, |
| {{"zve64d"}, {ImpliedExtsZve64d}}, |
| {{"zve64f"}, {ImpliedExtsZve64f}}, |
| {{"zve64x"}, {ImpliedExtsZve64x}}, |
| {{"zvfh"}, {ImpliedExtsZvfh}}, |
| {{"zvl1024b"}, {ImpliedExtsZvl1024b}}, |
| {{"zvl128b"}, {ImpliedExtsZvl128b}}, |
| {{"zvl16384b"}, {ImpliedExtsZvl16384b}}, |
| {{"zvl2048b"}, {ImpliedExtsZvl2048b}}, |
| {{"zvl256b"}, {ImpliedExtsZvl256b}}, |
| {{"zvl32768b"}, {ImpliedExtsZvl32768b}}, |
| {{"zvl4096b"}, {ImpliedExtsZvl4096b}}, |
| {{"zvl512b"}, {ImpliedExtsZvl512b}}, |
| {{"zvl64b"}, {ImpliedExtsZvl64b}}, |
| {{"zvl65536b"}, {ImpliedExtsZvl65536b}}, |
| {{"zvl8192b"}, {ImpliedExtsZvl8192b}}, |
| }; |
| |
| void RISCVISAInfo::updateImplication() { |
| bool HasE = Exts.count("e") != 0; |
| bool HasI = Exts.count("i") != 0; |
| |
| // If not in e extension and i extension does not exist, i extension is |
| // implied |
| if (!HasE && !HasI) { |
| auto Version = findDefaultVersion("i"); |
| addExtension("i", Version->Major, Version->Minor); |
| } |
| |
| assert(llvm::is_sorted(ImpliedExts) && "Table not sorted by Name"); |
| |
| // This loop may execute over 1 iteration since implication can be layered |
| // Exits loop if no more implication is applied |
| SmallSetVector<StringRef, 16> WorkList; |
| for (auto const &Ext : Exts) |
| WorkList.insert(Ext.first); |
| |
| while (!WorkList.empty()) { |
| StringRef ExtName = WorkList.pop_back_val(); |
| auto I = llvm::lower_bound(ImpliedExts, ExtName); |
| if (I != std::end(ImpliedExts) && I->Name == ExtName) { |
| for (const char *ImpliedExt : I->Exts) { |
| if (WorkList.count(ImpliedExt)) |
| continue; |
| if (Exts.count(ImpliedExt)) |
| continue; |
| auto Version = findDefaultVersion(ImpliedExt); |
| addExtension(ImpliedExt, Version->Major, Version->Minor); |
| WorkList.insert(ImpliedExt); |
| } |
| } |
| } |
| } |
| |
| struct CombinedExtsEntry { |
| StringLiteral CombineExt; |
| ArrayRef<const char *> RequiredExts; |
| }; |
| |
| static constexpr CombinedExtsEntry CombineIntoExts[] = { |
| {{"zk"}, {ImpliedExtsZk}}, |
| {{"zkn"}, {ImpliedExtsZkn}}, |
| {{"zks"}, {ImpliedExtsZks}}, |
| }; |
| |
| void RISCVISAInfo::updateCombination() { |
| bool IsNewCombine = false; |
| do { |
| IsNewCombine = false; |
| for (CombinedExtsEntry CombineIntoExt : CombineIntoExts) { |
| auto CombineExt = CombineIntoExt.CombineExt; |
| auto RequiredExts = CombineIntoExt.RequiredExts; |
| if (hasExtension(CombineExt)) |
| continue; |
| bool IsAllRequiredFeatureExist = true; |
| for (const char *Ext : RequiredExts) |
| IsAllRequiredFeatureExist &= hasExtension(Ext); |
| if (IsAllRequiredFeatureExist) { |
| auto Version = findDefaultVersion(CombineExt); |
| addExtension(CombineExt, Version->Major, Version->Minor); |
| IsNewCombine = true; |
| } |
| } |
| } while (IsNewCombine); |
| } |
| |
| void RISCVISAInfo::updateFLen() { |
| FLen = 0; |
| // TODO: Handle q extension. |
| if (Exts.count("d")) |
| FLen = 64; |
| else if (Exts.count("f")) |
| FLen = 32; |
| } |
| |
| void RISCVISAInfo::updateMinVLen() { |
| for (auto const &Ext : Exts) { |
| StringRef ExtName = Ext.first; |
| bool IsZvlExt = ExtName.consume_front("zvl") && ExtName.consume_back("b"); |
| if (IsZvlExt) { |
| unsigned ZvlLen; |
| if (!ExtName.getAsInteger(10, ZvlLen)) |
| MinVLen = std::max(MinVLen, ZvlLen); |
| } |
| } |
| } |
| |
| void RISCVISAInfo::updateMaxELen() { |
| // handles EEW restriction by sub-extension zve |
| for (auto const &Ext : Exts) { |
| StringRef ExtName = Ext.first; |
| bool IsZveExt = ExtName.consume_front("zve"); |
| if (IsZveExt) { |
| if (ExtName.back() == 'f') |
| MaxELenFp = std::max(MaxELenFp, 32u); |
| if (ExtName.back() == 'd') |
| MaxELenFp = std::max(MaxELenFp, 64u); |
| ExtName = ExtName.drop_back(); |
| unsigned ZveELen; |
| ExtName.getAsInteger(10, ZveELen); |
| MaxELen = std::max(MaxELen, ZveELen); |
| } |
| } |
| } |
| |
| std::string RISCVISAInfo::toString() const { |
| std::string Buffer; |
| raw_string_ostream Arch(Buffer); |
| |
| Arch << "rv" << XLen; |
| |
| ListSeparator LS("_"); |
| for (auto const &Ext : Exts) { |
| StringRef ExtName = Ext.first; |
| auto ExtInfo = Ext.second; |
| Arch << LS << ExtName; |
| Arch << ExtInfo.MajorVersion << "p" << ExtInfo.MinorVersion; |
| } |
| |
| return Arch.str(); |
| } |
| |
| std::vector<std::string> RISCVISAInfo::toFeatureVector() const { |
| std::vector<std::string> FeatureVector; |
| for (auto const &Ext : Exts) { |
| std::string ExtName = Ext.first; |
| if (ExtName == "i") // i is not recognized in clang -cc1 |
| continue; |
| std::string Feature = isExperimentalExtension(ExtName) |
| ? "+experimental-" + ExtName |
| : "+" + ExtName; |
| FeatureVector.push_back(Feature); |
| } |
| return FeatureVector; |
| } |
| |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> |
| RISCVISAInfo::postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo) { |
| ISAInfo->updateImplication(); |
| ISAInfo->updateCombination(); |
| ISAInfo->updateFLen(); |
| ISAInfo->updateMinVLen(); |
| ISAInfo->updateMaxELen(); |
| |
| if (Error Result = ISAInfo->checkDependency()) |
| return std::move(Result); |
| return std::move(ISAInfo); |
| } |
| |
| StringRef RISCVISAInfo::computeDefaultABI() const { |
| if (XLen == 32) { |
| if (hasExtension("d")) |
| return "ilp32d"; |
| if (hasExtension("e")) |
| return "ilp32e"; |
| return "ilp32"; |
| } else if (XLen == 64) { |
| if (hasExtension("d")) |
| return "lp64d"; |
| return "lp64"; |
| } |
| llvm_unreachable("Invalid XLEN"); |
| } |