Rewrite the gold plugin to fix pr19901.
authorRafael Espindola <rafael.espindola@gmail.com>
Thu, 21 Aug 2014 20:28:55 +0000 (20:28 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Thu, 21 Aug 2014 20:28:55 +0000 (20:28 +0000)
There is a fundamental difference between how the gold API and lib/LTO view
the LTO process.

The gold API talks about a particular symbol in a particular file. The lib/LTO
API talks about a symbol in the merged module.

The merged module is then defined in terms of the IR semantics. In particular,
a linkonce_odr GV is only copied if it is used, since it is valid to drop
unused linkonce_odr GVs.

In the testcase in pr19901 both properties collide. What happens is that gold
asks us to keep a particular linkonce_odr symbol, but the IR linker doesn't
copy it to the merged module and we never have a chance to ask lib/LTO to keep
it.

This patch fixes it by having a more direct implementation of the gold API. If
it asks us to keep a symbol, we change the linkage so it is not linkonce. If it
says we can drop a symbol, we do so. All of this before we even send the module
to lib/Linker.

Since now we don't have to produce LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN,
during symbol resolution we can use a temporary LLVMContext and do lazy
module loading. This allows us to keep the minimum possible amount of
allocated memory around. This should also allow as much parallelism as
we want, since there is no shared context.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216215 91177308-0d34-0410-b5e6-96231b3b80d8

test/tools/gold/Inputs/alias-1.ll [new file with mode: 0644]
test/tools/gold/Inputs/pr19901-1.ll [new file with mode: 0644]
test/tools/gold/alias.ll [new file with mode: 0644]
test/tools/gold/emit-llvm.ll
test/tools/gold/mtriple.ll
test/tools/gold/pr19901.ll [new file with mode: 0644]
tools/gold/CMakeLists.txt
tools/gold/Makefile
tools/gold/gold-plugin.cpp

diff --git a/test/tools/gold/Inputs/alias-1.ll b/test/tools/gold/Inputs/alias-1.ll
new file mode 100644 (file)
index 0000000..96183aa
--- /dev/null
@@ -0,0 +1 @@
+@a = global i32 42
diff --git a/test/tools/gold/Inputs/pr19901-1.ll b/test/tools/gold/Inputs/pr19901-1.ll
new file mode 100644 (file)
index 0000000..2f71532
--- /dev/null
@@ -0,0 +1,4 @@
+target triple = "x86_64-unknown-linux-gnu"
+define linkonce_odr hidden void @f() {
+  ret void
+}
diff --git a/test/tools/gold/alias.ll b/test/tools/gold/alias.ll
new file mode 100644 (file)
index 0000000..6a64a6a
--- /dev/null
@@ -0,0 +1,11 @@
+; RUN: llvm-as %s -o %t.o
+; RUN: llvm-as %p/Inputs/alias-1.ll -o %t2.o
+; RUN: ld -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t2.o %t.o \
+; RUN:  -plugin-opt=emit-llvm
+; RUN: llvm-dis %t3.o -o - | FileCheck %s
+
+; CHECK: @a = global i32 42
+; CHECK: @b = global i32 1
+
+@a = weak alias i32* @b
+@b = global i32 1
index 7ea0e5e3e63d96b944ea36ff323321a00a512e95..ede77ecf7d5146d39add3edc130adf4ad553aee5 100644 (file)
@@ -55,7 +55,11 @@ define linkonce_odr void @f6() unnamed_addr {
 @g6 = global void()* @f6
 
 
-; API: f3
-; API: f5
-; API: g5
-; API: g6
+; API: f1 PREVAILING_DEF_IRONLY
+; API: f2 PREVAILING_DEF_IRONLY
+; API: f3 PREVAILING_DEF_IRONLY_EXP
+; API: f4 PREVAILING_DEF_IRONLY_EXP
+; API: f5 PREVAILING_DEF_IRONLY_EXP
+; API: f6 PREVAILING_DEF_IRONLY_EXP
+; API: g5 PREVAILING_DEF_IRONLY_EXP
+; API: g6 PREVAILING_DEF_IRONLY_EXP
index 61a0191efc9e1c2af59cbb9ded420fe9a64794d8..6395af6f1ab94b8ced914534fc9a8c85dee6dbb0 100644 (file)
@@ -1,7 +1,13 @@
 ; RUN: llvm-as %s -o %t.o
 ; RUN: ld -plugin %llvmshlibdir/LLVMgold.so -m elf32ppc \
 ; RUN:    -plugin-opt=mtriple=powerpc-linux-gnu \
-; RUN:    -shared %t.o -o %t2.o
-; RUN: llvm-readobj %t2.o | FileCheck %s
+; RUN:    -plugin-opt=obj-path=%t3.o \
+; RUN:    -shared %t.o -o %t2
+; RUN: llvm-readobj --file-headers %t2 | FileCheck  --check-prefix=DSO %s
+; RUN: llvm-readobj --file-headers %t3.o | FileCheck --check-prefix=REL %s
 
-; CHECK: Format: ELF32-ppc
+; REL:       Type: Relocatable
+; REL-NEXT:  Machine: EM_PPC
+
+; DSO:       Type: SharedObject
+; DSO-NEXT:  Machine: EM_PPC
diff --git a/test/tools/gold/pr19901.ll b/test/tools/gold/pr19901.ll
new file mode 100644 (file)
index 0000000..71d52a6
--- /dev/null
@@ -0,0 +1,17 @@
+; RUN: llc %s -o %t.o -filetype=obj -relocation-model=pic
+; RUN: llvm-as %p/Inputs/pr19901-1.ll -o %t2.o
+; RUN: ld -shared -o %t.so -plugin %llvmshlibdir/LLVMgold.so %t2.o %t.o
+; RUN: llvm-objdump -d -symbolize %t.so | FileCheck %s
+
+; CHECK: g:
+; CHECK-NEXT: push
+; CHECK-NEXT: callq f
+
+target triple = "x86_64-unknown-linux-gnu"
+define i32 @g() {
+  call void @f()
+  ret i32 0
+}
+define linkonce_odr hidden void @f() {
+  ret void
+}
index 3864e154548c9728a838079e0a2ff0b1d70b72e2..3033010182986adc374f85b02d56308f442eeb4b 100644 (file)
@@ -16,7 +16,9 @@ else()
 
   set(LLVM_LINK_COMPONENTS
      ${LLVM_TARGETS_TO_BUILD}
-     LTO
+     Linker
+     BitWriter
+     IPO
      )
 
   add_llvm_loadable_module(LLVMgold
index 593d8eab2932cc99125b45e2e040ea80c0e6ece3..aa006b0048fc7de9a759de6058bfc56ed4fc327e 100644 (file)
@@ -20,7 +20,7 @@ EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/gold.exports
 # early so we can set up LINK_COMPONENTS before including Makefile.rules
 include $(LEVEL)/Makefile.config
 
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) LTO
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) Linker BitWriter IPO
 
 # Because off_t is used in the public API, the largefile parts are required for
 # ABI compatibility.
index afea2b1401147547273a62cc1cba300082d17b52..25a21b7a1562441cb471189aa28185eb6156a3fa 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
-#include "llvm-c/lto.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/CodeGen/Analysis.h"
 #include "llvm/CodeGen/CommandFlags.h"
-#include "llvm/LTO/LTOCodeGenerator.h"
-#include "llvm/LTO/LTOModule.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/PassManager.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetRegistry.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetLibraryInfo.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/GlobalStatus.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
 #include <list>
 #include <plugin-api.h>
 #include <system_error>
@@ -46,18 +60,18 @@ static ld_plugin_status discard_message(int level, const char *format, ...) {
   abort();
 }
 
+static ld_plugin_get_input_file get_input_file = nullptr;
+static ld_plugin_release_input_file release_input_file = nullptr;
 static ld_plugin_add_symbols add_symbols = nullptr;
 static ld_plugin_get_symbols get_symbols = nullptr;
 static ld_plugin_add_input_file add_input_file = nullptr;
 static ld_plugin_set_extra_library_path set_extra_library_path = nullptr;
 static ld_plugin_get_view get_view = nullptr;
 static ld_plugin_message message = discard_message;
-static lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
+static Reloc::Model RelocationModel = Reloc::Default;
 static std::string output_name = "";
 static std::list<claimed_file> Modules;
 static std::vector<std::string> Cleanup;
-static LTOCodeGenerator *CodeGen = nullptr;
-static StringSet<> CannotBeHidden;
 static llvm::TargetOptions TargetOpts;
 
 namespace options {
@@ -108,6 +122,11 @@ namespace options {
       }
     } else {
       // Save this option to pass to the code generator.
+      // ParseCommandLineOptions() expects argv[0] to be program name. Lazily
+      // add that.
+      if (extra.empty())
+        extra.push_back("LLVMgold");
+
       extra.push_back(opt_);
     }
   }
