Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -29,6 +29,8 @@ void main(ArrayRef Args); void addFile(StringRef Path); void addLibrary(StringRef Name); + std::vector> OwningMBs; + SmallString<0> OwningLTOData; private: void readConfigs(llvm::opt::InputArgList &Args); @@ -38,7 +40,6 @@ llvm::BumpPtrAllocator Alloc; bool WholeArchive = false; std::vector> Files; - std::vector> OwningMBs; }; // Parses command line options. Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -16,6 +16,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 @@ -109,6 +110,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)); } @@ -290,6 +294,11 @@ } template void LinkerDriver::link(opt::InputArgList &Args) { + // For LTO + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + SymbolTable Symtab; Target.reset(createTarget()); @@ -348,6 +357,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,30 @@ } } +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 @@ -1415,6 +1415,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 @@ -36,6 +36,7 @@ public: void addFile(std::unique_ptr File); + void addCombinedLtoObject(); const llvm::MapVector &getSymbols() const { return Symtab; @@ -67,6 +68,7 @@ void addMemberFile(Undefined *Undef, Lazy *L); void resolve(SymbolBody *Body); std::string conflictMsg(SymbolBody *Old, SymbolBody *New); + 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. @@ -87,6 +89,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 @@ -16,9 +16,15 @@ #include "SymbolTable.h" #include "Config.h" +#include "Driver.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 +80,14 @@ return; } + 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); } +static std::unique_ptr 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(Driver->OwningLTOData); + legacy::PassManager CodeGenPasses; + if (TM->addPassesToEmitFile(CodeGenPasses, OS, + TargetMachine::CGFT_ObjectFile)) + fatal("Failed to setup codegen"); + CodeGenPasses.run(M); + } + std::unique_ptr Buffer = + MemoryBuffer::getMemBuffer(Driver->OwningLTOData, "", false); + Driver->OwningMBs.push_back(std::move(Buffer)); + return createObjectFile(*Driver->OwningMBs.back()); +} + +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-start.ll =================================================================== --- /dev/null +++ test/ELF/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 +}