1c269ed51SPeter Collingbourne //===-- ParallelCG.cpp ----------------------------------------------------===//
2c269ed51SPeter Collingbourne //
3c269ed51SPeter Collingbourne //                     The LLVM Compiler Infrastructure
4c269ed51SPeter Collingbourne //
5c269ed51SPeter Collingbourne // This file is distributed under the University of Illinois Open Source
6c269ed51SPeter Collingbourne // License. See LICENSE.TXT for details.
7c269ed51SPeter Collingbourne //
8c269ed51SPeter Collingbourne //===----------------------------------------------------------------------===//
9c269ed51SPeter Collingbourne //
10c269ed51SPeter Collingbourne // This file defines functions that can be used for parallel code generation.
11c269ed51SPeter Collingbourne //
12c269ed51SPeter Collingbourne //===----------------------------------------------------------------------===//
13c269ed51SPeter Collingbourne 
14c269ed51SPeter Collingbourne #include "llvm/CodeGen/ParallelCG.h"
15c269ed51SPeter Collingbourne #include "llvm/Bitcode/ReaderWriter.h"
16c269ed51SPeter Collingbourne #include "llvm/IR/LLVMContext.h"
17c269ed51SPeter Collingbourne #include "llvm/IR/LegacyPassManager.h"
18c269ed51SPeter Collingbourne #include "llvm/IR/Module.h"
19c269ed51SPeter Collingbourne #include "llvm/Support/ErrorOr.h"
20c269ed51SPeter Collingbourne #include "llvm/Support/MemoryBuffer.h"
21c269ed51SPeter Collingbourne #include "llvm/Support/TargetRegistry.h"
22c269ed51SPeter Collingbourne #include "llvm/Support/thread.h"
23c269ed51SPeter Collingbourne #include "llvm/Target/TargetMachine.h"
24c269ed51SPeter Collingbourne #include "llvm/Transforms/Utils/SplitModule.h"
25c269ed51SPeter Collingbourne 
26c269ed51SPeter Collingbourne using namespace llvm;
27c269ed51SPeter Collingbourne 
28c269ed51SPeter Collingbourne static void codegen(Module *M, llvm::raw_pwrite_stream &OS,
29c269ed51SPeter Collingbourne                     const Target *TheTarget, StringRef CPU, StringRef Features,
30c269ed51SPeter Collingbourne                     const TargetOptions &Options, Reloc::Model RM,
31c269ed51SPeter Collingbourne                     CodeModel::Model CM, CodeGenOpt::Level OL) {
32c269ed51SPeter Collingbourne   std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(
33c269ed51SPeter Collingbourne       M->getTargetTriple(), CPU, Features, Options, RM, CM, OL));
34c269ed51SPeter Collingbourne 
35c269ed51SPeter Collingbourne   legacy::PassManager CodeGenPasses;
36c269ed51SPeter Collingbourne   if (TM->addPassesToEmitFile(CodeGenPasses, OS,
37c269ed51SPeter Collingbourne                               TargetMachine::CGFT_ObjectFile))
38c269ed51SPeter Collingbourne     report_fatal_error("Failed to setup codegen");
39c269ed51SPeter Collingbourne   CodeGenPasses.run(*M);
40c269ed51SPeter Collingbourne }
41c269ed51SPeter Collingbourne 
42c269ed51SPeter Collingbourne std::unique_ptr<Module>
43c269ed51SPeter Collingbourne llvm::splitCodeGen(std::unique_ptr<Module> M,
44c269ed51SPeter Collingbourne                    ArrayRef<llvm::raw_pwrite_stream *> OSs, StringRef CPU,
45c269ed51SPeter Collingbourne                    StringRef Features, const TargetOptions &Options,
46c269ed51SPeter Collingbourne                    Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL) {
47c269ed51SPeter Collingbourne   StringRef TripleStr = M->getTargetTriple();
48c269ed51SPeter Collingbourne   std::string ErrMsg;
49c269ed51SPeter Collingbourne   const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
50c269ed51SPeter Collingbourne   if (!TheTarget)
51c269ed51SPeter Collingbourne     report_fatal_error(Twine("Target not found: ") + ErrMsg);
52c269ed51SPeter Collingbourne 
53c269ed51SPeter Collingbourne   if (OSs.size() == 1) {
54c269ed51SPeter Collingbourne     codegen(M.get(), *OSs[0], TheTarget, CPU, Features, Options, RM, CM,
55c269ed51SPeter Collingbourne             OL);
56c269ed51SPeter Collingbourne     return M;
57c269ed51SPeter Collingbourne   }
58c269ed51SPeter Collingbourne 
59*592ee15eSPeter Collingbourne   std::vector<thread> Threads;
60c269ed51SPeter Collingbourne   SplitModule(std::move(M), OSs.size(), [&](std::unique_ptr<Module> MPart) {
61c269ed51SPeter Collingbourne     // We want to clone the module in a new context to multi-thread the codegen.
62c269ed51SPeter Collingbourne     // We do it by serializing partition modules to bitcode (while still on the
63c269ed51SPeter Collingbourne     // main thread, in order to avoid data races) and spinning up new threads
64c269ed51SPeter Collingbourne     // which deserialize the partitions into separate contexts.
65c269ed51SPeter Collingbourne     // FIXME: Provide a more direct way to do this in LLVM.
66c269ed51SPeter Collingbourne     SmallVector<char, 0> BC;
67c269ed51SPeter Collingbourne     raw_svector_ostream BCOS(BC);
68c269ed51SPeter Collingbourne     WriteBitcodeToFile(MPart.get(), BCOS);
69c269ed51SPeter Collingbourne 
70c269ed51SPeter Collingbourne     llvm::raw_pwrite_stream *ThreadOS = OSs[Threads.size()];
71c269ed51SPeter Collingbourne     Threads.emplace_back(
72c269ed51SPeter Collingbourne         [TheTarget, CPU, Features, Options, RM, CM, OL,
73c269ed51SPeter Collingbourne          ThreadOS](const SmallVector<char, 0> &BC) {
74c269ed51SPeter Collingbourne           LLVMContext Ctx;
75c269ed51SPeter Collingbourne           ErrorOr<std::unique_ptr<Module>> MOrErr =
76c269ed51SPeter Collingbourne               parseBitcodeFile(MemoryBufferRef(StringRef(BC.data(), BC.size()),
77c269ed51SPeter Collingbourne                                                "<split-module>"),
78c269ed51SPeter Collingbourne                                Ctx);
79c269ed51SPeter Collingbourne           if (!MOrErr)
80c269ed51SPeter Collingbourne             report_fatal_error("Failed to read bitcode");
81c269ed51SPeter Collingbourne           std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());
82c269ed51SPeter Collingbourne 
83c269ed51SPeter Collingbourne           codegen(MPartInCtx.get(), *ThreadOS, TheTarget, CPU, Features,
84c269ed51SPeter Collingbourne                   Options, RM, CM, OL);
85c269ed51SPeter Collingbourne         },
86c269ed51SPeter Collingbourne         // Pass BC using std::move to ensure that it get moved rather than
87c269ed51SPeter Collingbourne         // copied into the thread's context.
88c269ed51SPeter Collingbourne         std::move(BC));
89c269ed51SPeter Collingbourne   });
90c269ed51SPeter Collingbourne 
91*592ee15eSPeter Collingbourne   for (thread &T : Threads)
92c269ed51SPeter Collingbourne     T.join();
93c269ed51SPeter Collingbourne 
94c269ed51SPeter Collingbourne   return {};
95c269ed51SPeter Collingbourne }
96