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