Index: clang/include/clang/CodeGen/BackendUtil.h =================================================================== --- clang/include/clang/CodeGen/BackendUtil.h +++ clang/include/clang/CodeGen/BackendUtil.h @@ -15,6 +15,8 @@ #include namespace llvm { + class BitcodeModule; + template class Expected; class Module; class MemoryBufferRef; } @@ -44,6 +46,9 @@ void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts, llvm::MemoryBufferRef Buf); + + llvm::Expected + FindThinLTOModule(llvm::MemoryBufferRef MBRef); } #endif Index: clang/include/clang/CodeGen/CodeGenAction.h =================================================================== --- clang/include/clang/CodeGen/CodeGenAction.h +++ clang/include/clang/CodeGen/CodeGenAction.h @@ -31,6 +31,8 @@ llvm::LLVMContext *VMContext; bool OwnsVMContext; + std::unique_ptr loadModule(llvm::MemoryBufferRef MBRef); + protected: /// Create a new code generation action. If the optional \p _VMContext /// parameter is supplied, the action uses it without taking ownership, Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -864,6 +864,23 @@ } } +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 with a + // summary. + for (BitcodeModule &BM : *BMsOrErr) { + Expected HasSummary = BM.hasSummary(); + if (HasSummary && *HasSummary) + return BM; + } + + return make_error("Could not find module summary", + inconvertibleErrorCode()); +} + static void runThinLTOBackend(ModuleSummaryIndex *CombinedIndex, Module *M, std::unique_ptr OS, std::string SampleProfile) { @@ -901,32 +918,15 @@ return; } - Expected> BMsOrErr = - getBitcodeModuleList(**MBOrErr); - if (!BMsOrErr) { - handleAllErrors(BMsOrErr.takeError(), [&](ErrorInfoBase &EIB) { + Expected BMOrErr = FindThinLTOModule(**MBOrErr); + if (!BMOrErr) { + handleAllErrors(BMOrErr.takeError(), [&](ErrorInfoBase &EIB) { errs() << "Error loading imported file '" << I.first() << "': " << EIB.message() << '\n'; }); return; } - - // The bitcode file may contain multiple modules, we want the one with a - // summary. - bool FoundModule = false; - for (BitcodeModule &BM : *BMsOrErr) { - Expected HasSummary = BM.hasSummary(); - if (HasSummary && *HasSummary) { - ModuleMap.insert({I.first(), BM}); - FoundModule = true; - break; - } - } - if (!FoundModule) { - errs() << "Error loading imported file '" << I.first() - << "': Could not find module summary\n"; - return; - } + ModuleMap.insert({I.first(), *BMOrErr}); OwnedImports.push_back(std::move(*MBOrErr)); } Index: clang/lib/CodeGen/CodeGenAction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenAction.cpp +++ clang/lib/CodeGen/CodeGenAction.cpp @@ -838,6 +838,62 @@ Diags->Report(DiagID).AddString("cannot compile inline asm"); } +std::unique_ptr CodeGenAction::loadModule(MemoryBufferRef MBRef) { + CompilerInstance &CI = getCompilerInstance(); + SourceManager &SM = CI.getSourceManager(); + + // For ThinLTO backend invocations, ensure that the context + // merges types based on ODR identifiers. We also need to read + // the correct module out of a multi-module bitcode file. + if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) { + VMContext->enableDebugTypeODRUniquing(); + + auto DiagErrors = [&](Error E) -> std::unique_ptr { + unsigned DiagID = + CI.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, "%0"); + handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { + CI.getDiagnostics().Report(DiagID) << EIB.message(); + }); + return {}; + }; + + Expected BMOrErr = FindThinLTOModule(MBRef); + if (!BMOrErr) + return DiagErrors(BMOrErr.takeError()); + + Expected> MOrErr = + BMOrErr->parseModule(*VMContext); + if (!MOrErr) + return DiagErrors(MOrErr.takeError()); + return std::move(*MOrErr); + } + + llvm::SMDiagnostic Err; + if (std::unique_ptr M = parseIR(MBRef, Err, *VMContext)) + return M; + + // Translate from the diagnostic info to the SourceManager location if + // available. + // TODO: Unify this with ConvertBackendLocation() + SourceLocation Loc; + if (Err.getLineNo() > 0) { + assert(Err.getColumnNo() >= 0); + Loc = SM.translateFileLineCol(SM.getFileEntryForID(SM.getMainFileID()), + Err.getLineNo(), Err.getColumnNo() + 1); + } + + // Strip off a leading diagnostic code if there is one. + StringRef Msg = Err.getMessage(); + if (Msg.startswith("error: ")) + Msg = Msg.substr(7); + + unsigned DiagID = + CI.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, "%0"); + + CI.getDiagnostics().Report(Loc, DiagID) << Msg; + return {}; +} + void CodeGenAction::ExecuteAction() { // If this is an IR file, we have to treat it specially. if (getCurrentFileKind() == IK_LLVM_IR) { @@ -855,35 +911,10 @@ if (Invalid) return; - // For ThinLTO backend invocations, ensure that the context - // merges types based on ODR identifiers. - if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) - VMContext->enableDebugTypeODRUniquing(); - - llvm::SMDiagnostic Err; - TheModule = parseIR(MainFile->getMemBufferRef(), Err, *VMContext); - if (!TheModule) { - // Translate from the diagnostic info to the SourceManager location if - // available. - // TODO: Unify this with ConvertBackendLocation() - SourceLocation Loc; - if (Err.getLineNo() > 0) { - assert(Err.getColumnNo() >= 0); - Loc = SM.translateFileLineCol(SM.getFileEntryForID(FID), - Err.getLineNo(), Err.getColumnNo() + 1); - } - - // Strip off a leading diagnostic code if there is one. - StringRef Msg = Err.getMessage(); - if (Msg.startswith("error: ")) - Msg = Msg.substr(7); - - unsigned DiagID = - CI.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, "%0"); - - CI.getDiagnostics().Report(Loc, DiagID) << Msg; + TheModule = loadModule(*MainFile); + if (!TheModule) return; - } + const TargetOptions &TargetOpts = CI.getTargetOpts(); if (TheModule->getTargetTriple() != TargetOpts.Triple) { CI.getDiagnostics().Report(SourceLocation(), Index: clang/test/CMakeLists.txt =================================================================== --- clang/test/CMakeLists.txt +++ clang/test/CMakeLists.txt @@ -79,6 +79,7 @@ FileCheck count not llc llvm-bcanalyzer + llvm-cat llvm-dis llvm-modextract llvm-nm Index: clang/test/CodeGen/thinlto_backend.ll =================================================================== --- clang/test/CodeGen/thinlto_backend.ll +++ clang/test/CodeGen/thinlto_backend.ll @@ -2,6 +2,7 @@ ; RUN: opt -module-summary -o %t1.o %s ; RUN: opt -module-summary -o %t2.o %S/Inputs/thinlto_backend.ll +; RUN: opt -o %t2merge.o %S/Inputs/thinlto_backend.ll ; RUN: llvm-lto -thinlto -o %t %t1.o %t2.o ; Ensure clang -cc1 give expected error for incorrect input type @@ -14,12 +15,17 @@ ; Ensure we ignore empty index file under -ignore-empty-index-file, and run ; non-ThinLTO compilation which would not import f2 -; RUN: touch %t4.thinlto.bc -; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t4.o -x ir %t1.o -c -fthinlto-index=%t4.thinlto.bc -mllvm -ignore-empty-index-file +; RUN: touch %t.empty.thinlto.bc +; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t4.o -x ir %t1.o -c -fthinlto-index=%t.empty.thinlto.bc -mllvm -ignore-empty-index-file ; RUN: llvm-nm %t4.o | FileCheck --check-prefix=CHECK-OBJ-IGNORE-EMPTY %s ; CHECK-OBJ-IGNORE-EMPTY: T f1 ; CHECK-OBJ-IGNORE-EMPTY: U f2 +; Ensure we load the correct module from a multi-module bitcode file +; RUN: llvm-cat -b -o %t5.o %t1.o %t2merge.o +; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t6.o -x ir %t5.o -c -fthinlto-index=%t.empty.thinlto.bc -mllvm -ignore-empty-index-file +; RUN: llvm-nm %t5.o | FileCheck --check-prefix=CHECK-OBJ-IGNORE-EMPTY %s + ; Ensure f2 was imported ; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t3.o -x ir %t1.o -c -fthinlto-index=%t.thinlto.bc ; RUN: llvm-nm %t3.o | FileCheck --check-prefix=CHECK-OBJ %s