[clang] b3b2538 - [lld/coff] Make lld-link work in a non-MSVC shell, add /winsysroot:

Nico Weber via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 11 10:55:34 PST 2022


Author: Peter Kasting
Date: 2022-02-11T13:55:18-05:00
New Revision: b3b2538df100ec7f6587b0ee70819a3c8ee2c27e

URL: https://github.com/llvm/llvm-project/commit/b3b2538df100ec7f6587b0ee70819a3c8ee2c27e
DIFF: https://github.com/llvm/llvm-project/commit/b3b2538df100ec7f6587b0ee70819a3c8ee2c27e.diff

LOG: [lld/coff] Make lld-link work in a non-MSVC shell, add /winsysroot:

Makes lld-link work in a non-MSVC shell by autodetecting MSVC toolchain. Also
adds support for /winsysroot and a few other switches.

All this is done by refactoring to share code with clang-cl's existing support
for the same.

Differential Revision: https://reviews.llvm.org/D118070

Added: 
    lld/test/COFF/winsysroot.test
    llvm/include/llvm/Support/MSVCPaths.h
    llvm/include/llvm/Support/MSVCSetupApi.h
    llvm/lib/Support/MSVCPaths.cpp

Modified: 
    clang/docs/tools/clang-formatted-files.txt
    clang/lib/Driver/ToolChains/MSVC.cpp
    clang/lib/Driver/ToolChains/MSVC.h
    lld/COFF/Driver.cpp
    lld/COFF/Driver.h
    lld/COFF/Options.td
    lld/COFF/SymbolTable.cpp
    lld/docs/ReleaseNotes.rst
    llvm/lib/Support/CMakeLists.txt
    llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
    utils/bazel/llvm-project-overlay/clang/BUILD.bazel

Removed: 
    clang/lib/Driver/ToolChains/MSVCSetupApi.h


################################################################################
diff  --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt
index c7defa9cd88c6..5666ad499d791 100644
--- a/clang/docs/tools/clang-formatted-files.txt
+++ b/clang/docs/tools/clang-formatted-files.txt
@@ -285,7 +285,6 @@ clang/lib/Driver/ToolChains/InterfaceStubs.h
 clang/lib/Driver/ToolChains/Minix.h
 clang/lib/Driver/ToolChains/MipsLinux.cpp
 clang/lib/Driver/ToolChains/MSP430.h
-clang/lib/Driver/ToolChains/MSVCSetupApi.h
 clang/lib/Driver/ToolChains/PPCFreeBSD.cpp
 clang/lib/Driver/ToolChains/PPCFreeBSD.h
 clang/lib/Driver/ToolChains/PPCLinux.h
@@ -5150,6 +5149,8 @@ llvm/include/llvm/Support/MemoryBufferRef.h
 llvm/include/llvm/Support/MSP430AttributeParser.h
 llvm/include/llvm/Support/MSP430Attributes.h
 llvm/include/llvm/Support/MSVCErrorWorkarounds.h
+llvm/include/llvm/Support/MSVCPaths.h
+llvm/include/llvm/Support/MSVCSetupApi.h
 llvm/include/llvm/Support/Parallel.h
 llvm/include/llvm/Support/PGOOptions.h
 llvm/include/llvm/Support/PointerLikeTypeTraits.h
@@ -5840,6 +5841,7 @@ llvm/lib/Support/Memory.cpp
 llvm/lib/Support/MemoryBufferRef.cpp
 llvm/lib/Support/MSP430AttributeParser.cpp
 llvm/lib/Support/MSP430Attributes.cpp
+llvm/lib/Support/MSVCPaths.cpp
 llvm/lib/Support/Optional.cpp
 llvm/lib/Support/Parallel.cpp
 llvm/lib/Support/Program.cpp

diff  --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 9f4751167ac12..36e190f69154b 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
+#include "llvm/Support/MSVCPaths.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
@@ -40,91 +41,12 @@
   #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>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-#include "MSVCSetupApi.h"
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-#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;
 
-// 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 "";
-  }
-}
-
-// 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 "";
-  }
-}
-
-// 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 "";
-  }
-}
-
 static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) {
   auto Status = VFS.status(Path);
   if (!Status)
@@ -132,294 +54,6 @@ static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) {
   return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0;
 }
 
