Index: COFF/CMakeLists.txt =================================================================== --- COFF/CMakeLists.txt +++ COFF/CMakeLists.txt @@ -9,6 +9,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 @@ -25,6 +25,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include @@ -165,10 +166,16 @@ return Filename; } -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)); } // Parses .drectve section contents and returns a list of files @@ -199,6 +206,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); @@ -224,7 +238,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; } @@ -232,6 +251,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 @@ -30,7 +31,7 @@ // 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 +163,27 @@ StringAllocator StringAlloc; }; +class BitcodeFile : public InputFile { +public: + explicit BitcodeFile(StringRef S) : InputFile(BitcodeKind), Name(S) {} + explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind), MBRef(M) {} + static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } + StringRef getName() override { return Name; } + virtual std::vector &getSymbols() { return SymbolBodies; } + + llvm::LTOModule *getModule() const { return M.get(); } + llvm::LTOModule *releaseModule() { return M.release(); } + +private: + std::error_code parse() override; + + std::string Name; + 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,28 @@ return std::error_code(); } +std::error_code BitcodeFile::parse() { + std::string Err; + if (!Name.empty()) + M.reset(llvm::LTOModule::createFromFile(Name.c_str(), llvm::TargetOptions(), + Err)); + else + M.reset(llvm::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 @@ -49,6 +49,10 @@ // 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. + std::error_code addCombinedLTOObject(); + // The writer needs to handle DLL import libraries specially in // order to create the import descriptor table. std::vector> ImportFiles; @@ -57,6 +61,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); @@ -65,7 +70,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)); } @@ -59,6 +62,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::addArchive(ArchiveFile *File) { ArchiveFiles.emplace_back(File); for (SymbolBody *Body : File->getSymbols()) @@ -171,5 +185,60 @@ } } +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 (auto &Obj : ObjectFiles) + for (SymbolBody *Body : Obj->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; I != BitcodeFiles.size(); ++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)); + + // Undefine the preserved bitcode symbols. They are about to be replaced with + // real symbols. Any other bitcode symbols are removed from the symbol table, + // as they are no longer needed, and will confuse the writer. + for (auto I = Symtab.begin(), E = Symtab.end(); I != E;) { + StringRef Name = I->first; + Symbol *Ref = I->second; + if (auto *S = dyn_cast(Ref->Body)) { + if (PreservedBitcodeSymbols.count(S)) { + Ref->Body = new (Alloc) Undefined(Name); + ++I; + } else { + auto OldI = I; + ++I; + Symtab.erase(OldI); + } + } else { + ++I; + } + } + + addFile(llvm::make_unique("", + LTOObjectFile->getMemBufferRef())); + return std::error_code(); +} + } // namespace coff } // namespace lld Index: COFF/Symbols.h =================================================================== --- COFF/Symbols.h +++ COFF/Symbols.h @@ -47,6 +47,7 @@ DefinedAbsoluteKind, DefinedImportDataKind, DefinedImportThunkKind, + DefinedBitcodeKind, DefinedLast, UndefinedKind, LazyKind, @@ -247,6 +248,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(llvm::make_unique(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