1043 lines · cpp
1//===-- MSVC.cpp - MSVC ToolChain Implementations -------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "MSVC.h"10#include "Darwin.h"11#include "clang/Config/config.h"12#include "clang/Driver/CommonArgs.h"13#include "clang/Driver/Compilation.h"14#include "clang/Driver/Driver.h"15#include "clang/Driver/SanitizerArgs.h"16#include "clang/Options/Options.h"17#include "llvm/Option/Arg.h"18#include "llvm/Option/ArgList.h"19#include "llvm/Support/ConvertUTF.h"20#include "llvm/Support/ErrorHandling.h"21#include "llvm/Support/FileSystem.h"22#include "llvm/Support/Path.h"23#include "llvm/Support/Process.h"24#include "llvm/Support/VirtualFileSystem.h"25#include "llvm/TargetParser/Host.h"26#include <cstdio>27 28#ifdef _WIN3229 #define WIN32_LEAN_AND_MEAN30 #define NOGDI31 #ifndef NOMINMAX32 #define NOMINMAX33 #endif34 #include <windows.h>35#endif36 37using namespace clang::driver;38using namespace clang::driver::toolchains;39using namespace clang::driver::tools;40using namespace clang;41using namespace llvm::opt;42 43static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) {44 auto Status = VFS.status(Path);45 if (!Status)46 return false;47 return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0;48}49 50// Try to find Exe from a Visual Studio distribution. This first tries to find51// an installed copy of Visual Studio and, failing that, looks in the PATH,52// making sure that whatever executable that's found is not a same-named exe53// from clang itself to prevent clang from falling back to itself.54static std::string FindVisualStudioExecutable(const ToolChain &TC,55 const char *Exe) {56 const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC);57 SmallString<128> FilePath(58 MSVC.getSubDirectoryPath(llvm::SubDirectoryType::Bin));59 llvm::sys::path::append(FilePath, Exe);60 return std::string(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe);61}62 63void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,64 const InputInfo &Output,65 const InputInfoList &Inputs,66 const ArgList &Args,67 const char *LinkingOutput) const {68 ArgStringList CmdArgs;69 70 auto &TC = static_cast<const toolchains::MSVCToolChain &>(getToolChain());71 72 assert((Output.isFilename() || Output.isNothing()) && "invalid output");73 if (Output.isFilename())74 CmdArgs.push_back(75 Args.MakeArgString(std::string("-out:") + Output.getFilename()));76 77 if (Args.hasArg(options::OPT_marm64x))78 CmdArgs.push_back("-machine:arm64x");79 else if (TC.getTriple().isWindowsArm64EC())80 CmdArgs.push_back("-machine:arm64ec");81 82 if (const Arg *A = Args.getLastArg(options::OPT_fveclib)) {83 StringRef V = A->getValue();84 if (V == "ArmPL")85 CmdArgs.push_back(Args.MakeArgString("--dependent-lib=amath"));86 }87 88 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&89 !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {90 CmdArgs.push_back("-defaultlib:libcmt");91 CmdArgs.push_back("-defaultlib:oldnames");92 }93 94 // If the VC environment hasn't been configured (perhaps because the user95 // did not run vcvarsall), try to build a consistent link environment. If96 // the environment variable is set however, assume the user knows what97 // they're doing. If the user passes /vctoolsdir or /winsdkdir, trust that98 // over env vars.99 if (const Arg *A = Args.getLastArg(options::OPT__SLASH_diasdkdir,100 options::OPT__SLASH_winsysroot)) {101 // cl.exe doesn't find the DIA SDK automatically, so this too requires102 // explicit flags and doesn't automatically look in "DIA SDK" relative103 // to the path we found for VCToolChainPath.104 llvm::SmallString<128> DIAPath(A->getValue());105 if (A->getOption().getID() == options::OPT__SLASH_winsysroot)106 llvm::sys::path::append(DIAPath, "DIA SDK");107 108 // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.109 llvm::sys::path::append(DIAPath, "lib",110 llvm::archToLegacyVCArch(TC.getArch()));111 CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath));112 }113 if (!llvm::sys::Process::GetEnv("LIB") ||114 Args.hasArg(options::OPT__SLASH_vctoolsdir,115 options::OPT__SLASH_vctoolsversion,116 options::OPT__SLASH_winsysroot)) {117 CmdArgs.push_back(Args.MakeArgString(118 Twine("-libpath:") +119 TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib)));120 CmdArgs.push_back(Args.MakeArgString(121 Twine("-libpath:") +122 TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib, "atlmfc")));123 }124 if (!llvm::sys::Process::GetEnv("LIB") ||125 Args.hasArg(options::OPT__SLASH_winsdkdir,126 options::OPT__SLASH_winsdkversion,127 options::OPT__SLASH_winsysroot)) {128 if (TC.useUniversalCRT()) {129 std::string UniversalCRTLibPath;130 if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))131 CmdArgs.push_back(132 Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath));133 }134 std::string WindowsSdkLibPath;135 if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath))136 CmdArgs.push_back(137 Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath));138 }139 140 if (!C.getDriver().IsCLMode() && Args.hasArg(options::OPT_L))141 for (const auto &LibPath : Args.getAllArgValues(options::OPT_L))142 CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));143 144 if (C.getDriver().IsFlangMode() &&145 !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {146 TC.addFortranRuntimeLibraryPath(Args, CmdArgs);147 TC.addFortranRuntimeLibs(Args, CmdArgs);148 149 // Inform the MSVC linker that we're generating a console application, i.e.150 // one with `main` as the "user-defined" entry point. The `main` function is151 // defined in flang's runtime libraries.152 CmdArgs.push_back("/subsystem:console");153 }154 155 // Add the compiler-rt library directories to libpath if they exist to help156 // the linker find the various sanitizer, builtin, and profiling runtimes.157 for (const auto &LibPath : TC.getLibraryPaths()) {158 if (TC.getVFS().exists(LibPath))159 CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));160 }161 auto CRTPath = TC.getCompilerRTPath();162 if (TC.getVFS().exists(CRTPath))163 CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));164 165 CmdArgs.push_back("-nologo");166 167 if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))168 CmdArgs.push_back("-debug");169 170 // If we specify /hotpatch, let the linker add padding in front of each171 // function, like MSVC does.172 if (Args.hasArg(options::OPT_fms_hotpatch, options::OPT__SLASH_hotpatch))173 CmdArgs.push_back("-functionpadmin");174 175 // Pass on /Brepro if it was passed to the compiler.176 // Note that /Brepro maps to -mno-incremental-linker-compatible.177 bool DefaultIncrementalLinkerCompatible =178 C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment();179 if (!Args.hasFlag(options::OPT_mincremental_linker_compatible,180 options::OPT_mno_incremental_linker_compatible,181 DefaultIncrementalLinkerCompatible))182 CmdArgs.push_back("-Brepro");183 184 bool DLL = Args.hasArg(options::OPT__SLASH_LD, options::OPT__SLASH_LDd,185 options::OPT_shared);186 if (DLL) {187 CmdArgs.push_back(Args.MakeArgString("-dll"));188 189 SmallString<128> ImplibName(Output.getFilename());190 llvm::sys::path::replace_extension(ImplibName, "lib");191 CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName));192 }193 194 if (TC.getSanitizerArgs(Args).needsFuzzer()) {195 if (!Args.hasArg(options::OPT_shared))196 CmdArgs.push_back(197 Args.MakeArgString(std::string("-wholearchive:") +198 TC.getCompilerRTArgString(Args, "fuzzer")));199 CmdArgs.push_back(Args.MakeArgString("-debug"));200 // Prevent the linker from padding sections we use for instrumentation201 // arrays.202 CmdArgs.push_back(Args.MakeArgString("-incremental:no"));203 }204 205 if (TC.getSanitizerArgs(Args).needsAsanRt()) {206 CmdArgs.push_back(Args.MakeArgString("-debug"));207 CmdArgs.push_back(Args.MakeArgString("-incremental:no"));208 CmdArgs.push_back(TC.getCompilerRTArgString(Args, "asan_dynamic"));209 auto defines = Args.getAllArgValues(options::OPT_D);210 if (Args.hasArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd) ||211 llvm::is_contained(defines, "_DLL")) {212 // Make sure the dynamic runtime thunk is not optimized out at link time213 // to ensure proper SEH handling.214 CmdArgs.push_back(Args.MakeArgString(215 TC.getArch() == llvm::Triple::x86216 ? "-include:___asan_seh_interceptor"217 : "-include:__asan_seh_interceptor"));218 // Make sure the linker consider all object files from the dynamic runtime219 // thunk.220 CmdArgs.push_back(Args.MakeArgString(221 std::string("-wholearchive:") +222 TC.getCompilerRT(Args, "asan_dynamic_runtime_thunk")));223 } else {224 // Make sure the linker consider all object files from the static runtime225 // thunk.226 CmdArgs.push_back(Args.MakeArgString(227 std::string("-wholearchive:") +228 TC.getCompilerRT(Args, "asan_static_runtime_thunk")));229 }230 }231 232 if (C.getDriver().isUsingLTO()) {233 if (Arg *A = tools::getLastProfileSampleUseArg(Args))234 CmdArgs.push_back(Args.MakeArgString(std::string("-lto-sample-profile:") +235 A->getValue()));236 }237 Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);238 239 // Control Flow Guard checks240 for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) {241 StringRef GuardArgs = A->getValue();242 if (GuardArgs.equals_insensitive("cf") ||243 GuardArgs.equals_insensitive("cf,nochecks")) {244 // MSVC doesn't yet support the "nochecks" modifier.245 CmdArgs.push_back("-guard:cf");246 } else if (GuardArgs.equals_insensitive("cf-")) {247 CmdArgs.push_back("-guard:cf-");248 } else if (GuardArgs.equals_insensitive("ehcont")) {249 CmdArgs.push_back("-guard:ehcont");250 } else if (GuardArgs.equals_insensitive("ehcont-")) {251 CmdArgs.push_back("-guard:ehcont-");252 }253 }254 255 if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,256 options::OPT_fno_openmp, false)) {257 CmdArgs.push_back("-nodefaultlib:vcomp.lib");258 CmdArgs.push_back("-nodefaultlib:vcompd.lib");259 CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +260 TC.getDriver().Dir + "/../lib"));261 switch (TC.getDriver().getOpenMPRuntime(Args)) {262 case Driver::OMPRT_OMP:263 CmdArgs.push_back("-defaultlib:libomp.lib");264 break;265 case Driver::OMPRT_IOMP5:266 CmdArgs.push_back("-defaultlib:libiomp5md.lib");267 break;268 case Driver::OMPRT_GOMP:269 break;270 case Driver::OMPRT_Unknown:271 // Already diagnosed.272 break;273 }274 }275 276 // Add compiler-rt lib in case if it was explicitly277 // specified as an argument for --rtlib option.278 if (!Args.hasArg(options::OPT_nostdlib)) {279 AddRunTimeLibs(TC, TC.getDriver(), CmdArgs, Args);280 }281 282 StringRef Linker = Args.getLastArgValue(options::OPT_fuse_ld_EQ,283 TC.getDriver().getPreferredLinker());284 if (Linker.empty())285 Linker = "link";286 // We need to translate 'lld' into 'lld-link'.287 else if (Linker.equals_insensitive("lld"))288 Linker = "lld-link";289 290 if (Linker == "lld-link") {291 for (Arg *A : Args.filtered(options::OPT_vfsoverlay))292 CmdArgs.push_back(293 Args.MakeArgString(std::string("/vfsoverlay:") + A->getValue()));294 295 if (C.getDriver().isUsingLTO() &&296 Args.hasFlag(options::OPT_gsplit_dwarf, options::OPT_gno_split_dwarf,297 false))298 CmdArgs.push_back(Args.MakeArgString(Twine("/dwodir:") +299 Output.getFilename() + "_dwo"));300 }301 302 // Add filenames, libraries, and other linker inputs.303 for (const auto &Input : Inputs) {304 if (Input.isFilename()) {305 CmdArgs.push_back(Input.getFilename());306 continue;307 }308 309 const Arg &A = Input.getInputArg();310 311 // Render -l options differently for the MSVC linker.312 if (A.getOption().matches(options::OPT_l)) {313 StringRef Lib = A.getValue();314 const char *LinkLibArg;315 if (Lib.ends_with(".lib"))316 LinkLibArg = Args.MakeArgString(Lib);317 else318 LinkLibArg = Args.MakeArgString(Lib + ".lib");319 CmdArgs.push_back(LinkLibArg);320 continue;321 }322 323 // Otherwise, this is some other kind of linker input option like -Wl, -z,324 // or -L. Render it, even if MSVC doesn't understand it.325 A.renderAsInput(Args, CmdArgs);326 }327 328 addHIPRuntimeLibArgs(TC, C, Args, CmdArgs);329 330 TC.addProfileRTLibs(Args, CmdArgs);331 332 std::vector<const char *> Environment;333 334 // We need to special case some linker paths. In the case of the regular msvc335 // linker, we need to use a special search algorithm.336 llvm::SmallString<128> linkPath;337 if (Linker.equals_insensitive("link")) {338 // If we're using the MSVC linker, it's not sufficient to just use link339 // from the program PATH, because other environments like GnuWin32 install340 // their own link.exe which may come first.341 linkPath = FindVisualStudioExecutable(TC, "link.exe");342 343 if (!TC.FoundMSVCInstall() && !canExecute(TC.getVFS(), linkPath)) {344 llvm::SmallString<128> ClPath;345 ClPath = TC.GetProgramPath("cl.exe");346 if (canExecute(TC.getVFS(), ClPath)) {347 linkPath = llvm::sys::path::parent_path(ClPath);348 llvm::sys::path::append(linkPath, "link.exe");349 if (!canExecute(TC.getVFS(), linkPath))350 C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);351 } else {352 C.getDriver().Diag(clang::diag::warn_drv_msvc_not_found);353 }354 }355 356 // Clang handles passing the proper asan libs to the linker, which goes357 // against link.exe's /INFERASANLIBS which automatically finds asan libs.358 if (TC.getSanitizerArgs(Args).needsAsanRt())359 CmdArgs.push_back("/INFERASANLIBS:NO");360 361#ifdef _WIN32362 // When cross-compiling with VS2017 or newer, link.exe expects to have363 // its containing bin directory at the top of PATH, followed by the364 // native target bin directory.365 // e.g. when compiling for x86 on an x64 host, PATH should start with:366 // /bin/Hostx64/x86;/bin/Hostx64/x64367 // This doesn't attempt to handle llvm::ToolsetLayout::DevDivInternal.368 if (TC.getIsVS2017OrNewer() &&369 llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) {370 auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch();371 372 auto EnvBlockWide =373 std::unique_ptr<wchar_t[], decltype(&FreeEnvironmentStringsW)>(374 GetEnvironmentStringsW(), FreeEnvironmentStringsW);375 if (!EnvBlockWide)376 goto SkipSettingEnvironment;377 378 size_t EnvCount = 0;379 size_t EnvBlockLen = 0;380 while (EnvBlockWide[EnvBlockLen] != L'\0') {381 ++EnvCount;382 EnvBlockLen += std::wcslen(&EnvBlockWide[EnvBlockLen]) +383 1 /*string null-terminator*/;384 }385 ++EnvBlockLen; // add the block null-terminator386 387 std::string EnvBlock;388 if (!llvm::convertUTF16ToUTF8String(389 llvm::ArrayRef<char>(reinterpret_cast<char *>(EnvBlockWide.get()),390 EnvBlockLen * sizeof(EnvBlockWide[0])),391 EnvBlock))392 goto SkipSettingEnvironment;393 394 Environment.reserve(EnvCount);395 396 // Now loop over each string in the block and copy them into the397 // environment vector, adjusting the PATH variable as needed when we398 // find it.399 for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) {400 llvm::StringRef EnvVar(Cursor);401 if (EnvVar.starts_with_insensitive("path=")) {402 constexpr size_t PrefixLen = 5; // strlen("path=")403 Environment.push_back(Args.MakeArgString(404 EnvVar.substr(0, PrefixLen) +405 TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin) +406 llvm::Twine(llvm::sys::EnvPathSeparator) +407 TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin, HostArch) +408 (EnvVar.size() > PrefixLen409 ? llvm::Twine(llvm::sys::EnvPathSeparator) +410 EnvVar.substr(PrefixLen)411 : "")));412 } else {413 Environment.push_back(Args.MakeArgString(EnvVar));414 }415 Cursor += EnvVar.size() + 1 /*null-terminator*/;416 }417 }418 SkipSettingEnvironment:;419#endif420 } else {421 linkPath = TC.GetProgramPath(Linker.str().c_str());422 }423 424 auto LinkCmd = std::make_unique<Command>(425 JA, *this, ResponseFileSupport::AtFileUTF16(),426 Args.MakeArgString(linkPath), CmdArgs, Inputs, Output);427 if (!Environment.empty())428 LinkCmd->setEnvironment(Environment);429 C.addCommand(std::move(LinkCmd));430}431 432MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,433 const ArgList &Args)434 : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args),435 RocmInstallation(D, Triple, Args), SYCLInstallation(D, Triple, Args) {436 getProgramPaths().push_back(getDriver().Dir);437 438 std::optional<llvm::StringRef> VCToolsDir, VCToolsVersion;439 if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir))440 VCToolsDir = A->getValue();441 if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion))442 VCToolsVersion = A->getValue();443 if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir))444 WinSdkDir = A->getValue();445 if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion))446 WinSdkVersion = A->getValue();447 if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot))448 WinSysRoot = A->getValue();449 450 // Check the command line first, that's the user explicitly telling us what to451 // use. Check the environment next, in case we're being invoked from a VS452 // command prompt. Failing that, just try to find the newest Visual Studio453 // version we can and use its default VC toolchain.454 llvm::findVCToolChainViaCommandLine(getVFS(), VCToolsDir, VCToolsVersion,455 WinSysRoot, VCToolChainPath, VSLayout) ||456 llvm::findVCToolChainViaEnvironment(getVFS(), VCToolChainPath,457 VSLayout) ||458 llvm::findVCToolChainViaSetupConfig(getVFS(), VCToolsVersion,459 VCToolChainPath, VSLayout) ||460 llvm::findVCToolChainViaRegistry(VCToolChainPath, VSLayout);461}462 463Tool *MSVCToolChain::buildLinker() const {464 return new tools::visualstudio::Linker(*this);465}466 467Tool *MSVCToolChain::buildAssembler() const {468 if (getTriple().isOSBinFormatMachO())469 return new tools::darwin::Assembler(*this);470 getDriver().Diag(clang::diag::err_no_external_assembler);471 return nullptr;472}473 474ToolChain::UnwindTableLevel475MSVCToolChain::getDefaultUnwindTableLevel(const ArgList &Args) const {476 // Don't emit unwind tables by default for MachO targets.477 if (getTriple().isOSBinFormatMachO())478 return UnwindTableLevel::None;479 480 // All non-x86_32 Windows targets require unwind tables. However, LLVM481 // doesn't know how to generate them for all targets, so only enable482 // the ones that are actually implemented.483 if (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::arm ||484 getArch() == llvm::Triple::thumb || getArch() == llvm::Triple::aarch64)485 return UnwindTableLevel::Asynchronous;486 487 return UnwindTableLevel::None;488}489 490bool MSVCToolChain::isPICDefault() const {491 return getArch() == llvm::Triple::x86_64 ||492 getArch() == llvm::Triple::aarch64;493}494 495bool MSVCToolChain::isPIEDefault(const llvm::opt::ArgList &Args) const {496 return false;497}498 499bool MSVCToolChain::isPICDefaultForced() const {500 return getArch() == llvm::Triple::x86_64 ||501 getArch() == llvm::Triple::aarch64;502}503 504void MSVCToolChain::AddCudaIncludeArgs(const ArgList &DriverArgs,505 ArgStringList &CC1Args) const {506 CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args);507}508 509void MSVCToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,510 ArgStringList &CC1Args) const {511 RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args);512}513 514void MSVCToolChain::addSYCLIncludeArgs(const ArgList &DriverArgs,515 ArgStringList &CC1Args) const {516 SYCLInstallation->addSYCLIncludeArgs(DriverArgs, CC1Args);517}518 519void MSVCToolChain::AddHIPRuntimeLibArgs(const ArgList &Args,520 ArgStringList &CmdArgs) const {521 CmdArgs.append({Args.MakeArgString(StringRef("-libpath:") +522 RocmInstallation->getLibPath()),523 "amdhip64.lib"});524}525 526void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const {527 CudaInstallation->print(OS);528 RocmInstallation->print(OS);529}530 531std::string532MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,533 llvm::StringRef SubdirParent) const {534 return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(),535 SubdirParent);536}537 538std::string539MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,540 llvm::Triple::ArchType TargetArch) const {541 return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch,542 "");543}544 545// Find the most recent version of Universal CRT or Windows 10 SDK.546// vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include547// directory by name and uses the last one of the list.548// So we compare entry names lexicographically to find the greatest one.549// Gets the library path required to link against the Windows SDK.550bool MSVCToolChain::getWindowsSDKLibraryPath(const ArgList &Args,551 std::string &path) const {552 std::string sdkPath;553 int sdkMajor = 0;554 std::string windowsSDKIncludeVersion;555 std::string windowsSDKLibVersion;556 557 path.clear();558 if (!llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot,559 sdkPath, sdkMajor, windowsSDKIncludeVersion,560 windowsSDKLibVersion))561 return false;562 563 llvm::SmallString<128> libPath(sdkPath);564 llvm::sys::path::append(libPath, "Lib");565 if (sdkMajor >= 10)566 if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&567 WinSdkVersion.has_value())568 windowsSDKLibVersion = *WinSdkVersion;569 if (sdkMajor >= 8)570 llvm::sys::path::append(libPath, windowsSDKLibVersion, "um");571 return llvm::appendArchToWindowsSDKLibPath(sdkMajor, libPath, getArch(),572 path);573}574 575bool MSVCToolChain::useUniversalCRT() const {576 return llvm::useUniversalCRT(VSLayout, VCToolChainPath, getArch(), getVFS());577}578 579bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,580 std::string &Path) const {581 std::string UniversalCRTSdkPath;582 std::string UCRTVersion;583 584 Path.clear();585 if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,586 WinSysRoot, UniversalCRTSdkPath,587 UCRTVersion))588 return false;589 590 if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&591 WinSdkVersion.has_value())592 UCRTVersion = *WinSdkVersion;593 594 StringRef ArchName = llvm::archToWindowsSDKArch(getArch());595 if (ArchName.empty())596 return false;597 598 llvm::SmallString<128> LibPath(UniversalCRTSdkPath);599 llvm::sys::path::append(LibPath, "Lib", UCRTVersion, "ucrt", ArchName);600 601 Path = std::string(LibPath);602 return true;603}604 605static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) {606 VersionTuple Version;607#ifdef _WIN32608 SmallString<128> ClExe(BinDir);609 llvm::sys::path::append(ClExe, "cl.exe");610 611 std::wstring ClExeWide;612 if (!llvm::ConvertUTF8toWide(ClExe.c_str(), ClExeWide))613 return Version;614 615 const DWORD VersionSize = ::GetFileVersionInfoSizeW(ClExeWide.c_str(),616 nullptr);617 if (VersionSize == 0)618 return Version;619 620 SmallVector<uint8_t, 4 * 1024> VersionBlock(VersionSize);621 if (!::GetFileVersionInfoW(ClExeWide.c_str(), 0, VersionSize,622 VersionBlock.data()))623 return Version;624 625 VS_FIXEDFILEINFO *FileInfo = nullptr;626 UINT FileInfoSize = 0;627 if (!::VerQueryValueW(VersionBlock.data(), L"\\",628 reinterpret_cast<LPVOID *>(&FileInfo), &FileInfoSize) ||629 FileInfoSize < sizeof(*FileInfo))630 return Version;631 632 const unsigned Major = (FileInfo->dwFileVersionMS >> 16) & 0xFFFF;633 const unsigned Minor = (FileInfo->dwFileVersionMS ) & 0xFFFF;634 const unsigned Micro = (FileInfo->dwFileVersionLS >> 16) & 0xFFFF;635 636 Version = VersionTuple(Major, Minor, Micro);637#endif638 return Version;639}640 641void MSVCToolChain::AddSystemIncludeWithSubfolder(642 const ArgList &DriverArgs, ArgStringList &CC1Args,643 const std::string &folder, const Twine &subfolder1, const Twine &subfolder2,644 const Twine &subfolder3) const {645 llvm::SmallString<128> path(folder);646 llvm::sys::path::append(path, subfolder1, subfolder2, subfolder3);647 addSystemInclude(DriverArgs, CC1Args, path);648}649 650void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,651 ArgStringList &CC1Args) const {652 if (DriverArgs.hasArg(options::OPT_nostdinc))653 return;654 655 if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {656 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, getDriver().ResourceDir,657 "include");658 }659 660 // Add %INCLUDE%-like directories from the -imsvc flag.661 for (const auto &Path : DriverArgs.getAllArgValues(options::OPT__SLASH_imsvc))662 addSystemInclude(DriverArgs, CC1Args, Path);663 664 auto AddSystemIncludesFromEnv = [&](StringRef Var) -> bool {665 if (auto Val = llvm::sys::Process::GetEnv(Var)) {666 SmallVector<StringRef, 8> Dirs;667 StringRef(*Val).split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);668 if (!Dirs.empty()) {669 addSystemIncludes(DriverArgs, CC1Args, Dirs);670 return true;671 }672 }673 return false;674 };675 676 // Add %INCLUDE%-like dirs via /external:env: flags.677 for (const auto &Var :678 DriverArgs.getAllArgValues(options::OPT__SLASH_external_env)) {679 AddSystemIncludesFromEnv(Var);680 }681 682 // Add DIA SDK include if requested.683 if (const Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_diasdkdir,684 options::OPT__SLASH_winsysroot)) {685 // cl.exe doesn't find the DIA SDK automatically, so this too requires686 // explicit flags and doesn't automatically look in "DIA SDK" relative687 // to the path we found for VCToolChainPath.688 llvm::SmallString<128> DIASDKPath(A->getValue());689 if (A->getOption().getID() == options::OPT__SLASH_winsysroot)690 llvm::sys::path::append(DIASDKPath, "DIA SDK");691 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, std::string(DIASDKPath),692 "include");693 }694 695 if (DriverArgs.hasArg(options::OPT_nostdlibinc))696 return;697 698 // Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search699 // paths set by vcvarsall.bat. Skip if the user expressly set any of the700 // Windows SDK or VC Tools options.701 if (!DriverArgs.hasArg(702 options::OPT__SLASH_vctoolsdir, options::OPT__SLASH_vctoolsversion,703 options::OPT__SLASH_winsysroot, options::OPT__SLASH_winsdkdir,704 options::OPT__SLASH_winsdkversion)) {705 bool Found = AddSystemIncludesFromEnv("INCLUDE");706 Found |= AddSystemIncludesFromEnv("EXTERNAL_INCLUDE");707 if (Found)708 return;709 }710 711 // When built with access to the proper Windows APIs, try to actually find712 // the correct include paths first.713 if (!VCToolChainPath.empty()) {714 addSystemInclude(DriverArgs, CC1Args,715 getSubDirectoryPath(llvm::SubDirectoryType::Include));716 addSystemInclude(717 DriverArgs, CC1Args,718 getSubDirectoryPath(llvm::SubDirectoryType::Include, "atlmfc"));719 720 if (useUniversalCRT()) {721 std::string UniversalCRTSdkPath;722 std::string UCRTVersion;723 if (llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,724 WinSysRoot, UniversalCRTSdkPath,725 UCRTVersion)) {726 if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&727 WinSdkVersion.has_value())728 UCRTVersion = *WinSdkVersion;729 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath,730 "Include", UCRTVersion, "ucrt");731 }732 }733 734 std::string WindowsSDKDir;735 int major = 0;736 std::string windowsSDKIncludeVersion;737 std::string windowsSDKLibVersion;738 if (llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot,739 WindowsSDKDir, major, windowsSDKIncludeVersion,740 windowsSDKLibVersion)) {741 if (major >= 10)742 if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) &&743 WinSdkVersion.has_value())744 windowsSDKIncludeVersion = windowsSDKLibVersion = *WinSdkVersion;745 if (major >= 8) {746 // Note: windowsSDKIncludeVersion is empty for SDKs prior to v10.747 // Anyway, llvm::sys::path::append is able to manage it.748 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,749 "Include", windowsSDKIncludeVersion,750 "shared");751 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,752 "Include", windowsSDKIncludeVersion,753 "um");754 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,755 "Include", windowsSDKIncludeVersion,756 "winrt");757 if (major >= 10) {758 llvm::VersionTuple Tuple;759 if (!Tuple.tryParse(windowsSDKIncludeVersion) &&760 Tuple.getSubminor().value_or(0) >= 17134) {761 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,762 "Include", windowsSDKIncludeVersion,763 "cppwinrt");764 }765 }766 } else {767 AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,768 "Include");769 }770 }771 772 return;773 }774 775#if defined(_WIN32)776 // As a fallback, select default install paths.777 // FIXME: Don't guess drives and paths like this on Windows.778 const StringRef Paths[] = {779 "C:/Program Files/Microsoft Visual Studio 10.0/VC/include",780 "C:/Program Files/Microsoft Visual Studio 9.0/VC/include",781 "C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include",782 "C:/Program Files/Microsoft Visual Studio 8/VC/include",783 "C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include"784 };785 addSystemIncludes(DriverArgs, CC1Args, Paths);786#endif787}788 789void MSVCToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,790 ArgStringList &CC1Args) const {791 // FIXME: There should probably be logic here to find libc++ on Windows.792}793 794VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,795 const ArgList &Args) const {796 bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment();797 VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args);798 if (MSVT.empty())799 MSVT = getTriple().getEnvironmentVersion();800 if (MSVT.empty() && IsWindowsMSVC)801 MSVT =802 getMSVCVersionFromExe(getSubDirectoryPath(llvm::SubDirectoryType::Bin));803 if (MSVT.empty() &&804 Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions,805 IsWindowsMSVC)) {806 // -fms-compatibility-version=19.33 is default, aka 2022, 17.3807 // NOTE: when changing this value, also update808 // clang/docs/CommandGuide/clang.rst and clang/docs/UsersManual.rst809 // accordingly.810 MSVT = VersionTuple(19, 33);811 }812 return MSVT;813}814 815std::string816MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args,817 types::ID InputType) const {818 // The MSVC version doesn't care about the architecture, even though it819 // may look at the triple internally.820 VersionTuple MSVT = computeMSVCVersion(/*D=*/nullptr, Args);821 MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().value_or(0),822 MSVT.getSubminor().value_or(0));823 824 // For the rest of the triple, however, a computed architecture name may825 // be needed.826 llvm::Triple Triple(ToolChain::ComputeEffectiveClangTriple(Args, InputType));827 if (Triple.getEnvironment() == llvm::Triple::MSVC) {828 StringRef ObjFmt = Triple.getEnvironmentName().split('-').second;829 if (ObjFmt.empty())830 Triple.setEnvironmentName((Twine("msvc") + MSVT.getAsString()).str());831 else832 Triple.setEnvironmentName(833 (Twine("msvc") + MSVT.getAsString() + Twine('-') + ObjFmt).str());834 }835 return Triple.getTriple();836}837 838SanitizerMask MSVCToolChain::getSupportedSanitizers() const {839 SanitizerMask Res = ToolChain::getSupportedSanitizers();840 Res |= SanitizerKind::Address;841 Res |= SanitizerKind::PointerCompare;842 Res |= SanitizerKind::PointerSubtract;843 Res |= SanitizerKind::Fuzzer;844 Res |= SanitizerKind::FuzzerNoLink;845 Res &= ~SanitizerKind::CFIMFCall;846 return Res;847}848 849static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,850 bool SupportsForcingFramePointer,851 const char *ExpandChar, const OptTable &Opts) {852 assert(A->getOption().matches(options::OPT__SLASH_O));853 854 StringRef OptStr = A->getValue();855 for (size_t I = 0, E = OptStr.size(); I != E; ++I) {856 const char &OptChar = *(OptStr.data() + I);857 switch (OptChar) {858 default:859 break;860 case '1':861 case '2':862 case 'x':863 case 'd':864 // Ignore /O[12xd] flags that aren't the last one on the command line.865 // Only the last one gets expanded.866 if (&OptChar != ExpandChar) {867 A->claim();868 break;869 }870 if (OptChar == 'd') {871 DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));872 } else {873 if (OptChar == '1') {874 DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");875 } else if (OptChar == '2' || OptChar == 'x') {876 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));877 DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");878 }879 if (SupportsForcingFramePointer &&880 !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))881 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));882 if (OptChar == '1' || OptChar == '2')883 DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));884 }885 break;886 case 'b':887 if (I + 1 != E && isdigit(OptStr[I + 1])) {888 switch (OptStr[I + 1]) {889 case '0':890 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));891 break;892 case '1':893 DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions));894 break;895 case '2':896 case '3':897 DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));898 break;899 }900 ++I;901 }902 break;903 case 'g':904 A->claim();905 break;906 case 'i':907 if (I + 1 != E && OptStr[I + 1] == '-') {908 ++I;909 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));910 } else {911 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));912 }913 break;914 case 's':915 DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");916 break;917 case 't':918 DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");919 break;920 case 'y': {921 bool OmitFramePointer = true;922 if (I + 1 != E && OptStr[I + 1] == '-') {923 OmitFramePointer = false;924 ++I;925 }926 if (SupportsForcingFramePointer) {927 if (OmitFramePointer)928 DAL.AddFlagArg(A,929 Opts.getOption(options::OPT_fomit_frame_pointer));930 else931 DAL.AddFlagArg(932 A, Opts.getOption(options::OPT_fno_omit_frame_pointer));933 } else {934 // Don't warn about /Oy- in x86-64 builds (where935 // SupportsForcingFramePointer is false). The flag having no effect936 // there is a compiler-internal optimization, and people shouldn't have937 // to special-case their build files for x86-64 clang-cl.938 A->claim();939 }940 break;941 }942 }943 }944}945 946static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL,947 const OptTable &Opts) {948 assert(A->getOption().matches(options::OPT_D));949 950 StringRef Val = A->getValue();951 size_t Hash = Val.find('#');952 if (Hash == StringRef::npos || Hash > Val.find('=')) {953 DAL.append(A);954 return;955 }956 957 std::string NewVal = std::string(Val);958 NewVal[Hash] = '=';959 DAL.AddJoinedArg(A, Opts.getOption(options::OPT_D), NewVal);960}961 962static void TranslatePermissive(Arg *A, llvm::opt::DerivedArgList &DAL,963 const OptTable &Opts) {964 DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase_));965 DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_operator_names));966}967 968static void TranslatePermissiveMinus(Arg *A, llvm::opt::DerivedArgList &DAL,969 const OptTable &Opts) {970 DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase));971 DAL.AddFlagArg(A, Opts.getOption(options::OPT_foperator_names));972}973 974llvm::opt::DerivedArgList *975MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,976 StringRef BoundArch,977 Action::OffloadKind OFK) const {978 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());979 const OptTable &Opts = getDriver().getOpts();980 981 // /Oy and /Oy- don't have an effect on X86-64982 bool SupportsForcingFramePointer = getArch() != llvm::Triple::x86_64;983 984 // The -O[12xd] flag actually expands to several flags. We must desugar the985 // flags so that options embedded can be negated. For example, the '-O2' flag986 // enables '-Oy'. Expanding '-O2' into its constituent flags allows us to987 // correctly handle '-O2 -Oy-' where the trailing '-Oy-' disables a single988 // aspect of '-O2'.989 //990 // Note that this expansion logic only applies to the *last* of '[12xd]'.991 992 // First step is to search for the character we'd like to expand.993 const char *ExpandChar = nullptr;994 for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {995 StringRef OptStr = A->getValue();996 for (size_t I = 0, E = OptStr.size(); I != E; ++I) {997 char OptChar = OptStr[I];998 char PrevChar = I > 0 ? OptStr[I - 1] : '0';999 if (PrevChar == 'b') {1000 // OptChar does not expand; it's an argument to the previous char.1001 continue;1002 }1003 if (OptChar == '1' || OptChar == '2' || OptChar == 'x' || OptChar == 'd')1004 ExpandChar = OptStr.data() + I;1005 }1006 }1007 1008 for (Arg *A : Args) {1009 if (A->getOption().matches(options::OPT__SLASH_O)) {1010 // The -O flag actually takes an amalgam of other options. For example,1011 // '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'.1012 TranslateOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar, Opts);1013 } else if (A->getOption().matches(options::OPT_D)) {1014 // Translate -Dfoo#bar into -Dfoo=bar.1015 TranslateDArg(A, *DAL, Opts);1016 } else if (A->getOption().matches(options::OPT__SLASH_permissive)) {1017 // Expand /permissive1018 TranslatePermissive(A, *DAL, Opts);1019 } else if (A->getOption().matches(options::OPT__SLASH_permissive_)) {1020 // Expand /permissive-1021 TranslatePermissiveMinus(A, *DAL, Opts);1022 } else if (OFK != Action::OFK_HIP) {1023 // HIP Toolchain translates input args by itself.1024 DAL->append(A);1025 }1026 }1027 1028 return DAL;1029}1030 1031void MSVCToolChain::addClangTargetOptions(1032 const ArgList &DriverArgs, ArgStringList &CC1Args,1033 Action::OffloadKind DeviceOffloadKind) const {1034 // MSVC STL kindly allows removing all usages of typeid by defining1035 // _HAS_STATIC_RTTI to 0. Do so, when compiling with -fno-rtti1036 if (DriverArgs.hasFlag(options::OPT_fno_rtti, options::OPT_frtti,1037 /*Default=*/false))1038 CC1Args.push_back("-D_HAS_STATIC_RTTI=0");1039 1040 if (Arg *A = DriverArgs.getLastArgNoClaim(options::OPT_marm64x))1041 A->ignoreTargetSpecific();1042}1043