Index: lld/trunk/COFF/Driver.h =================================================================== --- lld/trunk/COFF/Driver.h +++ lld/trunk/COFF/Driver.h @@ -178,6 +178,8 @@ std::unique_ptr convertResToCOFF(const std::vector &MBs); +void runMSVCLinker(llvm::opt::InputArgList &Args, ArrayRef Objects); + // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -814,6 +814,14 @@ addUndefined(mangle("_load_config_used")); } while (run()); + // 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)) { + std::vector ObjectFiles = Symtab.compileBitcodeFiles(); + runMSVCLinker(Args, ObjectFiles); + exit(0); + } + // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab.addCombinedLTOObjects(); Index: lld/trunk/COFF/DriverUtils.cpp =================================================================== --- lld/trunk/COFF/DriverUtils.cpp +++ lld/trunk/COFF/DriverUtils.cpp @@ -50,10 +50,18 @@ void add(const char *S) { Args.push_back(Saver.save(S).data()); } void run() { + if (Config->Verbose) { + outs() << Prog; + for (const char *Arg : Args) + outs() << " " << Arg; + outs() << '\n'; + } + ErrorOr ExeOrErr = sys::findProgramByName(Prog); if (auto EC = ExeOrErr.getError()) fatal(EC, "unable to find " + Prog + " in PATH: "); const char *Exe = Saver.save(*ExeOrErr).data(); + Args.insert(Args.begin(), Exe); Args.push_back(nullptr); if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) { @@ -282,11 +290,19 @@ namespace { class TemporaryFile { public: - TemporaryFile(StringRef Prefix, StringRef Extn) { + TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { SmallString<128> S; if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) fatal(EC, "cannot create a temporary file"); Path = S.str(); + + if (!Contents.empty()) { + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + fatal(EC, "failed to open " + Path); + OS << Contents; + } } TemporaryFile(TemporaryFile &&Obj) { @@ -617,6 +633,60 @@ return File.getMemoryBuffer(); } +static bool isBitcodeFile(StringRef Path) { + std::unique_ptr MB = check( + MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + StringRef Buf = MB->getBuffer(); + return sys::fs::identify_magic(Buf) == sys::fs::file_magic::bitcode; +} + +// Run MSVC link.exe for given in-memory object files. +// Command line options are copied from those given to LLD. +// This is for the /msvclto option. +void runMSVCLinker(opt::InputArgList &Args, ArrayRef Objects) { + // Write the in-memory object files to disk. + std::vector Temps; + for (StringRef S : Objects) + Temps.emplace_back("lto", "obj", S); + + // Create a response file. + std::string Rsp = "/nologo "; + for (TemporaryFile &T : Temps) + Rsp += quote(T.Path) + " "; + + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_linkrepro: + case OPT_lldmap: + case OPT_lldmap_file: + case OPT_msvclto: + // LLD-specific options are stripped. + break; + case OPT_opt: + if (!StringRef(Arg->getValue()).startswith("lld")) + Rsp += toString(Arg) + " "; + break; + case OPT_INPUT: + // Bitcode files are stripped as they've been compiled to + // native object files. + if (!isBitcodeFile(Arg->getValue())) + Rsp += quote(Arg->getValue()) + " "; + break; + default: + Rsp += toString(Arg) + " "; + } + } + + if (Config->Verbose) + outs() << "link.exe " << Rsp << "\n"; + + // Run MSVC link.exe. + Temps.emplace_back("lto", "rsp", Rsp); + Executor E("link.exe"); + E.add(Twine("@" + Temps.back().Path)); + E.run(); +} + // Create OptTable // Create prefix string literals used in Options.td Index: lld/trunk/COFF/LTO.h =================================================================== --- lld/trunk/COFF/LTO.h +++ lld/trunk/COFF/LTO.h @@ -44,7 +44,7 @@ ~BitcodeCompiler(); void add(BitcodeFile &F); - std::vector compile(); + std::vector compile(); private: std::unique_ptr LTOObj; Index: lld/trunk/COFF/LTO.cpp =================================================================== --- lld/trunk/COFF/LTO.cpp +++ lld/trunk/COFF/LTO.cpp @@ -105,9 +105,8 @@ } // Merge all the bitcode files we have seen, codegen the result -// and return the resulting ObjectFile(s). -std::vector BitcodeCompiler::compile() { - std::vector Ret; +// and return the resulting objects. +std::vector BitcodeCompiler::compile() { unsigned MaxTasks = LTOObj->getMaxTasks(); Buff.resize(MaxTasks); @@ -116,10 +115,9 @@ llvm::make_unique(Buff[Task])); })); - for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buff[I].empty()) - continue; - Ret.push_back(make(MemoryBufferRef(Buff[I], "lto.tmp"))); - } + std::vector Ret; + for (unsigned I = 0; I != MaxTasks; ++I) + if (!Buff[I].empty()) + Ret.emplace_back(Buff[I].data(), Buff[I].size()); return Ret; } Index: lld/trunk/COFF/Options.td =================================================================== --- lld/trunk/COFF/Options.td +++ lld/trunk/COFF/Options.td @@ -92,6 +92,7 @@ // LLD extensions def nosymtab : F<"nosymtab">; +def msvclto : F<"msvclto">; // Flags for debugging def debugpdb : F<"debugpdb">; Index: lld/trunk/COFF/SymbolTable.h =================================================================== --- lld/trunk/COFF/SymbolTable.h +++ lld/trunk/COFF/SymbolTable.h @@ -74,6 +74,7 @@ // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); + std::vector compileBitcodeFiles(); // The writer needs to handle DLL import libraries specially in // order to create the import descriptor table. Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -351,19 +351,22 @@ return addUndefined(Name, nullptr, false)->body(); } -void SymbolTable::addCombinedLTOObjects() { - if (BitcodeFiles.empty()) - return; - +std::vector SymbolTable::compileBitcodeFiles() { LTO.reset(new BitcodeCompiler); for (BitcodeFile *F : BitcodeFiles) LTO->add(*F); + return LTO->compile(); +} - for (auto *File : LTO->compile()) { - auto *Obj = cast(File); - ObjectFiles.push_back(Obj); +void SymbolTable::addCombinedLTOObjects() { + if (BitcodeFiles.empty()) + return; + for (StringRef Object : compileBitcodeFiles()) { + auto *Obj = make(MemoryBufferRef(Object, "lto.tmp")); Obj->parse(); + ObjectFiles.push_back(Obj); } } + } // namespace coff } // namespace lld Index: lld/trunk/test/COFF/Inputs/msvclto.s =================================================================== --- lld/trunk/test/COFF/Inputs/msvclto.s +++ lld/trunk/test/COFF/Inputs/msvclto.s @@ -0,0 +1,3 @@ +.globl foo +foo: +ret Index: lld/trunk/test/COFF/msvclto.ll =================================================================== --- lld/trunk/test/COFF/msvclto.ll +++ lld/trunk/test/COFF/msvclto.ll @@ -0,0 +1,17 @@ +; RUN: llvm-as -o %t1.obj %s +; RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t2.obj %p/Inputs/msvclto.s +; RUN: lld-link %t1.obj %t2.obj /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \ +; RUN: /entry:main /verbose /subsystem:console > %t.log 2>&1 || true +; RUN: FileCheck %s < %t.log + +; CHECK: link.exe /nologo {{.*}} {{.*}}2.obj /out:{{.*}}.exe /opt:icf /entry:main /verbose /subsystem:console + +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 +}