10b57cec5SDimitry Andric //===-- ParallelCG.cpp ----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines functions that can be used for parallel code generation.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/CodeGen/ParallelCG.h"
140b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeReader.h"
150b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeWriter.h"
160b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h"
170b57cec5SDimitry Andric #include "llvm/IR/LegacyPassManager.h"
180b57cec5SDimitry Andric #include "llvm/IR/Module.h"
190b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
200b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
210b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
220b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h"
230b57cec5SDimitry Andric #include "llvm/Transforms/Utils/SplitModule.h"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace llvm;
260b57cec5SDimitry Andric 
codegen(Module * M,llvm::raw_pwrite_stream & OS,function_ref<std::unique_ptr<TargetMachine> ()> TMFactory,CodeGenFileType FileType)270b57cec5SDimitry Andric static void codegen(Module *M, llvm::raw_pwrite_stream &OS,
280b57cec5SDimitry Andric                     function_ref<std::unique_ptr<TargetMachine>()> TMFactory,
29480093f4SDimitry Andric                     CodeGenFileType FileType) {
300b57cec5SDimitry Andric   std::unique_ptr<TargetMachine> TM = TMFactory();
31af732203SDimitry Andric   assert(TM && "Failed to create target machine!");
32af732203SDimitry Andric 
330b57cec5SDimitry Andric   legacy::PassManager CodeGenPasses;
340b57cec5SDimitry Andric   if (TM->addPassesToEmitFile(CodeGenPasses, OS, nullptr, FileType))
350b57cec5SDimitry Andric     report_fatal_error("Failed to setup codegen");
360b57cec5SDimitry Andric   CodeGenPasses.run(*M);
370b57cec5SDimitry Andric }
380b57cec5SDimitry Andric 
splitCodeGen(Module & M,ArrayRef<llvm::raw_pwrite_stream * > OSs,ArrayRef<llvm::raw_pwrite_stream * > BCOSs,const std::function<std::unique_ptr<TargetMachine> ()> & TMFactory,CodeGenFileType FileType,bool PreserveLocals)39*5f7ddb14SDimitry Andric void llvm::splitCodeGen(
40*5f7ddb14SDimitry Andric     Module &M, ArrayRef<llvm::raw_pwrite_stream *> OSs,
410b57cec5SDimitry Andric     ArrayRef<llvm::raw_pwrite_stream *> BCOSs,
420b57cec5SDimitry Andric     const std::function<std::unique_ptr<TargetMachine>()> &TMFactory,
43480093f4SDimitry Andric     CodeGenFileType FileType, bool PreserveLocals) {
440b57cec5SDimitry Andric   assert(BCOSs.empty() || BCOSs.size() == OSs.size());
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric   if (OSs.size() == 1) {
470b57cec5SDimitry Andric     if (!BCOSs.empty())
48*5f7ddb14SDimitry Andric       WriteBitcodeToFile(M, *BCOSs[0]);
49*5f7ddb14SDimitry Andric     codegen(&M, *OSs[0], TMFactory, FileType);
50*5f7ddb14SDimitry Andric     return;
510b57cec5SDimitry Andric   }
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   // Create ThreadPool in nested scope so that threads will be joined
540b57cec5SDimitry Andric   // on destruction.
550b57cec5SDimitry Andric   {
565ffd83dbSDimitry Andric     ThreadPool CodegenThreadPool(hardware_concurrency(OSs.size()));
570b57cec5SDimitry Andric     int ThreadCount = 0;
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric     SplitModule(
60*5f7ddb14SDimitry Andric         M, OSs.size(),
610b57cec5SDimitry Andric         [&](std::unique_ptr<Module> MPart) {
620b57cec5SDimitry Andric           // We want to clone the module in a new context to multi-thread the
630b57cec5SDimitry Andric           // codegen. We do it by serializing partition modules to bitcode
640b57cec5SDimitry Andric           // (while still on the main thread, in order to avoid data races) and
650b57cec5SDimitry Andric           // spinning up new threads which deserialize the partitions into
660b57cec5SDimitry Andric           // separate contexts.
670b57cec5SDimitry Andric           // FIXME: Provide a more direct way to do this in LLVM.
680b57cec5SDimitry Andric           SmallString<0> BC;
690b57cec5SDimitry Andric           raw_svector_ostream BCOS(BC);
700b57cec5SDimitry Andric           WriteBitcodeToFile(*MPart, BCOS);
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric           if (!BCOSs.empty()) {
730b57cec5SDimitry Andric             BCOSs[ThreadCount]->write(BC.begin(), BC.size());
740b57cec5SDimitry Andric             BCOSs[ThreadCount]->flush();
750b57cec5SDimitry Andric           }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric           llvm::raw_pwrite_stream *ThreadOS = OSs[ThreadCount++];
780b57cec5SDimitry Andric           // Enqueue the task
790b57cec5SDimitry Andric           CodegenThreadPool.async(
800b57cec5SDimitry Andric               [TMFactory, FileType, ThreadOS](const SmallString<0> &BC) {
810b57cec5SDimitry Andric                 LLVMContext Ctx;
820b57cec5SDimitry Andric                 Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
830b57cec5SDimitry Andric                     MemoryBufferRef(StringRef(BC.data(), BC.size()),
840b57cec5SDimitry Andric                                     "<split-module>"),
850b57cec5SDimitry Andric                     Ctx);
860b57cec5SDimitry Andric                 if (!MOrErr)
870b57cec5SDimitry Andric                   report_fatal_error("Failed to read bitcode");
880b57cec5SDimitry Andric                 std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric                 codegen(MPartInCtx.get(), *ThreadOS, TMFactory, FileType);
910b57cec5SDimitry Andric               },
920b57cec5SDimitry Andric               // Pass BC using std::move to ensure that it get moved rather than
930b57cec5SDimitry Andric               // copied into the thread's context.
940b57cec5SDimitry Andric               std::move(BC));
950b57cec5SDimitry Andric         },
960b57cec5SDimitry Andric         PreserveLocals);
970b57cec5SDimitry Andric   }
980b57cec5SDimitry Andric }
99