Index: COFF/Config.h =================================================================== --- COFF/Config.h +++ COFF/Config.h @@ -144,6 +144,10 @@ // Used for /lldmap. std::string MapFile; + bool MSVCLTO = false; + // Object files that will be passed to MSVC's linker before any archives. + std::vector MSVCLTOEarlyObjects; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -153,6 +153,18 @@ InputFile *Obj; if (Magic == file_magic::coff_object) { Obj = make(MB); + if (Config->MSVCLTO) { + // Write the member to a temporary file so we can link it early + // to force MSVC symbol resolution to match ours. + SmallString<128> S; + int Fd; + if (auto EC = sys::fs::createTemporaryFile( + "lld-" + sys::path::filename(ParentName), ".obj", Fd, S)) + fatal(EC, "cannot create a temporary file"); + raw_fd_ostream OS(Fd, /*shouldClose*/ true); + OS << MB.getBuffer(); + Config->MSVCLTOEarlyObjects.push_back(S.str()); + } } else if (Magic == file_magic::bitcode) { Obj = make(MB); } else { @@ -512,6 +524,9 @@ std::string Rsp = "/nologo\n"; std::vector Temps; + for (auto &Path : Config->MSVCLTOEarlyObjects) + Rsp += quote(Path) + "\n"; + for (auto *Arg : Args) { switch (Arg->getOption().getID()) { case OPT_linkrepro: @@ -601,6 +616,10 @@ return; } + // Handle /msvclto + if (Args.hasArg(OPT_msvclto)) + Config->MSVCLTO = true; + if (auto *Arg = Args.getLastArg(OPT_linkrepro)) { SmallString<64> Path = StringRef(Arg->getValue()); sys::path::append(Path, "repro.tar"); @@ -987,7 +1006,7 @@ // If /msvclto is given, we use the MSVC linker to link LTO output files. // This is useful because MSVC link.exe can generate complete PDBs. - if (Args.hasArg(OPT_msvclto)) { + if (Config->MSVCLTO) { invokeMSVC(Args); exit(0); } Index: test/COFF/Inputs/msvclto-order-a.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/msvclto-order-a.ll @@ -0,0 +1,7 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @foo() { + ret void +} + Index: test/COFF/Inputs/msvclto-order-b.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/msvclto-order-b.ll @@ -0,0 +1,10 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare void @doesntexist() + +define void @foo() { + call void @doesntexist() + ret void +} + Index: test/COFF/msvclto-order.ll =================================================================== --- /dev/null +++ test/COFF/msvclto-order.ll @@ -0,0 +1,23 @@ +; RUN: opt -thinlto-bc %s -o %t.obj +; RUN: llc -filetype=obj %S/Inputs/msvclto-order-a.ll -o %T/msvclto-order-a.obj +; RUN: llvm-ar crs %T/msvclto-order-a.lib %T/msvclto-order-a.obj +; RUN: llc -filetype=obj %S/Inputs/msvclto-order-b.ll -o %T/msvclto-order-b.obj +; RUN: llvm-ar crs %T/msvclto-order-b.lib %T/msvclto-order-b.obj +; RUN: lld-link /verbose /msvclto /out:%t.exe /entry:main %t.obj \ +; RUN: %T/msvclto-order-a.lib %T/msvclto-order-b.lib > %t.log || true +; RUN: FileCheck %s < %t.log + +; CHECK: : link.exe +; CHECK-NOT: .lib{{$}} +; CHECK: lld-msvclto-order-a{{.*}}.obj +; CHECK: .lib{{$}} + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare void @foo() + +define i32 @main() { + call void @foo() + ret i32 0 +}