-// 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);
-
-static std::string getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS,
-                                                     StringRef Directory) {
-  std::string Highest;
-  llvm::VersionTuple HighestTuple;
-
-  std::error_code EC;
-  for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC),
-                                     DirEnd;
-       !EC && DirIt != DirEnd; DirIt.increment(EC)) {
-    auto Status = VFS.status(DirIt->path());
-    if (!Status || !Status->isDirectory())
-      continue;
-    StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
-    llvm::VersionTuple Tuple;
-    if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.
-      continue;
-    if (Tuple > HighestTuple) {
-      HighestTuple = Tuple;
-      Highest = CandidateName.str();
-    }
-  }
-
-  return Highest;
-}
-
-// Check command line arguments to try and find a toolchain.
-static bool
-findVCToolChainViaCommandLine(llvm::vfs::FileSystem &VFS, 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,
-                               options::OPT__SLASH_winsysroot)) {
-    if (A->getOption().getID() == options::OPT__SLASH_winsysroot) {
-      llvm::SmallString<128> ToolsPath(A->getValue());
-      llvm::sys::path::append(ToolsPath, "VC", "Tools", "MSVC");
-      std::string VCToolsVersion;
-      if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion))
-        VCToolsVersion = A->getValue();
-      else
-        VCToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath);
-      llvm::sys::path::append(ToolsPath, VCToolsVersion);
-      Path = std::string(ToolsPath.str());
-    } else {
-      Path = A->getValue();
-    }
-    VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer;
-    return true;
-  }
-  return false;
-}
-
-// Check various environment variables to try and find a toolchain.
-static bool
-findVCToolChainViaEnvironment(llvm::vfs::FileSystem &VFS, 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 (!VFS.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 (!VFS.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_insensitive("bin");
-      if (!IsBin) {
-        // Strip any architecture subdir like "amd64".
-        TestPath = llvm::sys::path::parent_path(TestPath);
-        IsBin = llvm::sys::path::filename(TestPath).equals_insensitive("bin");
-      }
-      if (IsBin) {
-        llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath);
-        llvm::StringRef ParentFilename = llvm::sys::path::filename(ParentPath);
-        if (ParentFilename.equals_insensitive("VC")) {
-          Path = std::string(ParentPath);
-          VSLayout = MSVCToolChain::ToolsetLayout::OlderVS;
-          return true;
-        }
-        if (ParentFilename.equals_insensitive("x86ret") ||
-            ParentFilename.equals_insensitive("x86chk") ||
-            ParentFilename.equals_insensitive("amd64ret") ||
-            ParentFilename.equals_insensitive("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_insensitive(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(llvm::vfs::FileSystem &VFS, 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 
diff erent
-  // 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)
-    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());
-  auto Status = VFS.status(ToolchainPath);
-  if (!Status || !Status->isDirectory())
-    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;
-}
-
 // Try to find Exe from a Visual Studio distribution.  This first tries to find
 // an installed copy of Visual Studio and, failing that, looks in the PATH,
 // making sure that whatever executable that's found is not a same-named exe
@@ -427,8 +61,8 @@ 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(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe);
 }
@@ -469,7 +103,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
 
     // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
     llvm::sys::path::append(DIAPath, "lib",
-                            llvmArchToLegacyVCArch(TC.getArch()));
+                            llvm::archToLegacyVCArch(TC.getArch()));
     CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath));
   }
   if (!llvm::sys::Process::GetEnv("LIB") ||
@@ -477,12 +111,10 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
                       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,
@@ -695,7 +327,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     // 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();
@@ -730,13 +362,12 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
       for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) {
         llvm::StringRef EnvVar(Cursor);
         if (EnvVar.startswith_insensitive("path=")) {
-          using SubDirectoryType = toolchains::MSVCToolChain::SubDirectoryType;
           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)
@@ -769,14 +400,29 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
   if (getDriver().getInstalledDir() != getDriver().Dir)
     getProgramPaths().push_back(getDriver().Dir);
 
+  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(getVFS(), Args, VCToolChainPath, VSLayout) ||
-      findVCToolChainViaEnvironment(getVFS(), VCToolChainPath, VSLayout) ||
-      findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath, VSLayout) ||
-      findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
+  llvm::findVCToolChainViaCommandLine(getVFS(), VCToolsDir, VCToolsVersion,
+                                      WinSysRoot, VCToolChainPath, VSLayout) ||
+      llvm::findVCToolChainViaEnvironment(getVFS(), VCToolChainPath,
+                                          VSLayout) ||
+      llvm::findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath,
+                                          VSLayout) ||
+      llvm::findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
 }
 
 Tool *MSVCToolChain::buildLinker() const {
@@ -835,355 +481,48 @@ void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const {
   RocmInstallation.print(OS);
 }
 
-// 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,
-                                   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;
+MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,
+                                   llvm::StringRef SubdirParent) const {
+  return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(),
+                                   SubdirParent);
 }
-#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
+std::string
+MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type,
+                                   llvm::Triple::ArchType TargetArch) const {
+  return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch,
+                                   "");
 }
 
 // Find the most recent version of Universal CRT or Windows 10 SDK.
 // vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include
 // directory by name and uses the last one of the list.
 // So we compare entry names lexicographically to find the greatest one.
-static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS,
-                                           const std::string &SDKPath,
-                                           std::string &SDKVersion) {
-  llvm::SmallString<128> IncludePath(SDKPath);
-  llvm::sys::path::append(IncludePath, "Include");
-  SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath);
-  return !SDKVersion.empty();
-}
-
-static bool getWindowsSDKDirViaCommandLine(llvm::vfs::FileSystem &VFS,
-                                           const ArgList &Args,
-                                           std::string &Path, int &Major,
-                                           std::string &Version) {
-  if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir,
-                               options::OPT__SLASH_winsysroot)) {
-    // Don't validate the input; trust the value supplied by the user.
-    // The motivation is to prevent unnecessary file and registry access.
-    llvm::VersionTuple SDKVersion;
-    if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion))
-      SDKVersion.tryParse(A->getValue());
-
-    if (A->getOption().getID() == options::OPT__SLASH_winsysroot) {
-      llvm::SmallString<128> SDKPath(A->getValue());
-      llvm::sys::path::append(SDKPath, "Windows Kits");
-      if (!SDKVersion.empty())
-        llvm::sys::path::append(SDKPath, Twine(SDKVersion.getMajor()));
-      else
-        llvm::sys::path::append(
-            SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath));
-      Path = std::string(SDKPath.str());
-    } else {
-      Path = A->getValue();
-    }
-
-    if (!SDKVersion.empty()) {
-      Major = SDKVersion.getMajor();
-      Version = SDKVersion.getAsString();
-    } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) {
-      Major = 10;
-    }
-    return true;
-  }
-  return false;
-}
-
-/// Get Windows SDK installation directory.
-static bool getWindowsSDKDir(llvm::vfs::FileSystem &VFS, const ArgList &Args,
-                             std::string &Path, int &Major,
-                             std::string &WindowsSDKIncludeVersion,
-                             std::string &WindowsSDKLibVersion) {
-  // Trust /winsdkdir and /winsdkversion if present.
-  if (getWindowsSDKDirViaCommandLine(VFS, Args, Path, Major,
-                                     WindowsSDKIncludeVersion)) {
-    WindowsSDKLibVersion = WindowsSDKIncludeVersion;
-    return true;
-  }
-
-  // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to registry.
-
-  // Try the Windows registry.
-  std::string RegistrySDKVersion;
-  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 (VFS.exists(TestPath)) {
-        WindowsSDKLibVersion = Test;
-        break;
-      }
-    }
-    return !WindowsSDKLibVersion.empty();
-  }
-  if (Major == 10) {
-    if (!getWindows10SDKVersionFromPath(VFS, 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(
-    const ArgList &Args, 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(getVFS(), Args, 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 >= 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 !getVFS().exists(TestPath);
-}
-
-static bool getUniversalCRTSdkDir(llvm::vfs::FileSystem &VFS,
-                                  const ArgList &Args, std::string &Path,
-                                  std::string &UCRTVersion) {
-  // If /winsdkdir is passed, use it as location for the UCRT too.
-  // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?
-  int Major;
-  if (getWindowsSDKDirViaCommandLine(VFS, Args, Path, Major, UCRTVersion))
-    return true;
-
-  // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
-  // registry.
-
-  // 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(VFS, Path, UCRTVersion);
+  return llvm::useUniversalCRT(VSLayout, VCToolChainPath, getArch(), getVFS());
 }
 
 bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,
@@ -1192,10 +531,12 @@ bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args,
   std::string UCRTVersion;
 
   Path.clear();
-  if (!getUniversalCRTSdkDir(getVFS(), Args, UniversalCRTSdkPath, UCRTVersion))
+  if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,
+                                   WinSysRoot, UniversalCRTSdkPath,
+                                   UCRTVersion))
     return false;
 
-  StringRef ArchName = llvmArchToWindowsSDKArch(getArch());
+  StringRef ArchName = llvm::archToWindowsSDKArch(getArch());
   if (ArchName.empty())
     return false;
 
@@ -1313,15 +654,17 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
   // 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(getVFS(), DriverArgs, UniversalCRTSdkPath,
-                                UCRTVersion)) {
+      if (llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion,
+                                      WinSysRoot, UniversalCRTSdkPath,
+                                      UCRTVersion)) {
         AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath,
                                       "Include", UCRTVersion, "ucrt");
       }
