summaryrefslogtreecommitdiff
path: root/lib/Driver/ToolChains/CommonArgs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Driver/ToolChains/CommonArgs.cpp')
-rw-r--r--lib/Driver/ToolChains/CommonArgs.cpp344
1 files changed, 300 insertions, 44 deletions
diff --git a/lib/Driver/ToolChains/CommonArgs.cpp b/lib/Driver/ToolChains/CommonArgs.cpp
index f26880123d8c2..1e093b25b909f 100644
--- a/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/lib/Driver/ToolChains/CommonArgs.cpp
@@ -8,14 +8,14 @@
//===----------------------------------------------------------------------===//
#include "CommonArgs.h"
-#include "InputInfo.h"
-#include "Hexagon.h"
#include "Arch/AArch64.h"
#include "Arch/ARM.h"
#include "Arch/Mips.h"
#include "Arch/PPC.h"
#include "Arch/SystemZ.h"
#include "Arch/X86.h"
+#include "Hexagon.h"
+#include "InputInfo.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/ObjCRuntime.h"
@@ -31,6 +31,7 @@
#include "clang/Driver/SanitizerArgs.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Driver/Util.h"
+#include "clang/Driver/XRayArgs.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
@@ -41,6 +42,7 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compression.h"
+#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
@@ -144,12 +146,14 @@ void tools::AddLinkerInputs(const ToolChain &TC, const InputInfoList &Inputs,
Args.AddAllArgValues(CmdArgs, options::OPT_Zlinker_input);
for (const auto &II : Inputs) {
- // If the current tool chain refers to an OpenMP offloading host, we should
- // ignore inputs that refer to OpenMP offloading devices - they will be
- // embedded according to a proper linker script.
+ // If the current tool chain refers to an OpenMP or HIP offloading host, we
+ // should ignore inputs that refer to OpenMP or HIP offloading devices -
+ // they will be embedded according to a proper linker script.
if (auto *IA = II.getAction())
- if (JA.isHostOffloading(Action::OFK_OpenMP) &&
- IA->isDeviceOffloading(Action::OFK_OpenMP))
+ if ((JA.isHostOffloading(Action::OFK_OpenMP) &&
+ IA->isDeviceOffloading(Action::OFK_OpenMP)) ||
+ (JA.isHostOffloading(Action::OFK_HIP) &&
+ IA->isDeviceOffloading(Action::OFK_HIP)))
continue;
if (!TC.HasNativeLLVMSupport() && types::isLLVMIR(II.getType()))
@@ -363,23 +367,20 @@ unsigned tools::getLTOParallelism(const ArgList &Args, const Driver &D) {
return Parallelism;
}
-// CloudABI and WebAssembly use -ffunction-sections and -fdata-sections by
-// default.
+// CloudABI uses -ffunction-sections and -fdata-sections by default.
bool tools::isUseSeparateSections(const llvm::Triple &Triple) {
- return Triple.getOS() == llvm::Triple::CloudABI ||
- Triple.getArch() == llvm::Triple::wasm32 ||
- Triple.getArch() == llvm::Triple::wasm64;
+ return Triple.getOS() == llvm::Triple::CloudABI;
}
void tools::AddGoldPlugin(const ToolChain &ToolChain, const ArgList &Args,
- ArgStringList &CmdArgs, bool IsThinLTO,
- const Driver &D) {
+ ArgStringList &CmdArgs, const InputInfo &Output,
+ const InputInfo &Input, bool IsThinLTO) {
// Tell the linker to load the plugin. This has to come before AddLinkerInputs
// as gold requires -plugin to come before any -plugin-opt that -Wl might
// forward.
CmdArgs.push_back("-plugin");
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
const char *Suffix = ".dll";
#elif defined(__APPLE__)
const char *Suffix = ".dylib";
@@ -415,10 +416,16 @@ void tools::AddGoldPlugin(const ToolChain &ToolChain, const ArgList &Args,
CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=O") + OOpt));
}
+ if (Args.hasArg(options::OPT_gsplit_dwarf)) {
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-plugin-opt=dwo_dir=") +
+ Output.getFilename() + "_dwo"));
+ }
+
if (IsThinLTO)
CmdArgs.push_back("-plugin-opt=thinlto");
- if (unsigned Parallelism = getLTOParallelism(Args, D))
+ if (unsigned Parallelism = getLTOParallelism(Args, ToolChain.getDriver()))
CmdArgs.push_back(
Args.MakeArgString("-plugin-opt=jobs=" + Twine(Parallelism)));
@@ -449,7 +456,7 @@ void tools::AddGoldPlugin(const ToolChain &ToolChain, const ArgList &Args,
if (Arg *A = getLastProfileSampleUseArg(Args)) {
StringRef FName = A->getValue();
if (!llvm::sys::fs::exists(FName))
- D.Diag(diag::err_drv_no_such_file) << FName;
+ ToolChain.getDriver().Diag(diag::err_drv_no_such_file) << FName;
else
CmdArgs.push_back(
Args.MakeArgString(Twine("-plugin-opt=sample-profile=") + FName));
@@ -458,14 +465,24 @@ void tools::AddGoldPlugin(const ToolChain &ToolChain, const ArgList &Args,
// Need this flag to turn on new pass manager via Gold plugin.
if (Args.hasFlag(options::OPT_fexperimental_new_pass_manager,
options::OPT_fno_experimental_new_pass_manager,
- /* Default */ false)) {
+ /* Default */ ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER)) {
CmdArgs.push_back("-plugin-opt=new-pass-manager");
}
+ // Setup statistics file output.
+ SmallString<128> StatsFile =
+ getStatsFileName(Args, Output, Input, ToolChain.getDriver());
+ if (!StatsFile.empty())
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-plugin-opt=stats-file=") + StatsFile));
}
void tools::addArchSpecificRPath(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
+ if (!Args.hasFlag(options::OPT_frtlib_add_rpath,
+ options::OPT_fno_rtlib_add_rpath, false))
+ return;
+
std::string CandidateRPath = TC.getArchSpecificLibPath();
if (TC.getVFS().exists(CandidateRPath)) {
CmdArgs.push_back("-rpath");
@@ -511,9 +528,9 @@ static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args,
bool IsShared, bool IsWhole) {
// Wrap any static runtimes that must be forced into executable in
// whole-archive.
- if (IsWhole) CmdArgs.push_back("-whole-archive");
+ if (IsWhole) CmdArgs.push_back("--whole-archive");
CmdArgs.push_back(TC.getCompilerRTArgString(Args, Sanitizer, IsShared));
- if (IsWhole) CmdArgs.push_back("-no-whole-archive");
+ if (IsWhole) CmdArgs.push_back("--no-whole-archive");
if (IsShared) {
addArchSpecificRPath(TC, Args, CmdArgs);
@@ -525,6 +542,15 @@ static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args,
static bool addSanitizerDynamicList(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs,
StringRef Sanitizer) {
+ // Solaris ld defaults to --export-dynamic behaviour but doesn't support
+ // the option, so don't try to pass it.
+ if (TC.getTriple().getOS() == llvm::Triple::Solaris)
+ return true;
+ // Myriad is static linking only. Furthermore, some versions of its
+ // linker have the bug where --export-dynamic overrides -static, so
+ // don't use --export-dynamic on that platform.
+ if (TC.getTriple().getVendor() == llvm::Triple::Myriad)
+ return true;
SmallString<128> SanRT(TC.getCompilerRT(Args, Sanitizer));
if (llvm::sys::fs::exists(SanRT + ".syms")) {
CmdArgs.push_back(Args.MakeArgString("--dynamic-list=" + SanRT + ".syms"));
@@ -538,22 +564,23 @@ void tools::linkSanitizerRuntimeDeps(const ToolChain &TC,
// Force linking against the system libraries sanitizers depends on
// (see PR15823 why this is necessary).
CmdArgs.push_back("--no-as-needed");
- // There's no libpthread or librt on RTEMS.
- if (TC.getTriple().getOS() != llvm::Triple::RTEMS) {
+ // There's no libpthread or librt on RTEMS & Android.
+ if (TC.getTriple().getOS() != llvm::Triple::RTEMS &&
+ !TC.getTriple().isAndroid()) {
CmdArgs.push_back("-lpthread");
- CmdArgs.push_back("-lrt");
+ if (TC.getTriple().getOS() != llvm::Triple::OpenBSD)
+ CmdArgs.push_back("-lrt");
}
CmdArgs.push_back("-lm");
// There's no libdl on all OSes.
if (TC.getTriple().getOS() != llvm::Triple::FreeBSD &&
TC.getTriple().getOS() != llvm::Triple::NetBSD &&
+ TC.getTriple().getOS() != llvm::Triple::OpenBSD &&
TC.getTriple().getOS() != llvm::Triple::RTEMS)
CmdArgs.push_back("-ldl");
- // Required for forkpty on some OSes
- if (TC.getTriple().getOS() == llvm::Triple::NetBSD)
- CmdArgs.push_back("-lutil");
// Required for backtrace on some OSes
- if (TC.getTriple().getOS() == llvm::Triple::NetBSD)
+ if (TC.getTriple().getOS() == llvm::Triple::NetBSD ||
+ TC.getTriple().getOS() == llvm::Triple::FreeBSD)
CmdArgs.push_back("-lexecinfo");
}
@@ -573,14 +600,17 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
HelperStaticRuntimes.push_back("asan-preinit");
}
if (SanArgs.needsUbsanRt()) {
- if (SanArgs.requiresMinimalRuntime()) {
+ if (SanArgs.requiresMinimalRuntime())
SharedRuntimes.push_back("ubsan_minimal");
- } else {
+ else
SharedRuntimes.push_back("ubsan_standalone");
- }
}
- if (SanArgs.needsScudoRt())
- SharedRuntimes.push_back("scudo");
+ if (SanArgs.needsScudoRt()) {
+ if (SanArgs.requiresMinimalRuntime())
+ SharedRuntimes.push_back("scudo_minimal");
+ else
+ SharedRuntimes.push_back("scudo");
+ }
if (SanArgs.needsHwasanRt())
SharedRuntimes.push_back("hwasan");
}
@@ -646,9 +676,15 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
if (SanArgs.needsEsanRt())
StaticRuntimes.push_back("esan");
if (SanArgs.needsScudoRt()) {
- StaticRuntimes.push_back("scudo");
- if (SanArgs.linkCXXRuntimes())
- StaticRuntimes.push_back("scudo_cxx");
+ if (SanArgs.requiresMinimalRuntime()) {
+ StaticRuntimes.push_back("scudo_minimal");
+ if (SanArgs.linkCXXRuntimes())
+ StaticRuntimes.push_back("scudo_cxx_minimal");
+ } else {
+ StaticRuntimes.push_back("scudo");
+ if (SanArgs.linkCXXRuntimes())
+ StaticRuntimes.push_back("scudo_cxx");
+ }
}
}
@@ -691,7 +727,7 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
// If there is a static runtime with no dynamic list, force all the symbols
// to be dynamic to be sure we export sanitizer interface functions.
if (AddExportDynamic)
- CmdArgs.push_back("-export-dynamic");
+ CmdArgs.push_back("--export-dynamic");
const SanitizerArgs &SanArgs = TC.getSanitizerArgs();
if (SanArgs.hasCrossDsoCfi() && !AddExportDynamic)
@@ -700,6 +736,35 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
return !StaticRuntimes.empty() || !NonWholeStaticRuntimes.empty();
}
+bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args, ArgStringList &CmdArgs) {
+ if (Args.hasArg(options::OPT_shared))
+ return false;
+
+ if (TC.getXRayArgs().needsXRayRt()) {
+ CmdArgs.push_back("-whole-archive");
+ CmdArgs.push_back(TC.getCompilerRTArgString(Args, "xray", false));
+ for (const auto &Mode : TC.getXRayArgs().modeList())
+ CmdArgs.push_back(TC.getCompilerRTArgString(Args, Mode, false));
+ CmdArgs.push_back("-no-whole-archive");
+ return true;
+ }
+
+ return false;
+}
+
+void tools::linkXRayRuntimeDeps(const ToolChain &TC, ArgStringList &CmdArgs) {
+ CmdArgs.push_back("--no-as-needed");
+ CmdArgs.push_back("-lpthread");
+ if (TC.getTriple().getOS() != llvm::Triple::OpenBSD)
+ CmdArgs.push_back("-lrt");
+ CmdArgs.push_back("-lm");
+
+ if (TC.getTriple().getOS() != llvm::Triple::FreeBSD &&
+ TC.getTriple().getOS() != llvm::Triple::NetBSD &&
+ TC.getTriple().getOS() != llvm::Triple::OpenBSD)
+ CmdArgs.push_back("-ldl");
+}
+
bool tools::areOptimizationsEnabled(const ArgList &Args) {
// Find the last -O arg and see if it is non-zero.
if (Arg *A = Args.getLastArg(options::OPT_O_Group))
@@ -859,6 +924,10 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) {
}
}
+ // AMDGPU-specific defaults for PIC.
+ if (Triple.getArch() == llvm::Triple::amdgcn)
+ PIC = true;
+
// The last argument relating to either PIC or PIE wins, and no
// other argument is used. If the last argument is any flavor of the
// '-fno-...' arguments, both PIC and PIE are disabled. Any PIE
@@ -964,16 +1033,26 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) {
RWPI = true;
}
- // ROPI and RWPI are not comaptible with PIC or PIE.
+ // ROPI and RWPI are not compatible with PIC or PIE.
if ((ROPI || RWPI) && (PIC || PIE))
ToolChain.getDriver().Diag(diag::err_drv_ropi_rwpi_incompatible_with_pic);
- // When targettng MIPS64 with N64, the default is PIC, unless -mno-abicalls is
- // used.
- if ((Triple.getArch() == llvm::Triple::mips64 ||
- Triple.getArch() == llvm::Triple::mips64el) &&
- Args.hasArg(options::OPT_mno_abicalls))
- return std::make_tuple(llvm::Reloc::Static, 0U, false);
+ if (Triple.isMIPS()) {
+ StringRef CPUName;
+ StringRef ABIName;
+ mips::getMipsCPUAndABI(Args, Triple, CPUName, ABIName);
+ // When targeting the N64 ABI, PIC is the default, except in the case
+ // when the -mno-abicalls option is used. In that case we exit
+ // at next check regardless of PIC being set below.
+ if (ABIName == "n64")
+ PIC = true;
+ // When targettng MIPS with -mno-abicalls, it's always static.
+ if(Args.hasArg(options::OPT_mno_abicalls))
+ return std::make_tuple(llvm::Reloc::Static, 0U, false);
+ // Unlike other architectures, MIPS, even with -fPIC/-mxgot/multigot,
+ // does not use PIC level 2 for historical reasons.
+ IsPICLevelTwo = false;
+ }
if (PIC)
return std::make_tuple(llvm::Reloc::PIC_, IsPICLevelTwo ? 2U : 1U, PIE);
@@ -989,6 +1068,40 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) {
return std::make_tuple(RelocM, 0U, false);
}
+// `-falign-functions` indicates that the functions should be aligned to a
+// 16-byte boundary.
+//
+// `-falign-functions=1` is the same as `-fno-align-functions`.
+//
+// The scalar `n` in `-falign-functions=n` must be an integral value between
+// [0, 65536]. If the value is not a power-of-two, it will be rounded up to
+// the nearest power-of-two.
+//
+// If we return `0`, the frontend will default to the backend's preferred
+// alignment.
+//
+// NOTE: icc only allows values between [0, 4096]. icc uses `-falign-functions`
+// to mean `-falign-functions=16`. GCC defaults to the backend's preferred
+// alignment. For unaligned functions, we default to the backend's preferred
+// alignment.
+unsigned tools::ParseFunctionAlignment(const ToolChain &TC,
+ const ArgList &Args) {
+ const Arg *A = Args.getLastArg(options::OPT_falign_functions,
+ options::OPT_falign_functions_EQ,
+ options::OPT_fno_align_functions);
+ if (!A || A->getOption().matches(options::OPT_fno_align_functions))
+ return 0;
+
+ if (A->getOption().matches(options::OPT_falign_functions))
+ return 0;
+
+ unsigned Value = 0;
+ if (StringRef(A->getValue()).getAsInteger(10, Value) || Value > 65536)
+ TC.getDriver().Diag(diag::err_drv_invalid_int_value)
+ << A->getAsString(Args) << A->getValue();
+ return Value ? llvm::Log2_32_Ceil(std::min(Value, 65536u)) : Value;
+}
+
void tools::AddAssemblerKPIC(const ToolChain &ToolChain, const ArgList &Args,
ArgStringList &CmdArgs) {
llvm::Reloc::Model RelocationModel;
@@ -1000,7 +1113,7 @@ void tools::AddAssemblerKPIC(const ToolChain &ToolChain, const ArgList &Args,
CmdArgs.push_back("-KPIC");
}
-/// \brief Determine whether Objective-C automated reference counting is
+/// Determine whether Objective-C automated reference counting is
/// enabled.
bool tools::isObjCAutoRefCount(const ArgList &Args) {
return Args.hasFlag(options::OPT_fobjc_arc, options::OPT_fno_objc_arc, false);
@@ -1189,3 +1302,146 @@ void tools::AddOpenMPLinkerScript(const ToolChain &TC, Compilation &C,
Lksf << LksBuffer;
}
+
+/// Add HIP linker script arguments at the end of the argument list so that
+/// the fat binary is built by embedding the device images into the host. The
+/// linker script also defines a symbol required by the code generation so that
+/// the image can be retrieved at runtime. This should be used only in tool
+/// chains that support linker scripts.
+void tools::AddHIPLinkerScript(const ToolChain &TC, Compilation &C,
+ const InputInfo &Output,
+ const InputInfoList &Inputs, const ArgList &Args,
+ ArgStringList &CmdArgs, const JobAction &JA,
+ const Tool &T) {
+
+ // If this is not a HIP host toolchain, we don't need to do anything.
+ if (!JA.isHostOffloading(Action::OFK_HIP))
+ return;
+
+ // Create temporary linker script. Keep it if save-temps is enabled.
+ const char *LKS;
+ SmallString<256> Name = llvm::sys::path::filename(Output.getFilename());
+ if (C.getDriver().isSaveTempsEnabled()) {
+ llvm::sys::path::replace_extension(Name, "lk");
+ LKS = C.getArgs().MakeArgString(Name.c_str());
+ } else {
+ llvm::sys::path::replace_extension(Name, "");
+ Name = C.getDriver().GetTemporaryPath(Name, "lk");
+ LKS = C.addTempFile(C.getArgs().MakeArgString(Name.c_str()));
+ }
+
+ // Add linker script option to the command.
+ CmdArgs.push_back("-T");
+ CmdArgs.push_back(LKS);
+
+ // Create a buffer to write the contents of the linker script.
+ std::string LksBuffer;
+ llvm::raw_string_ostream LksStream(LksBuffer);
+
+ // Get the HIP offload tool chain.
+ auto *HIPTC = static_cast<const toolchains::CudaToolChain *>(
+ C.getSingleOffloadToolChain<Action::OFK_HIP>());
+ assert(HIPTC->getTriple().getArch() == llvm::Triple::amdgcn &&
+ "Wrong platform");
+ (void)HIPTC;
+
+ // Construct clang-offload-bundler command to bundle object files for
+ // for different GPU archs.
+ ArgStringList BundlerArgs;
+ BundlerArgs.push_back(Args.MakeArgString("-type=o"));
+
+ // ToDo: Remove the dummy host binary entry which is required by
+ // clang-offload-bundler.
+ std::string BundlerTargetArg = "-targets=host-x86_64-unknown-linux";
+ std::string BundlerInputArg = "-inputs=/dev/null";
+
+ for (const auto &II : Inputs) {
+ const Action *A = II.getAction();
+ // Is this a device linking action?
+ if (A && isa<LinkJobAction>(A) && A->isDeviceOffloading(Action::OFK_HIP)) {
+ BundlerTargetArg = BundlerTargetArg + ",hip-amdgcn-amd-amdhsa-" +
+ StringRef(A->getOffloadingArch()).str();
+ BundlerInputArg = BundlerInputArg + "," + II.getFilename();
+ }
+ }
+ BundlerArgs.push_back(Args.MakeArgString(BundlerTargetArg));
+ BundlerArgs.push_back(Args.MakeArgString(BundlerInputArg));
+
+ std::string BundleFileName = C.getDriver().GetTemporaryPath("BUNDLE", "o");
+ const char *BundleFile =
+ C.addTempFile(C.getArgs().MakeArgString(BundleFileName.c_str()));
+ auto BundlerOutputArg =
+ Args.MakeArgString(std::string("-outputs=").append(BundleFile));
+ BundlerArgs.push_back(BundlerOutputArg);
+
+ SmallString<128> BundlerPath(C.getDriver().Dir);
+ llvm::sys::path::append(BundlerPath, "clang-offload-bundler");
+ const char *Bundler = Args.MakeArgString(BundlerPath);
+ C.addCommand(llvm::make_unique<Command>(JA, T, Bundler, BundlerArgs, Inputs));
+
+ // Add commands to embed target binaries. We ensure that each section and
+ // image is 16-byte aligned. This is not mandatory, but increases the
+ // likelihood of data to be aligned with a cache block in several main host
+ // machines.
+ LksStream << "/*\n";
+ LksStream << " HIP Offload Linker Script\n";
+ LksStream << " *** Automatically generated by Clang ***\n";
+ LksStream << "*/\n";
+ LksStream << "TARGET(binary)\n";
+ LksStream << "INPUT(" << BundleFileName << ")\n";
+ LksStream << "SECTIONS\n";
+ LksStream << "{\n";
+ LksStream << " .hip_fatbin :\n";
+ LksStream << " ALIGN(0x10)\n";
+ LksStream << " {\n";
+ LksStream << " PROVIDE_HIDDEN(__hip_fatbin = .);\n";
+ LksStream << " " << BundleFileName << "\n";
+ LksStream << " }\n";
+ LksStream << "}\n";
+ LksStream << "INSERT BEFORE .data\n";
+ LksStream.flush();
+
+ // Dump the contents of the linker script if the user requested that. We
+ // support this option to enable testing of behavior with -###.
+ if (C.getArgs().hasArg(options::OPT_fhip_dump_offload_linker_script))
+ llvm::errs() << LksBuffer;
+
+ // If this is a dry run, do not create the linker script file.
+ if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH))
+ return;
+
+ // Open script file and write the contents.
+ std::error_code EC;
+ llvm::raw_fd_ostream Lksf(LKS, EC, llvm::sys::fs::F_None);
+
+ if (EC) {
+ C.getDriver().Diag(clang::diag::err_unable_to_make_temp) << EC.message();
+ return;
+ }
+
+ Lksf << LksBuffer;
+}
+
+SmallString<128> tools::getStatsFileName(const llvm::opt::ArgList &Args,
+ const InputInfo &Output,
+ const InputInfo &Input,
+ const Driver &D) {
+ const Arg *A = Args.getLastArg(options::OPT_save_stats_EQ);
+ if (!A)
+ return {};
+
+ StringRef SaveStats = A->getValue();
+ SmallString<128> StatsFile;
+ if (SaveStats == "obj" && Output.isFilename()) {
+ StatsFile.assign(Output.getFilename());
+ llvm::sys::path::remove_filename(StatsFile);
+ } else if (SaveStats != "cwd") {
+ D.Diag(diag::err_drv_invalid_value) << A->getAsString(Args) << SaveStats;
+ return {};
+ }
+
+ StringRef BaseName = llvm::sys::path::filename(Input.getBaseInput());
+ llvm::sys::path::append(StatsFile, BaseName);
+ llvm::sys::path::replace_extension(StatsFile, "stats");
+ return StatsFile;
+}