diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h --- a/clang/lib/Basic/Targets/RISCV.h +++ b/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; @@ -44,6 +44,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; @@ -97,6 +104,9 @@ return false; } + bool isValidCPUName(StringRef Name) const override; + void fillValidCPUList(SmallVectorImpl &Values) const override; + void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; @@ -121,6 +131,9 @@ return false; } + bool isValidCPUName(StringRef Name) const override; + void fillValidCPUList(SmallVectorImpl &Values) const override; + void setMaxAtomicWidth() override { MaxAtomicPromoteWidth = 128; diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/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; @@ -166,3 +167,23 @@ return true; } + +bool RISCV32TargetInfo::isValidCPUName(StringRef Name) const { + return llvm::RISCV::checkCPUKind(llvm::RISCV::parseCPUKind(Name), + /*Is64Bit=*/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), + /*Is64Bit=*/true); +} + +void RISCV64TargetInfo::fillValidCPUList( + SmallVectorImpl &Values) const { + llvm::RISCV::fillValidCPUArchList(Values, true); +} diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.h b/clang/lib/Driver/ToolChains/Arch/RISCV.h --- a/clang/lib/Driver/ToolChains/Arch/RISCV.h +++ b/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 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 @@ -436,6 +436,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) { @@ -444,6 +457,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"); @@ -533,11 +551,9 @@ // GCC's logic around choosing a default `-mabi=` is complex. If GCC is not // configured using `--with-abi=`, then the logic for the default choice is - // defined in config.gcc. This function is based on the logic in GCC 9.2.0. We - // deviate from GCC's default only on baremetal targets (UnknownOS) where - // neither `-march` nor `-mabi` is specified. + // defined in config.gcc. This function is based on the logic in GCC 9.2.0. // - // The logic uses the following, in order: + // The logic uses in GCC 9.2.0 as 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 @@ -546,55 +562,40 @@ // // Clang does not have `--with-arch=` or `--with-abi=`, so we use `-march=` // and `-mabi=` respectively instead. + // + // In order to make chosing logic more clear, clang uses the following logic, + // in order: + // 1. Explicit choices using `-mabi=` + // 2. A default based on clang's choosing arch logic. The order of default + // arch login is `-march`, `-mcpu` and target triples`. // 1. If `-mabi=` is specified, use it. 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 clang's choosing arch logic. // // 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"; } } @@ -607,10 +608,11 @@ // GCC's logic around choosing a default `-march=` is complex. If GCC is not // configured using `--with-arch=`, then the logic for the default choice is // defined in config.gcc. This function is based on the logic in GCC 9.2.0. We - // deviate from GCC's default only on baremetal targets (UnknownOS) where - // neither `-march` nor `-mabi` is specified. + // deviate from GCC's default on additional `-mcpu` option (GCC does not + // support `-mcpu`) and baremetal targets (UnknownOS) where neither `-march` + // nor `-mabi` is specified. // - // The logic uses the following, in order: + // The logic uses in GCC 9.2.0 as 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 @@ -620,6 +622,12 @@ // Clang does not have `--with-arch=` or `--with-abi=`, so we use `-march=` // and `-mabi=` respectively instead. // + // Clang uses the following logic, in order: + // 1. Explicit choices using `-march=` + // 2. Based on `-mcpu` if target cpu has default isa extension feature + // 3. A default based on `-mabi`, if provided + // 4. A default based on the target triple's arch + // // Clang does not yet support MULTILIB_REUSE, so we use `rv{XLEN}imafdc` // instead of `rv{XLEN}gc` though they are (currently) equivalent. @@ -627,7 +635,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 @@ -643,7 +659,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` diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -333,6 +333,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: diff --git a/clang/test/Driver/riscv-cpus.c b/clang/test/Driver/riscv-cpus.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/riscv-cpus.c @@ -0,0 +1,38 @@ +// 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" +// MCPU-SIFIVE-U54: "-target-abi" "lp64d" + +// mcpu with mabi option +// RUN: %clang -target riscv64 -### -c %s 2>&1 -mcpu=sifive-u54 -mabi=lp64 | FileCheck -check-prefix=MCPU-ABI-SIFIVE-U54 %s +// MCPU-ABI-SIFIVE-U54: "-nostdsysteminc" "-target-cpu" "sifive-u54" +// MCPU-ABI-SIFIVE-U54: "-target-feature" "+m" "-target-feature" "+a" "-target-feature" "+f" "-target-feature" "+d" +// MCPU-ABI-SIFIVE-U54: "-target-feature" "+c" "-target-feature" "+64bit" +// MCPU-ABI-SIFIVE-U54: "-target-abi" "lp64" + +// 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" +// MCPU-MARCH: "-target-abi" "ilp32" + +// 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' diff --git a/llvm/include/llvm/Support/RISCVTargetParser.def b/llvm/include/llvm/Support/RISCVTargetParser.def new file mode 100644 --- /dev/null +++ b/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 diff --git a/llvm/include/llvm/Support/TargetParser.h b/llvm/include/llvm/Support/TargetParser.h --- a/llvm/include/llvm/Support/TargetParser.h +++ b/llvm/include/llvm/Support/TargetParser.h @@ -130,6 +130,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 diff --git a/llvm/lib/Support/TargetParser.cpp b/llvm/lib/Support/TargetParser.cpp --- a/llvm/lib/Support/TargetParser.cpp +++ b/llvm/lib/Support/TargetParser.cpp @@ -13,6 +13,7 @@ #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" @@ -208,3 +209,64 @@ default: return {0, 0, 0}; } } + +namespace llvm { +namespace RISCV { + +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" +}; + +bool checkCPUKind(CPUKind Kind, bool IsRV64) { + if (Kind == CK_INVALID) + return false; + return RISCVCPUInfo[static_cast(Kind)].Is64Bit() == IsRV64; +} + +CPUKind 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 getMArchFromMcpu(StringRef CPU) { + CPUKind Kind = parseCPUKind(CPU); + return RISCVCPUInfo[static_cast(Kind)].DefaultMarch; +} + +void 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 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"); + else + Features.push_back("-64bit"); + + return true; +} + +} // namespace RISCV +} // namespace llvm diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td --- a/llvm/lib/Target/RISCV/RISCV.td +++ b/llvm/lib/Target/RISCV/RISCV.td @@ -215,6 +215,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.