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 @@ -5251,6 +5251,10 @@ HelpText<"Print supported cpu models for the given target (if target is not specified," " it will print the supported cpus for the default target)">, MarshallingInfoFlag>; +def print_supported_extensions : Flag<["-", "--"], "print-supported-extensions">, + Visibility<[ClangOption, CC1Option, CLOption]>, + HelpText<"Print supported extensions for RISC-V">, + MarshallingInfoFlag>; def : Flag<["-"], "mcpu=help">, Alias; def : Flag<["-"], "mtune=help">, Alias; def time : Flag<["-"], "time">, diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -283,6 +283,9 @@ /// print the supported cpus for the current target unsigned PrintSupportedCPUs : 1; + /// Print the supported extensions for the current target. + unsigned PrintSupportedExtensions : 1; + /// Show the -version text. unsigned ShowVersion : 1; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2103,7 +2103,8 @@ if (C.getArgs().hasArg(options::OPT_v) || C.getArgs().hasArg(options::OPT__HASH_HASH_HASH) || - C.getArgs().hasArg(options::OPT_print_supported_cpus)) { + C.getArgs().hasArg(options::OPT_print_supported_cpus) || + C.getArgs().hasArg(options::OPT_print_supported_extensions)) { PrintVersion(C, llvm::errs()); SuppressMissingInputWarning = true; } @@ -4273,16 +4274,30 @@ C.MakeAction(MergerInputs, types::TY_Image)); } - // If --print-supported-cpus, -mcpu=? or -mtune=? is specified, build a custom - // Compile phase that prints out supported cpu models and quits. - if (Arg *A = Args.getLastArg(options::OPT_print_supported_cpus)) { - // Use the -mcpu=? flag as the dummy input to cc1. - Actions.clear(); - Action *InputAc = C.MakeAction(*A, types::TY_C); - Actions.push_back( - C.MakeAction(InputAc, types::TY_Nothing)); - for (auto &I : Inputs) - I.second->claim(); + for (auto Opt : {options::OPT_print_supported_cpus, + options::OPT_print_supported_extensions}) { + // If --print-supported-cpus, -mcpu=? or -mtune=? is specified, build a + // custom Compile phase that prints out supported cpu models and quits. + // + // If --print-supported-extensions is specified, call the helper function + // RISCVMarchHelp in RISCVISAInfo.cpp that prints out supported extensions + // and quits. + if (Arg *A = Args.getLastArg(Opt)) { + if (Opt == options::OPT_print_supported_extensions && + !C.getDefaultToolChain().getTriple().isRISCV()) { + C.getDriver().Diag(diag::err_opt_not_valid_on_target) + << "--print-supported-extensions"; + return; + } + + // Use the -mcpu=? flag as the dummy input to cc1. + Actions.clear(); + Action *InputAc = C.MakeAction(*A, types::TY_C); + Actions.push_back( + C.MakeAction(InputAc, types::TY_Nothing)); + for (auto &I : Inputs) + I.second->claim(); + } } // Call validator for dxil when -Vd not in Args. diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/RISCVISAInfo.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TimeProfiler.h" @@ -221,6 +222,10 @@ if (Clang->getFrontendOpts().PrintSupportedCPUs) return PrintSupportedCPUs(Clang->getTargetOpts().Triple); + // --print-supported-extensions takes priority over the actual compilation. + if (Clang->getFrontendOpts().PrintSupportedExtensions) + return llvm::riscvExtensionsHelp(), 0; + // Infer the builtin include path if unspecified. if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && Clang->getHeaderSearchOpts().ResourceDir.empty()) diff --git a/llvm/include/llvm/Support/RISCVISAInfo.h b/llvm/include/llvm/Support/RISCVISAInfo.h --- a/llvm/include/llvm/Support/RISCVISAInfo.h +++ b/llvm/include/llvm/Support/RISCVISAInfo.h @@ -22,6 +22,8 @@ unsigned MinorVersion; }; +void riscvExtensionsHelp(); + class RISCVISAInfo { public: RISCVISAInfo(const RISCVISAInfo &) = delete; diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp --- a/llvm/lib/Support/RISCVISAInfo.cpp +++ b/llvm/lib/Support/RISCVISAInfo.cpp @@ -210,6 +210,29 @@ #endif } +void llvm::riscvExtensionsHelp() { + outs() << "All available -march extensions for RISC-V\n\n"; + outs() << '\t' << left_justify("Name", 20) << "Version\n"; + + RISCVISAInfo::OrderedExtensionMap ExtMap; + for (const auto &E : SupportedExtensions) + ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; + for (const auto &E : ExtMap) + outs() << format("\t%-20s%d.%d\n", E.first.c_str(), E.second.MajorVersion, + E.second.MinorVersion); + + outs() << "\nExperimental extensions\n"; + ExtMap.clear(); + for (const auto &E : SupportedExperimentalExtensions) + ExtMap[E.Name] = {E.Version.Major, E.Version.Minor}; + for (const auto &E : ExtMap) + outs() << format("\t%-20s%d.%d\n", E.first.c_str(), E.second.MajorVersion, + E.second.MinorVersion); + + outs() << "\nUse -march to specify the target's extension.\n" + "For example, clang -march=rv32i_v1p0\n"; +} + static bool stripExperimentalPrefix(StringRef &Ext) { return Ext.consume_front("experimental-"); } diff --git a/llvm/unittests/Support/RISCVISAInfoTest.cpp b/llvm/unittests/Support/RISCVISAInfoTest.cpp --- a/llvm/unittests/Support/RISCVISAInfoTest.cpp +++ b/llvm/unittests/Support/RISCVISAInfoTest.cpp @@ -626,3 +626,143 @@ EXPECT_EQ(RISCVISAInfo::getTargetFeatureForExtension(""), ""); EXPECT_EQ(RISCVISAInfo::getTargetFeatureForExtension("zbbzihintntl"), ""); } + +TEST(RiscvExtensionsHelp, CheckExtensions) { + std::string ExpectedOutput = +R"(All available -march extensions for RISC-V + + Name Version + i 2.1 + e 2.0 + m 2.0 + a 2.1 + f 2.2 + d 2.2 + c 2.0 + v 1.0 + h 1.0 + zicbom 1.0 + zicbop 1.0 + zicboz 1.0 + zicntr 1.0 + zicsr 2.0 + zifencei 2.0 + zihintntl 1.0 + zihintpause 2.0 + zihpm 1.0 + zmmul 1.0 + zawrs 1.0 + zfh 1.0 + zfhmin 1.0 + zfinx 1.0 + zdinx 1.0 + zca 1.0 + zcb 1.0 + zcd 1.0 + zce 1.0 + zcf 1.0 + zcmp 1.0 + zcmt 1.0 + zba 1.0 + zbb 1.0 + zbc 1.0 + zbkb 1.0 + zbkc 1.0 + zbkx 1.0 + zbs 1.0 + zk 1.0 + zkn 1.0 + zknd 1.0 + zkne 1.0 + zknh 1.0 + zkr 1.0 + zks 1.0 + zksed 1.0 + zksh 1.0 + zkt 1.0 + zve32f 1.0 + zve32x 1.0 + zve64d 1.0 + zve64f 1.0 + zve64x 1.0 + zvfh 1.0 + zvfhmin 1.0 + zvl1024b 1.0 + zvl128b 1.0 + zvl16384b 1.0 + zvl2048b 1.0 + zvl256b 1.0 + zvl32768b 1.0 + zvl32b 1.0 + zvl4096b 1.0 + zvl512b 1.0 + zvl64b 1.0 + zvl65536b 1.0 + zvl8192b 1.0 + zhinx 1.0 + zhinxmin 1.0 + svinval 1.0 + svnapot 1.0 + svpbmt 1.0 + xcvalu 1.0 + xcvbi 1.0 + xcvbitmanip 1.0 + xcvmac 1.0 + xcvsimd 1.0 + xsfcie 1.0 + xsfvcp 1.0 + xtheadba 1.0 + xtheadbb 1.0 + xtheadbs 1.0 + xtheadcmo 1.0 + xtheadcondmov 1.0 + xtheadfmemidx 1.0 + xtheadmac 1.0 + xtheadmemidx 1.0 + xtheadmempair 1.0 + xtheadsync 1.0 + xtheadvdot 1.0 + xventanacondops 1.0 + +Experimental extensions + zicfilp 0.2 + zicond 1.0 + zacas 1.0 + zfa 0.2 + zfbfmin 0.8 + ztso 0.1 + zvbb 1.0 + zvbc 1.0 + zvfbfmin 0.8 + zvfbfwma 0.8 + zvkb 1.0 + zvkg 1.0 + zvkn 1.0 + zvknc 1.0 + zvkned 1.0 + zvkng 1.0 + zvknha 1.0 + zvknhb 1.0 + zvks 1.0 + zvksc 1.0 + zvksed 1.0 + zvksg 1.0 + zvksh 1.0 + zvkt 1.0 + smaia 1.0 + ssaia 1.0 + +Use -march to specify the target's extension. +For example, clang -march=rv32i_v1p0)"; + + outs().flush(); + testing::internal::CaptureStdout(); + + llvm::riscvExtensionsHelp(); + outs().flush(); + + std::string CapturedOutput = testing::internal::GetCapturedStdout(); + EXPECT_TRUE([](std::string &Captured, std::string &Expected) { + return Captured.find(Expected) != std::string::npos; + }(CapturedOutput, ExpectedOutput)); +}