@@ -1331,8 +674,9 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
     int major = 0;
     std::string windowsSDKIncludeVersion;
     std::string windowsSDKLibVersion;
-    if (getWindowsSDKDir(getVFS(), DriverArgs, WindowsSDKDir, major,
-                         windowsSDKIncludeVersion, windowsSDKLibVersion)) {
+    if (llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot,
+                               WindowsSDKDir, major, windowsSDKIncludeVersion,
+                               windowsSDKLibVersion)) {
       if (major >= 8) {
         // Note: windowsSDKIncludeVersion is empty for SDKs prior to v10.
         // Anyway, llvm::sys::path::append is able to manage it.
@@ -1389,7 +733,8 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
   if (MSVT.empty())
     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)) {

diff  --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index c842773996eda..fa22919dd1e70 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -15,6 +15,7 @@
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Tool.h"
 #include "clang/Driver/ToolChain.h"
+#include "llvm/Support/MSVCPaths.h"
 
 namespace clang {
 namespace driver {
@@ -73,29 +74,15 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
     return 4;
   }
 
-  enum class SubDirectoryType {
-    Bin,
-    Include,
-    Lib,
-  };
-  std::string getSubDirectoryPath(SubDirectoryType Type,
-                                  llvm::StringRef SubdirParent,
+  std::string getSubDirectoryPath(llvm::SubDirectoryType Type,
+                                  llvm::StringRef SubdirParent = "") const;
+  std::string getSubDirectoryPath(llvm::SubDirectoryType Type,
                                   llvm::Triple::ArchType TargetArch) const;
 
-  // Convenience overload.
-  // Uses the current target arch.
-  std::string getSubDirectoryPath(SubDirectoryType Type,
-                                  llvm::StringRef SubdirParent = "") const {
-    return getSubDirectoryPath(Type, SubdirParent, getArch());
+  bool getIsVS2017OrNewer() const {
+    return VSLayout == llvm::ToolsetLayout::VS2017OrNewer;
   }
 
-  enum class ToolsetLayout {
-    OlderVS,
-    VS2017OrNewer,
-    DevDivInternal,
-  };
-  bool getIsVS2017OrNewer() const { return VSLayout == ToolsetLayout::VS2017OrNewer; }
-
   void
   AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
                             llvm::opt::ArgStringList &CC1Args) const override;
@@ -142,8 +129,9 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
   Tool *buildLinker() const override;
   Tool *buildAssembler() const override;
 private:
+  llvm::StringRef WinSdkDir, WinSdkVersion, WinSysRoot;
   std::string VCToolChainPath;
-  ToolsetLayout VSLayout = ToolsetLayout::OlderVS;
+  llvm::ToolsetLayout VSLayout = llvm::ToolsetLayout::OlderVS;
   CudaInstallationDetector CudaInstallation;
   RocmInstallationDetector RocmInstallation;
 };

diff  --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 1546291e16c6b..cbe6979dab76b 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -24,6 +24,7 @@
 #include "lld/Common/Version.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/LTO/LTO.h"
@@ -38,6 +39,7 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/LEB128.h"
+#include "llvm/Support/MSVCPaths.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
@@ -150,6 +152,21 @@ static StringRef mangle(StringRef sym) {
   return sym;
 }
 
+static llvm::Triple::ArchType getArch() {
+  switch (config->machine) {
+  case I386:
+    return llvm::Triple::ArchType::x86;
+  case AMD64:
+    return llvm::Triple::ArchType::x86_64;
+  case ARMNT:
+    return llvm::Triple::ArchType::arm;
+  case ARM64:
+    return llvm::Triple::ArchType::aarch64;
+  default:
+    return llvm::Triple::ArchType::UnknownArch;
+  }
+}
+
 bool LinkerDriver::findUnderscoreMangle(StringRef sym) {
   Symbol *s = ctx.symtab.findMangle(mangle(sym));
   return s && !isa<Undefined>(s);
@@ -504,6 +521,101 @@ Optional<StringRef> LinkerDriver::findLib(StringRef filename) {
   return path;
 }
 
+void LinkerDriver::detectWinSysRoot(const opt::InputArgList &Args) {
+  IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem();
+
+  // 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.
+  StringRef VCToolsDir, VCToolsVersion, WinSysRoot;
+  if (auto *A = Args.getLastArg(OPT_vctoolsdir))
+    VCToolsDir = A->getValue();
+  if (auto *A = Args.getLastArg(OPT_vctoolsversion))
+    VCToolsVersion = A->getValue();
+  if (auto *A = Args.getLastArg(OPT_winsysroot))
+    WinSysRoot = A->getValue();
+  if (!findVCToolChainViaCommandLine(*VFS, VCToolsDir, VCToolsVersion,
+                                     WinSysRoot, vcToolChainPath, vsLayout) &&
+      (Args.hasArg(OPT_lldignoreenv) ||
+       !findVCToolChainViaEnvironment(*VFS, vcToolChainPath, vsLayout)) &&
+      !findVCToolChainViaSetupConfig(*VFS, vcToolChainPath, vsLayout) &&
+      !findVCToolChainViaRegistry(vcToolChainPath, vsLayout))
+    return;
+
+  // 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 auto *A = Args.getLastArg(OPT_diasdkdir, OPT_winsysroot)) {
+    diaPath = A->getValue();
+    if (A->getOption().getID() == OPT_winsysroot)
+      path::append(diaPath, "DIA SDK");
+  }
+  useWinSysRootLibPath = Args.hasArg(OPT_lldignoreenv) ||
+                         !Process::GetEnv("LIB") ||
+                         Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot);
+  if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") ||
+      Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) {
+    StringRef WinSdkDir, WinSdkVersion;
+    if (auto *A = Args.getLastArg(OPT_winsdkdir))
+      WinSdkDir = A->getValue();
+    if (auto *A = Args.getLastArg(OPT_winsdkversion))
+      WinSdkVersion = A->getValue();
+
+    if (useUniversalCRT(vsLayout, vcToolChainPath, getArch(), *VFS)) {
+      std::string UniversalCRTSdkPath;
+      std::string UCRTVersion;
+      if (getUniversalCRTSdkDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
+                                UniversalCRTSdkPath, UCRTVersion)) {
+        universalCRTLibPath = UniversalCRTSdkPath;
+        path::append(universalCRTLibPath, "Lib", UCRTVersion, "ucrt");
+      }
+    }
+
+    std::string sdkPath;
+    std::string windowsSDKIncludeVersion;
+    std::string windowsSDKLibVersion;
+    if (getWindowsSDKDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, sdkPath,
+                         sdkMajor, windowsSDKIncludeVersion,
+                         windowsSDKLibVersion)) {
+      windowsSdkLibPath = sdkPath;
+      path::append(windowsSdkLibPath, "Lib");
+      if (sdkMajor >= 8)
+        path::append(windowsSdkLibPath, windowsSDKLibVersion, "um");
+    }
+  }
+}
+
+void LinkerDriver::addWinSysRootLibSearchPaths() {
+  if (!diaPath.empty()) {
+    // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
+    path::append(diaPath, "lib", archToLegacyVCArch(getArch()));
+    searchPaths.push_back(saver().save(diaPath.str()));
+  }
+  if (useWinSysRootLibPath) {
+    searchPaths.push_back(saver().save(getSubDirectoryPath(
+        SubDirectoryType::Lib, vsLayout, vcToolChainPath, getArch())));
+    searchPaths.push_back(saver().save(
+        getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, vcToolChainPath,
+                            getArch(), "atlmfc")));
+  }
+  if (!universalCRTLibPath.empty()) {
+    StringRef ArchName = archToWindowsSDKArch(getArch());
+    if (!ArchName.empty()) {
+      path::append(universalCRTLibPath, ArchName);
+      searchPaths.push_back(saver().save(universalCRTLibPath.str()));
+    }
+  }
+  if (!windowsSdkLibPath.empty()) {
+    std::string path;
+    if (appendArchToWindowsSDKLibPath(sdkMajor, windowsSdkLibPath, getArch(),
+                                      path))
+      searchPaths.push_back(saver().save(path));
+  }
+}
+
 // Parses LIB environment which contains a list of search paths.
 void LinkerDriver::addLibSearchPaths() {
   Optional<std::string> envOpt = Process::GetEnv("LIB");
@@ -626,6 +738,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
     case OPT_INPUT:
     case OPT_defaultlib:
     case OPT_libpath:
+    case OPT_winsysroot:
       break;
     case OPT_call_graph_ordering_file:
     case OPT_deffile:
@@ -1335,7 +1448,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   searchPaths.push_back("");
   for (auto *arg : args.filtered(OPT_libpath))
     searchPaths.push_back(arg->getValue());
-  if (!args.hasArg(OPT_lldignoreenv))
+  detectWinSysRoot(args);
+  if (!args.hasArg(OPT_lldignoreenv) && !args.hasArg(OPT_winsysroot))
     addLibSearchPaths();
 
   // Handle /ignore
@@ -1475,6 +1589,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->machine = getMachineType(arg->getValue());
     if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN)
       fatal(Twine("unknown /machine argument: ") + arg->getValue());
