[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