@@ -145,10 +164,10 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
           case LDPO_REL:  // .o
           case LDPO_DYN:  // .so
           case LDPO_PIE:  // position independent executable
-            output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+            RelocationModel = Reloc::PIC_;
             break;
           case LDPO_EXEC:  // .exe
-            output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
+            RelocationModel = Reloc::Static;
             break;
           default:
             message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
@@ -183,6 +202,12 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
         if (callback(cleanup_hook) != LDPS_OK)
           return LDPS_ERR;
       } break;
+      case LDPT_GET_INPUT_FILE:
+        get_input_file = tv->tv_u.tv_get_input_file;
+        break;
+      case LDPT_RELEASE_INPUT_FILE:
+        release_input_file = tv->tv_u.tv_release_input_file;
+        break;
       case LDPT_ADD_SYMBOLS:
         add_symbols = tv->tv_u.tv_add_symbols;
         break;
@@ -218,28 +243,15 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
   if (!RegisteredAllSymbolsRead)
     return LDPS_OK;
 
-  CodeGen = new LTOCodeGenerator();
-
-  // Pass through extra options to the code generator.
-  if (!options::extra.empty()) {
-    for (const char *Opt : options::extra)
-      CodeGen->setCodeGenDebugOptions(Opt);
+  if (!get_input_file) {
+    message(LDPL_ERROR, "get_input_file not passed to LLVMgold.");
+    return LDPS_ERR;
   }
-
-  CodeGen->parseCodeGenDebugOptions();
-  if (MAttrs.size()) {
-    std::string Attrs;
-    for (unsigned I = 0; I < MAttrs.size(); ++I) {
-      if (I > 0)
-        Attrs.append(",");
-      Attrs.append(MAttrs[I]);
-    }
-    CodeGen->setAttr(Attrs.c_str());
+  if (!release_input_file) {
+    message(LDPL_ERROR, "relesase_input_file not passed to LLVMgold.");
+    return LDPS_ERR;
   }
 
-  TargetOpts = InitTargetOptionsFromCodeGenFlags();
-  CodeGen->setTargetOptions(TargetOpts);
-
   return LDPS_OK;
 }
 
@@ -248,13 +260,16 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
 /// possible.
 static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
                                         int *claimed) {
-  const void *view;
+  LLVMContext Context;
   std::unique_ptr<MemoryBuffer> buffer;
   if (get_view) {
+    const void *view;
     if (get_view(file->handle, &view) != LDPS_OK) {
       message(LDPL_ERROR, "Failed to get a view of %s", file->name);
       return LDPS_ERR;
     }
+    buffer.reset(MemoryBuffer::getMemBuffer(
+        StringRef((char *)view, file->filesize), "", false));
   } else {
     int64_t offset = 0;
     // Gold has found what might be IR part-way inside of a file, such as
@@ -270,95 +285,89 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
       return LDPS_ERR;
     }
     buffer = std::move(BufferOrErr.get());
-    view = buffer->getBufferStart();
   }
 