+    addWinSysRootLibSearchPaths();
   }
 
   // Handle /nodefaultlib:<filename>
@@ -1836,15 +1951,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  // Process files specified as /defaultlib. These should be enequeued after
-  // other files, which is why they are in a separate loop.
-  for (auto *arg : args.filtered(OPT_defaultlib))
-    if (Optional<StringRef> path = findLib(arg->getValue()))
-      enqueuePath(*path, false, false);
-
   // Read all input files given via the command line.
   run();
-
   if (errorCount())
     return;
 
@@ -1853,9 +1961,19 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
     warn("/machine is not specified. x64 is assumed");
     config->machine = AMD64;
+    addWinSysRootLibSearchPaths();
   }
   config->wordsize = config->is64() ? 8 : 4;
 
+  // Process files specified as /defaultlib. These must be processed after
+  // addWinSysRootLibSearchPaths(), which is why they are in a separate loop.
+  for (auto *arg : args.filtered(OPT_defaultlib))
+    if (Optional<StringRef> path = findLib(arg->getValue()))
+      enqueuePath(*path, false, false);
+  run();
+  if (errorCount())
+    return;
+
   // Handle /safeseh, x86 only, on by default, except for mingw.
   if (config->machine == I386) {
     config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
@@ -1866,10 +1984,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
     parseFunctionPadMin(arg, config->machine);
 
-  if (tar)
+  if (tar) {
     tar->append("response.txt",
                 createResponseFile(args, filePaths,
                                    ArrayRef<StringRef>(searchPaths).slice(1)));
+  }
 
   // Handle /largeaddressaware
   config->largeAddressAware = args.hasFlag(

diff  --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 518ec1470677d..17c0c72281985 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -22,6 +22,7 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MSVCPaths.h"
 #include "llvm/Support/TarWriter.h"
 #include <memory>
 #include <set>
@@ -82,6 +83,10 @@ class LinkerDriver {
 
   void linkerMain(llvm::ArrayRef<const char *> args);
 
+  // Adds various search paths based on the sysroot.  Must only be called once
+  // config->machine has been set.
+  void addWinSysRootLibSearchPaths();
+
   // Used by the resolver to parse .drectve section contents.
   void parseDirectives(InputFile *file);
 
@@ -107,6 +112,9 @@ class LinkerDriver {
 
   bool findUnderscoreMangle(StringRef sym);
 
+  // Determines the location of the sysroot based on `args`, environment, etc.
+  void detectWinSysRoot(const llvm::opt::InputArgList &args);
+
   // Parses LIB environment which contains a list of search paths.
   void addLibSearchPaths();
 
@@ -154,6 +162,14 @@ class LinkerDriver {
   llvm::StringSet<> directivesExports;
 
   COFFLinkerContext &ctx;
+
+  llvm::ToolsetLayout vsLayout = llvm::ToolsetLayout::OlderVS;
+  std::string vcToolChainPath;
+  llvm::SmallString<128> diaPath;
+  bool useWinSysRootLibPath = false;
+  llvm::SmallString<128> universalCRTLibPath;
+  int sdkMajor = 0;
+  llvm::SmallString<128> windowsSdkLibPath;
 };
 
 // Functions below this line are defined in DriverUtils.cpp.

diff  --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 7189088f8be6f..7593b99dc05a7 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -41,6 +41,7 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
     MetaVarName<"[auto,always,never]">;
 def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
 def delayload : P<"delayload", "Delay loaded DLL name">;
+def diasdkdir : P<"diasdkdir", "Set the location of the DIA SDK">;
 def entry   : P<"entry", "Name of entry point symbol">;
 def errorlimit : P<"errorlimit",
     "Maximum number of errors to emit before stopping (0 = no limit)">;
@@ -89,9 +90,16 @@ def stack   : P<"stack", "Size of the stack">;
 def stub    : P<"stub", "Specify DOS stub file">;
 def subsystem : P<"subsystem", "Specify subsystem">;
 def timestamp : P<"timestamp", "Specify the PE header timestamp">;
+def vctoolsdir : P<"vctoolsdir", "Set the location of the VC tools">;
+def vctoolsversion : P<"vctoolsversion",
+    "Specify which VC tools version to use">;
 def version : P<"version", "Specify a version number in the PE header">;
 def wholearchive_file : P<"wholearchive",
     "Include all object files from this library">;
+def winsdkdir : P<"winsdkdir", "Set the location of the Windows SDK">;
+def winsdkversion : P<"winsdkversion", "Specify which SDK version to use">;
+def winsysroot : P<"winsysroot",
+    "Adds several subdirectories to the library search paths">;
 
 def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
     Alias<nodefaultlib>;

diff  --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index db2db9c9272eb..9bd93a2ff1a7d 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -56,6 +56,7 @@ void SymbolTable::addFile(InputFile *file) {
   MachineTypes mt = file->getMachineType();
   if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
     config->machine = mt;
+    driver->addWinSysRootLibSearchPaths();
   } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
     error(toString(file) + ": machine type " + machineToStr(mt) +
           " conflicts with " + machineToStr(config->machine));

diff  --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 1a31c13eb8905..af09e1e539b87 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -36,6 +36,9 @@ Breaking changes
 COFF Improvements
 -----------------
 
+* Added autodetection of MSVC toolchain, a la clang-cl.  Also added /winsysroot
+  support for explicit specification of MSVC toolchain location.
+  (`D118070 <https://reviews.llvm.org/D118070>`_)
 * ...
 
 MinGW Improvements

diff  --git a/lld/test/COFF/winsysroot.test b/lld/test/COFF/winsysroot.test
new file mode 100644
index 0000000000000..5abcbce3548e9
--- /dev/null
+++ b/lld/test/COFF/winsysroot.test
@@ -0,0 +1,17 @@
+Check that /winsysroot results in the correct machine-specific subdirectory
+being searched for the defaultlibs, for a 32-bit .obj.
+# RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj
+# RUN: mkdir -p %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86
+# RUN: mkdir -p %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x64
+# RUN: cp %p/Inputs/std32.lib %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86
+# RUN: cp %p/Inputs/std64.lib %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x64
+# RUN: not lld-link %t.obj /winsysroot:%t.dir/sysroot /defaultlib:std32 2>&1 | FileCheck -check-prefix=MISSINGENTRY32 --implicit-check-not=_ExitProcess %s
+MISSINGENTRY32: undefined symbol: _mainCRTStartup
+
+Check the same for a 64-bit input .obj.
+# RUN: not lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot /defaultlib:std64 2>&1 | FileCheck -check-prefix=MISSINGENTRY64 --implicit-check-not=_ExitProcess %s
+MISSINGENTRY64: undefined symbol: mainCRTStartup
+
+Check that when /winsysroot is specified, %LIB% is ignored.
+# RUN: env LIB=foo.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86 not lld-link %t.obj /winsysroot:%t.dir/doesnotexist /defaultlib:std32 2>&1 | FileCheck -check-prefix=LIBIGNORED %s
+LIBIGNORED: could not open 'std32.lib'

diff  --git a/llvm/include/llvm/Support/MSVCPaths.h b/llvm/include/llvm/Support/MSVCPaths.h
new file mode 100644
index 0000000000000..cadc872654b9e
--- /dev/null
+++ b/llvm/include/llvm/Support/MSVCPaths.h
@@ -0,0 +1,99 @@
+//===-- MSVCPaths.h - MSVC path-parsing helpers -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_MSVCPATHS_H
+#define LLVM_SUPPORT_MSVCPATHS_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include <string>
+
+namespace llvm {
+
+enum class SubDirectoryType {
+  Bin,
+  Include,
+  Lib,
+};
+
+enum class ToolsetLayout {
+  OlderVS,
+  VS2017OrNewer,
+  DevDivInternal,
+};
+
+// 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.
+const char *archToWindowsSDKArch(llvm::Triple::ArchType Arch);
+
+// Similar to the above function, but for Visual Studios before VS2017.
+const char *archToLegacyVCArch(llvm::Triple::ArchType Arch);
+
+// Similar to the above function, but for DevDiv internal builds.
+const char *archToDevDivInternalArch(llvm::Triple::ArchType Arch);
+
+bool appendArchToWindowsSDKLibPath(int SDKMajor, llvm::SmallString<128> LibPath,
+                                   llvm::Triple::ArchType Arch,
+                                   std::string &path);
+
+// 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 getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout,
+                                const std::string &VCToolChainPath,
+                                llvm::Triple::ArchType TargetArch,
+                                llvm::StringRef SubdirParent = "");
+
+// 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 useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath,
+                     llvm::Triple::ArchType TargetArch,
+                     llvm::vfs::FileSystem &VFS);
+
+/// Get Windows SDK installation directory.
+bool getWindowsSDKDir(vfs::FileSystem &VFS, llvm::StringRef WinSdkDir,
+                      llvm::StringRef WinSdkVersion, llvm::StringRef WinSysRoot,
+                      std::string &Path, int &Major,
+                      std::string &WindowsSDKIncludeVersion,
+                      std::string &WindowsSDKLibVersion);
+
+bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, llvm::StringRef WinSdkDir,
+                           llvm::StringRef WinSdkVersion,
+                           llvm::StringRef WinSysRoot, std::string &Path,
+                           std::string &UCRTVersion);
+
+// Check command line arguments to try and find a toolchain.
+bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS,
+                                   llvm::StringRef VCToolsDir,
+                                   llvm::StringRef VCToolsVersion,
+                                   llvm::StringRef WinSysRoot,
+                                   std::string &Path, ToolsetLayout &VSLayout);
+
+// Check various environment variables to try and find a toolchain.
+bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path,
+                                   ToolsetLayout &VSLayout);
+
+// 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.
+bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, std::string &Path,
+                                   ToolsetLayout &VSLayout);
+
+// 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.
+bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout);
+
+} // namespace llvm
+
+#endif

