//===--- CommonArgs.cpp - Args handling for multiple toolchains -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "CommonArgs.h" #include "Arch/AArch64.h" #include "Arch/ARM.h" #include "Arch/CSKY.h" #include "Arch/LoongArch.h" #include "Arch/M68k.h" #include "Arch/Mips.h" #include "Arch/PPC.h" #include "Arch/RISCV.h" #include "Arch/Sparc.h" #include "Arch/SystemZ.h" #include "Arch/VE.h" #include "Arch/X86.h" #include "HIPAMD.h" #include "Hexagon.h" #include "MSP430.h" #include "Solaris.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Version.h" #include "clang/Config/config.h" #include "clang/Driver/Action.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/InputInfo.h" #include "clang/Driver/Job.h" #include "clang/Driver/Options.h" #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/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Config/llvm-config.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #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/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLParser.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/TargetParser.h" #include using namespace clang::driver; using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args, const llvm::Triple &Triple) { if (Args.hasArg(clang::driver::options::OPT_pg) && !Args.hasArg(clang::driver::options::OPT_mfentry)) return true; if (Triple.isAndroid()) { switch (Triple.getArch()) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: case llvm::Triple::riscv64: return true; default: break; } } switch (Triple.getArch()) { case llvm::Triple::xcore: case llvm::Triple::wasm32: case llvm::Triple::wasm64: case llvm::Triple::msp430: // XCore never wants frame pointers, regardless of OS. // WebAssembly never wants frame pointers. return false; case llvm::Triple::ppc: case llvm::Triple::ppcle: case llvm::Triple::ppc64: case llvm::Triple::ppc64le: case llvm::Triple::riscv32: case llvm::Triple::riscv64: case llvm::Triple::sparc: case llvm::Triple::sparcel: case llvm::Triple::sparcv9: case llvm::Triple::amdgcn: case llvm::Triple::r600: case llvm::Triple::csky: case llvm::Triple::loongarch32: case llvm::Triple::loongarch64: return !clang::driver::tools::areOptimizationsEnabled(Args); default: break; } if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) { return !clang::driver::tools::areOptimizationsEnabled(Args); } if (Triple.isOSLinux() || Triple.isOSHurd()) { switch (Triple.getArch()) { // Don't use a frame pointer on linux if optimizing for certain targets. case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::systemz: case llvm::Triple::x86: case llvm::Triple::x86_64: return !clang::driver::tools::areOptimizationsEnabled(Args); default: return true; } } if (Triple.isOSWindows()) { switch (Triple.getArch()) { case llvm::Triple::x86: return !clang::driver::tools::areOptimizationsEnabled(Args); case llvm::Triple::x86_64: return Triple.isOSBinFormatMachO(); case llvm::Triple::arm: case llvm::Triple::thumb: // Windows on ARM builds with FPO disabled to aid fast stack walking return true; default: // All other supported Windows ISAs use xdata unwind information, so frame // pointers are not generally useful. return false; } } return true; } static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) { switch (Triple.getArch()) { default: return false; case llvm::Triple::arm: case llvm::Triple::thumb: // ARM Darwin targets require a frame pointer to be always present to aid // offline debugging via backtraces. return Triple.isOSDarwin(); } } clang::CodeGenOptions::FramePointerKind getFramePointerKind(const llvm::opt::ArgList &Args, const llvm::Triple &Triple) { // We have 4 states: // // 00) leaf retained, non-leaf retained // 01) leaf retained, non-leaf omitted (this is invalid) // 10) leaf omitted, non-leaf retained // (what -momit-leaf-frame-pointer was designed for) // 11) leaf omitted, non-leaf omitted // // "omit" options taking precedence over "no-omit" options is the only way // to make 3 valid states representable llvm::opt::Arg *A = Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer, clang::driver::options::OPT_fno_omit_frame_pointer); bool OmitFP = A && A->getOption().matches( clang::driver::options::OPT_fomit_frame_pointer); bool NoOmitFP = A && A->getOption().matches( clang::driver::options::OPT_fno_omit_frame_pointer); bool OmitLeafFP = Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer, clang::driver::options::OPT_mno_omit_leaf_frame_pointer, Triple.isAArch64() || Triple.isPS() || Triple.isVE() || (Triple.isAndroid() && Triple.isRISCV64())); if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) || (!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) { if (OmitLeafFP) return clang::CodeGenOptions::FramePointerKind::NonLeaf; return clang::CodeGenOptions::FramePointerKind::All; } return clang::CodeGenOptions::FramePointerKind::None; } static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs, const StringRef PluginOptPrefix) { if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ)) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "-pass-remarks=" + A->getValue())); if (const Arg *A = Args.getLastArg(options::OPT_Rpass_missed_EQ)) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "-pass-remarks-missed=" + A->getValue())); if (const Arg *A = Args.getLastArg(options::OPT_Rpass_analysis_EQ)) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "-pass-remarks-analysis=" + A->getValue())); } static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, const llvm::Triple &Triple, const InputInfo &Input, const InputInfo &Output, const StringRef PluginOptPrefix) { StringRef Format = "yaml"; if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) Format = A->getValue(); SmallString<128> F; const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ); if (A) F = A->getValue(); else if (Output.isFilename()) F = Output.getFilename(); assert(!F.empty() && "Cannot determine remarks output name."); // Append "opt.ld." to the end of the file name. CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "opt-remarks-filename=" + F + ".opt.ld." + Format)); if (const Arg *A = Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "opt-remarks-passes=" + A->getValue())); CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "opt-remarks-format=" + Format.data())); } static void renderRemarksHotnessOptions(const ArgList &Args, ArgStringList &CmdArgs, const StringRef PluginOptPrefix) { if (Args.hasFlag(options::OPT_fdiagnostics_show_hotness, options::OPT_fno_diagnostics_show_hotness, false)) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "opt-remarks-with-hotness")); if (const Arg *A = Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "opt-remarks-hotness-threshold=" + A->getValue())); } static bool shouldIgnoreUnsupportedTargetFeature(const Arg &TargetFeatureArg, llvm::Triple T, StringRef Processor) { // Warn no-cumode for AMDGCN processors not supporing WGP mode. if (!T.isAMDGPU()) return false; auto GPUKind = T.isAMDGCN() ? llvm::AMDGPU::parseArchAMDGCN(Processor) : llvm::AMDGPU::parseArchR600(Processor); auto GPUFeatures = T.isAMDGCN() ? llvm::AMDGPU::getArchAttrAMDGCN(GPUKind) : llvm::AMDGPU::getArchAttrR600(GPUKind); if (GPUFeatures & llvm::AMDGPU::FEATURE_WGP) return false; return TargetFeatureArg.getOption().matches(options::OPT_mno_cumode); } void tools::addPathIfExists(const Driver &D, const Twine &Path, ToolChain::path_list &Paths) { if (D.getVFS().exists(Path)) Paths.push_back(Path.str()); } void tools::handleTargetFeaturesGroup(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, std::vector &Features, OptSpecifier Group) { std::set Warned; for (const Arg *A : Args.filtered(Group)) { StringRef Name = A->getOption().getName(); A->claim(); // Skip over "-m". assert(Name.starts_with("m") && "Invalid feature name."); Name = Name.substr(1); auto Proc = getCPUName(D, Args, Triple); if (shouldIgnoreUnsupportedTargetFeature(*A, Triple, Proc)) { if (Warned.count(Name) == 0) { D.getDiags().Report( clang::diag::warn_drv_unsupported_option_for_processor) << A->getAsString(Args) << Proc; Warned.insert(Name); } continue; } bool IsNegative = Name.starts_with("no-"); if (IsNegative) Name = Name.substr(3); Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); } } SmallVector tools::unifyTargetFeatures(ArrayRef Features) { // Only add a feature if it hasn't been seen before starting from the end. SmallVector UnifiedFeatures; llvm::DenseSet UsedFeatures; for (StringRef Feature : llvm::reverse(Features)) { if (UsedFeatures.insert(Feature.drop_front()).second) UnifiedFeatures.insert(UnifiedFeatures.begin(), Feature); } return UnifiedFeatures; } void tools::addDirectoryList(const ArgList &Args, ArgStringList &CmdArgs, const char *ArgName, const char *EnvVar) { const char *DirList = ::getenv(EnvVar); bool CombinedArg = false; if (!DirList) return; // Nothing to do. StringRef Name(ArgName); if (Name.equals("-I") || Name.equals("-L") || Name.empty()) CombinedArg = true; StringRef Dirs(DirList); if (Dirs.empty()) // Empty string should not add '.'. return; StringRef::size_type Delim; while ((Delim = Dirs.find(llvm::sys::EnvPathSeparator)) != StringRef::npos) { if (Delim == 0) { // Leading colon. if (CombinedArg) { CmdArgs.push_back(Args.MakeArgString(std::string(ArgName) + ".")); } else { CmdArgs.push_back(ArgName); CmdArgs.push_back("."); } } else { if (CombinedArg) { CmdArgs.push_back( Args.MakeArgString(std::string(ArgName) + Dirs.substr(0, Delim))); } else { CmdArgs.push_back(ArgName); CmdArgs.push_back(Args.MakeArgString(Dirs.substr(0, Delim))); } } Dirs = Dirs.substr(Delim + 1); } if (Dirs.empty()) { // Trailing colon. if (CombinedArg) { CmdArgs.push_back(Args.MakeArgString(std::string(ArgName) + ".")); } else { CmdArgs.push_back(ArgName); CmdArgs.push_back("."); } } else { // Add the last path. if (CombinedArg) { CmdArgs.push_back(Args.MakeArgString(std::string(ArgName) + Dirs)); } else { CmdArgs.push_back(ArgName); CmdArgs.push_back(Args.MakeArgString(Dirs)); } } } void tools::AddLinkerInputs(const ToolChain &TC, const InputInfoList &Inputs, const ArgList &Args, ArgStringList &CmdArgs, const JobAction &JA) { const Driver &D = TC.getDriver(); // Add extra linker input arguments which are not treated as inputs // (constructed via -Xarch_). Args.AddAllArgValues(CmdArgs, options::OPT_Zlinker_input); // LIBRARY_PATH are included before user inputs and only supported on native // toolchains. if (!TC.isCrossCompiling()) addDirectoryList(Args, CmdArgs, "-L", "LIBRARY_PATH"); 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 (auto *IA = II.getAction()) if ((JA.isHostOffloading(Action::OFK_OpenMP) && IA->isDeviceOffloading(Action::OFK_OpenMP))) continue; if (!TC.HasNativeLLVMSupport() && types::isLLVMIR(II.getType())) // Don't try to pass LLVM inputs unless we have native support. D.Diag(diag::err_drv_no_linker_llvm_support) << TC.getTripleString(); // Add filenames immediately. if (II.isFilename()) { CmdArgs.push_back(II.getFilename()); continue; } // In some error cases, the input could be Nothing; skip those. if (II.isNothing()) continue; // Otherwise, this is a linker input argument. const Arg &A = II.getInputArg(); // Handle reserved library options. if (A.getOption().matches(options::OPT_Z_reserved_lib_stdcxx)) TC.AddCXXStdlibLibArgs(Args, CmdArgs); else if (A.getOption().matches(options::OPT_Z_reserved_lib_cckext)) TC.AddCCKextLibArgs(Args, CmdArgs); else A.renderAsInput(Args, CmdArgs); } } void tools::addLinkerCompressDebugSectionsOption( const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { // GNU ld supports --compress-debug-sections=none|zlib|zlib-gnu|zlib-gabi // whereas zlib is an alias to zlib-gabi and zlib-gnu is obsoleted. Therefore // -gz=none|zlib are translated to --compress-debug-sections=none|zlib. -gz // is not translated since ld --compress-debug-sections option requires an // argument. if (const Arg *A = Args.getLastArg(options::OPT_gz_EQ)) { StringRef V = A->getValue(); if (V == "none" || V == "zlib" || V == "zstd") CmdArgs.push_back(Args.MakeArgString("--compress-debug-sections=" + V)); else TC.getDriver().Diag(diag::err_drv_unsupported_option_argument) << A->getSpelling() << V; } } void tools::AddTargetFeature(const ArgList &Args, std::vector &Features, OptSpecifier OnOpt, OptSpecifier OffOpt, StringRef FeatureName) { if (Arg *A = Args.getLastArg(OnOpt, OffOpt)) { if (A->getOption().matches(OnOpt)) Features.push_back(Args.MakeArgString("+" + FeatureName)); else Features.push_back(Args.MakeArgString("-" + FeatureName)); } } /// Get the (LLVM) name of the AMDGPU gpu we are targeting. static std::string getAMDGPUTargetGPU(const llvm::Triple &T, const ArgList &Args) { Arg *MArch = Args.getLastArg(options::OPT_march_EQ); if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { auto GPUName = getProcessorFromTargetID(T, A->getValue()); return llvm::StringSwitch(GPUName) .Cases("rv630", "rv635", "r600") .Cases("rv610", "rv620", "rs780", "rs880") .Case("rv740", "rv770") .Case("palm", "cedar") .Cases("sumo", "sumo2", "sumo") .Case("hemlock", "cypress") .Case("aruba", "cayman") .Default(GPUName.str()); } if (MArch) return getProcessorFromTargetID(T, MArch->getValue()).str(); return ""; } static std::string getLanaiTargetCPU(const ArgList &Args) { if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { return A->getValue(); } return ""; } /// Get the (LLVM) name of the WebAssembly cpu we are targeting. static StringRef getWebAssemblyTargetCPU(const ArgList &Args) { // If we have -mcpu=, use that. if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { StringRef CPU = A->getValue(); #ifdef __wasm__ // Handle "native" by examining the host. "native" isn't meaningful when // cross compiling, so only support this when the host is also WebAssembly. if (CPU == "native") return llvm::sys::getHostCPUName(); #endif return CPU; } return "generic"; } std::string tools::getCPUName(const Driver &D, const ArgList &Args, const llvm::Triple &T, bool FromAs) { Arg *A; switch (T.getArch()) { default: return ""; case llvm::Triple::aarch64: case llvm::Triple::aarch64_32: case llvm::Triple::aarch64_be: return aarch64::getAArch64TargetCPU(Args, T, A); case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: { StringRef MArch, MCPU; arm::getARMArchCPUFromArgs(Args, MArch, MCPU, FromAs); return arm::getARMTargetCPU(MCPU, MArch, T); } case llvm::Triple::avr: if (const Arg *A = Args.getLastArg(options::OPT_mmcu_EQ)) return A->getValue(); return ""; case llvm::Triple::m68k: return m68k::getM68kTargetCPU(Args); case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: { StringRef CPUName; StringRef ABIName; mips::getMipsCPUAndABI(Args, T, CPUName, ABIName); return std::string(CPUName); } case llvm::Triple::nvptx: case llvm::Triple::nvptx64: if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) return A->getValue(); return ""; case llvm::Triple::ppc: case llvm::Triple::ppcle: case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return ppc::getPPCTargetCPU(D, Args, T); case llvm::Triple::csky: if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) return A->getValue(); else if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) return A->getValue(); else return "ck810"; case llvm::Triple::riscv32: case llvm::Triple::riscv64: return riscv::getRISCVTargetCPU(Args, T); case llvm::Triple::bpfel: case llvm::Triple::bpfeb: if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) return A->getValue(); return ""; case llvm::Triple::sparc: case llvm::Triple::sparcel: case llvm::Triple::sparcv9: return sparc::getSparcTargetCPU(D, Args, T); case llvm::Triple::x86: case llvm::Triple::x86_64: return x86::getX86TargetCPU(D, Args, T); case llvm::Triple::hexagon: return "hexagon" + toolchains::HexagonToolChain::GetTargetCPUVersion(Args).str(); case llvm::Triple::lanai: return getLanaiTargetCPU(Args); case llvm::Triple::systemz: return systemz::getSystemZTargetCPU(Args); case llvm::Triple::r600: case llvm::Triple::amdgcn: return getAMDGPUTargetGPU(T, Args); case llvm::Triple::wasm32: case llvm::Triple::wasm64: return std::string(getWebAssemblyTargetCPU(Args)); case llvm::Triple::loongarch32: case llvm::Triple::loongarch64: return loongarch::getLoongArchTargetCPU(Args, T); } } static void getWebAssemblyTargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, std::vector &Features) { handleTargetFeaturesGroup(D, Triple, Args, Features, options::OPT_m_wasm_Features_Group); } void tools::getTargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, ArgStringList &CmdArgs, bool ForAS, bool IsAux) { std::vector Features; switch (Triple.getArch()) { default: break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: mips::getMIPSTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: arm::getARMTargetFeatures(D, Triple, Args, Features, ForAS); break; case llvm::Triple::ppc: case llvm::Triple::ppcle: case llvm::Triple::ppc64: case llvm::Triple::ppc64le: ppc::getPPCTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::riscv32: case llvm::Triple::riscv64: riscv::getRISCVTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::systemz: systemz::getSystemZTargetFeatures(D, Args, Features); break; case llvm::Triple::aarch64: case llvm::Triple::aarch64_32: case llvm::Triple::aarch64_be: aarch64::getAArch64TargetFeatures(D, Triple, Args, Features, ForAS); break; case llvm::Triple::x86: case llvm::Triple::x86_64: x86::getX86TargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::hexagon: hexagon::getHexagonTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::wasm32: case llvm::Triple::wasm64: getWebAssemblyTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::sparc: case llvm::Triple::sparcel: case llvm::Triple::sparcv9: sparc::getSparcTargetFeatures(D, Args, Features); break; case llvm::Triple::r600: case llvm::Triple::amdgcn: amdgpu::getAMDGPUTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::nvptx: case llvm::Triple::nvptx64: NVPTX::getNVPTXTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::m68k: m68k::getM68kTargetFeatures(D, Triple, Args, Features); break; case llvm::Triple::msp430: msp430::getMSP430TargetFeatures(D, Args, Features); break; case llvm::Triple::ve: ve::getVETargetFeatures(D, Args, Features); break; case llvm::Triple::csky: csky::getCSKYTargetFeatures(D, Triple, Args, CmdArgs, Features); break; case llvm::Triple::loongarch32: case llvm::Triple::loongarch64: loongarch::getLoongArchTargetFeatures(D, Triple, Args, Features); break; } for (auto Feature : unifyTargetFeatures(Features)) { CmdArgs.push_back(IsAux ? "-aux-target-feature" : "-target-feature"); CmdArgs.push_back(Feature.data()); } } llvm::StringRef tools::getLTOParallelism(const ArgList &Args, const Driver &D) { Arg *LtoJobsArg = Args.getLastArg(options::OPT_flto_jobs_EQ); if (!LtoJobsArg) return {}; if (!llvm::get_threadpool_strategy(LtoJobsArg->getValue())) D.Diag(diag::err_drv_invalid_int_value) << LtoJobsArg->getAsString(Args) << LtoJobsArg->getValue(); return LtoJobsArg->getValue(); } // PS4/PS5 uses -ffunction-sections and -fdata-sections by default. bool tools::isUseSeparateSections(const llvm::Triple &Triple) { return Triple.isPS(); } bool tools::isTLSDESCEnabled(const ToolChain &TC, const llvm::opt::ArgList &Args) { const llvm::Triple &Triple = TC.getEffectiveTriple(); Arg *A = Args.getLastArg(options::OPT_mtls_dialect_EQ); if (!A) return Triple.hasDefaultTLSDESC(); StringRef V = A->getValue(); bool SupportedArgument = false, EnableTLSDESC = false; bool Unsupported = !Triple.isOSBinFormatELF(); if (Triple.isRISCV()) { SupportedArgument = V == "desc" || V == "trad"; EnableTLSDESC = V == "desc"; } else if (Triple.isX86()) { SupportedArgument = V == "gnu"; } else { Unsupported = true; } if (Unsupported) { TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << A->getSpelling() << Triple.getTriple(); } else if (!SupportedArgument) { TC.getDriver().Diag(diag::err_drv_unsupported_option_argument_for_target) << A->getSpelling() << V << Triple.getTriple(); } return EnableTLSDESC; } void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, ArgStringList &CmdArgs, const InputInfo &Output, const InputInfo &Input, bool IsThinLTO) { const bool IsOSAIX = ToolChain.getTriple().isOSAIX(); const bool IsAMDGCN = ToolChain.getTriple().isAMDGCN(); const char *Linker = Args.MakeArgString(ToolChain.GetLinkerPath()); const Driver &D = ToolChain.getDriver(); const bool IsFatLTO = Args.hasArg(options::OPT_ffat_lto_objects); const bool IsUnifiedLTO = Args.hasArg(options::OPT_funified_lto); if (llvm::sys::path::filename(Linker) != "ld.lld" && llvm::sys::path::stem(Linker) != "ld.lld" && !ToolChain.getTriple().isOSOpenBSD()) { // Tell the linker to load the plugin. This has to come before // AddLinkerInputs as gold requires -plugin and AIX ld requires -bplugin to // come before any -plugin-opt/-bplugin_opt that -Wl might forward. const char *PluginPrefix = IsOSAIX ? "-bplugin:" : ""; const char *PluginName = IsOSAIX ? "/libLTO" : "/LLVMgold"; if (!IsOSAIX) CmdArgs.push_back("-plugin"); #if defined(_WIN32) const char *Suffix = ".dll"; #elif defined(__APPLE__) const char *Suffix = ".dylib"; #else const char *Suffix = ".so"; #endif SmallString<1024> Plugin; llvm::sys::path::native(Twine(D.Dir) + "/../" CLANG_INSTALL_LIBDIR_BASENAME + PluginName + Suffix, Plugin); CmdArgs.push_back(Args.MakeArgString(Twine(PluginPrefix) + Plugin)); } else { // Tell LLD to find and use .llvm.lto section in regular relocatable object // files if (IsFatLTO) CmdArgs.push_back("--fat-lto-objects"); } const char *PluginOptPrefix = IsOSAIX ? "-bplugin_opt:" : "-plugin-opt="; const char *ExtraDash = IsOSAIX ? "-" : ""; const char *ParallelismOpt = IsOSAIX ? "-threads=" : "jobs="; // Note, this solution is far from perfect, better to encode it into IR // metadata, but this may not be worth it, since it looks like aranges is on // the way out. if (Args.hasArg(options::OPT_gdwarf_aranges)) { CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "-generate-arange-section")); } // Pass vector library arguments to LTO. Arg *ArgVecLib = Args.getLastArg(options::OPT_fveclib); if (ArgVecLib && ArgVecLib->getNumValues() == 1) { // Map the vector library names from clang front-end to opt front-end. The // values are taken from the TargetLibraryInfo class command line options. std::optional OptVal = llvm::StringSwitch>(ArgVecLib->getValue()) .Case("Accelerate", "Accelerate") .Case("LIBMVEC", "LIBMVEC-X86") .Case("MASSV", "MASSV") .Case("SVML", "SVML") .Case("SLEEF", "sleefgnuabi") .Case("Darwin_libsystem_m", "Darwin_libsystem_m") .Case("ArmPL", "ArmPL") .Case("none", "none") .Default(std::nullopt); if (OptVal) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "-vector-library=" + OptVal.value())); } // Try to pass driver level flags relevant to LTO code generation down to // the plugin. // Handle flags for selecting CPU variants. std::string CPU = getCPUName(D, Args, ToolChain.getTriple()); if (!CPU.empty()) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "mcpu=" + CPU)); if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { // The optimization level matches // CompilerInvocation.cpp:getOptimizationLevel(). StringRef OOpt; if (A->getOption().matches(options::OPT_O4) || A->getOption().matches(options::OPT_Ofast)) OOpt = "3"; else if (A->getOption().matches(options::OPT_O)) { OOpt = A->getValue(); if (OOpt == "g") OOpt = "1"; else if (OOpt == "s" || OOpt == "z") OOpt = "2"; } else if (A->getOption().matches(options::OPT_O0)) OOpt = "0"; if (!OOpt.empty()) { CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "O" + OOpt)); if (IsAMDGCN) CmdArgs.push_back(Args.MakeArgString(Twine("--lto-CGO") + OOpt)); } } if (Args.hasArg(options::OPT_gsplit_dwarf)) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "dwo_dir=" + Output.getFilename() + "_dwo")); if (IsThinLTO && !IsOSAIX) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "thinlto")); else if (IsThinLTO && IsOSAIX) CmdArgs.push_back(Args.MakeArgString(Twine("-bdbg:thinlto"))); // Matrix intrinsic lowering happens at link time with ThinLTO. Enable // LowerMatrixIntrinsicsPass, which is transitively called by // buildThinLTODefaultPipeline under EnableMatrix. if ((IsThinLTO || IsFatLTO || IsUnifiedLTO) && Args.hasArg(options::OPT_fenable_matrix)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-enable-matrix")); StringRef Parallelism = getLTOParallelism(Args, D); if (!Parallelism.empty()) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + ParallelismOpt + Parallelism)); // Pass down GlobalISel options. if (Arg *A = Args.getLastArg(options::OPT_fglobal_isel, options::OPT_fno_global_isel)) { // Parsing -fno-global-isel explicitly gives architectures that enable GISel // by default a chance to disable it. CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "-global-isel=" + (A->getOption().matches(options::OPT_fglobal_isel) ? "1" : "0"))); } // If an explicit debugger tuning argument appeared, pass it along. if (Arg *A = Args.getLastArg(options::OPT_gTune_Group, options::OPT_ggdbN_Group)) { if (A->getOption().matches(options::OPT_glldb)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-debugger-tune=lldb")); else if (A->getOption().matches(options::OPT_gsce)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-debugger-tune=sce")); else if (A->getOption().matches(options::OPT_gdbx)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-debugger-tune=dbx")); else CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-debugger-tune=gdb")); } if (IsOSAIX) { if (!ToolChain.useIntegratedAs()) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-no-integrated-as=1")); // On AIX, clang assumes strict-dwarf is true if any debug option is // specified, unless it is told explicitly not to assume so. Arg *A = Args.getLastArg(options::OPT_g_Group); bool EnableDebugInfo = A && !A->getOption().matches(options::OPT_g0) && !A->getOption().matches(options::OPT_ggdb0); if (EnableDebugInfo && Args.hasFlag(options::OPT_gstrict_dwarf, options::OPT_gno_strict_dwarf, true)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-strict-dwarf=true")); for (const Arg *A : Args.filtered_reverse(options::OPT_mabi_EQ)) { StringRef V = A->getValue(); if (V == "vec-default") break; if (V == "vec-extabi") { CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-vec-extabi")); break; } } } bool UseSeparateSections = isUseSeparateSections(ToolChain.getEffectiveTriple()); if (Args.hasFlag(options::OPT_ffunction_sections, options::OPT_fno_function_sections, UseSeparateSections)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-function-sections=1")); else if (Args.hasArg(options::OPT_fno_function_sections)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-function-sections=0")); bool DataSectionsTurnedOff = false; if (Args.hasFlag(options::OPT_fdata_sections, options::OPT_fno_data_sections, UseSeparateSections)) { CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-data-sections=1")); } else if (Args.hasArg(options::OPT_fno_data_sections)) { DataSectionsTurnedOff = true; CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-data-sections=0")); } if (Args.hasArg(options::OPT_mxcoff_roptr) || Args.hasArg(options::OPT_mno_xcoff_roptr)) { bool HasRoptr = Args.hasFlag(options::OPT_mxcoff_roptr, options::OPT_mno_xcoff_roptr, false); StringRef OptStr = HasRoptr ? "-mxcoff-roptr" : "-mno-xcoff-roptr"; if (!IsOSAIX) D.Diag(diag::err_drv_unsupported_opt_for_target) << OptStr << ToolChain.getTriple().str(); if (HasRoptr) { // The data sections option is on by default on AIX. We only need to error // out when -fno-data-sections is specified explicitly to turn off data // sections. if (DataSectionsTurnedOff) D.Diag(diag::err_roptr_requires_data_sections); CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-mxcoff-roptr")); } } // Pass an option to enable split machine functions. if (auto *A = Args.getLastArg(options::OPT_fsplit_machine_functions, options::OPT_fno_split_machine_functions)) { if (A->getOption().matches(options::OPT_fsplit_machine_functions)) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "-split-machine-functions")); } if (Arg *A = getLastProfileSampleUseArg(Args)) { StringRef FName = A->getValue(); if (!llvm::sys::fs::exists(FName)) D.Diag(diag::err_drv_no_such_file) << FName; else CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "sample-profile=" + FName)); } if (auto *CSPGOGenerateArg = getLastCSProfileGenerateArg(Args)) { CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "cs-profile-generate")); if (CSPGOGenerateArg->getOption().matches( options::OPT_fcs_profile_generate_EQ)) { SmallString<128> Path(CSPGOGenerateArg->getValue()); llvm::sys::path::append(Path, "default_%m.profraw"); CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "cs-profile-path=" + Path)); } else CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "cs-profile-path=default_%m.profraw")); } else if (auto *ProfileUseArg = getLastProfileUseArg(Args)) { SmallString<128> Path( ProfileUseArg->getNumValues() == 0 ? "" : ProfileUseArg->getValue()); if (Path.empty() || llvm::sys::fs::is_directory(Path)) llvm::sys::path::append(Path, "default.profdata"); CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + ExtraDash + "cs-profile-path=" + Path)); } // This controls whether or not we perform JustMyCode instrumentation. if (Args.hasFlag(options::OPT_fjmc, options::OPT_fno_jmc, false)) { if (ToolChain.getEffectiveTriple().isOSBinFormatELF()) CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "-enable-jmc-instrument")); else D.Diag(clang::diag::warn_drv_fjmc_for_elf_only); } if (Args.hasFlag(options::OPT_femulated_tls, options::OPT_fno_emulated_tls, ToolChain.getTriple().hasDefaultEmulatedTLS())) { CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-emulated-tls")); } if (isTLSDESCEnabled(ToolChain, Args)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-enable-tlsdesc")); if (Args.hasFlag(options::OPT_fstack_size_section, options::OPT_fno_stack_size_section, false)) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-stack-size-section")); // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "stats-file=" + StatsFile)); // Setup crash diagnostics dir. if (Arg *A = Args.getLastArg(options::OPT_fcrash_diagnostics_dir)) CmdArgs.push_back(Args.MakeArgString( Twine(PluginOptPrefix) + "-crash-diagnostics-dir=" + A->getValue())); addX86AlignBranchArgs(D, Args, CmdArgs, /*IsLTO=*/true, PluginOptPrefix); // Handle remark diagnostics on screen options: '-Rpass-*'. renderRpassOptions(Args, CmdArgs, PluginOptPrefix); // Handle serialized remarks options: '-fsave-optimization-record' // and '-foptimization-record-*'. if (willEmitRemarks(Args)) renderRemarksOptions(Args, CmdArgs, ToolChain.getEffectiveTriple(), Input, Output, PluginOptPrefix); // Handle remarks hotness/threshold related options. renderRemarksHotnessOptions(Args, CmdArgs, PluginOptPrefix); addMachineOutlinerArgs(D, Args, CmdArgs, ToolChain.getEffectiveTriple(), /*IsLTO=*/true, PluginOptPrefix); } /// Adds the '-lcgpu' and '-lmgpu' libraries to the compilation to include the /// LLVM C library for GPUs. static void addOpenMPDeviceLibC(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (Args.hasArg(options::OPT_nogpulib) || Args.hasArg(options::OPT_nolibc)) return; // Check the resource directory for the LLVM libc GPU declarations. If it's // found we can assume that LLVM was built with support for the GPU libc. SmallString<256> LibCDecls(TC.getDriver().ResourceDir); llvm::sys::path::append(LibCDecls, "include", "llvm_libc_wrappers", "llvm-libc-decls"); bool HasLibC = llvm::sys::fs::exists(LibCDecls) && llvm::sys::fs::is_directory(LibCDecls); if (Args.hasFlag(options::OPT_gpulibc, options::OPT_nogpulibc, HasLibC)) { CmdArgs.push_back("-lcgpu"); CmdArgs.push_back("-lmgpu"); } } void tools::addOpenMPRuntimeLibraryPath(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { // Default to clang lib / lib64 folder, i.e. the same location as device // runtime. SmallString<256> DefaultLibPath = llvm::sys::path::parent_path(TC.getDriver().Dir); llvm::sys::path::append(DefaultLibPath, CLANG_INSTALL_LIBDIR_BASENAME); CmdArgs.push_back(Args.MakeArgString("-L" + DefaultLibPath)); } 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; for (const auto &CandidateRPath : TC.getArchSpecificLibPaths()) { if (TC.getVFS().exists(CandidateRPath)) { CmdArgs.push_back("-rpath"); CmdArgs.push_back(Args.MakeArgString(CandidateRPath)); } } } bool tools::addOpenMPRuntime(ArgStringList &CmdArgs, const ToolChain &TC, const ArgList &Args, bool ForceStaticHostRuntime, bool IsOffloadingHost, bool GompNeedsRT) { if (!Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) return false; Driver::OpenMPRuntimeKind RTKind = TC.getDriver().getOpenMPRuntime(Args); if (RTKind == Driver::OMPRT_Unknown) // Already diagnosed. return false; if (ForceStaticHostRuntime) CmdArgs.push_back("-Bstatic"); switch (RTKind) { case Driver::OMPRT_OMP: CmdArgs.push_back("-lomp"); break; case Driver::OMPRT_GOMP: CmdArgs.push_back("-lgomp"); break; case Driver::OMPRT_IOMP5: CmdArgs.push_back("-liomp5"); break; case Driver::OMPRT_Unknown: break; } if (ForceStaticHostRuntime) CmdArgs.push_back("-Bdynamic"); if (RTKind == Driver::OMPRT_GOMP && GompNeedsRT) CmdArgs.push_back("-lrt"); if (IsOffloadingHost) CmdArgs.push_back("-lomptarget"); if (IsOffloadingHost && !Args.hasArg(options::OPT_nogpulib)) CmdArgs.push_back("-lomptarget.devicertl"); if (IsOffloadingHost) addOpenMPDeviceLibC(TC, Args, CmdArgs); addArchSpecificRPath(TC, Args, CmdArgs); addOpenMPRuntimeLibraryPath(TC, Args, CmdArgs); return true; } /// Determines if --whole-archive is active in the list of arguments. static bool isWholeArchivePresent(const ArgList &Args) { bool WholeArchiveActive = false; for (auto *Arg : Args.filtered(options::OPT_Wl_COMMA)) { if (Arg) { for (StringRef ArgValue : Arg->getValues()) { if (ArgValue == "--whole-archive") WholeArchiveActive = true; if (ArgValue == "--no-whole-archive") WholeArchiveActive = false; } } } return WholeArchiveActive; } /// Determine if driver is invoked to create a shared object library (-static) static bool isSharedLinkage(const ArgList &Args) { return Args.hasArg(options::OPT_shared); } /// Determine if driver is invoked to create a static object library (-shared) static bool isStaticLinkage(const ArgList &Args) { return Args.hasArg(options::OPT_static); } /// Add Fortran runtime libs for MSVC static void addFortranRuntimeLibsMSVC(const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { unsigned RTOptionID = options::OPT__SLASH_MT; if (auto *rtl = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) { RTOptionID = llvm::StringSwitch(rtl->getValue()) .Case("static", options::OPT__SLASH_MT) .Case("static_dbg", options::OPT__SLASH_MTd) .Case("dll", options::OPT__SLASH_MD) .Case("dll_dbg", options::OPT__SLASH_MDd) .Default(options::OPT__SLASH_MT); } switch (RTOptionID) { case options::OPT__SLASH_MT: CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static.lib"); break; case options::OPT__SLASH_MTd: CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static_dbg.lib"); break; case options::OPT__SLASH_MD: CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic.lib"); break; case options::OPT__SLASH_MDd: CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic_dbg.lib"); break; } } // Add FortranMain runtime lib static void addFortranMain(const ToolChain &TC, const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { // 0. Shared-library linkage // If we are attempting to link a library, we should not add // -lFortran_main.a to the link line, as the `main` symbol is not // required for a library and should also be provided by one of // the translation units of the code that this shared library // will be linked against eventually. if (isSharedLinkage(Args) || isStaticLinkage(Args)) { return; } // 1. MSVC if (TC.getTriple().isKnownWindowsMSVCEnvironment()) { addFortranRuntimeLibsMSVC(Args, CmdArgs); return; } // 2. GNU and similar const Driver &D = TC.getDriver(); const char *FortranMainLinkFlag = "-lFortran_main"; // Warn if the user added `-lFortran_main` - this library is an implementation // detail of Flang and should be handled automaticaly by the driver. for (const char *arg : CmdArgs) { if (strncmp(arg, FortranMainLinkFlag, strlen(FortranMainLinkFlag)) == 0) D.Diag(diag::warn_drv_deprecated_custom) << FortranMainLinkFlag << "see the Flang driver documentation for correct usage"; } // The --whole-archive option needs to be part of the link line to make // sure that the main() function from Fortran_main.a is pulled in by the // linker. However, it shouldn't be used if it's already active. // TODO: Find an equivalent of `--whole-archive` for Darwin and AIX. if (!isWholeArchivePresent(Args) && !TC.getTriple().isMacOSX() && !TC.getTriple().isOSAIX()) { CmdArgs.push_back("--whole-archive"); CmdArgs.push_back(FortranMainLinkFlag); CmdArgs.push_back("--no-whole-archive"); return; } CmdArgs.push_back(FortranMainLinkFlag); } /// Add Fortran runtime libs void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { // 1. Link FortranMain // FortranMain depends on FortranRuntime, so needs to be listed first. If // -fno-fortran-main has been passed, skip linking Fortran_main.a if (!Args.hasArg(options::OPT_no_fortran_main)) addFortranMain(TC, Args, CmdArgs); // 2. Link FortranRuntime and FortranDecimal // These are handled earlier on Windows by telling the frontend driver to // add the correct libraries to link against as dependents in the object // file. if (!TC.getTriple().isKnownWindowsMSVCEnvironment()) { CmdArgs.push_back("-lFortranRuntime"); CmdArgs.push_back("-lFortranDecimal"); } } void tools::addFortranRuntimeLibraryPath(const ToolChain &TC, const llvm::opt::ArgList &Args, ArgStringList &CmdArgs) { // Default to the /../lib directory. This works fine on the // platforms that we have tested so far. We will probably have to re-fine // this in the future. In particular, on some platforms, we may need to use // lib64 instead of lib. SmallString<256> DefaultLibPath = llvm::sys::path::parent_path(TC.getDriver().Dir); llvm::sys::path::append(DefaultLibPath, "lib"); if (TC.getTriple().isKnownWindowsMSVCEnvironment()) CmdArgs.push_back(Args.MakeArgString("-libpath:" + DefaultLibPath)); else CmdArgs.push_back(Args.MakeArgString("-L" + DefaultLibPath)); } static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, StringRef Sanitizer, bool IsShared, bool IsWhole) { // Wrap any static runtimes that must be forced into executable in // whole-archive. if (IsWhole) CmdArgs.push_back("--whole-archive"); CmdArgs.push_back(TC.getCompilerRTArgString( Args, Sanitizer, IsShared ? ToolChain::FT_Shared : ToolChain::FT_Static)); if (IsWhole) CmdArgs.push_back("--no-whole-archive"); if (IsShared) { addArchSpecificRPath(TC, Args, CmdArgs); } } // Tries to use a file with the list of dynamic symbols that need to be exported // from the runtime library. Returns true if the file was found. static bool addSanitizerDynamicList(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, StringRef Sanitizer) { bool LinkerIsGnuLd = solaris::isLinkerGnuLd(TC, Args); // Solaris ld defaults to --export-dynamic behaviour but doesn't support // the option, so don't try to pass it. if (TC.getTriple().isOSSolaris() && !LinkerIsGnuLd) 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")); return true; } return false; } void tools::addAsNeededOption(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, bool as_needed) { assert(!TC.getTriple().isOSAIX() && "AIX linker does not support any form of --as-needed option yet."); bool LinkerIsGnuLd = solaris::isLinkerGnuLd(TC, Args); // While the Solaris 11.2 ld added --as-needed/--no-as-needed as aliases // for the native forms -z ignore/-z record, they are missing in Illumos, // so always use the native form. // GNU ld doesn't support -z ignore/-z record, so don't use them even on // Solaris. if (TC.getTriple().isOSSolaris() && !LinkerIsGnuLd) { CmdArgs.push_back("-z"); CmdArgs.push_back(as_needed ? "ignore" : "record"); } else { CmdArgs.push_back(as_needed ? "--as-needed" : "--no-as-needed"); } } void tools::linkSanitizerRuntimeDeps(const ToolChain &TC, const llvm::opt::ArgList &Args, ArgStringList &CmdArgs) { // Force linking against the system libraries sanitizers depends on // (see PR15823 why this is necessary). addAsNeededOption(TC, Args, CmdArgs, false); // There's no libpthread or librt on RTEMS & Android. if (TC.getTriple().getOS() != llvm::Triple::RTEMS && !TC.getTriple().isAndroid() && !TC.getTriple().isOHOSFamily()) { CmdArgs.push_back("-lpthread"); if (!TC.getTriple().isOSOpenBSD()) CmdArgs.push_back("-lrt"); } CmdArgs.push_back("-lm"); // There's no libdl on all OSes. if (!TC.getTriple().isOSFreeBSD() && !TC.getTriple().isOSNetBSD() && !TC.getTriple().isOSOpenBSD() && TC.getTriple().getOS() != llvm::Triple::RTEMS) CmdArgs.push_back("-ldl"); // Required for backtrace on some OSes if (TC.getTriple().isOSFreeBSD() || TC.getTriple().isOSNetBSD() || TC.getTriple().isOSOpenBSD()) CmdArgs.push_back("-lexecinfo"); // There is no libresolv on Android, FreeBSD, OpenBSD, etc. On musl // libresolv.a, even if exists, is an empty archive to satisfy POSIX -lresolv // requirement. if (TC.getTriple().isOSLinux() && !TC.getTriple().isAndroid() && !TC.getTriple().isMusl()) CmdArgs.push_back("-lresolv"); } static void collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, SmallVectorImpl &SharedRuntimes, SmallVectorImpl &StaticRuntimes, SmallVectorImpl &NonWholeStaticRuntimes, SmallVectorImpl &HelperStaticRuntimes, SmallVectorImpl &RequiredSymbols) { const SanitizerArgs &SanArgs = TC.getSanitizerArgs(Args); // Collect shared runtimes. if (SanArgs.needsSharedRt()) { if (SanArgs.needsAsanRt()) { SharedRuntimes.push_back("asan"); if (!Args.hasArg(options::OPT_shared) && !TC.getTriple().isAndroid()) HelperStaticRuntimes.push_back("asan-preinit"); } if (SanArgs.needsMemProfRt()) { SharedRuntimes.push_back("memprof"); if (!Args.hasArg(options::OPT_shared) && !TC.getTriple().isAndroid()) HelperStaticRuntimes.push_back("memprof-preinit"); } if (SanArgs.needsUbsanRt()) { if (SanArgs.requiresMinimalRuntime()) SharedRuntimes.push_back("ubsan_minimal"); else SharedRuntimes.push_back("ubsan_standalone"); } if (SanArgs.needsScudoRt()) { SharedRuntimes.push_back("scudo_standalone"); } if (SanArgs.needsTsanRt()) SharedRuntimes.push_back("tsan"); if (SanArgs.needsHwasanRt()) { if (SanArgs.needsHwasanAliasesRt()) SharedRuntimes.push_back("hwasan_aliases"); else SharedRuntimes.push_back("hwasan"); if (!Args.hasArg(options::OPT_shared)) HelperStaticRuntimes.push_back("hwasan-preinit"); } } // The stats_client library is also statically linked into DSOs. if (SanArgs.needsStatsRt()) StaticRuntimes.push_back("stats_client"); // Always link the static runtime regardless of DSO or executable. if (SanArgs.needsAsanRt()) HelperStaticRuntimes.push_back("asan_static"); // Collect static runtimes. if (Args.hasArg(options::OPT_shared)) { // Don't link static runtimes into DSOs. return; } // Each static runtime that has a DSO counterpart above is excluded below, // but runtimes that exist only as static are not affected by needsSharedRt. if (!SanArgs.needsSharedRt() && SanArgs.needsAsanRt()) { StaticRuntimes.push_back("asan"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("asan_cxx"); } if (!SanArgs.needsSharedRt() && SanArgs.needsMemProfRt()) { StaticRuntimes.push_back("memprof"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("memprof_cxx"); } if (!SanArgs.needsSharedRt() && SanArgs.needsHwasanRt()) { if (SanArgs.needsHwasanAliasesRt()) { StaticRuntimes.push_back("hwasan_aliases"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("hwasan_aliases_cxx"); } else { StaticRuntimes.push_back("hwasan"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("hwasan_cxx"); } } if (SanArgs.needsDfsanRt()) StaticRuntimes.push_back("dfsan"); if (SanArgs.needsLsanRt()) StaticRuntimes.push_back("lsan"); if (SanArgs.needsMsanRt()) { StaticRuntimes.push_back("msan"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("msan_cxx"); } if (!SanArgs.needsSharedRt() && SanArgs.needsTsanRt()) { StaticRuntimes.push_back("tsan"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("tsan_cxx"); } if (!SanArgs.needsSharedRt() && SanArgs.needsUbsanRt()) { if (SanArgs.requiresMinimalRuntime()) { StaticRuntimes.push_back("ubsan_minimal"); } else { StaticRuntimes.push_back("ubsan_standalone"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("ubsan_standalone_cxx"); } } if (SanArgs.needsSafeStackRt()) { NonWholeStaticRuntimes.push_back("safestack"); RequiredSymbols.push_back("__safestack_init"); } if (!(SanArgs.needsSharedRt() && SanArgs.needsUbsanRt())) { if (SanArgs.needsCfiRt()) StaticRuntimes.push_back("cfi"); if (SanArgs.needsCfiDiagRt()) { StaticRuntimes.push_back("cfi_diag"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("ubsan_standalone_cxx"); } } if (SanArgs.needsStatsRt()) { NonWholeStaticRuntimes.push_back("stats"); RequiredSymbols.push_back("__sanitizer_stats_register"); } if (!SanArgs.needsSharedRt() && SanArgs.needsScudoRt()) { StaticRuntimes.push_back("scudo_standalone"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("scudo_standalone_cxx"); } } // Should be called before we add system libraries (C++ ABI, libstdc++/libc++, // C runtime, etc). Returns true if sanitizer system deps need to be linked in. bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { const SanitizerArgs &SanArgs = TC.getSanitizerArgs(Args); SmallVector SharedRuntimes, StaticRuntimes, NonWholeStaticRuntimes, HelperStaticRuntimes, RequiredSymbols; if (SanArgs.linkRuntimes()) { collectSanitizerRuntimes(TC, Args, SharedRuntimes, StaticRuntimes, NonWholeStaticRuntimes, HelperStaticRuntimes, RequiredSymbols); } // Inject libfuzzer dependencies. if (SanArgs.needsFuzzer() && SanArgs.linkRuntimes() && !Args.hasArg(options::OPT_shared)) { addSanitizerRuntime(TC, Args, CmdArgs, "fuzzer", false, true); if (SanArgs.needsFuzzerInterceptors()) addSanitizerRuntime(TC, Args, CmdArgs, "fuzzer_interceptors", false, true); if (!Args.hasArg(clang::driver::options::OPT_nostdlibxx)) { bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) && !Args.hasArg(options::OPT_static); if (OnlyLibstdcxxStatic) CmdArgs.push_back("-Bstatic"); TC.AddCXXStdlibLibArgs(Args, CmdArgs); if (OnlyLibstdcxxStatic) CmdArgs.push_back("-Bdynamic"); } } for (auto RT : SharedRuntimes) addSanitizerRuntime(TC, Args, CmdArgs, RT, true, false); for (auto RT : HelperStaticRuntimes) addSanitizerRuntime(TC, Args, CmdArgs, RT, false, true); bool AddExportDynamic = false; for (auto RT : StaticRuntimes) { addSanitizerRuntime(TC, Args, CmdArgs, RT, false, true); AddExportDynamic |= !addSanitizerDynamicList(TC, Args, CmdArgs, RT); } for (auto RT : NonWholeStaticRuntimes) { addSanitizerRuntime(TC, Args, CmdArgs, RT, false, false); AddExportDynamic |= !addSanitizerDynamicList(TC, Args, CmdArgs, RT); } for (auto S : RequiredSymbols) { CmdArgs.push_back("-u"); CmdArgs.push_back(Args.MakeArgString(S)); } // 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"); if (SanArgs.hasCrossDsoCfi() && !AddExportDynamic) CmdArgs.push_back("--export-dynamic-symbol=__cfi_check"); if (SanArgs.hasMemTag()) { if (!TC.getTriple().isAndroid()) { TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << "-fsanitize=memtag*" << TC.getTriple().str(); } CmdArgs.push_back( Args.MakeArgString("--android-memtag-mode=" + SanArgs.getMemtagMode())); if (SanArgs.hasMemtagHeap()) CmdArgs.push_back("--android-memtag-heap"); if (SanArgs.hasMemtagStack()) CmdArgs.push_back("--android-memtag-stack"); } 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")); for (const auto &Mode : TC.getXRayArgs().modeList()) CmdArgs.push_back(TC.getCompilerRTArgString(Args, Mode)); CmdArgs.push_back("--no-whole-archive"); return true; } return false; } void tools::linkXRayRuntimeDeps(const ToolChain &TC, const llvm::opt::ArgList &Args, ArgStringList &CmdArgs) { addAsNeededOption(TC, Args, CmdArgs, false); CmdArgs.push_back("-lpthread"); if (!TC.getTriple().isOSOpenBSD()) CmdArgs.push_back("-lrt"); CmdArgs.push_back("-lm"); if (!TC.getTriple().isOSFreeBSD() && !TC.getTriple().isOSNetBSD() && !TC.getTriple().isOSOpenBSD()) 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)) return !A->getOption().matches(options::OPT_O0); // Defaults to -O0. return false; } const char *tools::SplitDebugName(const JobAction &JA, const ArgList &Args, const InputInfo &Input, const InputInfo &Output) { auto AddPostfix = [JA](auto &F) { if (JA.getOffloadingDeviceKind() == Action::OFK_HIP) F += (Twine("_") + JA.getOffloadingArch()).str(); F += ".dwo"; }; if (Arg *A = Args.getLastArg(options::OPT_gsplit_dwarf_EQ)) if (StringRef(A->getValue()) == "single" && Output.isFilename()) return Args.MakeArgString(Output.getFilename()); SmallString<128> T; if (const Arg *A = Args.getLastArg(options::OPT_dumpdir)) { T = A->getValue(); } else { Arg *FinalOutput = Args.getLastArg(options::OPT_o, options::OPT__SLASH_o); if (FinalOutput && Args.hasArg(options::OPT_c)) { T = FinalOutput->getValue(); llvm::sys::path::remove_filename(T); llvm::sys::path::append(T, llvm::sys::path::stem(FinalOutput->getValue())); AddPostfix(T); return Args.MakeArgString(T); } } T += llvm::sys::path::stem(Input.getBaseInput()); AddPostfix(T); return Args.MakeArgString(T); } void tools::SplitDebugInfo(const ToolChain &TC, Compilation &C, const Tool &T, const JobAction &JA, const ArgList &Args, const InputInfo &Output, const char *OutFile) { ArgStringList ExtractArgs; ExtractArgs.push_back("--extract-dwo"); ArgStringList StripArgs; StripArgs.push_back("--strip-dwo"); // Grabbing the output of the earlier compile step. StripArgs.push_back(Output.getFilename()); ExtractArgs.push_back(Output.getFilename()); ExtractArgs.push_back(OutFile); const char *Exec = Args.MakeArgString(TC.GetProgramPath(CLANG_DEFAULT_OBJCOPY)); InputInfo II(types::TY_Object, Output.getFilename(), Output.getFilename()); // First extract the dwo sections. C.addCommand(std::make_unique(JA, T, ResponseFileSupport::AtFileCurCP(), Exec, ExtractArgs, II, Output)); // Then remove them from the original .o file. C.addCommand(std::make_unique( JA, T, ResponseFileSupport::AtFileCurCP(), Exec, StripArgs, II, Output)); } // Claim options we don't want to warn if they are unused. We do this for // options that build systems might add but are unused when assembling or only // running the preprocessor for example. void tools::claimNoWarnArgs(const ArgList &Args) { // Don't warn about unused -f(no-)?lto. This can happen when we're // preprocessing, precompiling or assembling. Args.ClaimAllArgs(options::OPT_flto_EQ); Args.ClaimAllArgs(options::OPT_flto); Args.ClaimAllArgs(options::OPT_fno_lto); } Arg *tools::getLastCSProfileGenerateArg(const ArgList &Args) { auto *CSPGOGenerateArg = Args.getLastArg(options::OPT_fcs_profile_generate, options::OPT_fcs_profile_generate_EQ, options::OPT_fno_profile_generate); if (CSPGOGenerateArg && CSPGOGenerateArg->getOption().matches(options::OPT_fno_profile_generate)) CSPGOGenerateArg = nullptr; return CSPGOGenerateArg; } Arg *tools::getLastProfileUseArg(const ArgList &Args) { auto *ProfileUseArg = Args.getLastArg( options::OPT_fprofile_instr_use, options::OPT_fprofile_instr_use_EQ, options::OPT_fprofile_use, options::OPT_fprofile_use_EQ, options::OPT_fno_profile_instr_use); if (ProfileUseArg && ProfileUseArg->getOption().matches(options::OPT_fno_profile_instr_use)) ProfileUseArg = nullptr; return ProfileUseArg; } Arg *tools::getLastProfileSampleUseArg(const ArgList &Args) { auto *ProfileSampleUseArg = Args.getLastArg( options::OPT_fprofile_sample_use, options::OPT_fprofile_sample_use_EQ, options::OPT_fauto_profile, options::OPT_fauto_profile_EQ, options::OPT_fno_profile_sample_use, options::OPT_fno_auto_profile); if (ProfileSampleUseArg && (ProfileSampleUseArg->getOption().matches( options::OPT_fno_profile_sample_use) || ProfileSampleUseArg->getOption().matches(options::OPT_fno_auto_profile))) return nullptr; return Args.getLastArg(options::OPT_fprofile_sample_use_EQ, options::OPT_fauto_profile_EQ); } const char *tools::RelocationModelName(llvm::Reloc::Model Model) { switch (Model) { case llvm::Reloc::Static: return "static"; case llvm::Reloc::PIC_: return "pic"; case llvm::Reloc::DynamicNoPIC: return "dynamic-no-pic"; case llvm::Reloc::ROPI: return "ropi"; case llvm::Reloc::RWPI: return "rwpi"; case llvm::Reloc::ROPI_RWPI: return "ropi-rwpi"; } llvm_unreachable("Unknown Reloc::Model kind"); } /// Parses the various -fpic/-fPIC/-fpie/-fPIE arguments. Then, /// smooshes them together with platform defaults, to decide whether /// this compile should be using PIC mode or not. Returns a tuple of /// (RelocationModel, PICLevel, IsPIE). std::tuple tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) { const llvm::Triple &EffectiveTriple = ToolChain.getEffectiveTriple(); const llvm::Triple &Triple = ToolChain.getTriple(); bool PIE = ToolChain.isPIEDefault(Args); bool PIC = PIE || ToolChain.isPICDefault(); // The Darwin/MachO default to use PIC does not apply when using -static. if (Triple.isOSBinFormatMachO() && Args.hasArg(options::OPT_static)) PIE = PIC = false; bool IsPICLevelTwo = PIC; bool KernelOrKext = Args.hasArg(options::OPT_mkernel, options::OPT_fapple_kext); // Android-specific defaults for PIC/PIE if (Triple.isAndroid()) { switch (Triple.getArch()) { case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: case llvm::Triple::aarch64: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: PIC = true; // "-fpic" break; case llvm::Triple::x86: case llvm::Triple::x86_64: PIC = true; // "-fPIC" IsPICLevelTwo = true; break; default: break; } } // OHOS-specific defaults for PIC/PIE if (Triple.isOHOSFamily() && Triple.getArch() == llvm::Triple::aarch64) PIC = true; // OpenBSD-specific defaults for PIE if (Triple.isOSOpenBSD()) { switch (ToolChain.getArch()) { case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::x86: case llvm::Triple::x86_64: IsPICLevelTwo = false; // "-fpie" break; case llvm::Triple::ppc: case llvm::Triple::sparcv9: IsPICLevelTwo = true; // "-fPIE" break; default: break; } } // 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 // option implicitly enables PIC at the same level. Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC, options::OPT_fpic, options::OPT_fno_pic, options::OPT_fPIE, options::OPT_fno_PIE, options::OPT_fpie, options::OPT_fno_pie); if (Triple.isOSWindows() && !Triple.isOSCygMing() && LastPICArg && LastPICArg == Args.getLastArg(options::OPT_fPIC, options::OPT_fpic, options::OPT_fPIE, options::OPT_fpie)) { ToolChain.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << LastPICArg->getSpelling() << Triple.str(); if (Triple.getArch() == llvm::Triple::x86_64) return std::make_tuple(llvm::Reloc::PIC_, 2U, false); return std::make_tuple(llvm::Reloc::Static, 0U, false); } // Check whether the tool chain trumps the PIC-ness decision. If the PIC-ness // is forced, then neither PIC nor PIE flags will have no effect. if (!ToolChain.isPICDefaultForced()) { if (LastPICArg) { Option O = LastPICArg->getOption(); if (O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic) || O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie)) { PIE = O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie); PIC = PIE || O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic); IsPICLevelTwo = O.matches(options::OPT_fPIE) || O.matches(options::OPT_fPIC); } else { PIE = PIC = false; if (EffectiveTriple.isPS()) { Arg *ModelArg = Args.getLastArg(options::OPT_mcmodel_EQ); StringRef Model = ModelArg ? ModelArg->getValue() : ""; if (Model != "kernel") { PIC = true; ToolChain.getDriver().Diag(diag::warn_drv_ps_force_pic) << LastPICArg->getSpelling() << (EffectiveTriple.isPS4() ? "PS4" : "PS5"); } } } } } // Introduce a Darwin and PS4/PS5-specific hack. If the default is PIC, but // the PIC level would've been set to level 1, force it back to level 2 PIC // instead. if (PIC && (Triple.isOSDarwin() || EffectiveTriple.isPS())) IsPICLevelTwo |= ToolChain.isPICDefault(); // This kernel flags are a trump-card: they will disable PIC/PIE // generation, independent of the argument order. if (KernelOrKext && ((!EffectiveTriple.isiOS() || EffectiveTriple.isOSVersionLT(6)) && !EffectiveTriple.isWatchOS() && !EffectiveTriple.isDriverKit())) PIC = PIE = false; if (Arg *A = Args.getLastArg(options::OPT_mdynamic_no_pic)) { // This is a very special mode. It trumps the other modes, almost no one // uses it, and it isn't even valid on any OS but Darwin. if (!Triple.isOSDarwin()) ToolChain.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << A->getSpelling() << Triple.str(); // FIXME: Warn when this flag trumps some other PIC or PIE flag. // Only a forced PIC mode can cause the actual compile to have PIC defines // etc., no flags are sufficient. This behavior was selected to closely // match that of llvm-gcc and Apple GCC before that. PIC = ToolChain.isPICDefault() && ToolChain.isPICDefaultForced(); return std::make_tuple(llvm::Reloc::DynamicNoPIC, PIC ? 2U : 0U, false); } bool EmbeddedPISupported; switch (Triple.getArch()) { case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: EmbeddedPISupported = true; break; default: EmbeddedPISupported = false; break; } bool ROPI = false, RWPI = false; Arg* LastROPIArg = Args.getLastArg(options::OPT_fropi, options::OPT_fno_ropi); if (LastROPIArg && LastROPIArg->getOption().matches(options::OPT_fropi)) { if (!EmbeddedPISupported) ToolChain.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << LastROPIArg->getSpelling() << Triple.str(); ROPI = true; } Arg *LastRWPIArg = Args.getLastArg(options::OPT_frwpi, options::OPT_fno_rwpi); if (LastRWPIArg && LastRWPIArg->getOption().matches(options::OPT_frwpi)) { if (!EmbeddedPISupported) ToolChain.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) << LastRWPIArg->getSpelling() << Triple.str(); RWPI = true; } // 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); 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); llvm::Reloc::Model RelocM = llvm::Reloc::Static; if (ROPI && RWPI) RelocM = llvm::Reloc::ROPI_RWPI; else if (ROPI) RelocM = llvm::Reloc::ROPI; else if (RWPI) RelocM = llvm::Reloc::RWPI; 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::addDebugInfoKind( ArgStringList &CmdArgs, llvm::codegenoptions::DebugInfoKind DebugInfoKind) { switch (DebugInfoKind) { case llvm::codegenoptions::DebugDirectivesOnly: CmdArgs.push_back("-debug-info-kind=line-directives-only"); break; case llvm::codegenoptions::DebugLineTablesOnly: CmdArgs.push_back("-debug-info-kind=line-tables-only"); break; case llvm::codegenoptions::DebugInfoConstructor: CmdArgs.push_back("-debug-info-kind=constructor"); break; case llvm::codegenoptions::LimitedDebugInfo: CmdArgs.push_back("-debug-info-kind=limited"); break; case llvm::codegenoptions::FullDebugInfo: CmdArgs.push_back("-debug-info-kind=standalone"); break; case llvm::codegenoptions::UnusedTypeInfo: CmdArgs.push_back("-debug-info-kind=unused-types"); break; default: break; } } // Convert an arg of the form "-gN" or "-ggdbN" or one of their aliases // to the corresponding DebugInfoKind. llvm::codegenoptions::DebugInfoKind tools::debugLevelToInfoKind(const Arg &A) { assert(A.getOption().matches(options::OPT_gN_Group) && "Not a -g option that specifies a debug-info level"); if (A.getOption().matches(options::OPT_g0) || A.getOption().matches(options::OPT_ggdb0)) return llvm::codegenoptions::NoDebugInfo; if (A.getOption().matches(options::OPT_gline_tables_only) || A.getOption().matches(options::OPT_ggdb1)) return llvm::codegenoptions::DebugLineTablesOnly; if (A.getOption().matches(options::OPT_gline_directives_only)) return llvm::codegenoptions::DebugDirectivesOnly; return llvm::codegenoptions::DebugInfoConstructor; } static unsigned ParseDebugDefaultVersion(const ToolChain &TC, const ArgList &Args) { const Arg *A = Args.getLastArg(options::OPT_fdebug_default_version); if (!A) return 0; unsigned Value = 0; if (StringRef(A->getValue()).getAsInteger(10, Value) || Value > 5 || Value < 2) TC.getDriver().Diag(diag::err_drv_invalid_int_value) << A->getAsString(Args) << A->getValue(); return Value; } unsigned tools::DwarfVersionNum(StringRef ArgValue) { return llvm::StringSwitch(ArgValue) .Case("-gdwarf-2", 2) .Case("-gdwarf-3", 3) .Case("-gdwarf-4", 4) .Case("-gdwarf-5", 5) .Default(0); } const Arg *tools::getDwarfNArg(const ArgList &Args) { return Args.getLastArg(options::OPT_gdwarf_2, options::OPT_gdwarf_3, options::OPT_gdwarf_4, options::OPT_gdwarf_5, options::OPT_gdwarf); } unsigned tools::getDwarfVersion(const ToolChain &TC, const llvm::opt::ArgList &Args) { unsigned DwarfVersion = ParseDebugDefaultVersion(TC, Args); if (const Arg *GDwarfN = getDwarfNArg(Args)) if (int N = DwarfVersionNum(GDwarfN->getSpelling())) DwarfVersion = N; if (DwarfVersion == 0) { DwarfVersion = TC.GetDefaultDwarfVersion(); assert(DwarfVersion && "toolchain default DWARF version must be nonzero"); } return DwarfVersion; } void tools::AddAssemblerKPIC(const ToolChain &ToolChain, const ArgList &Args, ArgStringList &CmdArgs) { llvm::Reloc::Model RelocationModel; unsigned PICLevel; bool IsPIE; std::tie(RelocationModel, PICLevel, IsPIE) = ParsePICArgs(ToolChain, Args); if (RelocationModel != llvm::Reloc::Static) CmdArgs.push_back("-KPIC"); } /// 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); } enum class LibGccType { UnspecifiedLibGcc, StaticLibGcc, SharedLibGcc }; static LibGccType getLibGccType(const ToolChain &TC, const Driver &D, const ArgList &Args) { if (Args.hasArg(options::OPT_static_libgcc) || Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_static_pie) || // The Android NDK only provides libunwind.a, not libunwind.so. TC.getTriple().isAndroid()) return LibGccType::StaticLibGcc; if (Args.hasArg(options::OPT_shared_libgcc)) return LibGccType::SharedLibGcc; return LibGccType::UnspecifiedLibGcc; } // Gcc adds libgcc arguments in various ways: // // gcc : -lgcc --as-needed -lgcc_s --no-as-needed // g++ : -lgcc_s -lgcc // gcc shared: -lgcc_s -lgcc // g++ shared: -lgcc_s -lgcc // gcc static: -lgcc -lgcc_eh // g++ static: -lgcc -lgcc_eh // gcc static-pie: -lgcc -lgcc_eh // g++ static-pie: -lgcc -lgcc_eh // // Also, certain targets need additional adjustments. static void AddUnwindLibrary(const ToolChain &TC, const Driver &D, ArgStringList &CmdArgs, const ArgList &Args) { ToolChain::UnwindLibType UNW = TC.GetUnwindLibType(Args); // By default OHOS binaries are linked statically to libunwind. if (TC.getTriple().isOHOSFamily() && UNW == ToolChain::UNW_CompilerRT) { CmdArgs.push_back("-l:libunwind.a"); return; } // Targets that don't use unwind libraries. if ((TC.getTriple().isAndroid() && UNW == ToolChain::UNW_Libgcc) || TC.getTriple().isOSIAMCU() || TC.getTriple().isOSBinFormatWasm() || TC.getTriple().isWindowsMSVCEnvironment() || UNW == ToolChain::UNW_None) return; LibGccType LGT = getLibGccType(TC, D, Args); bool AsNeeded = LGT == LibGccType::UnspecifiedLibGcc && (UNW == ToolChain::UNW_CompilerRT || !D.CCCIsCXX()) && !TC.getTriple().isAndroid() && !TC.getTriple().isOSCygMing() && !TC.getTriple().isOSAIX(); if (AsNeeded) addAsNeededOption(TC, Args, CmdArgs, true); switch (UNW) { case ToolChain::UNW_None: return; case ToolChain::UNW_Libgcc: { if (LGT == LibGccType::StaticLibGcc) CmdArgs.push_back("-lgcc_eh"); else CmdArgs.push_back("-lgcc_s"); break; } case ToolChain::UNW_CompilerRT: if (TC.getTriple().isOSAIX()) { // AIX only has libunwind as a shared library. So do not pass // anything in if -static is specified. if (LGT != LibGccType::StaticLibGcc) CmdArgs.push_back("-lunwind"); } else if (LGT == LibGccType::StaticLibGcc) { CmdArgs.push_back("-l:libunwind.a"); } else if (LGT == LibGccType::SharedLibGcc) { if (TC.getTriple().isOSCygMing()) CmdArgs.push_back("-l:libunwind.dll.a"); else CmdArgs.push_back("-l:libunwind.so"); } else { // Let the linker choose between libunwind.so and libunwind.a // depending on what's available, and depending on the -static flag CmdArgs.push_back("-lunwind"); } break; } if (AsNeeded) addAsNeededOption(TC, Args, CmdArgs, false); } static void AddLibgcc(const ToolChain &TC, const Driver &D, ArgStringList &CmdArgs, const ArgList &Args) { LibGccType LGT = getLibGccType(TC, D, Args); if (LGT == LibGccType::StaticLibGcc || (LGT == LibGccType::UnspecifiedLibGcc && !D.CCCIsCXX())) CmdArgs.push_back("-lgcc"); AddUnwindLibrary(TC, D, CmdArgs, Args); if (LGT == LibGccType::SharedLibGcc || (LGT == LibGccType::UnspecifiedLibGcc && D.CCCIsCXX())) CmdArgs.push_back("-lgcc"); } void tools::AddRunTimeLibs(const ToolChain &TC, const Driver &D, ArgStringList &CmdArgs, const ArgList &Args) { // Make use of compiler-rt if --rtlib option is used ToolChain::RuntimeLibType RLT = TC.GetRuntimeLibType(Args); switch (RLT) { case ToolChain::RLT_CompilerRT: CmdArgs.push_back(TC.getCompilerRTArgString(Args, "builtins")); AddUnwindLibrary(TC, D, CmdArgs, Args); break; case ToolChain::RLT_Libgcc: // Make sure libgcc is not used under MSVC environment by default if (TC.getTriple().isKnownWindowsMSVCEnvironment()) { // Issue error diagnostic if libgcc is explicitly specified // through command line as --rtlib option argument. Arg *A = Args.getLastArg(options::OPT_rtlib_EQ); if (A && A->getValue() != StringRef("platform")) { TC.getDriver().Diag(diag::err_drv_unsupported_rtlib_for_platform) << A->getValue() << "MSVC"; } } else AddLibgcc(TC, D, CmdArgs, Args); break; } // On Android, the unwinder uses dl_iterate_phdr (or one of // dl_unwind_find_exidx/__gnu_Unwind_Find_exidx on arm32) from libdl.so. For // statically-linked executables, these functions come from libc.a instead. if (TC.getTriple().isAndroid() && !Args.hasArg(options::OPT_static) && !Args.hasArg(options::OPT_static_pie)) CmdArgs.push_back("-ldl"); } 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 && !D.CCPrintInternalStats) return {}; SmallString<128> StatsFile; if (A) { StringRef SaveStats = A->getValue(); 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"); } else { assert(D.CCPrintInternalStats); StatsFile.assign(D.CCPrintInternalStatReportFilename.empty() ? "-" : D.CCPrintInternalStatReportFilename); } return StatsFile; } void tools::addMultilibFlag(bool Enabled, const StringRef Flag, Multilib::flags_list &Flags) { assert(Flag.front() == '-'); if (Enabled) { Flags.push_back(Flag.str()); } else { Flags.push_back(("!" + Flag.substr(1)).str()); } } void tools::addX86AlignBranchArgs(const Driver &D, const ArgList &Args, ArgStringList &CmdArgs, bool IsLTO, const StringRef PluginOptPrefix) { auto addArg = [&, IsLTO](const Twine &Arg) { if (IsLTO) { assert(!PluginOptPrefix.empty() && "Cannot have empty PluginOptPrefix!"); CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + Arg)); } else { CmdArgs.push_back("-mllvm"); CmdArgs.push_back(Args.MakeArgString(Arg)); } }; if (Args.hasArg(options::OPT_mbranches_within_32B_boundaries)) { addArg(Twine("-x86-branches-within-32B-boundaries")); } if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_boundary_EQ)) { StringRef Value = A->getValue(); unsigned Boundary; if (Value.getAsInteger(10, Boundary) || Boundary < 16 || !llvm::isPowerOf2_64(Boundary)) { D.Diag(diag::err_drv_invalid_argument_to_option) << Value << A->getOption().getName(); } else { addArg("-x86-align-branch-boundary=" + Twine(Boundary)); } } if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_EQ)) { std::string AlignBranch; for (StringRef T : A->getValues()) { if (T != "fused" && T != "jcc" && T != "jmp" && T != "call" && T != "ret" && T != "indirect") D.Diag(diag::err_drv_invalid_malign_branch_EQ) << T << "fused, jcc, jmp, call, ret, indirect"; if (!AlignBranch.empty()) AlignBranch += '+'; AlignBranch += T; } addArg("-x86-align-branch=" + Twine(AlignBranch)); } if (const Arg *A = Args.getLastArg(options::OPT_mpad_max_prefix_size_EQ)) { StringRef Value = A->getValue(); unsigned PrefixSize; if (Value.getAsInteger(10, PrefixSize)) { D.Diag(diag::err_drv_invalid_argument_to_option) << Value << A->getOption().getName(); } else { addArg("-x86-pad-max-prefix-size=" + Twine(PrefixSize)); } } } /// SDLSearch: Search for Static Device Library /// The search for SDL bitcode files is consistent with how static host /// libraries are discovered. That is, the -l option triggers a search for /// files in a set of directories called the LINKPATH. The host library search /// procedure looks for a specific filename in the LINKPATH. The filename for /// a host library is lib.a or lib.so. For SDLs, there is an /// ordered-set of filenames that are searched. We call this ordered-set of /// filenames as SEARCH-ORDER. Since an SDL can either be device-type specific, /// architecture specific, or generic across all architectures, a naming /// convention and search order is used where the file name embeds the /// architecture name (nvptx or amdgcn) and the GPU device type /// such as sm_30 and gfx906. is absent in case of /// device-independent SDLs. To reduce congestion in host library directories, /// the search first looks for files in the “libdevice” subdirectory. SDLs that /// are bc files begin with the prefix “lib”. /// /// Machine-code SDLs can also be managed as an archive (*.a file). The /// convention has been to use the prefix “lib”. To avoid confusion with host /// archive libraries, we use prefix "libbc-" for the bitcode SDL archives. /// static bool SDLSearch(const Driver &D, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, const SmallVectorImpl &LibraryPaths, StringRef Lib, StringRef Arch, StringRef Target, bool isBitCodeSDL) { SmallVector SDLs; std::string LibDeviceLoc = "/libdevice"; std::string LibBcPrefix = "/libbc-"; std::string LibPrefix = "/lib"; if (isBitCodeSDL) { // SEARCH-ORDER for Bitcode SDLs: // libdevice/libbc---.a // libbc---.a // libdevice/libbc--.a // libbc--.a // libdevice/libbc-.a // libbc-.a // libdevice/lib--.bc // lib--.bc // libdevice/lib-.bc // lib-.bc // libdevice/lib.bc // lib.bc for (StringRef Base : {LibBcPrefix, LibPrefix}) { const auto *Ext = Base.contains(LibBcPrefix) ? ".a" : ".bc"; for (auto Suffix : {Twine(Lib + "-" + Arch + "-" + Target).str(), Twine(Lib + "-" + Arch).str(), Twine(Lib).str()}) { SDLs.push_back(Twine(LibDeviceLoc + Base + Suffix + Ext).str()); SDLs.push_back(Twine(Base + Suffix + Ext).str()); } } } else { // SEARCH-ORDER for Machine-code SDLs: // libdevice/lib--.a // lib--.a // libdevice/lib-.a // lib-.a const auto *Ext = ".a"; for (auto Suffix : {Twine(Lib + "-" + Arch + "-" + Target).str(), Twine(Lib + "-" + Arch).str()}) { SDLs.push_back(Twine(LibDeviceLoc + LibPrefix + Suffix + Ext).str()); SDLs.push_back(Twine(LibPrefix + Suffix + Ext).str()); } } // The CUDA toolchain does not use a global device llvm-link before the LLVM // backend generates ptx. So currently, the use of bitcode SDL for nvptx is // only possible with post-clang-cc1 linking. Clang cc1 has a feature that // will link libraries after clang compilation while the LLVM IR is still in // memory. This utilizes a clang cc1 option called “-mlink-builtin-bitcode”. // This is a clang -cc1 option that is generated by the clang driver. The // option value must a full path to an existing file. bool FoundSDL = false; for (auto LPath : LibraryPaths) { for (auto SDL : SDLs) { auto FullName = Twine(LPath + SDL).str(); if (llvm::sys::fs::exists(FullName)) { CC1Args.push_back(DriverArgs.MakeArgString(FullName)); FoundSDL = true; break; } } if (FoundSDL) break; } return FoundSDL; } /// Search if a user provided archive file lib.a exists in any of /// the library paths. If so, add a new command to clang-offload-bundler to /// unbundle this archive and create a temporary device specific archive. Name /// of this SDL is passed to the llvm-link tool. static void GetSDLFromOffloadArchive( Compilation &C, const Driver &D, const Tool &T, const JobAction &JA, const InputInfoList &Inputs, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, const SmallVectorImpl &LibraryPaths, StringRef Lib, StringRef Arch, StringRef Target, bool isBitCodeSDL) { // We don't support bitcode archive bundles for nvptx if (isBitCodeSDL && Arch.contains("nvptx")) return; bool FoundAOB = false; std::string ArchiveOfBundles; llvm::Triple Triple(D.getTargetTriple()); bool IsMSVC = Triple.isWindowsMSVCEnvironment(); auto Ext = IsMSVC ? ".lib" : ".a"; if (!Lib.starts_with(":") && !Lib.starts_with("-l")) { if (llvm::sys::fs::exists(Lib)) { ArchiveOfBundles = Lib; FoundAOB = true; } } else { Lib.consume_front("-l"); for (auto LPath : LibraryPaths) { ArchiveOfBundles.clear(); auto LibFile = (Lib.starts_with(":") ? Lib.drop_front() : IsMSVC ? Lib + Ext : "lib" + Lib + Ext) .str(); for (auto Prefix : {"/libdevice/", "/"}) { auto AOB = Twine(LPath + Prefix + LibFile).str(); if (llvm::sys::fs::exists(AOB)) { ArchiveOfBundles = AOB; FoundAOB = true; break; } } if (FoundAOB) break; } } if (!FoundAOB) return; llvm::file_magic Magic; auto EC = llvm::identify_magic(ArchiveOfBundles, Magic); if (EC || Magic != llvm::file_magic::archive) return; StringRef Prefix = isBitCodeSDL ? "libbc-" : "lib"; std::string OutputLib = D.GetTemporaryPath(Twine(Prefix + llvm::sys::path::filename(Lib) + "-" + Arch + "-" + Target) .str(), "a"); C.addTempFile(C.getArgs().MakeArgString(OutputLib)); ArgStringList CmdArgs; SmallString<128> DeviceTriple; DeviceTriple += Action::GetOffloadKindName(JA.getOffloadingDeviceKind()); DeviceTriple += '-'; std::string NormalizedTriple = T.getToolChain().getTriple().normalize(); DeviceTriple += NormalizedTriple; if (!Target.empty()) { DeviceTriple += '-'; DeviceTriple += Target; } std::string UnbundleArg("-unbundle"); std::string TypeArg("-type=a"); std::string InputArg("-input=" + ArchiveOfBundles); std::string OffloadArg("-targets=" + std::string(DeviceTriple)); std::string OutputArg("-output=" + OutputLib); const char *UBProgram = DriverArgs.MakeArgString( T.getToolChain().GetProgramPath("clang-offload-bundler")); ArgStringList UBArgs; UBArgs.push_back(C.getArgs().MakeArgString(UnbundleArg)); UBArgs.push_back(C.getArgs().MakeArgString(TypeArg)); UBArgs.push_back(C.getArgs().MakeArgString(InputArg)); UBArgs.push_back(C.getArgs().MakeArgString(OffloadArg)); UBArgs.push_back(C.getArgs().MakeArgString(OutputArg)); // Add this flag to not exit from clang-offload-bundler if no compatible // code object is found in heterogenous archive library. std::string AdditionalArgs("-allow-missing-bundles"); UBArgs.push_back(C.getArgs().MakeArgString(AdditionalArgs)); // Add this flag to treat hip and hipv4 offload kinds as compatible with // openmp offload kind while extracting code objects from a heterogenous // archive library. Vice versa is also considered compatible. std::string HipCompatibleArgs("-hip-openmp-compatible"); UBArgs.push_back(C.getArgs().MakeArgString(HipCompatibleArgs)); C.addCommand(std::make_unique( JA, T, ResponseFileSupport::AtFileCurCP(), UBProgram, UBArgs, Inputs, InputInfo(&JA, C.getArgs().MakeArgString(OutputLib)))); CC1Args.push_back(DriverArgs.MakeArgString(OutputLib)); return; } // Wrapper function used by driver for adding SDLs during link phase. void tools::AddStaticDeviceLibsLinking(Compilation &C, const Tool &T, const JobAction &JA, const InputInfoList &Inputs, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, StringRef Arch, StringRef Target, bool isBitCodeSDL) { AddStaticDeviceLibs(&C, &T, &JA, &Inputs, C.getDriver(), DriverArgs, CC1Args, Arch, Target, isBitCodeSDL); } // User defined Static Device Libraries(SDLs) can be passed to clang for // offloading GPU compilers. Like static host libraries, the use of a SDL is // specified with the -l command line option. The primary difference between // host and SDLs is the filenames for SDLs (refer SEARCH-ORDER for Bitcode SDLs // and SEARCH-ORDER for Machine-code SDLs for the naming convention). // SDLs are of following types: // // * Bitcode SDLs: They can either be a *.bc file or an archive of *.bc files. // For NVPTX, these libraries are post-clang linked following each // compilation. For AMDGPU, these libraries are linked one time // during the application link phase. // // * Machine-code SDLs: They are archive files. For AMDGPU, the process for // machine code SDLs is still in development. But they will be linked // by the LLVM tool lld. // // * Bundled objects that contain both host and device codes: Bundled objects // may also contain library code compiled from source. For NVPTX, the // bundle contains cubin. For AMDGPU, the bundle contains bitcode. // // For Bitcode and Machine-code SDLs, current compiler toolchains hardcode the // inclusion of specific SDLs such as math libraries and the OpenMP device // library libomptarget. void tools::AddStaticDeviceLibs(Compilation *C, const Tool *T, const JobAction *JA, const InputInfoList *Inputs, const Driver &D, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, StringRef Arch, StringRef Target, bool isBitCodeSDL) { SmallVector LibraryPaths; // Add search directories from LIBRARY_PATH env variable std::optional LibPath = llvm::sys::Process::GetEnv("LIBRARY_PATH"); if (LibPath) { SmallVector Frags; const char EnvPathSeparatorStr[] = {llvm::sys::EnvPathSeparator, '\0'}; llvm::SplitString(*LibPath, Frags, EnvPathSeparatorStr); for (StringRef Path : Frags) LibraryPaths.emplace_back(Path.trim()); } // Add directories from user-specified -L options for (std::string Search_Dir : DriverArgs.getAllArgValues(options::OPT_L)) LibraryPaths.emplace_back(Search_Dir); // Add path to lib-debug folders SmallString<256> DefaultLibPath = llvm::sys::path::parent_path(D.Dir); llvm::sys::path::append(DefaultLibPath, CLANG_INSTALL_LIBDIR_BASENAME); LibraryPaths.emplace_back(DefaultLibPath.c_str()); // Build list of Static Device Libraries SDLs specified by -l option llvm::SmallSet SDLNames; static const StringRef HostOnlyArchives[] = { "omp", "cudart", "m", "gcc", "gcc_s", "pthread", "hip_hcc"}; for (auto SDLName : DriverArgs.getAllArgValues(options::OPT_l)) { if (!llvm::is_contained(HostOnlyArchives, SDLName)) { SDLNames.insert(std::string("-l") + SDLName); } } for (auto Input : DriverArgs.getAllArgValues(options::OPT_INPUT)) { auto FileName = StringRef(Input); // Clang treats any unknown file types as archives and passes them to the // linker. Files with extension 'lib' are classified as TY_Object by clang // but they are usually archives. It is OK if the file is not really an // archive since GetSDLFromOffloadArchive will check the magic of the file // and only unbundle it if it is really an archive. const StringRef LibFileExt = ".lib"; if (!llvm::sys::path::has_extension(FileName) || types::lookupTypeForExtension( llvm::sys::path::extension(FileName).drop_front()) == types::TY_INVALID || llvm::sys::path::extension(FileName) == LibFileExt) SDLNames.insert(Input); } // The search stops as soon as an SDL file is found. The driver then provides // the full filename of the SDL to the llvm-link command. If no SDL is found // after searching each LINKPATH with SEARCH-ORDER, it is possible that an // archive file lib.a exists and may contain bundled object files. for (auto SDLName : SDLNames) { // This is the only call to SDLSearch if (!SDLSearch(D, DriverArgs, CC1Args, LibraryPaths, SDLName, Arch, Target, isBitCodeSDL)) { GetSDLFromOffloadArchive(*C, D, *T, *JA, *Inputs, DriverArgs, CC1Args, LibraryPaths, SDLName, Arch, Target, isBitCodeSDL); } } } static llvm::opt::Arg * getAMDGPUCodeObjectArgument(const Driver &D, const llvm::opt::ArgList &Args) { return Args.getLastArg(options::OPT_mcode_object_version_EQ); } void tools::checkAMDGPUCodeObjectVersion(const Driver &D, const llvm::opt::ArgList &Args) { const unsigned MinCodeObjVer = 4; const unsigned MaxCodeObjVer = 5; if (auto *CodeObjArg = getAMDGPUCodeObjectArgument(D, Args)) { if (CodeObjArg->getOption().getID() == options::OPT_mcode_object_version_EQ) { unsigned CodeObjVer = MaxCodeObjVer; auto Remnant = StringRef(CodeObjArg->getValue()).getAsInteger(0, CodeObjVer); if (Remnant || CodeObjVer < MinCodeObjVer || CodeObjVer > MaxCodeObjVer) D.Diag(diag::err_drv_invalid_int_value) << CodeObjArg->getAsString(Args) << CodeObjArg->getValue(); } } } unsigned tools::getAMDGPUCodeObjectVersion(const Driver &D, const llvm::opt::ArgList &Args) { unsigned CodeObjVer = 4; // default if (auto *CodeObjArg = getAMDGPUCodeObjectArgument(D, Args)) StringRef(CodeObjArg->getValue()).getAsInteger(0, CodeObjVer); return CodeObjVer; } bool tools::haveAMDGPUCodeObjectVersionArgument( const Driver &D, const llvm::opt::ArgList &Args) { return getAMDGPUCodeObjectArgument(D, Args) != nullptr; } void tools::addMachineOutlinerArgs(const Driver &D, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, const llvm::Triple &Triple, bool IsLTO, const StringRef PluginOptPrefix) { auto addArg = [&, IsLTO](const Twine &Arg) { if (IsLTO) { assert(!PluginOptPrefix.empty() && "Cannot have empty PluginOptPrefix!"); CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + Arg)); } else { CmdArgs.push_back("-mllvm"); CmdArgs.push_back(Args.MakeArgString(Arg)); } }; if (Arg *A = Args.getLastArg(options::OPT_moutline, options::OPT_mno_outline)) { if (A->getOption().matches(options::OPT_moutline)) { // We only support -moutline in AArch64 and ARM targets right now. If // we're not compiling for these, emit a warning and ignore the flag. // Otherwise, add the proper mllvm flags. if (!(Triple.isARM() || Triple.isThumb() || Triple.isAArch64())) { D.Diag(diag::warn_drv_moutline_unsupported_opt) << Triple.getArchName(); } else { addArg(Twine("-enable-machine-outliner")); } } else { // Disable all outlining behaviour. addArg(Twine("-enable-machine-outliner=never")); } } } void tools::addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, StringRef BitcodeSuffix, const llvm::Triple &Triple) { SmallVector LibraryPaths; // Add path to clang lib / lib64 folder. SmallString<256> DefaultLibPath = llvm::sys::path::parent_path(D.Dir); llvm::sys::path::append(DefaultLibPath, CLANG_INSTALL_LIBDIR_BASENAME); LibraryPaths.emplace_back(DefaultLibPath.c_str()); // Add user defined library paths from LIBRARY_PATH. std::optional LibPath = llvm::sys::Process::GetEnv("LIBRARY_PATH"); if (LibPath) { SmallVector Frags; const char EnvPathSeparatorStr[] = {llvm::sys::EnvPathSeparator, '\0'}; llvm::SplitString(*LibPath, Frags, EnvPathSeparatorStr); for (StringRef Path : Frags) LibraryPaths.emplace_back(Path.trim()); } OptSpecifier LibomptargetBCPathOpt = Triple.isAMDGCN() ? options::OPT_libomptarget_amdgpu_bc_path_EQ : options::OPT_libomptarget_nvptx_bc_path_EQ; StringRef ArchPrefix = Triple.isAMDGCN() ? "amdgpu" : "nvptx"; std::string LibOmpTargetName = ("libomptarget-" + ArchPrefix + "-" + BitcodeSuffix + ".bc").str(); // First check whether user specifies bc library if (const Arg *A = DriverArgs.getLastArg(LibomptargetBCPathOpt)) { SmallString<128> LibOmpTargetFile(A->getValue()); if (llvm::sys::fs::exists(LibOmpTargetFile) && llvm::sys::fs::is_directory(LibOmpTargetFile)) { llvm::sys::path::append(LibOmpTargetFile, LibOmpTargetName); } if (llvm::sys::fs::exists(LibOmpTargetFile)) { CC1Args.push_back("-mlink-builtin-bitcode"); CC1Args.push_back(DriverArgs.MakeArgString(LibOmpTargetFile)); } else { D.Diag(diag::err_drv_omp_offload_target_bcruntime_not_found) << LibOmpTargetFile; } } else { bool FoundBCLibrary = false; for (StringRef LibraryPath : LibraryPaths) { SmallString<128> LibOmpTargetFile(LibraryPath); llvm::sys::path::append(LibOmpTargetFile, LibOmpTargetName); if (llvm::sys::fs::exists(LibOmpTargetFile)) { CC1Args.push_back("-mlink-builtin-bitcode"); CC1Args.push_back(DriverArgs.MakeArgString(LibOmpTargetFile)); FoundBCLibrary = true; break; } } if (!FoundBCLibrary) D.Diag(diag::err_drv_omp_offload_target_missingbcruntime) << LibOmpTargetName << ArchPrefix; } } void tools::addHIPRuntimeLibArgs(const ToolChain &TC, Compilation &C, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { if ((C.getActiveOffloadKinds() & Action::OFK_HIP) && !Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_no_hip_rt)) { TC.AddHIPRuntimeLibArgs(Args, CmdArgs); } else { // Claim "no HIP libraries" arguments if any for (auto *Arg : Args.filtered(options::OPT_no_hip_rt)) { Arg->claim(); } } }