Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -17,6 +17,7 @@ #include "Writer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include @@ -112,6 +113,9 @@ case file_magic::elf_shared_object: Files.push_back(createSharedFile(MBRef)); return; + case sys::fs::file_magic::bitcode: + Files.push_back(make_unique(MBRef)); + return; default: Files.push_back(createObjectFile(MBRef)); } @@ -293,6 +297,11 @@ } template void LinkerDriver::link(opt::InputArgList &Args) { + // For LTO + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + SymbolTable Symtab; std::unique_ptr TI(createTarget()); Target = TI.get(); @@ -353,6 +362,8 @@ for (StringRef S : Config->Undefined) Symtab.addUndefinedOpt(S); + Symtab.addCombinedLtoObject(); + for (auto *Arg : Args.filtered(OPT_wrap)) Symtab.wrap(Arg->getValue()); Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -20,6 +20,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/StringSaver.h" namespace lld { namespace elf2 { @@ -33,10 +34,11 @@ // The root class of input files. class InputFile { public: - enum Kind { ObjectKind, SharedKind, ArchiveKind }; + enum Kind { ObjectKind, SharedKind, ArchiveKind, BitcodeKind }; Kind kind() const { return FileKind; } StringRef getName() const { return MB.getBufferIdentifier(); } + MemoryBufferRef MB; // Filename of .a which contained this file. If this file was // not in an archive file, it is the empty string. We use this @@ -45,7 +47,6 @@ protected: InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} - MemoryBufferRef MB; private: const Kind FileKind; @@ -178,6 +179,19 @@ llvm::DenseSet Seen; }; +class BitcodeFile : public InputFile { +public: + explicit BitcodeFile(MemoryBufferRef M); + static bool classof(const InputFile *F); + void parse(); + + std::vector SymbolBodies; + +private: + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver{Alloc}; +}; + // .so file. template class SharedFile : public ELFFileBase { typedef ELFFileBase Base; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -12,6 +12,9 @@ #include "InputSection.h" #include "Symbols.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::ELF; @@ -423,6 +426,28 @@ } } +BitcodeFile::BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {} + +bool BitcodeFile::classof(const InputFile *F) { + return F->kind() == BitcodeKind; +} + +void BitcodeFile::parse() { + LLVMContext Context; + ErrorOr> ObjOrErr = + IRObjectFile::create(MB, Context); + fatal(ObjOrErr); + IRObjectFile &Obj = **ObjOrErr; + for (const BasicSymbolRef &Sym : Obj.symbols()) { + SmallString<64> Name; + raw_svector_ostream OS(Name); + Sym.printName(OS); + StringRef NameRef = Saver.save(StringRef(Name)); + SymbolBody *Body = new (Alloc) DefinedBitcode(NameRef); + SymbolBodies.push_back(Body); + } +} + template static std::unique_ptr createELFFileAux(MemoryBufferRef MB) { std::unique_ptr Ret = llvm::make_unique(MB); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1483,6 +1483,8 @@ case SymbolBody::UndefinedKind: case SymbolBody::LazyKind: break; + case SymbolBody::DefinedBitcodeKind: + llvm_unreachable("Should have been replaced"); } ESym->st_name = P.second; Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -13,6 +13,10 @@ #include "InputFiles.h" #include "llvm/ADT/MapVector.h" +namespace llvm { +class Module; +} + namespace lld { namespace elf2 { class Lazy; @@ -36,6 +40,7 @@ public: void addFile(std::unique_ptr File); + void addCombinedLtoObject(); const llvm::MapVector &getSymbols() const { return Symtab; @@ -66,8 +71,13 @@ void addLazy(Lazy *New); void addMemberFile(Undefined *Undef, Lazy *L); void resolve(SymbolBody *Body); + std::unique_ptr codegen(llvm::Module &M); std::string conflictMsg(SymbolBody *Old, SymbolBody *New); + SmallString<0> OwningLTOData; + std::unique_ptr LtoBuffer; + ObjectFile *createCombinedLtoObject(); + // The order the global symbols are in is not defined. We can use an arbitrary // order, but it has to be reproducible. That is true even when cross linking. // The default hashing of StringRef produces different results on 32 and 64 @@ -87,6 +97,7 @@ std::vector> ArchiveFiles; std::vector>> ObjectFiles; std::vector>> SharedFiles; + std::vector> BitcodeFiles; // Set of .so files to not link the same shared object file more than once. llvm::DenseSet SoNames; Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -18,7 +18,12 @@ #include "Config.h" #include "Error.h" #include "Symbols.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Linker/Linker.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" using namespace llvm; using namespace llvm::object; @@ -74,6 +79,15 @@ return; } + // LLVM bitcode file. + if (auto *F = dyn_cast(FileP)) { + BitcodeFiles.emplace_back(cast(File.release())); + F->parse(); + for (SymbolBody *B : F->SymbolBodies) + resolve(B); + return; + } + // .o file auto *F = cast>(FileP); ObjectFiles.emplace_back(cast>(File.release())); @@ -82,6 +96,70 @@ resolve(B); } +// Codegen the module M and returns the resulting InputFile. +template +std::unique_ptr SymbolTable::codegen(Module &M) { + StringRef TripleStr = M.getTargetTriple(); + Triple TheTriple(TripleStr); + + // FIXME: Should we have a default triple? The gold plugin uses + // sys::getDefaultTargetTriple(), but that is probably wrong given that this + // might be a cross linker. + + std::string ErrMsg; + const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg); + if (!TheTarget) + fatal("Target not found: " + ErrMsg); + + TargetOptions Options; + std::unique_ptr TM( + TheTarget->createTargetMachine(TripleStr, "", "", Options)); + + raw_svector_ostream OS(OwningLTOData); + legacy::PassManager CodeGenPasses; + if (TM->addPassesToEmitFile(CodeGenPasses, OS, + TargetMachine::CGFT_ObjectFile)) + fatal("Failed to setup codegen"); + CodeGenPasses.run(M); + LtoBuffer = MemoryBuffer::getMemBuffer(OwningLTOData, "", false); + return createObjectFile(*LtoBuffer); +} + +// Merge all the bitcode files we have seen, codegen the result and return +// the resulting ObjectFile. +template +ObjectFile *SymbolTable::createCombinedLtoObject() { + LLVMContext Context; + Module Combined("ld-temp.o", Context); + Linker L(Combined); + for (const std::unique_ptr &F : BitcodeFiles) { + std::unique_ptr Buffer = + MemoryBuffer::getMemBuffer(F->MB, false); + ErrorOr> MOrErr = + getLazyBitcodeModule(std::move(Buffer), Context, + /*ShouldLazyLoadMetadata*/ true); + fatal(MOrErr); + std::unique_ptr &M = *MOrErr; + L.linkInModule(std::move(M)); + } + std::unique_ptr F = codegen(Combined); + ObjectFiles.emplace_back(cast>(F.release())); + return &*ObjectFiles.back(); +} + +template void SymbolTable::addCombinedLtoObject() { + if (BitcodeFiles.empty()) + return; + ObjectFile *Obj = createCombinedLtoObject(); + // FIXME: We probably have to ignore comdats here. + Obj->parse(ComdatGroups); + for (SymbolBody *Body : Obj->getSymbols()) { + Symbol *Sym = insert(Body); + assert(isa(Sym->Body)); + Sym->Body = Body; + } +} + // Add an undefined symbol. template SymbolBody *SymbolTable::addUndefined(StringRef Name) { Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -65,6 +65,7 @@ SharedKind, DefinedElfLast = SharedKind, DefinedCommonKind, + DefinedBitcodeKind, DefinedSyntheticKind, DefinedLast = DefinedSyntheticKind, UndefinedElfKind, @@ -187,6 +188,12 @@ } }; +class DefinedBitcode : public Defined { +public: + DefinedBitcode(StringRef Name); + static bool classof(const SymbolBody *S); +}; + class DefinedCommon : public Defined { public: DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, bool IsWeak, Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -69,6 +69,8 @@ case LazyKind: assert(isUsedInRegularObj() && "Lazy symbol reached writer"); return 0; + case DefinedBitcodeKind: + llvm_unreachable("Should have been replaced"); } llvm_unreachable("Invalid symbol kind"); } @@ -159,6 +161,13 @@ bool IsTls, bool IsFunction) : SymbolBody(K, Name, IsWeak, Visibility, IsTls, IsFunction) {} +DefinedBitcode::DefinedBitcode(StringRef Name) + : Defined(DefinedBitcodeKind, Name, false, STV_DEFAULT, false, false) {} + +bool DefinedBitcode::classof(const SymbolBody *S) { + return S->kind() == DefinedBitcodeKind; +} + Undefined::Undefined(SymbolBody::Kind K, StringRef N, bool IsWeak, uint8_t Visibility, bool IsTls) : SymbolBody(K, N, IsWeak, Visibility, IsTls, /*IsFunction*/ false), Index: test/ELF/lit.local.cfg =================================================================== --- test/ELF/lit.local.cfg +++ test/ELF/lit.local.cfg @@ -1,2 +1,2 @@ -config.suffixes = ['.test', '.s'] +config.suffixes = ['.test', '.s', '.ll'] Index: test/ELF/lto/lto-start.ll =================================================================== --- /dev/null +++ test/ELF/lto/lto-start.ll @@ -0,0 +1,23 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 +; RUN: llvm-readobj -t %t2 | FileCheck %s + +; CHECK: Format: ELF64-x86-64 +; CHECK-NEXT: Arch: x86_64 +; CHECK-NEXT: AddressSize: 64bit + +; CHECK: Name: _start +; CHECK-NEXT: Value: +; CHECK-NEXT: Size: 1 +; CHECK-NEXT: Binding: Global +; CHECK-NEXT: Type: Function +; CHECK-NEXT: Other: +; CHECK-NEXT: Section: .text + +target triple = "x86_64-unknown-linux-gnu" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @_start() { + ret void +}