blob: fbf2f45b543844827f388bcd24fc88b9e9b6f842 [file] [log] [blame]
//===-- MSVC.cpp - MSVC ToolChain Implementations -------------------------===//
//
// 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 "MSVC.h"
#include "CommonArgs.h"
#include "Darwin.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Config/config.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/SanitizerArgs.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/TargetParser/Host.h"
#include <cstdio>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOGDI
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
using namespace clang::driver;
using namespace clang::driver::toolchains;
using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;
static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) {
auto Status = VFS.status(Path);
if (!Status)
return false;
return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0;
}
// Try to find Exe from a Visual Studio distribution. This first tries to find
// an installed copy of Visual Studio and, failing that, looks in the PATH,
// making sure that whatever executable that's found is not a same-named exe
// from clang itself to prevent clang from falling back to itself.
static std::string FindVisualStudioExecutable(const ToolChain &TC,
const char *Exe) {
const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC);
SmallString<128> FilePath(
MSVC.getSubDirectoryPath(llvm::SubDirectoryType::Bin));
llvm::sys::path::append(FilePath, Exe);
return std::string(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe);
}
void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
ArgStringList CmdArgs;
auto &TC = static_cast<const toolchains::MSVCToolChain &>(getToolChain());
assert((Output.isFilename() || Output.isNothing()) && "invalid output");
if (Output.isFilename())
CmdArgs.push_back(
Args.MakeArgString(std::string("-out:") + Output.getFilename()));
if (Args.hasArg(options::OPT_marm64x))
CmdArgs.push_back("-machine:arm64x");
else if (TC.getTriple().isWindowsArm64EC())
CmdArgs.push_back("-machine:arm64ec");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&
!C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {
CmdArgs.push_back("-defaultlib:libcmt");
CmdArgs.push_back("-defaultlib:oldnames");
}
// If the VC environment hasn't been configured (perhaps because the user
// did not run vcvarsall), try to build a consistent link environment. If
// the environment variable is set however, assume the user knows what
// they're doing. If the user passes /vctoolsdir or /winsdkdir, trust that
// over env vars.
if (const Arg *A = Args.getLastArg(options::OPT__SLASH_diasdkdir,
options::OPT__SLASH_winsysroot)) {
// cl.exe doesn't find the DIA SDK automatically, so this too requires
// explicit flags and doesn't automatically look in "DIA SDK" relative
// to the path we found for VCToolChainPath.
llvm::SmallString<128> DIAPath(A->getValue());
if (A->getOption().getID() == options::OPT__SLASH_winsysroot)
llvm::sys::path::append(DIAPath, "DIA SDK");
// The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
llvm::sys::path::append(DIAPath, "lib",
llvm::archToLegacyVCArch(TC.getArch()));
CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath));
}
if (!llvm::sys::Process::GetEnv("LIB") ||
Args.getLastArg(options::OPT__SLASH_vctoolsdir,
options::OPT__SLASH_winsysroot)) {
CmdArgs.push_back(Args.MakeArgString(
Twine("-libpath:") +
TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib)));
CmdArgs.push_back(Args.MakeArgString(
Twine("-libpath:") +
TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib, "atlmfc")));
}
if (!llvm::sys::Process::GetEnv("LIB") ||
Args.getLastArg(options::OPT__SLASH_winsdkdir,
options::OPT__SLASH_winsysroot)) {
if (TC.useUniversalCRT()) {
std::string UniversalCRTLibPath;
if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))
CmdArgs.push_back(
Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath));
}
std::string WindowsSdkLibPath;
if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath))
CmdArgs.push_back(
Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath));
}
if (C.getDriver().IsFlangMode()) {
addFortranRuntimeLibraryPath(TC, Args, CmdArgs);
addFortranRuntimeLibs(TC, Args, CmdArgs);
// Inform the MSVC linker that we're generating a console application, i.e.
// one with `main` as the "user-defined" entry point. The `main` function is
// defined in flang's runtime libraries.
CmdArgs.push_back("/subsystem:console");
}
// Add the compiler-rt library directories to libpath if they exist to help
// the linker find the various sanitizer, builtin, and profiling runtimes.
for (const auto &LibPath : TC.getLibraryPaths()) {
if (TC.getVFS().exists(LibPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
}
auto CRTPath = TC.getCompilerRTPath();
if (TC.getVFS().exists(CRTPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));
if (!C.getDriver().IsCLMode() && Args.hasArg(options::OPT_L))
for (const auto &LibPath : Args.getAllArgValues(options::OPT_L))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
CmdArgs.push_back("-nologo");
if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))
CmdArgs.push_back("-debug");
// If we specify /hotpatch, let the linker add padding in front of each
// function, like MSVC does.
if (Args.hasArg(options::OPT_fms_hotpatch, options::OPT__SLASH_hotpatch))
CmdArgs.push_back("-functionpadmin");
// Pass on /Brepro if it was passed to the compiler.
// Note that /Brepro maps to -mno-incremental-linker-compatible.
bool DefaultIncrementalLinkerCompatible =
C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment();
if (!Args.hasFlag(options::OPT_mincremental_linker_compatible,
options::OPT_mno_incremental_linker_compatible,
DefaultIncrementalLinkerCompatible))
CmdArgs.push_back("-Brepro");
bool DLL = Args.hasArg(options::OPT__SLASH_LD, options::OPT__SLASH_LDd,
options::OPT_shared);
if (DLL) {
CmdArgs.push_back(Args.MakeArgString("-dll"));
SmallString<128> ImplibName(Output.getFilename());
llvm::sys::path::replace_extension(ImplibName, "lib");
CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName));
}
if (TC.getSanitizerArgs(Args).needsFuzzer()) {
if (!Args.hasArg(options::OPT_shared))
CmdArgs.push_back(
Args.MakeArgString(std::string("-wholearchive:") +
TC.getCompilerRTArgString(Args, "fuzzer")));
CmdArgs.push_back(Args.MakeArgString("-debug"));
// Prevent the linker from padding sections we use for instrumentation
// arrays.
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
}
if (TC.getSanitizerArgs(Args).needsAsanRt()) {
CmdArgs.push_back(Args.MakeArgString("-debug"));
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
if (TC.getSanitizerArgs(Args).needsSharedRt() ||
Args.hasArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd)) {
for (const auto &Lib : {"asan_dynamic", "asan_dynamic_runtime_thunk"})
CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
// Make sure the dynamic runtime thunk is not optimized out at link time
// to ensure proper SEH handling.
CmdArgs.push_back(Args.MakeArgString(
TC.getArch() == llvm::Triple::x86
? "-include:___asan_seh_interceptor"
: "-include:__asan_seh_interceptor"));
// Make sure the linker consider all object files from the dynamic runtime
// thunk.
CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
TC.getCompilerRT(Args, "asan_dynamic_runtime_thunk")));
} else if (DLL) {
CmdArgs.push_back(TC.getCompilerRTArgString(Args, "asan_dll_thunk"));
} else {
for (const auto &Lib : {"asan", "asan_cxx"}) {
CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
// Make sure the linker consider all object files from the static lib.
// This is necessary because instrumented dlls need access to all the
// interface exported by the static lib in the main executable.
CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
TC.getCompilerRT(Args, Lib)));
}
}
}
Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);
// Control Flow Guard checks
for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) {
StringRef GuardArgs = A->getValue();
if (GuardArgs.equals_insensitive("cf") ||
GuardArgs.equals_insensitive("cf,nochecks")) {
// MSVC doesn't yet support the "nochecks" modifier.
CmdArgs.push_back("-guard:cf");
} else if (GuardArgs.equals_insensitive("cf-")) {
CmdArgs.push_back("-guard:cf-");
} else if (GuardArgs.equals_insensitive("ehcont")) {
CmdArgs.push_back("-guard:ehcont");
} else if (GuardArgs.equals_insensitive("ehcont-")) {
CmdArgs.push_back("-guard:ehcont-");
}
}
if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
options::OPT_fno_openmp, false)) {
CmdArgs.push_back("-nodefaultlib:vcomp.lib");
CmdArgs.push_back("-nodefaultlib:vcompd.lib");
CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
TC.getDriver().Dir + "/../lib"));
switch (TC.getDriver().getOpenMPRuntime(Args)) {
case Driver::OMPRT_OMP:
CmdArgs.push_back("-defaultlib:libomp.lib");
break;
case Driver::OMPRT_IOMP5:
CmdArgs.push_back("-defaultlib:libiomp5md.lib");
break;
case Driver::OMPRT_GOMP:
break;
case Driver::OMPRT_Unknown:
// Already diagnosed.
break;
}
}
// Add compiler-rt lib in case if it was explicitly
// specified as an argument for --rtlib option.
if (!Args.hasArg(options::OPT_nostdlib)) {
AddRunTimeLibs(TC, TC.getDriver(), CmdArgs, Args);
}
StringRef Linker =
Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER);
if (Linker.empty())
Linker = "link";
// We need to translate 'lld' into 'lld-link'.
else if (Linker.equals_insensitive("lld"))
Linker = "lld-link";
if (Linker == "lld-link") {
for (Arg *A : Args.filtered(options::OPT_vfsoverlay))
CmdArgs.push_back(
Args.MakeArgString(std::string("/vfsoverlay:") + A->getValue()));
if (C.getDriver().isUsingLTO() &&
Args.hasFlag(options::OPT_gsplit_dwarf, options::OPT_gno_split_dwarf,
false))
CmdArgs.push_back(Args.MakeArgString(Twine("/dwodir:") +
Output.getFilename() + "_dwo"));
}
// Add filenames, libraries, and other linker inputs.
for (const auto &Input : Inputs) {
if (Input.isFilename()) {
CmdArgs.push_back(Input.getFilename());
continue;
}
const Arg &A = Input.getInputArg();
// Render -l options differently for the MSVC linker.
if (A.getOption().matches(options::OPT_l)) {
StringRef Lib = A.getValue();
const char *LinkLibArg;
if (Lib.ends_with(".lib"))
LinkLibArg = Args.MakeArgString(Lib);
else
LinkLibArg = Args.MakeArgString(Lib + ".lib");
CmdArgs.push_back(LinkLibArg);
continue;
}
// Otherwise, this is some other kind of linker input option like -Wl, -z,
// or -L. Render it, even if MSVC doesn't understand it.
A.renderAsInput(Args, CmdArgs);
}
addHIPRuntimeLibArgs(TC, C, Args, CmdArgs);
TC.addProfileRTLibs(Args, CmdArgs);
std::vector<const char *> Environment;
// We need to special case some linker paths. In the case of the regular msvc
// linker, we need to use a special search algorithm.
llvm::SmallString<128> linkPath;
if (Linker.equals_insensitive("link")) {
// If we're using the MSVC linker, it's not sufficient to just use link
// from the program PATH, because other environments like GnuWin32 install
// their own link.exe which may come first.
linkPath = FindVisualStudioExecutable(TC, "link.exe");
if (!TC.FoundMSVCInstall() && !canExecute(TC.getVFS(), linkPath)) {
llvm::SmallString<128> ClPath;
ClPath = TC.GetProgramPath("cl.exe");
if (canExecute(TC.getVFS(), ClPath)) {
linkPath = llvm::sys::path::parent_path(ClPath);
llvm::sys::path::append(linkPath, "link.exe");
if (!canExecute(TC.getVFS(), linkPath))
C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);
} else {
C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);
}
}
// Clang handles passing the proper asan libs to the linker, which goes
// against link.exe's /INFERASANLIBS which automatically finds asan libs.
if (TC.getSanitizerArgs(Args).needsAsanRt())
CmdArgs.push_back("/INFERASANLIBS:NO");
#ifdef _WIN32
// When cross-compiling with VS2017 or newer, link.exe expects to have
// its containing bin directory at the top of PATH, followed by the
// native target bin directory.
// e.g. when compiling for x86 on an x64 host, PATH should start with:
// /bin/Hostx64/x86;/bin/Hostx64/x64
// This doesn't attempt to handle llvm::ToolsetLayout::DevDivInternal.
if (TC.getIsVS2017OrNewer() &&
llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) {
auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch();
auto EnvBlockWide =
std::unique_ptr<wchar_t[], decltype(&FreeEnvironmentStringsW)>(
GetEnvironmentStringsW(), FreeEnvironmentStringsW);
if (!EnvBlockWide)
goto SkipSettingEnvironment;
size_t EnvCount = 0;
size_t EnvBlockLen = 0;
while (EnvBlockWide[EnvBlockLen] != L'\0') {
++EnvCount;
EnvBlockLen += std::wcslen(&EnvBlockWide[EnvBlockLen]) +
1 /*string null-terminator*/;
}
++EnvBlockLen; // add the block null-terminator
std::string EnvBlock;
if (!llvm::convertUTF16ToUTF8String(
llvm::ArrayRef<char>(reinterpret_cast<char *>(EnvBlockWide.get()),
EnvBlockLen * sizeof(EnvBlockWide[0])),
EnvBlock))
goto SkipSettingEnvironment;
Environment.reserve(EnvCount);
// Now loop over each string in the block and copy them into the
// environment vector, adjusting the PATH variable as needed when we
// find it.
for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) {
llvm::StringRef EnvVar(Cursor);
if (EnvVar.starts_with_insensitive("path=")) {
constexpr size_t PrefixLen = 5; // strlen("path=")
Environment.push_back(Args.MakeArgString(
EnvVar.substr(0, PrefixLen) +
TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin) +
llvm::Twine(llvm::sys::EnvPathSeparator) +
TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin, HostArch) +
(EnvVar.size() > PrefixLen
? llvm::Twine(llvm::sys::EnvPathSeparator) +
EnvVar.substr(PrefixLen)
: "")));
} else {
Environment.push_back(Args.MakeArgString(EnvVar));
}
Cursor += EnvVar.size() + 1 /*null-terminator*/;
}
}
SkipSettingEnvironment:;
#endif
} else {
linkPath = TC.GetProgramPath(Linker.str().c_str());
}
auto LinkCmd = std::make_unique<Command>(
JA, *this, ResponseFileSupport::AtFileUTF16(),
Args.MakeArgString(linkPath), CmdArgs, Inputs, Output);
if (!Environment.empty())
LinkCmd->setEnvironment(Environment);
C.addCommand(std::move(LinkCmd));
}
MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args)
: ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args),
RocmInstallation(D, Triple, Args) {
getProgramPaths().push_back(getDriver().Dir);
std::optional<llvm::StringRef> VCToolsDir, VCToolsVersion;
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir))
VCToolsDir = A->getValue();
if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion))
VCToolsVersion = A->getValue();
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir))
WinSdkDir = A->getValue();
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion))
WinSdkVersion = A->getValue();
if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot))
WinSysRoot = A->getValue();
// Check the command line first, that's the user explicitly telling us what to
// use. Check the environment next, in case we're being invoked from a VS
// command prompt. Failing that, just try to find the newest Visual Studio
// version we can and use its default VC toolchain.
llvm::findVCToolChainViaCommandLine(getVFS(), VCToolsDir, VCToolsVersion,
WinSysRoot, VCToolChainPath, VSLayout) ||
llvm::findVCToolChainViaEnvironment(getVFS(), VCToolChainPath,
VSLayout) ||
llvm::findVCToolChainViaSetupConfig(getVFS(), VCToolsVersion,
VCToolChainPath, VSLayout) ||
llvm::findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
}
Tool *MSVCToolChain::buildLinker() const {
return new tools::visualstudio::Linker(*this);
}
Tool *MSVCToolChain::buildAssembler() const {
if (getTriple().isOSBinFormatMachO())
return new tools::darwin::Assembler(*this);
getDriver().Diag(clang::diag::err_no_external_assembler);
return nullptr;
}
ToolChain::UnwindTableLevel
MSVCToolChain::getDefaultUnwindTableLevel(const ArgList &Args) const {
// Don't emit unwind tables by default for MachO targets.
if (getTriple().isOSBinFormatMachO())
return UnwindTableLevel::None;
// All non-x86_32 Windows targets require unwind tables. However, LLVM
// doesn't know how to generate them for all targets, so only enable
// the ones that are actually implemented.
if (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::arm ||
getArch() == llvm::Triple::thumb || getArch() == llvm::Triple::aarch64)
return UnwindTableLevel::Asynchronous;
return UnwindTableLevel::None;
}
bool MSVCToolChain::isPICDefault() const {
return getArch() == llvm::Triple::x86_64 ||
getArch() == llvm::Triple::aarch64;
}
bool MSVCToolChain::isPIEDefault(const llvm::opt::ArgList &Args) const {
return false;
}
bool MSVCToolChain::isPICDefaultForced() const {
return getArch() == llvm::Triple::x86_64 ||
getArch() == llvm::Triple::aarch64;
}
void MSVCToolChain::AddCudaIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args);
}
void MSVCToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args);
}
void MSVCToolChain::AddHIPRuntimeLibArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
CmdArgs.append({Args.MakeArgString(StringRef("-libpath:") +
RocmInstallation->getLibPath()),
"amdhip64.lib"});
}
void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const {
CudaInstallation->print(OS);
RocmInstallation->print(OS);
}
std::string
MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,
llvm::StringRef SubdirParent) const {
return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(),
SubdirParent);
}
std::string
MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,
llvm::Triple::ArchType TargetArch) const {
return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch,
"");
}
// Find the most recent version of Universal CRT or Windows 10 SDK.
// vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include
// directory by name and uses the last one of the list.
// So we compare entry names lexicographically to find the greatest one.
// Gets the library path required to link against the Windows SDK.
bool MSVCToolChain::getWindowsSDKLibraryPath(const ArgList &Args,
std::string &path) const {
std::string sdkPath;
int sdkMajor = 0;
std::string windowsSDKIncludeVersion;
std::string windowsSDKLibVersion;
path.clear();
if (!llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot,
sdkPath, sdkMajor, windowsSDKIncludeVersion,
windowsSDKLibVersion))
return false;
llvm::SmallString<128> libPath(sdkPath);
llvm::sys::path::append(libPath, "Lib");
if (sdkMajor >= 10)
if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&
WinSdkVersion.has_value())
windowsSDKLibVersion = *WinSdkVersion;
if (sdkMajor >= 8)
llvm::sys::path::append(libPath, windowsSDKLibVersion, "um");
return llvm::appendArchToWindowsSDKLibPath(sdkMajor, libPath, getArch(),
path);
}
bool MSVCToolChain::useUniversalCRT() const {
return llvm::useUniversalCRT(VSLayout, VCToolChainPath, getArch(), getVFS());
}
bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,
std::string &Path) const {
std::string UniversalCRTSdkPath;
std::string UCRTVersion;
Path.clear();
if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,
WinSysRoot, UniversalCRTSdkPath,
UCRTVersion))
return false;
if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&
WinSdkVersion.has_value())
UCRTVersion = *WinSdkVersion;
StringRef ArchName = llvm::archToWindowsSDKArch(getArch());
if (ArchName.empty())
return false;
llvm::SmallString<128> LibPath(UniversalCRTSdkPath);
llvm::sys::path::append(LibPath, "Lib", UCRTVersion, "ucrt", ArchName);
Path = std::string(LibPath);
return true;
}
static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) {
VersionTuple Version;
#ifdef _WIN32
SmallString<128> ClExe(BinDir);
llvm::sys::path::append(ClExe, "cl.exe");
std::wstring ClExeWide;
if (!llvm::ConvertUTF8toWide(ClExe.c_str(), ClExeWide))
return Version;
const DWORD VersionSize = ::GetFileVersionInfoSizeW(ClExeWide.c_str(),
nullptr);
if (VersionSize == 0)
return Version;
SmallVector<uint8_t, 4 * 1024> VersionBlock(VersionSize);
if (!::GetFileVersionInfoW(ClExeWide.c_str(), 0, VersionSize,
VersionBlock.data()))
return Version;
VS_FIXEDFILEINFO *FileInfo = nullptr;
UINT FileInfoSize = 0;
if (!::VerQueryValueW(VersionBlock.data(), L"\\",
reinterpret_cast<LPVOID *>(&FileInfo), &FileInfoSize) ||
FileInfoSize < sizeof(*FileInfo))
return Version;
const unsigned Major = (FileInfo->dwFileVersionMS >> 16) & 0xFFFF;
const unsigned Minor = (FileInfo->dwFileVersionMS ) & 0xFFFF;
const unsigned Micro = (FileInfo->dwFileVersionLS >> 16) & 0xFFFF;
Version = VersionTuple(Major, Minor, Micro);
#endif
return Version;
}
void MSVCToolChain::AddSystemIncludeWithSubfolder(
const ArgList &DriverArgs, ArgStringList &CC1Args,
const std::string &folder, const Twine &subfolder1, const Twine &subfolder2,
const Twine &subfolder3) const {
llvm::SmallString<128> path(folder);
llvm::sys::path::append(path, subfolder1, subfolder2, subfolder3);
addSystemInclude(DriverArgs, CC1Args, path);
}
void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_nostdinc))
return;
if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, getDriver().ResourceDir,
"include");
}
// Add %INCLUDE%-like directories from the -imsvc flag.
for (const auto &Path : DriverArgs.getAllArgValues(options::OPT__SLASH_imsvc))
addSystemInclude(DriverArgs, CC1Args, Path);
auto AddSystemIncludesFromEnv = [&](StringRef Var) -> bool {
if (auto Val = llvm::sys::Process::GetEnv(Var)) {
SmallVector<StringRef, 8> Dirs;
StringRef(*Val).split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
if (!Dirs.empty()) {
addSystemIncludes(DriverArgs, CC1Args, Dirs);
return true;
}
}
return false;
};
// Add %INCLUDE%-like dirs via /external:env: flags.
for (const auto &Var :
DriverArgs.getAllArgValues(options::OPT__SLASH_external_env)) {
AddSystemIncludesFromEnv(Var);
}
// Add DIA SDK include if requested.
if (const Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_diasdkdir,
options::OPT__SLASH_winsysroot)) {
// cl.exe doesn't find the DIA SDK automatically, so this too requires
// explicit flags and doesn't automatically look in "DIA SDK" relative
// to the path we found for VCToolChainPath.
llvm::SmallString<128> DIASDKPath(A->getValue());
if (A->getOption().getID() == options::OPT__SLASH_winsysroot)
llvm::sys::path::append(DIASDKPath, "DIA SDK");
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, std::string(DIASDKPath),
"include");
}
if (DriverArgs.hasArg(options::OPT_nostdlibinc))
return;
// Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search
// paths set by vcvarsall.bat. Skip if the user expressly set a vctoolsdir.
if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir,
options::OPT__SLASH_winsysroot)) {
bool Found = AddSystemIncludesFromEnv("INCLUDE");
Found |= AddSystemIncludesFromEnv("EXTERNAL_INCLUDE");
if (Found)
return;
}
// When built with access to the proper Windows APIs, try to actually find
// the correct include paths first.
if (!VCToolChainPath.empty()) {
addSystemInclude(DriverArgs, CC1Args,
getSubDirectoryPath(llvm::SubDirectoryType::Include));
addSystemInclude(
DriverArgs, CC1Args,
getSubDirectoryPath(llvm::SubDirectoryType::Include, "atlmfc"));
if (useUniversalCRT()) {
std::string UniversalCRTSdkPath;
std::string UCRTVersion;
if (llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,
WinSysRoot, UniversalCRTSdkPath,
UCRTVersion)) {
if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&
WinSdkVersion.has_value())
UCRTVersion = *WinSdkVersion;
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath,
"Include", UCRTVersion, "ucrt");
}
}
std::string WindowsSDKDir;
int major = 0;
std::string windowsSDKIncludeVersion;
std::string windowsSDKLibVersion;
if (llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot,
WindowsSDKDir, major, windowsSDKIncludeVersion,
windowsSDKLibVersion)) {
if (major >= 10)
if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&
WinSdkVersion.has_value())
windowsSDKIncludeVersion = windowsSDKLibVersion = *WinSdkVersion;
if (major >= 8) {
// Note: windowsSDKIncludeVersion is empty for SDKs prior to v10.
// Anyway, llvm::sys::path::append is able to manage it.
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
"Include", windowsSDKIncludeVersion,
"shared");
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
"Include", windowsSDKIncludeVersion,
"um");
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
"Include", windowsSDKIncludeVersion,
"winrt");
if (major >= 10) {
llvm::VersionTuple Tuple;
if (!Tuple.tryParse(windowsSDKIncludeVersion) &&
Tuple.getSubminor().value_or(0) >= 17134) {
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
"Include", windowsSDKIncludeVersion,
"cppwinrt");
}
}
} else {
AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
"Include");
}
}
return;
}
#if defined(_WIN32)
// As a fallback, select default install paths.
// FIXME: Don't guess drives and paths like this on Windows.
const StringRef Paths[] = {
"C:/Program Files/Microsoft Visual Studio 10.0/VC/include",
"C:/Program Files/Microsoft Visual Studio 9.0/VC/include",
"C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include",
"C:/Program Files/Microsoft Visual Studio 8/VC/include",
"C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include"
};
addSystemIncludes(DriverArgs, CC1Args, Paths);
#endif
}
void MSVCToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
// FIXME: There should probably be logic here to find libc++ on Windows.
}
VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
const ArgList &Args) const {
bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment();
VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args);
if (MSVT.empty())
MSVT = getTriple().getEnvironmentVersion();
if (MSVT.empty() && IsWindowsMSVC)
MSVT =
getMSVCVersionFromExe(getSubDirectoryPath(llvm::SubDirectoryType::Bin));
if (MSVT.empty() &&
Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions,
IsWindowsMSVC)) {
// -fms-compatibility-version=19.33 is default, aka 2022, 17.3
// NOTE: when changing this value, also update
// clang/docs/CommandGuide/clang.rst and clang/docs/UsersManual.rst
// accordingly.
MSVT = VersionTuple(19, 33);
}
return MSVT;
}
std::string
MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args,
types::ID InputType) const {
// The MSVC version doesn't care about the architecture, even though it
// may look at the triple internally.
VersionTuple MSVT = computeMSVCVersion(/*D=*/nullptr, Args);
MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().value_or(0),
MSVT.getSubminor().value_or(0));
// For the rest of the triple, however, a computed architecture name may
// be needed.
llvm::Triple Triple(ToolChain::ComputeEffectiveClangTriple(Args, InputType));
if (Triple.getEnvironment() == llvm::Triple::MSVC) {
StringRef ObjFmt = Triple.getEnvironmentName().split('-').second;
if (ObjFmt.empty())
Triple.setEnvironmentName((Twine("msvc") + MSVT.getAsString()).str());
else
Triple.setEnvironmentName(
(Twine("msvc") + MSVT.getAsString() + Twine('-') + ObjFmt).str());
}
return Triple.getTriple();
}
SanitizerMask MSVCToolChain::getSupportedSanitizers() const {
SanitizerMask Res = ToolChain::getSupportedSanitizers();
Res |= SanitizerKind::Address;
Res |= SanitizerKind::PointerCompare;
Res |= SanitizerKind::PointerSubtract;
Res |= SanitizerKind::Fuzzer;
Res |= SanitizerKind::FuzzerNoLink;
Res &= ~SanitizerKind::CFIMFCall;
return Res;
}
static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
bool SupportsForcingFramePointer,
const char *ExpandChar, const OptTable &Opts) {
assert(A->getOption().matches(options::OPT__SLASH_O));
StringRef OptStr = A->getValue();
for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
const char &OptChar = *(OptStr.data() + I);
switch (OptChar) {
default:
break;
case '1':
case '2':
case 'x':
case 'd':
// Ignore /O[12xd] flags that aren't the last one on the command line.
// Only the last one gets expanded.
if (&OptChar != ExpandChar) {
A->claim();
break;
}
if (OptChar == 'd') {
DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
} else {
if (OptChar == '1') {
DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
} else if (OptChar == '2' || OptChar == 'x') {
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2");
}
if (SupportsForcingFramePointer &&
!DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
if (OptChar == '1' || OptChar == '2')
DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
}
break;
case 'b':
if (I + 1 != E && isdigit(OptStr[I + 1])) {
switch (OptStr[I + 1]) {
case '0':
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
break;
case '1':
DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions));
break;
case '2':
DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
break;
}
++I;
}
break;
case 'g':
A->claim();
break;
case 'i':
if (I + 1 != E && OptStr[I + 1] == '-') {
++I;
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
} else {
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
}
break;
case 's':
DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
break;
case 't':
DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2");
break;
case 'y': {
bool OmitFramePointer = true;
if (I + 1 != E && OptStr[I + 1] == '-') {
OmitFramePointer = false;
++I;
}
if (SupportsForcingFramePointer) {
if (OmitFramePointer)
DAL.AddFlagArg(A,
Opts.getOption(options::OPT_fomit_frame_pointer));
else
DAL.AddFlagArg(
A, Opts.getOption(options::OPT_fno_omit_frame_pointer));
} else {
// Don't warn about /Oy- in x86-64 builds (where
// SupportsForcingFramePointer is false). The flag having no effect
// there is a compiler-internal optimization, and people shouldn't have
// to special-case their build files for x86-64 clang-cl.
A->claim();
}
break;
}
}
}
}
static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL,
const OptTable &Opts) {
assert(A->getOption().matches(options::OPT_D));
StringRef Val = A->getValue();
size_t Hash = Val.find('#');
if (Hash == StringRef::npos || Hash > Val.find('=')) {
DAL.append(A);
return;
}
std::string NewVal = std::string(Val);
NewVal[Hash] = '=';
DAL.AddJoinedArg(A, Opts.getOption(options::OPT_D), NewVal);
}
static void TranslatePermissive(Arg *A, llvm::opt::DerivedArgList &DAL,
const OptTable &Opts) {
DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase_));
DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_operator_names));
}
static void TranslatePermissiveMinus(Arg *A, llvm::opt::DerivedArgList &DAL,
const OptTable &Opts) {
DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase));
DAL.AddFlagArg(A, Opts.getOption(options::OPT_foperator_names));
}
llvm::opt::DerivedArgList *
MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
StringRef BoundArch,
Action::OffloadKind OFK) const {
DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
const OptTable &Opts = getDriver().getOpts();
// /Oy and /Oy- don't have an effect on X86-64
bool SupportsForcingFramePointer = getArch() != llvm::Triple::x86_64;
// The -O[12xd] flag actually expands to several flags. We must desugar the
// flags so that options embedded can be negated. For example, the '-O2' flag
// enables '-Oy'. Expanding '-O2' into its constituent flags allows us to
// correctly handle '-O2 -Oy-' where the trailing '-Oy-' disables a single
// aspect of '-O2'.
//
// Note that this expansion logic only applies to the *last* of '[12xd]'.
// First step is to search for the character we'd like to expand.
const char *ExpandChar = nullptr;
for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {
StringRef OptStr = A->getValue();
for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
char OptChar = OptStr[I];
char PrevChar = I > 0 ? OptStr[I - 1] : '0';
if (PrevChar == 'b') {
// OptChar does not expand; it's an argument to the previous char.
continue;
}
if (OptChar == '1' || OptChar == '2' || OptChar == 'x' || OptChar == 'd')
ExpandChar = OptStr.data() + I;
}
}
for (Arg *A : Args) {
if (A->getOption().matches(options::OPT__SLASH_O)) {
// The -O flag actually takes an amalgam of other options. For example,
// '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'.
TranslateOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar, Opts);
} else if (A->getOption().matches(options::OPT_D)) {
// Translate -Dfoo#bar into -Dfoo=bar.
TranslateDArg(A, *DAL, Opts);
} else if (A->getOption().matches(options::OPT__SLASH_permissive)) {
// Expand /permissive
TranslatePermissive(A, *DAL, Opts);
} else if (A->getOption().matches(options::OPT__SLASH_permissive_)) {
// Expand /permissive-
TranslatePermissiveMinus(A, *DAL, Opts);
} else if (OFK != Action::OFK_HIP) {
// HIP Toolchain translates input args by itself.
DAL->append(A);
}
}
return DAL;
}
void MSVCToolChain::addClangTargetOptions(
const ArgList &DriverArgs, ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadKind) const {
// MSVC STL kindly allows removing all usages of typeid by defining
// _HAS_STATIC_RTTI to 0. Do so, when compiling with -fno-rtti
if (DriverArgs.hasFlag(options::OPT_fno_rtti, options::OPT_frtti,
/*Default=*/false))
CC1Args.push_back("-D_HAS_STATIC_RTTI=0");
if (Arg *A = DriverArgs.getLastArgNoClaim(options::OPT_marm64x))
A->ignoreTargetSpecific();
}