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 @@ -2291,6 +2291,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"; @@ -47,20 +55,27 @@ return false; } +// If the extension is supported as experimental, return the version of that +// extension that the compiler currently supports. +static Optional +isExperimentalExtension(StringRef Ext) { + if (Ext == "b") + return RISCVExtensionVersion{"0", "92"}; + return None; +} + // 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, 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 +89,43 @@ } } - // TODO: Handle extensions with version number. + // 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; @@ -156,7 +207,7 @@ if (Pos != StringRef::npos) { auto Next = Name.substr(Pos); Name = Name.substr(0, Pos); - if (!getExtensionVersion(D, MArch, Ext, Next, Major, Minor)) + if (!getExtensionVersion(D, Args, MArch, Ext, Next, Major, Minor)) return; } @@ -262,17 +313,23 @@ } 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,18 +352,15 @@ // 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". @@ -331,7 +385,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 @@ -327,3 +327,22 @@ // 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"