Index: clang/lib/Basic/Targets/RISCV.h =================================================================== --- clang/lib/Basic/Targets/RISCV.h +++ clang/lib/Basic/Targets/RISCV.h @@ -24,7 +24,7 @@ // RISC-V Target class RISCVTargetInfo : public TargetInfo { protected: - std::string ABI; + std::string ABI, CPU; bool HasM; bool HasA; bool HasF; @@ -43,6 +43,13 @@ WIntType = UnsignedInt; } + bool setCPU(const std::string &Name) override { + if (!isValidCPUName(Name)) + return false; + CPU = Name; + return true; + } + StringRef getABI() const override { return ABI; } void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; @@ -94,6 +101,9 @@ return false; } + bool isValidCPUName(StringRef Name) const override; + void fillValidCPUList(SmallVectorImpl &Values) const override; + void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; @@ -118,6 +128,9 @@ return false; } + bool isValidCPUName(StringRef Name) const override; + void fillValidCPUList(SmallVectorImpl &Values) const override; + void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; Index: clang/lib/Basic/Targets/RISCV.cpp =================================================================== --- clang/lib/Basic/Targets/RISCV.cpp +++ clang/lib/Basic/Targets/RISCV.cpp @@ -13,6 +13,7 @@ #include "RISCV.h" #include "clang/Basic/MacroBuilder.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/TargetParser.h" using namespace clang; using namespace clang::targets; @@ -160,3 +161,21 @@ return true; } + +bool RISCV32TargetInfo::isValidCPUName(StringRef Name) const { + return llvm::RISCV::checkCPUKind(llvm::RISCV::parseCPUKind(Name), false); +} + +void RISCV32TargetInfo::fillValidCPUList( + SmallVectorImpl &Values) const { + llvm::RISCV::fillValidCPUArchList(Values, false); +} + +bool RISCV64TargetInfo::isValidCPUName(StringRef Name) const { + return llvm::RISCV::checkCPUKind(llvm::RISCV::parseCPUKind(Name), true); +} + +void RISCV64TargetInfo::fillValidCPUList( + SmallVectorImpl &Values) const { + llvm::RISCV::fillValidCPUArchList(Values, true); +} Index: clang/lib/Driver/ToolChains/Arch/RISCV.h =================================================================== --- clang/lib/Driver/ToolChains/Arch/RISCV.h +++ clang/lib/Driver/ToolChains/Arch/RISCV.h @@ -26,6 +26,8 @@ const llvm::Triple &Triple); StringRef getRISCVArch(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); +StringRef getMArchFromMabi(StringRef MABI); +StringRef getMArchFromTriple(const llvm::Triple &Triple); } // end namespace riscv } // namespace tools } // end namespace driver Index: clang/lib/Driver/ToolChains/Arch/RISCV.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -354,6 +354,19 @@ return true; } +// Get features except standard extension feature +void getRISCFeaturesFromMcpu(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + const llvm::opt::Arg *A, StringRef Mcpu, + std::vector &Features) { + bool Is64Bit = (Triple.getArch() == llvm::Triple::riscv64); + llvm::RISCV::CPUKind CPUKind = llvm::RISCV::parseCPUKind(Mcpu); + if (!llvm::RISCV::checkCPUKind(CPUKind, Is64Bit) || + !llvm::RISCV::getCPUFeaturesExceptStdExt(CPUKind, Features)) { + D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); + } +} + void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, std::vector &Features) { @@ -362,6 +375,11 @@ if (!getArchFeatures(D, MArch, Features, Args)) return; + // If users give march and mcpu, get std extension feature from MArch + // and other features (ex. mirco architecture feature) from mcpu + if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) + getRISCFeaturesFromMcpu(D, Triple, Args, A, A->getValue(), Features); + // Handle features corresponding to "-ffixed-X" options if (Args.hasArg(options::OPT_ffixed_x1)) Features.push_back("+reserve-x1"); @@ -457,8 +475,7 @@ // // The logic uses the following, in order: // 1. Explicit choices using `--with-abi=` - // 2. A default based on `--with-arch=`, if provided - // 3. A default based on the target triple's arch + // 2. A default based on arch // // The logic in config.gcc is a little circular but it is not inconsistent. // @@ -469,50 +486,29 @@ if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) return A->getValue(); - // 2. Choose a default based on `-march=` + // 2. Choose a default based on arch // // rv32g | rv32*d -> ilp32d // rv32e -> ilp32e // rv32* -> ilp32 // rv64g | rv64*d -> lp64d // rv64* -> lp64 - if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) { - StringRef MArch = A->getValue(); - - if (MArch.startswith_lower("rv32")) { - // FIXME: parse `March` to find `D` extension properly - if (MArch.substr(4).contains_lower("d") || - MArch.startswith_lower("rv32g")) - return "ilp32d"; - else if (MArch.startswith_lower("rv32e")) - return "ilp32e"; - else - return "ilp32"; - } else if (MArch.startswith_lower("rv64")) { - // FIXME: parse `March` to find `D` extension properly - if (MArch.substr(4).contains_lower("d") || - MArch.startswith_lower("rv64g")) - return "lp64d"; - else - return "lp64"; - } - } + StringRef MArch = getRISCVArch(Args, Triple); - // 3. Choose a default based on the triple - // - // We deviate from GCC's defaults here: - // - On `riscv{XLEN}-unknown-elf` we use the integer calling convention only. - // - On all other OSs we use the double floating point calling convention. - if (Triple.getArch() == llvm::Triple::riscv32) { - if (Triple.getOS() == llvm::Triple::UnknownOS) - return "ilp32"; - else + if (MArch.startswith_lower("rv32")) { + // FIXME: parse `March` to find `D` extension properly + if (MArch.substr(4).contains_lower("d") || MArch.startswith_lower("rv32g")) return "ilp32d"; - } else { - if (Triple.getOS() == llvm::Triple::UnknownOS) - return "lp64"; + else if (MArch.startswith_lower("rv32e")) + return "ilp32e"; else + return "ilp32"; + } else if (MArch.startswith_lower("rv64")) { + // FIXME: parse `March` to find `D` extension properly + if (MArch.substr(4).contains_lower("d") || MArch.startswith_lower("rv64g")) return "lp64d"; + else + return "lp64"; } } @@ -530,8 +526,9 @@ // // The logic uses the following, in order: // 1. Explicit choices using `--with-arch=` - // 2. A default based on `--with-abi=`, if provided - // 3. A default based on the target triple's arch + // 2. Based on `-mcpu` if target cpu has default isa extension feature + // 3. A default based on `--with-abi=`, if provided + // 4. A default based on the target triple's arch // // The logic in config.gcc is a little circular but it is not inconsistent. // @@ -545,7 +542,15 @@ if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) return A->getValue(); - // 2. Choose a default based on `-mabi=` + // 2. Get march (isa string) based on `-mcpu=` + if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { + StringRef MArch = llvm::RISCV::getMArchFromMcpu(A->getValue()); + // Bypass if target cpu's default march is empty. + if (MArch != "") + return MArch; + } + + // 3. Choose a default based on `-mabi=` // // ilp32e -> rv32e // ilp32 | ilp32f | ilp32d -> rv32imafdc @@ -561,7 +566,7 @@ return "rv64imafdc"; } - // 3. Choose a default based on the triple + // 4. Choose a default based on the triple // // We deviate from GCC's defaults here: // - On `riscv{XLEN}-unknown-elf` we default to `rv{XLEN}imac` Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -305,6 +305,11 @@ } return TargetCPUName; } + case llvm::Triple::riscv32: + case llvm::Triple::riscv64: + if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) + return A->getValue(); + return ""; case llvm::Triple::bpfel: case llvm::Triple::bpfeb: Index: clang/test/Driver/riscv-cpus.c =================================================================== --- /dev/null +++ clang/test/Driver/riscv-cpus.c @@ -0,0 +1,29 @@ +// Check target CPUs are correctly passed. + +// RUN: %clang -target riscv32 -### -c %s 2>&1 -mcpu=rocket-rv32 | FileCheck -check-prefix=MCPU-ROCKETCHIP32 %s +// MCPU-ROCKETCHIP32: "-nostdsysteminc" "-target-cpu" "rocket-rv32" + +// RUN: %clang -target riscv64 -### -c %s 2>&1 -mcpu=rocket-rv64 | FileCheck -check-prefix=MCPU-ROCKETCHIP64 %s +// MCPU-ROCKETCHIP64: "-nostdsysteminc" "-target-cpu" "rocket-rv64" +// MCPU-ROCKETCHIP64: "-target-feature" "+64bit" + +// mcpu with default march +// RUN: %clang -target riscv64 -### -c %s 2>&1 -mcpu=sifive-u54 | FileCheck -check-prefix=MCPU-SIFIVE-U54 %s +// MCPU-SIFIVE-U54: "-nostdsysteminc" "-target-cpu" "sifive-u54" +// MCPU-SIFIVE-U54: "-target-feature" "+m" "-target-feature" "+a" "-target-feature" "+f" "-target-feature" "+d" +// MCPU-SIFIVE-U54: "-target-feature" "+c" "-target-feature" "+64bit" + +// march overwirte mcpu's default march +// RUN: %clang -target riscv32 -### -c %s 2>&1 -mcpu=sifive-e31 -march=rv32imc | FileCheck -check-prefix=MCPU-MARCH %s +// MCPU-MARCH: "-nostdsysteminc" "-target-cpu" "sifive-e31" "-target-feature" "+m" "-target-feature" "+c" + +// Check failed cases + +// RUN: %clang -target riscv32 -### -c %s 2>&1 -mcpu=generic-rv321 | FileCheck -check-prefix=FAIL-MCPU-NAME %s +// FAIL-MCPU-NAME: clang-11: error: the clang compiler does not support '-mcpu=generic-rv321' + +// RUN: %clang -target riscv32 -### -c %s 2>&1 -mcpu=generic-rv32 -march=rv64i | FileCheck -check-prefix=MISMATCH-ARCH %s +// MISMATCH-ARCH: clang-11: error: the clang compiler does not support '-mcpu=generic-rv32' + +// RUN: %clang -target riscv32 -### -c %s 2>&1 -mcpu=generic-rv64 | FileCheck -check-prefix=MISMATCH-MCPU %s +// MISMATCH-MCPU: clang-11: error: the clang compiler does not support '-mcpu=generic-rv64' Index: llvm/include/llvm/Support/RISCVTargetParser.def =================================================================== --- /dev/null +++ llvm/include/llvm/Support/RISCVTargetParser.def @@ -0,0 +1,13 @@ +#ifndef PROC +#define PROC(ENUM, NAME, FEATURES, DEFAULT_MARCH) +#endif + +PROC(INVALID, {"invalid"}, FK_INVALID, {""}) +PROC(GENERIC_RV32, {"generic-rv32"}, FK_NONE, {""}) +PROC(GENERIC_RV64, {"generic-rv64"}, FK_64BIT, {""}) +PROC(ROCKET_RV32, {"rocket-rv32"}, FK_NONE, {""}) +PROC(ROCKET_RV64, {"rocket-rv64"}, FK_64BIT, {""}) +PROC(SIFIVE_E31, {"sifive-e31"}, FK_NONE, {"rv32imac"}) +PROC(SIFIVE_U54, {"sifive-u54"}, FK_64BIT, {"rv64gc"}) + +#undef PROC Index: llvm/include/llvm/Support/TargetParser.h =================================================================== --- llvm/include/llvm/Support/TargetParser.h +++ llvm/include/llvm/Support/TargetParser.h @@ -169,6 +169,32 @@ } // namespace AMDGPU +namespace RISCV { + +enum CPUKind : unsigned { +#define PROC(ENUM, NAME, FEATURES, DEFAULT_MARCH) CK_##ENUM, +#include "RISCVTargetParser.def" +}; + +enum FeatureKind : unsigned { + FK_INVALID = 0, + FK_NONE = 1, + FK_STDEXTM = 1 << 2, + FK_STDEXTA = 1 << 3, + FK_STDEXTF = 1 << 4, + FK_STDEXTD = 1 << 5, + FK_STDEXTC = 1 << 6, + FK_64BIT = 1 << 7, +}; + +bool checkCPUKind(CPUKind kind, bool IsRV64); +CPUKind parseCPUKind(StringRef CPU); +StringRef getMArchFromMcpu(StringRef CPU); +void fillValidCPUArchList(SmallVectorImpl &Values, bool IsRV64); +bool getCPUFeaturesExceptStdExt(CPUKind kind, std::vector &Features); + +} // namespace RISCV + } // namespace llvm #endif Index: llvm/lib/Support/TargetParser.cpp =================================================================== --- llvm/lib/Support/TargetParser.cpp +++ llvm/lib/Support/TargetParser.cpp @@ -13,12 +13,14 @@ #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/TargetParser.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" using namespace llvm; using namespace AMDGPU; +using namespace RISCV; namespace { @@ -117,6 +119,20 @@ return I; } +struct CPUInfo { + StringLiteral Name; + CPUKind Kind; + unsigned Features; + StringLiteral DefaultMarch; + bool Is64Bit() const { return (Features & FK_64BIT); } +}; + +constexpr CPUInfo RISCVCPUInfo[] = { +#define PROC(ENUM, NAME, FEATURES, DEFAULT_MARCH) \ + {NAME, CK_##ENUM, FEATURES, DEFAULT_MARCH}, +#include "llvm/Support/RISCVTargetParser.def" +}; + } // namespace StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) { @@ -206,3 +222,42 @@ default: return {0, 0, 0}; } } + +bool RISCV::checkCPUKind(CPUKind Kind, bool IsRV64) { + if (Kind == CK_INVALID) + return false; + return RISCVCPUInfo[static_cast(Kind)].Is64Bit() == IsRV64; +} + +RISCV::CPUKind RISCV::parseCPUKind(StringRef CPU) { + return llvm::StringSwitch(CPU) +#define PROC(ENUM, NAME, FEATURES, DEFAULT_MARCH) .Case(NAME, CK_##ENUM) +#include "llvm/Support/RISCVTargetParser.def" + .Default(CK_INVALID); +} + +StringRef RISCV::getMArchFromMcpu(StringRef CPU) { + CPUKind Kind = parseCPUKind(CPU); + return RISCVCPUInfo[static_cast(Kind)].DefaultMarch; +} + +void RISCV::fillValidCPUArchList(SmallVectorImpl &Values, bool IsRV64) { + for (const auto &C : RISCVCPUInfo) { + if (IsRV64 == C.Is64Bit()) + Values.emplace_back(C.Name); + } +} + +// Get all features except standard extension feature +bool RISCV::getCPUFeaturesExceptStdExt(CPUKind Kind, + std::vector &Features) { + unsigned features = RISCVCPUInfo[static_cast(Kind)].Features; + + if (features == FK_INVALID) + return false; + + if (features & FK_64BIT) + Features.push_back("+64bit"); + + return true; +} Index: llvm/lib/Target/RISCV/RISCV.td =================================================================== --- llvm/lib/Target/RISCV/RISCV.td +++ llvm/lib/Target/RISCV/RISCV.td @@ -115,6 +115,16 @@ def : ProcessorModel<"rocket-rv64", Rocket64Model, [Feature64Bit]>; +def : ProcessorModel<"sifive-e31", Rocket32Model, [FeatureStdExtM, + FeatureStdExtA, + FeatureStdExtC]>; + +def : ProcessorModel<"sifive-u54", Rocket64Model, [Feature64Bit, + FeatureStdExtM, + FeatureStdExtA, + FeatureStdExtF, + FeatureStdExtD, + FeatureStdExtC]>; //===----------------------------------------------------------------------===// // Define the RISC-V target.