[lld] r238777 - COFF: Initial implementation of link-time optimization.

Peter Collingbourne peter at pcc.me.uk
Mon Jun 1 13:10:11 PDT 2015


Author: pcc
Date: Mon Jun  1 15:10:10 2015
New Revision: 238777

URL: http://llvm.org/viewvc/llvm-project?rev=238777&view=rev
Log:
COFF: Initial implementation of link-time optimization.

This implementation is known to work in very simple cases (see new test case).

Differential Revision: http://reviews.llvm.org/D10115

Added:
    lld/trunk/test/COFF/Inputs/lto-dep.ll
    lld/trunk/test/COFF/lto.ll
Modified:
    lld/trunk/COFF/CMakeLists.txt
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/InputFiles.cpp
    lld/trunk/COFF/InputFiles.h
    lld/trunk/COFF/SymbolTable.cpp
    lld/trunk/COFF/SymbolTable.h
    lld/trunk/COFF/Symbols.cpp
    lld/trunk/COFF/Symbols.h
    lld/trunk/test/CMakeLists.txt
    lld/trunk/test/lit.cfg

Modified: lld/trunk/COFF/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/CMakeLists.txt?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/CMakeLists.txt (original)
+++ lld/trunk/COFF/CMakeLists.txt Mon Jun  1 15:10:10 2015
@@ -10,6 +10,14 @@ add_llvm_library(lldCOFF
   SymbolTable.cpp
   Symbols.cpp
   Writer.cpp
+
+  LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  Core
+  LTO
+  MC
+  MCDisassembler
+  Support
   )
 
 add_dependencies(lldCOFF COFFOptionsTableGen)

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Mon Jun  1 15:10:10 2015
@@ -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 <memory>
 
@@ -75,6 +76,8 @@ ErrorOr<std::unique_ptr<InputFile>> Link
   file_magic Magic = identify_magic(MBRef.getBuffer());
   if (Magic == file_magic::archive)
     return std::unique_ptr<InputFile>(new ArchiveFile(MBRef));
+  if (Magic == file_magic::bitcode)
+    return std::unique_ptr<InputFile>(new BitcodeFile(Path));
   return std::unique_ptr<InputFile>(new ObjectFile(MBRef));
 }
 
@@ -191,6 +194,14 @@ std::vector<StringRef> LinkerDriver::get
 }
 
 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 @@ bool LinkerDriver::link(int Argc, const
   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();

Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Mon Jun  1 15:10:10 2015
@@ -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 @@ std::error_code ImportFile::parse() {
   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

Modified: lld/trunk/COFF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.h?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.h (original)
+++ lld/trunk/COFF/InputFiles.h Mon Jun  1 15:10:10 2015
@@ -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 <memory>
@@ -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 @@ private:
   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<SymbolBody *> &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<SymbolBody *> SymbolBodies;
+  llvm::BumpPtrAllocator Alloc;
+  std::unique_ptr<LTOModule> M;
+};
+
 } // namespace coff
 } // namespace lld
 

Modified: lld/trunk/COFF/SymbolTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.cpp?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.cpp (original)
+++ lld/trunk/COFF/SymbolTable.cpp Mon Jun  1 15:10:10 2015
@@ -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 @@ std::error_code SymbolTable::addFile(std
     return addObject(P);
   if (auto *P = dyn_cast<ArchiveFile>(FileP))
     return addArchive(P);
+  if (auto *P = dyn_cast<BitcodeFile>(FileP))
+    return addBitcode(P);
   return addImport(cast<ImportFile>(FileP));
 }
 
@@ -65,6 +68,17 @@ std::error_code SymbolTable::addArchive(
   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 @@ void SymbolTable::dump() {
   }
 }
 
