summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/test/tools/gold/X86/Inputs/thinlto_archive1.ll4
-rw-r--r--llvm/test/tools/gold/X86/Inputs/thinlto_archive2.ll4
-rw-r--r--llvm/test/tools/gold/X86/thinlto_archive.ll29
-rw-r--r--llvm/tools/gold/CMakeLists.txt1
-rw-r--r--llvm/tools/gold/gold-plugin.cpp157
5 files changed, 142 insertions, 53 deletions
diff --git a/llvm/test/tools/gold/X86/Inputs/thinlto_archive1.ll b/llvm/test/tools/gold/X86/Inputs/thinlto_archive1.ll
new file mode 100644
index 00000000000..4e0840f3691
--- /dev/null
+++ b/llvm/test/tools/gold/X86/Inputs/thinlto_archive1.ll
@@ -0,0 +1,4 @@
+define void @g() {
+entry:
+ ret void
+}
diff --git a/llvm/test/tools/gold/X86/Inputs/thinlto_archive2.ll b/llvm/test/tools/gold/X86/Inputs/thinlto_archive2.ll
new file mode 100644
index 00000000000..a95d912ea5c
--- /dev/null
+++ b/llvm/test/tools/gold/X86/Inputs/thinlto_archive2.ll
@@ -0,0 +1,4 @@
+define void @h() {
+entry:
+ ret void
+}
diff --git a/llvm/test/tools/gold/X86/thinlto_archive.ll b/llvm/test/tools/gold/X86/thinlto_archive.ll
new file mode 100644
index 00000000000..d3f0fceb163
--- /dev/null
+++ b/llvm/test/tools/gold/X86/thinlto_archive.ll
@@ -0,0 +1,29 @@
+; Generate summary sections
+; RUN: opt -module-summary %s -o %t.o
+; RUN: opt -module-summary %p/Inputs/thinlto_archive1.ll -o %t2.o
+; RUN: opt -module-summary %p/Inputs/thinlto_archive2.ll -o %t3.o
+
+; Generate the static library
+; RUN: llvm-ar r %t.a %t2.o %t3.o
+
+; Test importing from archive library via gold, using jobs=1 to ensure
+; output messages are not interleaved.
+; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
+; RUN: --plugin-opt=thinlto \
+; RUN: --plugin-opt=-print-imports \
+; RUN: --plugin-opt=jobs=1 \
+; RUN: -shared %t.o %t.a -o %t4 2>&1 | FileCheck %s
+; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
+
+; CHECK-DAG: Import g
+declare void @g(...)
+; CHECK-DAG: Import h
+declare void @h(...)
+
+; NM: T f
+define void @f() {
+entry:
+ call void (...) @g()
+ call void (...) @h()
+ ret void
+}
diff --git a/llvm/tools/gold/CMakeLists.txt b/llvm/tools/gold/CMakeLists.txt
index 1a6169d65c2..e9029e1e7c1 100644
--- a/llvm/tools/gold/CMakeLists.txt
+++ b/llvm/tools/gold/CMakeLists.txt
@@ -10,6 +10,7 @@ if( LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR )
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Linker
+ LTO
BitWriter
IPO
)
diff --git a/llvm/tools/gold/gold-plugin.cpp b/llvm/tools/gold/gold-plugin.cpp
index a72ae077cd7..7fa1730453a 100644
--- a/llvm/tools/gold/gold-plugin.cpp
+++ b/llvm/tools/gold/gold-plugin.cpp
@@ -28,6 +28,7 @@
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Object/IRObjectFile.h"
@@ -73,7 +74,10 @@ static ld_plugin_message message = discard_message;
namespace {
struct claimed_file {
void *handle;
+ void *leader_handle;
std::vector<ld_plugin_symbol> syms;
+ off_t filesize;
+ std::string name;
};
/// RAII wrapper to manage opening and releasing of a ld_plugin_input_file.
@@ -113,9 +117,6 @@ struct ResolutionInfo {
/// Class to own information used by a task or during its cleanup for a
/// ThinLTO backend instantiation.
class ThinLTOTaskInfo {
- /// The input file holding the module bitcode read by the ThinLTO task.
- PluginInputFile InputFile;
-
/// The output stream the task will codegen into.
std::unique_ptr<raw_fd_ostream> OS;
@@ -127,10 +128,9 @@ class ThinLTOTaskInfo {
bool TempOutFile;
public:
- ThinLTOTaskInfo(PluginInputFile InputFile, std::unique_ptr<raw_fd_ostream> OS,
- std::string Filename, bool TempOutFile)
- : InputFile(std::move(InputFile)), OS(std::move(OS)), Filename(Filename),
- TempOutFile(TempOutFile) {}
+ ThinLTOTaskInfo(std::unique_ptr<raw_fd_ostream> OS, std::string Filename,
+ bool TempOutFile)
+ : OS(std::move(OS)), Filename(Filename), TempOutFile(TempOutFile) {}
/// Performs task related cleanup activities that must be done
/// single-threaded (i.e. call backs to gold).
@@ -146,6 +146,7 @@ static ld_plugin_get_view get_view = nullptr;
static Optional<Reloc::Model> RelocationModel;
static std::string output_name = "";
static std::list<claimed_file> Modules;
+static DenseMap<int, void *> FDToLeaderHandle;
static StringMap<ResolutionInfo> ResInfo;
static std::vector<std::string> Cleanup;
static llvm::TargetOptions TargetOpts;
@@ -503,6 +504,23 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
claimed_file &cf = Modules.back();
cf.handle = file->handle;
+ // Keep track of the first handle for each file descriptor, since there are
+ // multiple in the case of an archive. This is used later in the case of
+ // ThinLTO parallel backends to ensure that each file is only opened and
+ // released once.
+ auto LeaderHandle =
+ FDToLeaderHandle.insert(std::make_pair(file->fd, file->handle)).first;
+ cf.leader_handle = LeaderHandle->second;
+ // Save the filesize since for parallel ThinLTO backends we can only
+ // invoke get_input_file once per archive (only for the leader handle).
+ cf.filesize = file->filesize;
+ // In the case of an archive library, all but the first member must have a
+ // non-zero offset, which we can append to the file name to obtain a
+ // unique name.
+ cf.name = file->name;
+ if (file->offset)
+ cf.name += ".llvm." + std::to_string(file->offset) + "." +
+ sys::path::filename(Obj->getModule().getSourceFileName()).str();
// If we are doing ThinLTO compilation, don't need to process the symbols.
// Later we simply build a combined index file after all files are claimed.
@@ -643,13 +661,12 @@ static const void *getSymbolsAndView(claimed_file &F) {
}
static std::unique_ptr<ModuleSummaryIndex>
-getModuleSummaryIndexForFile(claimed_file &F, ld_plugin_input_file &Info) {
+getModuleSummaryIndexForFile(claimed_file &F) {
const void *View = getSymbolsAndView(F);
if (!View)
return nullptr;
- MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
- Info.name);
+ MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), F.name);
// Don't bother trying to build an index if there is no summary information
// in this bitcode file.
@@ -671,14 +688,11 @@ getModuleSummaryIndexForFile(claimed_file &F, ld_plugin_input_file &Info) {
return Obj.takeIndex();
}
-static std::unique_ptr<Module>
-getModuleForFile(LLVMContext &Context, claimed_file &F, const void *View,
- ld_plugin_input_file &Info, raw_fd_ostream *ApiFile,
- StringSet<> &Internalize, StringSet<> &Maybe,
- std::vector<GlobalValue *> &Keep,
- StringMap<unsigned> &Realign) {
- MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
- Info.name);
+static std::unique_ptr<Module> getModuleForFile(
+ LLVMContext &Context, claimed_file &F, const void *View, StringRef Name,
+ raw_fd_ostream *ApiFile, StringSet<> &Internalize, StringSet<> &Maybe,
+ std::vector<GlobalValue *> &Keep, StringMap<unsigned> &Realign) {
+ MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), Name);
ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr =
object::IRObjectFile::create(BufferRef, Context);
@@ -874,17 +888,23 @@ class CodeGen {
/// a unique and identifiable save-temps output file for each ThinLTO backend.
std::string SaveTempsFilename;
+ /// Map from a module name to the corresponding buffer holding a view of the
+ /// bitcode provided via the get_view gold callback.
+ StringMap<MemoryBufferRef> *ModuleMap;
+
public:
/// Constructor used by full LTO.
CodeGen(std::unique_ptr<llvm::Module> M)
- : M(std::move(M)), OS(nullptr), TaskID(-1), CombinedIndex(nullptr) {
+ : M(std::move(M)), OS(nullptr), TaskID(-1), CombinedIndex(nullptr),
+ ModuleMap(nullptr) {
initTargetMachine();
}
/// Constructor used by ThinLTO.
CodeGen(std::unique_ptr<llvm::Module> M, raw_fd_ostream *OS, int TaskID,
- const ModuleSummaryIndex *CombinedIndex, std::string Filename)
+ const ModuleSummaryIndex *CombinedIndex, std::string Filename,
+ StringMap<MemoryBufferRef> *ModuleMap)
: M(std::move(M)), OS(OS), TaskID(TaskID), CombinedIndex(CombinedIndex),
- SaveTempsFilename(Filename) {
+ SaveTempsFilename(Filename), ModuleMap(ModuleMap) {
assert(options::thinlto == !!CombinedIndex &&
"Expected module summary index iff performing ThinLTO");
initTargetMachine();
@@ -968,6 +988,21 @@ std::unique_ptr<TargetMachine> CodeGen::createTargetMachine() {
void CodeGen::runLTOPasses() {
M->setDataLayout(TM->createDataLayout());
+ if (CombinedIndex) {
+ // First collect the import list.
+ FunctionImporter::ImportMapTy ImportList;
+ ComputeCrossModuleImportForModule(M->getModuleIdentifier(), *CombinedIndex,
+ ImportList);
+
+ // Create a loader that will parse the bitcode from the buffers
+ // in the ModuleMap.
+ ModuleLoader Loader(M->getContext(), *ModuleMap);
+
+ // Perform function importing.
+ FunctionImporter Importer(*CombinedIndex, Loader);
+ Importer.importFunctions(*M, ImportList);
+ }
+
legacy::PassManager passes;
passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
@@ -981,7 +1016,6 @@ void CodeGen::runLTOPasses() {
PMB.LoopVectorize = true;
PMB.SLPVectorize = true;
PMB.OptLevel = options::OptLevel;
- PMB.ModuleSummary = CombinedIndex;
if (options::thinlto)
PMB.populateThinLTOPassManager(passes);
else
@@ -1099,13 +1133,13 @@ void CodeGen::runAll() {
/// Links the module in \p View from file \p F into the combined module
/// saved in the IRMover \p L. Returns true on error, false on success.
static bool linkInModule(LLVMContext &Context, IRMover &L, claimed_file &F,
- const void *View, ld_plugin_input_file &File,
+ const void *View, StringRef Name,
raw_fd_ostream *ApiFile, StringSet<> &Internalize,
StringSet<> &Maybe) {
std::vector<GlobalValue *> Keep;
StringMap<unsigned> Realign;
std::unique_ptr<Module> M = getModuleForFile(
- Context, F, View, File, ApiFile, Internalize, Maybe, Keep, Realign);
+ Context, F, View, Name, ApiFile, Internalize, Maybe, Keep, Realign);
if (!M.get())
return false;
if (!options::triple.empty())
@@ -1130,10 +1164,10 @@ static bool linkInModule(LLVMContext &Context, IRMover &L, claimed_file &F,
/// Perform the ThinLTO backend on a single module, invoking the LTO and codegen
/// pipelines.
static void thinLTOBackendTask(claimed_file &F, const void *View,
- ld_plugin_input_file &File,
- raw_fd_ostream *ApiFile,
+ StringRef Name, raw_fd_ostream *ApiFile,
const ModuleSummaryIndex &CombinedIndex,
- raw_fd_ostream *OS, unsigned TaskID) {
+ raw_fd_ostream *OS, unsigned TaskID,
+ StringMap<MemoryBufferRef> &ModuleMap) {
// Need to use a separate context for each task
LLVMContext Context;
Context.setDiscardValueNames(options::TheOutputType !=
@@ -1141,22 +1175,24 @@ static void thinLTOBackendTask(claimed_file &F, const void *View,
Context.enableDebugTypeODRUniquing(); // Merge debug info types.
Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true);
- std::unique_ptr<llvm::Module> NewModule(new llvm::Module(File.name, Context));
+ std::unique_ptr<llvm::Module> NewModule(new llvm::Module(Name, Context));
IRMover L(*NewModule.get());
StringSet<> Dummy;
- if (linkInModule(Context, L, F, View, File, ApiFile, Dummy, Dummy))
+ if (linkInModule(Context, L, F, View, Name, ApiFile, Dummy, Dummy))
message(LDPL_FATAL, "Failed to rename module for ThinLTO");
if (renameModuleForThinLTO(*NewModule, CombinedIndex))
message(LDPL_FATAL, "Failed to rename module for ThinLTO");
- CodeGen codeGen(std::move(NewModule), OS, TaskID, &CombinedIndex, File.name);
+ CodeGen codeGen(std::move(NewModule), OS, TaskID, &CombinedIndex, Name,
+ &ModuleMap);
codeGen.runAll();
}
/// Launch each module's backend pipeline in a separate task in a thread pool.
static void thinLTOBackends(raw_fd_ostream *ApiFile,
- const ModuleSummaryIndex &CombinedIndex) {
+ const ModuleSummaryIndex &CombinedIndex,
+ StringMap<MemoryBufferRef> &ModuleMap) {
unsigned TaskCount = 0;
std::vector<ThinLTOTaskInfo> Tasks;
Tasks.reserve(Modules.size());
@@ -1171,7 +1207,6 @@ static void thinLTOBackends(raw_fd_ostream *ApiFile,
for (claimed_file &F : Modules) {
// Do all the gold callbacks in the main thread, since gold is not thread
// safe by default.
- PluginInputFile InputFile(F.handle);
const void *View = getSymbolsAndView(F);
if (!View)
continue;
@@ -1183,7 +1218,7 @@ static void thinLTOBackends(raw_fd_ostream *ApiFile,
else if (options::TheOutputType == options::OT_SAVE_TEMPS) {
// Use the input file name so that we get a unique and identifiable
// output file for each ThinLTO backend task.
- Filename = InputFile.file().name;
+ Filename = F.name;
Filename += ".thinlto.o";
}
bool TempOutFile = Filename.empty();
@@ -1198,15 +1233,14 @@ static void thinLTOBackends(raw_fd_ostream *ApiFile,
llvm::make_unique<raw_fd_ostream>(FD, true);
// Enqueue the task
- ThinLTOThreadPool.async(thinLTOBackendTask, std::ref(F), View,
- std::ref(InputFile.file()), ApiFile,
- std::ref(CombinedIndex), OS.get(), TaskCount);
+ ThinLTOThreadPool.async(thinLTOBackendTask, std::ref(F), View, F.name,
+ ApiFile, std::ref(CombinedIndex), OS.get(),
+ TaskCount, std::ref(ModuleMap));
// Record the information needed by the task or during its cleanup
// to a ThinLTOTaskInfo instance. For information needed by the task
// the unique_ptr ownership is transferred to the ThinLTOTaskInfo.
- Tasks.emplace_back(std::move(InputFile), std::move(OS),
- NewFilename.c_str(), TempOutFile);
+ Tasks.emplace_back(std::move(OS), NewFilename.c_str(), TempOutFile);
}
}
@@ -1249,13 +1283,32 @@ static std::string getThinLTOOutputFile(const std::string &Path,
/// Also, either launch backend threads or (under thinlto-index-only)
/// emit individual index files for distributed backends and exit.
static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) {
+ // Map from a module name to the corresponding buffer holding a view of the
+ // bitcode provided via the get_view gold callback.
+ StringMap<MemoryBufferRef> ModuleMap;
+ // Map to own RAII objects that manage the file opening and releasing
+ // interfaces with gold.
+ DenseMap<void *, std::unique_ptr<PluginInputFile>> HandleToInputFile;
+
ModuleSummaryIndex CombinedIndex;
uint64_t NextModuleId = 0;
for (claimed_file &F : Modules) {
- PluginInputFile InputFile(F.handle);
+ if (!HandleToInputFile.count(F.leader_handle))
+ HandleToInputFile.insert(std::make_pair(
+ F.leader_handle, llvm::make_unique<PluginInputFile>(F.handle)));
+ // Pass this into getModuleSummaryIndexForFile
+ const void *View = getSymbolsAndView(F);
+ if (!View)
+ continue;
+
+ MemoryBufferRef ModuleBuffer(StringRef((const char *)View, F.filesize),
+ F.name);
+ assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) ==
+ ModuleMap.end() &&
+ "Expect unique Buffer Identifier");
+ ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer;
- std::unique_ptr<ModuleSummaryIndex> Index =
- getModuleSummaryIndexForFile(F, InputFile.file());
+ std::unique_ptr<ModuleSummaryIndex> Index = getModuleSummaryIndexForFile(F);
// Skip files without a module summary.
if (Index)
@@ -1292,11 +1345,10 @@ static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) {
// contains summaries only for its own global values, and for any that
// should be imported.
for (claimed_file &F : Modules) {
- PluginInputFile InputFile(F.handle);
std::error_code EC;
std::string NewModulePath =
- getThinLTOOutputFile(InputFile.file().name, OldPrefix, NewPrefix);
+ getThinLTOOutputFile(F.name, OldPrefix, NewPrefix);
raw_fd_ostream OS((Twine(NewModulePath) + ".thinlto.bc").str(), EC,
sys::fs::OpenFlags::F_None);
if (EC)
@@ -1305,16 +1357,14 @@ static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) {
// Build a map of module to the GUIDs and summary objects that should
// be written to its index.
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
- gatherImportedSummariesForModule(InputFile.file().name,
- ModuleToDefinedGVSummaries, ImportLists,
- ModuleToSummariesForIndex);
+ gatherImportedSummariesForModule(F.name, ModuleToDefinedGVSummaries,
+ ImportLists, ModuleToSummariesForIndex);
WriteIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
if (options::thinlto_emit_imports_files) {
- if ((EC = EmitImportsFiles(
- InputFile.file().name,
- (Twine(NewModulePath) + ".imports").str(),
- ImportLists)))
+ if ((EC = EmitImportsFiles(F.name,
+ (Twine(NewModulePath) + ".imports").str(),
+ ImportLists)))
message(LDPL_FATAL, "Unable to open %s.imports",
NewModulePath.c_str(), EC.message().c_str());
}
@@ -1335,7 +1385,7 @@ static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) {
WriteIndexToFile(CombinedIndex, OS);
}
- thinLTOBackends(ApiFile, CombinedIndex);
+ thinLTOBackends(ApiFile, CombinedIndex, ModuleMap);
return LDPS_OK;
}
@@ -1364,12 +1414,13 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
StringSet<> Internalize;
StringSet<> Maybe;
for (claimed_file &F : Modules) {
+ // RAII object to manage the file opening and releasing interfaces with
+ // gold.
PluginInputFile InputFile(F.handle);
const void *View = getSymbolsAndView(F);
if (!View)
continue;
- if (linkInModule(Context, L, F, View, InputFile.file(), ApiFile,
- Internalize, Maybe))
+ if (linkInModule(Context, L, F, View, F.name, ApiFile, Internalize, Maybe))
message(LDPL_FATAL, "Failed to link module");
}
OpenPOWER on IntegriCloud