-  if (!LTOModule::isBitcodeFile(view, file->filesize))
+  ErrorOr<object::IRObjectFile *> ObjOrErr =
+      object::IRObjectFile::createIRObjectFile(buffer->getMemBufferRef(),
+                                               Context);
+  std::error_code EC = ObjOrErr.getError();
+  if (EC == BitcodeError::InvalidBitcodeSignature)
     return LDPS_OK;
 
   *claimed = 1;
 
-  std::string Error;
-  LTOModule *M =
-      LTOModule::createFromBuffer(view, file->filesize, TargetOpts, Error);
-  if (!M) {
+  if (EC) {
     message(LDPL_ERROR, "LLVM gold plugin has failed to create LTO module: %s",
-            Error.c_str());
+            EC.message().c_str());
     return LDPS_ERR;
   }
+  std::unique_ptr<object::IRObjectFile> Obj(ObjOrErr.get());
 
   Modules.resize(Modules.size() + 1);
   claimed_file &cf = Modules.back();
 
-  if (!options::triple.empty())
-    M->setTargetTriple(options::triple.c_str());
-
   cf.handle = file->handle;
-  unsigned sym_count = M->getSymbolCount();
-  cf.syms.reserve(sym_count);
 
-  for (unsigned i = 0; i != sym_count; ++i) {
-    lto_symbol_attributes attrs = M->getSymbolAttributes(i);
-    if ((attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
+  for (auto &Sym : Obj->symbols()) {
+    uint32_t Symflags = Sym.getFlags();
+    if (!(Symflags & object::BasicSymbolRef::SF_Global))
+      continue;
+
+    if (Symflags & object::BasicSymbolRef::SF_FormatSpecific)
       continue;
 
     cf.syms.push_back(ld_plugin_symbol());
     ld_plugin_symbol &sym = cf.syms.back();
-    sym.name = strdup(M->getSymbolName(i));
     sym.version = nullptr;
 
-    int scope = attrs & LTO_SYMBOL_SCOPE_MASK;
-    bool CanBeHidden = scope == LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN;
-    if (!CanBeHidden)
-      CannotBeHidden.insert(sym.name);
-    switch (scope) {
-      case LTO_SYMBOL_SCOPE_HIDDEN:
+    SmallString<64> Name;
+    {
+      raw_svector_ostream OS(Name);
+      Sym.printName(OS);
+    }
+    sym.name = strdup(Name.c_str());
+
+    const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
+
+    sym.visibility = LDPV_DEFAULT;
+    if (GV) {
+      switch (GV->getVisibility()) {
+      case GlobalValue::DefaultVisibility:
+        sym.visibility = LDPV_DEFAULT;
+        break;
+      case GlobalValue::HiddenVisibility:
         sym.visibility = LDPV_HIDDEN;
         break;
-      case LTO_SYMBOL_SCOPE_PROTECTED:
+      case GlobalValue::ProtectedVisibility:
         sym.visibility = LDPV_PROTECTED;
         break;
-      case 0: // extern
-      case LTO_SYMBOL_SCOPE_DEFAULT:
-      case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN:
-        sym.visibility = LDPV_DEFAULT;
-        break;
-      default:
-        message(LDPL_ERROR, "Unknown scope attribute: %d", scope);
-        return LDPS_ERR;
+      }
     }
 
-    int definition = attrs & LTO_SYMBOL_DEFINITION_MASK;
-    sym.comdat_key = nullptr;
-    switch (definition) {
-      case LTO_SYMBOL_DEFINITION_REGULAR:
-        sym.def = LDPK_DEF;
-        break;
-      case LTO_SYMBOL_DEFINITION_UNDEFINED:
-        sym.def = LDPK_UNDEF;
-        break;
-      case LTO_SYMBOL_DEFINITION_TENTATIVE:
-        sym.def = LDPK_COMMON;
-        break;
-      case LTO_SYMBOL_DEFINITION_WEAK:
-        sym.comdat_key = sym.name;
-        sym.def = LDPK_WEAKDEF;
-        break;
-      case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
+    if (Symflags & object::BasicSymbolRef::SF_Undefined) {
+      sym.def = LDPK_UNDEF;
+      if (GV && GV->hasExternalWeakLinkage())
         sym.def = LDPK_WEAKUNDEF;
-        break;
-      default:
-        message(LDPL_ERROR, "Unknown definition attribute: %d", definition);
-        return LDPS_ERR;
+    } else {
+      sym.def = LDPK_DEF;
+      if (GV) {
+        assert(!GV->hasExternalWeakLinkage() &&
+               !GV->hasAvailableExternallyLinkage() && "Not a declaration!");
+        if (GV->hasCommonLinkage())
+          sym.def = LDPK_COMMON;
+        else if (GV->isWeakForLinker())
+          sym.def = LDPK_WEAKDEF;
+      }
     }
 
     sym.size = 0;
+    sym.comdat_key = nullptr;
+    if (GV && (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage()))
+      sym.comdat_key = sym.name;
 
     sym.resolution = LDPR_UNKNOWN;
   }
 
-  cf.syms.reserve(cf.syms.size());
-
   if (!cf.syms.empty()) {
     if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) {
       message(LDPL_ERROR, "Unable to add symbols!");
@@ -366,51 +375,311 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
     }
   }
 
-  if (CodeGen) {
-    std::string Error;
-    if (!CodeGen->addModule(M, Error)) {
-      message(LDPL_ERROR, "Error linking module: %s", Error.c_str());
-      return LDPS_ERR;
+  return LDPS_OK;
+}
+
+static void keepGlobalValue(GlobalValue &GV) {
+  assert(!GV.hasLocalLinkage());
+
+  switch (GV.getLinkage()) {
+  default:
+    break;
+  case GlobalValue::LinkOnceAnyLinkage:
+    GV.setLinkage(GlobalValue::WeakAnyLinkage);
+    break;
+  case GlobalValue::LinkOnceODRLinkage:
+    GV.setLinkage(GlobalValue::WeakODRLinkage);
+    break;
+  }
+
+  assert(!GV.isDiscardableIfUnused());
+}
+
+static bool isDeclaration(const GlobalValue &V) {
+  if (V.hasAvailableExternallyLinkage())
+    return true;
+
+  if (V.isMaterializable())
+    return false;
+
+  return V.isDeclaration();
+}
+
+static void internalize(GlobalValue &GV) {
+  if (isDeclaration(GV))
+    return; // We get here if there is a matching asm definition.
+  if (!GV.hasLocalLinkage())
+    GV.setLinkage(GlobalValue::InternalLinkage);
+}
+
+static void drop(GlobalValue &GV) {
+  if (auto *F = dyn_cast<Function>(&GV)) {
+    F->deleteBody();
+    return;
+  }
+
+  if (auto *Var = dyn_cast<GlobalVariable>(&GV)) {
+    Var->setInitializer(nullptr);
+    Var->setLinkage(GlobalValue::ExternalLinkage);
+    return;
+  }
+
+  auto &Alias = cast<GlobalAlias>(GV);
+  Module &M = *Alias.getParent();
+  PointerType &Ty = *cast<PointerType>(Alias.getType());
+  GlobalValue::LinkageTypes L = Alias.getLinkage();
+  auto *Var =
+      new GlobalVariable(M, Ty.getElementType(), /*isConstant*/ false, L,
+                         /*Initializer*/ nullptr);
+  Var->takeName(&Alias);
+  Alias.replaceAllUsesWith(Var);
+}
+
+static const char *getResolutionName(ld_plugin_symbol_resolution R) {
+  switch (R) {
+  case LDPR_UNKNOWN:
+    return "UNKNOWN";
+  case LDPR_UNDEF:
+    return "UNDEF";
+  case LDPR_PREVAILING_DEF:
+    return "PREVAILING_DEF";
+  case LDPR_PREVAILING_DEF_IRONLY:
+    return "PREVAILING_DEF_IRONLY";
+  case LDPR_PREEMPTED_REG:
+    return "PREEMPTED_REG";
+  case LDPR_PREEMPTED_IR:
+    return "PREEMPTED_IR";
+  case LDPR_RESOLVED_IR:
+    return "RESOLVED_IR";
+  case LDPR_RESOLVED_EXEC:
+    return "RESOLVED_EXEC";
+  case LDPR_RESOLVED_DYN:
+    return "RESOLVED_DYN";
+  case LDPR_PREVAILING_DEF_IRONLY_EXP:
+    return "PREVAILING_DEF_IRONLY_EXP";
+  }
+}
+
+static std::unique_ptr<Module>
+getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
+                 StringSet<> &Internalize, StringSet<> &Maybe) {
+  ld_plugin_input_file File;
+  if (get_input_file(F.handle, &File) != LDPS_OK)
+    message(LDPL_FATAL, "Failed to get file information");
+
+  if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
+    message(LDPL_FATAL, "Failed to get symbol information");
+
+  const void *View;
+  if (get_view(F.handle, &View) != LDPS_OK)
+    message(LDPL_FATAL, "Failed to get a view of file");
+
+  std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer(
+      StringRef((char *)View, File.filesize), "", false));
+
+  if (release_input_file(F.handle) != LDPS_OK)
+    message(LDPL_FATAL, "Failed to release file information");
+
+  ErrorOr<Module *> MOrErr = getLazyBitcodeModule(Buffer.get(), Context);
+
+  if (std::error_code EC = MOrErr.getError())
+    message(LDPL_FATAL, "Could not read bitcode from file : %s",
+            EC.message().c_str());
+  Buffer.release();
+
+  std::unique_ptr<Module> M(MOrErr.get());
+
+  SmallPtrSet<GlobalValue *, 8> Used;
+  collectUsedGlobalVariables(*M, Used, /*CompilerUsed*/ false);
+
+  std::vector<GlobalValue *> Drop;
+  for (ld_plugin_symbol &Sym : F.syms) {
+    ld_plugin_symbol_resolution Resolution =
+        (ld_plugin_symbol_resolution)Sym.resolution;
+
+    if (options::generate_api_file)
+      *ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n';
+
+    GlobalValue *GV = M->getNamedValue(Sym.name);
+    if (!GV)
+      continue; // Asm symbol.
+
+    switch (Resolution) {
+    case LDPR_UNKNOWN:
+      llvm_unreachable("Unexpected resolution");
+
+    case LDPR_RESOLVED_IR:
+    case LDPR_RESOLVED_EXEC:
+    case LDPR_RESOLVED_DYN:
+    case LDPR_UNDEF:
+      assert(isDeclaration(*GV));
+      break;
+
+    case LDPR_PREVAILING_DEF_IRONLY: {
+      if (!Used.count(GV)) {
+        // Since we use the regular lib/Linker, we cannot just internalize GV
+        // now or it will not be copied to the merged module. Instead we force
+        // it to be copied and then internalize it.
+        keepGlobalValue(*GV);
+        Internalize.insert(Sym.name);
+      }
+      break;
     }
+
+    case LDPR_PREVAILING_DEF:
+      keepGlobalValue(*GV);
+      break;
+
+    case LDPR_PREEMPTED_REG:
+    case LDPR_PREEMPTED_IR:
+      Drop.push_back(GV);
+      break;
+
+    case LDPR_PREVAILING_DEF_IRONLY_EXP: {
+      // We can only check for address uses after we merge the modules. The
+      // reason is that this GV might have a copy in another module
+      // and in that module the address might be significant, but that
+      // copy will be LDPR_PREEMPTED_IR.
+      if (GV->hasLinkOnceODRLinkage())
+        Maybe.insert(Sym.name);
+      keepGlobalValue(*GV);
+      break;
+    }
+    }
+
+    free(Sym.name);
+    Sym.name = nullptr;
+    Sym.comdat_key = nullptr;
   }
 
-  delete M;
+  if (!Drop.empty()) {
+    // This is horrible. Given how lazy loading is implemented, dropping
+    // the body while there is a materializer present doesn't work, the
+    // linker will just read the body back.
+    M->materializeAllPermanently();
+    for (auto *GV : Drop)
+      drop(*GV);
+  }
 
-  return LDPS_OK;
+  return M;
 }
 
-static bool mustPreserve(ld_plugin_symbol &Sym) {
-  if (Sym.resolution == LDPR_PREVAILING_DEF)
-    return true;
-  if (Sym.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
-    return CannotBeHidden.count(Sym.name);
-  return false;
+static void runLTOPasses(Module &M, TargetMachine &TM) {
+  PassManager passes;
+  PassManagerBuilder PMB;
+  PMB.LibraryInfo = new TargetLibraryInfo(Triple(TM.getTargetTriple()));
+  PMB.Inliner = createFunctionInliningPass();
+  PMB.VerifyInput = true;
+  PMB.VerifyOutput = true;
+  PMB.populateLTOPassManager(passes, &TM);
+  passes.run(M);
+}
+
+static void codegen(Module &M) {
+  const std::string &TripleStr = M.getTargetTriple();
+  Triple TheTriple(TripleStr);
+
+  std::string ErrMsg;
+  const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
+  if (!TheTarget)
+    message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str());
+
+  if (unsigned NumOpts = options::extra.size())
+    cl::ParseCommandLineOptions(NumOpts, &options::extra[0]);
+
+  SubtargetFeatures Features;
+  Features.getDefaultSubtargetFeatures(TheTriple);
+  for (const std::string &A : MAttrs)
+    Features.AddFeature(A);
+
+  TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
+  std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(
+      TripleStr, options::mcpu, Features.getString(), Options, RelocationModel,
+      CodeModel::Default, CodeGenOpt::Aggressive));
+
+  runLTOPasses(M, *TM);
+
+  PassManager CodeGenPasses;
+  CodeGenPasses.add(new DataLayoutPass(&M));
+
+  SmallString<128> Filename;
+  int FD;
+  if (options::obj_path.empty()) {
+    std::error_code EC =
+        sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filename);
+    if (EC)
+      message(LDPL_FATAL, "Could not create temorary file: %s",
+              EC.message().c_str());
+  } else {
+    Filename = options::obj_path;
+    std::error_code EC =
+        sys::fs::openFileForWrite(Filename.c_str(), FD, sys::fs::F_None);
+    if (EC)
+      message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
+  }
+
+  {
+    raw_fd_ostream OS(FD, true);
+    formatted_raw_ostream FOS(OS);
+
+    if (TM->addPassesToEmitFile(CodeGenPasses, FOS,
+                                TargetMachine::CGFT_ObjectFile))
+      message(LDPL_FATAL, "Failed to setup codegen");
+    CodeGenPasses.run(M);
+  }
+
+  if (add_input_file(Filename.c_str()) != LDPS_OK)
+    message(LDPL_FATAL,
+            "Unable to add .o file to the link. File left behind in: %s",
+            Filename.c_str());
+
+  if (options::obj_path.empty())
+    Cleanup.push_back(Filename.c_str());
 }
 
 /// gold informs us that all symbols have been read. At this point, we use
 /// get_symbols to see if any of our definitions have been overridden by a
 /// native object file. Then, perform optimization and codegen.
-static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *apiFile) {
-  assert(CodeGen);
+static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
+  if (Modules.empty())
+    return LDPS_OK;
 
-  for (claimed_file &F : Modules) {
-    if (F.syms.empty())
-      continue;
-    get_symbols(F.handle, F.syms.size(), &F.syms[0]);
-    for (ld_plugin_symbol &Sym : F.syms) {
-      if (mustPreserve(Sym)) {
-        CodeGen->addMustPreserveSymbol(Sym.name);
+  LLVMContext Context;
+  std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context));
+  Linker L(Combined.get());
 
-        if (options::generate_api_file)
-          (*apiFile) << Sym.name << "\n";
-      }
+  std::string DefaultTriple = sys::getDefaultTargetTriple();
+
+  StringSet<> Internalize;
+  StringSet<> Maybe;
+  for (claimed_file &F : Modules) {
+    std::unique_ptr<Module> M =
+        getModuleForFile(Context, F, ApiFile, Internalize, Maybe);
+    if (!options::triple.empty())
+      M->setTargetTriple(options::triple.c_str());
+    else if (M->getTargetTriple().empty()) {
+      M->setTargetTriple(DefaultTriple);
     }
+
+    std::string ErrMsg;
+    if (L.linkInModule(M.get(), &ErrMsg))
+      message(LDPL_FATAL, "Failed to link module: %s", ErrMsg.c_str());
+  }
+
+  for (const auto &Name : Internalize) {
+    GlobalValue *GV = Combined->getNamedValue(Name.first());
+    if (GV)
+      internalize(*GV);
   }
 
-  CodeGen->setCodePICModel(output_type);
-  CodeGen->setDebugInfo(LTO_DEBUG_MODEL_DWARF);
-  if (!options::mcpu.empty())
-    CodeGen->setCpu(options::mcpu.c_str());
+  for (const auto &Name : Maybe) {
+    GlobalValue *GV = Combined->getNamedValue(Name.first());
+    if (!GV)
+      continue;
+    GV->setLinkage(GlobalValue::LinkOnceODRLinkage);
+    if (canBeOmittedFromSymbolTable(GV))
+      internalize(*GV);
+  }
 
   if (options::generate_bc_file != options::BC_NO) {
     std::string path;
@@ -420,42 +689,22 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *apiFile) {
       path = options::bc_path;
     else
       path = output_name + ".bc";
-    std::string Error;
-    if (!CodeGen->writeMergedModules(path.c_str(), Error))
-      message(LDPL_FATAL, "Failed to write the output file.");
+    {
+      std::string Error;
+      raw_fd_ostream OS(path.c_str(), Error, sys::fs::OpenFlags::F_None);
+      if (!Error.empty())
+        message(LDPL_FATAL, "Failed to write the output file.");
+      WriteBitcodeToFile(L.getModule(), OS);
+    }
     if (options::generate_bc_file == options::BC_ONLY)
       return LDPS_OK;
   }
 
-  std::string ObjPath;
-  {
-    const char *Temp;
-    std::string Error;
-    if (!CodeGen->compile_to_file(&Temp, /*DisableOpt*/ false, /*DisableInline*/
-                                  false, /*DisableGVNLoadPRE*/ false, Error))
-      message(LDPL_ERROR, "Could not produce a combined object file\n");
-    ObjPath = Temp;
-  }
-
-  for (claimed_file &F : Modules) {
-    for (ld_plugin_symbol &Sym : F.syms)
-      free(Sym.name);
-  }
-
-  if (add_input_file(ObjPath.c_str()) != LDPS_OK) {
-    message(LDPL_ERROR, "Unable to add .o file to the link.");
-    message(LDPL_ERROR, "File left behind in: %s", ObjPath.c_str());
-    return LDPS_ERR;
-  }
+  codegen(*L.getModule());
 
   if (!options::extra_library_path.empty() &&
-      set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) {
-    message(LDPL_ERROR, "Unable to set the extra library path.");
-    return LDPS_ERR;
-  }
-
-  if (options::obj_path.empty())
-    Cleanup.push_back(ObjPath);
+      set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK)
+    message(LDPL_FATAL, "Unable to set the extra library path.");
 
   return LDPS_OK;
 }
@@ -466,15 +715,13 @@ static ld_plugin_status all_symbols_read_hook(void) {
     Ret = allSymbolsReadHook(nullptr);
   } else {
     std::string Error;
-    raw_fd_ostream apiFile("apifile.txt", Error, sys::fs::F_None);
+    raw_fd_ostream ApiFile("apifile.txt", Error, sys::fs::F_None);
     if (!Error.empty())
       message(LDPL_FATAL, "Unable to open apifile.txt for writing: %s",
               Error.c_str());
-    Ret = allSymbolsReadHook(&apiFile);
+    Ret = allSymbolsReadHook(&ApiFile);
   }
 
-  delete CodeGen;
-
   if (options::generate_bc_file == options::BC_ONLY)
     exit(0);