Index: lld/trunk/COFF/CMakeLists.txt =================================================================== --- lld/trunk/COFF/CMakeLists.txt +++ lld/trunk/COFF/CMakeLists.txt @@ -10,6 +10,14 @@ SymbolTable.cpp Symbols.cpp Writer.cpp + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + LTO + MC + MCDisassembler + Support ) add_dependencies(lldCOFF COFFOptionsTableGen) Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include @@ -75,6 +76,8 @@ file_magic Magic = identify_magic(MBRef.getBuffer()); if (Magic == file_magic::archive) return std::unique_ptr(new ArchiveFile(MBRef)); + if (Magic == file_magic::bitcode) + return std::unique_ptr(new BitcodeFile(Path)); return std::unique_ptr(new ObjectFile(MBRef)); } @@ -191,6 +194,14 @@ } bool LinkerDriver::link(int Argc, const char *Argv[]) { + // Needed for LTO. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllDisassemblers(); + // Parse command line options. auto ArgsOrErr = parseArgs(Argc, Argv); if (auto EC = ArgsOrErr.getError()) { @@ -356,6 +367,11 @@ if (Symtab.reportRemainingUndefines()) return false; + if (auto EC = Symtab.addCombinedLTOObject()) { + llvm::errs() << EC.message() << "\n"; + return false; + } + // /include option takes precedence over garbage collection. for (auto *Arg : Args->filtered(OPT_incl)) Symtab.find(Arg->getValue())->markLive(); Index: lld/trunk/COFF/InputFiles.h =================================================================== --- lld/trunk/COFF/InputFiles.h +++ lld/trunk/COFF/InputFiles.h @@ -15,6 +15,7 @@ #include "Symbols.h" #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/LTO/LTOModule.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include @@ -24,13 +25,14 @@ namespace lld { namespace coff { +using llvm::LTOModule; using llvm::object::Archive; using llvm::object::COFFObjectFile; // The root class of input files. class InputFile { public: - enum Kind { ArchiveKind, ObjectKind, ImportKind }; + enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; Kind kind() const { return FileKind; } virtual ~InputFile() {} @@ -157,6 +159,28 @@ StringAllocator StringAlloc; }; +// Used for LTO. +class BitcodeFile : public InputFile { +public: + explicit BitcodeFile(StringRef S) : InputFile(BitcodeKind), Filename(S) {} + explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind), MBRef(M) {} + static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } + StringRef getName() override { return Filename; } + std::vector &getSymbols() override { return SymbolBodies; } + + LTOModule *getModule() const { return M.get(); } + LTOModule *releaseModule() { return M.release(); } + +private: + std::error_code parse() override; + + std::string Filename; + MemoryBufferRef MBRef; + std::vector SymbolBodies; + llvm::BumpPtrAllocator Alloc; + std::unique_ptr M; +}; + } // namespace coff } // namespace lld Index: lld/trunk/COFF/InputFiles.cpp =================================================================== --- lld/trunk/COFF/InputFiles.cpp +++ lld/trunk/COFF/InputFiles.cpp @@ -12,6 +12,7 @@ #include "InputFiles.h" #include "Writer.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/LTO/LTOModule.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Debug.h" @@ -239,5 +240,32 @@ return std::error_code(); } +std::error_code BitcodeFile::parse() { + std::string Err; + if (!Filename.empty()) { + M.reset(LTOModule::createFromFile(Filename.c_str(), llvm::TargetOptions(), + Err)); + } else { + M.reset(LTOModule::createFromBuffer(MBRef.getBufferStart(), + MBRef.getBufferSize(), + llvm::TargetOptions(), Err)); + } + if (!Err.empty()) { + llvm::errs() << Err << '\n'; + return make_error_code(LLDError::BrokenFile); + } + + for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) { + StringRef SymName = M->getSymbolName(I); + if ((M->getSymbolAttributes(I) & LTO_SYMBOL_DEFINITION_MASK) == + LTO_SYMBOL_DEFINITION_UNDEFINED) { + SymbolBodies.push_back(new (Alloc) Undefined(SymName)); + } else { + SymbolBodies.push_back(new (Alloc) DefinedBitcode(SymName)); + } + } + + return std::error_code(); +} } // namespace coff } // namespace lld Index: lld/trunk/COFF/SymbolTable.h =================================================================== --- lld/trunk/COFF/SymbolTable.h +++ lld/trunk/COFF/SymbolTable.h @@ -58,6 +58,11 @@ // Dump contents of the symbol table to stderr. void dump(); + // Build a COFF object representing the combined contents of BitcodeFiles + // and add it to the symbol table. Called after all files are added and + // before the writer writes results to a file. + std::error_code addCombinedLTOObject(); + // The writer needs to handle DLL import libraries specially in // order to create the import descriptor table. std::vector> ImportFiles; @@ -75,6 +80,7 @@ std::error_code addObject(ObjectFile *File); std::error_code addArchive(ArchiveFile *File); std::error_code addImport(ImportFile *File); + std::error_code addBitcode(BitcodeFile *File); std::error_code resolve(SymbolBody *Body); std::error_code addMemberFile(Lazy *Body); @@ -82,7 +88,9 @@ std::unordered_map Symtab; std::vector> ArchiveFiles; + std::vector> BitcodeFiles; std::vector> OwningSymbols; + std::unique_ptr LTOObjectFile; llvm::BumpPtrAllocator Alloc; }; Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -12,6 +12,7 @@ #include "Error.h" #include "SymbolTable.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -34,6 +35,8 @@ return addObject(P); if (auto *P = dyn_cast(FileP)) return addArchive(P); + if (auto *P = dyn_cast(FileP)) + return addBitcode(P); return addImport(cast(FileP)); } @@ -65,6 +68,17 @@ return std::error_code(); } +std::error_code SymbolTable::addBitcode(BitcodeFile *File) { + BitcodeFiles.emplace_back(File); + for (SymbolBody *Body : File->getSymbols()) + if (Body->isExternal()) + if (auto EC = resolve(Body)) + return EC; + + // TODO: Handle linker directives. + return std::error_code(); +} + std::error_code SymbolTable::addImport(ImportFile *File) { ImportFiles.emplace_back(File); for (SymbolBody *Body : File->getSymbols()) @@ -213,5 +227,68 @@ } } +std::error_code SymbolTable::addCombinedLTOObject() { + if (BitcodeFiles.empty()) + return std::error_code(); + + llvm::LTOCodeGenerator CG; + std::set PreservedBitcodeSymbols; + + // All symbols referenced by non-bitcode objects must be preserved. + for (std::unique_ptr &File : ObjectFiles) + for (SymbolBody *Body : File->getSymbols()) + if (auto *S = dyn_cast(Body->getReplacement())) + PreservedBitcodeSymbols.insert(S); + + // Likewise for the linker-generated reference to the entry point. + if (auto *S = dyn_cast(Symtab[Config->EntryName]->Body)) + PreservedBitcodeSymbols.insert(S); + + for (DefinedBitcode *S : PreservedBitcodeSymbols) + CG.addMustPreserveSymbol(S->getName()); + + CG.setModule(BitcodeFiles[0]->releaseModule()); + for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) + CG.addModule(BitcodeFiles[I]->getModule()); + + std::string ErrMsg; + LTOObjectFile = CG.compile(false, false, false, ErrMsg); + if (!LTOObjectFile) { + llvm::errs() << ErrMsg << '\n'; + return make_error_code(LLDError::BrokenFile); + } + + // Create an object file and add it to the symbol table by replacing any + // DefinedBitcode symbols with the definitions in the object file. + auto Obj = new ObjectFile(LTOObjectFile->getMemBufferRef()); + ObjectFiles.emplace_back(Obj); + if (auto EC = Obj->parse()) + return EC; + for (SymbolBody *Body : Obj->getSymbols()) { + if (!Body->isExternal()) + continue; + + // Find an existing Symbol. We should not see any new symbols at this point. + StringRef Name = Body->getName(); + Symbol *Sym = Symtab[Name]; + if (!Sym) { + llvm::errs() << "LTO: unexpected new symbol: " << Name << '\n'; + return make_error_code(LLDError::BrokenFile); + } + Body->setBackref(Sym); + + if (isa(Sym->Body)) { + // The symbol should now be defined. + if (!isa(Body)) { + llvm::errs() << "LTO: undefined symbol: " << Name << '\n'; + return make_error_code(LLDError::BrokenFile); + } + Sym->Body = Body; + } + } + + return std::error_code(); +} + } // namespace coff } // namespace lld Index: lld/trunk/COFF/Symbols.h =================================================================== --- lld/trunk/COFF/Symbols.h +++ lld/trunk/COFF/Symbols.h @@ -48,6 +48,7 @@ DefinedAbsoluteKind, DefinedImportDataKind, DefinedImportThunkKind, + DefinedBitcodeKind, DefinedLast, UndefinedKind, LazyKind, @@ -251,6 +252,18 @@ ImportThunkChunk Data; }; +class DefinedBitcode : public Defined { +public: + DefinedBitcode(StringRef Name) : Defined(DefinedBitcodeKind, Name) {} + + static bool classof(const SymbolBody *S) { + return S->kind() == DefinedBitcodeKind; + } + + uint64_t getRVA() override { llvm_unreachable("bitcode reached writer"); } + uint64_t getFileOff() override { llvm_unreachable("bitcode reached writer"); } +}; + } // namespace coff } // namespace lld Index: lld/trunk/COFF/Symbols.cpp =================================================================== --- lld/trunk/COFF/Symbols.cpp +++ lld/trunk/COFF/Symbols.cpp @@ -81,6 +81,8 @@ return std::unique_ptr(nullptr); file_magic Magic = identify_magic(MBRef.getBuffer()); + if (Magic == file_magic::bitcode) + return std::unique_ptr(new BitcodeFile(MBRef)); if (Magic == file_magic::coff_import_library) return std::unique_ptr(new ImportFile(MBRef)); Index: lld/trunk/test/CMakeLists.txt =================================================================== --- lld/trunk/test/CMakeLists.txt +++ lld/trunk/test/CMakeLists.txt @@ -20,7 +20,7 @@ ) set(LLD_TEST_DEPS - FileCheck not llvm-ar llvm-nm + FileCheck not llvm-ar llvm-as llvm-nm llc lld llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml linker-script-test macho-dump llvm-mc llvm-nm ) Index: lld/trunk/test/COFF/Inputs/lto-dep.ll =================================================================== --- lld/trunk/test/COFF/Inputs/lto-dep.ll +++ lld/trunk/test/COFF/Inputs/lto-dep.ll @@ -0,0 +1,6 @@ +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: lld/trunk/test/COFF/lto.ll =================================================================== --- lld/trunk/test/COFF/lto.ll +++ lld/trunk/test/COFF/lto.ll @@ -0,0 +1,81 @@ +; RUN: llvm-as -o %T/main.lto.obj %s +; RUN: llvm-as -o %T/foo.lto.obj %S/Inputs/lto-dep.ll +; RUN: rm -f %T/foo.lto.lib +; RUN: llvm-ar cru %T/foo.lto.lib %T/foo.lto.obj + +; RUN: llc -filetype=obj -o %T/main.obj %s +; RUN: llc -filetype=obj -o %T/foo.obj %S/Inputs/lto-dep.ll +; RUN: rm -f %T/foo.lib +; RUN: llvm-ar cru %T/foo.lib %T/foo.obj + +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.lto.obj %T/foo.lto.obj +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-11 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-11 %s +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.lto.obj %T/foo.lto.lib +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-11 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-11 %s + +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.obj %T/foo.lto.obj +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-01 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-01 %s +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.obj %T/foo.lto.lib +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-01 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-01 %s + +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.lto.obj %T/foo.obj +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-10 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-10 %s +; RUN: lld -flavor link2 /out:%T/main.exe /entry:main /subsystem:console %T/main.lto.obj %T/foo.lib +; RUN: llvm-readobj -file-headers %T/main.exe | FileCheck -check-prefix=HEADERS-10 %s +; RUN: llvm-objdump -d %T/main.exe | FileCheck -check-prefix=TEXT-10 %s + +; HEADERS-11: AddressOfEntryPoint: 0x1000 +; TEXT-11: Disassembly of section .text: +; TEXT-11-NEXT: .text: +; TEXT-11-NEXT: xorl %eax, %eax +; TEXT-11-NEXT: retq + +; HEADERS-01: AddressOfEntryPoint: 0x1000 +; TEXT-01: Disassembly of section .text: +; TEXT-01-NEXT: .text: +; TEXT-01-NEXT: subq $40, %rsp +; TEXT-01-NEXT: callq 7 +; TEXT-01-NEXT: xorl %eax, %eax +; TEXT-01-NEXT: addq $40, %rsp +; TEXT-01-NEXT: retq +; TEXT-01-NEXT: retq + +; HEADERS-10: AddressOfEntryPoint: 0x1010 +; TEXT-10: Disassembly of section .text: +; TEXT-10-NEXT: .text: +; TEXT-10-NEXT: retq +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: subq $40, %rsp +; TEXT-10-NEXT: callq -25 +; TEXT-10-NEXT: xorl %eax, %eax +; TEXT-10-NEXT: addq $40, %rsp +; TEXT-10-NEXT: retq + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define i32 @main() { + call void @foo() + ret i32 0 +} + +declare void @foo() Index: lld/trunk/test/lit.cfg =================================================================== --- lld/trunk/test/lit.cfg +++ lld/trunk/test/lit.cfg @@ -33,7 +33,7 @@ config.test_format = lit.formats.ShTest(execute_external) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.objtxt', '.test'] +config.suffixes = ['.ll', '.objtxt', '.test'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent