| //===-- ParallelCG.cpp ----------------------------------------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file defines functions that can be used for parallel code generation. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CodeGen/ParallelCG.h" | |
| #include "llvm/Bitcode/BitcodeReader.h" | |
| #include "llvm/Bitcode/BitcodeWriter.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/LegacyPassManager.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Support/ErrorOr.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| #include "llvm/Transforms/Utils/SplitModule.h" | |
| using namespace llvm; | |
| static void codegen(Module *M, llvm::raw_pwrite_stream &OS, | |
| function_ref<std::unique_ptr<TargetMachine>()> TMFactory, | |
| TargetMachine::CodeGenFileType FileType) { | |
| std::unique_ptr<TargetMachine> TM = TMFactory(); | |
| legacy::PassManager CodeGenPasses; | |
| if (TM->addPassesToEmitFile(CodeGenPasses, OS, nullptr, FileType)) | |
| report_fatal_error("Failed to setup codegen"); | |
| CodeGenPasses.run(*M); | |
| } | |
| std::unique_ptr<Module> llvm::splitCodeGen( | |
| std::unique_ptr<Module> M, ArrayRef<llvm::raw_pwrite_stream *> OSs, | |
| ArrayRef<llvm::raw_pwrite_stream *> BCOSs, | |
| const std::function<std::unique_ptr<TargetMachine>()> &TMFactory, | |
| TargetMachine::CodeGenFileType FileType, bool PreserveLocals) { | |
| assert(BCOSs.empty() || BCOSs.size() == OSs.size()); | |
| if (OSs.size() == 1) { | |
| if (!BCOSs.empty()) | |
| WriteBitcodeToFile(*M, *BCOSs[0]); | |
| codegen(M.get(), *OSs[0], TMFactory, FileType); | |
| return M; | |
| } | |
| // Create ThreadPool in nested scope so that threads will be joined | |
| // on destruction. | |
| { | |
| ThreadPool CodegenThreadPool(OSs.size()); | |
| int ThreadCount = 0; | |
| SplitModule( | |
| std::move(M), OSs.size(), | |
| [&](std::unique_ptr<Module> MPart) { | |
| // We want to clone the module in a new context to multi-thread the | |
| // codegen. We do it by serializing partition modules to bitcode | |
| // (while still on the main thread, in order to avoid data races) and | |
| // spinning up new threads which deserialize the partitions into | |
| // separate contexts. | |
| // FIXME: Provide a more direct way to do this in LLVM. | |
| SmallString<0> BC; | |
| raw_svector_ostream BCOS(BC); | |
| WriteBitcodeToFile(*MPart, 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( | |
| [TMFactory, FileType, ThreadOS](const SmallString<0> &BC) { | |
| LLVMContext Ctx; | |
| Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile( | |
| MemoryBufferRef(StringRef(BC.data(), BC.size()), | |
| "<split-module>"), | |
| Ctx); | |
| if (!MOrErr) | |
| report_fatal_error("Failed to read bitcode"); | |
| std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get()); | |
| codegen(MPartInCtx.get(), *ThreadOS, TMFactory, FileType); | |
| }, | |
| // Pass BC using std::move to ensure that it get moved rather than | |
| // copied into the thread's context. | |
| std::move(BC)); | |
| }, | |
| PreserveLocals); | |
| } | |
| return {}; | |
| } |