+std::error_code SymbolTable::addCombinedLTOObject() {
+  if (BitcodeFiles.empty())
+    return std::error_code();
+
+  llvm::LTOCodeGenerator CG;
+  std::set<DefinedBitcode *> PreservedBitcodeSymbols;
+
+  // All symbols referenced by non-bitcode objects must be preserved.
+  for (std::unique_ptr<ObjectFile> &File : ObjectFiles)
+    for (SymbolBody *Body : File->getSymbols())
+      if (auto *S = dyn_cast<DefinedBitcode>(Body->getReplacement()))
+        PreservedBitcodeSymbols.insert(S);
+
+  // Likewise for the linker-generated reference to the entry point.
+  if (auto *S = dyn_cast<DefinedBitcode>(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<DefinedBitcode>(Sym->Body)) {
+      // The symbol should now be defined.
+      if (!isa<Defined>(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

Modified: lld/trunk/COFF/SymbolTable.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.h?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.h (original)
+++ lld/trunk/COFF/SymbolTable.h Mon Jun  1 15:10:10 2015
@@ -58,6 +58,11 @@ public:
   // 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<std::unique_ptr<ImportFile>> ImportFiles;
@@ -75,6 +80,7 @@ private:
   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 @@ private:
 
   std::unordered_map<StringRef, Symbol *> Symtab;
   std::vector<std::unique_ptr<ArchiveFile>> ArchiveFiles;
+  std::vector<std::unique_ptr<BitcodeFile>> BitcodeFiles;
   std::vector<std::unique_ptr<SymbolBody>> OwningSymbols;
+  std::unique_ptr<MemoryBuffer> LTOObjectFile;
   llvm::BumpPtrAllocator Alloc;
 };
 

Modified: lld/trunk/COFF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Symbols.cpp?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/Symbols.cpp (original)
+++ lld/trunk/COFF/Symbols.cpp Mon Jun  1 15:10:10 2015
@@ -81,6 +81,8 @@ ErrorOr<std::unique_ptr<InputFile>> Lazy
     return std::unique_ptr<InputFile>(nullptr);
 
   file_magic Magic = identify_magic(MBRef.getBuffer());
+  if (Magic == file_magic::bitcode)
+    return std::unique_ptr<InputFile>(new BitcodeFile(MBRef));
   if (Magic == file_magic::coff_import_library)
     return std::unique_ptr<InputFile>(new ImportFile(MBRef));
 

Modified: lld/trunk/COFF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Symbols.h?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/COFF/Symbols.h (original)
+++ lld/trunk/COFF/Symbols.h Mon Jun  1 15:10:10 2015
@@ -48,6 +48,7 @@ public:
     DefinedAbsoluteKind,
     DefinedImportDataKind,
     DefinedImportThunkKind,
+    DefinedBitcodeKind,
     DefinedLast,
     UndefinedKind,
     LazyKind,
@@ -251,6 +252,18 @@ private:
   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
 

Modified: lld/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/CMakeLists.txt?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/test/CMakeLists.txt (original)
+++ lld/trunk/test/CMakeLists.txt Mon Jun  1 15:10:10 2015
@@ -20,7 +20,7 @@ configure_lit_site_cfg(
   )
 
 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
   )

Added: lld/trunk/test/COFF/Inputs/lto-dep.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/Inputs/lto-dep.ll?rev=238777&view=auto
==============================================================================
--- lld/trunk/test/COFF/Inputs/lto-dep.ll (added)
+++ lld/trunk/test/COFF/Inputs/lto-dep.ll Mon Jun  1 15:10:10 2015
@@ -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
+}

Added: lld/trunk/test/COFF/lto.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/lto.ll?rev=238777&view=auto
==============================================================================
--- lld/trunk/test/COFF/lto.ll (added)
+++ lld/trunk/test/COFF/lto.ll Mon Jun  1 15:10:10 2015
@@ -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()

Modified: lld/trunk/test/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/lit.cfg?rev=238777&r1=238776&r2=238777&view=diff
==============================================================================
--- lld/trunk/test/lit.cfg (original)
+++ lld/trunk/test/lit.cfg Mon Jun  1 15:10:10 2015
@@ -33,7 +33,7 @@ else:
 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





More information about the llvm-commits mailing list