RVY is a new base ISA, so the syntax to enable it is rv32y/rv64y. Since the compressed instructions reuse the space for Zcf (RV32) and Zcd (RV64), those are marked as incompatible and the logic for C/Zce is updated as part of this PR. RVY can also extend RVE instead of RVY (as is done for CHERIoT), but the official arch string syntax for that has not been finalized yet. Related discussion on that includes the "long base name" proposal: https://lists.riscv.org/g/tech-unprivileged/message/1134 Reviewers: topperc, lenary, jrtc27 Pull Request: https://github.com/llvm/llvm-project/pull/201931
1160 lines
38 KiB
C++
1160 lines
38 KiB
C++
//===-- 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"}, {"xqccmp"}, {"xqccmt"}, {"xqciac"}, {"xqcicm"},
|
|
};
|
|
static constexpr StringLiteral RV32Only[] = {
|
|
{"zcf"}, {"zclsd"}, {"zilsd"}, {"xwchc"}, {"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 {
|
|
if (XLen == 32) {
|
|
if (Exts.count("e"))
|
|
return "ilp32e";
|
|
if (Exts.count("d"))
|
|
return "ilp32d";
|
|
if (Exts.count("f"))
|
|
return "ilp32f";
|
|
return "ilp32";
|
|
} else if (XLen == 64) {
|
|
if (Exts.count("e"))
|
|
return "lp64e";
|
|
if (Exts.count("d"))
|
|
return "lp64d";
|
|
if (Exts.count("f"))
|
|
return "lp64f";
|
|
return "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);
|
|
}
|