aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Driver/Driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Driver/Driver.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Driver/Driver.cpp3365
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;
+ }
+}