Index: COFF/CMakeLists.txt =================================================================== --- COFF/CMakeLists.txt +++ 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: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -22,6 +22,8 @@ #include "llvm/Support/CommandLine.h" #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 @@ -45,10 +47,16 @@ llvm_unreachable("internal error"); } -std::unique_ptr createFile(StringRef Path) { +ErrorOr> createFile(StringRef Path) { if (StringRef(Path).endswith_lower(".lib")) - return llvm::make_unique(Path); - return llvm::make_unique(Path); + return std::unique_ptr(llvm::make_unique(Path)); + + llvm::sys::fs::file_magic Magic; + if (auto EC = llvm::sys::fs::identify_magic(Path, Magic)) + return EC; + if (Magic == llvm::sys::fs::file_magic::bitcode) + return std::unique_ptr(llvm::make_unique(Path)); + return std::unique_ptr(llvm::make_unique(Path)); } namespace { @@ -90,6 +98,13 @@ } bool link(int Argc, const char *Argv[]) { + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllDisassemblers(); + // Parse command line options. Config = new Configuration(); auto ArgsOrErr = parseArgs(Argc, Argv); @@ -178,7 +193,12 @@ std::string Path = findFile(Arg->getValue()); if (!Config->insertFile(Path)) continue; - if (auto EC = Symtab.addFile(createFile(Path))) { + auto FileOrErr = createFile(Path); + if (auto EC = FileOrErr.getError()) { + llvm::errs() << Path << ": " << EC.message() << "\n"; + return false; + } + if (auto EC = Symtab.addFile(std::move(FileOrErr.get()))) { llvm::errs() << Path << ": " << EC.message() << "\n"; return false; } @@ -186,6 +206,11 @@ if (Symtab.reportRemainingUndefines()) return false; + if (auto EC = Symtab.addCombinedLTOObject()) { + llvm::errs() << EC.message() << "\n"; + return false; + } + // Write the result. Writer Out(&Symtab); if (auto EC = Out.write(getOutputPath(Args.get()))) { Index: COFF/InputFiles.h =================================================================== --- COFF/InputFiles.h +++ 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() {} @@ -162,6 +164,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: COFF/InputFiles.cpp =================================================================== --- COFF/InputFiles.cpp +++ COFF/InputFiles.cpp @@ -12,6 +12,7 @@ #include "Writer.h" #include "lld/Core/Error.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" @@ -243,5 +244,30 @@ 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()) + return make_dynamic_error_code(StringRef(Err)); + + 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: COFF/SymbolTable.h =================================================================== --- COFF/SymbolTable.h +++ COFF/SymbolTable.h @@ -53,6 +53,11 @@ // table. llvm::COFF::MachineTypes getInferredMachineType(); + // 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; @@ -61,6 +66,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); @@ -69,7 +75,9 @@ std::unordered_map Symtab; std::vector> ObjectFiles; std::vector> ArchiveFiles; + std::vector> BitcodeFiles; std::vector> OwnedSymbols; + std::unique_ptr LTOObjectFile; llvm::BumpPtrAllocator Alloc; StringAllocator StringAlloc; }; Index: COFF/SymbolTable.cpp =================================================================== --- COFF/SymbolTable.cpp +++ COFF/SymbolTable.cpp @@ -12,6 +12,7 @@ #include "SymbolTable.h" #include "lld/Core/Error.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -36,6 +37,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)); } @@ -67,6 +70,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()) @@ -182,5 +196,63 @@ return llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; } +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) + return make_dynamic_error_code(StringRef(ErrMsg)); + + // 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()); + if (auto EC = Obj->parse()) + return EC; + ObjectFiles.emplace_back(Obj); + 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) + return make_dynamic_error_code(Twine("LTO: unexpected new symbol: ") + + Name); + Body->setBackref(Sym); + + if (isa(Sym->Body)) { + // The symbol should now be defined. + if (!isa(Body)) + return make_dynamic_error_code(Twine("LTO: undefined symbol: ") + Name); + Sym->Body = Body; + } + } + + return std::error_code(); +} + } // namespace coff } // namespace lld Index: COFF/Symbols.h =================================================================== --- COFF/Symbols.h +++ 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: COFF/Symbols.cpp =================================================================== --- COFF/Symbols.cpp +++ 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: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ 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: test/COFF/Inputs/lto-dep.ll =================================================================== --- /dev/null +++ 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: test/COFF/lto.ll =================================================================== --- /dev/null +++ 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 %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 %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 %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 %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 %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 %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: test/lit.cfg =================================================================== --- test/lit.cfg +++ 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