diff options
Diffstat (limited to 'clang/lib/Driver/ToolChains/Arch/RISCV.cpp')
| -rw-r--r-- | clang/lib/Driver/ToolChains/Arch/RISCV.cpp | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp new file mode 100644 index 000000000000..624788a5874e --- /dev/null +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -0,0 +1,402 @@ +//===--- RISCV.cpp - RISCV Helpers for Tools --------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "RISCV.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/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; + +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"; + 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"; + return StringRef(); +} + +static bool isSupportedExtension(StringRef Ext) { + // 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, StringRef MArch, + StringRef Ext, StringRef In, + std::string &Major, std::string &Minor) { + Major = In.take_while(isDigit); + In = In.substr(Major.size()); + if (Major.empty()) + return true; + + if (In.consume_front("p")) { + Minor = In.take_while(isDigit); + In = In.substr(Major.size()); + + // 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; + } + } + + // TODO: Handle 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 '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, 3> Prefix{"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 Name(Ext.substr(Type.size())); + StringRef Desc = getExtensionTypeDesc(Ext); + + 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 = 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.empty()) { + std::string Error = Desc; + Error += " name missing after"; + D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) + << MArch << Error << Ext; + 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; + } + + // Check if duplicated extension. + if (llvm::is_contained(AllExts, Ext)) { + std::string Error = "duplicated "; + Error += Desc; + D.Diag(diag::err_drv_invalid_riscv_ext_arch_name) + << MArch << Error << Ext; + return; + } + + // Extension format is correct, keep parsing the extensions. + // TODO: Save Type, Name, Major, Minor to avoid parsing them later. + AllExts.push_back(Ext); + } + + // 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; + } + Features.push_back(Args.MakeArgString("+" + Ext)); + } +} + +// Returns false if an error is diagnosed. +static bool getArchFeatures(const Driver &D, StringRef MArch, + 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 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'. + StringRef OtherExts; + size_t Pos = Exts.find_first_of("sx"); + 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)) + return false; + + // 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) { + 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; + + 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. + } + + // The order is OK, then push it into 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; + } + } + + // 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); + + return true; +} + +void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args, + std::vector<StringRef> &Features) { + llvm::Optional<StringRef> MArch; + if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) + MArch = A->getValue(); + else if (Triple.getOS() == llvm::Triple::Linux) + // RISC-V Linux defaults to rv{32,64}gc. + MArch = Triple.getArch() == llvm::Triple::riscv32 ? "rv32gc" : "rv64gc"; + + if (MArch.hasValue() && !getArchFeatures(D, *MArch, Features, Args)) + return; + + // -mrelax is default, unless -mno-relax is specified. + if (Args.hasFlag(options::OPT_mrelax, options::OPT_mno_relax, true)) + Features.push_back("+relax"); + else + Features.push_back("-relax"); + + // GCC Compatibility: -mno-save-restore is default, unless -msave-restore is + // specified... + if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false)) { + // ... but we don't support -msave-restore, so issue a warning. + D.Diag(diag::warn_drv_clang_unsupported) + << Args.getLastArg(options::OPT_msave_restore)->getAsString(Args); + } + + // Now add any that the user explicitly requested on the command line, + // which may override the defaults. + handleTargetFeaturesGroup(Args, Features, options::OPT_m_riscv_Features_Group); +} + +StringRef riscv::getRISCVABI(const ArgList &Args, const llvm::Triple &Triple) { + assert((Triple.getArch() == llvm::Triple::riscv32 || + Triple.getArch() == llvm::Triple::riscv64) && + "Unexpected triple"); + + if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) + return A->getValue(); + + // RISC-V Linux defaults to ilp32d/lp64d + if (Triple.getOS() == llvm::Triple::Linux) + return Triple.getArch() == llvm::Triple::riscv32 ? "ilp32d" : "lp64d"; + else + return Triple.getArch() == llvm::Triple::riscv32 ? "ilp32" : "lp64"; +} |
