diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -306,6 +306,8 @@ "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.h b/clang/lib/Basic/Targets/RISCV.h --- 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 @@ 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 ISAInfo; static const Builtin::Info BuiltinInfo[]; public: @@ -141,7 +123,7 @@ void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; - if (HasA) + if (ISAInfo->hasExtension("a")) MaxAtomicInlineWidth = 32; } }; @@ -170,7 +152,7 @@ void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; - if (HasA) + if (ISAInfo->hasExtension("a")) MaxAtomicInlineWidth = 64; } }; diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp --- 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 @@ 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 @@ 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 @@ 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 @@ /// 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(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>(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 &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/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp --- 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 -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 &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 Split; - Exts.split(Split, StringRef("_")); - - SmallVector Prefix{"z", "x", "s", "sx"}; - auto I = Prefix.begin(); - auto E = Prefix.end(); - - SmallVector 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 &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 @@ // 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 --- 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 --- 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 --- 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 --- /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 +#include +#include + +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 + OrderedExtensionMap; + + /// Parse RISCV ISA info from arch string. + static llvm::Expected> + parseArchString(StringRef Arch, bool EnableExperimentalExtension, + bool ExperimentalExtensionVersionCheck = true); + + /// Parse RISCV ISA info from feature vector. + static llvm::Expected> + parseFeatures(unsigned XLen, const std::vector &Features); + + /// Convert RISCV ISA info to a feature vector. + void toFeatures(const llvm::opt::ArgList &Args, + std::vector &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 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -184,6 +184,7 @@ 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 --- /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 +#include +#include + +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 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 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 &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> +RISCVISAInfo::parseFeatures(unsigned XLen, + const std::vector &Features) { + assert(XLen == 32 || XLen == 64); + std::unique_ptr 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> +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 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 Split; + OtherExts.split(Split, '_'); + + SmallVector AllExts; + std::array 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 --- 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 @@ -50,6 +51,10 @@ 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 @@ 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 @@ 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 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.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -324,6 +324,10 @@ // triple. Exits with report_fatal_error if not. void validate(const Triple &TT, const FeatureBitset &FeatureBits); +// Convert FeatureBitset to FeatureVector. +void toFeatureVector(std::vector &FeatureVector, + const FeatureBitset &FeatureBits); + } // namespace RISCVFeatures namespace RISCVVType { diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp --- 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 @@ report_fatal_error("RV32E can't be enabled for an RV64 target"); } +void toFeatureVector(std::vector &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/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp --- 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 @@ 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 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 --- 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 --- 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 --- 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