diff --git a/clang/include/clang/CodeGen/BackendUtil.h b/clang/include/clang/CodeGen/BackendUtil.h --- a/clang/include/clang/CodeGen/BackendUtil.h +++ b/clang/include/clang/CodeGen/BackendUtil.h @@ -45,11 +45,6 @@ void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts, llvm::MemoryBufferRef Buf); - - llvm::Expected - FindThinLTOModule(llvm::MemoryBufferRef MBRef); - llvm::BitcodeModule * - FindThinLTOModule(llvm::MutableArrayRef BMs); } #endif diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1497,29 +1497,6 @@ DwoOS->keep(); } -Expected clang::FindThinLTOModule(MemoryBufferRef MBRef) { - Expected> BMsOrErr = getBitcodeModuleList(MBRef); - if (!BMsOrErr) - return BMsOrErr.takeError(); - - // The bitcode file may contain multiple modules, we want the one that is - // marked as being the ThinLTO module. - if (const BitcodeModule *Bm = FindThinLTOModule(*BMsOrErr)) - return *Bm; - - return make_error("Could not find module summary", - inconvertibleErrorCode()); -} - -BitcodeModule *clang::FindThinLTOModule(MutableArrayRef BMs) { - for (BitcodeModule &BM : BMs) { - Expected LTOInfo = BM.getLTOInfo(); - if (LTOInfo && LTOInfo->IsThinLTO) - return &BM; - } - return nullptr; -} - static void runThinLTOBackend( DiagnosticsEngine &Diags, ModuleSummaryIndex *CombinedIndex, Module *M, const HeaderSearchOptions &HeaderOpts, const CodeGenOptions &CGOpts, @@ -1536,46 +1513,12 @@ // we should only invoke this using the individual indexes written out // via a WriteIndexesThinBackend. FunctionImporter::ImportMapTy ImportList; - for (auto &GlobalList : *CombinedIndex) { - // Ignore entries for undefined references. - if (GlobalList.second.SummaryList.empty()) - continue; - - auto GUID = GlobalList.first; - for (auto &Summary : GlobalList.second.SummaryList) { - // Skip the summaries for the importing module. These are included to - // e.g. record required linkage changes. - if (Summary->modulePath() == M->getModuleIdentifier()) - continue; - // Add an entry to provoke importing by thinBackend. - ImportList[Summary->modulePath()].insert(GUID); - } - } - std::vector> OwnedImports; MapVector ModuleMap; + if (!lto::loadReferencedModules(*M, *CombinedIndex, ImportList, ModuleMap, + OwnedImports)) + return; - for (auto &I : ImportList) { - ErrorOr> MBOrErr = - llvm::MemoryBuffer::getFile(I.first()); - if (!MBOrErr) { - errs() << "Error loading imported file '" << I.first() - << "': " << MBOrErr.getError().message() << "\n"; - return; - } - - Expected BMOrErr = FindThinLTOModule(**MBOrErr); - if (!BMOrErr) { - handleAllErrors(BMOrErr.takeError(), [&](ErrorInfoBase &EIB) { - errs() << "Error loading imported file '" << I.first() - << "': " << EIB.message() << '\n'; - }); - return; - } - ModuleMap.insert({I.first(), *BMOrErr}); - - OwnedImports.push_back(std::move(*MBOrErr)); - } auto AddStream = [&](size_t Task) { return std::make_unique(std::move(OS)); }; diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -35,6 +35,7 @@ #include "llvm/IR/LLVMRemarkStreamer.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" +#include "llvm/LTO/LTOBackend.h" #include "llvm/Linker/Linker.h" #include "llvm/Pass.h" #include "llvm/Support/MemoryBuffer.h" @@ -1066,7 +1067,7 @@ Expected> BMsOrErr = getBitcodeModuleList(MBRef); if (!BMsOrErr) return DiagErrors(BMsOrErr.takeError()); - BitcodeModule *Bm = FindThinLTOModule(*BMsOrErr); + BitcodeModule *Bm = llvm::lto::findThinLTOModule(*BMsOrErr); // We have nothing to do if the file contains no ThinLTO module. This is // possible if ThinLTO compilation was not able to split module. Content of // the file was already processed by indexing and will be passed to the diff --git a/clang/test/CodeGen/thinlto_embed_bitcode.ll b/clang/test/CodeGen/thinlto_embed_bitcode.ll --- a/clang/test/CodeGen/thinlto_embed_bitcode.ll +++ b/clang/test/CodeGen/thinlto_embed_bitcode.ll @@ -7,16 +7,23 @@ ; RUN: opt -module-summary %p/Inputs/start-lib2.ll -o %t2.bc ; RUN: llvm-lto -thinlto -o %t.o %t1.bc %t2.bc +; For the optimized case, we expect the inlining of foo into bar to happen. +; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t-opt.o -x ir %t1.bc -c -fthinlto-index=%t.o.thinlto.bc -mllvm -lto-embed-bitcode=optimized +; RUN: llvm-readelf -S %t-opt.o | FileCheck %s --check-prefixes=CHECK-ELF,CHECK-NO-CMD +; RUN: llvm-objcopy --dump-section=.llvmbc=%t-embedded.bc %t-opt.o /dev/null +; RUN: llvm-dis %t-embedded.bc -o - | FileCheck %s --check-prefixes=CHECK,CHECK-OPT + +; For the post-merge case, perform the embedded bitcode extraction, then +; round-trip through compilation and ensure the objects are the same. ; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t.o -x ir %t1.bc -c -fthinlto-index=%t.o.thinlto.bc -mllvm -lto-embed-bitcode=post-merge-pre-opt ; RUN: llvm-readelf -S %t.o | FileCheck %s --check-prefixes=CHECK-ELF,CHECK-CMD ; RUN: llvm-objcopy --dump-section=.llvmbc=%t-embedded.bc %t.o /dev/null ; RUN: llvm-dis %t-embedded.bc -o - | FileCheck %s --check-prefixes=CHECK,CHECK-NOOPT - -; For the optimized case, we expect the inlining of foo into bar to happen. -; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t.o -x ir %t1.bc -c -fthinlto-index=%t.o.thinlto.bc -mllvm -lto-embed-bitcode=optimized -; RUN: llvm-readelf -S %t.o | FileCheck %s --check-prefixes=CHECK-ELF,CHECK-NO-CMD -; RUN: llvm-objcopy --dump-section=.llvmbc=%t-embedded.bc %t.o /dev/null -; RUN: llvm-dis %t-embedded.bc -o - | FileCheck %s --check-prefixes=CHECK,CHECK-OPT +; We should only need the index and the post-thinlto merged module to generate +; the exact same .o as we originally did. +; RUN: rm %t1.bc %t2.bc +; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t-redo.o -x ir %t-embedded.bc -c -fthinlto-index=%t.o.thinlto.bc -mllvm -lto-embed-bitcode=post-merge-pre-opt -mllvm -thinlto-assume-merged +; RUN: diff %t-redo.o %t.o ; CHECK-ELF: .text PROGBITS 0000000000000000 [[#%x,OFF:]] [[#%x,SIZE:]] 00 AX 0 ; CHECK-ELF-NEXT: .llvmbc PROGBITS 0000000000000000 [[#%x,OFF:]] [[#%x,SIZE:]] 00 0 diff --git a/llvm/include/llvm/LTO/LTOBackend.h b/llvm/include/llvm/LTO/LTOBackend.h --- a/llvm/include/llvm/LTO/LTOBackend.h +++ b/llvm/include/llvm/LTO/LTOBackend.h @@ -49,6 +49,22 @@ Error finalizeOptimizationRemarks( std::unique_ptr DiagOutputFile); + +/// Returns the BitcodeModule that is ThinLTO. +BitcodeModule *findThinLTOModule(MutableArrayRef BMs); + +/// Variant of the above. +Expected findThinLTOModule(MemoryBufferRef MBRef); + +/// Distributed ThinlTO: load the referenced modules, keeping their buffers +/// alive in the provided OwnedImportLifetimeManager. Returns false if the +/// operation failed. +bool loadReferencedModules( + const Module &M, const ModuleSummaryIndex &CombinedIndex, + FunctionImporter::ImportMapTy &ImportList, + MapVector &ModuleMap, + std::vector> + &OwnedImportsLifetimeManager); } } diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -69,6 +69,11 @@ "Embed post merge, but before optimizations")), cl::desc("Embed LLVM bitcode in object files produced by LTO")); +static cl::opt ThinLTOAssumeMerged( + "thinlto-assume-merged", cl::init(false), + cl::desc("Assume the input has already undergone ThinLTO function " + "importing and the other pre-optimization pipeline changes.")); + LLVM_ATTRIBUTE_NORETURN static void reportOpenError(StringRef Path, Twine Msg) { errs() << "failed to open " << Path << ": " << Msg << '\n'; errs().flush(); @@ -583,6 +588,21 @@ if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod)) return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); + auto OptimizeAndCodegen = + [&](Module &Mod, TargetMachine *TM, + std::unique_ptr DiagnosticOutputFile) { + if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true, + /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex, + CmdArgs)) + return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); + + codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex); + return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); + }; + + if (ThinLTOAssumeMerged) + return OptimizeAndCodegen(Mod, TM.get(), std::move(DiagnosticOutputFile)); + // When linking an ELF shared object, dso_local should be dropped. We // conservatively do this for -fpic. bool ClearDSOLocalOnDeclarations = @@ -623,11 +643,81 @@ if (Conf.PostImportModuleHook && !Conf.PostImportModuleHook(Task, Mod)) return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); - if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLTO=*/true, - /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex, - CmdArgs)) - return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); + return OptimizeAndCodegen(Mod, TM.get(), std::move(DiagnosticOutputFile)); +} + +BitcodeModule *lto::findThinLTOModule(MutableArrayRef BMs) { + if (ThinLTOAssumeMerged && BMs.size() == 1) + return BMs.begin(); - codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex); - return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); + for (BitcodeModule &BM : BMs) { + Expected LTOInfo = BM.getLTOInfo(); + if (LTOInfo && LTOInfo->IsThinLTO) + return &BM; + } + return nullptr; } + +Expected lto::findThinLTOModule(MemoryBufferRef MBRef) { + Expected> BMsOrErr = getBitcodeModuleList(MBRef); + if (!BMsOrErr) + return BMsOrErr.takeError(); + + // The bitcode file may contain multiple modules, we want the one that is + // marked as being the ThinLTO module. + if (const BitcodeModule *Bm = lto::findThinLTOModule(*BMsOrErr)) + return *Bm; + + return make_error("Could not find module summary", + inconvertibleErrorCode()); +} + +bool lto::loadReferencedModules( + const Module &M, const ModuleSummaryIndex &CombinedIndex, + FunctionImporter::ImportMapTy &ImportList, + MapVector &ModuleMap, + std::vector> + &OwnedImportsLifetimeManager) { + if (ThinLTOAssumeMerged) + return true; + // We can simply import the values mentioned in the combined index, since + // we should only invoke this using the individual indexes written out + // via a WriteIndexesThinBackend. + for (const auto &GlobalList : CombinedIndex) { + // Ignore entries for undefined references. + if (GlobalList.second.SummaryList.empty()) + continue; + + auto GUID = GlobalList.first; + for (const auto &Summary : GlobalList.second.SummaryList) { + // Skip the summaries for the importing module. These are included to + // e.g. record required linkage changes. + if (Summary->modulePath() == M.getModuleIdentifier()) + continue; + // Add an entry to provoke importing by thinBackend. + ImportList[Summary->modulePath()].insert(GUID); + } + } + + for (auto &I : ImportList) { + ErrorOr> MBOrErr = + llvm::MemoryBuffer::getFile(I.first()); + if (!MBOrErr) { + errs() << "Error loading imported file '" << I.first() + << "': " << MBOrErr.getError().message() << "\n"; + return false; + } + + Expected BMOrErr = findThinLTOModule(**MBOrErr); + if (!BMOrErr) { + handleAllErrors(BMOrErr.takeError(), [&](ErrorInfoBase &EIB) { + errs() << "Error loading imported file '" << I.first() + << "': " << EIB.message() << '\n'; + }); + return false; + } + ModuleMap.insert({I.first(), *BMOrErr}); + OwnedImportsLifetimeManager.push_back(std::move(*MBOrErr)); + } + return true; +} \ No newline at end of file