[clang] ff13189 - [RISCV] Unify the arch string parsing logic to to RISCVISAInfo.
Kito Cheng via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 17 01:26:00 PDT 2021
Author: Kito Cheng
Date: 2021-10-17T16:25:23+08:00
New Revision: ff13189c5d0d96d0f955e9b1e951cf0ddc9e1d92
URL: https://github.com/llvm/llvm-project/commit/ff13189c5d0d96d0f955e9b1e951cf0ddc9e1d92
DIFF: https://github.com/llvm/llvm-project/commit/ff13189c5d0d96d0f955e9b1e951cf0ddc9e1d92.diff
LOG: [RISCV] Unify the arch string parsing logic to to RISCVISAInfo.
How many place you need to modify when implementing a new extension for RISC-V?
At least 7 places as I know:
- Add new SubtargetFeature at RISCV.td
- -march parser in RISCV.cpp
- RISCVTargetInfo::initFeatureMap at RISCV.cpp for handling feature vector.
- RISCVTargetInfo::getTargetDefines at RISCV.cpp for pre-define marco.
- Arch string parser for ELF attribute in RISCVAsmParser.cpp
- ELF attribute emittion in RISCVAsmParser.cpp, and make sure it's in
canonical order...
- ELF attribute emittion in RISCVTargetStreamer.cpp, and again, must in
canonical order...
And now, this patch provide an unified infrastructure for handling (almost)
everything of RISC-V arch string.
After this patch, you only need to update 2 places for implement an extension
for RISC-V:
- Add new SubtargetFeature at RISCV.td, hmmm, it's hard to avoid.
- Add new entry to RISCVSupportedExtension at RISCVISAInfo.cpp or
SupportedExperimentalExtensions at RISCVISAInfo.cpp .
Most codes are come from existing -march parser, but with few new feature/bug
fixes:
- Accept version for -march, e.g. -march=rv32i2p0.
- Reject version info with `p` but without minor version number like `rv32i2p`.
Differential Revision: https://reviews.llvm.org/D105168
Added:
llvm/include/llvm/Support/RISCVISAInfo.h
llvm/lib/Support/RISCVISAInfo.cpp
Modified:
clang/include/clang/Basic/DiagnosticCommonKinds.td
clang/lib/Basic/Targets/RISCV.cpp
clang/lib/Basic/Targets/RISCV.h
clang/lib/Driver/ToolChains/Arch/RISCV.cpp
clang/test/Driver/riscv-abi.c
clang/test/Driver/riscv-arch.c
clang/test/Driver/riscv-features.c
llvm/lib/Support/CMakeLists.txt
llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
llvm/test/MC/RISCV/attribute-arch.s
llvm/test/MC/RISCV/attribute-with-insts.s
llvm/test/MC/RISCV/invalid-attribute.s
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 4e21e276c69c5..1ed1c8cd9a19f 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -306,6 +306,8 @@ def err_opt_not_valid_without_opt : Error<
"option '%0' cannot be specified without '%1'">;
def err_opt_not_valid_on_target : Error<
"option '%0' cannot be specified on this target">;
+def err_invalid_feature_combination : Error<
+ "invalid feature combination: %0">;
// Source manager
def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp
index 00da8cdaef0b5..83b2fb95b3d1c 100644
--- a/clang/lib/Basic/Targets/RISCV.cpp
+++ b/clang/lib/Basic/Targets/RISCV.cpp
@@ -11,10 +11,12 @@
//===----------------------------------------------------------------------===//
#include "RISCV.h"
+#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/MacroBuilder.h"
#include "clang/Basic/TargetBuiltins.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/TargetParser.h"
+#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace clang::targets;
@@ -122,6 +124,7 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
bool Is64Bit = getTriple().getArch() == llvm::Triple::riscv64;
Builder.defineMacro("__riscv_xlen", Is64Bit ? "64" : "32");
StringRef CodeModel = getTargetOpts().CodeModel;
+ unsigned FLen = ISAInfo->getFLen();
if (CodeModel == "default")
CodeModel = "small";
@@ -142,17 +145,23 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__riscv_abi_rve");
Builder.defineMacro("__riscv_arch_test");
- Builder.defineMacro("__riscv_i", "2000000");
- if (HasM) {
- Builder.defineMacro("__riscv_m", "2000000");
+ for (auto &Extension : ISAInfo->getExtensions()) {
+ auto ExtName = Extension.first;
+ auto ExtInfo = Extension.second;
+ unsigned Version =
+ (ExtInfo.MajorVersion * 1000000) + (ExtInfo.MinorVersion * 1000);
+
+ Builder.defineMacro(Twine("__riscv_", ExtName), Twine(Version));
+ }
+
+ if (ISAInfo->hasExtension("m")) {
Builder.defineMacro("__riscv_mul");
Builder.defineMacro("__riscv_div");
Builder.defineMacro("__riscv_muldiv");
}
- if (HasA) {
- Builder.defineMacro("__riscv_a", "2000000");
+ if (ISAInfo->hasExtension("a")) {
Builder.defineMacro("__riscv_atomic");
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
@@ -161,64 +170,17 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
}
- if (HasF || HasD) {
- Builder.defineMacro("__riscv_f", "2000000");
- Builder.defineMacro("__riscv_flen", HasD ? "64" : "32");
+ if (FLen) {
+ Builder.defineMacro("__riscv_flen", Twine(FLen));
Builder.defineMacro("__riscv_fdiv");
Builder.defineMacro("__riscv_fsqrt");
}
- if (HasD)
- Builder.defineMacro("__riscv_d", "2000000");
-
- if (HasC) {
- Builder.defineMacro("__riscv_c", "2000000");
+ if (ISAInfo->hasExtension("c"))
Builder.defineMacro("__riscv_compressed");
- }
- if (HasV) {
- Builder.defineMacro("__riscv_v", "10000");
+ if (ISAInfo->hasExtension("v"))
Builder.defineMacro("__riscv_vector");
- }
-
- if (HasZba)
- Builder.defineMacro("__riscv_zba", "1000000");
-
- if (HasZbb)
- Builder.defineMacro("__riscv_zbb", "1000000");
-
- if (HasZbc)
- Builder.defineMacro("__riscv_zbc", "1000000");
-
- if (HasZbe)
- Builder.defineMacro("__riscv_zbe", "93000");
-
- if (HasZbf)
- Builder.defineMacro("__riscv_zbf", "93000");
-
- if (HasZbm)
- Builder.defineMacro("__riscv_zbm", "93000");
-
- if (HasZbp)
- Builder.defineMacro("__riscv_zbp", "93000");
-
- if (HasZbr)
- Builder.defineMacro("__riscv_zbr", "93000");
-
- if (HasZbs)
- Builder.defineMacro("__riscv_zbs", "1000000");
-
- if (HasZbt)
- Builder.defineMacro("__riscv_zbt", "93000");
-
- if (HasZfh)
- Builder.defineMacro("__riscv_zfh", "1000");
-
- if (HasZvamo)
- Builder.defineMacro("__riscv_zvamo", "10000");
-
- if (HasZvlsseg)
- Builder.defineMacro("__riscv_zvlsseg", "10000");
}
const Builtin::Info RISCVTargetInfo::BuiltinInfo[] = {
@@ -247,75 +209,36 @@ bool RISCVTargetInfo::initFeatureMap(
/// Return true if has this feature, need to sync with handleTargetFeatures.
bool RISCVTargetInfo::hasFeature(StringRef Feature) const {
bool Is64Bit = getTriple().getArch() == llvm::Triple::riscv64;
- return llvm::StringSwitch<bool>(Feature)
- .Case("riscv", true)
- .Case("riscv32", !Is64Bit)
- .Case("riscv64", Is64Bit)
- .Case("64bit", Is64Bit)
- .Case("m", HasM)
- .Case("a", HasA)
- .Case("f", HasF)
- .Case("d", HasD)
- .Case("c", HasC)
- .Case("experimental-v", HasV)
- .Case("experimental-zba", HasZba)
- .Case("experimental-zbb", HasZbb)
- .Case("experimental-zbc", HasZbc)
- .Case("experimental-zbe", HasZbe)
- .Case("experimental-zbf", HasZbf)
- .Case("experimental-zbm", HasZbm)
- .Case("experimental-zbp", HasZbp)
- .Case("experimental-zbr", HasZbr)
- .Case("experimental-zbs", HasZbs)
- .Case("experimental-zbt", HasZbt)
- .Case("experimental-zfh", HasZfh)
- .Case("experimental-zvamo", HasZvamo)
- .Case("experimental-zvlsseg", HasZvlsseg)
- .Default(false);
+ auto Result = llvm::StringSwitch<Optional<bool>>(Feature)
+ .Case("riscv", true)
+ .Case("riscv32", !Is64Bit)
+ .Case("riscv64", Is64Bit)
+ .Case("64bit", Is64Bit)
+ .Default(None);
+ if (Result.hasValue())
+ return Result.getValue();
+
+ if (ISAInfo->isSupportedExtensionFeature(Feature))
+ return ISAInfo->hasExtension(Feature);
+
+ return false;
}
/// Perform initialization based on the user configured set of features.
bool RISCVTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
DiagnosticsEngine &Diags) {
- for (const auto &Feature : Features) {
- if (Feature == "+m")
- HasM = true;
- else if (Feature == "+a")
- HasA = true;
- else if (Feature == "+f")
- HasF = true;
- else if (Feature == "+d")
- HasD = true;
- else if (Feature == "+c")
- HasC = true;
- else if (Feature == "+experimental-v")
- HasV = true;
- else if (Feature == "+experimental-zba")
- HasZba = true;
- else if (Feature == "+experimental-zbb")
- HasZbb = true;
- else if (Feature == "+experimental-zbc")
- HasZbc = true;
- else if (Feature == "+experimental-zbe")
- HasZbe = true;
- else if (Feature == "+experimental-zbf")
- HasZbf = true;
- else if (Feature == "+experimental-zbm")
- HasZbm = true;
- else if (Feature == "+experimental-zbp")
- HasZbp = true;
- else if (Feature == "+experimental-zbr")
- HasZbr = true;
- else if (Feature == "+experimental-zbs")
- HasZbs = true;
- else if (Feature == "+experimental-zbt")
- HasZbt = true;
- else if (Feature == "+experimental-zfh")
- HasZfh = true;
- else if (Feature == "+experimental-zvamo")
- HasZvamo = true;
- else if (Feature == "+experimental-zvlsseg")
- HasZvlsseg = true;
+ unsigned XLen = getTriple().isArch64Bit() ? 64 : 32;
+ auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, Features);
+ if (!ParseResult) {
+ std::string Buffer;
+ llvm::raw_string_ostream OutputErrMsg(Buffer);
+ handleAllErrors(ParseResult.takeError(), [&](llvm::StringError &ErrMsg) {
+ OutputErrMsg << ErrMsg.getMessage();
+ });
+ Diags.Report(diag::err_invalid_feature_combination) << OutputErrMsg.str();
+ return false;
+ } else {
+ ISAInfo = std::move(*ParseResult);
}
return true;
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index f8b1e697ef512..f7ffe9febcd00 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -17,6 +17,7 @@
#include "clang/Basic/TargetOptions.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/RISCVISAInfo.h"
namespace clang {
namespace targets {
@@ -25,26 +26,7 @@ namespace targets {
class RISCVTargetInfo : public TargetInfo {
protected:
std::string ABI, CPU;
- bool HasM = false;
- bool HasA = false;
- bool HasF = false;
- bool HasD = false;
- bool HasC = false;
- bool HasV = false;
- bool HasZba = false;
- bool HasZbb = false;
- bool HasZbc = false;
- bool HasZbe = false;
- bool HasZbf = false;
- bool HasZbm = false;
- bool HasZbp = false;
- bool HasZbr = false;
- bool HasZbs = false;
- bool HasZbt = false;
- bool HasZfh = false;
- bool HasZvamo = false;
- bool HasZvlsseg = false;
-
+ std::unique_ptr<llvm::RISCVISAInfo> ISAInfo;
static const Builtin::Info BuiltinInfo[];
public:
@@ -141,7 +123,7 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
void setMaxAtomicWidth() override {
MaxAtomicPromoteWidth = 128;
- if (HasA)
+ if (ISAInfo->hasExtension("a"))
MaxAtomicInlineWidth = 32;
}
};
@@ -170,7 +152,7 @@ class LLVM_LIBRARY_VISIBILITY RISCV64TargetInfo : public RISCVTargetInfo {
void setMaxAtomicWidth() override {
MaxAtomicPromoteWidth = 128;
- if (HasA)
+ if (ISAInfo->hasExtension("a"))
MaxAtomicInlineWidth = 64;
}
};
diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
index a05b8a93a37b0..a28348a7af169 100644
--- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
@@ -7,450 +7,41 @@
//===----------------------------------------------------------------------===//
#include "RISCV.h"
+#include "ToolChains/CommonArgs.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
-#include "llvm/Option/ArgList.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/RISCVISAInfo.h"
#include "llvm/Support/TargetParser.h"
#include "llvm/Support/raw_ostream.h"
-#include "ToolChains/CommonArgs.h"
using namespace clang::driver;
using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;
-namespace {
-// Represents the major and version number components of a RISC-V extension
-struct RISCVExtensionVersion {
- StringRef Major;
- StringRef Minor;
-};
-} // end anonymous namespace
-
-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();
-}
-
-// If the extension is supported as experimental, return the version of that
-// extension that the compiler currently supports.
-static Optional<RISCVExtensionVersion>
-isExperimentalExtension(StringRef Ext) {
- if (Ext == "zba" || Ext == "zbb" || Ext == "zbc" || Ext == "zbs")
- return RISCVExtensionVersion{"1", "0"};
- if (Ext == "zbe" || Ext == "zbf" || Ext == "zbm" || Ext == "zbp" ||
- Ext == "zbr" || Ext == "zbt")
- return RISCVExtensionVersion{"0", "93"};
- if (Ext == "v" || Ext == "zvamo" || Ext == "zvlsseg")
- return RISCVExtensionVersion{"0", "10"};
- if (Ext == "zfh")
- return RISCVExtensionVersion{"0", "1"};
- return None;
-}
-
-static bool isSupportedExtension(StringRef Ext) {
- // LLVM supports "z" extensions which are marked as experimental.
- if (isExperimentalExtension(Ext))
- return true;
-
- // LLVM does not support "sx", "s" nor "x" extensions.
- return false;
-}
-
-// 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 bool getExtensionVersion(const Driver &D, const ArgList &Args,
- StringRef MArch, StringRef Ext, StringRef In,
- std::string &Major, std::string &Minor) {
- Major = std::string(In.take_while(isDigit));
- In = In.substr(Major.size());
-
- if (Major.size() && In.consume_front("p")) {
- Minor = std::string(In.take_while(isDigit));
- In = In.substr(Major.size() + 1);
-
- // Expected 'p' to be followed by minor version number.
- if (Minor.empty()) {
- std::string Error =
- "minor version number missing after 'p' for extension";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return false;
- }
- }
-
- // 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";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) << MArch << Error << In;
- return false;
- }
-
- // If experimental extension, require use of current version number number
- if (auto ExperimentalExtension = isExperimentalExtension(Ext)) {
- if (!Args.hasArg(options::OPT_menable_experimental_extensions)) {
- std::string Error =
- "requires '-menable-experimental-extensions' for experimental extension";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return false;
- } else if (Major.empty() && Minor.empty()) {
- std::string Error =
- "experimental extension requires explicit version number";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return false;
- }
- auto SupportedVers = *ExperimentalExtension;
- if (Major != SupportedVers.Major || Minor != SupportedVers.Minor) {
- std::string Error =
- "unsupported version number " + Major;
- if (!Minor.empty())
- Error += "." + Minor;
- Error += " for experimental extension (this compiler supports "
- + SupportedVers.Major.str() + "."
- + SupportedVers.Minor.str() + ")";
-
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return false;
- }
- return true;
- }
-
- // Allow extensions to declare no version number
- if (Major.empty() && Minor.empty())
- return true;
-
- // TODO: Handle supported extensions with version number.
- std::string Error = "unsupported version number " + Major;
- if (!Minor.empty())
- Error += "." + Minor;
- Error += " for extension";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) << MArch << Error << Ext;
-
- return false;
-}
-
-// 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.
-static void getExtensionFeatures(const Driver &D,
- const ArgList &Args,
- std::vector<StringRef> &Features,
- StringRef &MArch, StringRef &Exts) {
- if (Exts.empty())
- return;
-
- // Multi-letter extensions are seperated by a single underscore
- // as described in RISC-V User-Level ISA V2.2.
- SmallVector<StringRef, 8> Split;
- Exts.split(Split, StringRef("_"));
-
- SmallVector<StringRef, 4> Prefix{"z", "x", "s", "sx"};
- auto I = Prefix.begin();
- auto E = Prefix.end();
-
- SmallVector<StringRef, 8> AllExts;
-
- for (StringRef Ext : Split) {
- if (Ext.empty()) {
- D.Diag(diag::err_drv_invalid_riscv_arch_name) << MArch
- << "extension name missing after separator '_'";
- return;
- }
-
- StringRef Type = getExtensionType(Ext);
- StringRef Desc = getExtensionTypeDesc(Ext);
- auto Pos = Ext.find_if(isDigit);
- StringRef Name(Ext.substr(0, Pos));
- StringRef Vers(Ext.substr(Pos));
-
- if (Type.empty()) {
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << "invalid extension prefix" << Ext;
- return;
- }
-
- // Check ISA extensions are specified in the canonical order.
- while (I != E && *I != Type)
- ++I;
-
- if (I == E) {
- std::string Error = std::string(Desc);
- Error += " not given in canonical order";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return;
- }
-
- // The order is OK, do not advance I to the next prefix
- // to allow repeated extension type, e.g.: rv32ixabc_xdef.
-
- if (Name.size() == Type.size()) {
- std::string Error = std::string(Desc);
- Error += " name missing after";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Type;
- return;
- }
-
- std::string Major, Minor;
- if (!getExtensionVersion(D, Args, MArch, Name, Vers, Major, Minor))
- return;
-
- // Check if duplicated extension.
- if (llvm::is_contained(AllExts, Name)) {
- std::string Error = "duplicated ";
- Error += Desc;
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Name;
- return;
- }
-
- // Extension format is correct, keep parsing the extensions.
- // TODO: Save Type, Name, Major, Minor to avoid parsing them later.
- AllExts.push_back(Name);
- }
-
- // Set target features.
- // TODO: Hardware features to be handled in Support/TargetParser.cpp.
- // TODO: Use version number when setting target features.
- for (auto Ext : AllExts) {
- if (!isSupportedExtension(Ext)) {
- StringRef Desc = getExtensionTypeDesc(getExtensionType(Ext));
- std::string Error = "unsupported ";
- Error += Desc;
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << Ext;
- return;
- }
- if (Ext == "zvlsseg") {
- Features.push_back("+experimental-v");
- Features.push_back("+experimental-zvlsseg");
- } else if (Ext == "zvamo") {
- Features.push_back("+experimental-v");
- Features.push_back("+experimental-zvlsseg");
- Features.push_back("+experimental-zvamo");
- } else if (isExperimentalExtension(Ext))
- Features.push_back(Args.MakeArgString("+experimental-" + Ext));
- else
- Features.push_back(Args.MakeArgString("+" + Ext));
- }
-}
-
// Returns false if an error is diagnosed.
-static bool getArchFeatures(const Driver &D, StringRef MArch,
+static bool getArchFeatures(const Driver &D, StringRef Arch,
std::vector<StringRef> &Features,
const ArgList &Args) {
- // RISC-V ISA strings must be lowercase.
- if (llvm::any_of(MArch, [](char c) { return isupper(c); })) {
- D.Diag(diag::err_drv_invalid_riscv_arch_name)
- << MArch << "string must be lowercase";
- return false;
- }
-
- // ISA string must begin with rv32 or rv64.
- if (!(MArch.startswith("rv32") || MArch.startswith("rv64")) ||
- (MArch.size() < 5)) {
- D.Diag(diag::err_drv_invalid_riscv_arch_name)
- << MArch << "string must begin with rv32{i,e,g} or rv64{i,g}";
- return false;
- }
-
- bool HasRV64 = MArch.startswith("rv64");
-
- // The canonical order specified in ISA manual.
- // Ref: Table 22.1 in RISC-V User-Level ISA V2.2
- StringRef StdExts = "mafdqlcbjtpvn";
- bool HasF = false, HasD = false;
- char Baseline = MArch[4];
-
- // First letter should be 'e', 'i' or 'g'.
- switch (Baseline) {
- default:
- D.Diag(diag::err_drv_invalid_riscv_arch_name)
- << MArch << "first letter should be 'e', 'i' or 'g'";
- return false;
- case 'e': {
- StringRef Error;
- // Currently LLVM does not support 'e'.
- // Extension 'e' is not allowed in rv64.
- if (HasRV64)
- Error = "standard user-level extension 'e' requires 'rv32'";
- else
- Error = "unsupported standard user-level extension 'e'";
- D.Diag(diag::err_drv_invalid_riscv_arch_name) << MArch << Error;
- return false;
- }
- case 'i':
- break;
- case 'g':
- // g = imafd
- StdExts = StdExts.drop_front(4);
- Features.push_back("+m");
- Features.push_back("+a");
- Features.push_back("+f");
- Features.push_back("+d");
- HasF = true;
- HasD = true;
- break;
- }
-
- // Skip rvxxx
- StringRef Exts = MArch.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);
- }
+ bool EnableExperimentalExtensions =
+ Args.hasArg(options::OPT_menable_experimental_extensions);
+ auto ISAInfo =
+ llvm::RISCVISAInfo::parseArchString(Arch, EnableExperimentalExtensions);
+ if (!ISAInfo) {
+ handleAllErrors(ISAInfo.takeError(), [&](llvm::StringError &ErrMsg) {
+ D.Diag(diag::err_drv_invalid_riscv_arch_name)
+ << Arch << ErrMsg.getMessage();
+ });
- std::string Major, Minor;
- if (!getExtensionVersion(D, Args, MArch, std::string(1, Baseline), Exts,
- Major, Minor))
return false;
-
- // Consume the base ISA version number and any '_' between rvxxx and the
- // first extension
- Exts = Exts.drop_front(Major.size());
- if (!Minor.empty())
- Exts = Exts.drop_front(Minor.size() + 1 /*'p'*/);
- Exts.consume_front("_");
-
- // TODO: Use version number when setting target features
-
- auto StdExtsItr = StdExts.begin();
- auto StdExtsEnd = StdExts.end();
-
- 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.
- StringRef Error;
- if (StdExts.contains(c))
- Error = "standard user-level extension not given in canonical order";
- else
- Error = "invalid standard user-level extension";
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << Error << std::string(1, c);
- return false;
- }
-
- // Move to next char to prevent repeated letter.
- ++StdExtsItr;
-
- std::string Next, Major, Minor;
- if (std::next(I) != E)
- Next = std::string(std::next(I), E);
- if (!getExtensionVersion(D, Args, MArch, std::string(1, c), Next, Major,
- Minor))
- return false;
-
- // The order is OK, then push it into features.
- // TODO: Use version number when setting target features
- switch (c) {
- default:
- // Currently LLVM supports only "mafdc".
- D.Diag(diag::err_drv_invalid_riscv_ext_arch_name)
- << MArch << "unsupported standard user-level extension"
- << std::string(1, c);
- return false;
- case 'm':
- Features.push_back("+m");
- break;
- case 'a':
- Features.push_back("+a");
- break;
- case 'f':
- Features.push_back("+f");
- HasF = true;
- break;
- case 'd':
- Features.push_back("+d");
- HasD = true;
- break;
- case 'c':
- Features.push_back("+c");
- break;
- case 'v':
- Features.push_back("+experimental-v");
- Features.push_back("+experimental-zvlsseg");
- break;
- }
-
- // Consume full extension name and version, including any optional '_'
- // between this extension and the next
- ++I;
- I += Major.size();
- if (Minor.size())
- I += Minor.size() + 1 /*'p'*/;
- if (*I == '_')
- ++I;
}
- // Dependency check.
- // It's illegal to specify the 'd' (double-precision floating point)
- // extension without also specifying the 'f' (single precision
- // floating-point) extension.
- if (HasD && !HasF) {
- D.Diag(diag::err_drv_invalid_riscv_arch_name)
- << MArch << "d requires f extension to also be specified";
- return false;
- }
-
- // Additional dependency checks.
- // TODO: The 'q' extension requires rv64.
- // TODO: It is illegal to specify 'e' extensions with 'f' and 'd'.
-
- // Handle all other types of extensions.
- getExtensionFeatures(D, Args, Features, MArch, OtherExts);
-
+ (*ISAInfo)->toFeatures(Args, Features);
return true;
}
@@ -598,24 +189,30 @@ StringRef riscv::getRISCVABI(const ArgList &Args, const llvm::Triple &Triple) {
// rv32* -> ilp32
// rv64g | rv64*d -> lp64d
// rv64* -> lp64
- StringRef MArch = getRISCVArch(Args, Triple);
+ StringRef Arch = getRISCVArch(Args, Triple);
- if (MArch.startswith_insensitive("rv32")) {
- // FIXME: parse `March` to find `D` extension properly
- if (MArch.substr(4).contains_insensitive("d") ||
- MArch.startswith_insensitive("rv32g"))
- return "ilp32d";
- else if (MArch.startswith_insensitive("rv32e"))
- return "ilp32e";
- else
+ auto ParseResult = llvm::RISCVISAInfo::parseArchString(
+ Arch, /* EnableExperimentalExtension */ true);
+ if (!ParseResult) {
+ // Ignore parsing error, just go 3rd step.
+ consumeError(ParseResult.takeError());
+ } else {
+ auto &ISAInfo = *ParseResult;
+ bool HasD = ISAInfo->hasExtension("d");
+ unsigned XLen = ISAInfo->getXLen();
+ if (XLen == 32) {
+ bool HasE = ISAInfo->hasExtension("e");
+ if (HasD)
+ return "ilp32d";
+ if (HasE)
+ return "ilp32e";
return "ilp32";
- } else if (MArch.startswith_insensitive("rv64")) {
- // FIXME: parse `March` to find `D` extension properly
- if (MArch.substr(4).contains_insensitive("d") ||
- MArch.startswith_insensitive("rv64g"))
- return "lp64d";
- else
+ } else if (XLen == 64) {
+ if (HasD)
+ return "lp64d";
return "lp64";
+ }
+ llvm_unreachable();
}
// 3. Choose a default based on the triple
diff --git a/clang/test/Driver/riscv-abi.c b/clang/test/Driver/riscv-abi.c
index 1035fe4c668b0..07f2f1acf2128 100644
--- a/clang/test/Driver/riscv-abi.c
+++ b/clang/test/Driver/riscv-abi.c
@@ -65,9 +65,9 @@
// CHECK-LP64F: "-target-abi" "lp64f"
-// RUN: %clang -target riscv64-unknown-elf %s -### -o %t.o -march=rv64d -mabi=lp64d 2>&1 \
+// RUN: %clang -target riscv64-unknown-elf %s -### -o %t.o -march=rv64ifd -mabi=lp64d 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-LP64D %s
-// RUN: %clang -target riscv64-unknown-elf %s -### -o %t.o -march=rv64d 2>&1 \
+// RUN: %clang -target riscv64-unknown-elf %s -### -o %t.o -march=rv64ifd 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-LP64D %s
// RUN: %clang -target riscv64-unknown-elf %s -### -o %t.o -march=rv64g 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-LP64D %s
diff --git a/clang/test/Driver/riscv-arch.c b/clang/test/Driver/riscv-arch.c
index e0afaa9e5351e..4634cb7e9c9fb 100644
--- a/clang/test/Driver/riscv-arch.c
+++ b/clang/test/Driver/riscv-arch.c
@@ -1,5 +1,7 @@
// RUN: %clang -target riscv32-unknown-elf -march=rv32i -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck %s
+// RUN: %clang -target riscv32-unknown-elf -march=rv32i2p0 -### %s \
+// RUN: -fsyntax-only 2>&1 | FileCheck %s
// RUN: %clang -target riscv32-unknown-elf -march=rv32im -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck %s
// RUN: %clang -target riscv32-unknown-elf -march=rv32ima -### %s \
@@ -68,6 +70,8 @@
// RUN: %clang -target riscv64-unknown-elf -march=rv64i -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck %s
+// RUN: %clang -target riscv64-unknown-elf -march=rv64i2p0 -### %s \
+// RUN: -fsyntax-only 2>&1 | FileCheck %s
// RUN: %clang -target riscv64-unknown-elf -march=rv64im -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck %s
// RUN: %clang -target riscv64-unknown-elf -march=rv64ima -### %s \
@@ -195,11 +199,6 @@
// Testing specific messages and unsupported extensions.
-// RUN: %clang -target riscv32-unknown-elf -march=rv32e -### %s \
-// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32E %s
-// RV32E: error: invalid arch name 'rv32e',
-// RV32E: standard user-level extension 'e'
-
// RUN: %clang -target riscv64-unknown-elf -march=rv64e -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV64E %s
// RV64E: error: invalid arch name 'rv64e',
@@ -308,11 +307,6 @@
// RV32-IMINOR-MISS: error: invalid arch name 'rv32i2p',
// RV32-IMINOR-MISS: minor version number missing after 'p' for extension 'i'
-// RUN: %clang -target riscv32-unknown-elf -march=rv32i2p0 -### %s \
-// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-IMINOR0 %s
-// RV32-IMINOR0: error: invalid arch name 'rv32i2p0',
-// RV32-IMINOR0: unsupported version number 2.0 for extension 'i'
-
// RUN: %clang -target riscv32-unknown-elf -march=rv32i2p1 -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-IMINOR1 %s
// RV32-IMINOR1: error: invalid arch name 'rv32i2p1', unsupported
@@ -417,7 +411,7 @@
// RUN: %clang -target riscv32-unknown-elf -march=rv32iv0p1 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-V-BADVERS %s
// RV32-EXPERIMENTAL-V-BADVERS: error: invalid arch name 'rv32iv0p1'
-// RV32-EXPERIMENTAL-V-BADVERS: unsupported version number 0.1 for experimental extension
+// RV32-EXPERIMENTAL-V-BADVERS: unsupported version number 0.1 for experimental extension 'v'
// RUN: %clang -target riscv32-unknown-elf -march=rv32iv0p10 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-V-GOODVERS %s
@@ -445,7 +439,7 @@
// RUN: %clang -target riscv32-unknown-elf -march=rv32izvamo0p1 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-ZVAMO-BADVERS %s
// RV32-EXPERIMENTAL-ZVAMO-BADVERS: error: invalid arch name 'rv32izvamo0p1'
-// RV32-EXPERIMENTAL-ZVAMO-BADVERS: unsupported version number 0.1 for experimental extension
+// RV32-EXPERIMENTAL-ZVAMO-BADVERS: unsupported version number 0.1 for experimental extension 'zvamo'
// RUN: %clang -target riscv32-unknown-elf -march=rv32izvamo0p10 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-ZVAMO-GOODVERS %s
@@ -464,7 +458,7 @@
// RUN: %clang -target riscv32-unknown-elf -march=rv32izvlsseg0p1 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-ZVLSSEG-BADVERS %s
// RV32-EXPERIMENTAL-ZVLSSEG-BADVERS: error: invalid arch name 'rv32izvlsseg0p1'
-// RV32-EXPERIMENTAL-ZVLSSEG-BADVERS: unsupported version number 0.1 for experimental extension
+// RV32-EXPERIMENTAL-ZVLSSEG-BADVERS: unsupported version number 0.1 for experimental extension 'zvlsseg'
// RUN: %clang -target riscv32-unknown-elf -march=rv32izvlsseg0p10 -menable-experimental-extensions -### %s -c 2>&1 | \
// RUN: FileCheck -check-prefix=RV32-EXPERIMENTAL-ZVLSSEG-GOODVERS %s
diff --git a/clang/test/Driver/riscv-features.c b/clang/test/Driver/riscv-features.c
index 8d76e681289ba..0686e28459544 100644
--- a/clang/test/Driver/riscv-features.c
+++ b/clang/test/Driver/riscv-features.c
@@ -31,3 +31,7 @@
// DEFAULT-LINUX-SAME: "-target-feature" "+f"
// DEFAULT-LINUX-SAME: "-target-feature" "+d"
// DEFAULT-LINUX-SAME: "-target-feature" "+c"
+
+// RUN: not %clang -cc1 -triple riscv64-unknown-elf -target-feature +e 2>&1 | FileCheck %s -check-prefix=RV64-WITH-E
+
+// RV64-WITH-E: error: invalid feature combination: standard user-level extension 'e' requires 'rv32'
diff --git a/llvm/include/llvm/Support/RISCVISAInfo.h b/llvm/include/llvm/Support/RISCVISAInfo.h
new file mode 100644
index 0000000000000..26044cf0a2949
--- /dev/null
+++ b/llvm/include/llvm/Support/RISCVISAInfo.h
@@ -0,0 +1,90 @@
+//===-- RISCVISAInfo.h - RISCV ISA Information ------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_RISCVISAINFO_H
+#define LLVM_SUPPORT_RISCVISAINFO_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/Error.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace llvm {
+struct RISCVExtensionInfo {
+ std::string ExtName;
+ unsigned MajorVersion;
+ unsigned MinorVersion;
+};
+
+class RISCVISAInfo {
+public:
+ RISCVISAInfo(const RISCVISAInfo &) = delete;
+ RISCVISAInfo &operator=(const RISCVISAInfo &) = delete;
+
+ static bool compareExtension(const std::string &LHS, const std::string &RHS);
+
+ /// Helper class for OrderedExtensionMap.
+ struct ExtensionComparator {
+ bool operator()(const std::string &LHS, const std::string &RHS) const {
+ return compareExtension(LHS, RHS);
+ }
+ };
+
+ /// OrderedExtensionMap is std::map, it's specialized to keep entries
+ /// in canonical order of extension.
+ typedef std::map<std::string, RISCVExtensionInfo, ExtensionComparator>
+ OrderedExtensionMap;
+
+ /// Parse RISCV ISA info from arch string.
+ static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
+ parseArchString(StringRef Arch, bool EnableExperimentalExtension,
+ bool ExperimentalExtensionVersionCheck = true);
+
+ /// Parse RISCV ISA info from feature vector.
+ static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
+ parseFeatures(unsigned XLen, const std::vector<std::string> &Features);
+
+ /// Convert RISCV ISA info to a feature vector.
+ void toFeatures(const llvm::opt::ArgList &Args,
+ std::vector<StringRef> &Features) const;
+
+ const OrderedExtensionMap &getExtensions() const { return Exts; };
+
+ unsigned getXLen() const { return XLen; };
+ unsigned getFLen() const { return FLen; };
+
+ bool hasExtension(StringRef Ext) const;
+ std::string toString() const;
+
+ static bool isSupportedExtensionFeature(StringRef Ext);
+ static bool isSupportedExtension(StringRef Ext);
+ static bool isSupportedExtension(StringRef Ext, unsigned MajorVersion,
+ unsigned MinorVersion);
+
+private:
+ RISCVISAInfo(unsigned XLen) : XLen(XLen), FLen(0) {}
+
+ unsigned XLen;
+ unsigned FLen;
+
+ OrderedExtensionMap Exts;
+
+ void addExtension(StringRef ExtName, unsigned MajorVersion,
+ unsigned MinorVersion);
+
+ void updateFLen();
+};
+
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index b682bc59d4e8c..771b7366e695a 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -184,6 +184,7 @@ add_llvm_component_library(LLVMSupport
Regex.cpp
RISCVAttributes.cpp
RISCVAttributeParser.cpp
+ RISCVISAInfo.cpp
ScaledNumber.cpp
ScopedPrinter.cpp
SHA1.cpp
diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp
new file mode 100644
index 0000000000000..e0bf9ef90eeb6
--- /dev/null
+++ b/llvm/lib/Support/RISCVISAInfo.cpp
@@ -0,0 +1,716 @@
+//===-- RISCVISAInfo.cpp - RISCV 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/Support/RISCVISAInfo.h"
+#include "llvm/ADT/None.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 <array>
+#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 = "mafdqlcbjtpvn";
+
+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}},
+};
+
+static const RISCVSupportedExtension SupportedExperimentalExtensions[] = {
+ {"v", RISCVExtensionVersion{0, 10}},
+ {"zba", RISCVExtensionVersion{1, 0}},
+ {"zbb", RISCVExtensionVersion{1, 0}},
+ {"zbc", RISCVExtensionVersion{1, 0}},
+ {"zbe", RISCVExtensionVersion{0, 93}},
+ {"zbf", RISCVExtensionVersion{0, 93}},
+ {"zbm", RISCVExtensionVersion{0, 93}},
+ {"zbp", RISCVExtensionVersion{0, 93}},
+ {"zbr", RISCVExtensionVersion{0, 93}},
+ {"zbs", RISCVExtensionVersion{1, 0}},
+ {"zbt", RISCVExtensionVersion{0, 93}},
+
+ {"zvamo", RISCVExtensionVersion{0, 10}},
+ {"zvlsseg", RISCVExtensionVersion{0, 10}},
+
+ {"zfh", RISCVExtensionVersion{0, 1}},
+};
+
+static bool stripExperimentalPrefix(StringRef &Ext) {
+ return Ext.consume_front("experimental-");
+}
+
+struct FindByName {
+ FindByName(StringRef Ext) : Ext(Ext){};
+ StringRef Ext;
+ bool operator()(const RISCVSupportedExtension &ExtInfo) {
+ return ExtInfo.Name == Ext;
+ }
+};
+
+static 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 : {makeArrayRef(SupportedExtensions),
+ makeArrayRef(SupportedExperimentalExtensions)}) {
+ auto ExtensionInfoIterator = llvm::find_if(ExtInfo, FindByName(ExtName));
+
+ if (ExtensionInfoIterator == ExtInfo.end()) {
+ continue;
+ }
+ return ExtensionInfoIterator->Version;
+ }
+ return None;
+}
+
+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 Optional<RISCVExtensionVersion> isExperimentalExtension(StringRef Ext) {
+ auto ExtIterator =
+ llvm::find_if(SupportedExperimentalExtensions, FindByName(Ext));
+ if (ExtIterator == std::end(SupportedExperimentalExtensions))
+ return None;
+
+ 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 'h':
+ HighOrder = 1;
+ break;
+ case 'z':
+ HighOrder = 2;
+ // `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 = 3;
+ 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(const llvm::opt::ArgList &Args,
+ std::vector<StringRef> &Features) const {
+ for (auto &Ext : Exts) {
+ StringRef ExtName = Ext.first;
+
+ if (ExtName == "i")
+ continue;
+
+ if (ExtName == "zvlsseg") {
+ Features.push_back("+experimental-v");
+ Features.push_back("+experimental-zvlsseg");
+ } else if (ExtName == "zvamo") {
+ Features.push_back("+experimental-v");
+ Features.push_back("+experimental-zvlsseg");
+ Features.push_back("+experimental-zvamo");
+ } else if (isExperimentalExtension(ExtName)) {
+ Features.push_back(Args.MakeArgString("+experimental-" + ExtName));
+ } else {
+ Features.push_back(Args.MakeArgString("+" + 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() + 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));
+
+ bool HasE = false;
+ 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
+ ? makeArrayRef(SupportedExperimentalExtensions)
+ : makeArrayRef(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) {
+ if (ExtName == "e") {
+ if (XLen != 32)
+ return createStringError(
+ errc::invalid_argument,
+ "standard user-level extension 'e' requires 'rv32'");
+ HasE = true;
+ }
+
+ ISAInfo->addExtension(ExtName, ExtensionInfoIterator->Version.Major,
+ ExtensionInfoIterator->Version.Minor);
+ } else
+ ISAInfo->Exts.erase(ExtName.str());
+ }
+ if (!HasE) {
+ if (auto Version = findDefaultVersion("i"))
+ ISAInfo->addExtension("i", Version->Major, Version->Minor);
+ else
+ llvm_unreachable("Default extension version for 'i' not found?");
+ }
+
+ ISAInfo->updateFLen();
+
+ return std::move(ISAInfo);
+}
+
+llvm::Expected<std::unique_ptr<RISCVISAInfo>>
+RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension,
+ bool ExperimentalExtensionVersionCheck) {
+ // 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;
+ bool HasF = false, HasD = false;
+ 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);
+ HasF = true;
+ HasD = true;
+ 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 (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();
+ 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))
+ return std::move(E);
+
+ // The order is OK, then push it into features.
+ // TODO: Use version number when setting target features
+ switch (C) {
+ default:
+ // Currently LLVM supports only "mafdcbv".
+ return createStringError(errc::invalid_argument,
+ "unsupported standard user-level extension '%c'",
+ C);
+ case 'm':
+ ISAInfo->addExtension("m", Major, Minor);
+ break;
+ case 'a':
+ ISAInfo->addExtension("a", Major, Minor);
+ break;
+ case 'f':
+ ISAInfo->addExtension("f", Major, Minor);
+ HasF = true;
+ break;
+ case 'd':
+ ISAInfo->addExtension("d", Major, Minor);
+ HasD = true;
+ break;
+ case 'c':
+ ISAInfo->addExtension("c", Major, Minor);
+ break;
+ case 'v':
+ ISAInfo->addExtension("v", Major, Minor);
+ ISAInfo->addExtension("zvlsseg", Major, Minor);
+ break;
+ }
+ // Consume full extension name and version, including any optional '_'
+ // between this extension and the next
+ ++I;
+ I += ConsumeLength;
+ if (*I == '_')
+ ++I;
+ }
+ // Dependency check.
+ // 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");
+
+ // Additional dependency checks.
+ // TODO: The 'q' extension requires rv64.
+ // TODO: It is illegal to specify 'e' extensions with 'f' and 'd'.
+
+ if (OtherExts.empty())
+ return std::move(ISAInfo);
+
+ // 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();
+
+ 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 = Ext.find_if(isDigit);
+ StringRef Name(Ext.substr(0, Pos));
+ StringRef Vers(Ext.substr(Pos));
+
+ if (Type.empty())
+ 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)
+ return createStringError(errc::invalid_argument,
+ "%s not given in canonical order '%s'",
+ Desc.str().c_str(), Ext.str().c_str());
+
+ if (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))
+ return std::move(E);
+
+ // Check if duplicated extension.
+ if (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());
+ }
+ }
+
+ ISAInfo->updateFLen();
+
+ return std::move(ISAInfo);
+}
+
+void RISCVISAInfo::updateFLen() {
+ FLen = 0;
+ // TODO: Handle q extension.
+ if (Exts.count("d"))
+ FLen = 64;
+ else if (Exts.count("f"))
+ FLen = 32;
+}
+
+std::string RISCVISAInfo::toString() const {
+ std::string Buffer;
+ raw_string_ostream Arch(Buffer);
+
+ Arch << "rv" << XLen;
+
+ ListSeparator LS("_");
+ for (auto &Ext : Exts) {
+ StringRef ExtName = Ext.first;
+ auto ExtInfo = Ext.second;
+ Arch << LS << ExtName;
+ Arch << ExtInfo.MajorVersion << "p" << ExtInfo.MinorVersion;
+ }
+
+ return Arch.str();
+}
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 18b0fe0e13789..9a7a2f7551272 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -36,6 +36,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/Support/RISCVISAInfo.h"
#include <limits>
@@ -50,6 +51,10 @@ using namespace llvm;
STATISTIC(RISCVNumInstrsCompressed,
"Number of RISC-V Compressed instructions emitted");
+namespace llvm {
+extern const SubtargetFeatureKV RISCVFeatureKV[RISCV::NumSubtargetFeatures];
+} // namespace llvm
+
namespace {
struct RISCVOperand;
@@ -2059,106 +2064,35 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
if (Tag == RISCVAttrs::ARCH) {
StringRef Arch = StringValue;
- if (Arch.consume_front("rv32"))
+ for (auto Feature : RISCVFeatureKV)
+ if (llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key))
+ clearFeatureBits(Feature.Value, Feature.Key);
+
+ auto ParseResult = llvm::RISCVISAInfo::parseArchString(
+ StringValue, /*EnableExperimentalExtension=*/true,
+ /*ExperimentalExtensionVersionCheck=*/false);
+ if (!ParseResult) {
+ std::string Buffer;
+ raw_string_ostream OutputErrMsg(Buffer);
+ handleAllErrors(ParseResult.takeError(), [&](llvm::StringError &ErrMsg) {
+ OutputErrMsg << "invalid arch name '" << Arch << "', "
+ << ErrMsg.getMessage();
+ });
+
+ return Error(ValueExprLoc, OutputErrMsg.str());
+ }
+ auto &ISAInfo = *ParseResult;
+
+ for (auto Feature : RISCVFeatureKV)
+ if (ISAInfo->hasExtension(Feature.Key))
+ setFeatureBits(Feature.Value, Feature.Key);
+
+ if (ISAInfo->getXLen() == 32)
clearFeatureBits(RISCV::Feature64Bit, "64bit");
- else if (Arch.consume_front("rv64"))
+ else if (ISAInfo->getXLen() == 64)
setFeatureBits(RISCV::Feature64Bit, "64bit");
else
return Error(ValueExprLoc, "bad arch string " + Arch);
-
- // .attribute arch overrides the current architecture, so unset all
- // currently enabled extensions
- clearFeatureBits(RISCV::FeatureRV32E, "e");
- clearFeatureBits(RISCV::FeatureStdExtM, "m");
- clearFeatureBits(RISCV::FeatureStdExtA, "a");
- clearFeatureBits(RISCV::FeatureStdExtF, "f");
- clearFeatureBits(RISCV::FeatureStdExtD, "d");
- clearFeatureBits(RISCV::FeatureStdExtC, "c");
- clearFeatureBits(RISCV::FeatureStdExtV, "experimental-v");
- clearFeatureBits(RISCV::FeatureStdExtZfh, "experimental-zfh");
- clearFeatureBits(RISCV::FeatureStdExtZba, "experimental-zba");
- clearFeatureBits(RISCV::FeatureStdExtZbb, "experimental-zbb");
- clearFeatureBits(RISCV::FeatureStdExtZbc, "experimental-zbc");
- clearFeatureBits(RISCV::FeatureStdExtZbe, "experimental-zbe");
- clearFeatureBits(RISCV::FeatureStdExtZbf, "experimental-zbf");
- clearFeatureBits(RISCV::FeatureStdExtZbm, "experimental-zbm");
- clearFeatureBits(RISCV::FeatureStdExtZbp, "experimental-zbp");
- clearFeatureBits(RISCV::FeatureStdExtZbr, "experimental-zbr");
- clearFeatureBits(RISCV::FeatureStdExtZbs, "experimental-zbs");
- clearFeatureBits(RISCV::FeatureStdExtZbt, "experimental-zbt");
- clearFeatureBits(RISCV::FeatureStdExtZvamo, "experimental-zvamo");
- clearFeatureBits(RISCV::FeatureStdExtZvlsseg, "experimental-zvlsseg");
-
- while (!Arch.empty()) {
- bool DropFirst = true;
- if (Arch[0] == 'i')
- clearFeatureBits(RISCV::FeatureRV32E, "e");
- else if (Arch[0] == 'e')
- setFeatureBits(RISCV::FeatureRV32E, "e");
- else if (Arch[0] == 'g') {
- clearFeatureBits(RISCV::FeatureRV32E, "e");
- setFeatureBits(RISCV::FeatureStdExtM, "m");
- setFeatureBits(RISCV::FeatureStdExtA, "a");
- setFeatureBits(RISCV::FeatureStdExtF, "f");
- setFeatureBits(RISCV::FeatureStdExtD, "d");
- } else if (Arch[0] == 'm')
- setFeatureBits(RISCV::FeatureStdExtM, "m");
- else if (Arch[0] == 'a')
- setFeatureBits(RISCV::FeatureStdExtA, "a");
- else if (Arch[0] == 'f')
- setFeatureBits(RISCV::FeatureStdExtF, "f");
- else if (Arch[0] == 'd') {
- setFeatureBits(RISCV::FeatureStdExtF, "f");
- setFeatureBits(RISCV::FeatureStdExtD, "d");
- } else if (Arch[0] == 'c') {
- setFeatureBits(RISCV::FeatureStdExtC, "c");
- } else if (Arch[0] == 'v') {
- setFeatureBits(RISCV::FeatureStdExtV, "experimental-v");
- } else if (Arch[0] == 's' || Arch[0] == 'x' || Arch[0] == 'z') {
- StringRef Ext =
- Arch.take_until([](char c) { return ::isdigit(c) || c == '_'; });
- if (Ext == "zba")
- setFeatureBits(RISCV::FeatureStdExtZba, "experimental-zba");
- else if (Ext == "zbb")
- setFeatureBits(RISCV::FeatureStdExtZbb, "experimental-zbb");
- else if (Ext == "zbc")
- setFeatureBits(RISCV::FeatureStdExtZbc, "experimental-zbc");
- else if (Ext == "zbe")
- setFeatureBits(RISCV::FeatureStdExtZbe, "experimental-zbe");
- else if (Ext == "zbf")
- setFeatureBits(RISCV::FeatureStdExtZbf, "experimental-zbf");
- else if (Ext == "zbm")
- setFeatureBits(RISCV::FeatureStdExtZbm, "experimental-zbm");
- else if (Ext == "zbp")
- setFeatureBits(RISCV::FeatureStdExtZbp, "experimental-zbp");
- else if (Ext == "zbr")
- setFeatureBits(RISCV::FeatureStdExtZbr, "experimental-zbr");
- else if (Ext == "zbs")
- setFeatureBits(RISCV::FeatureStdExtZbs, "experimental-zbs");
- else if (Ext == "zbt")
- setFeatureBits(RISCV::FeatureStdExtZbt, "experimental-zbt");
- else if (Ext == "zfh")
- setFeatureBits(RISCV::FeatureStdExtZfh, "experimental-zfh");
- else if (Ext == "zvamo")
- setFeatureBits(RISCV::FeatureStdExtZvamo, "experimental-zvamo");
- else if (Ext == "zvlsseg")
- setFeatureBits(RISCV::FeatureStdExtZvlsseg, "experimental-zvlsseg");
- else
- return Error(ValueExprLoc, "bad arch string " + Ext);
- Arch = Arch.drop_until([](char c) { return ::isdigit(c) || c == '_'; });
- DropFirst = false;
- } else
- return Error(ValueExprLoc, "bad arch string " + Arch);
-
- if (DropFirst)
- Arch = Arch.drop_front(1);
- int major = 0;
- int minor = 0;
- Arch.consumeInteger(10, major);
- Arch.consume_front("p");
- Arch.consumeInteger(10, minor);
- Arch = Arch.drop_while([](char c) { return c == '_'; });
- }
}
if (IsIntegerValue)
@@ -2167,54 +2101,26 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
if (Tag != RISCVAttrs::ARCH) {
getTargetStreamer().emitTextAttribute(Tag, StringValue);
} else {
- std::string formalArchStr = "rv32";
- if (getFeatureBits(RISCV::Feature64Bit))
- formalArchStr = "rv64";
- if (getFeatureBits(RISCV::FeatureRV32E))
- formalArchStr = (Twine(formalArchStr) + "e1p9").str();
- else
- formalArchStr = (Twine(formalArchStr) + "i2p0").str();
-
- if (getFeatureBits(RISCV::FeatureStdExtM))
- formalArchStr = (Twine(formalArchStr) + "_m2p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtA))
- formalArchStr = (Twine(formalArchStr) + "_a2p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtF))
- formalArchStr = (Twine(formalArchStr) + "_f2p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtD))
- formalArchStr = (Twine(formalArchStr) + "_d2p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtC))
- formalArchStr = (Twine(formalArchStr) + "_c2p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtV))
- formalArchStr = (Twine(formalArchStr) + "_v0p10").str();
- if (getFeatureBits(RISCV::FeatureStdExtZfh))
- formalArchStr = (Twine(formalArchStr) + "_zfh0p1").str();
- if (getFeatureBits(RISCV::FeatureStdExtZba))
- formalArchStr = (Twine(formalArchStr) + "_zba1p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbb))
- formalArchStr = (Twine(formalArchStr) + "_zbb1p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbc))
- formalArchStr = (Twine(formalArchStr) + "_zbc1p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbe))
- formalArchStr = (Twine(formalArchStr) + "_zbe0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbf))
- formalArchStr = (Twine(formalArchStr) + "_zbf0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbm))
- formalArchStr = (Twine(formalArchStr) + "_zbm0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbp))
- formalArchStr = (Twine(formalArchStr) + "_zbp0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbr))
- formalArchStr = (Twine(formalArchStr) + "_zbr0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbs))
- formalArchStr = (Twine(formalArchStr) + "_zbs1p0").str();
- if (getFeatureBits(RISCV::FeatureStdExtZbt))
- formalArchStr = (Twine(formalArchStr) + "_zbt0p93").str();
- if (getFeatureBits(RISCV::FeatureStdExtZvamo))
- formalArchStr = (Twine(formalArchStr) + "_zvamo0p10").str();
- if (getFeatureBits(RISCV::FeatureStdExtZvlsseg))
- formalArchStr = (Twine(formalArchStr) + "_zvlsseg0p10").str();
-
- getTargetStreamer().emitTextAttribute(Tag, formalArchStr);
+ std::vector<std::string> FeatureVector;
+ RISCVFeatures::toFeatureVector(FeatureVector, getSTI().getFeatureBits());
+
+ // Parse that by RISCVISAInfo->
+ unsigned XLen = getFeatureBits(RISCV::Feature64Bit) ? 64 : 32;
+ auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, FeatureVector);
+ if (!ParseResult) {
+ std::string Buffer;
+ raw_string_ostream OutputErrMsg(Buffer);
+ handleAllErrors(ParseResult.takeError(),
+ [&](llvm::StringError &ErrMsg) {
+ OutputErrMsg << ErrMsg.getMessage();
+ });
+
+ return Error(ValueExprLoc, OutputErrMsg.str());
+ }
+ auto &ISAInfo = *ParseResult;
+
+ // Then emit the arch string.
+ getTargetStreamer().emitTextAttribute(Tag, ISAInfo->toString());
}
}
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
index 60e86093d9f45..0aba18b20f0da 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
@@ -14,9 +14,14 @@
#include "RISCVBaseInfo.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/RISCVISAInfo.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
+
+extern const SubtargetFeatureKV RISCVFeatureKV[RISCV::NumSubtargetFeatures];
+
namespace RISCVSysReg {
#define GET_SysRegsList_IMPL
#include "RISCVGenSearchableTables.inc"
@@ -96,6 +101,15 @@ void validate(const Triple &TT, const FeatureBitset &FeatureBits) {
report_fatal_error("RV32E can't be enabled for an RV64 target");
}
+void toFeatureVector(std::vector<std::string> &FeatureVector,
+ const FeatureBitset &FeatureBits) {
+ for (auto Feature : RISCVFeatureKV) {
+ if (FeatureBits[Feature.Value] &&
+ llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key))
+ FeatureVector.push_back(std::string("+") + Feature.Key);
+ }
+}
+
} // namespace RISCVFeatures
// Encode VTYPE into the binary format used by the the VSETVLI instruction which
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index 035d7046523d0..88684ad180046 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -324,6 +324,10 @@ namespace RISCVFeatures {
// triple. Exits with report_fatal_error if not.
void validate(const Triple &TT, const FeatureBitset &FeatureBits);
+// Convert FeatureBitset to FeatureVector.
+void toFeatureVector(std::vector<std::string> &FeatureVector,
+ const FeatureBitset &FeatureBits);
+
} // namespace RISCVFeatures
namespace RISCVVType {
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
index 8531f7bae5366..2f016374e6a23 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
@@ -11,9 +11,11 @@
//===----------------------------------------------------------------------===//
#include "RISCVTargetStreamer.h"
+#include "RISCVBaseInfo.h"
#include "RISCVMCTargetDesc.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/Support/RISCVISAInfo.h"
using namespace llvm;
@@ -43,53 +45,19 @@ void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) {
else
emitAttribute(RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_16);
- std::string Arch = "rv32";
- if (STI.hasFeature(RISCV::Feature64Bit))
- Arch = "rv64";
- if (STI.hasFeature(RISCV::FeatureRV32E))
- Arch += "e1p9";
- else
- Arch += "i2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtM))
- Arch += "_m2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtA))
- Arch += "_a2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtF))
- Arch += "_f2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtD))
- Arch += "_d2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtC))
- Arch += "_c2p0";
- if (STI.hasFeature(RISCV::FeatureStdExtV))
- Arch += "_v0p10";
- if (STI.hasFeature(RISCV::FeatureStdExtZfh))
- Arch += "_zfh0p1";
- if (STI.hasFeature(RISCV::FeatureStdExtZba))
- Arch += "_zba1p0";
- if (STI.hasFeature(RISCV::FeatureStdExtZbb))
- Arch += "_zbb1p0";
- if (STI.hasFeature(RISCV::FeatureStdExtZbc))
- Arch += "_zbc1p0";
- if (STI.hasFeature(RISCV::FeatureStdExtZbe))
- Arch += "_zbe0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZbf))
- Arch += "_zbf0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZbm))
- Arch += "_zbm0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZbp))
- Arch += "_zbp0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZbr))
- Arch += "_zbr0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZbs))
- Arch += "_zbs1p0";
- if (STI.hasFeature(RISCV::FeatureStdExtZbt))
- Arch += "_zbt0p93";
- if (STI.hasFeature(RISCV::FeatureStdExtZvamo))
- Arch += "_zvamo0p10";
- if (STI.hasFeature(RISCV::FeatureStdExtZvlsseg))
- Arch += "_zvlsseg0p10";
-
- emitTextAttribute(RISCVAttrs::ARCH, Arch);
+ unsigned XLen = STI.hasFeature(RISCV::Feature64Bit) ? 64 : 32;
+ std::vector<std::string> FeatureVector;
+ RISCVFeatures::toFeatureVector(FeatureVector, STI.getFeatureBits());
+
+ auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, FeatureVector);
+ if (!ParseResult) {
+ /* Assume any error about features should handled earlier. */
+ consumeError(ParseResult.takeError());
+ llvm_unreachable("Parsing feature error when emitTargetAttributes?");
+ } else {
+ auto &ISAInfo = *ParseResult;
+ emitTextAttribute(RISCVAttrs::ARCH, ISAInfo->toString());
+ }
}
// This part is for ascii assembly output
diff --git a/llvm/test/MC/RISCV/attribute-arch.s b/llvm/test/MC/RISCV/attribute-arch.s
index d2e756b19b6ea..a6365a1f5d00f 100644
--- a/llvm/test/MC/RISCV/attribute-arch.s
+++ b/llvm/test/MC/RISCV/attribute-arch.s
@@ -9,9 +9,6 @@
.attribute arch, "rv32i2"
# CHECK: attribute 5, "rv32i2p0"
-.attribute arch, "rv32i2p"
-# CHECK: attribute 5, "rv32i2p0"
-
.attribute arch, "rv32i2p0"
# CHECK: attribute 5, "rv32i2p0"
@@ -33,11 +30,11 @@
.attribute arch, "rv32ima2p0_fdc"
# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
-.attribute arch, "rv32ima2p_fdc"
+.attribute arch, "rv32ima2p0_fdc"
# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
.attribute arch, "rv32iv"
-# CHECK: attribute 5, "rv32i2p0_v0p10"
+# CHECK: attribute 5, "rv32i2p0_v0p10_zvlsseg0p10"
.attribute arch, "rv32izba"
# CHECK: attribute 5, "rv32i2p0_zba1p0"
diff --git a/llvm/test/MC/RISCV/attribute-with-insts.s b/llvm/test/MC/RISCV/attribute-with-insts.s
index 9d9170a9f5aa4..df180b70dd1ec 100644
--- a/llvm/test/MC/RISCV/attribute-with-insts.s
+++ b/llvm/test/MC/RISCV/attribute-with-insts.s
@@ -10,7 +10,7 @@
# RUN: | llvm-objdump --triple=riscv64 -d -M no-aliases - \
# RUN: | FileCheck -check-prefix=CHECK-INST %s
-.attribute arch, "rv64i2p0_m2p0_a2p0_d2p0_c2p0"
+.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
# CHECK-INST: lr.w t0, (t1)
lr.w t0, (t1)
diff --git a/llvm/test/MC/RISCV/invalid-attribute.s b/llvm/test/MC/RISCV/invalid-attribute.s
index e3cfe28fdf442..761a98902d5ef 100644
--- a/llvm/test/MC/RISCV/invalid-attribute.s
+++ b/llvm/test/MC/RISCV/invalid-attribute.s
@@ -7,10 +7,10 @@
# RUN: not llvm-mc %s -triple=riscv64 -filetype=asm 2>&1 | FileCheck %s
.attribute arch, "foo"
-# CHECK: [[@LINE-1]]:18: error: bad arch string foo
+# CHECK: [[@LINE-1]]:18: error: invalid arch name 'foo', string must begin with rv32{i,e,g} or rv64{i,g}
.attribute arch, "rv32i2p0_y2p0"
-# CHECK: [[@LINE-1]]:18: error: bad arch string y2p0
+# CHECK: [[@LINE-1]]:18: error: invalid arch name 'rv32i2p0_y2p0', invalid standard user-level extension 'y'
.attribute stack_align, "16"
# CHECK: [[@LINE-1]]:25: error: expected numeric constant
More information about the cfe-commits
mailing list