diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -390,6 +390,11 @@ "-ftest-module-file-extension argument '%0' is not of the required form " "'blockname:major:minor:hashed:user info'">; +def warn_drv_multilib_fallback : Warning< + "%0 option unrecognized in multi-lib configuration when parsing config " + "from GCC, falling back to built-in multi-lib configuration.">, + InGroup; + def warn_slash_u_filename : Warning<"'/U%0' treated as the '/U' option">, InGroup>; def note_use_dashdash : Note<"Use '--' to treat subsequent arguments as filenames">; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -767,6 +767,7 @@ def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">; def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">; def MisleadingIndentation : DiagGroup<"misleading-indentation">; +def MultilibFallback : DiagGroup<"multilib-fallback">; // This covers both the deprecated case (in C++98) // and the extension case (in C++11 onwards). 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,7 @@ const llvm::Triple &Triple); StringRef getRISCVArch(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); +StringRef getRISCVCodeModel(const llvm::opt::ArgList &Args); } // 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 @@ -721,3 +721,15 @@ return "rv64imafdc"; } } + +StringRef riscv::getRISCVCodeModel(const llvm::opt::ArgList &Args) +{ + /* Default code model is small(medlow). */ + StringRef CodeModel; + if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) + CodeModel = A->getValue(); + else + CodeModel = "small"; + + return CodeModel; +} diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -25,6 +25,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" #include "llvm/Support/TargetParser.h" #include "llvm/Support/VirtualFileSystem.h" #include @@ -1583,10 +1584,187 @@ return false; } +static std::string findGCCPath(const Driver &D, + llvm::StringRef BasePath) +{ + SmallString<128> GCCPath; + llvm::sys::path::append(GCCPath, BasePath, "bin", + D.getTargetTriple() + "-gcc"); + if (llvm::sys::fs::exists(GCCPath)) + return GCCPath.str().str(); + else + return ""; +} + +static std::string getGCCPath(const Driver &D, + const ArgList &Args) +{ + llvm::StringRef GCCBasePath; + std::string GCCPath; + + // Find GCC from -gcc-toolchain if given. + const Arg *A = Args.getLastArg(clang::driver::options::OPT_gcc_toolchain); + if (A) { + GCCPath = findGCCPath(D, A->getValue()); + } else { + // Try to find GCC from GCC_INSTALL_PREFIX if define. + llvm::StringRef GCCInstallPrefix = GCC_INSTALL_PREFIX; + if (!GCCInstallPrefix.empty()) + GCCPath = findGCCPath(D, GCCInstallPrefix); + else { + // Try to find GCC from the same folder as driver. + SmallString<128> GCCBasePath; + llvm::sys::path::append(GCCBasePath, D.Dir, ".."); + GCCPath = findGCCPath(D, GCCBasePath); + } + } + + return GCCPath; +} + +bool ScanGCCMultilibConfig( + const Driver &D, + const llvm::Triple &TargetTriple, + StringRef Path, + const ArgList &Args, + const std::string &MultilibOutput, + DetectedMultilibs &Result) { + llvm::StringSet<> AllABI; + llvm::StringSet<> AllArch; + llvm::StringSet<> AllMCmodel; + Multilib::flags_list Flags; + + FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS()); + + // Get current ABI, Arch, and code model. + StringRef ABIName = tools::riscv::getRISCVABI(Args, TargetTriple); + StringRef MArch = tools::riscv::getRISCVArch(Args, TargetTriple); + StringRef CodeModel = tools::riscv::getRISCVCodeModel(Args); + + // Turns it into option style, to make it able to compare + // to multi-lib option list. + std::string CurrentABIOpt = Twine("mabi=", ABIName).str(); + std::string CurrentArchOpt = Twine("march=", MArch).str(); + std::string CurrentMCmodelOpt = + llvm::StringSwitch(CodeModel) + .Case("medium", "mcmodel=medany") + .Default("mcmodel=medlow"); + + llvm::ErrorOr> File = + D.getVFS().getBufferForFile(MultilibOutput); + std::vector Ms; + + if (File) { + SmallVector Lines; + File.get()->getBuffer().split(Lines, "\n"); + for (StringRef Line : Lines) { + if (Line.empty()) + continue; + // Format for multi-lib: + // ;@opt1@opt2 + // For example: + // rv32ec/ilp32e;@march=rv32ec@mabi=ilp32e + // -march=rv32ec and -mabi=ilp32e using rv32ec/ilp32e. + auto MultilibInfo = Line.split(';'); + StringRef Path = MultilibInfo.first; + + // Skip default multi-lib path. + // Clang has implied a default multi-lib rule there, + // so we don't need to add it manually. + if (Path == ".") + continue; + + StringRef Options = MultilibInfo.second.substr(1); + SmallVector OptionList; + Options.split(OptionList, '@'); + // multilib path rule is ${march}/${mabi} + auto Multilib = makeMultilib(Path); + for (StringRef Option : OptionList) { + Multilib.flag(Twine("+", Option).str()); + + // Gather all used option from multi-lib config. + if (Option.startswith("march=")) { + if (!AllArch.contains(Option)) { + // Make sure every option we only process once + AllArch.insert(Option); + addMultilibFlag(CurrentArchOpt == Option, + Option.str().c_str(), Flags); + } + } else if (Option.startswith("mabi=")) { + if (!AllABI.contains(Option)) { + AllABI.insert(Option); + addMultilibFlag(CurrentABIOpt == Option, + Option.str().c_str(), Flags); + } + } else if (Option.startswith("mcmodel=")) { + if (!AllMCmodel.contains(Option)) { + AllMCmodel.insert(Option); + addMultilibFlag(CurrentMCmodelOpt == Option, + Option.str().c_str(), Flags); + } + } else { + // Got unrecognized option in multi-lib config, fallback. + D.Diag(diag::warn_drv_multilib_fallback) << Option; + return false; + } + } + Ms.emplace_back(Multilib); + } + } else { + // Ooops, some thing wrong during open file, let's fallback. + return false; + } + + MultilibSet RISCVMultilibs = + MultilibSet() + .FilterOut(NonExistent) + .Either(ArrayRef(Ms)); + + RISCVMultilibs.select(Flags, Result.SelectedMultilib); + + Result.Multilibs = RISCVMultilibs; + + return true; +} + + +static bool getMultilibFromGCC(const Driver &D, + const llvm::Triple &TargetTriple, + StringRef Path, + const ArgList &Args, + DetectedMultilibs &Result) { + // Try to find where is GCC. + std::string GCCPath = getGCCPath(D, Args); + + // Not found? fallback to built-in multi-lib. + if (GCCPath.empty ()) + return false; + + // Ask GCC's multi-lib config via --print-multi-lib. + StringRef GCCArgs[] = {{GCCPath}, {"--print-multi-lib"}}; + std::string MultilibOutput = D.GetTemporaryPath("gcc-output-", ""); + Optional Redirects[] = {None, {MultilibOutput}, {""}}; + + int rc = llvm::sys::ExecuteAndWait(GCCPath, GCCArgs, None, Redirects); + + // Any failed happen? let fallback to built-in multi-lib. + if (rc != 0) + return false; + + // Parsing output of --print-multi-lib, and use that info to determine + // which multi-lib should be used. + return ScanGCCMultilibConfig(D, TargetTriple, + Path, Args, MultilibOutput, Result); +} + static void findRISCVBareMetalMultilibs(const Driver &D, const llvm::Triple &TargetTriple, StringRef Path, const ArgList &Args, DetectedMultilibs &Result) { + // Try to get multilib from GCC first. + if (getMultilibFromGCC(D, TargetTriple, Path, Args, Result)) + return; + FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS()); struct RiscvMultilib { StringRef march; diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/bin/riscv32-unknown-elf-gcc b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/bin/riscv32-unknown-elf-gcc new file mode 100755 --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/bin/riscv32-unknown-elf-gcc @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +print ("rv32iac/ilp32;@march=rv32iac@mabi=ilp32") +print ("rv32imafc/ilp32f;@march=rv32iac@mabi=ilp32f") +print ("rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d") diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/crtend.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32iac/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32iac/ilp32/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32iac/ilp32/crtend.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32iac/ilp32/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32imafc/ilp32f/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32imafc/ilp32f/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32imafc/ilp32f/crtend.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv32imafc/ilp32f/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv64imafdc/lp64d/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv64imafdc/lp64d/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv64imafdc/lp64d/crtend.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/lib/gcc/riscv32-unknown-elf/8.2.0/rv64imafdc/lp64d/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv32iac/ilp32/crt0.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv32iac/ilp32/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv32imafc/ilp32f/crt0.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv32imafc/ilp32f/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv64imac/lp64/crt0.o b/clang/test/Driver/Inputs/multilib_riscv32_elf_sdk/riscv32-unknown-elf/lib/rv64imac/lp64/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/bin/riscv64-unknown-elf-gcc b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/bin/riscv64-unknown-elf-gcc new file mode 100755 --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/bin/riscv64-unknown-elf-gcc @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +print ("rv32iac/ilp32;@march=rv32iac@mabi=ilp32") +print ("rv32imafc/ilp32f;@march=rv32iac@mabi=ilp32f") +print ("rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d") diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/crtend.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32iac/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32iac/ilp32/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32iac/ilp32/crtend.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32iac/ilp32/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32imafc/ilp32f/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32imafc/ilp32f/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32imafc/ilp32f/crtend.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv32imafc/ilp32f/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv64imafdc/lp64d/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv64imafdc/lp64d/crtbegin.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv64imafdc/lp64d/crtend.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/lib/gcc/riscv64-unknown-elf/8.2.0/rv64imafdc/lp64d/crtend.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv32iac/ilp32/crt0.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv32iac/ilp32/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv32imafc/ilp32f/crt0.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv32imafc/ilp32f/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv64imac/lp64/crt0.o b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk/riscv64-unknown-elf/lib/rv64imac/lp64/crt0.o new file mode 100644 diff --git a/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk_bad/bin/riscv64-unknown-elf-gcc b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk_bad/bin/riscv64-unknown-elf-gcc new file mode 100755 --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_riscv64_elf_sdk_bad/bin/riscv64-unknown-elf-gcc @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +print ("rv32iac/ilp32;@march=rv32iac@mabi=ilp32") +print ("rv32imafc/ilp32f;@march=rv32iac@mabi=ilp32f") +print ("rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d@xxx") diff --git a/clang/test/Driver/riscv32-toolchain.c b/clang/test/Driver/riscv32-toolchain.c --- a/clang/test/Driver/riscv32-toolchain.c +++ b/clang/test/Driver/riscv32-toolchain.c @@ -197,6 +197,17 @@ // C-RV32-RTLIB-COMPILERRT-ILP32: "--start-group" "-lc" "-lgloss" "--end-group" "{{.*}}libclang_rt.builtins-riscv32.a" // C-RV32-RTLIB-COMPILERRT-ILP32: "{{.*}}clang_rt.crtend-riscv32.o" +// Test for multi-lib config from GCC. +// RUN: %clang %s \ +// RUN: -target riscv32-unknown-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv32_elf_sdk \ +// RUN: --print-multi-lib \ +// RUN: | FileCheck -check-prefix=C-RV32-GCC-MULTI-LIB %s +// C-RV32-GCC-MULTI-LIB: rv32iac/ilp32;@march=rv32iac@mabi=ilp32 +// C-RV32-GCC-MULTI-LIB-NEXT: rv32imafc/ilp32f;@march=rv32iac@mabi=ilp32f +// C-RV32-GCC-MULTI-LIB-NEXT: rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d +// C-RV32-GCC-MULTI-LIB-NOT: {{^.+$}} + // RUN: %clang -target riscv32 %s -emit-llvm -S -o - | FileCheck %s typedef __builtin_va_list va_list; diff --git a/clang/test/Driver/riscv64-toolchain.c b/clang/test/Driver/riscv64-toolchain.c --- a/clang/test/Driver/riscv64-toolchain.c +++ b/clang/test/Driver/riscv64-toolchain.c @@ -153,6 +153,24 @@ // C-RV64-RTLIB-COMPILERRT-LP64: "--start-group" "-lc" "-lgloss" "--end-group" "{{.*}}libclang_rt.builtins-riscv64.a" // C-RV64-RTLIB-COMPILERRT-LP64: "{{.*}}clang_rt.crtend-riscv64.o" +// Test for multi-lib config from GCC. +// RUN: %clang %s \ +// RUN: -target riscv64-unknown-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv64_elf_sdk \ +// RUN: --print-multi-lib \ +// RUN: | FileCheck -check-prefix=C-RV64-GCC-MULTI-LIB %s +// C-RV64-GCC-MULTI-LIB: rv32iac/ilp32;@march=rv32iac@mabi=ilp32 +// C-RV64-GCC-MULTI-LIB-NEXT: rv32imafc/ilp32f;@march=rv32iac@mabi=ilp32f +// C-RV64-GCC-MULTI-LIB-NEXT: rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d +// C-RV64-GCC-MULTI-LIB-NOT: {{^.+$}} + +// RUN: %clang %s \ +// RUN: -target riscv64-unknown-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv64_elf_sdk_bad \ +// RUN: --print-multi-lib 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV64-GCC-MULTI-LIB-BAD %s +// C-RV64-GCC-MULTI-LIB-BAD: warning: xxx option unrecognized in multi-lib configuration when parsing config from GCC, falling back to built-in multi-lib configuration. [-Wmultilib-fallback] + // RUN: %clang -target riscv64 %s -emit-llvm -S -o - | FileCheck %s typedef __builtin_va_list va_list;