diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2300,6 +2300,8 @@ def mcmodel_EQ_medany : Flag<["-"], "mcmodel=medany">, Group, Flags<[CC1Option]>, Alias, AliasArgs<["medium"]>, HelpText<"Equivalent to -mcmodel=medium, compatible with RISC-V gcc.">; +def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensions">, Group, + HelpText<"Enable use of experimental RISC-V extensions.">; def munaligned_access : Flag<["-"], "munaligned-access">, Group, HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64 only)">; 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 @@ -22,6 +22,14 @@ 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"; @@ -29,6 +37,8 @@ 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(); } @@ -39,10 +49,27 @@ 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 +getExperimentalExtension(StringRef Ext) { + if (Ext == "b" || Ext == "zbb" || Ext == "zbc" || Ext == "zbe" || + Ext == "zbf" || Ext == "zbm" || Ext == "zbp" || Ext == "zbr" || + Ext == "zbs" || Ext == "zbt") + return RISCVExtensionVersion{"0", "92"}; + return None; +} + static bool isSupportedExtension(StringRef Ext) { + // LLVM supports "z" extensions which are marked as experimental. + if (getExperimentalExtension(Ext)) + return true; + // LLVM does not support "sx", "s" nor "x" extensions. return false; } @@ -52,15 +79,13 @@ // 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, StringRef MArch, - StringRef Ext, StringRef In, +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.empty()) - return true; - if (In.consume_front("p")) { + if (Major.size() && In.consume_front("p")) { Minor = std::string(In.take_while(isDigit)); In = In.substr(Major.size()); @@ -74,7 +99,43 @@ } } - // TODO: Handle extensions with version number. + // If experimental extension, require use of supported version number + if (auto ExperimentalExtension = getExperimentalExtension(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()) { + 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()) + return true; + + // TODO: Handle supported extensions with version number. std::string Error = "unsupported version number " + Major; if (!Minor.empty()) Error += "." + Minor; @@ -89,7 +150,7 @@ // Parse the ISA string containing non-standard user-level // extensions, standard supervisor-level extensions and // non-standard supervisor-level extensions. -// These extensions start with 'x', 's', 'sx' prefixes, follow a +// 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. @@ -105,7 +166,7 @@ SmallVector Split; Exts.split(Split, StringRef("_")); - SmallVector Prefix{"x", "s", "sx"}; + SmallVector Prefix{"z", "x", "s", "sx"}; auto I = Prefix.begin(); auto E = Prefix.end(); @@ -119,8 +180,10 @@ } StringRef Type = getExtensionType(Ext); - StringRef Name(Ext.substr(Type.size())); 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) @@ -143,35 +206,30 @@ // The order is OK, do not advance I to the next prefix // to allow repeated extension type, e.g.: rv32ixabc_xdef. - if (Name.empty()) { + 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 << Ext; + << MArch << Error << Type; return; } std::string Major, Minor; - auto Pos = Name.find_if(isDigit); - if (Pos != StringRef::npos) { - auto Next = Name.substr(Pos); - Name = Name.substr(0, Pos); - if (!getExtensionVersion(D, MArch, Ext, Next, Major, Minor)) - return; - } + if (!getExtensionVersion(D, Args, MArch, Name, Vers, Major, Minor)) + return; // Check if duplicated extension. - if (llvm::is_contained(AllExts, Ext)) { + if (llvm::is_contained(AllExts, Name)) { std::string Error = "duplicated "; Error += Desc; D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) - << MArch << Error << Ext; + << 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(Ext); + AllExts.push_back(Name); } // Set target features. @@ -251,28 +309,35 @@ // Skip rvxxx StringRef Exts = MArch.substr(5); - // Remove non-standard extensions and supervisor-level extensions. - // They have 'x', 's', 'sx' prefixes. Parse them at the end. - // Find the very first occurrence of 's' or 'x'. + // 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("sx"); + size_t Pos = Exts.find_first_of("zsx"); if (Pos != StringRef::npos) { OtherExts = Exts.substr(Pos); Exts = Exts.substr(0, Pos); } std::string Major, Minor; - if (!getExtensionVersion(D, MArch, std::string(1, Baseline), Exts, 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 - // and consume the underscore '_' that might follow. auto StdExtsItr = StdExts.begin(); auto StdExtsEnd = StdExts.end(); - for (auto I = Exts.begin(), E = Exts.end(); I != E; ++I) { + for (auto I = Exts.begin(), E = Exts.end(); I != E; ) { char c = *I; // Check ISA extensions are specified in the canonical order. @@ -295,21 +360,18 @@ // Move to next char to prevent repeated letter. ++StdExtsItr; - if (std::next(I) != E) { - // Skip c. - std::string Next = std::string(std::next(I), E); - std::string Major, Minor; - if (!getExtensionVersion(D, MArch, std::string(1, c), Next, Major, Minor)) - return false; - - // TODO: Use version number when setting target features - // and consume the underscore '_' that might follow. - } + 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". + // Currently LLVM supports only "mafdcb". D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) << MArch << "unsupported standard user-level extension" << std::string(1, c); @@ -331,7 +393,19 @@ case 'c': Features.push_back("+c"); break; + case 'b': + Features.push_back("+b"); + 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. 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 @@ -264,20 +264,20 @@ // RV32-IMINOR1: error: invalid arch name 'rv32i2p1', unsupported // RV32-IMINOR1: version number 2.1 for extension 'i' -// RUN: %clang -target riscv32-unknown-elf -march=rv32ix2p -### %s \ +// RUN: %clang -target riscv32-unknown-elf -march=rv32ixt2p -### %s \ // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-XMINOR-MISS %s -// RV32-XMINOR-MISS: error: invalid arch name 'rv32ix2p', -// RV32-XMINOR-MISS: minor version number missing after 'p' for extension 'x2p' +// RV32-XMINOR-MISS: error: invalid arch name 'rv32ixt2p', +// RV32-XMINOR-MISS: minor version number missing after 'p' for extension 'xt' -// RUN: %clang -target riscv32-unknown-elf -march=rv32is2p0 -### %s \ +// RUN: %clang -target riscv32-unknown-elf -march=rv32ist2p0 -### %s \ // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-SMINOR0 %s -// RV32-SMINOR0: error: invalid arch name 'rv32is2p0', -// RV32-SMINOR0: unsupported version number 2.0 for extension 's2p0' +// RV32-SMINOR0: error: invalid arch name 'rv32ist2p0', +// RV32-SMINOR0: unsupported version number 2.0 for extension 'st' -// RUN: %clang -target riscv32-unknown-elf -march=rv32isx2p1 -### %s \ +// RUN: %clang -target riscv32-unknown-elf -march=rv32isxt2p1 -### %s \ // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-SXMINOR1 %s -// RV32-SXMINOR1: error: invalid arch name 'rv32isx2p1', unsupported -// RV32-SXMINOR1: version number 2.1 for extension 'sx2p1' +// RV32-SXMINOR1: error: invalid arch name 'rv32isxt2p1', unsupported +// RV32-SXMINOR1: version number 2.1 for extension 'sxt' // RUN: %clang -target riscv32-unknown-elf -march=rv32ixabc_ -### %s \ // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-XSEP %s @@ -327,3 +327,36 @@ // RUN: %clang -target riscv64-unknown-elf -march=rv64i -### %s \ // RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV64-TARGET %s // RV64-TARGET: "-triple" "riscv64-unknown-unknown-elf" + +// RUN: %clang -target riscv32-unknown-elf -march=rv32ib -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-NOFLAG %s +// RV32-EXPERIMENTAL-NOFLAG: error: invalid arch name 'rv32ib' +// RV32-EXPERIMENTAL-NOFLAG: requires '-menable-experimental-extensions' + +// RUN: %clang -target riscv32-unknown-elf -march=rv32ib -menable-experimental-extensions -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-NOVERS %s +// RV32-EXPERIMENTAL-NOVERS: error: invalid arch name 'rv32ib' +// RV32-EXPERIMENTAL-NOVERS: experimental extension requires explicit version number + +// RUN: %clang -target riscv32-unknown-elf -march=rv32ib0p1 -menable-experimental-extensions -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-BADVERS %s +// RV32-EXPERIMENTAL-BADVERS: error: invalid arch name 'rv32ib0p1' +// RV32-EXPERIMENTAL-BADVERS: unsupported version number 0.1 for experimental extension + +// RUN: %clang -target riscv32-unknown-elf -march=rv32ib0p92 -menable-experimental-extensions -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-GOODVERS %s +// RV32-EXPERIMENTAL-GOODVERS: "-target-feature" "+b" + +// RUN: %clang -target riscv32-unknown-elf -march=rv32izbb -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-ZBB-NOFLAG %s +// RV32-EXPERIMENTAL-ZBB-NOFLAG: error: invalid arch name 'rv32izbb' +// RV32-EXPERIMENTAL-ZBB-NOFLAG: requires '-menable-experimental-extensions' + +// RUN: %clang -target riscv32-unknown-elf -march=rv32izbb0p92 -menable-experimental-extensions -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-ZBB %s +// RV32-EXPERIMENTAL-ZBB: "-target-feature" "+zbb" + +// RUN: %clang -target riscv32-unknown-elf -march=rv32izbb0p92_zbp0p92 -menable-experimental-extensions -### %s \ +// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-ZBB-ZBP %s +// RV32-EXPERIMENTAL-ZBB-ZBP: "-target-feature" "+zbb" +// RV32-EXPERIMENTAL-ZBB-ZBP: "-target-feature" "+zbp"