diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Driver/Driver.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Driver/Driver.cpp | 3365 |
1 files changed, 2356 insertions, 1009 deletions
diff --git a/contrib/llvm-project/clang/lib/Driver/Driver.cpp b/contrib/llvm-project/clang/lib/Driver/Driver.cpp index 94a7553e273b..f9dc8ab24fa9 100644 --- a/contrib/llvm-project/clang/lib/Driver/Driver.cpp +++ b/contrib/llvm-project/clang/lib/Driver/Driver.cpp @@ -11,11 +11,10 @@ #include "ToolChains/AMDGPU.h" #include "ToolChains/AMDGPUOpenMP.h" #include "ToolChains/AVR.h" -#include "ToolChains/Ananas.h" +#include "ToolChains/Arch/RISCV.h" #include "ToolChains/BareMetal.h" +#include "ToolChains/CSKYToolChain.h" #include "ToolChains/Clang.h" -#include "ToolChains/CloudABI.h" -#include "ToolChains/Contiki.h" #include "ToolChains/CrossWindows.h" #include "ToolChains/Cuda.h" #include "ToolChains/Darwin.h" @@ -23,7 +22,9 @@ #include "ToolChains/FreeBSD.h" #include "ToolChains/Fuchsia.h" #include "ToolChains/Gnu.h" -#include "ToolChains/HIP.h" +#include "ToolChains/HIPAMD.h" +#include "ToolChains/HIPSPV.h" +#include "ToolChains/HLSL.h" #include "ToolChains/Haiku.h" #include "ToolChains/Hexagon.h" #include "ToolChains/Hurd.h" @@ -32,21 +33,23 @@ #include "ToolChains/MSP430.h" #include "ToolChains/MSVC.h" #include "ToolChains/MinGW.h" -#include "ToolChains/Minix.h" #include "ToolChains/MipsLinux.h" -#include "ToolChains/Myriad.h" #include "ToolChains/NaCl.h" #include "ToolChains/NetBSD.h" +#include "ToolChains/OHOS.h" #include "ToolChains/OpenBSD.h" +#include "ToolChains/PPCFreeBSD.h" #include "ToolChains/PPCLinux.h" #include "ToolChains/PS4CPU.h" #include "ToolChains/RISCVToolchain.h" +#include "ToolChains/SPIRV.h" #include "ToolChains/Solaris.h" #include "ToolChains/TCE.h" #include "ToolChains/VEToolchain.h" #include "ToolChains/WebAssembly.h" #include "ToolChains/XCore.h" #include "ToolChains/ZOS.h" +#include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/TargetID.h" #include "clang/Basic/Version.h" #include "clang/Config/config.h" @@ -56,17 +59,19 @@ #include "clang/Driver/InputInfo.h" #include "clang/Driver/Job.h" #include "clang/Driver/Options.h" +#include "clang/Driver/Phases.h" #include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" +#include "clang/Driver/Types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" +#include "llvm/MC/TargetRegistry.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptSpecifier.h" @@ -77,18 +82,22 @@ #include "llvm/Support/ExitCodes.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/Host.h" #include "llvm/Support/MD5.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" -#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/RISCVISAInfo.h" +#include <cstdlib> // ::getenv #include <map> #include <memory> +#include <optional> +#include <set> #include <utility> #if LLVM_ON_UNIX #include <unistd.h> // getpid @@ -98,9 +107,67 @@ using namespace clang::driver; using namespace clang; using namespace llvm::opt; -static llvm::Triple getHIPOffloadTargetTriple() { - static const llvm::Triple T("amdgcn-amd-amdhsa"); - return T; +static std::optional<llvm::Triple> getOffloadTargetTriple(const Driver &D, + const ArgList &Args) { + auto OffloadTargets = Args.getAllArgValues(options::OPT_offload_EQ); + // Offload compilation flow does not support multiple targets for now. We + // need the HIPActionBuilder (and possibly the CudaActionBuilder{,Base}too) + // to support multiple tool chains first. + switch (OffloadTargets.size()) { + default: + D.Diag(diag::err_drv_only_one_offload_target_supported); + return std::nullopt; + case 0: + D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << ""; + return std::nullopt; + case 1: + break; + } + return llvm::Triple(OffloadTargets[0]); +} + +static std::optional<llvm::Triple> +getNVIDIAOffloadTargetTriple(const Driver &D, const ArgList &Args, + const llvm::Triple &HostTriple) { + if (!Args.hasArg(options::OPT_offload_EQ)) { + return llvm::Triple(HostTriple.isArch64Bit() ? "nvptx64-nvidia-cuda" + : "nvptx-nvidia-cuda"); + } + auto TT = getOffloadTargetTriple(D, Args); + if (TT && (TT->getArch() == llvm::Triple::spirv32 || + TT->getArch() == llvm::Triple::spirv64)) { + if (Args.hasArg(options::OPT_emit_llvm)) + return TT; + D.Diag(diag::err_drv_cuda_offload_only_emit_bc); + return std::nullopt; + } + D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << TT->str(); + return std::nullopt; +} +static std::optional<llvm::Triple> +getHIPOffloadTargetTriple(const Driver &D, const ArgList &Args) { + if (!Args.hasArg(options::OPT_offload_EQ)) { + auto OffloadArchs = Args.getAllArgValues(options::OPT_offload_arch_EQ); + if (llvm::find(OffloadArchs, "amdgcnspirv") != OffloadArchs.cend()) { + if (OffloadArchs.size() == 1) + return llvm::Triple("spirv64-amd-amdhsa"); + // Mixing specific & SPIR-V compilation is not supported for now. + D.Diag(diag::err_drv_only_one_offload_target_supported); + return std::nullopt; + } + return llvm::Triple("amdgcn-amd-amdhsa"); // Default HIP triple. + } + auto TT = getOffloadTargetTriple(D, Args); + if (!TT) + return std::nullopt; + if (TT->getArch() == llvm::Triple::amdgcn && + TT->getVendor() == llvm::Triple::AMD && + TT->getOS() == llvm::Triple::AMDHSA) + return TT; + if (TT->getArch() == llvm::Triple::spirv64) + return TT; + D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << TT->str(); + return std::nullopt; } // static @@ -123,37 +190,39 @@ std::string Driver::GetResourcesPath(StringRef BinaryPath, // path of the embedding binary, which for LLVM binaries will be in bin/. // ../lib gets us to lib/ in both cases. P = llvm::sys::path::parent_path(Dir); - llvm::sys::path::append(P, Twine("lib") + CLANG_LIBDIR_SUFFIX, "clang", - CLANG_VERSION_STRING); + // This search path is also created in the COFF driver of lld, so any + // changes here also needs to happen in lld/COFF/Driver.cpp + llvm::sys::path::append(P, CLANG_INSTALL_LIBDIR_BASENAME, "clang", + CLANG_VERSION_MAJOR_STRING); } - return std::string(P.str()); + return std::string(P); } Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, DiagnosticsEngine &Diags, std::string Title, IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) : Diags(Diags), VFS(std::move(VFS)), Mode(GCCMode), - SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone), LTOMode(LTOK_None), + SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone), + Offload(OffloadHostDevice), CXX20HeaderType(HeaderMode_None), + ModulesModeCXX20(false), LTOMode(LTOK_None), ClangExecutable(ClangExecutable), SysRoot(DEFAULT_SYSROOT), - DriverTitle(Title), CCPrintStatReportFilename(), CCPrintOptionsFilename(), - CCPrintHeadersFilename(), CCLogDiagnosticsFilename(), - CCCPrintBindings(false), CCPrintOptions(false), CCPrintHeaders(false), + DriverTitle(Title), CCCPrintBindings(false), CCPrintOptions(false), CCLogDiagnostics(false), CCGenDiagnostics(false), - CCPrintProcessStats(false), TargetTriple(TargetTriple), - CCCGenericGCCName(""), Saver(Alloc), CheckInputsExist(true), - GenReproducer(false), SuppressMissingInputWarning(false) { + CCPrintProcessStats(false), CCPrintInternalStats(false), + TargetTriple(TargetTriple), Saver(Alloc), PrependArg(nullptr), + CheckInputsExist(true), ProbePrecompiled(true), + SuppressMissingInputWarning(false) { // Provide a sane fallback if no VFS is specified. if (!this->VFS) this->VFS = llvm::vfs::getRealFileSystem(); Name = std::string(llvm::sys::path::filename(ClangExecutable)); Dir = std::string(llvm::sys::path::parent_path(ClangExecutable)); - InstalledDir = Dir; // Provide a sensible default installed dir. if ((!SysRoot.empty()) && llvm::sys::path::is_relative(SysRoot)) { // Prepend InstalledDir if SysRoot is relative - SmallString<128> P(InstalledDir); + SmallString<128> P(Dir); llvm::sys::path::append(P, SysRoot); SysRoot = std::string(P); } @@ -162,7 +231,11 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR; #endif #if defined(CLANG_CONFIG_FILE_USER_DIR) - UserConfigDir = CLANG_CONFIG_FILE_USER_DIR; + { + SmallString<128> P; + llvm::sys::fs::expand_tilde(CLANG_CONFIG_FILE_USER_DIR, P); + UserConfigDir = static_cast<std::string>(P); + } #endif // Compute the path to the resource directory. @@ -170,40 +243,30 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, } void Driver::setDriverMode(StringRef Value) { - static const std::string OptName = + static StringRef OptName = getOpts().getOption(options::OPT_driver_mode).getPrefixedName(); - if (auto M = llvm::StringSwitch<llvm::Optional<DriverMode>>(Value) + if (auto M = llvm::StringSwitch<std::optional<DriverMode>>(Value) .Case("gcc", GCCMode) .Case("g++", GXXMode) .Case("cpp", CPPMode) .Case("cl", CLMode) .Case("flang", FlangMode) - .Default(None)) + .Case("dxc", DXCMode) + .Default(std::nullopt)) Mode = *M; else Diag(diag::err_drv_unsupported_option_argument) << OptName << Value; } InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings, - bool IsClCompatMode, - bool &ContainsError) { + bool UseDriverMode, bool &ContainsError) { llvm::PrettyStackTraceString CrashInfo("Command line argument parsing"); ContainsError = false; - unsigned IncludedFlagsBitmask; - unsigned ExcludedFlagsBitmask; - std::tie(IncludedFlagsBitmask, ExcludedFlagsBitmask) = - getIncludeExcludeOptionFlagMasks(IsClCompatMode); - - // Make sure that Flang-only options don't pollute the Clang output - // TODO: Make sure that Clang-only options don't pollute Flang output - if (!IsFlangMode()) - ExcludedFlagsBitmask |= options::FlangOnlyOption; - + llvm::opt::Visibility VisibilityMask = getOptionVisibilityMask(UseDriverMode); unsigned MissingArgIndex, MissingArgCount; - InputArgList Args = - getOpts().ParseArgs(ArgStrings, MissingArgIndex, MissingArgCount, - IncludedFlagsBitmask, ExcludedFlagsBitmask); + InputArgList Args = getOpts().ParseArgs(ArgStrings, MissingArgIndex, + MissingArgCount, VisibilityMask); // Check for missing argument error. if (MissingArgCount) { @@ -217,19 +280,9 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings, // Check for unsupported options. for (const Arg *A : Args) { if (A->getOption().hasFlag(options::Unsupported)) { - unsigned DiagID; - auto ArgString = A->getAsString(Args); - std::string Nearest; - if (getOpts().findNearest( - ArgString, Nearest, IncludedFlagsBitmask, - ExcludedFlagsBitmask | options::Unsupported) > 1) { - DiagID = diag::err_drv_unsupported_opt; - Diag(DiagID) << ArgString; - } else { - DiagID = diag::err_drv_unsupported_opt_with_suggestion; - Diag(DiagID) << ArgString << Nearest; - } - ContainsError |= Diags.getDiagnosticLevel(DiagID, SourceLocation()) > + Diag(diag::err_drv_unsupported_opt) << A->getAsString(Args); + ContainsError |= Diags.getDiagnosticLevel(diag::err_drv_unsupported_opt, + SourceLocation()) > DiagnosticsEngine::Warning; continue; } @@ -247,11 +300,17 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings, unsigned DiagID; auto ArgString = A->getAsString(Args); std::string Nearest; - if (getOpts().findNearest( - ArgString, Nearest, IncludedFlagsBitmask, ExcludedFlagsBitmask) > 1) { - DiagID = IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl - : diag::err_drv_unknown_argument; - Diags.Report(DiagID) << ArgString; + if (getOpts().findNearest(ArgString, Nearest, VisibilityMask) > 1) { + if (!IsCLMode() && + getOpts().findExact(ArgString, Nearest, + llvm::opt::Visibility(options::CC1Option))) { + DiagID = diag::err_drv_unknown_argument_with_suggestion; + Diags.Report(DiagID) << ArgString << "-Xclang " + Nearest; + } else { + DiagID = IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl + : diag::err_drv_unknown_argument; + Diags.Report(DiagID) << ArgString; + } } else { DiagID = IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl_with_suggestion @@ -262,6 +321,18 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings, DiagnosticsEngine::Warning; } + for (const Arg *A : Args.filtered(options::OPT_o)) { + if (ArgStrings[A->getIndex()] == A->getSpelling()) + continue; + + // Warn on joined arguments that are similar to a long argument. + std::string ArgString = ArgStrings[A->getIndex()]; + std::string Nearest; + if (getOpts().findExact("-" + ArgString, Nearest, VisibilityMask)) + Diags.Report(diag::warn_drv_potentially_misspelled_joined_argument) + << A->getAsString(Args) << Nearest; + } + return Args; } @@ -277,22 +348,29 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, if (CCCIsCPP() || (PhaseArg = DAL.getLastArg(options::OPT_E)) || (PhaseArg = DAL.getLastArg(options::OPT__SLASH_EP)) || (PhaseArg = DAL.getLastArg(options::OPT_M, options::OPT_MM)) || - (PhaseArg = DAL.getLastArg(options::OPT__SLASH_P))) { + (PhaseArg = DAL.getLastArg(options::OPT__SLASH_P)) || + CCGenDiagnostics) { FinalPhase = phases::Preprocess; - // --precompile only runs up to precompilation. - } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile))) { + // --precompile only runs up to precompilation. + // Options that cause the output of C++20 compiled module interfaces or + // header units have the same effect. + } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile)) || + (PhaseArg = DAL.getLastArg(options::OPT_extract_api)) || + (PhaseArg = DAL.getLastArg(options::OPT_fmodule_header, + options::OPT_fmodule_header_EQ))) { FinalPhase = phases::Precompile; - - // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler. + // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler. } else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) || (PhaseArg = DAL.getLastArg(options::OPT_print_supported_cpus)) || + (PhaseArg = DAL.getLastArg(options::OPT_print_enabled_extensions)) || (PhaseArg = DAL.getLastArg(options::OPT_module_file_info)) || (PhaseArg = DAL.getLastArg(options::OPT_verify_pch)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) || (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || (PhaseArg = DAL.getLastArg(options::OPT__analyze)) || + (PhaseArg = DAL.getLastArg(options::OPT_emit_cir)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) { FinalPhase = phases::Compile; @@ -304,6 +382,9 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, } else if ((PhaseArg = DAL.getLastArg(options::OPT_c))) { FinalPhase = phases::Assemble; + } else if ((PhaseArg = DAL.getLastArg(options::OPT_emit_interface_stubs))) { + FinalPhase = phases::IfsMerge; + // Otherwise do everything. } else FinalPhase = phases::Link; @@ -331,7 +412,20 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const { bool HasNostdlib = Args.hasArg(options::OPT_nostdlib); bool HasNostdlibxx = Args.hasArg(options::OPT_nostdlibxx); bool HasNodefaultlib = Args.hasArg(options::OPT_nodefaultlibs); + bool IgnoreUnused = false; for (Arg *A : Args) { + if (IgnoreUnused) + A->claim(); + + if (A->getOption().matches(options::OPT_start_no_unused_arguments)) { + IgnoreUnused = true; + continue; + } + if (A->getOption().matches(options::OPT_end_no_unused_arguments)) { + IgnoreUnused = false; + continue; + } + // Unfortunately, we have to parse some forwarding options (-Xassembler, // -Xlinker, -Xpreprocessor) because we either integrate their functionality // (assembler and preprocessor), or bypass a previous driver ('collect2'). @@ -397,9 +491,13 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const { DAL->append(A); } + // DXC mode quits before assembly if an output object file isn't specified. + if (IsDXCMode() && !Args.hasArg(options::OPT_dxc_Fo)) + DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S)); + // Enforce -static if -miamcu is present. if (Args.hasFlag(options::OPT_miamcu, options::OPT_mno_iamcu, false)) - DAL->AddFlagArg(0, Opts.getOption(options::OPT_static)); + DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_static)); // Add a default value of -mlinker-version=, if one was given and the user // didn't specify one. @@ -432,55 +530,52 @@ static llvm::Triple computeTargetTriple(const Driver &D, // GNU/Hurd's triples should have been -hurd-gnu*, but were historically made // -gnu* only, and we can not change this, so we have to detect that case as // being the Hurd OS. - if (TargetTriple.find("-unknown-gnu") != StringRef::npos || - TargetTriple.find("-pc-gnu") != StringRef::npos) + if (TargetTriple.contains("-unknown-gnu") || TargetTriple.contains("-pc-gnu")) Target.setOSName("hurd"); // Handle Apple-specific options available here. if (Target.isOSBinFormatMachO()) { // If an explicit Darwin arch name is given, that trumps all. if (!DarwinArchName.empty()) { - tools::darwin::setTripleTypeForMachOArchName(Target, DarwinArchName); + tools::darwin::setTripleTypeForMachOArchName(Target, DarwinArchName, + Args); return Target; } // Handle the Darwin '-arch' flag. if (Arg *A = Args.getLastArg(options::OPT_arch)) { StringRef ArchName = A->getValue(); - tools::darwin::setTripleTypeForMachOArchName(Target, ArchName); + tools::darwin::setTripleTypeForMachOArchName(Target, ArchName, Args); } } // Handle pseudo-target flags '-mlittle-endian'/'-EL' and // '-mbig-endian'/'-EB'. - if (Arg *A = Args.getLastArg(options::OPT_mlittle_endian, - options::OPT_mbig_endian)) { - if (A->getOption().matches(options::OPT_mlittle_endian)) { - llvm::Triple LE = Target.getLittleEndianArchVariant(); - if (LE.getArch() != llvm::Triple::UnknownArch) - Target = std::move(LE); - } else { - llvm::Triple BE = Target.getBigEndianArchVariant(); - if (BE.getArch() != llvm::Triple::UnknownArch) - Target = std::move(BE); + if (Arg *A = Args.getLastArgNoClaim(options::OPT_mlittle_endian, + options::OPT_mbig_endian)) { + llvm::Triple T = A->getOption().matches(options::OPT_mlittle_endian) + ? Target.getLittleEndianArchVariant() + : Target.getBigEndianArchVariant(); + if (T.getArch() != llvm::Triple::UnknownArch) { + Target = std::move(T); + Args.claimAllArgs(options::OPT_mlittle_endian, options::OPT_mbig_endian); } } // Skip further flag support on OSes which don't support '-m32' or '-m64'. - if (Target.getArch() == llvm::Triple::tce || - Target.getOS() == llvm::Triple::Minix) + if (Target.getArch() == llvm::Triple::tce) return Target; // On AIX, the env OBJECT_MODE may affect the resulting arch variant. if (Target.isOSAIX()) { - if (Optional<std::string> ObjectModeValue = + if (std::optional<std::string> ObjectModeValue = llvm::sys::Process::GetEnv("OBJECT_MODE")) { StringRef ObjectMode = *ObjectModeValue; llvm::Triple::ArchType AT = llvm::Triple::UnknownArch; - if (ObjectMode.equals("64")) { + if (ObjectMode == "64") { AT = Target.get64BitArchVariant().getArch(); - } else if (ObjectMode.equals("32")) { + } else if (ObjectMode == "32") { AT = Target.get32BitArchVariant().getArch(); } else { D.Diag(diag::err_drv_invalid_object_mode) << ObjectMode; @@ -491,15 +586,24 @@ static llvm::Triple computeTargetTriple(const Driver &D, } } + // The `-maix[32|64]` flags are only valid for AIX targets. + if (Arg *A = Args.getLastArgNoClaim(options::OPT_maix32, options::OPT_maix64); + A && !Target.isOSAIX()) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getAsString(Args) << Target.str(); + // Handle pseudo-target flags '-m64', '-mx32', '-m32' and '-m16'. Arg *A = Args.getLastArg(options::OPT_m64, options::OPT_mx32, - options::OPT_m32, options::OPT_m16); + options::OPT_m32, options::OPT_m16, + options::OPT_maix32, options::OPT_maix64); if (A) { llvm::Triple::ArchType AT = llvm::Triple::UnknownArch; - if (A->getOption().matches(options::OPT_m64)) { + if (A->getOption().matches(options::OPT_m64) || + A->getOption().matches(options::OPT_maix64)) { AT = Target.get64BitArchVariant().getArch(); - if (Target.getEnvironment() == llvm::Triple::GNUX32) + if (Target.getEnvironment() == llvm::Triple::GNUX32 || + Target.getEnvironment() == llvm::Triple::GNUT64) Target.setEnvironment(llvm::Triple::GNU); else if (Target.getEnvironment() == llvm::Triple::MuslX32) Target.setEnvironment(llvm::Triple::Musl); @@ -510,7 +614,8 @@ static llvm::Triple computeTargetTriple(const Driver &D, Target.setEnvironment(llvm::Triple::MuslX32); else Target.setEnvironment(llvm::Triple::GNUX32); - } else if (A->getOption().matches(options::OPT_m32)) { + } else if (A->getOption().matches(options::OPT_m32) || + A->getOption().matches(options::OPT_maix32)) { AT = Target.get32BitArchVariant().getArch(); if (Target.getEnvironment() == llvm::Triple::GNUX32) Target.setEnvironment(llvm::Triple::GNU); @@ -522,8 +627,11 @@ static llvm::Triple computeTargetTriple(const Driver &D, Target.setEnvironment(llvm::Triple::CODE16); } - if (AT != llvm::Triple::UnknownArch && AT != Target.getArch()) + if (AT != llvm::Triple::UnknownArch && AT != Target.getArch()) { Target.setArch(AT); + if (Target.isWindowsGNUEnvironment()) + toolchains::MinGW::fixTripleArch(D, Target, Args); + } } // Handle -miamcu flag. @@ -547,36 +655,46 @@ static llvm::Triple computeTargetTriple(const Driver &D, // If target is MIPS adjust the target triple // accordingly to provided ABI name. - A = Args.getLastArg(options::OPT_mabi_EQ); - if (A && Target.isMIPS()) { - StringRef ABIName = A->getValue(); - if (ABIName == "32") { - Target = Target.get32BitArchVariant(); - if (Target.getEnvironment() == llvm::Triple::GNUABI64 || - Target.getEnvironment() == llvm::Triple::GNUABIN32) - Target.setEnvironment(llvm::Triple::GNU); - } else if (ABIName == "n32") { - Target = Target.get64BitArchVariant(); - if (Target.getEnvironment() == llvm::Triple::GNU || - Target.getEnvironment() == llvm::Triple::GNUABI64) - Target.setEnvironment(llvm::Triple::GNUABIN32); - } else if (ABIName == "64") { - Target = Target.get64BitArchVariant(); - if (Target.getEnvironment() == llvm::Triple::GNU || - Target.getEnvironment() == llvm::Triple::GNUABIN32) - Target.setEnvironment(llvm::Triple::GNUABI64); + if (Target.isMIPS()) { + if ((A = Args.getLastArg(options::OPT_mabi_EQ))) { + StringRef ABIName = A->getValue(); + if (ABIName == "32") { + Target = Target.get32BitArchVariant(); + if (Target.getEnvironment() == llvm::Triple::GNUABI64 || + Target.getEnvironment() == llvm::Triple::GNUABIN32) + Target.setEnvironment(llvm::Triple::GNU); + } else if (ABIName == "n32") { + Target = Target.get64BitArchVariant(); + if (Target.getEnvironment() == llvm::Triple::GNU || + Target.getEnvironment() == llvm::Triple::GNUT64 || + Target.getEnvironment() == llvm::Triple::GNUABI64) + Target.setEnvironment(llvm::Triple::GNUABIN32); + } else if (ABIName == "64") { + Target = Target.get64BitArchVariant(); + if (Target.getEnvironment() == llvm::Triple::GNU || + Target.getEnvironment() == llvm::Triple::GNUT64 || + Target.getEnvironment() == llvm::Triple::GNUABIN32) + Target.setEnvironment(llvm::Triple::GNUABI64); + } } } // If target is RISC-V adjust the target triple according to // provided architecture name - A = Args.getLastArg(options::OPT_march_EQ); - if (A && Target.isRISCV()) { - StringRef ArchName = A->getValue(); - if (ArchName.startswith_insensitive("rv32")) - Target.setArch(llvm::Triple::riscv32); - else if (ArchName.startswith_insensitive("rv64")) - Target.setArch(llvm::Triple::riscv64); + if (Target.isRISCV()) { + if (Args.hasArg(options::OPT_march_EQ) || + Args.hasArg(options::OPT_mcpu_EQ)) { + std::string ArchName = tools::riscv::getRISCVArch(Args, Target); + auto ISAInfo = llvm::RISCVISAInfo::parseArchString( + ArchName, /*EnableExperimentalExtensions=*/true); + if (!llvm::errorToBool(ISAInfo.takeError())) { + unsigned XLen = (*ISAInfo)->getXLen(); + if (XLen == 32) + Target.setArch(llvm::Triple::riscv32); + else if (XLen == 64) + Target.setArch(llvm::Triple::riscv64); + } + } } return Target; @@ -585,53 +703,45 @@ static llvm::Triple computeTargetTriple(const Driver &D, // Parse the LTO options and record the type of LTO compilation // based on which -f(no-)?lto(=.*)? or -f(no-)?offload-lto(=.*)? // option occurs last. -static llvm::Optional<driver::LTOKind> -parseLTOMode(Driver &D, const llvm::opt::ArgList &Args, OptSpecifier OptPos, - OptSpecifier OptNeg, OptSpecifier OptEq, bool IsOffload) { - driver::LTOKind LTOMode = LTOK_None; - // Non-offload LTO allows -flto=auto and -flto=jobserver. Offload LTO does - // not support those options. - if (!Args.hasFlag(OptPos, OptEq, OptNeg, false) && - (IsOffload || - (!Args.hasFlag(options::OPT_flto_EQ_auto, options::OPT_fno_lto, false) && - !Args.hasFlag(options::OPT_flto_EQ_jobserver, options::OPT_fno_lto, - false)))) - return None; - - StringRef LTOName("full"); +static driver::LTOKind parseLTOMode(Driver &D, const llvm::opt::ArgList &Args, + OptSpecifier OptEq, OptSpecifier OptNeg) { + if (!Args.hasFlag(OptEq, OptNeg, false)) + return LTOK_None; const Arg *A = Args.getLastArg(OptEq); - if (A) - LTOName = A->getValue(); + StringRef LTOName = A->getValue(); - LTOMode = llvm::StringSwitch<LTOKind>(LTOName) - .Case("full", LTOK_Full) - .Case("thin", LTOK_Thin) - .Default(LTOK_Unknown); + driver::LTOKind LTOMode = llvm::StringSwitch<LTOKind>(LTOName) + .Case("full", LTOK_Full) + .Case("thin", LTOK_Thin) + .Default(LTOK_Unknown); if (LTOMode == LTOK_Unknown) { - assert(A); D.Diag(diag::err_drv_unsupported_option_argument) - << A->getOption().getName() << A->getValue(); - return None; + << A->getSpelling() << A->getValue(); + return LTOK_None; } return LTOMode; } // Parse the LTO options. void Driver::setLTOMode(const llvm::opt::ArgList &Args) { - LTOMode = LTOK_None; - if (auto M = parseLTOMode(*this, Args, options::OPT_flto, - options::OPT_fno_lto, options::OPT_flto_EQ, - /*IsOffload=*/false)) - LTOMode = M.getValue(); - - OffloadLTOMode = LTOK_None; - if (auto M = parseLTOMode(*this, Args, options::OPT_foffload_lto, - options::OPT_fno_offload_lto, - options::OPT_foffload_lto_EQ, - /*IsOffload=*/true)) - OffloadLTOMode = M.getValue(); + LTOMode = + parseLTOMode(*this, Args, options::OPT_flto_EQ, options::OPT_fno_lto); + + OffloadLTOMode = parseLTOMode(*this, Args, options::OPT_foffload_lto_EQ, + options::OPT_fno_offload_lto); + + // Try to enable `-foffload-lto=full` if `-fopenmp-target-jit` is on. + if (Args.hasFlag(options::OPT_fopenmp_target_jit, + options::OPT_fno_openmp_target_jit, false)) { + if (Arg *A = Args.getLastArg(options::OPT_foffload_lto_EQ, + options::OPT_fno_offload_lto)) + if (OffloadLTOMode != LTOK_Full) + Diag(diag::err_drv_incompatible_options) + << A->getSpelling() << "-fopenmp-target-jit"; + OffloadLTOMode = LTOK_Full; + } } /// Compute the desired OpenMP runtime from the flags provided. @@ -651,7 +761,7 @@ Driver::OpenMPRuntimeKind Driver::getOpenMPRuntime(const ArgList &Args) const { if (RT == OMPRT_Unknown) { if (A) Diag(diag::err_drv_unsupported_option_argument) - << A->getOption().getName() << A->getValue(); + << A->getSpelling() << A->getValue(); else // FIXME: We could use a nicer diagnostic here. Diag(diag::err_drv_unsupported_opt) << "-fopenmp"; @@ -677,7 +787,8 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, [](std::pair<types::ID, const llvm::opt::Arg *> &I) { return types::isHIP(I.first); }) || - C.getInputArgs().hasArg(options::OPT_hip_link); + C.getInputArgs().hasArg(options::OPT_hip_link) || + C.getInputArgs().hasArg(options::OPT_hipstdpar); if (IsCuda && IsHIP) { Diag(clang::diag::err_drv_mix_cuda_hip); return; @@ -685,108 +796,187 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, if (IsCuda) { const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>(); const llvm::Triple &HostTriple = HostTC->getTriple(); - StringRef DeviceTripleStr; auto OFK = Action::OFK_Cuda; - DeviceTripleStr = - HostTriple.isArch64Bit() ? "nvptx64-nvidia-cuda" : "nvptx-nvidia-cuda"; - llvm::Triple CudaTriple(DeviceTripleStr); + auto CudaTriple = + getNVIDIAOffloadTargetTriple(*this, C.getInputArgs(), HostTriple); + if (!CudaTriple) + return; // Use the CUDA and host triples as the key into the ToolChains map, // because the device toolchain we create depends on both. - auto &CudaTC = ToolChains[CudaTriple.str() + "/" + HostTriple.str()]; + auto &CudaTC = ToolChains[CudaTriple->str() + "/" + HostTriple.str()]; if (!CudaTC) { CudaTC = std::make_unique<toolchains::CudaToolChain>( - *this, CudaTriple, *HostTC, C.getInputArgs(), OFK); + *this, *CudaTriple, *HostTC, C.getInputArgs()); + + // Emit a warning if the detected CUDA version is too new. + CudaInstallationDetector &CudaInstallation = + static_cast<toolchains::CudaToolChain &>(*CudaTC).CudaInstallation; + if (CudaInstallation.isValid()) + CudaInstallation.WarnIfUnsupportedVersion(); } C.addOffloadDeviceToolChain(CudaTC.get(), OFK); } else if (IsHIP) { + if (auto *OMPTargetArg = + C.getInputArgs().getLastArg(options::OPT_fopenmp_targets_EQ)) { + Diag(clang::diag::err_drv_unsupported_opt_for_language_mode) + << OMPTargetArg->getSpelling() << "HIP"; + return; + } const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>(); - const llvm::Triple &HostTriple = HostTC->getTriple(); auto OFK = Action::OFK_HIP; - llvm::Triple HIPTriple = getHIPOffloadTargetTriple(); - // Use the HIP and host triples as the key into the ToolChains map, - // because the device toolchain we create depends on both. - auto &HIPTC = ToolChains[HIPTriple.str() + "/" + HostTriple.str()]; - if (!HIPTC) { - HIPTC = std::make_unique<toolchains::HIPToolChain>( - *this, HIPTriple, *HostTC, C.getInputArgs()); - } - C.addOffloadDeviceToolChain(HIPTC.get(), OFK); + auto HIPTriple = getHIPOffloadTargetTriple(*this, C.getInputArgs()); + if (!HIPTriple) + return; + auto *HIPTC = &getOffloadingDeviceToolChain(C.getInputArgs(), *HIPTriple, + *HostTC, OFK); + assert(HIPTC && "Could not create offloading device tool chain."); + C.addOffloadDeviceToolChain(HIPTC, OFK); } // // OpenMP // // We need to generate an OpenMP toolchain if the user specified targets with - // the -fopenmp-targets option. - if (Arg *OpenMPTargets = - C.getInputArgs().getLastArg(options::OPT_fopenmp_targets_EQ)) { - if (OpenMPTargets->getNumValues()) { - // We expect that -fopenmp-targets is always used in conjunction with the - // option -fopenmp specifying a valid runtime with offloading support, - // i.e. libomp or libiomp. - bool HasValidOpenMPRuntime = C.getInputArgs().hasFlag( - options::OPT_fopenmp, options::OPT_fopenmp_EQ, - options::OPT_fno_openmp, false); - if (HasValidOpenMPRuntime) { - OpenMPRuntimeKind OpenMPKind = getOpenMPRuntime(C.getInputArgs()); - HasValidOpenMPRuntime = - OpenMPKind == OMPRT_OMP || OpenMPKind == OMPRT_IOMP5; + // the -fopenmp-targets option or used --offload-arch with OpenMP enabled. + bool IsOpenMPOffloading = + C.getInputArgs().hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, + options::OPT_fno_openmp, false) && + (C.getInputArgs().hasArg(options::OPT_fopenmp_targets_EQ) || + C.getInputArgs().hasArg(options::OPT_offload_arch_EQ)); + if (IsOpenMPOffloading) { + // We expect that -fopenmp-targets is always used in conjunction with the + // option -fopenmp specifying a valid runtime with offloading support, i.e. + // libomp or libiomp. + OpenMPRuntimeKind RuntimeKind = getOpenMPRuntime(C.getInputArgs()); + if (RuntimeKind != OMPRT_OMP && RuntimeKind != OMPRT_IOMP5) { + Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets); + return; + } + + llvm::StringMap<llvm::DenseSet<StringRef>> DerivedArchs; + llvm::StringMap<StringRef> FoundNormalizedTriples; + std::multiset<StringRef> OpenMPTriples; + + // If the user specified -fopenmp-targets= we create a toolchain for each + // valid triple. Otherwise, if only --offload-arch= was specified we instead + // attempt to derive the appropriate toolchains from the arguments. + if (Arg *OpenMPTargets = + C.getInputArgs().getLastArg(options::OPT_fopenmp_targets_EQ)) { + if (OpenMPTargets && !OpenMPTargets->getNumValues()) { + Diag(clang::diag::warn_drv_empty_joined_argument) + << OpenMPTargets->getAsString(C.getInputArgs()); + return; + } + for (StringRef T : OpenMPTargets->getValues()) + OpenMPTriples.insert(T); + } else if (C.getInputArgs().hasArg(options::OPT_offload_arch_EQ) && + !IsHIP && !IsCuda) { + const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>(); + auto AMDTriple = getHIPOffloadTargetTriple(*this, C.getInputArgs()); + auto NVPTXTriple = getNVIDIAOffloadTargetTriple(*this, C.getInputArgs(), + HostTC->getTriple()); + + // Attempt to deduce the offloading triple from the set of architectures. + // We can only correctly deduce NVPTX / AMDGPU triples currently. We need + // to temporarily create these toolchains so that we can access tools for + // inferring architectures. + llvm::DenseSet<StringRef> Archs; + if (NVPTXTriple) { + auto TempTC = std::make_unique<toolchains::CudaToolChain>( + *this, *NVPTXTriple, *HostTC, C.getInputArgs()); + for (StringRef Arch : getOffloadArchs( + C, C.getArgs(), Action::OFK_OpenMP, &*TempTC, true)) + Archs.insert(Arch); + } + if (AMDTriple) { + auto TempTC = std::make_unique<toolchains::AMDGPUOpenMPToolChain>( + *this, *AMDTriple, *HostTC, C.getInputArgs()); + for (StringRef Arch : getOffloadArchs( + C, C.getArgs(), Action::OFK_OpenMP, &*TempTC, true)) + Archs.insert(Arch); + } + if (!AMDTriple && !NVPTXTriple) { + for (StringRef Arch : + getOffloadArchs(C, C.getArgs(), Action::OFK_OpenMP, nullptr, true)) + Archs.insert(Arch); } - if (HasValidOpenMPRuntime) { - llvm::StringMap<const char *> FoundNormalizedTriples; - for (const char *Val : OpenMPTargets->getValues()) { - llvm::Triple TT(Val); - std::string NormalizedName = TT.normalize(); - - // Make sure we don't have a duplicate triple. - auto Duplicate = FoundNormalizedTriples.find(NormalizedName); - if (Duplicate != FoundNormalizedTriples.end()) { - Diag(clang::diag::warn_drv_omp_offload_target_duplicate) - << Val << Duplicate->second; - continue; - } + for (StringRef Arch : Archs) { + if (NVPTXTriple && IsNVIDIAOffloadArch(StringToOffloadArch( + getProcessorFromTargetID(*NVPTXTriple, Arch)))) { + DerivedArchs[NVPTXTriple->getTriple()].insert(Arch); + } else if (AMDTriple && + IsAMDOffloadArch(StringToOffloadArch( + getProcessorFromTargetID(*AMDTriple, Arch)))) { + DerivedArchs[AMDTriple->getTriple()].insert(Arch); + } else { + Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch) << Arch; + return; + } + } - // Store the current triple so that we can check for duplicates in the - // following iterations. - FoundNormalizedTriples[NormalizedName] = Val; - - // If the specified target is invalid, emit a diagnostic. - if (TT.getArch() == llvm::Triple::UnknownArch) - Diag(clang::diag::err_drv_invalid_omp_target) << Val; - else { - const ToolChain *TC; - // Device toolchains have to be selected differently. They pair host - // and device in their implementation. - if (TT.isNVPTX() || TT.isAMDGCN()) { - const ToolChain *HostTC = - C.getSingleOffloadToolChain<Action::OFK_Host>(); - assert(HostTC && "Host toolchain should be always defined."); - auto &DeviceTC = - ToolChains[TT.str() + "/" + HostTC->getTriple().normalize()]; - if (!DeviceTC) { - if (TT.isNVPTX()) - DeviceTC = std::make_unique<toolchains::CudaToolChain>( - *this, TT, *HostTC, C.getInputArgs(), Action::OFK_OpenMP); - else if (TT.isAMDGCN()) - DeviceTC = - std::make_unique<toolchains::AMDGPUOpenMPToolChain>( - *this, TT, *HostTC, C.getInputArgs()); - else - assert(DeviceTC && "Device toolchain not defined."); - } - - TC = DeviceTC.get(); - } else - TC = &getToolChain(C.getInputArgs(), TT); - C.addOffloadDeviceToolChain(TC, Action::OFK_OpenMP); + // If the set is empty then we failed to find a native architecture. + if (Archs.empty()) { + Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch) + << "native"; + return; + } + + for (const auto &TripleAndArchs : DerivedArchs) + OpenMPTriples.insert(TripleAndArchs.first()); + } + + for (StringRef Val : OpenMPTriples) { + llvm::Triple TT(ToolChain::getOpenMPTriple(Val)); + std::string NormalizedName = TT.normalize(); + + // Make sure we don't have a duplicate triple. + auto Duplicate = FoundNormalizedTriples.find(NormalizedName); + if (Duplicate != FoundNormalizedTriples.end()) { + Diag(clang::diag::warn_drv_omp_offload_target_duplicate) + << Val << Duplicate->second; + continue; + } + + // Store the current triple so that we can check for duplicates in the + // following iterations. + FoundNormalizedTriples[NormalizedName] = Val; + + // If the specified target is invalid, emit a diagnostic. + if (TT.getArch() == llvm::Triple::UnknownArch) + Diag(clang::diag::err_drv_invalid_omp_target) << Val; + else { + const ToolChain *TC; + // Device toolchains have to be selected differently. They pair host + // and device in their implementation. + if (TT.isNVPTX() || TT.isAMDGCN()) { + const ToolChain *HostTC = + C.getSingleOffloadToolChain<Action::OFK_Host>(); + assert(HostTC && "Host toolchain should be always defined."); + auto &DeviceTC = + ToolChains[TT.str() + "/" + HostTC->getTriple().normalize()]; + if (!DeviceTC) { + if (TT.isNVPTX()) + DeviceTC = std::make_unique<toolchains::CudaToolChain>( + *this, TT, *HostTC, C.getInputArgs()); + else if (TT.isAMDGCN()) + DeviceTC = std::make_unique<toolchains::AMDGPUOpenMPToolChain>( + *this, TT, *HostTC, C.getInputArgs()); + else + assert(DeviceTC && "Device toolchain not defined."); } - } - } else - Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets); - } else - Diag(clang::diag::warn_drv_empty_joined_argument) - << OpenMPTargets->getAsString(C.getInputArgs()); + + TC = DeviceTC.get(); + } else + TC = &getToolChain(C.getInputArgs(), TT); + C.addOffloadDeviceToolChain(TC, Action::OFK_OpenMP); + if (DerivedArchs.contains(TT.getTriple())) + KnownArchs[TC] = DerivedArchs[TT.getTriple()]; + } + } + } else if (C.getInputArgs().hasArg(options::OPT_fopenmp_targets_EQ)) { + Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets); + return; } // @@ -794,69 +984,78 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, // } -/// Looks the given directories for the specified file. -/// -/// \param[out] FilePath File path, if the file was found. -/// \param[in] Dirs Directories used for the search. -/// \param[in] FileName Name of the file to search for. -/// \return True if file was found. -/// -/// Looks for file specified by FileName sequentially in directories specified -/// by Dirs. -/// -static bool searchForFile(SmallVectorImpl<char> &FilePath, - ArrayRef<StringRef> Dirs, StringRef FileName) { - SmallString<128> WPath; - for (const StringRef &Dir : Dirs) { - if (Dir.empty()) - continue; - WPath.clear(); - llvm::sys::path::append(WPath, Dir, FileName); - llvm::sys::path::native(WPath); - if (llvm::sys::fs::is_regular_file(WPath)) { - FilePath = std::move(WPath); - return true; - } - } - return false; +static void appendOneArg(InputArgList &Args, const Arg *Opt, + const Arg *BaseArg) { + // The args for config files or /clang: flags belong to different InputArgList + // objects than Args. This copies an Arg from one of those other InputArgLists + // to the ownership of Args. + unsigned Index = Args.MakeIndex(Opt->getSpelling()); + Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index), + Index, BaseArg); + Copy->getValues() = Opt->getValues(); + if (Opt->isClaimed()) + Copy->claim(); + Copy->setOwnsValues(Opt->getOwnsValues()); + Opt->setOwnsValues(false); + Args.append(Copy); } -bool Driver::readConfigFile(StringRef FileName) { +bool Driver::readConfigFile(StringRef FileName, + llvm::cl::ExpansionContext &ExpCtx) { + // Try opening the given file. + auto Status = getVFS().status(FileName); + if (!Status) { + Diag(diag::err_drv_cannot_open_config_file) + << FileName << Status.getError().message(); + return true; + } + if (Status->getType() != llvm::sys::fs::file_type::regular_file) { + Diag(diag::err_drv_cannot_open_config_file) + << FileName << "not a regular file"; + return true; + } + // Try reading the given file. SmallVector<const char *, 32> NewCfgArgs; - if (!llvm::cl::readConfigFile(FileName, Saver, NewCfgArgs)) { - Diag(diag::err_drv_cannot_read_config_file) << FileName; + if (llvm::Error Err = ExpCtx.readConfigFile(FileName, NewCfgArgs)) { + Diag(diag::err_drv_cannot_read_config_file) + << FileName << toString(std::move(Err)); return true; } // Read options from config file. llvm::SmallString<128> CfgFileName(FileName); llvm::sys::path::native(CfgFileName); - ConfigFile = std::string(CfgFileName); bool ContainErrors; - CfgOptions = std::make_unique<InputArgList>( - ParseArgStrings(NewCfgArgs, IsCLMode(), ContainErrors)); - if (ContainErrors) { - CfgOptions.reset(); + std::unique_ptr<InputArgList> NewOptions = std::make_unique<InputArgList>( + ParseArgStrings(NewCfgArgs, /*UseDriverMode=*/true, ContainErrors)); + if (ContainErrors) return true; - } - - if (CfgOptions->hasArg(options::OPT_config)) { - CfgOptions.reset(); - Diag(diag::err_drv_nested_config_file); - return true; - } // Claim all arguments that come from a configuration file so that the driver // does not warn on any that is unused. - for (Arg *A : *CfgOptions) + for (Arg *A : *NewOptions) A->claim(); + + if (!CfgOptions) + CfgOptions = std::move(NewOptions); + else { + // If this is a subsequent config file, append options to the previous one. + for (auto *Opt : *NewOptions) { + const Arg *BaseArg = &Opt->getBaseArg(); + if (BaseArg == Opt) + BaseArg = nullptr; + appendOneArg(*CfgOptions, Opt, BaseArg); + } + } + ConfigFiles.push_back(std::string(CfgFileName)); return false; } -bool Driver::loadConfigFile() { - std::string CfgFileName; - bool FileSpecifiedExplicitly = false; +bool Driver::loadConfigFiles() { + llvm::cl::ExpansionContext ExpCtx(Saver.getAllocator(), + llvm::cl::tokenizeConfigFile); + ExpCtx.setVFS(&getVFS()); // Process options that change search path for config files. if (CLOptions) { @@ -864,144 +1063,141 @@ bool Driver::loadConfigFile() { SmallString<128> CfgDir; CfgDir.append( CLOptions->getLastArgValue(options::OPT_config_system_dir_EQ)); - if (!CfgDir.empty()) { - if (llvm::sys::fs::make_absolute(CfgDir).value() != 0) - SystemConfigDir.clear(); - else - SystemConfigDir = std::string(CfgDir.begin(), CfgDir.end()); - } + if (CfgDir.empty() || getVFS().makeAbsolute(CfgDir)) + SystemConfigDir.clear(); + else + SystemConfigDir = static_cast<std::string>(CfgDir); } if (CLOptions->hasArg(options::OPT_config_user_dir_EQ)) { SmallString<128> CfgDir; - CfgDir.append( - CLOptions->getLastArgValue(options::OPT_config_user_dir_EQ)); - if (!CfgDir.empty()) { - if (llvm::sys::fs::make_absolute(CfgDir).value() != 0) - UserConfigDir.clear(); - else - UserConfigDir = std::string(CfgDir.begin(), CfgDir.end()); - } + llvm::sys::fs::expand_tilde( + CLOptions->getLastArgValue(options::OPT_config_user_dir_EQ), CfgDir); + if (CfgDir.empty() || getVFS().makeAbsolute(CfgDir)) + UserConfigDir.clear(); + else + UserConfigDir = static_cast<std::string>(CfgDir); } } - // First try to find config file specified in command line. - if (CLOptions) { - std::vector<std::string> ConfigFiles = - CLOptions->getAllArgValues(options::OPT_config); - if (ConfigFiles.size() > 1) { - if (!std::all_of(ConfigFiles.begin(), ConfigFiles.end(), - [ConfigFiles](const std::string &s) { - return s == ConfigFiles[0]; - })) { - Diag(diag::err_drv_duplicate_config); - return true; - } - } + // Prepare list of directories where config file is searched for. + StringRef CfgFileSearchDirs[] = {UserConfigDir, SystemConfigDir, Dir}; + ExpCtx.setSearchDirs(CfgFileSearchDirs); - if (!ConfigFiles.empty()) { - CfgFileName = ConfigFiles.front(); - assert(!CfgFileName.empty()); + // First try to load configuration from the default files, return on error. + if (loadDefaultConfigFiles(ExpCtx)) + return true; + // Then load configuration files specified explicitly. + SmallString<128> CfgFilePath; + if (CLOptions) { + for (auto CfgFileName : CLOptions->getAllArgValues(options::OPT_config)) { // If argument contains directory separator, treat it as a path to // configuration file. if (llvm::sys::path::has_parent_path(CfgFileName)) { - SmallString<128> CfgFilePath; - if (llvm::sys::path::is_relative(CfgFileName)) - llvm::sys::fs::current_path(CfgFilePath); - llvm::sys::path::append(CfgFilePath, CfgFileName); - if (!llvm::sys::fs::is_regular_file(CfgFilePath)) { - Diag(diag::err_drv_config_file_not_exist) << CfgFilePath; - return true; + CfgFilePath.assign(CfgFileName); + if (llvm::sys::path::is_relative(CfgFilePath)) { + if (getVFS().makeAbsolute(CfgFilePath)) { + Diag(diag::err_drv_cannot_open_config_file) + << CfgFilePath << "cannot get absolute path"; + return true; + } } - return readConfigFile(CfgFilePath); + } else if (!ExpCtx.findConfigFile(CfgFileName, CfgFilePath)) { + // Report an error that the config file could not be found. + Diag(diag::err_drv_config_file_not_found) << CfgFileName; + for (const StringRef &SearchDir : CfgFileSearchDirs) + if (!SearchDir.empty()) + Diag(diag::note_drv_config_file_searched_in) << SearchDir; + return true; } - FileSpecifiedExplicitly = true; + // Try to read the config file, return on error. + if (readConfigFile(CfgFilePath, ExpCtx)) + return true; } } - // If config file is not specified explicitly, try to deduce configuration - // from executable name. For instance, an executable 'armv7l-clang' will - // search for config file 'armv7l-clang.cfg'. - if (CfgFileName.empty() && !ClangNameParts.TargetPrefix.empty()) - CfgFileName = ClangNameParts.TargetPrefix + '-' + ClangNameParts.ModeSuffix; + // No error occurred. + return false; +} - if (CfgFileName.empty()) +bool Driver::loadDefaultConfigFiles(llvm::cl::ExpansionContext &ExpCtx) { + // Disable default config if CLANG_NO_DEFAULT_CONFIG is set to a non-empty + // value. + if (const char *NoConfigEnv = ::getenv("CLANG_NO_DEFAULT_CONFIG")) { + if (*NoConfigEnv) + return false; + } + if (CLOptions && CLOptions->hasArg(options::OPT_no_default_config)) return false; - // Determine architecture part of the file name, if it is present. - StringRef CfgFileArch = CfgFileName; - size_t ArchPrefixLen = CfgFileArch.find('-'); - if (ArchPrefixLen == StringRef::npos) - ArchPrefixLen = CfgFileArch.size(); - llvm::Triple CfgTriple; - CfgFileArch = CfgFileArch.take_front(ArchPrefixLen); - CfgTriple = llvm::Triple(llvm::Triple::normalize(CfgFileArch)); - if (CfgTriple.getArch() == llvm::Triple::ArchType::UnknownArch) - ArchPrefixLen = 0; - - if (!StringRef(CfgFileName).endswith(".cfg")) - CfgFileName += ".cfg"; - - // If config file starts with architecture name and command line options - // redefine architecture (with options like -m32 -LE etc), try finding new - // config file with that architecture. - SmallString<128> FixedConfigFile; - size_t FixedArchPrefixLen = 0; - if (ArchPrefixLen) { - // Get architecture name from config file name like 'i386.cfg' or - // 'armv7l-clang.cfg'. - // Check if command line options changes effective triple. - llvm::Triple EffectiveTriple = computeTargetTriple(*this, - CfgTriple.getTriple(), *CLOptions); - if (CfgTriple.getArch() != EffectiveTriple.getArch()) { - FixedConfigFile = EffectiveTriple.getArchName(); - FixedArchPrefixLen = FixedConfigFile.size(); - // Append the rest of original file name so that file name transforms - // like: i386-clang.cfg -> x86_64-clang.cfg. - if (ArchPrefixLen < CfgFileName.size()) - FixedConfigFile += CfgFileName.substr(ArchPrefixLen); - } + std::string RealMode = getExecutableForDriverMode(Mode); + std::string Triple; + + // If name prefix is present, no --target= override was passed via CLOptions + // and the name prefix is not a valid triple, force it for backwards + // compatibility. + if (!ClangNameParts.TargetPrefix.empty() && + computeTargetTriple(*this, "/invalid/", *CLOptions).str() == + "/invalid/") { + llvm::Triple PrefixTriple{ClangNameParts.TargetPrefix}; + if (PrefixTriple.getArch() == llvm::Triple::UnknownArch || + PrefixTriple.isOSUnknown()) + Triple = PrefixTriple.str(); + } + + // Otherwise, use the real triple as used by the driver. + if (Triple.empty()) { + llvm::Triple RealTriple = + computeTargetTriple(*this, TargetTriple, *CLOptions); + Triple = RealTriple.str(); + assert(!Triple.empty()); + } + + // Search for config files in the following order: + // 1. <triple>-<mode>.cfg using real driver mode + // (e.g. i386-pc-linux-gnu-clang++.cfg). + // 2. <triple>-<mode>.cfg using executable suffix + // (e.g. i386-pc-linux-gnu-clang-g++.cfg for *clang-g++). + // 3. <triple>.cfg + <mode>.cfg using real driver mode + // (e.g. i386-pc-linux-gnu.cfg + clang++.cfg). + // 4. <triple>.cfg + <mode>.cfg using executable suffix + // (e.g. i386-pc-linux-gnu.cfg + clang-g++.cfg for *clang-g++). + + // Try loading <triple>-<mode>.cfg, and return if we find a match. + SmallString<128> CfgFilePath; + std::string CfgFileName = Triple + '-' + RealMode + ".cfg"; + if (ExpCtx.findConfigFile(CfgFileName, CfgFilePath)) + return readConfigFile(CfgFilePath, ExpCtx); + + bool TryModeSuffix = !ClangNameParts.ModeSuffix.empty() && + ClangNameParts.ModeSuffix != RealMode; + if (TryModeSuffix) { + CfgFileName = Triple + '-' + ClangNameParts.ModeSuffix + ".cfg"; + if (ExpCtx.findConfigFile(CfgFileName, CfgFilePath)) + return readConfigFile(CfgFilePath, ExpCtx); + } + + // Try loading <mode>.cfg, and return if loading failed. If a matching file + // was not found, still proceed on to try <triple>.cfg. + CfgFileName = RealMode + ".cfg"; + if (ExpCtx.findConfigFile(CfgFileName, CfgFilePath)) { + if (readConfigFile(CfgFilePath, ExpCtx)) + return true; + } else if (TryModeSuffix) { + CfgFileName = ClangNameParts.ModeSuffix + ".cfg"; + if (ExpCtx.findConfigFile(CfgFileName, CfgFilePath) && + readConfigFile(CfgFilePath, ExpCtx)) + return true; } - // Prepare list of directories where config file is searched for. - StringRef CfgFileSearchDirs[] = {UserConfigDir, SystemConfigDir, Dir}; - - // Try to find config file. First try file with corrected architecture. - llvm::SmallString<128> CfgFilePath; - if (!FixedConfigFile.empty()) { - if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile)) - return readConfigFile(CfgFilePath); - // If 'x86_64-clang.cfg' was not found, try 'x86_64.cfg'. - FixedConfigFile.resize(FixedArchPrefixLen); - FixedConfigFile.append(".cfg"); - if (searchForFile(CfgFilePath, CfgFileSearchDirs, FixedConfigFile)) - return readConfigFile(CfgFilePath); - } - - // Then try original file name. - if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName)) - return readConfigFile(CfgFilePath); - - // Finally try removing driver mode part: 'x86_64-clang.cfg' -> 'x86_64.cfg'. - if (!ClangNameParts.ModeSuffix.empty() && - !ClangNameParts.TargetPrefix.empty()) { - CfgFileName.assign(ClangNameParts.TargetPrefix); - CfgFileName.append(".cfg"); - if (searchForFile(CfgFilePath, CfgFileSearchDirs, CfgFileName)) - return readConfigFile(CfgFilePath); - } - - // Report error but only if config file was specified explicitly, by option - // --config. If it was deduced from executable name, it is not an error. - if (FileSpecifiedExplicitly) { - Diag(diag::err_drv_config_file_not_found) << CfgFileName; - for (const StringRef &SearchDir : CfgFileSearchDirs) - if (!SearchDir.empty()) - Diag(diag::note_drv_config_file_searched_in) << SearchDir; - return true; - } + // Try loading <triple>.cfg and return if we find a match. + CfgFileName = Triple + ".cfg"; + if (ExpCtx.findConfigFile(CfgFileName, CfgFilePath)) + return readConfigFile(CfgFilePath, ExpCtx); + // If we were unable to find a config file deduced from executable name, + // that is not an error. return false; } @@ -1023,32 +1219,17 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { // Arguments specified in command line. bool ContainsError; CLOptions = std::make_unique<InputArgList>( - ParseArgStrings(ArgList.slice(1), IsCLMode(), ContainsError)); + ParseArgStrings(ArgList.slice(1), /*UseDriverMode=*/true, ContainsError)); // Try parsing configuration file. if (!ContainsError) - ContainsError = loadConfigFile(); + ContainsError = loadConfigFiles(); bool HasConfigFile = !ContainsError && (CfgOptions.get() != nullptr); // All arguments, from both config file and command line. InputArgList Args = std::move(HasConfigFile ? std::move(*CfgOptions) : std::move(*CLOptions)); - // The args for config files or /clang: flags belong to different InputArgList - // objects than Args. This copies an Arg from one of those other InputArgLists - // to the ownership of Args. - auto appendOneArg = [&Args](const Arg *Opt, const Arg *BaseArg) { - unsigned Index = Args.MakeIndex(Opt->getSpelling()); - Arg *Copy = new llvm::opt::Arg(Opt->getOption(), Args.getArgString(Index), - Index, BaseArg); - Copy->getValues() = Opt->getValues(); - if (Opt->isClaimed()) - Copy->claim(); - Copy->setOwnsValues(Opt->getOwnsValues()); - Opt->setOwnsValues(false); - Args.append(Copy); - }; - if (HasConfigFile) for (auto *Opt : *CLOptions) { if (Opt->getOption().matches(options::OPT_config)) @@ -1056,7 +1237,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { const Arg *BaseArg = &Opt->getBaseArg(); if (BaseArg == Opt) BaseArg = nullptr; - appendOneArg(Opt, BaseArg); + appendOneArg(Args, Opt, BaseArg); } // In CL mode, look for any pass-through arguments @@ -1071,11 +1252,12 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { // Parse any pass through args using default clang processing rather // than clang-cl processing. auto CLModePassThroughOptions = std::make_unique<InputArgList>( - ParseArgStrings(CLModePassThroughArgList, false, ContainsError)); + ParseArgStrings(CLModePassThroughArgList, /*UseDriverMode=*/false, + ContainsError)); if (!ContainsError) for (auto *Opt : *CLModePassThroughOptions) { - appendOneArg(Opt, nullptr); + appendOneArg(Args, Opt, nullptr); } } } @@ -1085,13 +1267,19 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { if (VFS->setCurrentWorkingDirectory(WD->getValue())) Diag(diag::err_drv_unable_to_set_working_directory) << WD->getValue(); + // Check for missing include directories. + if (!Diags.isIgnored(diag::warn_missing_include_dirs, SourceLocation())) { + for (auto IncludeDir : Args.getAllArgValues(options::OPT_I_Group)) { + if (!VFS->exists(IncludeDir)) + Diag(diag::warn_missing_include_dirs) << IncludeDir; + } + } + // FIXME: This stuff needs to go into the Compilation, not the driver. bool CCCPrintPhases; - // Silence driver warnings if requested - Diags.setIgnoreAllWarnings(Args.hasArg(options::OPT_w)); - - // -no-canonical-prefixes is used very early in main. + // -canonical-prefixes, -no-canonical-prefixes are used very early in main. + Args.ClaimAllArgs(options::OPT_canonical_prefixes); Args.ClaimAllArgs(options::OPT_no_canonical_prefixes); // f(no-)integated-cc1 is also used very early in main. @@ -1111,9 +1299,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { CCCPrintBindings = Args.hasArg(options::OPT_ccc_print_bindings); if (const Arg *A = Args.getLastArg(options::OPT_ccc_gcc_name)) CCCGenericGCCName = A->getValue(); - GenReproducer = Args.hasFlag(options::OPT_gen_reproducer, - options::OPT_fno_crash_diagnostics, - !!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")); // Process -fproc-stat-report options. if (const Arg *A = Args.getLastArg(options::OPT_fproc_stat_report_EQ)) { @@ -1132,17 +1317,54 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { T.setVendor(llvm::Triple::PC); T.setEnvironment(llvm::Triple::MSVC); T.setObjectFormat(llvm::Triple::COFF); + if (Args.hasArg(options::OPT__SLASH_arm64EC)) + T.setArch(llvm::Triple::aarch64, llvm::Triple::AArch64SubArch_arm64ec); TargetTriple = T.str(); + } else if (IsDXCMode()) { + // Build TargetTriple from target_profile option for clang-dxc. + if (const Arg *A = Args.getLastArg(options::OPT_target_profile)) { + StringRef TargetProfile = A->getValue(); + if (auto Triple = + toolchains::HLSLToolChain::parseTargetProfile(TargetProfile)) + TargetTriple = *Triple; + else + Diag(diag::err_drv_invalid_directx_shader_module) << TargetProfile; + + A->claim(); + + if (Args.hasArg(options::OPT_spirv)) { + llvm::Triple T(TargetTriple); + T.setArch(llvm::Triple::spirv); + T.setOS(llvm::Triple::Vulkan); + + // Set specific Vulkan version if applicable. + if (const Arg *A = Args.getLastArg(options::OPT_fspv_target_env_EQ)) { + const llvm::StringSet<> ValidValues = {"vulkan1.2", "vulkan1.3"}; + if (ValidValues.contains(A->getValue())) { + T.setOSName(A->getValue()); + } else { + Diag(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + A->claim(); + } + + TargetTriple = T.str(); + } + } else { + Diag(diag::err_drv_dxc_missing_target_profile); + } } + if (const Arg *A = Args.getLastArg(options::OPT_target)) TargetTriple = A->getValue(); if (const Arg *A = Args.getLastArg(options::OPT_ccc_install_dir)) - Dir = InstalledDir = A->getValue(); + Dir = Dir = A->getValue(); for (const Arg *A : Args.filtered(options::OPT_B)) { A->claim(); PrefixDirs.push_back(A->getValue(0)); } - if (Optional<std::string> CompilerPathValue = + if (std::optional<std::string> CompilerPathValue = llvm::sys::Process::GetEnv("COMPILER_PATH")) { StringRef CompilerPath = *CompilerPathValue; while (!CompilerPath.empty()) { @@ -1167,6 +1389,17 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { .Default(SaveTempsCwd); } + if (const Arg *A = Args.getLastArg(options::OPT_offload_host_only, + options::OPT_offload_device_only, + options::OPT_offload_host_device)) { + if (A->getOption().matches(options::OPT_offload_host_only)) + Offload = OffloadHost; + else if (A->getOption().matches(options::OPT_offload_device_only)) + Offload = OffloadDevice; + else + Offload = OffloadHostDevice; + } + setLTOMode(Args); // Process -fembed-bitcode= flags. @@ -1185,6 +1418,43 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { BitcodeEmbed = static_cast<BitcodeEmbedMode>(Model); } + // Remove existing compilation database so that each job can append to it. + if (Arg *A = Args.getLastArg(options::OPT_MJ)) + llvm::sys::fs::remove(A->getValue()); + + // Setting up the jobs for some precompile cases depends on whether we are + // treating them as PCH, implicit modules or C++20 ones. + // TODO: inferring the mode like this seems fragile (it meets the objective + // of not requiring anything new for operation, however). + const Arg *Std = Args.getLastArg(options::OPT_std_EQ); + ModulesModeCXX20 = + !Args.hasArg(options::OPT_fmodules) && Std && + (Std->containsValue("c++20") || Std->containsValue("c++2a") || + Std->containsValue("c++23") || Std->containsValue("c++2b") || + Std->containsValue("c++26") || Std->containsValue("c++2c") || + Std->containsValue("c++latest")); + + // Process -fmodule-header{=} flags. + if (Arg *A = Args.getLastArg(options::OPT_fmodule_header_EQ, + options::OPT_fmodule_header)) { + // These flags force C++20 handling of headers. + ModulesModeCXX20 = true; + if (A->getOption().matches(options::OPT_fmodule_header)) + CXX20HeaderType = HeaderMode_Default; + else { + StringRef ArgName = A->getValue(); + unsigned Kind = llvm::StringSwitch<unsigned>(ArgName) + .Case("user", HeaderMode_User) + .Case("system", HeaderMode_System) + .Default(~0U); + if (Kind == ~0U) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << ArgName; + } else + CXX20HeaderType = static_cast<ModuleHeaderMode>(Kind); + } + } + std::unique_ptr<llvm::opt::InputArgList> UArgs = std::make_unique<InputArgList>(std::move(Args)); @@ -1195,6 +1465,58 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { const ToolChain &TC = getToolChain( *UArgs, computeTargetTriple(*this, TargetTriple, *UArgs)); + // Check if the environment version is valid except wasm case. + llvm::Triple Triple = TC.getTriple(); + if (!Triple.isWasm()) { + StringRef TripleVersionName = Triple.getEnvironmentVersionString(); + StringRef TripleObjectFormat = + Triple.getObjectFormatTypeName(Triple.getObjectFormat()); + if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" && + TripleVersionName != TripleObjectFormat) { + Diags.Report(diag::err_drv_triple_version_invalid) + << TripleVersionName << TC.getTripleString(); + ContainsError = true; + } + } + + // Report warning when arm64EC option is overridden by specified target + if ((TC.getTriple().getArch() != llvm::Triple::aarch64 || + TC.getTriple().getSubArch() != llvm::Triple::AArch64SubArch_arm64ec) && + UArgs->hasArg(options::OPT__SLASH_arm64EC)) { + getDiags().Report(clang::diag::warn_target_override_arm64ec) + << TC.getTriple().str(); + } + + // A common user mistake is specifying a target of aarch64-none-eabi or + // arm-none-elf whereas the correct names are aarch64-none-elf & + // arm-none-eabi. Detect these cases and issue a warning. + if (TC.getTriple().getOS() == llvm::Triple::UnknownOS && + TC.getTriple().getVendor() == llvm::Triple::UnknownVendor) { + switch (TC.getTriple().getArch()) { + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + if (TC.getTriple().getEnvironmentName() == "elf") { + Diag(diag::warn_target_unrecognized_env) + << TargetTriple + << (TC.getTriple().getArchName().str() + "-none-eabi"); + } + break; + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + case llvm::Triple::aarch64_32: + if (TC.getTriple().getEnvironmentName().starts_with("eabi")) { + Diag(diag::warn_target_unrecognized_env) + << TargetTriple + << (TC.getTriple().getArchName().str() + "-none-elf"); + } + break; + default: + break; + } + } + // The compilation takes ownership of Args. Compilation *C = new Compilation(*this, TC, UArgs.release(), TranslatedArgs, ContainsError); @@ -1228,8 +1550,14 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { static void printArgList(raw_ostream &OS, const llvm::opt::ArgList &Args) { llvm::opt::ArgStringList ASL; - for (const auto *A : Args) + for (const auto *A : Args) { + // Use user's original spelling of flags. For example, use + // `/source-charset:utf-8` instead of `-finput-charset=utf-8` if the user + // wrote the former. + while (A->getAlias()) + A = A->getAlias(); A->render(Args, ASL); + } for (auto I = ASL.begin(), E = ASL.end(); I != E; ++I) { if (I != ASL.begin()) @@ -1249,7 +1577,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, // (or /Library/Logs/DiagnosticReports for root) and has the filename pattern // clang-<VERSION>_<YYYY-MM-DD-HHMMSS>_<hostname>.crash. path::home_directory(CrashDiagDir); - if (CrashDiagDir.startswith("/var/root")) + if (CrashDiagDir.starts_with("/var/root")) CrashDiagDir = "/"; path::append(CrashDiagDir, "Library/Logs/DiagnosticReports"); int PID = @@ -1267,7 +1595,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, for (fs::directory_iterator File(CrashDiagDir, EC), FileEnd; File != FileEnd && !EC; File.increment(EC)) { StringRef FileName = path::filename(File->path()); - if (!FileName.startswith(Name)) + if (!FileName.starts_with(Name)) continue; if (fs::status(File->path(), FileStatus)) continue; @@ -1278,7 +1606,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, // The first line should start with "Process:", otherwise this isn't a real // .crash file. StringRef Data = CrashFile.get()->getBuffer(); - if (!Data.startswith("Process:")) + if (!Data.starts_with("Process:")) continue; // Parse parent process pid line, e.g: "Parent Process: clang-4.0 [79141]" size_t ParentProcPos = Data.find("Parent Process:"); @@ -1328,6 +1656,11 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, return false; } +static const char BugReporMsg[] = + "\n********************\n\n" + "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" + "Preprocessed source(s) and associated run script(s) are located at:"; + // When clang crashes, produce diagnostic information including the fully // preprocessed source file(s). Request that the developer attach the // diagnostic information to a bug report. @@ -1337,16 +1670,40 @@ void Driver::generateCompilationDiagnostics( if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics)) return; - // Don't try to generate diagnostics for link or dsymutil jobs. - if (FailingCommand.getCreator().isLinkJob() || - FailingCommand.getCreator().isDsymutilJob()) + unsigned Level = 1; + if (Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_EQ)) { + Level = llvm::StringSwitch<unsigned>(A->getValue()) + .Case("off", 0) + .Case("compiler", 1) + .Case("all", 2) + .Default(1); + } + if (!Level) + return; + + // Don't try to generate diagnostics for dsymutil jobs. + if (FailingCommand.getCreator().isDsymutilJob()) return; + bool IsLLD = false; + ArgStringList SavedTemps; + if (FailingCommand.getCreator().isLinkJob()) { + C.getDefaultToolChain().GetLinkerPath(&IsLLD); + if (!IsLLD || Level < 2) + return; + + // If lld crashed, we will re-run the same command with the input it used + // to have. In that case we should not remove temp files in + // initCompilationForDiagnostics yet. They will be added back and removed + // later. + SavedTemps = std::move(C.getTempFiles()); + assert(!C.getTempFiles().size()); + } + // Print the version of the compiler. PrintVersion(C, llvm::errs()); // Suppress driver output and emit preprocessor output to temp file. - Mode = CPPMode; CCGenDiagnostics = true; // Save the original job command(s). @@ -1359,6 +1716,29 @@ void Driver::generateCompilationDiagnostics( // Suppress tool output. C.initCompilationForDiagnostics(); + // If lld failed, rerun it again with --reproduce. + if (IsLLD) { + const char *TmpName = CreateTempFile(C, "linker-crash", "tar"); + Command NewLLDInvocation = Cmd; + llvm::opt::ArgStringList ArgList = NewLLDInvocation.getArguments(); + StringRef ReproduceOption = + C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() + ? "/reproduce:" + : "--reproduce="; + ArgList.push_back(Saver.save(Twine(ReproduceOption) + TmpName).data()); + NewLLDInvocation.replaceArguments(std::move(ArgList)); + + // Redirect stdout/stderr to /dev/null. + NewLLDInvocation.Execute({std::nullopt, {""}, {""}}, nullptr, nullptr); + Diag(clang::diag::note_drv_command_failed_diag_msg) << BugReporMsg; + Diag(clang::diag::note_drv_command_failed_diag_msg) << TmpName; + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "\n\n********************"; + if (Report) + Report->TemporaryFiles.push_back(TmpName); + return; + } + // Construct the list of inputs. InputList Inputs; BuildInputs(C.getDefaultToolChain(), C.getArgs(), Inputs); @@ -1443,10 +1823,7 @@ void Driver::generateCompilationDiagnostics( return; } - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "\n********************\n\n" - "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" - "Preprocessed source(s) and associated run script(s) are located at:"; + Diag(clang::diag::note_drv_command_failed_diag_msg) << BugReporMsg; SmallString<128> VFS; SmallString<128> ReproCrashFilename; @@ -1458,7 +1835,7 @@ void Driver::generateCompilationDiagnostics( ReproCrashFilename = TempFile; llvm::sys::path::replace_extension(ReproCrashFilename, ".crash"); } - if (StringRef(TempFile).endswith(".cache")) { + if (StringRef(TempFile).ends_with(".cache")) { // In some cases (modules) we'll dump extra data to help with reproducing // the crash into a directory next to the output. VFS = llvm::sys::path::filename(TempFile); @@ -1466,6 +1843,9 @@ void Driver::generateCompilationDiagnostics( } } + for (const char *TempFile : SavedTemps) + C.addTempFile(TempFile); + // Assume associated files are based off of the first temporary file. CrashReportInfo CrashInfo(TempFiles[0], VFS); @@ -1489,7 +1869,7 @@ void Driver::generateCompilationDiagnostics( ScriptOS << "\n# Additional information: " << AdditionalInformation << "\n"; if (Report) - Report->TemporaryFiles.push_back(std::string(Script.str())); + Report->TemporaryFiles.push_back(std::string(Script)); Diag(clang::diag::note_drv_command_failed_diag_msg) << Script; } @@ -1511,9 +1891,6 @@ void Driver::generateCompilationDiagnostics( } } - for (const auto &A : C.getArgs().filtered(options::OPT_frewrite_map_file_EQ)) - Diag(clang::diag::note_drv_command_failed_diag_msg) << A->getValue(); - Diag(clang::diag::note_drv_command_failed_diag_msg) << "\n\n********************"; } @@ -1536,17 +1913,30 @@ void Driver::setUpResponseFiles(Compilation &C, Command &Cmd) { int Driver::ExecuteCompilation( Compilation &C, SmallVectorImpl<std::pair<int, const Command *>> &FailingCommands) { + if (C.getArgs().hasArg(options::OPT_fdriver_only)) { + if (C.getArgs().hasArg(options::OPT_v)) + C.getJobs().Print(llvm::errs(), "\n", true); + + C.ExecuteJobs(C.getJobs(), FailingCommands, /*LogOnly=*/true); + + // If there were errors building the compilation, quit now. + if (!FailingCommands.empty() || Diags.hasErrorOccurred()) + return 1; + + return 0; + } + // Just print if -### was present. if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) { C.getJobs().Print(llvm::errs(), "\n", true); - return 0; + return Diags.hasErrorOccurred() ? 1 : 0; } // If there were errors building the compilation, quit now. if (Diags.hasErrorOccurred()) return 1; - // Set up response file names for each command, if necessary + // Set up response file names for each command, if necessary. for (auto &Job : C.getJobs()) setUpResponseFiles(C, Job); @@ -1573,14 +1963,12 @@ int Driver::ExecuteCompilation( C.CleanupFileMap(C.getFailureResultFiles(), JA, true); } -#if LLVM_ON_UNIX - // llvm/lib/Support/Unix/Signals.inc will exit with a special return code + // llvm/lib/Support/*/Signals.inc will exit with a special return code // for SIGPIPE. Do not print diagnostics for this case. if (CommandRes == EX_IOERR) { Res = CommandRes; continue; } -#endif // Print extra information about abnormal failures, if possible. // @@ -1605,24 +1993,12 @@ int Driver::ExecuteCompilation( } void Driver::PrintHelp(bool ShowHidden) const { - unsigned IncludedFlagsBitmask; - unsigned ExcludedFlagsBitmask; - std::tie(IncludedFlagsBitmask, ExcludedFlagsBitmask) = - getIncludeExcludeOptionFlagMasks(IsCLMode()); - - ExcludedFlagsBitmask |= options::NoDriverOption; - if (!ShowHidden) - ExcludedFlagsBitmask |= HelpHidden; - - if (IsFlangMode()) - IncludedFlagsBitmask |= options::FlangOption; - else - ExcludedFlagsBitmask |= options::FlangOnlyOption; + llvm::opt::Visibility VisibilityMask = getOptionVisibilityMask(); std::string Usage = llvm::formatv("{0} [options] file...", Name).str(); getOpts().printHelp(llvm::outs(), Usage.c_str(), DriverTitle.c_str(), - IncludedFlagsBitmask, ExcludedFlagsBitmask, - /*ShowAllAliases=*/false); + ShowHidden, /*ShowAllAliases=*/false, + VisibilityMask); } void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const { @@ -1646,10 +2022,16 @@ void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const { OS << '\n'; // Print out the install directory. - OS << "InstalledDir: " << InstalledDir << '\n'; + OS << "InstalledDir: " << Dir << '\n'; - // If configuration file was used, print its path. - if (!ConfigFile.empty()) + // Print the build config if it's non-default. + // Intended to help LLVM developers understand the configs of compilers + // they're investigating. + if (!llvm::cl::getCompilerBuildConfig().empty()) + llvm::cl::printBuildConfig(OS); + + // If configuration files were used, print their paths. + for (auto ConfigFile : ConfigFiles) OS << "Configuration file: " << ConfigFile << '\n'; } @@ -1670,18 +2052,17 @@ void Driver::HandleAutocompletions(StringRef PassedFlags) const { std::vector<std::string> SuggestedCompletions; std::vector<std::string> Flags; - unsigned int DisableFlags = - options::NoDriverOption | options::Unsupported | options::Ignored; + llvm::opt::Visibility VisibilityMask(options::ClangOption); // Make sure that Flang-only options don't pollute the Clang output // TODO: Make sure that Clang-only options don't pollute Flang output - if (!IsFlangMode()) - DisableFlags |= options::FlangOnlyOption; + if (IsFlangMode()) + VisibilityMask = llvm::opt::Visibility(options::FlangOption); // Distinguish "--autocomplete=-someflag" and "--autocomplete=-someflag," // because the latter indicates that the user put space before pushing tab // which should end up in a file completion. - const bool HasSpace = PassedFlags.endswith(","); + const bool HasSpace = PassedFlags.ends_with(","); // Parse PassedFlags by "," as all the command-line flags are passed to this // function separated by "," @@ -1695,7 +2076,7 @@ void Driver::HandleAutocompletions(StringRef PassedFlags) const { // We want to show cc1-only options only when clang is invoked with -cc1 or // -Xclang. if (llvm::is_contained(Flags, "-Xclang") || llvm::is_contained(Flags, "-cc1")) - DisableFlags &= ~options::NoDriverOption; + VisibilityMask = llvm::opt::Visibility(options::CC1Option); const llvm::opt::OptTable &Opts = getOpts(); StringRef Cur; @@ -1721,17 +2102,19 @@ void Driver::HandleAutocompletions(StringRef PassedFlags) const { // When flag ends with '=' and there was no value completion, return empty // string and fall back to the file autocompletion. - if (SuggestedCompletions.empty() && !Cur.endswith("=")) { + if (SuggestedCompletions.empty() && !Cur.ends_with("=")) { // If the flag is in the form of "--autocomplete=-foo", // we were requested to print out all option names that start with "-foo". // For example, "--autocomplete=-fsyn" is expanded to "-fsyntax-only". - SuggestedCompletions = Opts.findByPrefix(Cur, DisableFlags); + SuggestedCompletions = Opts.findByPrefix( + Cur, VisibilityMask, + /*DisableFlags=*/options::Unsupported | options::Ignored); // We have to query the -W flags manually as they're not in the OptTable. // TODO: Find a good way to add them to OptTable instead and them remove // this code. for (StringRef S : DiagnosticIDs::getDiagnosticFlags()) - if (S.startswith(Cur)) + if (S.starts_with(Cur)) SuggestedCompletions.push_back(std::string(S)); } @@ -1748,7 +2131,7 @@ void Driver::HandleAutocompletions(StringRef PassedFlags) const { llvm::outs() << llvm::join(SuggestedCompletions, "\n") << '\n'; } -bool Driver::HandleImmediateArgs(const Compilation &C) { +bool Driver::HandleImmediateArgs(Compilation &C) { // The order these options are handled in gcc is all over the place, but we // don't expect inconsistencies w.r.t. that to matter in practice. @@ -1783,7 +2166,9 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { 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) || + C.getArgs().hasArg(options::OPT_print_enabled_extensions)) { PrintVersion(C, llvm::errs()); SuppressMissingInputWarning = true; } @@ -1841,15 +2226,27 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { return false; } + if (C.getArgs().hasArg(options::OPT_print_std_module_manifest_path)) { + llvm::outs() << GetStdModuleManifestPath(C, C.getDefaultToolChain()) + << '\n'; + return false; + } + if (C.getArgs().hasArg(options::OPT_print_runtime_dir)) { - std::string CandidateRuntimePath = TC.getRuntimePath(); - if (getVFS().exists(CandidateRuntimePath)) - llvm::outs() << CandidateRuntimePath << '\n'; + if (std::optional<std::string> RuntimePath = TC.getRuntimePath()) + llvm::outs() << *RuntimePath << '\n'; else llvm::outs() << TC.getCompilerRTPath() << '\n'; return false; } + if (C.getArgs().hasArg(options::OPT_print_diagnostic_options)) { + std::vector<std::string> Flags = DiagnosticIDs::getDiagnosticFlags(); + for (std::size_t I = 0; I != Flags.size(); I += 2) + llvm::outs() << " " << Flags[I] << "\n " << Flags[I + 1] << "\n\n"; + return false; + } + // FIXME: The following handlers should use a callback mechanism, we don't // know what the client would like to do. if (Arg *A = C.getArgs().getLastArg(options::OPT_print_file_name_EQ)) { @@ -1877,6 +2274,14 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { if (C.getArgs().hasArg(options::OPT_print_libgcc_file_name)) { ToolChain::RuntimeLibType RLT = TC.GetRuntimeLibType(C.getArgs()); const llvm::Triple Triple(TC.ComputeEffectiveClangTriple(C.getArgs())); + // The 'Darwin' toolchain is initialized only when its arguments are + // computed. Get the default arguments for OFK_None to ensure that + // initialization is performed before trying to access properties of + // the toolchain in the functions below. + // FIXME: Remove when darwin's toolchain is initialized during construction. + // FIXME: For some more esoteric targets the default toolchain is not the + // correct one. + C.getArgsForToolChain(&TC, Triple.getArchName(), Action::OFK_None); RegisterEffectiveTriple TripleRAII(TC, Triple); switch (RLT) { case ToolChain::RLT_CompilerRT: @@ -1895,14 +2300,26 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { return false; } + if (C.getArgs().hasArg(options::OPT_print_multi_flags)) { + Multilib::flags_list ArgFlags = TC.getMultilibFlags(C.getArgs()); + llvm::StringSet<> ExpandedFlags = TC.getMultilibs().expandFlags(ArgFlags); + std::set<llvm::StringRef> SortedFlags; + for (const auto &FlagEntry : ExpandedFlags) + SortedFlags.insert(FlagEntry.getKey()); + for (auto Flag : SortedFlags) + llvm::outs() << Flag << '\n'; + return false; + } + if (C.getArgs().hasArg(options::OPT_print_multi_directory)) { - const Multilib &Multilib = TC.getMultilib(); - if (Multilib.gccSuffix().empty()) - llvm::outs() << ".\n"; - else { - StringRef Suffix(Multilib.gccSuffix()); - assert(Suffix.front() == '/'); - llvm::outs() << Suffix.substr(1) << "\n"; + for (const Multilib &Multilib : TC.getSelectedMultilibs()) { + if (Multilib.gccSuffix().empty()) + llvm::outs() << ".\n"; + else { + StringRef Suffix(Multilib.gccSuffix()); + assert(Suffix.front() == '/'); + llvm::outs() << Suffix.substr(1) << "\n"; + } } return false; } @@ -1918,12 +2335,6 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { return false; } - if (C.getArgs().hasArg(options::OPT_print_multiarch)) { - llvm::outs() << TC.getMultiarchTriple(*this, TC.getTriple(), SysRoot) - << "\n"; - return false; - } - if (C.getArgs().hasArg(options::OPT_print_targets)) { llvm::TargetRegistry::printRegisteredTargetsForVersion(llvm::outs()); return false; @@ -2041,11 +2452,7 @@ static bool ContainsCompileOrAssembleAction(const Action *A) { isa<AssembleJobAction>(A)) return true; - for (const Action *Input : A->inputs()) - if (ContainsCompileOrAssembleAction(Input)) - return true; - - return false; + return llvm::any_of(A->inputs(), ContainsCompileOrAssembleAction); } void Driver::BuildUniversalActions(Compilation &C, const ToolChain &TC, @@ -2145,21 +2552,16 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value, if (Value == "-") return true; - if (getVFS().exists(Value)) + // If it's a header to be found in the system or user search path, then defer + // complaints about its absence until those searches can be done. When we + // are definitely processing headers for C++20 header units, extend this to + // allow the user to put "-fmodule-header -xc++-header vector" for example. + if (Ty == types::TY_CXXSHeader || Ty == types::TY_CXXUHeader || + (ModulesModeCXX20 && Ty == types::TY_CXXHeader)) return true; - if (IsCLMode()) { - if (!llvm::sys::path::is_absolute(Twine(Value)) && - llvm::sys::Process::FindInEnvPath("LIB", Value, ';')) - return true; - - if (Args.hasArg(options::OPT__SLASH_link) && Ty == types::TY_Object) { - // Arguments to the /link flag might cause the linker to search for object - // and library files in paths we don't know about. Don't error in such - // cases. - return true; - } - } + if (getVFS().exists(Value)) + return true; if (TypoCorrect) { // Check if the filename is a typo for an option flag. OptTable thinks @@ -2167,23 +2569,70 @@ bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value, // filenames, but e.g. `/diagnostic:caret` is more likely a typo for // the option `/diagnostics:caret` than a reference to a file in the root // directory. - unsigned IncludedFlagsBitmask; - unsigned ExcludedFlagsBitmask; - std::tie(IncludedFlagsBitmask, ExcludedFlagsBitmask) = - getIncludeExcludeOptionFlagMasks(IsCLMode()); std::string Nearest; - if (getOpts().findNearest(Value, Nearest, IncludedFlagsBitmask, - ExcludedFlagsBitmask) <= 1) { + if (getOpts().findNearest(Value, Nearest, getOptionVisibilityMask()) <= 1) { Diag(clang::diag::err_drv_no_such_file_with_suggestion) << Value << Nearest; return false; } } + // In CL mode, don't error on apparently non-existent linker inputs, because + // they can be influenced by linker flags the clang driver might not + // understand. + // Examples: + // - `clang-cl main.cc ole32.lib` in a non-MSVC shell will make the driver + // module look for an MSVC installation in the registry. (We could ask + // the MSVCToolChain object if it can find `ole32.lib`, but the logic to + // look in the registry might move into lld-link in the future so that + // lld-link invocations in non-MSVC shells just work too.) + // - `clang-cl ... /link ...` can pass arbitrary flags to the linker, + // including /libpath:, which is used to find .lib and .obj files. + // So do not diagnose this on the driver level. Rely on the linker diagnosing + // it. (If we don't end up invoking the linker, this means we'll emit a + // "'linker' input unused [-Wunused-command-line-argument]" warning instead + // of an error.) + // + // Only do this skip after the typo correction step above. `/Brepo` is treated + // as TY_Object, but it's clearly a typo for `/Brepro`. It seems fine to emit + // an error if we have a flag that's within an edit distance of 1 from a + // flag. (Users can use `-Wl,` or `/linker` to launder the flag past the + // driver in the unlikely case they run into this.) + // + // Don't do this for inputs that start with a '/', else we'd pass options + // like /libpath: through to the linker silently. + // + // Emitting an error for linker inputs can also cause incorrect diagnostics + // with the gcc driver. The command + // clang -fuse-ld=lld -Wl,--chroot,some/dir /file.o + // will make lld look for some/dir/file.o, while we will diagnose here that + // `/file.o` does not exist. However, configure scripts check if + // `clang /GR-` compiles without error to see if the compiler is cl.exe, + // so we can't downgrade diagnostics for `/GR-` from an error to a warning + // in cc mode. (We can in cl mode because cl.exe itself only warns on + // unknown flags.) + if (IsCLMode() && Ty == types::TY_Object && !Value.starts_with("/")) + return true; + Diag(clang::diag::err_drv_no_such_file) << Value; return false; } +// Get the C++20 Header Unit type corresponding to the input type. +static types::ID CXXHeaderUnitType(ModuleHeaderMode HM) { + switch (HM) { + case HeaderMode_User: + return types::TY_CXXUHeader; + case HeaderMode_System: + return types::TY_CXXSHeader; + case HeaderMode_Default: + break; + case HeaderMode_None: + llvm_unreachable("should not be called in this case"); + } + return types::TY_CXXHUHeader; +} + // Construct a the list of inputs and their types. void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, InputList &Inputs) const { @@ -2207,17 +2656,23 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, for (Arg *A : Args.filtered(options::OPT__SLASH_TC, options::OPT__SLASH_TP)) { if (Previous) { - Diag(clang::diag::warn_drv_overriding_flag_option) - << Previous->getSpelling() << A->getSpelling(); + Diag(clang::diag::warn_drv_overriding_option) + << Previous->getSpelling() << A->getSpelling(); ShowNote = true; } Previous = A; } if (ShowNote) Diag(clang::diag::note_drv_t_option_is_global); + } - // No driver mode exposes -x and /TC or /TP; we don't support mixing them. - assert(!Args.hasArg(options::OPT_x) && "-x and /TC or /TP is not allowed"); + // Warn -x after last input file has no effect + { + Arg *LastXArg = Args.getLastArgNoClaim(options::OPT_x); + Arg *LastInputArg = Args.getLastArgNoClaim(options::OPT_INPUT); + if (LastXArg && LastInputArg && + LastInputArg->getIndex() < LastXArg->getIndex()) + Diag(clang::diag::warn_drv_unused_x) << LastXArg->getValue(); } for (Arg *A : Args) { @@ -2235,6 +2690,8 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, if (memcmp(Value, "-", 2) == 0) { if (IsFlangMode()) { Ty = types::TY_Fortran; + } else if (IsDXCMode()) { + Ty = types::TY_HLSL; } else { // If running with -E, treat as a C input (this changes the // builtin macros, for example). This may be overridden by -ObjC @@ -2242,6 +2699,7 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, // // Otherwise emit an error but still use a valid type to avoid // spurious errors (e.g., no inputs). + assert(!CCGenDiagnostics && "stdin produces no crash reproducer"); if (!Args.hasArgNoClaim(options::OPT_E) && !CCCIsCPP()) Diag(IsCLMode() ? clang::diag::err_drv_unknown_stdin_type_clang_cl : clang::diag::err_drv_unknown_stdin_type); @@ -2257,10 +2715,10 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, Ty = TC.LookupTypeForExtension(Ext + 1); if (Ty == types::TY_INVALID) { - if (CCCIsCPP()) - Ty = types::TY_C; - else if (IsCLMode() && Args.hasArgNoClaim(options::OPT_E)) + if (IsCLMode() && (Args.hasArgNoClaim(options::OPT_E) || CCGenDiagnostics)) Ty = types::TY_CXX; + else if (CCCIsCPP() || CCGenDiagnostics) + Ty = types::TY_C; else Ty = types::TY_Object; } @@ -2271,7 +2729,9 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, types::ID OldTy = Ty; Ty = types::lookupCXXTypeForCType(Ty); - if (Ty != OldTy) + // Do not complain about foo.h, when we are known to be processing + // it as a C++20 header unit. + if (Ty != OldTy && !(OldTy == types::TY_CHeader && hasHeaderMode())) Diag(clang::diag::warn_drv_treating_input_as_cxx) << getTypeName(OldTy) << getTypeName(Ty); } @@ -2294,6 +2754,14 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, else if (Args.hasArg(options::OPT_ObjCXX)) Ty = types::TY_ObjCXX; } + + // Disambiguate headers that are meant to be header units from those + // intended to be PCH. Avoid missing '.h' cases that are counted as + // C headers by default - we know we are in C++ mode and we do not + // want to issue a complaint about compiling things in the wrong mode. + if ((Ty == types::TY_CXXHeader || Ty == types::TY_CHeader) && + hasHeaderMode()) + Ty = CXXHeaderUnitType(CXX20HeaderType); } else { assert(InputTypeArg && "InputType set w/o InputTypeArg"); if (!InputTypeArg->getOption().matches(options::OPT_x)) { @@ -2309,6 +2777,10 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, } } + if ((Ty == types::TY_C || Ty == types::TY_CXX) && + Args.hasArgNoClaim(options::OPT_hipstdpar)) + Ty = types::TY_HIP; + if (DiagnoseInputExistence(Args, Value, Ty, /*TypoCorrect=*/true)) Inputs.push_back(std::make_pair(Ty, A)); @@ -2345,6 +2817,11 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, Diag(clang::diag::err_drv_unknown_language) << A->getValue(); InputType = types::TY_Object; } + + // If the user has put -fmodule-header{,=} then we treat C++ headers as + // header unit inputs. So we 'promote' -xc++-header appropriately. + if (InputType == types::TY_CXXHeader && hasHeaderMode()) + InputType = CXXHeaderUnitType(CXX20HeaderType); } else if (A->getOption().getID() == options::OPT_U) { assert(A->getNumValues() == 1 && "The /U option has one value."); StringRef Val = A->getValue(0); @@ -2376,6 +2853,9 @@ class OffloadingActionBuilder final { /// Map between an input argument and the offload kinds used to process it. std::map<const Arg *, unsigned> InputArgToOffloadKindMap; + /// Map between a host action and its originating input argument. + std::map<Action *, const Arg *> HostActionToInputArgMap; + /// Builder interface. It doesn't build anything or keep any state. class DeviceActionBuilder { public: @@ -2428,7 +2908,7 @@ class OffloadingActionBuilder final { /// Update the state to include the provided host action \a HostAction as a /// dependency of the current device action. By default it is inactive. - virtual ActionBuilderReturnCode addDeviceDepences(Action *HostAction) { + virtual ActionBuilderReturnCode addDeviceDependences(Action *HostAction) { return ABRT_Inactive; } @@ -2479,7 +2959,7 @@ class OffloadingActionBuilder final { struct TargetID { /// Target ID string which is persistent throughout the compilation. const char *ID; - TargetID(CudaArch Arch) { ID = CudaArchToString(Arch); } + TargetID(OffloadArch Arch) { ID = OffloadArchToString(Arch); } TargetID(const char *ID) : ID(ID) {} operator const char *() { return ID; } operator StringRef() { return StringRef(ID); } @@ -2500,7 +2980,7 @@ class OffloadingActionBuilder final { bool Relocatable = false; /// Default GPU architecture if there's no one specified. - CudaArch DefaultCudaArch = CudaArch::UNKNOWN; + OffloadArch DefaultOffloadArch = OffloadArch::UNKNOWN; /// Method to generate compilation unit ID specified by option /// '-fuse-cuid='. @@ -2514,9 +2994,14 @@ class OffloadingActionBuilder final { CudaActionBuilderBase(Compilation &C, DerivedArgList &Args, const Driver::InputList &Inputs, Action::OffloadKind OFKind) - : DeviceActionBuilder(C, Args, Inputs, OFKind) {} + : DeviceActionBuilder(C, Args, Inputs, OFKind) { + + CompileDeviceOnly = C.getDriver().offloadDeviceOnly(); + Relocatable = Args.hasFlag(options::OPT_fgpu_rdc, + options::OPT_fno_gpu_rdc, /*Default=*/false); + } - ActionBuilderReturnCode addDeviceDepences(Action *HostAction) override { + ActionBuilderReturnCode addDeviceDependences(Action *HostAction) override { // While generating code for CUDA, we only depend on the host input action // to trigger the creation of all the CUDA device actions. @@ -2590,12 +3075,16 @@ class OffloadingActionBuilder final { std::string FileName = IA->getInputArg().getAsString(Args); // Check if the type of the file is the same as the action. Do not // unbundle it if it is not. Do not unbundle .so files, for example, - // which are not object files. + // which are not object files. Files with extension ".lib" is classified + // as TY_Object but they are actually archives, therefore should not be + // unbundled here as objects. They will be handled at other places. + const StringRef LibFileExt = ".lib"; if (IA->getType() == types::TY_Object && (!llvm::sys::path::has_extension(FileName) || types::lookupTypeForExtension( llvm::sys::path::extension(FileName).drop_front()) != - types::TY_Object)) + types::TY_Object || + llvm::sys::path::extension(FileName) == LibFileExt)) return ABRT_Inactive; for (auto Arch : GpuArchList) { @@ -2603,6 +3092,7 @@ class OffloadingActionBuilder final { UA->registerDependentActionInfo(ToolChains[0], Arch, AssociatedOffloadKind); } + IsActive = true; return ABRT_Success; } @@ -2619,7 +3109,7 @@ class OffloadingActionBuilder final { // If we have a fat binary, add it to the list. if (CudaFatBinary) { - AddTopLevel(CudaFatBinary, CudaArch::UNUSED); + AddTopLevel(CudaFatBinary, OffloadArch::UNUSED); CudaDeviceActions.clear(); CudaFatBinary = nullptr; return; @@ -2634,7 +3124,7 @@ class OffloadingActionBuilder final { assert(CudaDeviceActions.size() == GpuArchList.size() && "Expecting one action per GPU architecture."); assert(ToolChains.size() == 1 && - "Expecting to have a sing CUDA toolchain."); + "Expecting to have a single CUDA toolchain."); for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) AddTopLevel(CudaDeviceActions[I], GpuArchList[I]); @@ -2645,7 +3135,7 @@ class OffloadingActionBuilder final { /// option is invalid. virtual StringRef getCanonicalOffloadArch(StringRef Arch) = 0; - virtual llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>> + virtual std::optional<std::pair<llvm::StringRef, llvm::StringRef>> getConflictOffloadArchCombination(const std::set<StringRef> &GpuArchs) = 0; bool initialize() override { @@ -2662,9 +3152,6 @@ class OffloadingActionBuilder final { !C.hasOffloadToolChain<Action::OFK_HIP>()) return false; - Relocatable = Args.hasFlag(options::OPT_fgpu_rdc, - options::OPT_fno_gpu_rdc, /*Default=*/false); - const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>(); assert(HostTC && "No toolchain for host compilation."); if (HostTC->getTriple().isNVPTX() || @@ -2682,15 +3169,7 @@ class OffloadingActionBuilder final { ? C.getSingleOffloadToolChain<Action::OFK_Cuda>() : C.getSingleOffloadToolChain<Action::OFK_HIP>()); - Arg *PartialCompilationArg = Args.getLastArg( - options::OPT_cuda_host_only, options::OPT_cuda_device_only, - options::OPT_cuda_compile_host_device); - CompileHostOnly = PartialCompilationArg && - PartialCompilationArg->getOption().matches( - options::OPT_cuda_host_only); - CompileDeviceOnly = PartialCompilationArg && - PartialCompilationArg->getOption().matches( - options::OPT_cuda_device_only); + CompileHostOnly = C.getDriver().offloadHostOnly(); EmitLLVM = Args.getLastArg(options::OPT_emit_llvm); EmitAsm = Args.getLastArg(options::OPT_S); FixedCUID = Args.getLastArgValue(options::OPT_cuid_EQ); @@ -2709,7 +3188,15 @@ class OffloadingActionBuilder final { } } - // Collect all cuda_gpu_arch parameters, removing duplicates. + // --offload and --offload-arch options are mutually exclusive. + if (Args.hasArgNoClaim(options::OPT_offload_EQ) && + Args.hasArgNoClaim(options::OPT_offload_arch_EQ, + options::OPT_no_offload_arch_EQ)) { + C.getDriver().Diag(diag::err_opt_not_valid_with_opt) << "--offload-arch" + << "--offload"; + } + + // Collect all offload arch parameters, removing duplicates. std::set<StringRef> GpuArchs; bool Error = false; for (Arg *A : Args) { @@ -2718,28 +3205,41 @@ class OffloadingActionBuilder final { continue; A->claim(); - StringRef ArchStr = A->getValue(); - if (A->getOption().matches(options::OPT_no_offload_arch_EQ) && - ArchStr == "all") { - GpuArchs.clear(); - continue; + for (StringRef ArchStr : llvm::split(A->getValue(), ",")) { + if (A->getOption().matches(options::OPT_no_offload_arch_EQ) && + ArchStr == "all") { + GpuArchs.clear(); + } else if (ArchStr == "native") { + const ToolChain &TC = *ToolChains.front(); + auto GPUsOrErr = ToolChains.front()->getSystemGPUArchs(Args); + if (!GPUsOrErr) { + TC.getDriver().Diag(diag::err_drv_undetermined_gpu_arch) + << llvm::Triple::getArchTypeName(TC.getArch()) + << llvm::toString(GPUsOrErr.takeError()) << "--offload-arch"; + continue; + } + + for (auto GPU : *GPUsOrErr) { + GpuArchs.insert(Args.MakeArgString(GPU)); + } + } else { + ArchStr = getCanonicalOffloadArch(ArchStr); + if (ArchStr.empty()) { + Error = true; + } else if (A->getOption().matches(options::OPT_offload_arch_EQ)) + GpuArchs.insert(ArchStr); + else if (A->getOption().matches(options::OPT_no_offload_arch_EQ)) + GpuArchs.erase(ArchStr); + else + llvm_unreachable("Unexpected option."); + } } - ArchStr = getCanonicalOffloadArch(ArchStr); - if (ArchStr.empty()) { - Error = true; - } else if (A->getOption().matches(options::OPT_offload_arch_EQ)) - GpuArchs.insert(ArchStr); - else if (A->getOption().matches(options::OPT_no_offload_arch_EQ)) - GpuArchs.erase(ArchStr); - else - llvm_unreachable("Unexpected option."); } auto &&ConflictingArchs = getConflictOffloadArchCombination(GpuArchs); if (ConflictingArchs) { C.getDriver().Diag(clang::diag::err_drv_bad_offload_arch_combo) - << ConflictingArchs.getValue().first - << ConflictingArchs.getValue().second; + << ConflictingArchs->first << ConflictingArchs->second; C.setContainsError(); return true; } @@ -2751,8 +3251,16 @@ class OffloadingActionBuilder final { // Default to sm_20 which is the lowest common denominator for // supported GPUs. sm_20 code should work correctly, if // suboptimally, on all newer GPUs. - if (GpuArchList.empty()) - GpuArchList.push_back(DefaultCudaArch); + if (GpuArchList.empty()) { + if (ToolChains.front()->getTriple().isSPIRV()) { + if (ToolChains.front()->getTriple().getVendor() == llvm::Triple::AMD) + GpuArchList.push_back(OffloadArch::AMDGCNSPIRV); + else + GpuArchList.push_back(OffloadArch::Generic); + } else { + GpuArchList.push_back(DefaultOffloadArch); + } + } return Error; } @@ -2765,22 +3273,22 @@ class OffloadingActionBuilder final { CudaActionBuilder(Compilation &C, DerivedArgList &Args, const Driver::InputList &Inputs) : CudaActionBuilderBase(C, Args, Inputs, Action::OFK_Cuda) { - DefaultCudaArch = CudaArch::SM_20; + DefaultOffloadArch = OffloadArch::CudaDefault; } StringRef getCanonicalOffloadArch(StringRef ArchStr) override { - CudaArch Arch = StringToCudaArch(ArchStr); - if (Arch == CudaArch::UNKNOWN || !IsNVIDIAGpuArch(Arch)) { + OffloadArch Arch = StringToOffloadArch(ArchStr); + if (Arch == OffloadArch::UNKNOWN || !IsNVIDIAOffloadArch(Arch)) { C.getDriver().Diag(clang::diag::err_drv_cuda_bad_gpu_arch) << ArchStr; return StringRef(); } - return CudaArchToString(Arch); + return OffloadArchToString(Arch); } - llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>> + std::optional<std::pair<llvm::StringRef, llvm::StringRef>> getConflictOffloadArchCombination( const std::set<StringRef> &GpuArchs) override { - return llvm::None; + return std::nullopt; } ActionBuilderReturnCode @@ -2891,43 +3399,67 @@ class OffloadingActionBuilder final { class HIPActionBuilder final : public CudaActionBuilderBase { /// The linker inputs obtained for each device arch. SmallVector<ActionList, 8> DeviceLinkerInputs; - bool GPUSanitize; // The default bundling behavior depends on the type of output, therefore // BundleOutput needs to be tri-value: None, true, or false. // Bundle code objects except --no-gpu-output is specified for device // only compilation. Bundle other type of output files only if // --gpu-bundle-output is specified for device only compilation. - Optional<bool> BundleOutput; + std::optional<bool> BundleOutput; + std::optional<bool> EmitReloc; public: HIPActionBuilder(Compilation &C, DerivedArgList &Args, const Driver::InputList &Inputs) : CudaActionBuilderBase(C, Args, Inputs, Action::OFK_HIP) { - DefaultCudaArch = CudaArch::GFX803; - GPUSanitize = Args.hasFlag(options::OPT_fgpu_sanitize, - options::OPT_fno_gpu_sanitize, false); + + DefaultOffloadArch = OffloadArch::HIPDefault; + + if (Args.hasArg(options::OPT_fhip_emit_relocatable, + options::OPT_fno_hip_emit_relocatable)) { + EmitReloc = Args.hasFlag(options::OPT_fhip_emit_relocatable, + options::OPT_fno_hip_emit_relocatable, false); + + if (*EmitReloc) { + if (Relocatable) { + C.getDriver().Diag(diag::err_opt_not_valid_with_opt) + << "-fhip-emit-relocatable" + << "-fgpu-rdc"; + } + + if (!CompileDeviceOnly) { + C.getDriver().Diag(diag::err_opt_not_valid_without_opt) + << "-fhip-emit-relocatable" + << "--cuda-device-only"; + } + } + } + if (Args.hasArg(options::OPT_gpu_bundle_output, options::OPT_no_gpu_bundle_output)) BundleOutput = Args.hasFlag(options::OPT_gpu_bundle_output, - options::OPT_no_gpu_bundle_output); + options::OPT_no_gpu_bundle_output, true) && + (!EmitReloc || !*EmitReloc); } bool canUseBundlerUnbundler() const override { return true; } StringRef getCanonicalOffloadArch(StringRef IdStr) override { llvm::StringMap<bool> Features; - auto ArchStr = - parseTargetID(getHIPOffloadTargetTriple(), IdStr, &Features); + // getHIPOffloadTargetTriple() is known to return valid value as it has + // been called successfully in the CreateOffloadingDeviceToolChains(). + auto ArchStr = parseTargetID( + *getHIPOffloadTargetTriple(C.getDriver(), C.getInputArgs()), IdStr, + &Features); if (!ArchStr) { C.getDriver().Diag(clang::diag::err_drv_bad_target_id) << IdStr; C.setContainsError(); return StringRef(); } - auto CanId = getCanonicalTargetID(ArchStr.getValue(), Features); + auto CanId = getCanonicalTargetID(*ArchStr, Features); return Args.MakeArgStringRef(CanId); }; - llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>> + std::optional<std::pair<llvm::StringRef, llvm::StringRef>> getConflictOffloadArchCombination( const std::set<StringRef> &GpuArchs) override { return getConflictTargetIDCombination(GpuArchs); @@ -2937,9 +3469,12 @@ class OffloadingActionBuilder final { getDeviceDependences(OffloadAction::DeviceDependences &DA, phases::ID CurPhase, phases::ID FinalPhase, PhasesTy &Phases) override { + if (!IsActive) + return ABRT_Inactive; + // amdgcn does not support linking of object files, therefore we skip // backend and assemble phases to output LLVM IR. Except for generating - // non-relocatable device coee, where we generate fat binary for device + // non-relocatable device code, where we generate fat binary for device // code and pass to host in Backend phase. if (CudaDeviceActions.empty()) return ABRT_Success; @@ -2948,10 +3483,12 @@ class OffloadingActionBuilder final { CudaDeviceActions.size() == GpuArchList.size()) && "Expecting one action per GPU architecture."); assert(!CompileHostOnly && - "Not expecting CUDA actions in host-only compilation."); + "Not expecting HIP actions in host-only compilation."); + + bool ShouldLink = !EmitReloc || !*EmitReloc; if (!Relocatable && CurPhase == phases::Backend && !EmitLLVM && - !EmitAsm) { + !EmitAsm && ShouldLink) { // If we are in backend phase, we attempt to generate the fat binary. // We compile each arch to IR and use a link action to generate code // object containing ISA. Then we use a special "link" action to create @@ -2971,9 +3508,19 @@ class OffloadingActionBuilder final { // When LTO is not enabled, we follow the conventional // compiler phases, including backend and assemble phases. ActionList AL; - auto BackendAction = C.getDriver().ConstructPhaseAction( - C, Args, phases::Backend, CudaDeviceActions[I], - AssociatedOffloadKind); + Action *BackendAction = nullptr; + if (ToolChains.front()->getTriple().isSPIRV()) { + // Emit LLVM bitcode for SPIR-V targets. SPIR-V device tool chain + // (HIPSPVToolChain) runs post-link LLVM IR passes. + types::ID Output = Args.hasArg(options::OPT_S) + ? types::TY_LLVM_IR + : types::TY_LLVM_BC; + BackendAction = + C.MakeAction<BackendJobAction>(CudaDeviceActions[I], Output); + } else + BackendAction = C.getDriver().ConstructPhaseAction( + C, Args, phases::Backend, CudaDeviceActions[I], + AssociatedOffloadKind); auto AssembleAction = C.getDriver().ConstructPhaseAction( C, Args, phases::Assemble, BackendAction, AssociatedOffloadKind); @@ -2997,8 +3544,7 @@ class OffloadingActionBuilder final { DDep, CudaDeviceActions[I]->getType()); } - if (!CompileDeviceOnly || !BundleOutput.hasValue() || - BundleOutput.getValue()) { + if (!CompileDeviceOnly || !BundleOutput || *BundleOutput) { // Create HIP fat binary with a special "link" action. CudaFatBinary = C.MakeAction<LinkJobAction>(CudaDeviceActions, types::TY_HIP_FATBIN); @@ -3018,6 +3564,8 @@ class OffloadingActionBuilder final { return CompileDeviceOnly ? ABRT_Ignore_Host : ABRT_Success; } else if (CurPhase == phases::Link) { + if (!ShouldLink) + return ABRT_Success; // Save CudaDeviceActions to DeviceLinkerInputs for each GPU subarch. // This happens to each device action originated from each input file. // Later on, device actions in DeviceLinkerInputs are used to create @@ -3033,7 +3581,7 @@ class OffloadingActionBuilder final { // We will pass the device action as a host dependence, so we don't // need to do anything else with them. CudaDeviceActions.clear(); - return ABRT_Success; + return CompileDeviceOnly ? ABRT_Ignore_Host : ABRT_Success; } // By default, we produce an action for each device arch. @@ -3041,8 +3589,8 @@ class OffloadingActionBuilder final { A = C.getDriver().ConstructPhaseAction(C, Args, CurPhase, A, AssociatedOffloadKind); - if (CompileDeviceOnly && CurPhase == FinalPhase && - BundleOutput.hasValue() && BundleOutput.getValue()) { + if (CompileDeviceOnly && CurPhase == FinalPhase && BundleOutput && + *BundleOutput) { for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) { OffloadAction::DeviceDependences DDep; DDep.add(*CudaDeviceActions[I], *ToolChains.front(), GpuArchList[I], @@ -3055,8 +3603,11 @@ class OffloadingActionBuilder final { CudaDeviceActions.clear(); } - return (CompileDeviceOnly && CurPhase == FinalPhase) ? ABRT_Ignore_Host - : ABRT_Success; + return (CompileDeviceOnly && + (CurPhase == FinalPhase || + (!ShouldLink && CurPhase == phases::Assemble))) + ? ABRT_Ignore_Host + : ABRT_Success; } void appendLinkDeviceActions(ActionList &AL) override { @@ -3066,210 +3617,55 @@ class OffloadingActionBuilder final { assert(DeviceLinkerInputs.size() == GpuArchList.size() && "Linker inputs and GPU arch list sizes do not match."); - // Append a new link action for each device. + ActionList Actions; unsigned I = 0; + // Append a new link action for each device. + // Each entry in DeviceLinkerInputs corresponds to a GPU arch. for (auto &LI : DeviceLinkerInputs) { - // Each entry in DeviceLinkerInputs corresponds to a GPU arch. - auto *DeviceLinkAction = - C.MakeAction<LinkJobAction>(LI, types::TY_Image); + + types::ID Output = Args.hasArg(options::OPT_emit_llvm) + ? types::TY_LLVM_BC + : types::TY_Image; + + auto *DeviceLinkAction = C.MakeAction<LinkJobAction>(LI, Output); // Linking all inputs for the current GPU arch. // LI contains all the inputs for the linker. OffloadAction::DeviceDependences DeviceLinkDeps; DeviceLinkDeps.add(*DeviceLinkAction, *ToolChains[0], GpuArchList[I], AssociatedOffloadKind); - AL.push_back(C.MakeAction<OffloadAction>(DeviceLinkDeps, - DeviceLinkAction->getType())); + Actions.push_back(C.MakeAction<OffloadAction>( + DeviceLinkDeps, DeviceLinkAction->getType())); ++I; } DeviceLinkerInputs.clear(); - // Create a host object from all the device images by embedding them - // in a fat binary. - OffloadAction::DeviceDependences DDeps; - auto *TopDeviceLinkAction = - C.MakeAction<LinkJobAction>(AL, types::TY_Object); - DDeps.add(*TopDeviceLinkAction, *ToolChains[0], - nullptr, AssociatedOffloadKind); - - // Offload the host object to the host linker. - AL.push_back(C.MakeAction<OffloadAction>(DDeps, TopDeviceLinkAction->getType())); - } - - Action* appendLinkHostActions(ActionList &AL) override { return AL.back(); } - - void appendLinkDependences(OffloadAction::DeviceDependences &DA) override {} - }; - - /// OpenMP action builder. The host bitcode is passed to the device frontend - /// and all the device linked images are passed to the host link phase. - class OpenMPActionBuilder final : public DeviceActionBuilder { - /// The OpenMP actions for the current input. - ActionList OpenMPDeviceActions; - - /// The linker inputs obtained for each toolchain. - SmallVector<ActionList, 8> DeviceLinkerInputs; - - public: - OpenMPActionBuilder(Compilation &C, DerivedArgList &Args, - const Driver::InputList &Inputs) - : DeviceActionBuilder(C, Args, Inputs, Action::OFK_OpenMP) {} - - ActionBuilderReturnCode - getDeviceDependences(OffloadAction::DeviceDependences &DA, - phases::ID CurPhase, phases::ID FinalPhase, - PhasesTy &Phases) override { - if (OpenMPDeviceActions.empty()) - return ABRT_Inactive; - - // We should always have an action for each input. - assert(OpenMPDeviceActions.size() == ToolChains.size() && - "Number of OpenMP actions and toolchains do not match."); - - // The host only depends on device action in the linking phase, when all - // the device images have to be embedded in the host image. - if (CurPhase == phases::Link) { - assert(ToolChains.size() == DeviceLinkerInputs.size() && - "Toolchains and linker inputs sizes do not match."); - auto LI = DeviceLinkerInputs.begin(); - for (auto *A : OpenMPDeviceActions) { - LI->push_back(A); - ++LI; - } - - // We passed the device action as a host dependence, so we don't need to - // do anything else with them. - OpenMPDeviceActions.clear(); - return ABRT_Success; - } - - // By default, we produce an action for each device arch. - for (Action *&A : OpenMPDeviceActions) - A = C.getDriver().ConstructPhaseAction(C, Args, CurPhase, A); - - return ABRT_Success; - } - - ActionBuilderReturnCode addDeviceDepences(Action *HostAction) override { - - // If this is an input action replicate it for each OpenMP toolchain. - if (auto *IA = dyn_cast<InputAction>(HostAction)) { - OpenMPDeviceActions.clear(); - for (unsigned I = 0; I < ToolChains.size(); ++I) - OpenMPDeviceActions.push_back( - C.MakeAction<InputAction>(IA->getInputArg(), IA->getType())); - return ABRT_Success; - } - - // If this is an unbundling action use it as is for each OpenMP toolchain. - if (auto *UA = dyn_cast<OffloadUnbundlingJobAction>(HostAction)) { - OpenMPDeviceActions.clear(); - auto *IA = cast<InputAction>(UA->getInputs().back()); - std::string FileName = IA->getInputArg().getAsString(Args); - // Check if the type of the file is the same as the action. Do not - // unbundle it if it is not. Do not unbundle .so files, for example, - // which are not object files. - if (IA->getType() == types::TY_Object && - (!llvm::sys::path::has_extension(FileName) || - types::lookupTypeForExtension( - llvm::sys::path::extension(FileName).drop_front()) != - types::TY_Object)) - return ABRT_Inactive; - for (unsigned I = 0; I < ToolChains.size(); ++I) { - OpenMPDeviceActions.push_back(UA); - UA->registerDependentActionInfo( - ToolChains[I], /*BoundArch=*/StringRef(), Action::OFK_OpenMP); - } - return ABRT_Success; - } - - // When generating code for OpenMP we use the host compile phase result as - // a dependence to the device compile phase so that it can learn what - // declarations should be emitted. However, this is not the only use for - // the host action, so we prevent it from being collapsed. - if (isa<CompileJobAction>(HostAction)) { - HostAction->setCannotBeCollapsedWithNextDependentAction(); - assert(ToolChains.size() == OpenMPDeviceActions.size() && - "Toolchains and device action sizes do not match."); - OffloadAction::HostDependence HDep( - *HostAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(), - /*BoundArch=*/nullptr, Action::OFK_OpenMP); - auto TC = ToolChains.begin(); - for (Action *&A : OpenMPDeviceActions) { - assert(isa<CompileJobAction>(A)); - OffloadAction::DeviceDependences DDep; - DDep.add(*A, **TC, /*BoundArch=*/nullptr, Action::OFK_OpenMP); - A = C.MakeAction<OffloadAction>(HDep, DDep); - ++TC; - } - } - return ABRT_Success; - } - - void appendTopLevelActions(ActionList &AL) override { - if (OpenMPDeviceActions.empty()) - return; - - // We should always have an action for each input. - assert(OpenMPDeviceActions.size() == ToolChains.size() && - "Number of OpenMP actions and toolchains do not match."); - - // Append all device actions followed by the proper offload action. - auto TI = ToolChains.begin(); - for (auto *A : OpenMPDeviceActions) { - OffloadAction::DeviceDependences Dep; - Dep.add(*A, **TI, /*BoundArch=*/nullptr, Action::OFK_OpenMP); - AL.push_back(C.MakeAction<OffloadAction>(Dep, A->getType())); - ++TI; + // If emitting LLVM, do not generate final host/device compilation action + if (Args.hasArg(options::OPT_emit_llvm)) { + AL.append(Actions); + return; } - // We no longer need the action stored in this builder. - OpenMPDeviceActions.clear(); - } - void appendLinkDeviceActions(ActionList &AL) override { - assert(ToolChains.size() == DeviceLinkerInputs.size() && - "Toolchains and linker inputs sizes do not match."); - - // Append a new link action for each device. - auto TC = ToolChains.begin(); - for (auto &LI : DeviceLinkerInputs) { - auto *DeviceLinkAction = - C.MakeAction<LinkJobAction>(LI, types::TY_Image); - OffloadAction::DeviceDependences DeviceLinkDeps; - DeviceLinkDeps.add(*DeviceLinkAction, **TC, /*BoundArch=*/nullptr, - Action::OFK_OpenMP); - AL.push_back(C.MakeAction<OffloadAction>(DeviceLinkDeps, - DeviceLinkAction->getType())); - ++TC; + // Create a host object from all the device images by embedding them + // in a fat binary for mixed host-device compilation. For device-only + // compilation, creates a fat binary. + OffloadAction::DeviceDependences DDeps; + if (!CompileDeviceOnly || !BundleOutput || *BundleOutput) { + auto *TopDeviceLinkAction = C.MakeAction<LinkJobAction>( + Actions, + CompileDeviceOnly ? types::TY_HIP_FATBIN : types::TY_Object); + DDeps.add(*TopDeviceLinkAction, *ToolChains[0], nullptr, + AssociatedOffloadKind); + // Offload the host object to the host linker. + AL.push_back( + C.MakeAction<OffloadAction>(DDeps, TopDeviceLinkAction->getType())); + } else { + AL.append(Actions); } - DeviceLinkerInputs.clear(); } - Action* appendLinkHostActions(ActionList &AL) override { - // Create wrapper bitcode from the result of device link actions and compile - // it to an object which will be added to the host link command. - auto *BC = C.MakeAction<OffloadWrapperJobAction>(AL, types::TY_LLVM_BC); - auto *ASM = C.MakeAction<BackendJobAction>(BC, types::TY_PP_Asm); - return C.MakeAction<AssembleJobAction>(ASM, types::TY_Object); - } + Action* appendLinkHostActions(ActionList &AL) override { return AL.back(); } void appendLinkDependences(OffloadAction::DeviceDependences &DA) override {} - - bool initialize() override { - // Get the OpenMP toolchains. If we don't get any, the action builder will - // know there is nothing to do related to OpenMP offloading. - auto OpenMPTCRange = C.getOffloadToolChains<Action::OFK_OpenMP>(); - for (auto TI = OpenMPTCRange.first, TE = OpenMPTCRange.second; TI != TE; - ++TI) - ToolChains.push_back(TI->second); - - DeviceLinkerInputs.resize(ToolChains.size()); - return false; - } - - bool canUseBundlerUnbundler() const override { - // OpenMP should use bundled files whenever possible. - return true; - } }; /// @@ -3296,9 +3692,6 @@ public: // Create a specialized builder for HIP. SpecializedBuilders.push_back(new HIPActionBuilder(C, Args, Inputs)); - // Create a specialized builder for OpenMP. - SpecializedBuilders.push_back(new OpenMPActionBuilder(C, Args, Inputs)); - // // TODO: Build other specialized builders here. // @@ -3326,6 +3719,17 @@ public: delete SB; } + /// Record a host action and its originating input argument. + void recordHostAction(Action *HostAction, const Arg *InputArg) { + assert(HostAction && "Invalid host action"); + assert(InputArg && "Invalid input argument"); + auto Loc = HostActionToInputArgMap.find(HostAction); + if (Loc == HostActionToInputArgMap.end()) + HostActionToInputArgMap[HostAction] = InputArg; + assert(HostActionToInputArgMap[HostAction] == InputArg && + "host action mapped to multiple input arguments"); + } + /// Generate an action that adds device dependences (if any) to a host action. /// If no device dependence actions exist, just return the host action \a /// HostAction. If an error is found or if no builder requires the host action @@ -3341,6 +3745,7 @@ public: return HostAction; assert(HostAction && "Invalid host action!"); + recordHostAction(HostAction, InputArg); OffloadAction::DeviceDependences DDeps; // Check if all the programming models agree we should not emit the host @@ -3353,7 +3758,6 @@ public: ++InactiveBuilders; continue; } - auto RetCode = SB->getDeviceDependences(DDeps, CurPhase, FinalPhase, Phases); @@ -3394,6 +3798,8 @@ public: if (!IsValid) return true; + recordHostAction(HostAction, InputArg); + // If we are supporting bundling/unbundling and the current action is an // input action of non-source file, we replace the host action by the // unbundling action. The bundler tool has the logic to detect if an input @@ -3410,6 +3816,7 @@ public: C.getSingleOffloadToolChain<Action::OFK_Host>(), /*BoundArch=*/StringRef(), Action::OFK_Host); HostAction = UnbundlingHostAction; + recordHostAction(HostAction, InputArg); } assert(HostAction && "Invalid host action!"); @@ -3420,7 +3827,7 @@ public: if (!SB->isValid()) continue; - auto RetCode = SB->addDeviceDepences(HostAction); + auto RetCode = SB->addDeviceDependences(HostAction); // Host dependences for device actions are not compatible with that same // action being ignored. @@ -3446,6 +3853,9 @@ public: /// programming models allow it. bool appendTopLevelActions(ActionList &AL, Action *HostAction, const Arg *InputArg) { + if (HostAction) + recordHostAction(HostAction, InputArg); + // Get the device actions to be appended. ActionList OffloadAL; for (auto *SB : SpecializedBuilders) { @@ -3467,6 +3877,7 @@ public: // before this method was called. assert(HostAction == AL.back() && "Host action not in the list??"); HostAction = C.MakeAction<OffloadBundlingJobAction>(OffloadAL); + recordHostAction(HostAction, InputArg); AL.back() = HostAction; } else AL.append(OffloadAL.begin(), OffloadAL.end()); @@ -3479,15 +3890,18 @@ public: return false; } - Action* makeHostLinkAction() { - // Build a list of device linking actions. - ActionList DeviceAL; + void appendDeviceLinkActions(ActionList &AL) { for (DeviceActionBuilder *SB : SpecializedBuilders) { if (!SB->isValid()) continue; - SB->appendLinkDeviceActions(DeviceAL); + SB->appendLinkDeviceActions(AL); } + } + Action *makeHostLinkAction() { + // Build a list of device linking actions. + ActionList DeviceAL; + appendDeviceLinkActions(DeviceAL); if (DeviceAL.empty()) return nullptr; @@ -3497,6 +3911,11 @@ public: if (!SB->isValid()) continue; HA = SB->appendLinkHostActions(DeviceAL); + // This created host action has no originating input argument, therefore + // needs to set its offloading kind directly. + if (HA) + HA->propagateHostOffloadInfo(SB->getAssociatedOffloadKind(), + /*BoundArch=*/nullptr); } return HA; } @@ -3523,10 +3942,22 @@ public: // If we don't have device dependencies, we don't have to create an offload // action. if (DDeps.getActions().empty()) { - // Propagate all the active kinds to host action. Given that it is a link - // action it is assumed to depend on all actions generated so far. - HostAction->propagateHostOffloadInfo(ActiveOffloadKinds, - /*BoundArch=*/nullptr); + // Set all the active offloading kinds to the link action. Given that it + // is a link action it is assumed to depend on all actions generated so + // far. + HostAction->setHostOffloadInfo(ActiveOffloadKinds, + /*BoundArch=*/nullptr); + // Propagate active offloading kinds for each input to the link action. + // Each input may have different active offloading kind. + for (auto *A : HostAction->inputs()) { + auto ArgLoc = HostActionToInputArgMap.find(A); + if (ArgLoc == HostActionToInputArgMap.end()) + continue; + auto OFKLoc = InputArgToOffloadKindMap.find(ArgLoc->second); + if (OFKLoc == InputArgToOffloadKindMap.end()) + continue; + A->propagateHostOffloadInfo(OFKLoc->second, /*BoundArch=*/nullptr); + } return HostAction; } @@ -3564,12 +3995,34 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args, phases::ID FinalPhase = getFinalPhase(Args, &FinalPhaseArg); if (FinalPhase == phases::Link) { - if (Args.hasArg(options::OPT_emit_llvm)) + if (Args.hasArgNoClaim(options::OPT_hipstdpar)) { + Args.AddFlagArg(nullptr, getOpts().getOption(options::OPT_hip_link)); + Args.AddFlagArg(nullptr, + getOpts().getOption(options::OPT_frtlib_add_rpath)); + } + // Emitting LLVM while linking disabled except in HIPAMD Toolchain + if (Args.hasArg(options::OPT_emit_llvm) && !Args.hasArg(options::OPT_hip_link)) Diag(clang::diag::err_drv_emit_llvm_link); if (IsCLMode() && LTOMode != LTOK_None && !Args.getLastArgValue(options::OPT_fuse_ld_EQ) .equals_insensitive("lld")) Diag(clang::diag::err_drv_lto_without_lld); + + // If -dumpdir is not specified, give a default prefix derived from the link + // output filename. For example, `clang -g -gsplit-dwarf a.c -o x` passes + // `-dumpdir x-` to cc1. If -o is unspecified, use + // stem(getDefaultImageName()) (usually stem("a.out") = "a"). + if (!Args.hasArg(options::OPT_dumpdir)) { + Arg *FinalOutput = Args.getLastArg(options::OPT_o, options::OPT__SLASH_o); + Arg *Arg = Args.MakeSeparateArg( + nullptr, getOpts().getOption(options::OPT_dumpdir), + Args.MakeArgString( + (FinalOutput ? FinalOutput->getValue() + : llvm::sys::path::stem(getDefaultImageName())) + + "-")); + Arg->claim(); + Args.append(Arg); + } } if (FinalPhase == phases::Preprocess || Args.hasArg(options::OPT__SLASH_Y_)) { @@ -3665,11 +4118,6 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, return; } - // Reject -Z* at the top level, these options should never have been exposed - // by gcc. - if (Arg *A = Args.getLastArg(options::OPT_Z_Joined)) - Diag(clang::diag::err_drv_use_of_Z_option) << A->getAsString(Args); - // Diagnose misuse of /Fo. if (Arg *A = Args.getLastArg(options::OPT__SLASH_Fo)) { StringRef V = A->getValue(); @@ -3705,11 +4153,19 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, handleArguments(C, Args, Inputs, Actions); + bool UseNewOffloadingDriver = + C.isOffloadingHostKind(Action::OFK_OpenMP) || + Args.hasFlag(options::OPT_offload_new_driver, + options::OPT_no_offload_new_driver, false); + // Builder to be used to build offloading actions. - OffloadingActionBuilder OffloadBuilder(C, Args, Inputs); + std::unique_ptr<OffloadingActionBuilder> OffloadBuilder = + !UseNewOffloadingDriver + ? std::make_unique<OffloadingActionBuilder>(C, Args, Inputs) + : nullptr; // Construct the actions to perform. - HeaderModulePrecompileJobAction *HeaderModuleAction = nullptr; + ExtractAPIJobAction *ExtractAPIAction = nullptr; ActionList LinkerInputs; ActionList MergerInputs; @@ -3728,21 +4184,28 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, // Use the current host action in any of the offloading actions, if // required. - if (OffloadBuilder.addHostDependenceToDeviceActions(Current, InputArg)) - break; + if (!UseNewOffloadingDriver) + if (OffloadBuilder->addHostDependenceToDeviceActions(Current, InputArg)) + break; for (phases::ID Phase : PL) { // Add any offload action the host action depends on. - Current = OffloadBuilder.addDeviceDependencesToHostAction( - Current, InputArg, Phase, PL.back(), FullPL); + if (!UseNewOffloadingDriver) + Current = OffloadBuilder->addDeviceDependencesToHostAction( + Current, InputArg, Phase, PL.back(), FullPL); if (!Current) break; // Queue linker inputs. if (Phase == phases::Link) { assert(Phase == PL.back() && "linking must be final compilation step."); - LinkerInputs.push_back(Current); + // We don't need to generate additional link commands if emitting AMD + // bitcode or compiling only for the offload device + if (!(C.getInputArgs().hasArg(options::OPT_hip_link) && + (C.getInputArgs().hasArg(options::OPT_emit_llvm))) && + !offloadDeviceOnly()) + LinkerInputs.push_back(Current); Current = nullptr; break; } @@ -3758,12 +4221,8 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, break; } - // Each precompiled header file after a module file action is a module - // header of that same module file, rather than being compiled to a - // separate PCH. - if (Phase == phases::Precompile && HeaderModuleAction && - getPrecompiledType(InputType) == types::TY_PCH) { - HeaderModuleAction->addModuleHeaderInput(Current); + if (Phase == phases::Precompile && ExtractAPIAction) { + ExtractAPIAction->addHeaderInput(Current); Current = nullptr; break; } @@ -3778,14 +4237,19 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, if (NewCurrent == Current) continue; - if (auto *HMA = dyn_cast<HeaderModulePrecompileJobAction>(NewCurrent)) - HeaderModuleAction = HMA; + if (auto *EAA = dyn_cast<ExtractAPIJobAction>(NewCurrent)) + ExtractAPIAction = EAA; Current = NewCurrent; + // Try to build the offloading actions and add the result as a dependency + // to the host. + if (UseNewOffloadingDriver) + Current = BuildOffloadingActions(C, Args, I, Current); // Use the current host action in any of the offloading actions, if // required. - if (OffloadBuilder.addHostDependenceToDeviceActions(Current, InputArg)) + else if (OffloadBuilder->addHostDependenceToDeviceActions(Current, + InputArg)) break; if (Current->getType() == types::TY_Nothing) @@ -3797,21 +4261,40 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, Actions.push_back(Current); // Add any top level actions generated for offloading. - OffloadBuilder.appendTopLevelActions(Actions, Current, InputArg); + if (!UseNewOffloadingDriver) + OffloadBuilder->appendTopLevelActions(Actions, Current, InputArg); + else if (Current) + Current->propagateHostOffloadInfo(C.getActiveOffloadKinds(), + /*BoundArch=*/nullptr); } // Add a link action if necessary. + + if (LinkerInputs.empty()) { + Arg *FinalPhaseArg; + if (getFinalPhase(Args, &FinalPhaseArg) == phases::Link) + if (!UseNewOffloadingDriver) + OffloadBuilder->appendDeviceLinkActions(Actions); + } + if (!LinkerInputs.empty()) { - if (Action *Wrapper = OffloadBuilder.makeHostLinkAction()) - LinkerInputs.push_back(Wrapper); + if (!UseNewOffloadingDriver) + if (Action *Wrapper = OffloadBuilder->makeHostLinkAction()) + LinkerInputs.push_back(Wrapper); Action *LA; // Check if this Linker Job should emit a static library. if (ShouldEmitStaticLibrary(Args)) { LA = C.MakeAction<StaticLibJobAction>(LinkerInputs, types::TY_Image); + } else if (UseNewOffloadingDriver || + Args.hasArg(options::OPT_offload_link)) { + LA = C.MakeAction<LinkerWrapperJobAction>(LinkerInputs, types::TY_Image); + LA->propagateHostOffloadInfo(C.getActiveOffloadKinds(), + /*BoundArch=*/nullptr); } else { LA = C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image); } - LA = OffloadBuilder.processHostLinkAction(LA); + if (!UseNewOffloadingDriver) + LA = OffloadBuilder->processHostLinkAction(LA); Actions.push_back(LA); } @@ -3823,7 +4306,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, if (Args.hasArg(options::OPT_emit_interface_stubs)) { auto PhaseList = types::getCompilationPhases( types::TY_IFS_CPP, - Args.hasArg(options::OPT_c) ? phases::Compile : phases::LastPhase); + Args.hasArg(options::OPT_c) ? phases::Compile : phases::IfsMerge); ActionList MergerInputs; @@ -3876,25 +4359,393 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, C.MakeAction<IfsMergeJobAction>(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<InputAction>(*A, types::TY_C); - Actions.push_back( - C.MakeAction<PrecompileJobAction>(InputAc, types::TY_Nothing)); - for (auto &I : Inputs) - I.second->claim(); + for (auto Opt : {options::OPT_print_supported_cpus, + options::OPT_print_supported_extensions, + options::OPT_print_enabled_extensions}) { + // If --print-supported-cpus, -mcpu=? or -mtune=? is specified, build a + // custom Compile phase that prints out supported cpu models and quits. + // + // If either --print-supported-extensions or --print-enabled-extensions is + // specified, call the corresponding helper function that prints out the + // supported/enabled extensions and quits. + if (Arg *A = Args.getLastArg(Opt)) { + if (Opt == options::OPT_print_supported_extensions && + !C.getDefaultToolChain().getTriple().isRISCV() && + !C.getDefaultToolChain().getTriple().isAArch64() && + !C.getDefaultToolChain().getTriple().isARM()) { + C.getDriver().Diag(diag::err_opt_not_valid_on_target) + << "--print-supported-extensions"; + return; + } + if (Opt == options::OPT_print_enabled_extensions && + !C.getDefaultToolChain().getTriple().isRISCV() && + !C.getDefaultToolChain().getTriple().isAArch64()) { + C.getDriver().Diag(diag::err_opt_not_valid_on_target) + << "--print-enabled-extensions"; + return; + } + + // Use the -mcpu=? flag as the dummy input to cc1. + Actions.clear(); + Action *InputAc = C.MakeAction<InputAction>(*A, types::TY_C); + Actions.push_back( + C.MakeAction<PrecompileJobAction>(InputAc, types::TY_Nothing)); + for (auto &I : Inputs) + I.second->claim(); + } + } + + // Call validator for dxil when -Vd not in Args. + if (C.getDefaultToolChain().getTriple().isDXIL()) { + // Only add action when needValidation. + const auto &TC = + static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain()); + if (TC.requiresValidation(Args)) { + Action *LastAction = Actions.back(); + Actions.push_back(C.MakeAction<BinaryAnalyzeJobAction>( + LastAction, types::TY_DX_CONTAINER)); + } } // Claim ignored clang-cl options. Args.ClaimAllArgs(options::OPT_cl_ignored_Group); +} + +/// Returns the canonical name for the offloading architecture when using a HIP +/// or CUDA architecture. +static StringRef getCanonicalArchString(Compilation &C, + const llvm::opt::DerivedArgList &Args, + StringRef ArchStr, + const llvm::Triple &Triple, + bool SuppressError = false) { + // Lookup the CUDA / HIP architecture string. Only report an error if we were + // expecting the triple to be only NVPTX / AMDGPU. + OffloadArch Arch = + StringToOffloadArch(getProcessorFromTargetID(Triple, ArchStr)); + if (!SuppressError && Triple.isNVPTX() && + (Arch == OffloadArch::UNKNOWN || !IsNVIDIAOffloadArch(Arch))) { + C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch) + << "CUDA" << ArchStr; + return StringRef(); + } else if (!SuppressError && Triple.isAMDGPU() && + (Arch == OffloadArch::UNKNOWN || !IsAMDOffloadArch(Arch))) { + C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch) + << "HIP" << ArchStr; + return StringRef(); + } + + if (IsNVIDIAOffloadArch(Arch)) + return Args.MakeArgStringRef(OffloadArchToString(Arch)); + + if (IsAMDOffloadArch(Arch)) { + llvm::StringMap<bool> Features; + auto HIPTriple = getHIPOffloadTargetTriple(C.getDriver(), C.getInputArgs()); + if (!HIPTriple) + return StringRef(); + auto Arch = parseTargetID(*HIPTriple, ArchStr, &Features); + if (!Arch) { + C.getDriver().Diag(clang::diag::err_drv_bad_target_id) << ArchStr; + C.setContainsError(); + return StringRef(); + } + return Args.MakeArgStringRef(getCanonicalTargetID(*Arch, Features)); + } + + // If the input isn't CUDA or HIP just return the architecture. + return ArchStr; +} + +/// Checks if the set offloading architectures does not conflict. Returns the +/// incompatible pair if a conflict occurs. +static std::optional<std::pair<llvm::StringRef, llvm::StringRef>> +getConflictOffloadArchCombination(const llvm::DenseSet<StringRef> &Archs, + llvm::Triple Triple) { + if (!Triple.isAMDGPU()) + return std::nullopt; + + std::set<StringRef> ArchSet; + llvm::copy(Archs, std::inserter(ArchSet, ArchSet.begin())); + return getConflictTargetIDCombination(ArchSet); +} + +llvm::DenseSet<StringRef> +Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args, + Action::OffloadKind Kind, const ToolChain *TC, + bool SuppressError) const { + if (!TC) + TC = &C.getDefaultToolChain(); + + // --offload and --offload-arch options are mutually exclusive. + if (Args.hasArgNoClaim(options::OPT_offload_EQ) && + Args.hasArgNoClaim(options::OPT_offload_arch_EQ, + options::OPT_no_offload_arch_EQ)) { + C.getDriver().Diag(diag::err_opt_not_valid_with_opt) + << "--offload" + << (Args.hasArgNoClaim(options::OPT_offload_arch_EQ) + ? "--offload-arch" + : "--no-offload-arch"); + } + + if (KnownArchs.contains(TC)) + return KnownArchs.lookup(TC); + + llvm::DenseSet<StringRef> Archs; + for (auto *Arg : Args) { + // Extract any '--[no-]offload-arch' arguments intended for this toolchain. + std::unique_ptr<llvm::opt::Arg> ExtractedArg = nullptr; + if (Arg->getOption().matches(options::OPT_Xopenmp_target_EQ) && + ToolChain::getOpenMPTriple(Arg->getValue(0)) == TC->getTriple()) { + Arg->claim(); + unsigned Index = Args.getBaseArgs().MakeIndex(Arg->getValue(1)); + ExtractedArg = getOpts().ParseOneArg(Args, Index); + Arg = ExtractedArg.get(); + } + + // Add or remove the seen architectures in order of appearance. If an + // invalid architecture is given we simply exit. + if (Arg->getOption().matches(options::OPT_offload_arch_EQ)) { + for (StringRef Arch : llvm::split(Arg->getValue(), ",")) { + if (Arch == "native" || Arch.empty()) { + auto GPUsOrErr = TC->getSystemGPUArchs(Args); + if (!GPUsOrErr) { + if (SuppressError) + llvm::consumeError(GPUsOrErr.takeError()); + else + TC->getDriver().Diag(diag::err_drv_undetermined_gpu_arch) + << llvm::Triple::getArchTypeName(TC->getArch()) + << llvm::toString(GPUsOrErr.takeError()) << "--offload-arch"; + continue; + } + + for (auto ArchStr : *GPUsOrErr) { + Archs.insert( + getCanonicalArchString(C, Args, Args.MakeArgString(ArchStr), + TC->getTriple(), SuppressError)); + } + } else { + StringRef ArchStr = getCanonicalArchString( + C, Args, Arch, TC->getTriple(), SuppressError); + if (ArchStr.empty()) + return Archs; + Archs.insert(ArchStr); + } + } + } else if (Arg->getOption().matches(options::OPT_no_offload_arch_EQ)) { + for (StringRef Arch : llvm::split(Arg->getValue(), ",")) { + if (Arch == "all") { + Archs.clear(); + } else { + StringRef ArchStr = getCanonicalArchString( + C, Args, Arch, TC->getTriple(), SuppressError); + if (ArchStr.empty()) + return Archs; + Archs.erase(ArchStr); + } + } + } + } + + if (auto ConflictingArchs = + getConflictOffloadArchCombination(Archs, TC->getTriple())) { + C.getDriver().Diag(clang::diag::err_drv_bad_offload_arch_combo) + << ConflictingArchs->first << ConflictingArchs->second; + C.setContainsError(); + } + + // Skip filling defaults if we're just querying what is availible. + if (SuppressError) + return Archs; + + if (Archs.empty()) { + if (Kind == Action::OFK_Cuda) + Archs.insert(OffloadArchToString(OffloadArch::CudaDefault)); + else if (Kind == Action::OFK_HIP) + Archs.insert(OffloadArchToString(OffloadArch::HIPDefault)); + else if (Kind == Action::OFK_OpenMP) + Archs.insert(StringRef()); + } else { + Args.ClaimAllArgs(options::OPT_offload_arch_EQ); + Args.ClaimAllArgs(options::OPT_no_offload_arch_EQ); + } + + return Archs; +} + +Action *Driver::BuildOffloadingActions(Compilation &C, + llvm::opt::DerivedArgList &Args, + const InputTy &Input, + Action *HostAction) const { + // Don't build offloading actions if explicitly disabled or we do not have a + // valid source input and compile action to embed it in. If preprocessing only + // ignore embedding. + if (offloadHostOnly() || !types::isSrcFile(Input.first) || + !(isa<CompileJobAction>(HostAction) || + getFinalPhase(Args) == phases::Preprocess)) + return HostAction; + + ActionList OffloadActions; + OffloadAction::DeviceDependences DDeps; + + const Action::OffloadKind OffloadKinds[] = { + Action::OFK_OpenMP, Action::OFK_Cuda, Action::OFK_HIP}; + + for (Action::OffloadKind Kind : OffloadKinds) { + SmallVector<const ToolChain *, 2> ToolChains; + ActionList DeviceActions; + + auto TCRange = C.getOffloadToolChains(Kind); + for (auto TI = TCRange.first, TE = TCRange.second; TI != TE; ++TI) + ToolChains.push_back(TI->second); + + if (ToolChains.empty()) + continue; + + types::ID InputType = Input.first; + const Arg *InputArg = Input.second; + + // The toolchain can be active for unsupported file types. + if ((Kind == Action::OFK_Cuda && !types::isCuda(InputType)) || + (Kind == Action::OFK_HIP && !types::isHIP(InputType))) + continue; + + // Get the product of all bound architectures and toolchains. + SmallVector<std::pair<const ToolChain *, StringRef>> TCAndArchs; + for (const ToolChain *TC : ToolChains) { + llvm::DenseSet<StringRef> Arches = getOffloadArchs(C, Args, Kind, TC); + SmallVector<StringRef, 0> Sorted(Arches.begin(), Arches.end()); + llvm::sort(Sorted); + for (StringRef Arch : Sorted) + TCAndArchs.push_back(std::make_pair(TC, Arch)); + } + + for (unsigned I = 0, E = TCAndArchs.size(); I != E; ++I) + DeviceActions.push_back(C.MakeAction<InputAction>(*InputArg, InputType)); + + if (DeviceActions.empty()) + return HostAction; + + auto PL = types::getCompilationPhases(*this, Args, InputType); + + for (phases::ID Phase : PL) { + if (Phase == phases::Link) { + assert(Phase == PL.back() && "linking must be final compilation step."); + break; + } + + auto TCAndArch = TCAndArchs.begin(); + for (Action *&A : DeviceActions) { + if (A->getType() == types::TY_Nothing) + continue; + + // Propagate the ToolChain so we can use it in ConstructPhaseAction. + A->propagateDeviceOffloadInfo(Kind, TCAndArch->second.data(), + TCAndArch->first); + A = ConstructPhaseAction(C, Args, Phase, A, Kind); + + if (isa<CompileJobAction>(A) && isa<CompileJobAction>(HostAction) && + Kind == Action::OFK_OpenMP && + HostAction->getType() != types::TY_Nothing) { + // OpenMP offloading has a dependency on the host compile action to + // identify which declarations need to be emitted. This shouldn't be + // collapsed with any other actions so we can use it in the device. + HostAction->setCannotBeCollapsedWithNextDependentAction(); + OffloadAction::HostDependence HDep( + *HostAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(), + TCAndArch->second.data(), Kind); + OffloadAction::DeviceDependences DDep; + DDep.add(*A, *TCAndArch->first, TCAndArch->second.data(), Kind); + A = C.MakeAction<OffloadAction>(HDep, DDep); + } - // Claim --cuda-host-only and --cuda-compile-host-device, which may be passed - // to non-CUDA compilations and should not trigger warnings there. - Args.ClaimAllArgs(options::OPT_cuda_host_only); - Args.ClaimAllArgs(options::OPT_cuda_compile_host_device); + ++TCAndArch; + } + } + + // Compiling HIP in non-RDC mode requires linking each action individually. + for (Action *&A : DeviceActions) { + if ((A->getType() != types::TY_Object && + A->getType() != types::TY_LTO_BC) || + Kind != Action::OFK_HIP || + Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false)) + continue; + ActionList LinkerInput = {A}; + A = C.MakeAction<LinkJobAction>(LinkerInput, types::TY_Image); + } + + auto TCAndArch = TCAndArchs.begin(); + for (Action *A : DeviceActions) { + DDeps.add(*A, *TCAndArch->first, TCAndArch->second.data(), Kind); + OffloadAction::DeviceDependences DDep; + DDep.add(*A, *TCAndArch->first, TCAndArch->second.data(), Kind); + + // Compiling CUDA in non-RDC mode uses the PTX output if available. + for (Action *Input : A->getInputs()) + if (Kind == Action::OFK_Cuda && A->getType() == types::TY_Object && + !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, + false)) + DDep.add(*Input, *TCAndArch->first, TCAndArch->second.data(), Kind); + OffloadActions.push_back(C.MakeAction<OffloadAction>(DDep, A->getType())); + + ++TCAndArch; + } + } + + // HIP code in non-RDC mode will bundle the output if it invoked the linker. + bool ShouldBundleHIP = + C.isOffloadingHostKind(Action::OFK_HIP) && + Args.hasFlag(options::OPT_gpu_bundle_output, + options::OPT_no_gpu_bundle_output, true) && + !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false) && + !llvm::any_of(OffloadActions, + [](Action *A) { return A->getType() != types::TY_Image; }); + + // All kinds exit now in device-only mode except for non-RDC mode HIP. + if (offloadDeviceOnly() && !ShouldBundleHIP) + return C.MakeAction<OffloadAction>(DDeps, types::TY_Nothing); + + if (OffloadActions.empty()) + return HostAction; + + OffloadAction::DeviceDependences DDep; + if (C.isOffloadingHostKind(Action::OFK_Cuda) && + !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false)) { + // If we are not in RDC-mode we just emit the final CUDA fatbinary for + // each translation unit without requiring any linking. + Action *FatbinAction = + C.MakeAction<LinkJobAction>(OffloadActions, types::TY_CUDA_FATBIN); + DDep.add(*FatbinAction, *C.getSingleOffloadToolChain<Action::OFK_Cuda>(), + nullptr, Action::OFK_Cuda); + } else if (C.isOffloadingHostKind(Action::OFK_HIP) && + !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, + false)) { + // If we are not in RDC-mode we just emit the final HIP fatbinary for each + // translation unit, linking each input individually. + Action *FatbinAction = + C.MakeAction<LinkJobAction>(OffloadActions, types::TY_HIP_FATBIN); + DDep.add(*FatbinAction, *C.getSingleOffloadToolChain<Action::OFK_HIP>(), + nullptr, Action::OFK_HIP); + } else { + // Package all the offloading actions into a single output that can be + // embedded in the host and linked. + Action *PackagerAction = + C.MakeAction<OffloadPackagerJobAction>(OffloadActions, types::TY_Image); + DDep.add(*PackagerAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(), + nullptr, C.getActiveOffloadKinds()); + } + + // HIP wants '--offload-device-only' to create a fatbinary by default. + if (offloadDeviceOnly()) + return C.MakeAction<OffloadAction>(DDep, types::TY_Nothing); + + // If we are unable to embed a single device output into the host, we need to + // add each device output as a host dependency to ensure they are still built. + bool SingleDeviceOutput = !llvm::any_of(OffloadActions, [](Action *A) { + return A->getType() == types::TY_Nothing; + }) && isa<CompileJobAction>(HostAction); + OffloadAction::HostDependence HDep( + *HostAction, *C.getSingleOffloadToolChain<Action::OFK_Host>(), + /*BoundArch=*/nullptr, SingleDeviceOutput ? DDep : DDeps); + return C.MakeAction<OffloadAction>(HDep, SingleDeviceOutput ? DDep : DDeps); } Action *Driver::ConstructPhaseAction( @@ -3923,10 +4774,14 @@ Action *Driver::ConstructPhaseAction( OutputTy = types::TY_Dependencies; } else { OutputTy = Input->getType(); + // For these cases, the preprocessor is only translating forms, the Output + // still needs preprocessing. if (!Args.hasFlag(options::OPT_frewrite_includes, options::OPT_fno_rewrite_includes, false) && !Args.hasFlag(options::OPT_frewrite_imports, options::OPT_fno_rewrite_imports, false) && + !Args.hasFlag(options::OPT_fdirectives_only, + options::OPT_fno_directives_only, false) && !CCGenDiagnostics) OutputTy = types::getPreprocessedType(OutputTy); assert(OutputTy != types::TY_INVALID && @@ -3935,6 +4790,18 @@ Action *Driver::ConstructPhaseAction( return C.MakeAction<PreprocessJobAction>(Input, OutputTy); } case phases::Precompile: { + // API extraction should not generate an actual precompilation action. + if (Args.hasArg(options::OPT_extract_api)) + return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO); + + // With 'fexperimental-modules-reduced-bmi', we don't want to run the + // precompile phase unless the user specified '--precompile'. In the case + // the '--precompile' flag is enabled, we will try to emit the reduced BMI + // as a by product in GenerateModuleInterfaceAction. + if (Args.hasArg(options::OPT_modules_reduced_bmi) && + !Args.getLastArg(options::OPT__precompile)) + return Input; + types::ID OutputTy = getPrecompiledType(Input->getType()); assert(OutputTy != types::TY_INVALID && "Cannot precompile this input type!"); @@ -3954,9 +4821,6 @@ Action *Driver::ConstructPhaseAction( OutputTy = types::TY_Nothing; } - if (ModName) - return C.MakeAction<HeaderModulePrecompileJobAction>(Input, OutputTy, - ModName); return C.MakeAction<PrecompileJobAction>(Input, OutputTy); } case phases::Compile: { @@ -3973,24 +4837,50 @@ Action *Driver::ConstructPhaseAction( return C.MakeAction<MigrateJobAction>(Input, types::TY_Remap); if (Args.hasArg(options::OPT_emit_ast)) return C.MakeAction<CompileJobAction>(Input, types::TY_AST); + if (Args.hasArg(options::OPT_emit_cir)) + return C.MakeAction<CompileJobAction>(Input, types::TY_CIR); if (Args.hasArg(options::OPT_module_file_info)) return C.MakeAction<CompileJobAction>(Input, types::TY_ModuleFile); if (Args.hasArg(options::OPT_verify_pch)) return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing); + if (Args.hasArg(options::OPT_extract_api)) + return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO); return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC); } case phases::Backend: { if (isUsingLTO() && TargetDeviceOffloadKind == Action::OFK_None) { + types::ID Output; + if (Args.hasArg(options::OPT_ffat_lto_objects) && + !Args.hasArg(options::OPT_emit_llvm)) + Output = types::TY_PP_Asm; + else if (Args.hasArg(options::OPT_S)) + Output = types::TY_LTO_IR; + else + Output = types::TY_LTO_BC; + return C.MakeAction<BackendJobAction>(Input, Output); + } + if (isUsingLTO(/* IsOffload */ true) && + TargetDeviceOffloadKind != Action::OFK_None) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; return C.MakeAction<BackendJobAction>(Input, Output); } if (Args.hasArg(options::OPT_emit_llvm) || - (TargetDeviceOffloadKind == Action::OFK_HIP && - Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, - false))) { + (((Input->getOffloadingToolChain() && + Input->getOffloadingToolChain()->getTriple().isAMDGPU()) || + TargetDeviceOffloadKind == Action::OFK_HIP) && + (Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, + false) || + TargetDeviceOffloadKind == Action::OFK_OpenMP))) { types::ID Output = - Args.hasArg(options::OPT_S) ? types::TY_LLVM_IR : types::TY_LLVM_BC; + Args.hasArg(options::OPT_S) && + (TargetDeviceOffloadKind == Action::OFK_None || + offloadDeviceOnly() || + (TargetDeviceOffloadKind == Action::OFK_HIP && + !Args.hasFlag(options::OPT_offload_new_driver, + options::OPT_no_offload_new_driver, false))) + ? types::TY_LLVM_IR + : types::TY_LLVM_BC; return C.MakeAction<BackendJobAction>(Input, Output); } return C.MakeAction<BackendJobAction>(Input, types::TY_PP_Asm); @@ -4020,11 +4910,16 @@ void Driver::BuildJobs(Compilation &C) const { // we are also generating .o files. So we allow more than one output file in // this case as well. // + // OffloadClass of type TY_Nothing: device-only output will place many outputs + // into a single offloading action. We should count all inputs to the action + // as outputs. Also ignore device-only outputs if we're compiling with + // -fsyntax-only. if (FinalOutput) { unsigned NumOutputs = 0; unsigned NumIfsOutputs = 0; - for (const Action *A : C.getActions()) + for (const Action *A : C.getActions()) { if (A->getType() != types::TY_Nothing && + A->getType() != types::TY_DX_CONTAINER && !(A->getKind() == Action::IfsMergeJobClass || (A->getType() == clang::driver::types::TY_IFS_CPP && A->getKind() == clang::driver::Action::CompileJobClass && @@ -4032,6 +4927,11 @@ void Driver::BuildJobs(Compilation &C) const { (A->getKind() == Action::BindArchClass && A->getInputs().size() && A->getInputs().front()->getKind() == Action::IfsMergeJobClass))) ++NumOutputs; + else if (A->getKind() == Action::OffloadClass && + A->getType() == types::TY_Nothing && + !C.getArgs().hasArg(options::OPT_fsyntax_only)) + NumOutputs += A->size(); + } if (NumOutputs > 1) { Diag(clang::diag::err_drv_output_argument_with_multiple_files); @@ -4040,13 +4940,6 @@ void Driver::BuildJobs(Compilation &C) const { } const llvm::Triple &RawTriple = C.getDefaultToolChain().getTriple(); - if (RawTriple.isOSAIX()) { - if (Arg *A = C.getArgs().getLastArg(options::OPT_G)) - Diag(diag::err_drv_unsupported_opt_for_target) - << A->getSpelling() << RawTriple.str(); - if (LTOMode == LTOK_Thin) - Diag(diag::err_drv_clang_unsupported) << "thinLTO on AIX"; - } // Collect the list of architectures. llvm::StringSet<> ArchNames; @@ -4056,7 +4949,7 @@ void Driver::BuildJobs(Compilation &C) const { ArchNames.insert(A->getValue()); // Set of (Action, canonical ToolChain triple) pairs we've built jobs for. - std::map<std::pair<const Action *, std::string>, InputInfo> CachedResults; + std::map<std::pair<const Action *, std::string>, InputInfoList> CachedResults; for (Action *A : C.getActions()) { // If we are linking an image for multiple archs then the linker wants // -arch_multiple and -final_output <final image name>. Unfortunately, this @@ -4088,7 +4981,7 @@ void Driver::BuildJobs(Compilation &C) const { if (CCPrintProcessStats) { C.setPostCallback([=](const Command &Cmd, int Res) { - Optional<llvm::sys::ProcessStatistics> ProcStat = + std::optional<llvm::sys::ProcessStatistics> ProcStat = Cmd.getProcessStatistics(); if (!ProcStat) return; @@ -4124,7 +5017,7 @@ void Driver::BuildJobs(Compilation &C) const { << '\n'; Out.flush(); std::error_code EC; - llvm::raw_fd_ostream OS(CCPrintStatReportFilename.c_str(), EC, + llvm::raw_fd_ostream OS(CCPrintStatReportFilename, EC, llvm::sys::fs::OF_Append | llvm::sys::fs::OF_Text); if (EC) @@ -4148,6 +5041,8 @@ void Driver::BuildJobs(Compilation &C) const { C.getArgs().hasArg(options::OPT_Qunused_arguments)) return; + // Claim -fdriver-only here. + (void)C.getArgs().hasArg(options::OPT_fdriver_only); // Claim -### here. (void)C.getArgs().hasArg(options::OPT__HASH_HASH_HASH); @@ -4155,6 +5050,12 @@ void Driver::BuildJobs(Compilation &C) const { (void)C.getArgs().hasArg(options::OPT_driver_mode); (void)C.getArgs().hasArg(options::OPT_rsp_quoting); + bool HasAssembleJob = llvm::any_of(C.getJobs(), [](auto &J) { + // Match ClangAs and other derived assemblers of Tool. ClangAs uses a + // longer ShortName "clang integrated assembler" while other assemblers just + // use "assembler". + return strstr(J.getCreator().getShortName(), "assembler"); + }); for (Arg *A : C.getArgs()) { // FIXME: It would be nice to be able to send the argument to the // DiagnosticsEngine, so that extra values, position, and so on could be @@ -4182,9 +5083,21 @@ void Driver::BuildJobs(Compilation &C) const { // In clang-cl, don't mention unknown arguments here since they have // already been warned about. - if (!IsCLMode() || !A->getOption().matches(options::OPT_UNKNOWN)) - Diag(clang::diag::warn_drv_unused_argument) - << A->getAsString(C.getArgs()); + if (!IsCLMode() || !A->getOption().matches(options::OPT_UNKNOWN)) { + if (A->getOption().hasFlag(options::TargetSpecific) && + !A->isIgnoredTargetSpecific() && !HasAssembleJob && + // When for example -### or -v is used + // without a file, target specific options are not + // consumed/validated. + // Instead emitting an error emit a warning instead. + !C.getActions().empty()) { + Diag(diag::err_drv_unsupported_opt_for_target) + << A->getSpelling() << getTargetTriple(); + } else { + Diag(clang::diag::warn_drv_unused_argument) + << A->getAsString(C.getArgs()); + } + } } } } @@ -4259,7 +5172,8 @@ class ToolSelector final { return TC.useIntegratedAs() && !SaveTemps && !C.getArgs().hasArg(options::OPT_via_file_asm) && !C.getArgs().hasArg(options::OPT__SLASH_FA) && - !C.getArgs().hasArg(options::OPT__SLASH_Fa); + !C.getArgs().hasArg(options::OPT__SLASH_Fa) && + !C.getArgs().hasArg(options::OPT_dxc_Fc); } /// Return true if a preprocessor action can be collapsed. @@ -4316,6 +5230,12 @@ class ToolSelector final { if (!T) return nullptr; + // Can't collapse if we don't have codegen support unless we are + // emitting LLVM IR. + bool OutputIsLLVM = types::isLLVMIR(ActionInfo[0].JA->getType()); + if (!T->hasIntegratedBackend() && !(OutputIsLLVM && T->canEmitIR())) + return nullptr; + // When using -fembed-bitcode, it is required to have the same tool (clang) // for both CompilerJA and BackendJA. Otherwise, combine two stages. if (EmbedBitcode) { @@ -4385,6 +5305,12 @@ class ToolSelector final { if (!T) return nullptr; + // Can't collapse if we don't have codegen support unless we are + // emitting LLVM IR. + bool OutputIsLLVM = types::isLLVMIR(ActionInfo[0].JA->getType()); + if (!T->hasIntegratedBackend() && !(OutputIsLLVM && T->canEmitIR())) + return nullptr; + if (T->canEmitIR() && ((SaveTemps && !InputIsBitcode) || EmbedBitcode)) return nullptr; @@ -4501,10 +5427,11 @@ static std::string GetTriplePlusArchString(const ToolChain *TC, return TriplePlusArch; } -InputInfo Driver::BuildJobsForAction( +InputInfoList Driver::BuildJobsForAction( Compilation &C, const Action *A, const ToolChain *TC, StringRef BoundArch, bool AtTopLevel, bool MultipleArchs, const char *LinkingOutput, - std::map<std::pair<const Action *, std::string>, InputInfo> &CachedResults, + std::map<std::pair<const Action *, std::string>, InputInfoList> + &CachedResults, Action::OffloadKind TargetDeviceOffloadKind) const { std::pair<const Action *, std::string> ActionTC = { A, GetTriplePlusArchString(TC, BoundArch, TargetDeviceOffloadKind)}; @@ -4512,17 +5439,49 @@ InputInfo Driver::BuildJobsForAction( if (CachedResult != CachedResults.end()) { return CachedResult->second; } - InputInfo Result = BuildJobsForActionNoCache( + InputInfoList Result = BuildJobsForActionNoCache( C, A, TC, BoundArch, AtTopLevel, MultipleArchs, LinkingOutput, CachedResults, TargetDeviceOffloadKind); CachedResults[ActionTC] = Result; return Result; } -InputInfo Driver::BuildJobsForActionNoCache( +static void handleTimeTrace(Compilation &C, const ArgList &Args, + const JobAction *JA, const char *BaseInput, + const InputInfo &Result) { + Arg *A = + Args.getLastArg(options::OPT_ftime_trace, options::OPT_ftime_trace_EQ); + if (!A) + return; + SmallString<128> Path; + if (A->getOption().matches(options::OPT_ftime_trace_EQ)) { + Path = A->getValue(); + if (llvm::sys::fs::is_directory(Path)) { + SmallString<128> Tmp(Result.getFilename()); + llvm::sys::path::replace_extension(Tmp, "json"); + llvm::sys::path::append(Path, llvm::sys::path::filename(Tmp)); + } + } else { + if (Arg *DumpDir = Args.getLastArgNoClaim(options::OPT_dumpdir)) { + // The trace file is ${dumpdir}${basename}.json. Note that dumpdir may not + // end with a path separator. + Path = DumpDir->getValue(); + Path += llvm::sys::path::filename(BaseInput); + } else { + Path = Result.getFilename(); + } + llvm::sys::path::replace_extension(Path, "json"); + } + const char *ResultFile = C.getArgs().MakeArgString(Path); + C.addTimeTraceFile(ResultFile, JA); + C.addResultFile(ResultFile, JA); +} + +InputInfoList Driver::BuildJobsForActionNoCache( Compilation &C, const Action *A, const ToolChain *TC, StringRef BoundArch, bool AtTopLevel, bool MultipleArchs, const char *LinkingOutput, - std::map<std::pair<const Action *, std::string>, InputInfo> &CachedResults, + std::map<std::pair<const Action *, std::string>, InputInfoList> + &CachedResults, Action::OffloadKind TargetDeviceOffloadKind) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -4553,20 +5512,21 @@ InputInfo Driver::BuildJobsForActionNoCache( // \ // Device Action 1 ---> OffloadAction -> Device Action 2 // - // For a) and b), we just return the job generated for the dependence. For + // For a) and b), we just return the job generated for the dependences. For // c) and d) we override the current action with the host/device dependence // if the current toolchain is host/device and set the offload dependences // info with the jobs obtained from the device/host dependence(s). - // If there is a single device option, just generate the job for it. - if (OA->hasSingleDeviceDependence()) { - InputInfo DevA; + // If there is a single device option or has no host action, just generate + // the job for it. + if (OA->hasSingleDeviceDependence() || !OA->hasHostDependence()) { + InputInfoList DevA; OA->doOnEachDeviceDependence([&](Action *DepA, const ToolChain *DepTC, const char *DepBoundArch) { - DevA = - BuildJobsForAction(C, DepA, DepTC, DepBoundArch, AtTopLevel, - /*MultipleArchs*/ !!DepBoundArch, LinkingOutput, - CachedResults, DepA->getOffloadingDeviceKind()); + DevA.append(BuildJobsForAction(C, DepA, DepTC, DepBoundArch, AtTopLevel, + /*MultipleArchs*/ !!DepBoundArch, + LinkingOutput, CachedResults, + DepA->getOffloadingDeviceKind())); }); return DevA; } @@ -4578,7 +5538,7 @@ InputInfo Driver::BuildJobsForActionNoCache( OA->doOnEachDependence( /*IsHostDependence=*/BuildingForOffloadDevice, [&](Action *DepA, const ToolChain *DepTC, const char *DepBoundArch) { - OffloadDependencesInputInfo.push_back(BuildJobsForAction( + OffloadDependencesInputInfo.append(BuildJobsForAction( C, DepA, DepTC, DepBoundArch, /*AtTopLevel=*/false, /*MultipleArchs*/ !!DepBoundArch, LinkingOutput, CachedResults, DepA->getOffloadingDeviceKind())); @@ -4587,6 +5547,17 @@ InputInfo Driver::BuildJobsForActionNoCache( A = BuildingForOffloadDevice ? OA->getSingleDeviceDependence(/*DoNotConsiderHostActions=*/true) : OA->getHostDependence(); + + // We may have already built this action as a part of the offloading + // toolchain, return the cached input if so. + std::pair<const Action *, std::string> ActionTC = { + OA->getHostDependence(), + GetTriplePlusArchString(TC, BoundArch, TargetDeviceOffloadKind)}; + if (CachedResults.find(ActionTC) != CachedResults.end()) { + InputInfoList Inputs = CachedResults[ActionTC]; + Inputs.append(OffloadDependencesInputInfo); + return Inputs; + } } if (const InputAction *IA = dyn_cast<InputAction>(A)) { @@ -4596,9 +5567,9 @@ InputInfo Driver::BuildJobsForActionNoCache( Input.claim(); if (Input.getOption().matches(options::OPT_INPUT)) { const char *Name = Input.getValue(); - return InputInfo(A, Name, /* _BaseInput = */ Name); + return {InputInfo(A, Name, /* _BaseInput = */ Name)}; } - return InputInfo(A, &Input, /* _BaseInput = */ ""); + return {InputInfo(A, &Input, /* _BaseInput = */ "")}; } if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) { @@ -4628,26 +5599,7 @@ InputInfo Driver::BuildJobsForActionNoCache( const Tool *T = TS.getTool(Inputs, CollapsedOffloadActions); if (!T) - return InputInfo(); - - if (BuildingForOffloadDevice && - A->getOffloadingDeviceKind() == Action::OFK_OpenMP) { - if (TC->getTriple().isAMDGCN()) { - // AMDGCN treats backend and assemble actions as no-op because - // linker does not support object files. - if (const BackendJobAction *BA = dyn_cast<BackendJobAction>(A)) { - return BuildJobsForAction(C, *BA->input_begin(), TC, BoundArch, - AtTopLevel, MultipleArchs, LinkingOutput, - CachedResults, TargetDeviceOffloadKind); - } - - if (const AssembleJobAction *AA = dyn_cast<AssembleJobAction>(A)) { - return BuildJobsForAction(C, *AA->input_begin(), TC, BoundArch, - AtTopLevel, MultipleArchs, LinkingOutput, - CachedResults, TargetDeviceOffloadKind); - } - } - } + return {InputInfo()}; // If we've collapsed action list that contained OffloadAction we // need to build jobs for host/device-side inputs it may have held. @@ -4655,7 +5607,7 @@ InputInfo Driver::BuildJobsForActionNoCache( cast<OffloadAction>(OA)->doOnEachDependence( /*IsHostDependence=*/BuildingForOffloadDevice, [&](Action *DepA, const ToolChain *DepTC, const char *DepBoundArch) { - OffloadDependencesInputInfo.push_back(BuildJobsForAction( + OffloadDependencesInputInfo.append(BuildJobsForAction( C, DepA, DepTC, DepBoundArch, /* AtTopLevel */ false, /*MultipleArchs=*/!!DepBoundArch, LinkingOutput, CachedResults, DepA->getOffloadingDeviceKind())); @@ -4669,23 +5621,25 @@ InputInfo Driver::BuildJobsForActionNoCache( // FIXME: Clean this up. bool SubJobAtTopLevel = AtTopLevel && (isa<DsymutilJobAction>(A) || isa<VerifyJobAction>(A)); - InputInfos.push_back(BuildJobsForAction( + InputInfos.append(BuildJobsForAction( C, Input, TC, BoundArch, SubJobAtTopLevel, MultipleArchs, LinkingOutput, CachedResults, A->getOffloadingDeviceKind())); } - // Always use the first input as the base input. + // Always use the first file input as the base input. const char *BaseInput = InputInfos[0].getBaseInput(); + for (auto &Info : InputInfos) { + if (Info.isFilename()) { + BaseInput = Info.getBaseInput(); + break; + } + } // ... except dsymutil actions, which use their actual input as the base // input. if (JA->getType() == types::TY_dSYM) BaseInput = InputInfos[0].getFilename(); - // ... and in header module compilations, which use the module name. - if (auto *ModuleJA = dyn_cast<HeaderModulePrecompileJobAction>(JA)) - BaseInput = ModuleJA->getModuleName(); - // Append outputs of offload device jobs to the input list if (!OffloadDependencesInputInfo.empty()) InputInfos.append(OffloadDependencesInputInfo.begin(), @@ -4747,8 +5701,8 @@ InputInfo Driver::BuildJobsForActionNoCache( Arch = BoundArch; CachedResults[{A, GetTriplePlusArchString(UI.DependentToolChain, Arch, - UI.DependentOffloadKind)}] = - CurI; + UI.DependentOffloadKind)}] = { + CurI}; } // Now that we have all the results generated, select the one that should be @@ -4757,28 +5711,23 @@ InputInfo Driver::BuildJobsForActionNoCache( A, GetTriplePlusArchString(TC, BoundArch, TargetDeviceOffloadKind)}; assert(CachedResults.find(ActionTC) != CachedResults.end() && "Result does not exist??"); - Result = CachedResults[ActionTC]; + Result = CachedResults[ActionTC].front(); } else if (JA->getType() == types::TY_Nothing) - Result = InputInfo(A, BaseInput); + Result = {InputInfo(A, BaseInput)}; else { // We only have to generate a prefix for the host if this is not a top-level // action. std::string OffloadingPrefix = Action::GetOffloadingFileNamePrefix( A->getOffloadingDeviceKind(), TC->getTriple().normalize(), - /*CreatePrefixForHost=*/!!A->getOffloadingHostActiveKinds() && - !AtTopLevel); - if (isa<OffloadWrapperJobAction>(JA)) { - if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) - BaseInput = FinalOutput->getValue(); - else - BaseInput = getDefaultImageName(); - BaseInput = - C.getArgs().MakeArgString(std::string(BaseInput) + "-wrapper"); - } + /*CreatePrefixForHost=*/isa<OffloadPackagerJobAction>(A) || + !(A->getOffloadingHostActiveKinds() == Action::OFK_None || + AtTopLevel)); Result = InputInfo(A, GetNamedOutputPath(C, *JA, BaseInput, BoundArch, AtTopLevel, MultipleArchs, OffloadingPrefix), BaseInput); + if (T->canEmitIR() && OffloadingPrefix.empty()) + handleTimeTrace(C, Args, JA, BaseInput, Result); } if (CCCPrintBindings && !CCGenDiagnostics) { @@ -4812,7 +5761,7 @@ InputInfo Driver::BuildJobsForActionNoCache( C.getArgsForToolChain(TC, BoundArch, JA->getOffloadingDeviceKind()), LinkingOutput); } - return Result; + return {Result}; } const char *Driver::getDefaultImageName() const { @@ -4864,17 +5813,79 @@ static bool HasPreprocessOutput(const Action &JA) { return false; } +const char *Driver::CreateTempFile(Compilation &C, StringRef Prefix, + StringRef Suffix, bool MultipleArchs, + StringRef BoundArch, + bool NeedUniqueDirectory) const { + SmallString<128> TmpName; + Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir); + std::optional<std::string> CrashDirectory = + CCGenDiagnostics && A + ? std::string(A->getValue()) + : llvm::sys::Process::GetEnv("CLANG_CRASH_DIAGNOSTICS_DIR"); + if (CrashDirectory) { + if (!getVFS().exists(*CrashDirectory)) + llvm::sys::fs::create_directories(*CrashDirectory); + SmallString<128> Path(*CrashDirectory); + llvm::sys::path::append(Path, Prefix); + const char *Middle = !Suffix.empty() ? "-%%%%%%." : "-%%%%%%"; + if (std::error_code EC = + llvm::sys::fs::createUniqueFile(Path + Middle + Suffix, TmpName)) { + Diag(clang::diag::err_unable_to_make_temp) << EC.message(); + return ""; + } + } else { + if (MultipleArchs && !BoundArch.empty()) { + if (NeedUniqueDirectory) { + TmpName = GetTemporaryDirectory(Prefix); + llvm::sys::path::append(TmpName, + Twine(Prefix) + "-" + BoundArch + "." + Suffix); + } else { + TmpName = + GetTemporaryPath((Twine(Prefix) + "-" + BoundArch).str(), Suffix); + } + + } else { + TmpName = GetTemporaryPath(Prefix, Suffix); + } + } + return C.addTempFile(C.getArgs().MakeArgString(TmpName)); +} + +// Calculate the output path of the module file when compiling a module unit +// with the `-fmodule-output` option or `-fmodule-output=` option specified. +// The behavior is: +// - If `-fmodule-output=` is specfied, then the module file is +// writing to the value. +// - Otherwise if the output object file of the module unit is specified, the +// output path +// of the module file should be the same with the output object file except +// the corresponding suffix. This requires both `-o` and `-c` are specified. +// - Otherwise, the output path of the module file will be the same with the +// input with the corresponding suffix. +static const char *GetModuleOutputPath(Compilation &C, const JobAction &JA, + const char *BaseInput) { + assert(isa<PrecompileJobAction>(JA) && JA.getType() == types::TY_ModuleFile && + (C.getArgs().hasArg(options::OPT_fmodule_output) || + C.getArgs().hasArg(options::OPT_fmodule_output_EQ))); + + SmallString<256> OutputPath = + tools::getCXX20NamedModuleOutputPath(C.getArgs(), BaseInput); + + return C.addResultFile(C.getArgs().MakeArgString(OutputPath.c_str()), &JA); +} + const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, StringRef OrigBoundArch, bool AtTopLevel, bool MultipleArchs, StringRef OffloadingPrefix) const { std::string BoundArch = OrigBoundArch.str(); -#if defined(_WIN32) - // BoundArch may contains ':', which is invalid in file names on Windows, - // therefore replace it with '%'. - std::replace(BoundArch.begin(), BoundArch.end(), ':', '@'); -#endif + if (is_style_windows(llvm::sys::path::Style::native)) { + // BoundArch may contains ':', which is invalid in file names on Windows, + // therefore replace it with '%'. + std::replace(BoundArch.begin(), BoundArch.end(), ':', '@'); + } llvm::PrettyStackTraceString CrashInfo("Computing output path"); // Output to a user requested destination? @@ -4905,6 +5916,22 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, return "-"; } + if (JA.getType() == types::TY_PP_Asm && + C.getArgs().hasArg(options::OPT_dxc_Fc)) { + StringRef FcValue = C.getArgs().getLastArgValue(options::OPT_dxc_Fc); + // TODO: Should we use `MakeCLOutputFilename` here? If so, we can probably + // handle this as part of the SLASH_Fa handling below. + return C.addResultFile(C.getArgs().MakeArgString(FcValue.str()), &JA); + } + + if (JA.getType() == types::TY_Object && + C.getArgs().hasArg(options::OPT_dxc_Fo)) { + StringRef FoValue = C.getArgs().getLastArgValue(options::OPT_dxc_Fo); + // TODO: Should we use `MakeCLOutputFilename` here? If so, we can probably + // handle this as part of the SLASH_Fo handling below. + return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA); + } + // Is this the assembly listing for /FA? if (JA.getType() == types::TY_PP_Asm && (C.getArgs().hasArg(options::OPT__SLASH_FA) || @@ -4917,31 +5944,49 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, &JA); } + if (JA.getType() == types::TY_API_INFO && + C.getArgs().hasArg(options::OPT_emit_extension_symbol_graphs) && + C.getArgs().hasArg(options::OPT_o)) + Diag(clang::diag::err_drv_unexpected_symbol_graph_output) + << C.getArgs().getLastArgValue(options::OPT_o); + + // DXC defaults to standard out when generating assembly. We check this after + // any DXC flags that might specify a file. + if (AtTopLevel && JA.getType() == types::TY_PP_Asm && IsDXCMode()) + return "-"; + + bool SpecifiedModuleOutput = + C.getArgs().hasArg(options::OPT_fmodule_output) || + C.getArgs().hasArg(options::OPT_fmodule_output_EQ); + if (MultipleArchs && SpecifiedModuleOutput) + Diag(clang::diag::err_drv_module_output_with_multiple_arch); + + // If we're emitting a module output with the specified option + // `-fmodule-output`. + if (!AtTopLevel && isa<PrecompileJobAction>(JA) && + JA.getType() == types::TY_ModuleFile && SpecifiedModuleOutput) { + assert(!C.getArgs().hasArg(options::OPT_modules_reduced_bmi)); + return GetModuleOutputPath(C, JA, BaseInput); + } + // Output to a temporary file? if ((!AtTopLevel && !isSaveTempsEnabled() && !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); std::pair<StringRef, StringRef> Split = Name.split('.'); - SmallString<128> TmpName; - const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); - Arg *A = C.getArgs().getLastArg(options::OPT_fcrash_diagnostics_dir); - if (CCGenDiagnostics && A) { - SmallString<128> CrashDirectory(A->getValue()); - if (!getVFS().exists(CrashDirectory)) - llvm::sys::fs::create_directories(CrashDirectory); - llvm::sys::path::append(CrashDirectory, Split.first); - const char *Middle = Suffix ? "-%%%%%%." : "-%%%%%%"; - std::error_code EC = llvm::sys::fs::createUniqueFile( - CrashDirectory + Middle + Suffix, TmpName); - if (EC) { - Diag(clang::diag::err_unable_to_make_temp) << EC.message(); - return ""; - } - } else { - TmpName = GetTemporaryPath(Split.first, Suffix); - } - return C.addTempFile(C.getArgs().MakeArgString(TmpName)); + const char *Suffix = + types::getTypeTempSuffix(JA.getType(), IsCLMode() || IsDXCMode()); + // The non-offloading toolchain on Darwin requires deterministic input + // file name for binaries to be deterministic, therefore it needs unique + // directory. + llvm::Triple Triple(C.getDriver().getTargetTriple()); + bool NeedUniqueDirectory = + (JA.getOffloadingDeviceKind() == Action::OFK_None || + JA.getOffloadingDeviceKind() == Action::OFK_Host) && + Triple.isOSDarwin(); + return CreateTempFile(C, Split.first, Suffix, MultipleArchs, BoundArch, + NeedUniqueDirectory); } SmallString<128> BasePath(BaseInput); @@ -4997,7 +6042,8 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, bool IsHIPNoRDC = JA.getOffloadingDeviceKind() == Action::OFK_HIP && !C.getArgs().hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false); - if (IsHIPNoRDC) { + bool UseOutExtension = IsHIPNoRDC || isa<OffloadPackagerJobAction>(JA); + if (UseOutExtension) { Output = BaseName; llvm::sys::path::replace_extension(Output, ""); } @@ -5006,14 +6052,23 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, Output += "-"; Output.append(BoundArch); } - if (IsHIPNoRDC) + if (UseOutExtension) Output += ".out"; NamedOutput = C.getArgs().MakeArgString(Output.c_str()); } } else if (JA.getType() == types::TY_PCH && IsCLMode()) { NamedOutput = C.getArgs().MakeArgString(GetClPchPath(C, BaseName)); + } else if ((JA.getType() == types::TY_Plist || JA.getType() == types::TY_AST) && + C.getArgs().hasArg(options::OPT__SLASH_o)) { + StringRef Val = + C.getArgs() + .getLastArg(options::OPT__SLASH_o) + ->getValue(); + NamedOutput = + MakeCLOutputFilename(C.getArgs(), Val, BaseName, types::TY_Object); } else { - const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); + const char *Suffix = + types::getTypeTempSuffix(JA.getType(), IsCLMode() || IsDXCMode()); assert(Suffix && "All types used for output should have a suffix."); std::string::size_type End = std::string::npos; @@ -5028,19 +6083,22 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, // When using both -save-temps and -emit-llvm, use a ".tmp.bc" suffix for // the unoptimized bitcode so that it does not get overwritten by the ".bc" // optimized bitcode output. - auto IsHIPRDCInCompilePhase = [](const JobAction &JA, + auto IsAMDRDCInCompilePhase = [](const JobAction &JA, const llvm::opt::DerivedArgList &Args) { - // The relocatable compilation in HIP implies -emit-llvm. Similarly, use a - // ".tmp.bc" suffix for the unoptimized bitcode (generated in the compile - // phase.) + // The relocatable compilation in HIP and OpenMP implies -emit-llvm. + // Similarly, use a ".tmp.bc" suffix for the unoptimized bitcode + // (generated in the compile phase.) + const ToolChain *TC = JA.getOffloadingToolChain(); return isa<CompileJobAction>(JA) && - JA.getOffloadingDeviceKind() == Action::OFK_HIP && - Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, - false); + ((JA.getOffloadingDeviceKind() == Action::OFK_HIP && + Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, + false)) || + (JA.getOffloadingDeviceKind() == Action::OFK_OpenMP && TC && + TC->getTriple().isAMDGPU())); }; if (!AtTopLevel && JA.getType() == types::TY_LLVM_BC && (C.getArgs().hasArg(options::OPT_emit_llvm) || - IsHIPRDCInCompilePhase(JA, C.getArgs()))) + IsAMDRDCInCompilePhase(JA, C.getArgs()))) Suffixed += ".tmp"; Suffixed += '.'; Suffixed += Suffix; @@ -5071,7 +6129,8 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, StringRef Name = llvm::sys::path::filename(BaseInput); std::pair<StringRef, StringRef> Split = Name.split('.'); std::string TmpName = GetTemporaryPath( - Split.first, types::getTypeTempSuffix(JA.getType(), IsCLMode())); + Split.first, + types::getTypeTempSuffix(JA.getType(), IsCLMode() || IsDXCMode())); return C.addTempFile(C.getArgs().MakeArgString(TmpName)); } } @@ -5084,15 +6143,15 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, else llvm::sys::path::append(BasePath, NamedOutput); return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str()), &JA); - } else { - return C.addResultFile(NamedOutput, &JA); } + + return C.addResultFile(NamedOutput, &JA); } std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { // Search for Name in a list of paths. auto SearchPaths = [&](const llvm::SmallVectorImpl<std::string> &P) - -> llvm::Optional<std::string> { + -> std::optional<std::string> { // Respect a limited subset of the '-Bprefix' functionality in GCC by // attempting to use this prefix when looking for file paths. for (const auto &Dir : P) { @@ -5103,7 +6162,7 @@ std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { if (llvm::sys::fs::exists(Twine(P))) return std::string(P); } - return None; + return std::nullopt; }; if (auto P = SearchPaths(PrefixDirs)) @@ -5112,17 +6171,17 @@ std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { SmallString<128> R(ResourceDir); llvm::sys::path::append(R, Name); if (llvm::sys::fs::exists(Twine(R))) - return std::string(R.str()); + return std::string(R); SmallString<128> P(TC.getCompilerRTPath()); llvm::sys::path::append(P, Name); if (llvm::sys::fs::exists(Twine(P))) - return std::string(P.str()); + return std::string(P); SmallString<128> D(Dir); llvm::sys::path::append(D, "..", Name); if (llvm::sys::fs::exists(Twine(D))) - return std::string(D.str()); + return std::string(D); if (auto P = SearchPaths(TC.getLibraryPaths())) return *P; @@ -5130,6 +6189,11 @@ std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { if (auto P = SearchPaths(TC.getFilePaths())) return *P; + SmallString<128> R2(ResourceDir); + llvm::sys::path::append(R2, "..", "..", Name); + if (llvm::sys::fs::exists(Twine(R2))) + return std::string(R2); + return std::string(Name); } @@ -5159,11 +6223,11 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const { if (llvm::sys::fs::is_directory(PrefixDir)) { SmallString<128> P(PrefixDir); if (ScanDirForExecutable(P, Name)) - return std::string(P.str()); + return std::string(P); } else { SmallString<128> P((PrefixDir + Name).str()); if (llvm::sys::fs::can_execute(Twine(P))) - return std::string(P.str()); + return std::string(P); } } @@ -5179,7 +6243,7 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const { for (const auto &Path : List) { SmallString<128> P(Path); if (ScanDirForExecutable(P, TargetSpecificExecutable)) - return std::string(P.str()); + return std::string(P); } // Fall back to the path @@ -5191,6 +6255,51 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const { return std::string(Name); } +std::string Driver::GetStdModuleManifestPath(const Compilation &C, + const ToolChain &TC) const { + std::string error = "<NOT PRESENT>"; + + switch (TC.GetCXXStdlibType(C.getArgs())) { + case ToolChain::CST_Libcxx: { + auto evaluate = [&](const char *library) -> std::optional<std::string> { + std::string lib = GetFilePath(library, TC); + + // Note when there are multiple flavours of libc++ the module json needs + // to look at the command-line arguments for the proper json. These + // flavours do not exist at the moment, but there are plans to provide a + // variant that is built with sanitizer instrumentation enabled. + + // For example + // StringRef modules = [&] { + // const SanitizerArgs &Sanitize = TC.getSanitizerArgs(C.getArgs()); + // if (Sanitize.needsAsanRt()) + // return "libc++.modules-asan.json"; + // return "libc++.modules.json"; + // }(); + + SmallString<128> path(lib.begin(), lib.end()); + llvm::sys::path::remove_filename(path); + llvm::sys::path::append(path, "libc++.modules.json"); + if (TC.getVFS().exists(path)) + return static_cast<std::string>(path); + + return {}; + }; + + if (std::optional<std::string> result = evaluate("libc++.so"); result) + return *result; + + return evaluate("libc++.a").value_or(error); + } + + case ToolChain::CST_Libstdcxx: + // libstdc++ does not provide Standard library modules yet. + return error; + } + + return error; +} + std::string Driver::GetTemporaryPath(StringRef Prefix, StringRef Suffix) const { SmallString<128> Path; std::error_code EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, Path); @@ -5199,7 +6308,7 @@ std::string Driver::GetTemporaryPath(StringRef Prefix, StringRef Suffix) const { return ""; } - return std::string(Path.str()); + return std::string(Path); } std::string Driver::GetTemporaryDirectory(StringRef Prefix) const { @@ -5210,7 +6319,7 @@ std::string Driver::GetTemporaryDirectory(StringRef Prefix) const { return ""; } - return std::string(Path.str()); + return std::string(Path); } std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const { @@ -5232,7 +6341,7 @@ std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const { Output = BaseName; llvm::sys::path::replace_extension(Output, ".pch"); } - return std::string(Output.str()); + return std::string(Output); } const ToolChain &Driver::getToolChain(const ArgList &Args, @@ -5247,17 +6356,13 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::Haiku: TC = std::make_unique<toolchains::Haiku>(*this, Target, Args); break; - case llvm::Triple::Ananas: - TC = std::make_unique<toolchains::Ananas>(*this, Target, Args); - break; - case llvm::Triple::CloudABI: - TC = std::make_unique<toolchains::CloudABI>(*this, Target, Args); - break; case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: case llvm::Triple::WatchOS: + case llvm::Triple::XROS: + case llvm::Triple::DriverKit: TC = std::make_unique<toolchains::DarwinClang>(*this, Target, Args); break; case llvm::Triple::DragonFly: @@ -5270,10 +6375,11 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, TC = std::make_unique<toolchains::NetBSD>(*this, Target, Args); break; case llvm::Triple::FreeBSD: - TC = std::make_unique<toolchains::FreeBSD>(*this, Target, Args); - break; - case llvm::Triple::Minix: - TC = std::make_unique<toolchains::Minix>(*this, Target, Args); + if (Target.isPPC()) + TC = std::make_unique<toolchains::PPCFreeBSDToolChain>(*this, Target, + Args); + else + TC = std::make_unique<toolchains::FreeBSD>(*this, Target, Args); break; case llvm::Triple::Linux: case llvm::Triple::ELFIAMCU: @@ -5289,7 +6395,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, Args); else if (Target.getArch() == llvm::Triple::ve) TC = std::make_unique<toolchains::VEToolChain>(*this, Target, Args); - + else if (Target.isOHOSFamily()) + TC = std::make_unique<toolchains::OHOS>(*this, Target, Args); else TC = std::make_unique<toolchains::Linux>(*this, Target, Args); break; @@ -5302,6 +6409,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::Solaris: TC = std::make_unique<toolchains::Solaris>(*this, Target, Args); break; + case llvm::Triple::CUDA: + TC = std::make_unique<toolchains::NVPTXToolChain>(*this, Target, Args); + break; case llvm::Triple::AMDHSA: TC = std::make_unique<toolchains::ROCMToolChain>(*this, Target, Args); break; @@ -5329,7 +6439,7 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::MSVC: case llvm::Triple::UnknownEnvironment: if (Args.getLastArgValue(options::OPT_fuse_ld_EQ) - .startswith_insensitive("bfd")) + .starts_with_insensitive("bfd")) TC = std::make_unique<toolchains::CrossWindowsToolChain>( *this, Target, Args); else @@ -5341,15 +6451,21 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::PS4: TC = std::make_unique<toolchains::PS4CPU>(*this, Target, Args); break; - case llvm::Triple::Contiki: - TC = std::make_unique<toolchains::Contiki>(*this, Target, Args); + case llvm::Triple::PS5: + TC = std::make_unique<toolchains::PS5CPU>(*this, Target, Args); break; case llvm::Triple::Hurd: TC = std::make_unique<toolchains::Hurd>(*this, Target, Args); break; + case llvm::Triple::LiteOS: + TC = std::make_unique<toolchains::OHOS>(*this, Target, Args); + break; case llvm::Triple::ZOS: TC = std::make_unique<toolchains::ZOS>(*this, Target, Args); break; + case llvm::Triple::ShaderModel: + TC = std::make_unique<toolchains::HLSLToolChain>(*this, Target, Args); + break; default: // Of these targets, Hexagon is the only one that might have // an OS of Linux, in which case it got handled above already. @@ -5392,11 +6508,15 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::ve: TC = std::make_unique<toolchains::VEToolChain>(*this, Target, Args); break; + case llvm::Triple::spirv32: + case llvm::Triple::spirv64: + TC = std::make_unique<toolchains::SPIRVToolChain>(*this, Target, Args); + break; + case llvm::Triple::csky: + TC = std::make_unique<toolchains::CSKYToolChain>(*this, Target, Args); + break; default: - if (Target.getVendor() == llvm::Triple::Myriad) - TC = std::make_unique<toolchains::MyriadToolChain>(*this, Target, - Args); - else if (toolchains::BareMetal::handlesTarget(Target)) + if (toolchains::BareMetal::handlesTarget(Target)) TC = std::make_unique<toolchains::BareMetal>(*this, Target, Args); else if (Target.isOSBinFormatELF()) TC = std::make_unique<toolchains::Generic_ELF>(*this, Target, Args); @@ -5408,10 +6528,39 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, } } - // Intentionally omitted from the switch above: llvm::Triple::CUDA. CUDA - // compiles always need two toolchains, the CUDA toolchain and the host - // toolchain. So the only valid way to create a CUDA toolchain is via - // CreateOffloadingDeviceToolChains. + return *TC; +} + +const ToolChain &Driver::getOffloadingDeviceToolChain( + const ArgList &Args, const llvm::Triple &Target, const ToolChain &HostTC, + const Action::OffloadKind &TargetDeviceOffloadKind) const { + // Use device / host triples as the key into the ToolChains map because the + // device ToolChain we create depends on both. + auto &TC = ToolChains[Target.str() + "/" + HostTC.getTriple().str()]; + if (!TC) { + // Categorized by offload kind > arch rather than OS > arch like + // the normal getToolChain call, as it seems a reasonable way to categorize + // things. + switch (TargetDeviceOffloadKind) { + case Action::OFK_HIP: { + if (((Target.getArch() == llvm::Triple::amdgcn || + Target.getArch() == llvm::Triple::spirv64) && + Target.getVendor() == llvm::Triple::AMD && + Target.getOS() == llvm::Triple::AMDHSA) || + !Args.hasArgNoClaim(options::OPT_offload_EQ)) + TC = std::make_unique<toolchains::HIPAMDToolChain>(*this, Target, + HostTC, Args); + else if (Target.getArch() == llvm::Triple::spirv64 && + Target.getVendor() == llvm::Triple::UnknownVendor && + Target.getOS() == llvm::Triple::UnknownOS) + TC = std::make_unique<toolchains::HIPSPVToolChain>(*this, Target, + HostTC, Args); + break; + } + default: + break; + } + } return *TC; } @@ -5424,7 +6573,8 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const { // And say "no" if this is not a kind of action clang understands. if (!isa<PreprocessJobAction>(JA) && !isa<PrecompileJobAction>(JA) && - !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA)) + !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA) && + !isa<ExtractAPIJobAction>(JA)) return false; return true; @@ -5433,11 +6583,12 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const { bool Driver::ShouldUseFlangCompiler(const JobAction &JA) const { // Say "no" if there is not exactly one input of a type flang understands. if (JA.size() != 1 || - !types::isFortran((*JA.input_begin())->getType())) + !types::isAcceptedByFlang((*JA.input_begin())->getType())) return false; // And say "no" if this is not a kind of action flang understands. - if (!isa<PreprocessJobAction>(JA) && !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA)) + if (!isa<PreprocessJobAction>(JA) && !isa<CompileJobAction>(JA) && + !isa<BackendJobAction>(JA)) return false; return true; @@ -5467,18 +6618,15 @@ bool Driver::GetReleaseVersion(StringRef Str, unsigned &Major, unsigned &Minor, return false; if (Str.empty()) return true; - if (Str[0] != '.') + if (!Str.consume_front(".")) return false; - Str = Str.drop_front(1); - if (Str.consumeInteger(10, Minor)) return false; if (Str.empty()) return true; - if (Str[0] != '.') + if (!Str.consume_front(".")) return false; - Str = Str.drop_front(1); if (Str.consumeInteger(10, Micro)) return false; @@ -5506,9 +6654,8 @@ bool Driver::GetReleaseVersion(StringRef Str, Digits[CurDigit] = Digit; if (Str.empty()) return true; - if (Str[0] != '.') + if (!Str.consume_front(".")) return false; - Str = Str.drop_front(1); CurDigit++; } @@ -5516,20 +6663,37 @@ bool Driver::GetReleaseVersion(StringRef Str, return false; } -std::pair<unsigned, unsigned> -Driver::getIncludeExcludeOptionFlagMasks(bool IsClCompatMode) const { - unsigned IncludedFlagsBitmask = 0; - unsigned ExcludedFlagsBitmask = options::NoDriverOption; - - if (IsClCompatMode) { - // Include CL and Core options. - IncludedFlagsBitmask |= options::CLOption; - IncludedFlagsBitmask |= options::CoreOption; - } else { - ExcludedFlagsBitmask |= options::CLOption; - } +llvm::opt::Visibility +Driver::getOptionVisibilityMask(bool UseDriverMode) const { + if (!UseDriverMode) + return llvm::opt::Visibility(options::ClangOption); + if (IsCLMode()) + return llvm::opt::Visibility(options::CLOption); + if (IsDXCMode()) + return llvm::opt::Visibility(options::DXCOption); + if (IsFlangMode()) { + return llvm::opt::Visibility(options::FlangOption); + } + return llvm::opt::Visibility(options::ClangOption); +} - return std::make_pair(IncludedFlagsBitmask, ExcludedFlagsBitmask); +const char *Driver::getExecutableForDriverMode(DriverMode Mode) { + switch (Mode) { + case GCCMode: + return "clang"; + case GXXMode: + return "clang++"; + case CPPMode: + return "clang-cpp"; + case CLMode: + return "clang-cl"; + case FlangMode: + return "flang"; + case DXCMode: + return "clang-dxc"; + } + + llvm_unreachable("Unhandled Mode"); } bool clang::driver::isOptimizationLevelFast(const ArgList &Args) { @@ -5561,11 +6725,11 @@ bool clang::driver::willEmitRemarks(const ArgList &Args) { llvm::StringRef clang::driver::getDriverMode(StringRef ProgName, ArrayRef<const char *> Args) { - static const std::string OptName = + static StringRef OptName = getDriverOptTable().getOption(options::OPT_driver_mode).getPrefixedName(); llvm::StringRef Opt; for (StringRef Arg : Args) { - if (!Arg.startswith(OptName)) + if (!Arg.starts_with(OptName)) continue; Opt = Arg; } @@ -5574,4 +6738,187 @@ llvm::StringRef clang::driver::getDriverMode(StringRef ProgName, return Opt.consume_front(OptName) ? Opt : ""; } -bool driver::IsClangCL(StringRef DriverMode) { return DriverMode.equals("cl"); } +bool driver::IsClangCL(StringRef DriverMode) { return DriverMode == "cl"; } + +llvm::Error driver::expandResponseFiles(SmallVectorImpl<const char *> &Args, + bool ClangCLMode, + llvm::BumpPtrAllocator &Alloc, + llvm::vfs::FileSystem *FS) { + // Parse response files using the GNU syntax, unless we're in CL mode. There + // are two ways to put clang in CL compatibility mode: ProgName is either + // clang-cl or cl, or --driver-mode=cl is on the command line. The normal + // command line parsing can't happen until after response file parsing, so we + // have to manually search for a --driver-mode=cl argument the hard way. + // Finally, our -cc1 tools don't care which tokenization mode we use because + // response files written by clang will tokenize the same way in either mode. + enum { Default, POSIX, Windows } RSPQuoting = Default; + for (const char *F : Args) { + if (strcmp(F, "--rsp-quoting=posix") == 0) + RSPQuoting = POSIX; + else if (strcmp(F, "--rsp-quoting=windows") == 0) + RSPQuoting = Windows; + } + + // Determines whether we want nullptr markers in Args to indicate response + // files end-of-lines. We only use this for the /LINK driver argument with + // clang-cl.exe on Windows. + bool MarkEOLs = ClangCLMode; + + llvm::cl::TokenizerCallback Tokenizer; + if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode)) + Tokenizer = &llvm::cl::TokenizeWindowsCommandLine; + else + Tokenizer = &llvm::cl::TokenizeGNUCommandLine; + + if (MarkEOLs && Args.size() > 1 && StringRef(Args[1]).starts_with("-cc1")) + MarkEOLs = false; + + llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer); + ECtx.setMarkEOLs(MarkEOLs); + if (FS) + ECtx.setVFS(FS); + + if (llvm::Error Err = ECtx.expandResponseFiles(Args)) + return Err; + + // If -cc1 came from a response file, remove the EOL sentinels. + auto FirstArg = llvm::find_if(llvm::drop_begin(Args), + [](const char *A) { return A != nullptr; }); + if (FirstArg != Args.end() && StringRef(*FirstArg).starts_with("-cc1")) { + // If -cc1 came from a response file, remove the EOL sentinels. + if (MarkEOLs) { + auto newEnd = std::remove(Args.begin(), Args.end(), nullptr); + Args.resize(newEnd - Args.begin()); + } + } + + return llvm::Error::success(); +} + +static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) { + return SavedStrings.insert(S).first->getKeyData(); +} + +/// Apply a list of edits to the input argument lists. +/// +/// The input string is a space separated list of edits to perform, +/// they are applied in order to the input argument lists. Edits +/// should be one of the following forms: +/// +/// '#': Silence information about the changes to the command line arguments. +/// +/// '^': Add FOO as a new argument at the beginning of the command line. +/// +/// '+': Add FOO as a new argument at the end of the command line. +/// +/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command +/// line. +/// +/// 'xOPTION': Removes all instances of the literal argument OPTION. +/// +/// 'XOPTION': Removes all instances of the literal argument OPTION, +/// and the following argument. +/// +/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' +/// at the end of the command line. +/// +/// \param OS - The stream to write edit information to. +/// \param Args - The vector of command line arguments. +/// \param Edit - The override command to perform. +/// \param SavedStrings - Set to use for storing string representations. +static void applyOneOverrideOption(raw_ostream &OS, + SmallVectorImpl<const char *> &Args, + StringRef Edit, + llvm::StringSet<> &SavedStrings) { + // This does not need to be efficient. + + if (Edit[0] == '^') { + const char *Str = GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.ends_with("/") && + Edit.slice(2, Edit.size() - 1).contains('/')) { + StringRef MatchPattern = Edit.substr(2).split('/').first; + StringRef ReplPattern = Edit.substr(2).split('/').second; + ReplPattern = ReplPattern.slice(0, ReplPattern.size() - 1); + + for (unsigned i = 1, e = Args.size(); i != e; ++i) { + // Ignore end-of-line response file markers + if (Args[i] == nullptr) + continue; + std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]); + + if (Repl != Args[i]) { + OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n"; + Args[i] = GetStableCStr(SavedStrings, Repl); + } + } + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + auto Option = Edit.substr(1); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + OS << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + // Ignore end-of-line response file markers + if (A == nullptr) + continue; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + OS << "### Adding argument " << Edit << " at end\n"; + Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str())); + } else { + OS << "### Unrecognized edit: " << Edit << "\n"; + } +} + +void driver::applyOverrideOptions(SmallVectorImpl<const char *> &Args, + const char *OverrideStr, + llvm::StringSet<> &SavedStrings, + raw_ostream *OS) { + if (!OS) + OS = &llvm::nulls(); + + if (OverrideStr[0] == '#') { + ++OverrideStr; + OS = &llvm::nulls(); + } + + *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n"; + + // This does not need to be efficient. + + const char *S = OverrideStr; + while (*S) { + const char *End = ::strchr(S, ' '); + if (!End) + End = S + strlen(S); + if (End != S) + applyOneOverrideOption(*OS, Args, std::string(S, End), SavedStrings); + S = End; + if (*S != '\0') + ++S; + } +} |