| //===-- RISCVISAInfo.cpp - RISC-V Arch String Parser ----------------------===// | |
| // | |
| // 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/TargetParser/RISCVISAInfo.h" | |
| #include "llvm/ADT/STLExtras.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 <atomic> | |
| #include <optional> | |
| #include <string> | |
| #include <vector> | |
| using namespace llvm; | |
| namespace { | |
| struct RISCVSupportedExtension { | |
| const char *Name; | |
| /// Supported version. | |
| RISCVISAUtils::ExtensionVersion Version; | |
| bool operator<(const RISCVSupportedExtension &RHS) const { | |
| return StringRef(Name) < StringRef(RHS.Name); | |
| } | |
| }; | |
| struct RISCVProfile { | |
| StringLiteral Name; | |
| StringLiteral MArch; | |
| bool operator<(const RISCVProfile &RHS) const { | |
| return StringRef(Name) < StringRef(RHS.Name); | |
| } | |
| }; | |
| } // end anonymous namespace | |
| static const char *RISCVGImplications[] = {"i", "m", "a", "f", "d"}; | |
| static const char *RISCVGImplicationsZi[] = {"zicsr", "zifencei"}; | |
| #define GET_SUPPORTED_EXTENSIONS | |
| #include "llvm/TargetParser/RISCVTargetParserDef.inc" | |
| #define GET_SUPPORTED_PROFILES | |
| #include "llvm/TargetParser/RISCVTargetParserDef.inc" | |
| static void verifyTables() { | |
| #ifndef NDEBUG | |
| static std::atomic<bool> TableChecked(false); | |
| if (!TableChecked.load(std::memory_order_relaxed)) { | |
| assert(llvm::is_sorted(SupportedExtensions) && | |
| "Extensions are not sorted by name"); | |
| assert(llvm::is_sorted(SupportedExperimentalExtensions) && | |
| "Experimental extensions are not sorted by name"); | |
| assert(llvm::is_sorted(SupportedProfiles) && | |
| "Profiles are not sorted by name"); | |
| assert(llvm::is_sorted(SupportedExperimentalProfiles) && | |
| "Experimental profiles are not sorted by name"); | |
| TableChecked.store(true, std::memory_order_relaxed); | |
| } | |
| #endif | |
| } | |
| static void PrintExtension(StringRef Name, StringRef Version, | |
| StringRef Description) { | |
| outs().indent(4); | |
| unsigned VersionWidth = Description.empty() ? 0 : 10; | |
| outs() << left_justify(Name, 21) << left_justify(Version, VersionWidth) | |
| << Description << "\n"; | |
| } | |
| void RISCVISAInfo::printSupportedExtensions(StringMap<StringRef> &DescMap) { | |
| outs() << "All available -march extensions for RISC-V\n\n"; | |
| PrintExtension("Name", "Version", (DescMap.empty() ? "" : "Description")); | |
| RISCVISAUtils::OrderedExtensionMap ExtMap; | |
| for (const auto &E : SupportedExtensions) | |
| ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| for (const auto &E : ExtMap) { | |
| std::string Version = | |
| std::to_string(E.second.Major) + "." + std::to_string(E.second.Minor); | |
| PrintExtension(E.first, Version, DescMap[E.first]); | |
| } | |
| outs() << "\nExperimental extensions\n"; | |
| ExtMap.clear(); | |
| for (const auto &E : SupportedExperimentalExtensions) | |
| ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| for (const auto &E : ExtMap) { | |
| std::string Version = | |
| std::to_string(E.second.Major) + "." + std::to_string(E.second.Minor); | |
| PrintExtension(E.first, Version, DescMap["experimental-" + E.first]); | |
| } | |
| outs() << "\nSupported Profiles\n"; | |
| for (const auto &P : SupportedProfiles) | |
| outs().indent(4) << P.Name << "\n"; | |
| outs() << "\nExperimental Profiles\n"; | |
| for (const auto &P : SupportedExperimentalProfiles) | |
| outs().indent(4) << P.Name << "\n"; | |
| outs() << "\nUse -march to specify the target's extension.\n" | |
| "For example, clang -march=rv32i_v1p0\n"; | |
| } | |
| void RISCVISAInfo::printEnabledExtensions( | |
| bool IsRV64, std::set<StringRef> &EnabledFeatureNames, | |
| StringMap<StringRef> &DescMap) { | |
| outs() << "Extensions enabled for the given RISC-V target\n\n"; | |
| PrintExtension("Name", "Version", (DescMap.empty() ? "" : "Description")); | |
| RISCVISAUtils::OrderedExtensionMap FullExtMap; | |
| RISCVISAUtils::OrderedExtensionMap ExtMap; | |
| for (const auto &E : SupportedExtensions) | |
| if (EnabledFeatureNames.count(E.Name) != 0) { | |
| FullExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| } | |
| for (const auto &E : ExtMap) { | |
| std::string Version = | |
| std::to_string(E.second.Major) + "." + std::to_string(E.second.Minor); | |
| PrintExtension(E.first, Version, DescMap[E.first]); | |
| } | |
| outs() << "\nExperimental extensions\n"; | |
| ExtMap.clear(); | |
| for (const auto &E : SupportedExperimentalExtensions) { | |
| StringRef Name(E.Name); | |
| if (EnabledFeatureNames.count("experimental-" + Name.str()) != 0) { | |
| FullExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; | |
| } | |
| } | |
| for (const auto &E : ExtMap) { | |
| std::string Version = | |
| std::to_string(E.second.Major) + "." + std::to_string(E.second.Minor); | |
| PrintExtension(E.first, Version, DescMap["experimental-" + E.first]); | |
| } | |
| unsigned XLen = IsRV64 ? 64 : 32; | |
| if (auto ISAString = RISCVISAInfo::createFromExtMap(XLen, FullExtMap)) | |
| outs() << "\nISA String: " << ISAString.get()->toString() << "\n"; | |
| } | |
| static bool stripExperimentalPrefix(StringRef &Ext) { | |
| return Ext.consume_front("experimental-"); | |
| } | |
| // This function finds the last 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 findLastNonVersionCharacter(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 LessExtName { | |
| bool operator()(const RISCVSupportedExtension &LHS, StringRef RHS) { | |
| return StringRef(LHS.Name) < RHS; | |
| } | |
| bool operator()(StringRef LHS, const RISCVSupportedExtension &RHS) { | |
| return LHS < StringRef(RHS.Name); | |
| } | |
| }; | |
| } // namespace | |
| static std::optional<RISCVISAUtils::ExtensionVersion> | |
| 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 I = llvm::lower_bound(ExtInfo, ExtName, LessExtName()); | |
| if (I == ExtInfo.end() || I->Name != ExtName) | |
| continue; | |
| return I->Version; | |
| } | |
| return std::nullopt; | |
| } | |
| static StringRef getExtensionTypeDesc(StringRef Ext) { | |
| if (Ext.starts_with('s')) | |
| return "standard supervisor-level extension"; | |
| if (Ext.starts_with('x')) | |
| return "non-standard user-level extension"; | |
| if (Ext.starts_with('z')) | |
| return "standard user-level extension"; | |
| return StringRef(); | |
| } | |
| static StringRef getExtensionType(StringRef Ext) { | |
| if (Ext.starts_with('s')) | |
| return "s"; | |
| if (Ext.starts_with('x')) | |
| return "x"; | |
| if (Ext.starts_with('z')) | |
| return "z"; | |
| return StringRef(); | |
| } | |
| static std::optional<RISCVISAUtils::ExtensionVersion> | |
| isExperimentalExtension(StringRef Ext) { | |
| auto I = | |
| llvm::lower_bound(SupportedExperimentalExtensions, Ext, LessExtName()); | |
| if (I == std::end(SupportedExperimentalExtensions) || I->Name != Ext) | |
| return std::nullopt; | |
| return I->Version; | |
| } | |
| bool RISCVISAInfo::isSupportedExtensionFeature(StringRef Ext) { | |
| bool IsExperimental = stripExperimentalPrefix(Ext); | |
| ArrayRef<RISCVSupportedExtension> ExtInfo = | |
| IsExperimental ? ArrayRef(SupportedExperimentalExtensions) | |
| : ArrayRef(SupportedExtensions); | |
| auto I = llvm::lower_bound(ExtInfo, Ext, LessExtName()); | |
| return I != ExtInfo.end() && I->Name == Ext; | |
| } | |
| bool RISCVISAInfo::isSupportedExtension(StringRef Ext) { | |
| verifyTables(); | |
| for (auto ExtInfo : {ArrayRef(SupportedExtensions), | |
| ArrayRef(SupportedExperimentalExtensions)}) { | |
| auto I = llvm::lower_bound(ExtInfo, Ext, LessExtName()); | |
| if (I != ExtInfo.end() && I->Name == Ext) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool RISCVISAInfo::isSupportedExtension(StringRef Ext, unsigned MajorVersion, | |
| unsigned MinorVersion) { | |
| for (auto ExtInfo : {ArrayRef(SupportedExtensions), | |
| ArrayRef(SupportedExperimentalExtensions)}) { | |
| auto Range = | |
| std::equal_range(ExtInfo.begin(), ExtInfo.end(), Ext, LessExtName()); | |
| for (auto I = Range.first, E = Range.second; I != E; ++I) | |
| if (I->Version.Major == MajorVersion && I->Version.Minor == MinorVersion) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool RISCVISAInfo::hasExtension(StringRef Ext) const { | |
| stripExperimentalPrefix(Ext); | |
| if (!isSupportedExtension(Ext)) | |
| return false; | |
| return Exts.count(Ext.str()) != 0; | |
| } | |
| std::vector<std::string> RISCVISAInfo::toFeatures(bool AddAllExtensions, | |
| bool IgnoreUnknown) const { | |
| std::vector<std::string> Features; | |
| for (const auto &[ExtName, _] : Exts) { | |
| if (IgnoreUnknown && !isSupportedExtension(ExtName)) | |
| continue; | |
| if (isExperimentalExtension(ExtName)) { | |
| Features.push_back((llvm::Twine("+experimental-") + ExtName).str()); | |
| } else { | |
| Features.push_back((llvm::Twine("+") + ExtName).str()); | |
| } | |
| } | |
| if (AddAllExtensions) { | |
| for (const RISCVSupportedExtension &Ext : SupportedExtensions) { | |
| if (Exts.count(Ext.Name)) | |
| continue; | |
| Features.push_back((llvm::Twine("-") + Ext.Name).str()); | |
| } | |
| for (const RISCVSupportedExtension &Ext : SupportedExperimentalExtensions) { | |
| if (Exts.count(Ext.Name)) | |
| continue; | |
| Features.push_back((llvm::Twine("-experimental-") + Ext.Name).str()); | |
| } | |
| } | |
| return Features; | |
| } | |
| static Error getError(const Twine &Message) { | |
| return createStringError(errc::invalid_argument, Message); | |
| } | |
| static Error getErrorForInvalidExt(StringRef ExtName) { | |
| if (ExtName.size() == 1) { | |
| return getError("unsupported standard user-level extension '" + ExtName + | |
| "'"); | |
| } | |
| return getError("unsupported " + getExtensionTypeDesc(ExtName) + " '" + | |
| ExtName + "'"); | |
| } | |
| // 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 getError("minor version number missing after 'p' for extension '" + | |
| Ext + "'"); | |
| } | |
| } | |
| if (!MajorStr.empty() && MajorStr.getAsInteger(10, Major)) | |
| return getError("Failed to parse major version number for extension '" + | |
| Ext + "'"); | |
| if (!MinorStr.empty() && MinorStr.getAsInteger(10, Minor)) | |
| return getError("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()) | |
| return getError( | |
| "multi-character extensions must be separated by underscores"); | |
| // If experimental extension, require use of current version number | |
| if (auto ExperimentalExtension = isExperimentalExtension(Ext)) { | |
| if (!EnableExperimentalExtension) | |
| return getError("requires '-menable-experimental-extensions' " | |
| "for experimental extension '" + | |
| Ext + "'"); | |
| if (ExperimentalExtensionVersionCheck && | |
| (MajorStr.empty() && MinorStr.empty())) | |
| return getError( | |
| "experimental extension requires explicit version number `" + Ext + | |
| "`"); | |
| 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 getError(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(); | |
| if (!RISCVISAInfo::isSupportedExtension(Ext)) | |
| return getErrorForInvalidExt(Ext); | |
| std::string Error = "unsupported version number " + MajorStr.str(); | |
| if (!MinorStr.empty()) | |
| Error += "." + MinorStr.str(); | |
| Error += " for extension '" + Ext.str() + "'"; | |
| return getError(Error); | |
| } | |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> | |
| RISCVISAInfo::createFromExtMap(unsigned XLen, | |
| const RISCVISAUtils::OrderedExtensionMap &Exts) { | |
| assert(XLen == 32 || XLen == 64); | |
| std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); | |
| ISAInfo->Exts = Exts; | |
| return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); | |
| } | |
| 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 (StringRef ExtName : Features) { | |
| assert(ExtName.size() > 1 && (ExtName[0] == '+' || ExtName[0] == '-')); | |
| bool Add = ExtName[0] == '+'; | |
| ExtName = ExtName.drop_front(1); // Drop '+' or '-' | |
| bool Experimental = stripExperimentalPrefix(ExtName); | |
| auto ExtensionInfos = Experimental | |
| ? ArrayRef(SupportedExperimentalExtensions) | |
| : ArrayRef(SupportedExtensions); | |
| auto ExtensionInfoIterator = | |
| llvm::lower_bound(ExtensionInfos, ExtName, LessExtName()); | |
| // Not all features is related to ISA extension, like `relax` or | |
| // `save-restore`, skip those feature. | |
| if (ExtensionInfoIterator == ExtensionInfos.end() || | |
| ExtensionInfoIterator->Name != ExtName) | |
| continue; | |
| if (Add) | |
| ISAInfo->Exts[ExtName.str()] = ExtensionInfoIterator->Version; | |
| else | |
| ISAInfo->Exts.erase(ExtName.str()); | |
| } | |
| return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); | |
| } | |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> | |
| RISCVISAInfo::parseNormalizedArchString(StringRef Arch) { | |
| // RISC-V ISA strings must be [a-z0-9_] | |
| if (!llvm::all_of( | |
| Arch, [](char C) { return isDigit(C) || isLower(C) || C == '_'; })) | |
| return getError("string may only contain [a-z0-9_]"); | |
| // Must start with a valid base ISA name. | |
| unsigned XLen = 0; | |
| if (Arch.consume_front("rv32")) | |
| XLen = 32; | |
| else if (Arch.consume_front("rv64")) | |
| XLen = 64; | |
| if (XLen == 0 || Arch.empty() || (Arch[0] != 'i' && Arch[0] != 'e')) | |
| return getError("arch string must begin with valid base ISA"); | |
| std::unique_ptr<RISCVISAInfo> ISAInfo(new RISCVISAInfo(XLen)); | |
| // 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. | |
| while (!Arch.empty()) { | |
| if (Arch[0] == '_') { | |
| if (Arch.size() == 1 || Arch[1] == '_') | |
| return getError("extension name missing after separator '_'"); | |
| Arch = Arch.drop_front(); | |
| } | |
| size_t Idx = Arch.find('_'); | |
| StringRef Ext = Arch.slice(0, Idx); | |
| Arch = Arch.substr(Idx); | |
| StringRef Prefix, MinorVersionStr; | |
| std::tie(Prefix, MinorVersionStr) = Ext.rsplit('p'); | |
| if (MinorVersionStr.empty()) | |
| return getError("extension lacks version in expected format"); | |
| unsigned MajorVersion, MinorVersion; | |
| if (MinorVersionStr.getAsInteger(10, MinorVersion)) | |
| return getError("failed to parse minor version number"); | |
| // Split Prefix into the extension name and the major version number | |
| // (the trailing digits of Prefix). | |
| size_t VersionStart = Prefix.size(); | |
| while (VersionStart != 0) { | |
| if (!isDigit(Prefix[VersionStart - 1])) | |
| break; | |
| --VersionStart; | |
| } | |
| if (VersionStart == Prefix.size()) | |
| return getError("extension lacks version in expected format"); | |
| if (VersionStart == 0) | |
| return getError("missing extension name"); | |
| StringRef ExtName = Prefix.slice(0, VersionStart); | |
| StringRef MajorVersionStr = Prefix.substr(VersionStart); | |
| if (MajorVersionStr.getAsInteger(10, MajorVersion)) | |
| return getError("failed to parse major version number"); | |
| if ((ExtName[0] == 'z' || ExtName[0] == 's' || ExtName[0] == 'x') && | |
| (ExtName.size() == 1 || isDigit(ExtName[1]))) | |
| return getError("'" + Twine(ExtName[0]) + | |
| "' must be followed by a letter"); | |
| if (!ISAInfo->Exts | |
| .emplace( | |
| ExtName.str(), | |
| RISCVISAUtils::ExtensionVersion{MajorVersion, MinorVersion}) | |
| .second) | |
| return getError("duplicate extension '" + ExtName + "'"); | |
| } | |
| ISAInfo->updateImpliedLengths(); | |
| return std::move(ISAInfo); | |
| } | |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> | |
| RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, | |
| bool ExperimentalExtensionVersionCheck) { | |
| // RISC-V ISA strings must be [a-z0-9_] | |
| if (!llvm::all_of( | |
| Arch, [](char C) { return isDigit(C) || isLower(C) || C == '_'; })) | |
| return getError("string may only contain [a-z0-9_]"); | |
| // ISA string must begin with rv32, rv64, or a profile. | |
| unsigned XLen = 0; | |
| if (Arch.consume_front("rv32")) { | |
| XLen = 32; | |
| } else if (Arch.consume_front("rv64")) { | |
| XLen = 64; | |
| } else { | |
| // Try parsing as a profile. | |
| auto ProfileCmp = [](StringRef Arch, const RISCVProfile &Profile) { | |
| return Arch < Profile.Name; | |
| }; | |
| auto I = llvm::upper_bound(SupportedProfiles, Arch, ProfileCmp); | |
| bool FoundProfile = I != std::begin(SupportedProfiles) && | |
| Arch.starts_with(std::prev(I)->Name); | |
| if (!FoundProfile) { | |
| I = llvm::upper_bound(SupportedExperimentalProfiles, Arch, ProfileCmp); | |
| FoundProfile = (I != std::begin(SupportedExperimentalProfiles) && | |
| Arch.starts_with(std::prev(I)->Name)); | |
| if (FoundProfile && !EnableExperimentalExtension) { | |
| return getError("requires '-menable-experimental-extensions' " | |
| "for profile '" + | |
| std::prev(I)->Name + "'"); | |
| } | |
| } | |
| if (FoundProfile) { | |
| --I; | |
| std::string NewArch = I->MArch.str(); | |
| StringRef ArchWithoutProfile = Arch.drop_front(I->Name.size()); | |
| if (!ArchWithoutProfile.empty()) { | |
| if (ArchWithoutProfile.front() != '_') | |
| return getError("additional extensions must be after separator '_'"); | |
| NewArch += ArchWithoutProfile.str(); | |
| } | |
| return parseArchString(NewArch, EnableExperimentalExtension, | |
| ExperimentalExtensionVersionCheck); | |
| } | |
| } | |
| if (XLen == 0 || Arch.empty()) | |
| return getError( | |
| "string must begin with rv32{i,e,g,y}, rv64{i,e,g,y}, or a supported " | |
| "profile name"); | |
| 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 | |
| char Baseline = Arch.front(); | |
| // Skip the baseline. | |
| Arch = Arch.drop_front(); | |
| unsigned Major, Minor, ConsumeLength; | |
| // First letter should be 'e', 'i', 'g' or 'y'. | |
| switch (Baseline) { | |
| default: | |
| return getError("first letter after \'rv" + Twine(XLen) + | |
| "\' should be 'e', 'i', 'g' or 'y'"); | |
| case 'e': | |
| case 'i': | |
| // Baseline is `i` or `e` | |
| if (auto E = getExtensionVersion( | |
| StringRef(&Baseline, 1), Arch, Major, Minor, ConsumeLength, | |
| EnableExperimentalExtension, ExperimentalExtensionVersionCheck)) | |
| return std::move(E); | |
| ISAInfo->Exts[std::string(1, Baseline)] = {Major, Minor}; | |
| break; | |
| case 'y': { | |
| // If the first character is 'y', this is equivalent to "iy". | |
| // TODO: arch string syntax for RVE+RVY (and y in non-first position) will | |
| // be included following conclusion of "long base name" syntax | |
| // https://lists.riscv.org/g/tech-unprivileged/message/1134 | |
| if (auto E = getExtensionVersion("y", Arch, Major, Minor, ConsumeLength, | |
| EnableExperimentalExtension, | |
| ExperimentalExtensionVersionCheck)) | |
| return std::move(E); | |
| ISAInfo->Exts["y"] = {Major, Minor}; | |
| auto IVersion = findDefaultVersion("i"); | |
| assert(IVersion && "Default 'i' extension version not found?"); | |
| ISAInfo->Exts["i"] = {IVersion->Major, IVersion->Minor}; | |
| break; | |
| } | |
| case 'g': | |
| // g expands to extensions in RISCVGImplications. | |
| if (!Arch.empty() && isDigit(Arch.front())) | |
| return getError("version not supported for 'g'"); | |
| // Versions for g are disallowed, and this was checked for previously. | |
| ConsumeLength = 0; | |
| // 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 char *Ext : RISCVGImplications) { | |
| auto Version = findDefaultVersion(Ext); | |
| assert(Version && "Default extension version not found?"); | |
| ISAInfo->Exts[std::string(Ext)] = {Version->Major, Version->Minor}; | |
| } | |
| break; | |
| } | |
| // Consume the base ISA version number and any '_' between rvxxx and the | |
| // first extension | |
| Arch = Arch.drop_front(ConsumeLength); | |
| while (!Arch.empty()) { | |
| if (Arch.front() == '_') { | |
| if (Arch.size() == 1 || Arch[1] == '_') | |
| return getError("extension name missing after separator '_'"); | |
| Arch = Arch.drop_front(); | |
| } | |
| size_t Idx = Arch.find('_'); | |
| StringRef Ext = Arch.slice(0, Idx); | |
| Arch = Arch.substr(Idx); | |
| do { | |
| StringRef Name, Vers, Desc; | |
| if (RISCVISAUtils::AllStdExts.contains(Ext.front())) { | |
| Name = Ext.take_front(1); | |
| Ext = Ext.drop_front(); | |
| Vers = Ext; | |
| Desc = "standard user-level extension"; | |
| } else if (Ext.front() == 'z' || Ext.front() == 's' || | |
| Ext.front() == 'x') { | |
| // 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', 's', 'x' prefixes, might have a | |
| // version number (major, minor) and are separated by a single | |
| // underscore '_'. We do not enforce a canonical order for them. | |
| StringRef Type = getExtensionType(Ext); | |
| Desc = getExtensionTypeDesc(Ext); | |
| auto Pos = findLastNonVersionCharacter(Ext) + 1; | |
| Name = Ext.substr(0, Pos); | |
| Vers = Ext.substr(Pos); | |
| Ext = StringRef(); | |
| assert(!Type.empty() && "Empty type?"); | |
| if (Name.size() == Type.size()) | |
| return getError(Desc + " name missing after '" + Type + "'"); | |
| } else { | |
| return getError("invalid standard user-level extension '" + | |
| Twine(Ext.front()) + "'"); | |
| } | |
| unsigned Major, Minor, ConsumeLength; | |
| if (auto E = getExtensionVersion(Name, Vers, Major, Minor, ConsumeLength, | |
| EnableExperimentalExtension, | |
| ExperimentalExtensionVersionCheck)) | |
| return E; | |
| if (Name.size() == 1) | |
| Ext = Ext.substr(ConsumeLength); | |
| if (!RISCVISAInfo::isSupportedExtension(Name)) | |
| return getErrorForInvalidExt(Name); | |
| // Insert and error for duplicates. | |
| if (!ISAInfo->Exts | |
| .emplace(Name.str(), | |
| RISCVISAUtils::ExtensionVersion{Major, Minor}) | |
| .second) | |
| return getError("duplicated " + Desc + " '" + Name + "'"); | |
| } while (!Ext.empty()); | |
| } | |
| // We add Zicsr/Zifenci as final to allow duplicated "zicsr"/"zifencei" like | |
| // "rv64g_zicsr_zifencei". | |
| if (Baseline == 'g') { | |
| for (const char *Ext : RISCVGImplicationsZi) { | |
| if (ISAInfo->Exts.count(Ext)) | |
| continue; | |
| auto Version = findDefaultVersion(Ext); | |
| assert(Version && "Default extension version not found?"); | |
| ISAInfo->Exts[std::string(Ext)] = {Version->Major, Version->Minor}; | |
| } | |
| } | |
| return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); | |
| } | |
| static Error getIncompatibleError(StringRef Ext1, StringRef Ext2) { | |
| return getError("'" + Ext1 + "' and '" + Ext2 + | |
| "' extensions are incompatible"); | |
| } | |
| static Error getBaseIncompatibleError(StringRef Ext, StringRef Base) { | |
| return getError("'" + Ext + "' is incompatible with " + Base + " base"); | |
| } | |
| static Error getExtensionRequiresError(StringRef Ext, StringRef ReqExt) { | |
| return getError("'" + Ext + "' requires '" + ReqExt + | |
| "' extension to also be specified"); | |
| } | |
| Error RISCVISAInfo::checkDependency() { | |
| bool HasE = Exts.count("e") != 0; | |
| bool HasI = Exts.count("i") != 0; | |
| bool HasF = Exts.count("f") != 0; | |
| bool HasD = Exts.count("d") != 0; | |
| bool HasZfinx = Exts.count("zfinx") != 0; | |
| bool HasVector = Exts.count("zve32x") != 0; | |
| bool HasZvl = MinVLen != 0; | |
| bool HasZcmp = Exts.count("zcmp") != 0; | |
| bool HasXqccmp = Exts.count("xqccmp") != 0; | |
| bool HasZcmt = Exts.count("zcmt") != 0; | |
| bool HasXqccmt = Exts.count("xqccmt") != 0; | |
| static constexpr StringLiteral ZcdOverlaps[] = { | |
| {"zcmt"}, {"zcmp"}, {"xqccmi"}, {"xqccmp"}, | |
| {"xqccmt"}, {"xqciac"}, {"xqcicm"}, | |
| }; | |
| static constexpr StringLiteral RV32Only[] = { | |
| {"zcf"}, {"zclsd"}, {"zilsd"}, {"xwchc"}, {"xqccmi"}, | |
| {"xqci"}, {"xqcia"}, {"xqciac"}, {"xqcibi"}, {"xqcibm"}, | |
| {"xqcicli"}, {"xqcicm"}, {"xqcics"}, {"xqcicsr"}, {"xqciint"}, | |
| {"xqciio"}, {"xqcilb"}, {"xqcili"}, {"xqcilia"}, {"xqcilo"}, | |
| {"xqcilsm"}, {"xqcisim"}, {"xqcisls"}, {"xqcisync"}, | |
| }; | |
| if (HasI && HasE) | |
| return getIncompatibleError("i", "e"); | |
| if (HasF && HasZfinx) | |
| return getIncompatibleError("f", "zfinx"); | |
| if (HasZvl && !HasVector) | |
| return getExtensionRequiresError("zvl*b", "v' or 'zve*"); | |
| if (Exts.count("xsfvfbfexp16e") && | |
| !(Exts.count("zvfbfmin") || Exts.count("zvfbfa"))) | |
| return createStringError(errc::invalid_argument, | |
| "'xsfvfbfexp16e' requires 'zvfbfmin' or " | |
| "'zvfbfa' extension to also be specified"); | |
| if (Exts.count("zcd")) | |
| for (auto Ext : ZcdOverlaps) | |
| if (Exts.count(Ext.str())) | |
| return getIncompatibleError(Ext, "zcd"); | |
| if (XLen != 32) | |
| for (auto Ext : RV32Only) | |
| if (Exts.count(Ext.str())) | |
| return getError(Twine("'") + Ext + "' is only supported for 'rv32'"); | |
| if (Exts.count("xwchc") != 0) { | |
| if (HasD) | |
| return getIncompatibleError("d", "xwchc"); | |
| if (Exts.count("zcb") != 0) | |
| return getIncompatibleError("xwchc", "zcb"); | |
| } | |
| if (Exts.count("zclsd") != 0 && Exts.count("zcf") != 0) | |
| return getIncompatibleError("zclsd", "zcf"); | |
| if (Exts.count("y") != 0) { | |
| if (XLen == 32) { | |
| // On RV32Y systems the zclsd/zcf encodings are used for y load/stores. | |
| if (Exts.count("zclsd") != 0) | |
| return getBaseIncompatibleError("zclsd", "rv32y"); | |
| if (Exts.count("zcf") != 0) | |
| return getBaseIncompatibleError("zcf", "rv32y"); | |
| } else { | |
| // On RV64Y systems the zcd encodings are used for y load/stores. | |
| if (Exts.count("zcd") != 0) | |
| return getBaseIncompatibleError("zcd", "rv64y"); | |
| for (auto Ext : ZcdOverlaps) | |
| if (Exts.count(Ext.str())) | |
| return getBaseIncompatibleError(Ext, "rv64y"); | |
| } | |
| } | |
| if (HasZcmp && HasXqccmp) | |
| return getIncompatibleError("zcmp", "xqccmp"); | |
| if (HasZcmt && HasXqccmt) | |
| return getIncompatibleError("zcmt", "xqccmt"); | |
| return Error::success(); | |
| } | |
| struct ImpliedExtsEntry { | |
| StringLiteral Name; | |
| const char *ImpliedExt; | |
| bool operator<(const ImpliedExtsEntry &Other) const { | |
| return Name < Other.Name; | |
| } | |
| }; | |
| static bool operator<(const ImpliedExtsEntry &LHS, StringRef RHS) { | |
| return LHS.Name < RHS; | |
| } | |
| static bool operator<(StringRef LHS, const ImpliedExtsEntry &RHS) { | |
| return LHS < RHS.Name; | |
| } | |
| #define GET_IMPLIED_EXTENSIONS | |
| #include "llvm/TargetParser/RISCVTargetParserDef.inc" | |
| void RISCVISAInfo::updateImplication() { | |
| 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 | |
| SmallVector<StringRef, 16> WorkList; | |
| for (auto const &Ext : Exts) | |
| WorkList.push_back(Ext.first); | |
| while (!WorkList.empty()) { | |
| StringRef ExtName = WorkList.pop_back_val(); | |
| auto Range = std::equal_range(std::begin(ImpliedExts), | |
| std::end(ImpliedExts), ExtName); | |
| for (const ImpliedExtsEntry &Implied : llvm::make_range(Range)) { | |
| const char *ImpliedExt = Implied.ImpliedExt; | |
| auto [It, Inserted] = Exts.try_emplace(ImpliedExt); | |
| if (!Inserted) | |
| continue; | |
| auto Version = findDefaultVersion(ImpliedExt); | |
| It->second = *Version; | |
| WorkList.push_back(ImpliedExt); | |
| } | |
| } | |
| // Add Zcd if C and D are enabled and we aren't targeting 64-bit RVY. | |
| if (Exts.count("c") && Exts.count("d") && !Exts.count("zcd") && | |
| (XLen == 32 || !Exts.count("y"))) { | |
| auto Version = findDefaultVersion("zcd"); | |
| Exts["zcd"] = *Version; | |
| } | |
| // Add Zcf if C and F are enabled on RV32 and Y is not enabled. | |
| if (XLen == 32 && Exts.count("c") && Exts.count("f") && !Exts.count("zcf") && | |
| !Exts.count("y")) { | |
| auto Version = findDefaultVersion("zcf"); | |
| Exts["zcf"] = *Version; | |
| } | |
| // Add Zcf if Zce and F are enabled on RV32 and Y is not enabled. | |
| if (XLen == 32 && Exts.count("zce") && Exts.count("f") && | |
| !Exts.count("zcf") && !Exts.count("y")) { | |
| auto Version = findDefaultVersion("zcf"); | |
| Exts["zcf"] = *Version; | |
| } | |
| // Add C if Zca is enabled and the conditions are met. | |
| // This follows the RISC-V spec rules for MISA.C and matches GCC behavior | |
| // (PR119122). The rule is: | |
| // For RV32: | |
| // - No F and no D: Zca alone implies C | |
| // - F but no D: Zca + Zcf implies C | |
| // - F and D: Zca + Zcf + Zcd implies C | |
| // For RV64: | |
| // - No D: Zca alone implies C | |
| // - D: Zca + Zcd implies C | |
| // For RV32Y (Zcf incompatible): | |
| // - No D (but maybe F): Zca alone implies C | |
| // - F and D: Zca + Zcd implies C | |
| // For RV64Y (Zcf and Zcd incompatible): | |
| // - Zca alone implies C | |
| if (Exts.count("zca") && !Exts.count("c")) { | |
| bool ShouldAddC = false; | |
| if (XLen == 32) { | |
| if (Exts.count("d")) | |
| ShouldAddC = | |
| Exts.count("zcd") && (Exts.count("y") || Exts.count("zcf")); | |
| else if (Exts.count("f")) | |
| ShouldAddC = Exts.count("y") || Exts.count("zcf"); | |
| else | |
| ShouldAddC = true; | |
| } else if (XLen == 64) { | |
| if (Exts.count("d")) | |
| ShouldAddC = Exts.count("y") || Exts.count("zcd"); | |
| else | |
| ShouldAddC = true; | |
| } | |
| if (ShouldAddC) { | |
| auto Version = findDefaultVersion("c"); | |
| Exts["c"] = *Version; | |
| } | |
| } | |
| if (!Exts.count("zce") && Exts.count("zca") && Exts.count("zcb") && | |
| Exts.count("zcmp") && Exts.count("zcmt")) { | |
| bool ShouldAddZce = false; | |
| if (XLen == 32) { | |
| ShouldAddZce = !Exts.count("f") || Exts.count("zcf") || Exts.count("y"); | |
| } else if (XLen == 64) { | |
| // Zce is incompatible with RV64Y, only add it if Y is not enabled. | |
| ShouldAddZce = !Exts.count("y"); | |
| } | |
| if (ShouldAddZce) | |
| Exts["zce"] = *findDefaultVersion("zce"); | |
| } | |
| // Handle I/E after implications have been resolved, in case either | |
| // of them was implied by another extension. | |
| 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"); | |
| Exts["i"] = *Version; | |
| } | |
| if (HasE && HasI) | |
| Exts.erase("i"); | |
| } | |
| static constexpr StringLiteral CombineIntoExts[] = { | |
| {"a"}, {"b"}, {"zk"}, {"zkn"}, {"zks"}, | |
| {"zvkn"}, {"zvknc"}, {"zvkng"}, {"zvks"}, {"zvksc"}, | |
| {"zvksg"}, {"xqci"}, {"xsfmm32a"}, | |
| }; | |
| void RISCVISAInfo::updateCombination() { | |
| bool MadeChange = false; | |
| do { | |
| MadeChange = false; | |
| for (StringRef CombineExt : CombineIntoExts) { | |
| if (Exts.count(CombineExt.str())) | |
| continue; | |
| // Look up the extension in the ImpliesExt table to find everything it | |
| // depends on. | |
| auto Range = std::equal_range(std::begin(ImpliedExts), | |
| std::end(ImpliedExts), CombineExt); | |
| bool HasAllRequiredFeatures = std::all_of( | |
| Range.first, Range.second, [&](const ImpliedExtsEntry &Implied) { | |
| return Exts.count(Implied.ImpliedExt); | |
| }); | |
| if (HasAllRequiredFeatures) { | |
| auto Version = findDefaultVersion(CombineExt); | |
| Exts[CombineExt.str()] = *Version; | |
| MadeChange = true; | |
| } | |
| } | |
| } while (MadeChange); | |
| } | |
| void RISCVISAInfo::updateImpliedLengths() { | |
| assert(FLen == 0 && MaxELenFp == 0 && MaxELen == 0 && MinVLen == 0 && | |
| "Expected lengths to be initialied to zero"); | |
| if (Exts.count("q")) | |
| FLen = 128; | |
| else if (Exts.count("d")) | |
| FLen = 64; | |
| else if (Exts.count("f")) | |
| FLen = 32; | |
| if (Exts.count("v")) { | |
| MaxELenFp = std::max(MaxELenFp, 64u); | |
| MaxELen = std::max(MaxELen, 64u); | |
| } | |
| for (auto const &Ext : Exts) { | |
| StringRef ExtName = Ext.first; | |
| // Infer MaxELen and MaxELenFp from Zve(32/64)(x/f/d) | |
| if (ExtName.consume_front("zve")) { | |
| unsigned ZveELen; | |
| if (ExtName.consumeInteger(10, ZveELen)) | |
| continue; | |
| if (ExtName == "f") | |
| MaxELenFp = std::max(MaxELenFp, 32u); | |
| else if (ExtName == "d") | |
| MaxELenFp = std::max(MaxELenFp, 64u); | |
| else if (ExtName != "x") | |
| continue; | |
| MaxELen = std::max(MaxELen, ZveELen); | |
| continue; | |
| } | |
| // Infer MinVLen from zvl*b. | |
| if (ExtName.consume_front("zvl")) { | |
| unsigned ZvlLen; | |
| if (ExtName.consumeInteger(10, ZvlLen)) | |
| continue; | |
| if (ExtName != "b") | |
| continue; | |
| MinVLen = std::max(MinVLen, ZvlLen); | |
| continue; | |
| } | |
| } | |
| } | |
| 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.Major << "p" << ExtInfo.Minor; | |
| } | |
| return Arch.str(); | |
| } | |
| llvm::Expected<std::unique_ptr<RISCVISAInfo>> | |
| RISCVISAInfo::postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo) { | |
| ISAInfo->updateImplication(); | |
| ISAInfo->updateCombination(); | |
| ISAInfo->updateImpliedLengths(); | |
| if (Error Result = ISAInfo->checkDependency()) | |
| return std::move(Result); | |
| return std::move(ISAInfo); | |
| } | |
| StringRef RISCVISAInfo::computeDefaultABI() const { | |
| bool HasY = Exts.count("y") != 0; | |
| if (XLen == 32) { | |
| if (Exts.count("xcheriot")) | |
| return "cheriot"; | |
| if (Exts.count("e")) | |
| return HasY ? "il32pc64e" : "ilp32e"; | |
| if (Exts.count("d")) | |
| return HasY ? "il32pc64d" : "ilp32d"; | |
| if (Exts.count("f")) | |
| return HasY ? "il32pc64f" : "ilp32f"; | |
| return HasY ? "il32pc64" : "ilp32"; | |
| } else if (XLen == 64) { | |
| // RV64E has no capability ABI (yet). | |
| if (Exts.count("e")) | |
| return "lp64e"; | |
| if (Exts.count("d")) | |
| return HasY ? "l64pc128d" : "lp64d"; | |
| if (Exts.count("f")) | |
| return HasY ? "l64pc128f" : "lp64f"; | |
| return HasY ? "l64pc128" : "lp64"; | |
| } | |
| llvm_unreachable("Invalid XLEN"); | |
| } | |
| bool RISCVISAInfo::isSupportedExtensionWithVersion(StringRef Ext) { | |
| if (Ext.empty()) | |
| return false; | |
| auto Pos = findLastNonVersionCharacter(Ext) + 1; | |
| StringRef Name = Ext.substr(0, Pos); | |
| StringRef Vers = Ext.substr(Pos); | |
| if (Vers.empty()) | |
| return false; | |
| unsigned Major, Minor, ConsumeLength; | |
| if (auto E = getExtensionVersion(Name, Vers, Major, Minor, ConsumeLength, | |
| true, true)) { | |
| consumeError(std::move(E)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::string RISCVISAInfo::getTargetFeatureForExtension(StringRef Ext) { | |
| if (Ext.empty()) | |
| return std::string(); | |
| auto Pos = findLastNonVersionCharacter(Ext) + 1; | |
| StringRef Name = Ext.substr(0, Pos); | |
| if (Pos != Ext.size() && !isSupportedExtensionWithVersion(Ext)) | |
| return std::string(); | |
| if (!isSupportedExtension(Name)) | |
| return std::string(); | |
| return isExperimentalExtension(Name) ? "experimental-" + Name.str() | |
| : Name.str(); | |
| } | |
| struct RISCVExtensionBitmask { | |
| const char *Name; | |
| unsigned GroupID; | |
| unsigned BitPosition; | |
| }; | |
| #define GET_RISCVExtensionBitmaskTable_IMPL | |
| #include "llvm/TargetParser/RISCVTargetParserDef.inc" | |
| std::pair<int, int> RISCVISAInfo::getRISCVFeaturesBitsInfo(StringRef Ext) { | |
| // Note that this code currently accepts mixed case extension names, but | |
| // does not handle extension versions at all. That's probably fine because | |
| // there's only one extension version in the __riscv_feature_bits vector. | |
| for (auto E : ExtensionBitmask) | |
| if (Ext.equals_insensitive(E.Name)) | |
| return std::make_pair(E.GroupID, E.BitPosition); | |
| return std::make_pair(-1, -1); | |
| } |