diff  --git a/clang/lib/Driver/ToolChains/MSVCSetupApi.h b/llvm/include/llvm/Support/MSVCSetupApi.h
similarity index 100%
rename from clang/lib/Driver/ToolChains/MSVCSetupApi.h
rename to llvm/include/llvm/Support/MSVCSetupApi.h

diff  --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 4d9099554192d..d7fc4d2fac1fc 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -175,6 +175,7 @@ add_llvm_component_library(LLVMSupport
   MD5.cpp
   MSP430Attributes.cpp
   MSP430AttributeParser.cpp
+  MSVCPaths.cpp
   NativeFormatting.cpp
   OptimizedStructLayout.cpp
   Optional.cpp

diff  --git a/llvm/lib/Support/MSVCPaths.cpp b/llvm/lib/Support/MSVCPaths.cpp
new file mode 100644
index 0000000000000..b0a46ca3bc7b4
--- /dev/null
+++ b/llvm/lib/Support/MSVCPaths.cpp
@@ -0,0 +1,709 @@
+//===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/MSVCPaths.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/VersionTuple.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include <string>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#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 "llvm/Support/COM.h"
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include "llvm/Support/MSVCSetupApi.h"
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+_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
+
+static std::string
+getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS,
+                                  llvm::StringRef Directory) {
+  std::string Highest;
+  llvm::VersionTuple HighestTuple;
+
+  std::error_code EC;
+  for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC),
+                                     DirEnd;
+       !EC && DirIt != DirEnd; DirIt.increment(EC)) {
+    auto Status = VFS.status(DirIt->path());
+    if (!Status || !Status->isDirectory())
+      continue;
+    llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
+    llvm::VersionTuple Tuple;
+    if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.
+      continue;
+    if (Tuple > HighestTuple) {
+      HighestTuple = Tuple;
+      Highest = CandidateName.str();
+    }
+  }
+
+  return Highest;
+}
+
+static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS,
+                                           const std::string &SDKPath,
+                                           std::string &SDKVersion) {
+  llvm::SmallString<128> IncludePath(SDKPath);
+  llvm::sys::path::append(IncludePath, "Include");
+  SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath);
+  return !SDKVersion.empty();
+}
+
+static bool getWindowsSDKDirViaCommandLine(llvm::vfs::FileSystem &VFS,
+                                           llvm::StringRef WinSdkDir,
+                                           llvm::StringRef WinSdkVersion,
+                                           llvm::StringRef WinSysRoot,
+                                           std::string &Path, int &Major,
+                                           std::string &Version) {
+  if (!WinSdkDir.empty() || !WinSysRoot.empty()) {
+    // Don't validate the input; trust the value supplied by the user.
+    // The motivation is to prevent unnecessary file and registry access.
+    llvm::VersionTuple SDKVersion;
+    if (!WinSdkVersion.empty())
+      SDKVersion.tryParse(WinSdkVersion);
+
+    if (!WinSysRoot.empty()) {
+      llvm::SmallString<128> SDKPath(WinSysRoot);
+      llvm::sys::path::append(SDKPath, "Windows Kits");
+      if (!SDKVersion.empty())
+        llvm::sys::path::append(SDKPath, llvm::Twine(SDKVersion.getMajor()));
+      else
+        llvm::sys::path::append(
+            SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath));
+      Path = std::string(SDKPath.str());
+    } else {
+      Path = WinSdkDir.str();
+    }
+
+    if (!SDKVersion.empty()) {
+      Major = SDKVersion.getMajor();
+      Version = SDKVersion.getAsString();
+    } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) {
+      Major = 10;
+    }
+    return true;
+  }
+  return false;
+}
+
+#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 && !llvm::isDigit(*sp))
+          sp++;
+        if (!*sp)
+          continue;
+        const char *ep = sp + 1;
+        while (*ep && (llvm::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
+}
+
+namespace llvm {
+
+const char *archToWindowsSDKArch(Triple::ArchType Arch) {
+  switch (Arch) {
+  case Triple::ArchType::x86:
+    return "x86";
+  case Triple::ArchType::x86_64:
+    return "x64";
+  case Triple::ArchType::arm:
+    return "arm";
+  case Triple::ArchType::aarch64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+const char *archToLegacyVCArch(Triple::ArchType Arch) {
+  switch (Arch) {
+  case Triple::ArchType::x86:
+    // x86 is default in legacy VC toolchains.
+    // e.g. x86 libs are directly in /lib as opposed to /lib/x86.
+    return "";
+  case Triple::ArchType::x86_64:
+    return "amd64";
+  case Triple::ArchType::arm:
+    return "arm";
+  case Triple::ArchType::aarch64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+const char *archToDevDivInternalArch(Triple::ArchType Arch) {
+  switch (Arch) {
+  case Triple::ArchType::x86:
+    return "i386";
+  case Triple::ArchType::x86_64:
+    return "amd64";
+  case Triple::ArchType::arm:
+    return "arm";
+  case Triple::ArchType::aarch64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath,
+                                   Triple::ArchType Arch, std::string &path) {
+  if (SDKMajor >= 8) {
+    sys::path::append(LibPath, archToWindowsSDKArch(Arch));
+  } else {
+    switch (Arch) {
+    // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
+    case Triple::x86:
+      break;
+    case Triple::x86_64:
+      sys::path::append(LibPath, "x64");
+      break;
+    case 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;
+}
+
+std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout,
+                                const std::string &VCToolChainPath,
+                                Triple::ArchType TargetArch,
+                                StringRef SubdirParent) {
+  const char *SubdirName;
+  const char *IncludeName;
+  switch (VSLayout) {
+  case ToolsetLayout::OlderVS:
+    SubdirName = archToLegacyVCArch(TargetArch);
+    IncludeName = "include";
+    break;
+  case ToolsetLayout::VS2017OrNewer:
+    SubdirName = archToWindowsSDKArch(TargetArch);
+    IncludeName = "include";
+    break;
+  case ToolsetLayout::DevDivInternal:
+    SubdirName = archToDevDivInternalArch(TargetArch);
+    IncludeName = "inc";
+    break;
+  }
+
+  SmallString<256> Path(VCToolChainPath);
+  if (!SubdirParent.empty())
+    sys::path::append(Path, SubdirParent);
+
+  switch (Type) {
+  case SubDirectoryType::Bin:
+    if (VSLayout == ToolsetLayout::VS2017OrNewer) {
+      const bool HostIsX64 = Triple(sys::getProcessTriple()).isArch64Bit();
+      const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86";
+      sys::path::append(Path, "bin", HostName, SubdirName);
+    } else { // OlderVS or DevDivInternal
+      sys::path::append(Path, "bin", SubdirName);
+    }
+    break;
+  case SubDirectoryType::Include:
+    sys::path::append(Path, IncludeName);
+    break;
+  case SubDirectoryType::Lib:
+    sys::path::append(Path, "lib", SubdirName);
+    break;
+  }
+  return std::string(Path.str());
+}
+
+bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath,
+                     Triple::ArchType TargetArch, vfs::FileSystem &VFS) {
+  SmallString<128> TestPath(getSubDirectoryPath(
+      SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch));
+  sys::path::append(TestPath, "stdlib.h");
+  return !VFS.exists(TestPath);
+}
+
+bool getWindowsSDKDir(vfs::FileSystem &VFS, StringRef WinSdkDir,
+                      StringRef WinSdkVersion, StringRef WinSysRoot,
+                      std::string &Path, int &Major,
+                      std::string &WindowsSDKIncludeVersion,
+                      std::string &WindowsSDKLibVersion) {
+  // Trust /winsdkdir and /winsdkversion if present.
+  if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
+                                     Path, Major, WindowsSDKIncludeVersion)) {
+    WindowsSDKLibVersion = WindowsSDKIncludeVersion;
+    return true;
+  }
+
+  // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to
+  // registry.
+
+  // Try the Windows registry.
+  std::string RegistrySDKVersion;
+  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) {
+      SmallString<128> TestPath(Path);
+      sys::path::append(TestPath, "Lib", Test);
+      if (VFS.exists(TestPath)) {
+        WindowsSDKLibVersion = Test;
+        break;
+      }
+    }
+    return !WindowsSDKLibVersion.empty();
+  }
+  if (Major == 10) {
+    if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion))
+      return false;
+    WindowsSDKLibVersion = WindowsSDKIncludeVersion;
+    return true;
+  }
+  // Unsupported SDK version
+  return false;
+}
+
+bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, StringRef WinSdkDir,
+                           StringRef WinSdkVersion, StringRef WinSysRoot,
+                           std::string &Path, std::string &UCRTVersion) {
+  // If /winsdkdir is passed, use it as location for the UCRT too.
+  // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?
+  int Major;
+  if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
+                                     Path, Major, UCRTVersion))
+    return true;
+
+  // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
+  // registry.
+
+  // 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(VFS, Path, UCRTVersion);
+}
+
+bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, StringRef VCToolsDir,
+                                   StringRef VCToolsVersion,
+                                   StringRef WinSysRoot, std::string &Path,
+                                   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 (!VCToolsDir.empty() || !WinSysRoot.empty()) {
+    if (!WinSysRoot.empty()) {
+      SmallString<128> ToolsPath(WinSysRoot);
+      sys::path::append(ToolsPath, "VC", "Tools", "MSVC");
+      std::string ToolsVersion;
+      if (!VCToolsVersion.empty())
+        ToolsVersion = VCToolsVersion.str();
+      else
+        ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath);
+      sys::path::append(ToolsPath, ToolsVersion);
+      Path = std::string(ToolsPath.str());
+    } else {
+      Path = VCToolsDir.str();
+    }
+    VSLayout = ToolsetLayout::VS2017OrNewer;
+    return true;
+  }
+  return false;
+}
+
+bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path,
+                                   ToolsetLayout &VSLayout) {
+  // These variables are typically set by vcvarsall.bat
+  // when launching a developer command prompt.
+  if (Optional<std::string> VCToolsInstallDir =
+          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 = ToolsetLayout::VS2017OrNewer;
+    return true;
+  }
+  if (Optional<std::string> VCInstallDir =
+          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 = 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 (Optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) {
+    SmallVector<StringRef, 8> PathEntries;
+    StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator);
+    for (StringRef PathEntry : PathEntries) {
+      if (PathEntry.empty())
+        continue;
+
+      SmallString<256> ExeTestPath;
+
+      // If cl.exe doesn't exist, then this definitely isn't a VC toolchain.
+      ExeTestPath = PathEntry;
+      sys::path::append(ExeTestPath, "cl.exe");
+      if (!VFS.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;
+      sys::path::append(ExeTestPath, "link.exe");
+      if (!VFS.exists(ExeTestPath))
+        continue;
+
+      // whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
+      StringRef TestPath = PathEntry;
+      bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin");
+      if (!IsBin) {
+        // Strip any architecture subdir like "amd64".
+        TestPath = sys::path::parent_path(TestPath);
+        IsBin = sys::path::filename(TestPath).equals_insensitive("bin");
+      }
+      if (IsBin) {
+        StringRef ParentPath = sys::path::parent_path(TestPath);
+        StringRef ParentFilename = sys::path::filename(ParentPath);
+        if (ParentFilename.equals_insensitive("VC")) {
+          Path = std::string(ParentPath);
+          VSLayout = ToolsetLayout::OlderVS;
+          return true;
+        }
+        if (ParentFilename.equals_insensitive("x86ret") ||
+            ParentFilename.equals_insensitive("x86chk") ||
+            ParentFilename.equals_insensitive("amd64ret") ||
+            ParentFilename.equals_insensitive("amd64chk")) {
+          Path = std::string(ParentPath);
+          VSLayout = 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.
+        StringRef ExpectedPrefixes[] = {"",     "Host",  "bin", "",
+                                        "MSVC", "Tools", "VC"};
+
+        auto It = sys::path::rbegin(PathEntry);
+        auto End = sys::path::rend(PathEntry);
+        for (StringRef Prefix : ExpectedPrefixes) {
+          if (It == End)
+            goto NotAToolChain;
+          if (!It->startswith_insensitive(Prefix))
+            goto NotAToolChain;
+          ++It;
+        }
+
+        // We've found a new toolchain!
+        // Back up 3 times (/bin/Host/arch) to get the root path.
+        StringRef ToolChainPath(PathEntry);
+        for (int i = 0; i < 3; ++i)
+          ToolChainPath = sys::path::parent_path(ToolChainPath);
+
+        Path = std::string(ToolChainPath);
+        VSLayout = ToolsetLayout::VS2017OrNewer;
+        return true;
+      }
+
+    NotAToolChain:
+      continue;
+    }
+  }
+  return false;
+}
+
+bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, std::string &Path,
+                                   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 
diff erent
+  // threading model otherwise.
+  sys::InitializeCOMRAII COM(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)
+    return false;
+
+  bstr_t VCPathWide;
+  HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress());
+  if (FAILED(HR))
+    return false;
+
+  std::string VCRootPath;
+  convertWideToUTF8(std::wstring(VCPathWide), VCRootPath);
+
+  SmallString<256> ToolsVersionFilePath(VCRootPath);
+  sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build",
+                    "Microsoft.VCToolsVersion.default.txt");
+
+  auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath);
+  if (!ToolsVersionFile)
+    return false;
+
+  SmallString<256> ToolchainPath(VCRootPath);
+  sys::path::append(ToolchainPath, "Tools", "MSVC",
+                    ToolsVersionFile->get()->getBuffer().rtrim());
+  auto Status = VFS.status(ToolchainPath);
+  if (!Status || !Status->isDirectory())
+    return false;
+
+  Path = std::string(ToolchainPath.str());
+  VSLayout = ToolsetLayout::VS2017OrNewer;
+  return true;
+#endif
+}
+
+bool findVCToolChainViaRegistry(std::string &Path, 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()) {
+      SmallString<256> VCPath(StringRef(VSInstallPath.c_str(),
+                                        VSInstallPath.find(R"(\Common7\IDE)")));
+      sys::path::append(VCPath, "VC");
+
+      Path = std::string(VCPath.str());
+      VSLayout = ToolsetLayout::OlderVS;
+      return true;
+    }
+  }
+  return false;
+}
+
+} // namespace llvm

diff  --git a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
index 461b7498978f4..254d464ef022c 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
@@ -98,6 +98,7 @@ static_library("Support") {
     "MD5.cpp",
     "MSP430AttributeParser.cpp",
     "MSP430Attributes.cpp",
+    "MSVCPaths.cpp",
     "ManagedStatic.cpp",
     "MathExtras.cpp",
     "MemAlloc.cpp",

diff  --git a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
index 43462d77d00ed..7fe7cb8553540 100644
--- a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
@@ -1310,9 +1310,6 @@ cc_library(
             "lib/Driver/ToolChains/Arch/*.cpp",
             "lib/Driver/ToolChains/Arch/*.h",
         ],
-        exclude = [
-            "lib/Driver/ToolChains/MSVCSetupApi.h",
-        ],
     ),
     hdrs = glob([
         "include/clang/Driver/*.h",


        


More information about the cfe-commits mailing list