1*0b57cec5SDimitry Andric //===-- ParallelCG.cpp ----------------------------------------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This file defines functions that can be used for parallel code generation.
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12*0b57cec5SDimitry Andric 
13*0b57cec5SDimitry Andric #include "llvm/CodeGen/ParallelCG.h"
14*0b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeReader.h"
15*0b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeWriter.h"
16*0b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h"
17*0b57cec5SDimitry Andric #include "llvm/IR/LegacyPassManager.h"
18*0b57cec5SDimitry Andric #include "llvm/IR/Module.h"
19*0b57cec5SDimitry Andric #include "llvm/Support/MemoryBufferRef.h"
20*0b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
21*0b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h"
22*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils/SplitModule.h"
23*0b57cec5SDimitry Andric 
24*0b57cec5SDimitry Andric using namespace llvm;
25*0b57cec5SDimitry Andric 
codegen(Module * M,llvm::raw_pwrite_stream & OS,function_ref<std::unique_ptr<TargetMachine> ()> TMFactory,CodeGenFileType FileType)26*0b57cec5SDimitry Andric static void codegen(Module *M, llvm::raw_pwrite_stream &OS,
27*0b57cec5SDimitry Andric                     function_ref<std::unique_ptr<TargetMachine>()> TMFactory,
28*0b57cec5SDimitry Andric                     CodeGenFileType FileType) {
29*0b57cec5SDimitry Andric   std::unique_ptr<TargetMachine> TM = TMFactory();
30*0b57cec5SDimitry Andric   assert(TM && "Failed to create target machine!");
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric   legacy::PassManager CodeGenPasses;
33*0b57cec5SDimitry Andric   if (TM->addPassesToEmitFile(CodeGenPasses, OS, nullptr, FileType))
34*0b57cec5SDimitry Andric     report_fatal_error("Failed to setup codegen");
35*0b57cec5SDimitry Andric   CodeGenPasses.run(*M);
36*0b57cec5SDimitry Andric }
37*0b57cec5SDimitry 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)38*0b57cec5SDimitry Andric void llvm::splitCodeGen(
39*0b57cec5SDimitry Andric     Module &M, ArrayRef<llvm::raw_pwrite_stream *> OSs,
40*0b57cec5SDimitry Andric     ArrayRef<llvm::raw_pwrite_stream *> BCOSs,
41*0b57cec5SDimitry Andric     const std::function<std::unique_ptr<TargetMachine>()> &TMFactory,
42*0b57cec5SDimitry Andric     CodeGenFileType FileType, bool PreserveLocals) {
43*0b57cec5SDimitry Andric   assert(BCOSs.empty() || BCOSs.size() == OSs.size());
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric   if (OSs.size() == 1) {
46*0b57cec5SDimitry Andric     if (!BCOSs.empty())
47*0b57cec5SDimitry Andric       WriteBitcodeToFile(M, *BCOSs[0]);
48*0b57cec5SDimitry Andric     codegen(&M, *OSs[0], TMFactory, FileType);
49*0b57cec5SDimitry Andric     return;
50*0b57cec5SDimitry Andric   }
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric   // Create ThreadPool in nested scope so that threads will be joined
53*0b57cec5SDimitry Andric   // on destruction.
54*0b57cec5SDimitry Andric   {
55*0b57cec5SDimitry Andric     ThreadPool CodegenThreadPool(hardware_concurrency(OSs.size()));
56*0b57cec5SDimitry Andric     int ThreadCount = 0;
57*0b57cec5SDimitry Andric 
58*0b57cec5SDimitry Andric     SplitModule(
59*0b57cec5SDimitry Andric         M, OSs.size(),
60*0b57cec5SDimitry Andric         [&](std::unique_ptr<Module> MPart) {
61*0b57cec5SDimitry Andric           // We want to clone the module in a new context to multi-thread the
62*0b57cec5SDimitry Andric           // codegen. We do it by serializing partition modules to bitcode
63*0b57cec5SDimitry Andric           // (while still on the main thread, in order to avoid data races) and
64*0b57cec5SDimitry Andric           // spinning up new threads which deserialize the partitions into
65*0b57cec5SDimitry Andric           // separate contexts.
66*0b57cec5SDimitry Andric           // FIXME: Provide a more direct way to do this in LLVM.
67*0b57cec5SDimitry Andric           SmallString<0> BC;
68*0b57cec5SDimitry Andric           raw_svector_ostream BCOS(BC);
69*0b57cec5SDimitry Andric           WriteBitcodeToFile(*MPart, BCOS);
70*0b57cec5SDimitry Andric 
71*0b57cec5SDimitry Andric           if (!BCOSs.empty()) {
72*0b57cec5SDimitry Andric             BCOSs[ThreadCount]->write(BC.begin(), BC.size());
73*0b57cec5SDimitry Andric             BCOSs[ThreadCount]->flush();
74*0b57cec5SDimitry Andric           }
75*0b57cec5SDimitry Andric 
76*0b57cec5SDimitry Andric           llvm::raw_pwrite_stream *ThreadOS = OSs[ThreadCount++];
77*0b57cec5SDimitry Andric           // Enqueue the task
78*0b57cec5SDimitry Andric           CodegenThreadPool.async(
79*0b57cec5SDimitry Andric               [TMFactory, FileType, ThreadOS](const SmallString<0> &BC) {
80*0b57cec5SDimitry Andric                 LLVMContext Ctx;
81*0b57cec5SDimitry Andric                 Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
82*0b57cec5SDimitry Andric                     MemoryBufferRef(StringRef(BC.data(), BC.size()),
83*0b57cec5SDimitry Andric                                     "<split-module>"),
84*0b57cec5SDimitry Andric                     Ctx);
85*0b57cec5SDimitry Andric                 if (!MOrErr)
86*0b57cec5SDimitry Andric                   report_fatal_error("Failed to read bitcode");
87*0b57cec5SDimitry Andric                 std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());
88*0b57cec5SDimitry Andric 
89*0b57cec5SDimitry Andric                 codegen(MPartInCtx.get(), *ThreadOS, TMFactory, FileType);
90*0b57cec5SDimitry Andric               },
91*0b57cec5SDimitry Andric               // Pass BC using std::move to ensure that it get moved rather than
92*0b57cec5SDimitry Andric               // copied into the thread's context.
93*0b57cec5SDimitry Andric               std::move(BC));
94*0b57cec5SDimitry Andric         },
95*0b57cec5SDimitry Andric         PreserveLocals);
96*0b57cec5SDimitry Andric   }
97*0b57cec5SDimitry Andric }
98*0b57cec5SDimitry Andric