Index: include/llvm/CodeGen/ParallelCG.h =================================================================== --- include/llvm/CodeGen/ParallelCG.h +++ include/llvm/CodeGen/ParallelCG.h @@ -29,10 +29,14 @@ /// files if linked together are intended to be equivalent to the single output /// file that would have been code generated from M. /// +/// Writes bitcode for individual partitions into output streams in BCOSs, if +/// BCOSs is not empty. +/// /// \returns M if OSs.size() == 1, otherwise returns std::unique_ptr(). std::unique_ptr splitCodeGen(std::unique_ptr M, ArrayRef OSs, - StringRef CPU, StringRef Features, const TargetOptions &Options, + ArrayRef BCOSs, StringRef CPU, + StringRef Features, const TargetOptions &Options, Reloc::Model RM = Reloc::Default, CodeModel::Model CM = CodeModel::Default, CodeGenOpt::Level OL = CodeGenOpt::Default, Index: lib/CodeGen/ParallelCG.cpp =================================================================== --- lib/CodeGen/ParallelCG.cpp +++ lib/CodeGen/ParallelCG.cpp @@ -39,22 +39,25 @@ CodeGenPasses.run(*M); } -std::unique_ptr -llvm::splitCodeGen(std::unique_ptr M, - ArrayRef OSs, StringRef CPU, - StringRef Features, const TargetOptions &Options, - Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL, - TargetMachine::CodeGenFileType FileType, - bool PreserveLocals) { +std::unique_ptr llvm::splitCodeGen( + std::unique_ptr M, ArrayRef OSs, + ArrayRef BCOSs, StringRef CPU, + StringRef Features, const TargetOptions &Options, Reloc::Model RM, + CodeModel::Model CM, CodeGenOpt::Level OL, + TargetMachine::CodeGenFileType FileType, bool PreserveLocals) { StringRef TripleStr = M->getTargetTriple(); std::string ErrMsg; const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg); if (!TheTarget) report_fatal_error(Twine("Target not found: ") + ErrMsg); + assert(BCOSs.empty() || BCOSs.size() == OSs.size()); + if (OSs.size() == 1) { - codegen(M.get(), *OSs[0], TheTarget, CPU, Features, Options, RM, CM, - OL, FileType); + if (!BCOSs.empty()) + WriteBitcodeToFile(M.get(), *BCOSs[0]); + codegen(M.get(), *OSs[0], TheTarget, CPU, Features, Options, RM, CM, OL, + FileType); return M; } @@ -77,6 +80,11 @@ raw_svector_ostream BCOS(BC); WriteBitcodeToFile(MPart.get(), BCOS); + if (!BCOSs.empty()) { + BCOSs[ThreadCount]->write(BC.begin(), BC.size()); + BCOSs[ThreadCount]->flush(); + } + llvm::raw_pwrite_stream *ThreadOS = OSs[ThreadCount++]; // Enqueue the task CodegenThreadPool.async( Index: lib/LTO/LTOCodeGenerator.cpp =================================================================== --- lib/LTO/LTOCodeGenerator.cpp +++ lib/LTO/LTOCodeGenerator.cpp @@ -562,10 +562,9 @@ // parallelism level 1. This is achieved by having splitCodeGen return the // original module at parallelism level 1 which we then assign back to // MergedModule. - MergedModule = - splitCodeGen(std::move(MergedModule), Out, MCpu, FeatureStr, Options, - RelocModel, CodeModel::Default, CGOptLevel, FileType, - ShouldRestoreGlobalsLinkage); + MergedModule = splitCodeGen( + std::move(MergedModule), Out, {}, MCpu, FeatureStr, Options, RelocModel, + CodeModel::Default, CGOptLevel, FileType, ShouldRestoreGlobalsLinkage); // If statistics were requested, print them out after codegen. if (llvm::AreStatisticsEnabled()) Index: test/tools/gold/X86/parallel.ll =================================================================== --- test/tools/gold/X86/parallel.ll +++ test/tools/gold/X86/parallel.ll @@ -1,10 +1,14 @@ ; RUN: llvm-as -o %t.bc %s ; RUN: env LD_PRELOAD=%llvmshlibdir/LLVMgold.so %gold -plugin %llvmshlibdir/LLVMgold.so -u foo -u bar -plugin-opt jobs=2 -plugin-opt save-temps -m elf_x86_64 -o %t %t.bc +; RUN: llvm-dis %t.opt.bc0 -o - | FileCheck --check-prefix=CHECK-BC0 %s +; RUN: llvm-dis %t.opt.bc1 -o - | FileCheck --check-prefix=CHECK-BC1 %s ; RUN: llvm-nm %t.o0 | FileCheck --check-prefix=CHECK0 %s ; RUN: llvm-nm %t.o1 | FileCheck --check-prefix=CHECK1 %s target triple = "x86_64-unknown-linux-gnu" +; CHECK-BC0: define void @foo +; CHECK-BC0: declare void @bar ; CHECK0-NOT: bar ; CHECK0: T foo ; CHECK0-NOT: bar @@ -13,6 +17,8 @@ ret void } +; CHECK-BC1: declare void @foo +; CHECK-BC1: define void @bar ; CHECK1-NOT: foo ; CHECK1: T bar ; CHECK1-NOT: foo Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -888,7 +888,9 @@ /// Sets up output files necessary to perform optional multi-threaded /// split code generation, and invokes the code generation implementation. - void runSplitCodeGen(); + /// If BCFileName is not empty, saves bitcode for module partitions into + /// {BCFileName}0 .. {BCFileName}N. + void runSplitCodeGen(const SmallString<128> &BCFilename); }; } @@ -987,7 +989,7 @@ CodeGenPasses.run(*M); } -void CodeGen::runSplitCodeGen() { +void CodeGen::runSplitCodeGen(const SmallString<128> &BCFilename) { const std::string &TripleStr = M->getTargetTriple(); Triple TheTriple(TripleStr); @@ -1010,6 +1012,7 @@ unsigned int MaxThreads = options::Parallelism ? options::Parallelism : 1; std::vector> Filenames(MaxThreads); + std::vector> BCFilenames(MaxThreads); bool TempOutFile = Filename.empty(); { // Open a file descriptor for each backend task. This is done in a block @@ -1024,8 +1027,18 @@ OSPtrs[I] = &OSs.back(); } + std::list BCOSs; + std::vector BCOSPtrs; + if (!BCFilename.empty() && MaxThreads > 1) { + for (unsigned I = 0; I != MaxThreads; ++I) { + int FD = openOutputFile(BCFilename, false, BCFilenames[I], I); + BCOSs.emplace_back(FD, true); + BCOSPtrs.push_back(&BCOSs.back()); + } + } + // Run backend tasks. - splitCodeGen(std::move(M), OSPtrs, options::mcpu, Features.getString(), + splitCodeGen(std::move(M), OSPtrs, BCOSPtrs, options::mcpu, Features.getString(), Options, RelocationModel, CodeModel::Default, CGOptLevel); } @@ -1036,14 +1049,16 @@ void CodeGen::runAll() { runLTOPasses(); + SmallString<128> OptFilename; if (options::TheOutputType == options::OT_SAVE_TEMPS) { - std::string OptFilename = output_name; + OptFilename = output_name; // If the CodeGen client provided a filename, use it. Always expect // a provided filename if we are in a task (i.e. ThinLTO backend). assert(!SaveTempsFilename.empty() || TaskID == -1); if (!SaveTempsFilename.empty()) OptFilename = SaveTempsFilename; - saveBCFile(OptFilename + ".opt.bc", *M); + OptFilename += ".opt.bc"; + saveBCFile(OptFilename, *M); } // If we are already in a thread (i.e. ThinLTO), just perform @@ -1052,7 +1067,7 @@ runCodegenPasses(); // Otherwise attempt split code gen. else - runSplitCodeGen(); + runSplitCodeGen(OptFilename); } /// Links the module in \p View from file \p F into the combined module