diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp | 1150 |
1 files changed, 302 insertions, 848 deletions
diff --git a/contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp b/contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp index 13943b6c404a..ca266e3e1d1d 100644 --- a/contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/contrib/llvm-project/clang/lib/Driver/ToolChains/MSVC.cpp @@ -18,16 +18,16 @@ #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringSwitch.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/Host.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 @@ -39,270 +39,17 @@ #include <windows.h> #endif -#ifdef _MSC_VER -// Don't support SetupApi on MinGW. -#define USE_MSVC_SETUP_API - -// Make sure this comes before MSVCSetupApi.h -#include <comdef.h> - -#include "MSVCSetupApi.h" -#include "llvm/Support/COM.h" -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); -_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); -_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); -_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); -_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); -#endif - using namespace clang::driver; using namespace clang::driver::toolchains; using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; -// Defined below. -// Forward declare this so there aren't too many things above the constructor. -static bool getSystemRegistryString(const char *keyPath, const char *valueName, - std::string &value, std::string *phValue); - -// Check command line arguments to try and find a toolchain. -static bool -findVCToolChainViaCommandLine(const ArgList &Args, std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { - // Don't validate the input; trust the value supplied by the user. - // The primary motivation is to prevent unnecessary file and registry access. - if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir)) { - Path = A->getValue(); - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - return false; -} - -// Check various environment variables to try and find a toolchain. -static bool findVCToolChainViaEnvironment(std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { - // These variables are typically set by vcvarsall.bat - // when launching a developer command prompt. - if (llvm::Optional<std::string> VCToolsInstallDir = - llvm::sys::Process::GetEnv("VCToolsInstallDir")) { - // This is only set by newer Visual Studios, and it leads straight to - // the toolchain directory. - Path = std::move(*VCToolsInstallDir); - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - if (llvm::Optional<std::string> VCInstallDir = - llvm::sys::Process::GetEnv("VCINSTALLDIR")) { - // If the previous variable isn't set but this one is, then we've found - // an older Visual Studio. This variable is set by newer Visual Studios too, - // so this check has to appear second. - // In older Visual Studios, the VC directory is the toolchain. - Path = std::move(*VCInstallDir); - VSLayout = MSVCToolChain::ToolsetLayout::OlderVS; - return true; - } - - // We couldn't find any VC environment variables. Let's walk through PATH and - // see if it leads us to a VC toolchain bin directory. If it does, pick the - // first one that we find. - if (llvm::Optional<std::string> PathEnv = - llvm::sys::Process::GetEnv("PATH")) { - llvm::SmallVector<llvm::StringRef, 8> PathEntries; - llvm::StringRef(*PathEnv).split(PathEntries, llvm::sys::EnvPathSeparator); - for (llvm::StringRef PathEntry : PathEntries) { - if (PathEntry.empty()) - continue; - - llvm::SmallString<256> ExeTestPath; - - // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. - ExeTestPath = PathEntry; - llvm::sys::path::append(ExeTestPath, "cl.exe"); - if (!llvm::sys::fs::exists(ExeTestPath)) - continue; - - // cl.exe existing isn't a conclusive test for a VC toolchain; clang also - // has a cl.exe. So let's check for link.exe too. - ExeTestPath = PathEntry; - llvm::sys::path::append(ExeTestPath, "link.exe"); - if (!llvm::sys::fs::exists(ExeTestPath)) - continue; - - // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. - llvm::StringRef TestPath = PathEntry; - bool IsBin = llvm::sys::path::filename(TestPath).equals_lower("bin"); - if (!IsBin) { - // Strip any architecture subdir like "amd64". - TestPath = llvm::sys::path::parent_path(TestPath); - IsBin = llvm::sys::path::filename(TestPath).equals_lower("bin"); - } - if (IsBin) { - llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath); - llvm::StringRef ParentFilename = llvm::sys::path::filename(ParentPath); - if (ParentFilename == "VC") { - Path = std::string(ParentPath); - VSLayout = MSVCToolChain::ToolsetLayout::OlderVS; - return true; - } - if (ParentFilename == "x86ret" || ParentFilename == "x86chk" - || ParentFilename == "amd64ret" || ParentFilename == "amd64chk") { - Path = std::string(ParentPath); - VSLayout = MSVCToolChain::ToolsetLayout::DevDivInternal; - return true; - } - - } else { - // This could be a new (>=VS2017) toolchain. If it is, we should find - // path components with these prefixes when walking backwards through - // the path. - // Note: empty strings match anything. - llvm::StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", - "MSVC", "Tools", "VC"}; - - auto It = llvm::sys::path::rbegin(PathEntry); - auto End = llvm::sys::path::rend(PathEntry); - for (llvm::StringRef Prefix : ExpectedPrefixes) { - if (It == End) - goto NotAToolChain; - if (!It->startswith(Prefix)) - goto NotAToolChain; - ++It; - } - - // We've found a new toolchain! - // Back up 3 times (/bin/Host/arch) to get the root path. - llvm::StringRef ToolChainPath(PathEntry); - for (int i = 0; i < 3; ++i) - ToolChainPath = llvm::sys::path::parent_path(ToolChainPath); - - Path = std::string(ToolChainPath); - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - - NotAToolChain: - continue; - } - } - return false; -} - -// Query the Setup Config server for installs, then pick the newest version -// and find its default VC toolchain. -// This is the preferred way to discover new Visual Studios, as they're no -// longer listed in the registry. -static bool findVCToolChainViaSetupConfig(std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { -#if !defined(USE_MSVC_SETUP_API) - return false; -#else - // FIXME: This really should be done once in the top-level program's main - // function, as it may have already been initialized with a different - // threading model otherwise. - llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::SingleThreaded); - HRESULT HR; - - // _com_ptr_t will throw a _com_error if a COM calls fail. - // The LLVM coding standards forbid exception handling, so we'll have to - // stop them from being thrown in the first place. - // The destructor will put the regular error handler back when we leave - // this scope. - struct SuppressCOMErrorsRAII { - static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} - - SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } - - ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } - - } COMErrorSuppressor; - - ISetupConfigurationPtr Query; - HR = Query.CreateInstance(__uuidof(SetupConfiguration)); - if (FAILED(HR)) - return false; - - IEnumSetupInstancesPtr EnumInstances; - HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); - if (FAILED(HR)) - return false; - - ISetupInstancePtr Instance; - HR = EnumInstances->Next(1, &Instance, nullptr); - if (HR != S_OK) - return false; - - ISetupInstancePtr NewestInstance; - Optional<uint64_t> NewestVersionNum; - do { - bstr_t VersionString; - uint64_t VersionNum; - HR = Instance->GetInstallationVersion(VersionString.GetAddress()); - if (FAILED(HR)) - continue; - HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum); - if (FAILED(HR)) - continue; - if (!NewestVersionNum || (VersionNum > NewestVersionNum)) { - NewestInstance = Instance; - NewestVersionNum = VersionNum; - } - } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK); - - if (!NewestInstance) +static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) { + auto Status = VFS.status(Path); + if (!Status) return false; - - bstr_t VCPathWide; - HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress()); - if (FAILED(HR)) - return false; - - std::string VCRootPath; - llvm::convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); - - llvm::SmallString<256> ToolsVersionFilePath(VCRootPath); - llvm::sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", - "Microsoft.VCToolsVersion.default.txt"); - - auto ToolsVersionFile = llvm::MemoryBuffer::getFile(ToolsVersionFilePath); - if (!ToolsVersionFile) - return false; - - llvm::SmallString<256> ToolchainPath(VCRootPath); - llvm::sys::path::append(ToolchainPath, "Tools", "MSVC", - ToolsVersionFile->get()->getBuffer().rtrim()); - if (!llvm::sys::fs::is_directory(ToolchainPath)) - return false; - - Path = std::string(ToolchainPath.str()); - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; -#endif -} - -// Look in the registry for Visual Studio installs, and use that to get -// a toolchain path. VS2017 and newer don't get added to the registry. -// So if we find something here, we know that it's an older version. -static bool findVCToolChainViaRegistry(std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { - std::string VSInstallPath; - if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", - "InstallDir", VSInstallPath, nullptr) || - getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", - "InstallDir", VSInstallPath, nullptr)) { - if (!VSInstallPath.empty()) { - llvm::SmallString<256> VCPath(llvm::StringRef( - VSInstallPath.c_str(), VSInstallPath.find(R"(\Common7\IDE)"))); - llvm::sys::path::append(VCPath, "VC"); - - Path = std::string(VCPath.str()); - VSLayout = MSVCToolChain::ToolsetLayout::OlderVS; - return true; - } - } - 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 @@ -312,11 +59,10 @@ static bool findVCToolChainViaRegistry(std::string &Path, static std::string FindVisualStudioExecutable(const ToolChain &TC, const char *Exe) { const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC); - SmallString<128> FilePath(MSVC.getSubDirectoryPath( - toolchains::MSVCToolChain::SubDirectoryType::Bin)); + SmallString<128> FilePath( + MSVC.getSubDirectoryPath(llvm::SubDirectoryType::Bin)); llvm::sys::path::append(FilePath, Exe); - return std::string(llvm::sys::fs::can_execute(FilePath) ? FilePath.str() - : Exe); + return std::string(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe); } void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, @@ -333,40 +79,75 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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().IsCLMode() && !C.getDriver().IsFlangMode()) { CmdArgs.push_back("-defaultlib:libcmt"); CmdArgs.push_back("-defaultlib:oldnames"); } - if (!llvm::sys::Process::GetEnv("LIB")) { - // 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 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( - toolchains::MSVCToolChain::SubDirectoryType::Lib))); - + TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib))); CmdArgs.push_back(Args.MakeArgString( Twine("-libpath:") + - TC.getSubDirectoryPath(toolchains::MSVCToolChain::SubDirectoryType::Lib, - "atlmfc"))); - + 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(UniversalCRTLibPath)) + if (TC.getUniversalCRTLibraryPath(Args, UniversalCRTLibPath)) CmdArgs.push_back( Args.MakeArgString(Twine("-libpath:") + UniversalCRTLibPath)); } - std::string WindowsSdkLibPath; - if (TC.getWindowsSDKLibraryPath(WindowsSdkLibPath)) + if (TC.getWindowsSDKLibraryPath(Args, WindowsSdkLibPath)) CmdArgs.push_back( Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath)); } + 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)); + + 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()) { @@ -377,15 +158,16 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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 = @@ -405,7 +187,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName)); } - if (TC.getSanitizerArgs().needsFuzzer()) { + if (TC.getSanitizerArgs(Args).needsFuzzer()) { if (!Args.hasArg(options::OPT_shared)) CmdArgs.push_back( Args.MakeArgString(std::string("-wholearchive:") + @@ -416,10 +198,10 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString("-incremental:no")); } - if (TC.getSanitizerArgs().needsAsanRt()) { + if (TC.getSanitizerArgs(Args).needsAsanRt()) { CmdArgs.push_back(Args.MakeArgString("-debug")); CmdArgs.push_back(Args.MakeArgString("-incremental:no")); - if (TC.getSanitizerArgs().needsSharedRt() || + 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)); @@ -450,13 +232,18 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link); // Control Flow Guard checks - if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) { + for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) { StringRef GuardArgs = A->getValue(); - if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) { + 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_lower("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-"); } } @@ -487,6 +274,26 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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()) { @@ -500,7 +307,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (A.getOption().matches(options::OPT_l)) { StringRef Lib = A.getValue(); const char *LinkLibArg; - if (Lib.endswith(".lib")) + if (Lib.ends_with(".lib")) LinkLibArg = Args.MakeArgString(Lib); else LinkLibArg = Args.MakeArgString(Lib + ".lib"); @@ -513,47 +320,46 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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 lld, we need to - // translate 'lld' into 'lld-link', and in the case of the regular msvc + // 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; - StringRef Linker - = Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER); - if (Linker.empty()) - Linker = "link"; - if (Linker.equals_lower("lld")) - Linker = "lld-link"; - - if (Linker.equals_lower("link")) { + 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() && !llvm::sys::fs::can_execute(linkPath)) { + if (!TC.FoundMSVCInstall() && !canExecute(TC.getVFS(), linkPath)) { llvm::SmallString<128> ClPath; ClPath = TC.GetProgramPath("cl.exe"); - if (llvm::sys::fs::can_execute(ClPath)) { + if (canExecute(TC.getVFS(), ClPath)) { linkPath = llvm::sys::path::parent_path(ClPath); llvm::sys::path::append(linkPath, "link.exe"); - if (!llvm::sys::fs::can_execute(linkPath)) + 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 ToolsetLayout::DevDivInternal. + // 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(); @@ -587,14 +393,13 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, // find it. for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) { llvm::StringRef EnvVar(Cursor); - if (EnvVar.startswith_lower("path=")) { - using SubDirectoryType = toolchains::MSVCToolChain::SubDirectoryType; + if (EnvVar.starts_with_insensitive("path=")) { constexpr size_t PrefixLen = 5; // strlen("path=") Environment.push_back(Args.MakeArgString( EnvVar.substr(0, PrefixLen) + - TC.getSubDirectoryPath(SubDirectoryType::Bin) + + TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin) + llvm::Twine(llvm::sys::EnvPathSeparator) + - TC.getSubDirectoryPath(SubDirectoryType::Bin, "", HostArch) + + TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin, HostArch) + (EnvVar.size() > PrefixLen ? llvm::Twine(llvm::sys::EnvPathSeparator) + EnvVar.substr(PrefixLen) @@ -619,161 +424,35 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(std::move(LinkCmd)); } -void visualstudio::Compiler::ConstructJob(Compilation &C, const JobAction &JA, - const InputInfo &Output, - const InputInfoList &Inputs, - const ArgList &Args, - const char *LinkingOutput) const { - C.addCommand(GetCommand(C, JA, Output, Inputs, Args, LinkingOutput)); -} - -std::unique_ptr<Command> visualstudio::Compiler::GetCommand( - Compilation &C, const JobAction &JA, const InputInfo &Output, - const InputInfoList &Inputs, const ArgList &Args, - const char *LinkingOutput) const { - ArgStringList CmdArgs; - CmdArgs.push_back("/nologo"); - CmdArgs.push_back("/c"); // Compile only. - CmdArgs.push_back("/W0"); // No warnings. - - // The goal is to be able to invoke this tool correctly based on - // any flag accepted by clang-cl. - - // These are spelled the same way in clang and cl.exe,. - Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I}); - - // Optimization level. - if (Arg *A = Args.getLastArg(options::OPT_fbuiltin, options::OPT_fno_builtin)) - CmdArgs.push_back(A->getOption().getID() == options::OPT_fbuiltin ? "/Oi" - : "/Oi-"); - if (Arg *A = Args.getLastArg(options::OPT_O, options::OPT_O0)) { - if (A->getOption().getID() == options::OPT_O0) { - CmdArgs.push_back("/Od"); - } else { - CmdArgs.push_back("/Og"); - - StringRef OptLevel = A->getValue(); - if (OptLevel == "s" || OptLevel == "z") - CmdArgs.push_back("/Os"); - else - CmdArgs.push_back("/Ot"); - - CmdArgs.push_back("/Ob2"); - } - } - if (Arg *A = Args.getLastArg(options::OPT_fomit_frame_pointer, - options::OPT_fno_omit_frame_pointer)) - CmdArgs.push_back(A->getOption().getID() == options::OPT_fomit_frame_pointer - ? "/Oy" - : "/Oy-"); - if (!Args.hasArg(options::OPT_fwritable_strings)) - CmdArgs.push_back("/GF"); - - // Flags for which clang-cl has an alias. - // FIXME: How can we ensure this stays in sync with relevant clang-cl options? - - if (Args.hasFlag(options::OPT__SLASH_GR_, options::OPT__SLASH_GR, - /*Default=*/false)) - CmdArgs.push_back("/GR-"); - - if (Args.hasFlag(options::OPT__SLASH_GS_, options::OPT__SLASH_GS, - /*Default=*/false)) - CmdArgs.push_back("/GS-"); - - if (Arg *A = Args.getLastArg(options::OPT_ffunction_sections, - options::OPT_fno_function_sections)) - CmdArgs.push_back(A->getOption().getID() == options::OPT_ffunction_sections - ? "/Gy" - : "/Gy-"); - if (Arg *A = Args.getLastArg(options::OPT_fdata_sections, - options::OPT_fno_data_sections)) - CmdArgs.push_back( - A->getOption().getID() == options::OPT_fdata_sections ? "/Gw" : "/Gw-"); - if (Args.hasArg(options::OPT_fsyntax_only)) - CmdArgs.push_back("/Zs"); - if (Args.hasArg(options::OPT_g_Flag, options::OPT_gline_tables_only, - options::OPT__SLASH_Z7)) - CmdArgs.push_back("/Z7"); - - std::vector<std::string> Includes = - Args.getAllArgValues(options::OPT_include); - for (const auto &Include : Includes) - CmdArgs.push_back(Args.MakeArgString(std::string("/FI") + Include)); - - // Flags that can simply be passed through. - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LD); - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LDd); - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_GX); - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_GX_); - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_EH); - Args.AddAllArgs(CmdArgs, options::OPT__SLASH_Zl); - - // The order of these flags is relevant, so pick the last one. - if (Arg *A = Args.getLastArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd, - options::OPT__SLASH_MT, options::OPT__SLASH_MTd)) - A->render(Args, CmdArgs); - - // Use MSVC's default threadsafe statics behaviour unless there was a flag. - if (Arg *A = Args.getLastArg(options::OPT_fthreadsafe_statics, - options::OPT_fno_threadsafe_statics)) { - CmdArgs.push_back(A->getOption().getID() == options::OPT_fthreadsafe_statics - ? "/Zc:threadSafeInit" - : "/Zc:threadSafeInit-"); - } - - // Control Flow Guard checks - if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) { - StringRef GuardArgs = A->getValue(); - if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) { - // MSVC doesn't yet support the "nochecks" modifier. - CmdArgs.push_back("/guard:cf"); - } else if (GuardArgs.equals_lower("cf-")) { - CmdArgs.push_back("/guard:cf-"); - } - } - - // Pass through all unknown arguments so that the fallback command can see - // them too. - Args.AddAllArgs(CmdArgs, options::OPT_UNKNOWN); - - // Input filename. - assert(Inputs.size() == 1); - const InputInfo &II = Inputs[0]; - assert(II.getType() == types::TY_C || II.getType() == types::TY_CXX); - CmdArgs.push_back(II.getType() == types::TY_C ? "/Tc" : "/Tp"); - if (II.isFilename()) - CmdArgs.push_back(II.getFilename()); - else - II.getInputArg().renderAsInput(Args, CmdArgs); - - // Output filename. - assert(Output.getType() == types::TY_Object); - const char *Fo = - Args.MakeArgString(std::string("/Fo") + Output.getFilename()); - CmdArgs.push_back(Fo); - - std::string Exec = FindVisualStudioExecutable(getToolChain(), "cl.exe"); - return std::make_unique<Command>( - JA, *this, ResponseFileSupport::AtFileUTF16(), Args.MakeArgString(Exec), - CmdArgs, Inputs, Output); -} - 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().getInstalledDir()); - if (getDriver().getInstalledDir() != getDriver().Dir) - getProgramPaths().push_back(getDriver().Dir); + 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. - findVCToolChainViaCommandLine(Args, VCToolChainPath, VSLayout) || - findVCToolChainViaEnvironment(VCToolChainPath, VSLayout) || - findVCToolChainViaSetupConfig(VCToolChainPath, VSLayout) || - findVCToolChainViaRegistry(VCToolChainPath, VSLayout); + 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 { @@ -787,439 +466,132 @@ Tool *MSVCToolChain::buildAssembler() const { return nullptr; } -bool MSVCToolChain::IsIntegratedAssemblerDefault() const { - return true; -} - -bool MSVCToolChain::IsUnwindTablesDefault(const ArgList &Args) const { +ToolChain::UnwindTableLevel +MSVCToolChain::getDefaultUnwindTableLevel(const ArgList &Args) const { // Don't emit unwind tables by default for MachO targets. if (getTriple().isOSBinFormatMachO()) - return false; + 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. - return getArch() == llvm::Triple::x86_64 || - getArch() == llvm::Triple::aarch64; + 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; + return getArch() == llvm::Triple::x86_64 || + getArch() == llvm::Triple::aarch64; } -bool MSVCToolChain::isPIEDefault() const { +bool MSVCToolChain::isPIEDefault(const llvm::opt::ArgList &Args) const { return false; } bool MSVCToolChain::isPICDefaultForced() const { - return getArch() == llvm::Triple::x86_64; + return getArch() == llvm::Triple::x86_64 || + getArch() == llvm::Triple::aarch64; } void MSVCToolChain::AddCudaIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { - CudaInstallation.AddCudaIncludeArgs(DriverArgs, CC1Args); + CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args); } void MSVCToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { - RocmInstallation.AddHIPIncludeArgs(DriverArgs, CC1Args); + RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args); } -void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const { - CudaInstallation.print(OS); - RocmInstallation.print(OS); -} - -// Windows SDKs and VC Toolchains group their contents into subdirectories based -// on the target architecture. This function converts an llvm::Triple::ArchType -// to the corresponding subdirectory name. -static const char *llvmArchToWindowsSDKArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - return "x86"; - case ArchType::x86_64: - return "x64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } +void MSVCToolChain::AddHIPRuntimeLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + CmdArgs.append({Args.MakeArgString(StringRef("-libpath:") + + RocmInstallation->getLibPath()), + "amdhip64.lib"}); } -// Similar to the above function, but for Visual Studios before VS2017. -static const char *llvmArchToLegacyVCArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - // x86 is default in legacy VC toolchains. - // e.g. x86 libs are directly in /lib as opposed to /lib/x86. - return ""; - case ArchType::x86_64: - return "amd64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } +void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const { + CudaInstallation->print(OS); + RocmInstallation->print(OS); } -// Similar to the above function, but for DevDiv internal builds. -static const char *llvmArchToDevDivInternalArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - return "i386"; - case ArchType::x86_64: - return "amd64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } +std::string +MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, + llvm::StringRef SubdirParent) const { + return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(), + SubdirParent); } -// Get the path to a specific subdirectory in the current toolchain for -// a given target architecture. -// VS2017 changed the VC toolchain layout, so this should be used instead -// of hardcoding paths. std::string -MSVCToolChain::getSubDirectoryPath(SubDirectoryType Type, - llvm::StringRef SubdirParent, +MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, llvm::Triple::ArchType TargetArch) const { - const char *SubdirName; - const char *IncludeName; - switch (VSLayout) { - case ToolsetLayout::OlderVS: - SubdirName = llvmArchToLegacyVCArch(TargetArch); - IncludeName = "include"; - break; - case ToolsetLayout::VS2017OrNewer: - SubdirName = llvmArchToWindowsSDKArch(TargetArch); - IncludeName = "include"; - break; - case ToolsetLayout::DevDivInternal: - SubdirName = llvmArchToDevDivInternalArch(TargetArch); - IncludeName = "inc"; - break; - } - - llvm::SmallString<256> Path(VCToolChainPath); - if (!SubdirParent.empty()) - llvm::sys::path::append(Path, SubdirParent); - - switch (Type) { - case SubDirectoryType::Bin: - if (VSLayout == ToolsetLayout::VS2017OrNewer) { - const bool HostIsX64 = - llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit(); - const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; - llvm::sys::path::append(Path, "bin", HostName, SubdirName); - } else { // OlderVS or DevDivInternal - llvm::sys::path::append(Path, "bin", SubdirName); - } - break; - case SubDirectoryType::Include: - llvm::sys::path::append(Path, IncludeName); - break; - case SubDirectoryType::Lib: - llvm::sys::path::append(Path, "lib", SubdirName); - break; - } - return std::string(Path.str()); -} - -#ifdef _WIN32 -static bool readFullStringValue(HKEY hkey, const char *valueName, - std::string &value) { - std::wstring WideValueName; - if (!llvm::ConvertUTF8toWide(valueName, WideValueName)) - return false; - - DWORD result = 0; - DWORD valueSize = 0; - DWORD type = 0; - // First just query for the required size. - result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL, - &valueSize); - if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize) - return false; - std::vector<BYTE> buffer(valueSize); - result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], - &valueSize); - if (result == ERROR_SUCCESS) { - std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()), - valueSize / sizeof(wchar_t)); - if (valueSize && WideValue.back() == L'\0') { - WideValue.pop_back(); - } - // The destination buffer must be empty as an invariant of the conversion - // function; but this function is sometimes called in a loop that passes in - // the same buffer, however. Simply clear it out so we can overwrite it. - value.clear(); - return llvm::convertWideToUTF8(WideValue, value); - } - return false; -} -#endif - -/// Read registry string. -/// This also supports a means to look for high-versioned keys by use -/// of a $VERSION placeholder in the key path. -/// $VERSION in the key path is a placeholder for the version number, -/// causing the highest value path to be searched for and used. -/// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION". -/// There can be additional characters in the component. Only the numeric -/// characters are compared. This function only searches HKLM. -static bool getSystemRegistryString(const char *keyPath, const char *valueName, - std::string &value, std::string *phValue) { -#ifndef _WIN32 - return false; -#else - HKEY hRootKey = HKEY_LOCAL_MACHINE; - HKEY hKey = NULL; - long lResult; - bool returnValue = false; - - const char *placeHolder = strstr(keyPath, "$VERSION"); - std::string bestName; - // If we have a $VERSION placeholder, do the highest-version search. - if (placeHolder) { - const char *keyEnd = placeHolder - 1; - const char *nextKey = placeHolder; - // Find end of previous key. - while ((keyEnd > keyPath) && (*keyEnd != '\\')) - keyEnd--; - // Find end of key containing $VERSION. - while (*nextKey && (*nextKey != '\\')) - nextKey++; - size_t partialKeyLength = keyEnd - keyPath; - char partialKey[256]; - if (partialKeyLength >= sizeof(partialKey)) - partialKeyLength = sizeof(partialKey) - 1; - strncpy(partialKey, keyPath, partialKeyLength); - partialKey[partialKeyLength] = '\0'; - HKEY hTopKey = NULL; - lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY, - &hTopKey); - if (lResult == ERROR_SUCCESS) { - char keyName[256]; - double bestValue = 0.0; - DWORD index, size = sizeof(keyName) - 1; - for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL, - NULL, NULL) == ERROR_SUCCESS; - index++) { - const char *sp = keyName; - while (*sp && !isDigit(*sp)) - sp++; - if (!*sp) - continue; - const char *ep = sp + 1; - while (*ep && (isDigit(*ep) || (*ep == '.'))) - ep++; - char numBuf[32]; - strncpy(numBuf, sp, sizeof(numBuf) - 1); - numBuf[sizeof(numBuf) - 1] = '\0'; - double dvalue = strtod(numBuf, NULL); - if (dvalue > bestValue) { - // Test that InstallDir is indeed there before keeping this index. - // Open the chosen key path remainder. - bestName = keyName; - // Append rest of key. - bestName.append(nextKey); - lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0, - KEY_READ | KEY_WOW64_32KEY, &hKey); - if (lResult == ERROR_SUCCESS) { - if (readFullStringValue(hKey, valueName, value)) { - bestValue = dvalue; - if (phValue) - *phValue = bestName; - returnValue = true; - } - RegCloseKey(hKey); - } - } - size = sizeof(keyName) - 1; - } - RegCloseKey(hTopKey); - } - } else { - lResult = - RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey); - if (lResult == ERROR_SUCCESS) { - if (readFullStringValue(hKey, valueName, value)) - returnValue = true; - if (phValue) - phValue->clear(); - RegCloseKey(hKey); - } - } - return returnValue; -#endif // _WIN32 + 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. -static bool getWindows10SDKVersionFromPath(const std::string &SDKPath, - std::string &SDKVersion) { - SDKVersion.clear(); - - std::error_code EC; - llvm::SmallString<128> IncludePath(SDKPath); - llvm::sys::path::append(IncludePath, "Include"); - for (llvm::sys::fs::directory_iterator DirIt(IncludePath, EC), DirEnd; - DirIt != DirEnd && !EC; DirIt.increment(EC)) { - if (!llvm::sys::fs::is_directory(DirIt->path())) - continue; - StringRef CandidateName = llvm::sys::path::filename(DirIt->path()); - // If WDK is installed, there could be subfolders like "wdf" in the - // "Include" directory. - // Allow only directories which names start with "10.". - if (!CandidateName.startswith("10.")) - continue; - if (CandidateName > SDKVersion) - SDKVersion = std::string(CandidateName); - } - - return !SDKVersion.empty(); -} - -/// Get Windows SDK installation directory. -static bool getWindowsSDKDir(std::string &Path, int &Major, - std::string &WindowsSDKIncludeVersion, - std::string &WindowsSDKLibVersion) { - std::string RegistrySDKVersion; - // Try the Windows registry. - if (!getSystemRegistryString( - "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", - "InstallationFolder", Path, &RegistrySDKVersion)) - return false; - if (Path.empty() || RegistrySDKVersion.empty()) - return false; - - WindowsSDKIncludeVersion.clear(); - WindowsSDKLibVersion.clear(); - Major = 0; - std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major); - if (Major <= 7) - return true; - if (Major == 8) { - // Windows SDK 8.x installs libraries in a folder whose names depend on the - // version of the OS you're targeting. By default choose the newest, which - // usually corresponds to the version of the OS you've installed the SDK on. - const char *Tests[] = {"winv6.3", "win8", "win7"}; - for (const char *Test : Tests) { - llvm::SmallString<128> TestPath(Path); - llvm::sys::path::append(TestPath, "Lib", Test); - if (llvm::sys::fs::exists(TestPath.c_str())) { - WindowsSDKLibVersion = Test; - break; - } - } - return !WindowsSDKLibVersion.empty(); - } - if (Major == 10) { - if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion)) - return false; - WindowsSDKLibVersion = WindowsSDKIncludeVersion; - return true; - } - // Unsupported SDK version - return false; -} - // Gets the library path required to link against the Windows SDK. -bool MSVCToolChain::getWindowsSDKLibraryPath(std::string &path) const { +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 (!getWindowsSDKDir(sdkPath, sdkMajor, windowsSDKIncludeVersion, - windowsSDKLibVersion)) + 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 >= 8) { - llvm::sys::path::append(libPath, windowsSDKLibVersion, "um", - llvmArchToWindowsSDKArch(getArch())); - } else { - switch (getArch()) { - // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. - case llvm::Triple::x86: - break; - case llvm::Triple::x86_64: - llvm::sys::path::append(libPath, "x64"); - break; - case llvm::Triple::arm: - // It is not necessary to link against Windows SDK 7.x when targeting ARM. - return false; - default: - return false; - } - } - - path = std::string(libPath.str()); - return true; + 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); } -// Check if the Include path of a specified version of Visual Studio contains -// specific header files. If not, they are probably shipped with Universal CRT. bool MSVCToolChain::useUniversalCRT() const { - llvm::SmallString<128> TestPath( - getSubDirectoryPath(SubDirectoryType::Include)); - llvm::sys::path::append(TestPath, "stdlib.h"); - return !llvm::sys::fs::exists(TestPath); -} - -static bool getUniversalCRTSdkDir(std::string &Path, std::string &UCRTVersion) { - // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry - // for the specific key "KitsRoot10". So do we. - if (!getSystemRegistryString( - "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", - Path, nullptr)) - return false; - - return getWindows10SDKVersionFromPath(Path, UCRTVersion); + return llvm::useUniversalCRT(VSLayout, VCToolChainPath, getArch(), getVFS()); } -bool MSVCToolChain::getUniversalCRTLibraryPath(std::string &Path) const { +bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, + std::string &Path) const { std::string UniversalCRTSdkPath; std::string UCRTVersion; Path.clear(); - if (!getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) + if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion, + WinSysRoot, UniversalCRTSdkPath, + UCRTVersion)) return false; - StringRef ArchName = llvmArchToWindowsSDKArch(getArch()); + 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.str()); + Path = std::string(LibPath); return true; } -static VersionTuple getMSVCVersionFromTriple(const llvm::Triple &Triple) { - unsigned Major, Minor, Micro; - Triple.getEnvironmentVersion(Major, Minor, Micro); - if (Major || Minor || Micro) - return VersionTuple(Major, Minor, Micro); - return VersionTuple(); -} - static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) { VersionTuple Version; #ifdef _WIN32 @@ -1279,62 +651,108 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 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%. It should know essential search paths with vcvarsall.bat. - // Skip if the user expressly set a vctoolsdir - if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir)) { - if (llvm::Optional<std::string> cl_include_dir = - llvm::sys::Process::GetEnv("INCLUDE")) { - SmallVector<StringRef, 8> Dirs; - StringRef(*cl_include_dir) - .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (StringRef Dir : Dirs) - addSystemInclude(DriverArgs, CC1Args, Dir); - if (!Dirs.empty()) - 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(SubDirectoryType::Include)); - addSystemInclude(DriverArgs, CC1Args, - getSubDirectoryPath(SubDirectoryType::Include, "atlmfc")); + getSubDirectoryPath(llvm::SubDirectoryType::Include)); + addSystemInclude( + DriverArgs, CC1Args, + getSubDirectoryPath(llvm::SubDirectoryType::Include, "atlmfc")); if (useUniversalCRT()) { std::string UniversalCRTSdkPath; std::string UCRTVersion; - if (getUniversalCRTSdkDir(UniversalCRTSdkPath, 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; + int major = 0; std::string windowsSDKIncludeVersion; std::string windowsSDKLibVersion; - if (getWindowsSDKDir(WindowsSDKDir, major, windowsSDKIncludeVersion, - 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, + "Include", windowsSDKIncludeVersion, "shared"); AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include", windowsSDKIncludeVersion, + "Include", windowsSDKIncludeVersion, "um"); AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, - "include", windowsSDKIncludeVersion, + "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"); + "Include"); } } @@ -1365,14 +783,18 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D, bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment(); VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args); if (MSVT.empty()) - MSVT = getMSVCVersionFromTriple(getTriple()); + MSVT = getTriple().getEnvironmentVersion(); if (MSVT.empty() && IsWindowsMSVC) - MSVT = getMSVCVersionFromExe(getSubDirectoryPath(SubDirectoryType::Bin)); + 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.11 is default, aka 2017, 15.3 - MSVT = VersionTuple(19, 11); + // -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; } @@ -1383,8 +805,8 @@ MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args, // 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().getValueOr(0), - MSVT.getSubminor().getValueOr(0)); + 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. @@ -1439,7 +861,7 @@ static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL, 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"); + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3"); } if (SupportsForcingFramePointer && !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer)) @@ -1458,6 +880,7 @@ static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL, DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions)); break; case '2': + case '3': DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions)); break; } @@ -1479,7 +902,7 @@ static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL, DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s"); break; case 't': - DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2"); + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3"); break; case 'y': { bool OmitFramePointer = true; @@ -1523,6 +946,18 @@ static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL, 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, @@ -1565,6 +1000,12 @@ MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, } 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); @@ -1573,3 +1014,16 @@ MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, 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(); +} |