diff --git a/llvm/test/tools/sycl-post-link/basic-module-split.ll b/llvm/test/tools/sycl-post-link/basic-module-split.ll new file mode 100644 index 0000000000000..e26fd509bc84f --- /dev/null +++ b/llvm/test/tools/sycl-post-link/basic-module-split.ll @@ -0,0 +1,121 @@ +; RUN: sycl-post-link -S %s -o %t +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-TU0,CHECK +; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-TU1,CHECK +; RUN: FileCheck %s -input-file=%t_0.txt --check-prefixes CHECK-TU0-TXT +; RUN: FileCheck %s -input-file=%t_1.txt --check-prefixes CHECK-TU1-TXT +; ModuleID = 'basic-module-split.ll' +source_filename = "basic-module-split.ll" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" +target triple = "spir64-unknown-linux-sycldevice" + +$_Z3barIiET_S0_ = comdat any + +;CHECK-TU0-NOT: @{{.*}}GV{{.*}} +;CHECK-TU1: @{{.*}}GV{{.*}} = internal addrspace(1) constant [1 x i32] [i32 42], align 4 +@_ZL2GV = internal addrspace(1) constant [1 x i32] [i32 42], align 4 + +; CHECK-TU0: define dso_local spir_kernel void @{{.*}}TU0_kernel0{{.*}} +; CHECK-TU0-TXT: {{.*}}TU0_kernel0{{.*}} +; CHECK-TU1-NOT: define dso_local spir_kernel void @{{.*}}TU0_kernel0{{.*}} +; CHECK-TU1-TXT-NOT: {{.*}}TU0_kernel0{{.*}} + +; CHECK-TU0: call spir_func void @{{.*}}foo{{.*}}() + +define dso_local spir_kernel void @_ZTSZ4mainE11TU0_kernel0() #0 { +entry: + call spir_func void @_Z3foov() + ret void +} + +; CHECK-TU0: define dso_local spir_func void @{{.*}}foo{{.*}}() +; CHECK-TU1-NOT: define dso_local spir_func void @{{.*}}foo{{.*}}() + +; CHECK-TU0: call spir_func i32 @{{.*}}bar{{.*}}(i32 1) + +define dso_local spir_func void @_Z3foov() { +entry: + %a = alloca i32, align 4 + %call = call spir_func i32 @_Z3barIiET_S0_(i32 1) + %add = add nsw i32 2, %call + store i32 %add, i32* %a, align 4 + ret void +} + +; CHECK-TU0: define {{.*}} spir_func i32 @{{.*}}bar{{.*}}(i32 %arg) +; CHECK-TU1-NOT: define {{.*}} spir_func i32 @{{.*}}bar{{.*}}(i32 %arg) + +; Function Attrs: nounwind +define linkonce_odr dso_local spir_func i32 @_Z3barIiET_S0_(i32 %arg) comdat { +entry: + %arg.addr = alloca i32, align 4 + store i32 %arg, i32* %arg.addr, align 4 + %0 = load i32, i32* %arg.addr, align 4 + ret i32 %0 +} + +; CHECK-TU0: define dso_local spir_kernel void @{{.*}}TU0_kernel1{{.*}}() +; CHECK-TU0-TXT: {{.*}}TU0_kernel1{{.*}} +; CHECK-TU1-NOT: define dso_local spir_kernel void @{{.*}}TU0_kernel1{{.*}}() +; CHECK-TU1-TXT-NOT: {{.*}}TU0_kernel1{{.*}} + +; CHECK-TU0: call spir_func void @{{.*}}foo1{{.*}}() + +define dso_local spir_kernel void @_ZTSZ4mainE11TU0_kernel1() #0 { +entry: + call spir_func void @_Z4foo1v() + ret void +} + +; CHECK-TU0: define dso_local spir_func void @{{.*}}foo1{{.*}}() +; CHECK-TU1-NOT: define dso_local spir_func void @{{.*}}foo1{{.*}}() + +; Function Attrs: nounwind +define dso_local spir_func void @_Z4foo1v() { +entry: + %a = alloca i32, align 4 + store i32 2, i32* %a, align 4 + ret void +} + +; CHECK-TU0-NOT: define dso_local spir_kernel void @{{.*}}TU1_kernel{{.*}}() +; CHECK-TU0-TXT-NOT: {{.*}}TU1_kernel{{.*}} +; CHECK-TU1: define dso_local spir_kernel void @{{.*}}TU1_kernel{{.*}}() +; CHECK-TU1-TXT: {{.*}}TU1_kernel{{.*}} + +; CHECK-TU1: call spir_func void @{{.*}}foo2{{.*}}() + +define dso_local spir_kernel void @_ZTSZ4mainE10TU1_kernel() #1 { +entry: + call spir_func void @_Z4foo2v() + ret void +} + +; CHECK-TU0-NOT: define dso_local spir_func void @{{.*}}foo2{{.*}}() +; CHECK-TU1: define dso_local spir_func void @{{.*}}foo2{{.*}}() + +; Function Attrs: nounwind +define dso_local spir_func void @_Z4foo2v() { +entry: + %a = alloca i32, align 4 +; CHECK-TU1: %0 = load i32, i32 addrspace(4)* getelementptr inbounds ([1 x i32], [1 x i32] addrspace(4)* addrspacecast ([1 x i32] addrspace(1)* @{{.*}}GV{{.*}} to [1 x i32] addrspace(4)*), i64 0, i64 0), align 4 + %0 = load i32, i32 addrspace(4)* getelementptr inbounds ([1 x i32], [1 x i32] addrspace(4)* addrspacecast ([1 x i32] addrspace(1)* @_ZL2GV to [1 x i32] addrspace(4)*), i64 0, i64 0), align 4 + %add = add nsw i32 4, %0 + store i32 %add, i32* %a, align 4 + ret void +} + +attributes #0 = { "sycl-module-id"="TU1.cpp" } +attributes #1 = { "sycl-module-id"="TU2.cpp" } + +; Metadata is saved in both modules. +; CHECK: !opencl.spir.version = !{!0, !0} +; CHECK: !spirv.Source = !{!1, !1} + +!opencl.spir.version = !{!0, !0} +!spirv.Source = !{!1, !1} + +; CHECK; !0 = !{i32 1, i32 2} +; CHECK; !1 = !{i32 4, i32 100000} + +!0 = !{i32 1, i32 2} +!1 = !{i32 4, i32 100000} diff --git a/llvm/test/tools/sycl-post-link/one-kernel-per-module.ll b/llvm/test/tools/sycl-post-link/one-kernel-per-module.ll new file mode 100644 index 0000000000000..1ad0f0bf28b9f --- /dev/null +++ b/llvm/test/tools/sycl-post-link/one-kernel-per-module.ll @@ -0,0 +1,132 @@ +; RUN: sycl-post-link --one-kernel -S %s -o %t +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-MODULE0,CHECK +; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-MODULE1,CHECK +; RUN: FileCheck %s -input-file=%t_2.ll --check-prefixes CHECK-MODULE2,CHECK +; RUN: FileCheck %s -input-file=%t_0.txt --check-prefixes CHECK-MODULE0-TXT +; RUN: FileCheck %s -input-file=%t_1.txt --check-prefixes CHECK-MODULE1-TXT +; RUN: FileCheck %s -input-file=%t_2.txt --check-prefixes CHECK-MODULE2-TXT +; ModuleID = 'basic-module-split.ll' +source_filename = "basic-module-split.ll" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" +target triple = "spir64-unknown-linux-sycldevice" + +$_Z3barIiET_S0_ = comdat any + +;CHECK-MODULE0-NOT: @{{.*}}GV{{.*}} +;CHECK-MODULE1-NOT: @{{.*}}GV{{.*}} +;CHECK-MODULE2: @{{.*}}GV{{.*}} = internal addrspace(1) constant [1 x i32] [i32 42], align 4 +@_ZL2GV = internal addrspace(1) constant [1 x i32] [i32 42], align 4 + +; CHECK-MODULE0: define dso_local spir_kernel void @{{.*}}TU0_kernel0{{.*}} +; CHECK-MODULE0-TXT: {{.*}}TU0_kernel0{{.*}} +; CHECK-MODULE1-NOT: define dso_local spir_kernel void @{{.*}}TU0_kernel0{{.*}} +; CHECK-MODULE1-TXT-NOT: {{.*}}TU0_kernel0{{.*}} + +; CHECK-MODULE0: call spir_func void @{{.*}}foo{{.*}}() + +define dso_local spir_kernel void @TU0_kernel0() #0 { +entry: + call spir_func void @_Z3foov() + ret void +} + +; CHECK-MODULE0: define dso_local spir_func void @{{.*}}foo{{.*}}() +; CHECK-MODULE1-NOT: define dso_local spir_func void @{{.*}}foo{{.*}}() +; CHECK-MODULE2-NOT: define dso_local spir_func void @{{.*}}foo{{.*}}() + +; CHECK-MODULE0: call spir_func i32 @{{.*}}bar{{.*}}(i32 1) + +define dso_local spir_func void @_Z3foov() { +entry: + %a = alloca i32, align 4 + %call = call spir_func i32 @_Z3barIiET_S0_(i32 1) + %add = add nsw i32 2, %call + store i32 %add, i32* %a, align 4 + ret void +} + +; CHECK-MODULE0: define {{.*}} spir_func i32 @{{.*}}bar{{.*}}(i32 %arg) +; CHECK-MODULE1-NOT: define {{.*}} spir_func i32 @{{.*}}bar{{.*}}(i32 %arg) +; CHECK-MODULE2-NOT: define {{.*}} spir_func i32 @{{.*}}bar{{.*}}(i32 %arg) + +; Function Attrs: nounwind +define linkonce_odr dso_local spir_func i32 @_Z3barIiET_S0_(i32 %arg) comdat { +entry: + %arg.addr = alloca i32, align 4 + store i32 %arg, i32* %arg.addr, align 4 + %0 = load i32, i32* %arg.addr, align 4 + ret i32 %0 +} + +; CHECK-MODULE0-NOT: define dso_local spir_kernel void @{{.*}}TU0_kernel1{{.*}}() +; CHECK-MODULE0-TXT-NOT: {{.*}}TU0_kernel1{{.*}} +; CHECK-MODULE1: define dso_local spir_kernel void @{{.*}}TU0_kernel1{{.*}}() +; CHECK-MODULE1-TXT: {{.*}}TU0_kernel1{{.*}} +; CHECK-MODULE2-NOT: define dso_local spir_kernel void @{{.*}}TU0_kernel1{{.*}}() +; CHECK-MODULE2-TXT-NOT: {{.*}}TU0_kernel1{{.*}} + +; CHECK-MODULE1: call spir_func void @{{.*}}foo1{{.*}}() + +define dso_local spir_kernel void @TU0_kernel1() #0 { +entry: + call spir_func void @_Z4foo1v() + ret void +} + +; CHECK-MODULE0-NOT: define dso_local spir_func void @{{.*}}foo1{{.*}}() +; CHECK-MODULE1: define dso_local spir_func void @{{.*}}foo1{{.*}}() +; CHECK-MODULE2-NOT: define dso_local spir_func void @{{.*}}foo1{{.*}}() + +; Function Attrs: nounwind +define dso_local spir_func void @_Z4foo1v() { +entry: + %a = alloca i32, align 4 + store i32 2, i32* %a, align 4 + ret void +} + +; CHECK-MODULE0-NOT: define dso_local spir_kernel void @{{.*}}TU1_kernel{{.*}}() +; CHECK-MODULE0-TXT-NOT: {{.*}}TU1_kernel{{.*}} +; CHECK-MODULE1-NOT: define dso_local spir_kernel void @{{.*}}TU1_kernel{{.*}}() +; CHECK-MODULE1-TXT-NOT: {{.*}}TU1_kernel{{.*}} +; CHECK-MODULE2: define dso_local spir_kernel void @{{.*}}TU1_kernel{{.*}}() +; CHECK-MODULE2-TXT: {{.*}}TU1_kernel{{.*}} + +; CHECK-MODULE2: call spir_func void @{{.*}}foo2{{.*}}() + +define dso_local spir_kernel void @TU1_kernel() #1 { +entry: + call spir_func void @_Z4foo2v() + ret void +} + +; CHECK-MODULE0-NOT: define dso_local spir_func void @{{.*}}foo2{{.*}}() +; CHECK-MODULE1-NOT: define dso_local spir_func void @{{.*}}foo2{{.*}}() +; CHECK-MODULE2: define dso_local spir_func void @{{.*}}foo2{{.*}}() + +; Function Attrs: nounwind +define dso_local spir_func void @_Z4foo2v() { +entry: + %a = alloca i32, align 4 +; CHECK-MODULE2: %0 = load i32, i32 addrspace(4)* getelementptr inbounds ([1 x i32], [1 x i32] addrspace(4)* addrspacecast ([1 x i32] addrspace(1)* @{{.*}}GV{{.*}} to [1 x i32] addrspace(4)*), i64 0, i64 0), align 4 + %0 = load i32, i32 addrspace(4)* getelementptr inbounds ([1 x i32], [1 x i32] addrspace(4)* addrspacecast ([1 x i32] addrspace(1)* @_ZL2GV to [1 x i32] addrspace(4)*), i64 0, i64 0), align 4 + %add = add nsw i32 4, %0 + store i32 %add, i32* %a, align 4 + ret void +} + +attributes #0 = { "sycl-module-id"="TU1.cpp" } +attributes #1 = { "sycl-module-id"="TU2.cpp" } + +; Metadata is saved in both modules. +; CHECK: !opencl.spir.version = !{!0, !0} +; CHECK: !spirv.Source = !{!1, !1} + +!opencl.spir.version = !{!0, !0} +!spirv.Source = !{!1, !1} + +; CHECK; !0 = !{i32 1, i32 2} +; CHECK; !1 = !{i32 4, i32 100000} + +!0 = !{i32 1, i32 2} +!1 = !{i32 4, i32 100000} diff --git a/llvm/tools/LLVMBuild.txt b/llvm/tools/LLVMBuild.txt index 327751ebb4fb5..6674783ba96ec 100644 --- a/llvm/tools/LLVMBuild.txt +++ b/llvm/tools/LLVMBuild.txt @@ -56,6 +56,7 @@ subdirectories = llvm-split llvm-undname opt + sycl-post-link verify-uselistorder [component_0] diff --git a/llvm/tools/sycl-post-link/CMakeLists.txt b/llvm/tools/sycl-post-link/CMakeLists.txt new file mode 100644 index 0000000000000..0d93c9330065e --- /dev/null +++ b/llvm/tools/sycl-post-link/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS + BitWriter + Core + IPO + IRReader + Support + ) + +add_llvm_tool(sycl-post-link + sycl-post-link.cpp + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/tools/sycl-post-link/LLVMBuild.txt b/llvm/tools/sycl-post-link/LLVMBuild.txt new file mode 100644 index 0000000000000..ad208216383be --- /dev/null +++ b/llvm/tools/sycl-post-link/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/sycl-post-link/LLVMBuild.txt ---------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = sycl-post-link +parent = Tools +required_libraries = AsmParser BitReader BitWriter IRReader IPO diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp new file mode 100644 index 0000000000000..52dbecaf2a827 --- /dev/null +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -0,0 +1,286 @@ +//===- sycl-post-link.cpp - SYCL post-link device code processing tool ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This source is a collection of utilities run on device code's LLVM IR before +// handing off to back-end for further compilation or emitting SPIRV. The +// utilities are: +// - module splitter to split a big input module into smaller ones +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SetVector.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/SystemUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/IPO.h" +#include + +using namespace llvm; + +cl::OptionCategory ExtractCat{"sycl-post-link Options"}; + +// InputFilename - The filename to read from. +static cl::opt InputFilename{ + cl::Positional, cl::desc(""), cl::init("-"), + cl::value_desc("filename")}; + +static cl::opt BaseOutputFilename{ + "o", + cl::desc("Specify base output filename. E.g. if base is 'out', then output " + "filenames will be saved " + "into out_0.bc, out_1.bc, ..., out_0.txt, out_1.txt, ...."), + cl::value_desc("filename"), cl::init("-"), cl::cat(ExtractCat)}; + +// Module splitter produces multiple IR files. IR files list is a file list +// with prodced IR modules files names. +static cl::opt OutputIRFilesList{ + "ir-files-list", cl::desc("Specify output filename for IR files list"), + cl::value_desc("filename"), cl::init("-"), cl::cat(ExtractCat)}; + +// Module splitter produces multiple TXT files. These files contain kernel names +// list presented in a produced module. TXT files list is a file list +// with produced TXT files names. +static cl::opt OutputTxtFilesList{ + "txt-files-list", cl::desc("Specify output filename for txt files list"), + cl::value_desc("filename"), cl::init("-"), cl::cat(ExtractCat)}; + +static cl::opt Force{"f", cl::desc("Enable binary output on terminals"), + cl::cat(ExtractCat)}; + +static cl::opt OutputAssembly{"S", + cl::desc("Write output as LLVM assembly"), + cl::Hidden, cl::cat(ExtractCat)}; + +static cl::opt OneKernelPerModule{ + "one-kernel", cl::desc("Emit a separate module for each kernel"), + cl::init(false), cl::cat(ExtractCat)}; + +static void error(const Twine &Msg) { + errs() << "sycl-post-link: " << Msg << '\n'; + exit(1); +} + +static void error(std::error_code EC, const Twine &Prefix) { + if (EC) + error(Prefix + ": " + EC.message()); +} + +static void writeToFile(std::string Filename, std::string Content) { + std::error_code EC; + raw_fd_ostream OS{Filename, EC, sys::fs::OpenFlags::OF_None}; + error(EC, "error opening the file '" + Filename + "'"); + OS.write(Content.data(), Content.size()); + OS.close(); +} + +static void collectKernelsSet( + Module &M, std::map> &ResKernelsSet) { + for (auto &F : M.functions()) { + if (F.getCallingConv() == CallingConv::SPIR_KERNEL) { + if (OneKernelPerModule) { + ResKernelsSet[F.getName()].push_back(&F); + } else if (F.hasFnAttribute("sycl-module-id")) { + auto Id = F.getFnAttribute("sycl-module-id"); + auto Val = Id.getValueAsString(); + ResKernelsSet[Val].push_back(&F); + } + } + } +} + +// Splits input LLVM IR module M into smaller ones. +// Input parameter KernelsSet is a map containing groups of kernels with same +// values in the sycl-module-id attribute. For each group of kernels a separate +// IR module will be produced. +// ResModules and ResSymbolsLists are output parameters. +// Result modules are stored into +// ResModules vector. For each result module set of kernel names is collected. +// Sets of kernel names are stored into ResSymbolsLists. +static void +splitModule(Module &M, + std::map> &KernelsSet, + std::vector> &ResModules, + std::vector &ResSymbolsLists) { + for (auto &It : KernelsSet) { + // For each group of kernels collect all dependencies. + SetVector GVs; + std::vector Workqueue; + std::string SymbolsList; + + for (auto &F : It.second) { + GVs.insert(F); + Workqueue.push_back(F); + SymbolsList = + (Twine(SymbolsList) + Twine(F->getName()) + Twine("\n")).str(); + } + + while (!Workqueue.empty()) { + Function *F = &*Workqueue.back(); + Workqueue.pop_back(); + for (auto &I : instructions(F)) { + if (CallBase *CB = dyn_cast(&I)) + if (Function *CF = CB->getCalledFunction()) + if (!CF->isDeclaration() && !GVs.count(CF)) { + GVs.insert(CF); + Workqueue.push_back(CF); + } + } + } + + // It's not easy to trace global variable's uses inside needed functions + // because global variable can be used inside a combination of operators, so + // mark all global variables as needed and remove dead ones after + // extraction. + for (auto &G : M.globals()) { + GVs.insert(&G); + } + + // Clone the module, understand which globals we need to extract from the + // clone. + ValueToValueMapTy VMap; + std::unique_ptr MClone = CloneModule(M, VMap); + std::vector GVsInClone(GVs.size()); + int I = 0; + for (GlobalValue *GV : GVs) { + GVsInClone[I] = cast(VMap[GV]); + ++I; + } + + // TODO: Use the new PassManager instead? + legacy::PassManager Extract; + + // Extract needed globals. + Extract.add(createGVExtractionPass(GVsInClone, /* deleteS */ false)); + Extract.run(*MClone.get()); + + // Extactor pass sets external linkage to all globals. Return linkage back. + for (auto &G : MClone->globals()) { + if (G.getVisibility() == GlobalValue::HiddenVisibility) { + G.setLinkage(GlobalValue::InternalLinkage); + } + } + + legacy::PassManager Passes; + // Do cleanup. + Passes.add(createGlobalDCEPass()); // Delete unreachable globals. + Passes.add(createStripDeadDebugInfoPass()); // Remove dead debug info. + Passes.add(createStripDeadPrototypesPass()); // Remove dead func decls. + Passes.run(*MClone.get()); + + // Save results. + ResModules.push_back(std::move(MClone)); + ResSymbolsLists.push_back(std::move(SymbolsList)); + } +} + +// Saves specified collections of llvm IR modules and corresponding lists of +// kernel names to files. Saves IR files list and TXT files list if user +// specified corresponding filenames. +static void saveResults(std::vector> &ResModules, + std::vector &ResSymbolsLists) { + int NumOfFile = 0; + std::string IRFilesList; + std::string TxtFilesList; + for (size_t I = 0; I < ResModules.size(); ++I) { + std::error_code EC; + std::string CurOutFileName = BaseOutputFilename + "_" + + std::to_string(NumOfFile) + + ((OutputAssembly) ? ".ll" : ".bc"); + + raw_fd_ostream Out{CurOutFileName, EC, sys::fs::OF_None}; + error(EC, "error opening the file '" + CurOutFileName + "'"); + + // TODO: Use the new PassManager instead? + legacy::PassManager PrintModule; + + if (OutputAssembly) + PrintModule.add(createPrintModulePass(Out, "")); + else if (Force || !CheckBitcodeOutputToConsole(Out, true)) + PrintModule.add(createBitcodeWriterPass(Out)); + PrintModule.run(*ResModules[I].get()); + + IRFilesList = + (Twine(IRFilesList) + Twine(CurOutFileName) + Twine("\n")).str(); + + CurOutFileName = + BaseOutputFilename + "_" + std::to_string(NumOfFile) + ".txt"; + writeToFile(CurOutFileName, ResSymbolsLists[I]); + + TxtFilesList = + (Twine(TxtFilesList) + Twine(CurOutFileName) + Twine("\n")).str(); + + ++NumOfFile; + } + + if (OutputIRFilesList != "-") + writeToFile(OutputIRFilesList, IRFilesList); + if (OutputTxtFilesList != "-") + writeToFile(OutputTxtFilesList, TxtFilesList); +} + +int main(int argc, char **argv) { + InitLLVM X{argc, argv}; + + LLVMContext Context; + cl::HideUnrelatedOptions(ExtractCat); + cl::ParseCommandLineOptions( + argc, argv, + "SYCL post-link device code processing tool.\n" + "This is a collection of utilities run on device code's LLVM IR before\n" + "handing off to back-end for further compilation or emitting SPIRV.\n" + "The utilities are:\n" + "- Module splitter to split a big input module into smaller ones.\n" + " Groups kernels using function attribute 'sycl-module-id', i.e.\n" + " kernels with the same values of the 'sycl-module-id' attribute will\n" + " be put into the same module. If --one-kernel option is specified,\n" + " one module per kernel will be emitted.\n" + " For each produced module a text file\n" + " containing the names of all spir kernels in it is generated.\n" + " Optionally can generate lists produced files.\n" + " Usage:\n" + " sycl-post-link -S linked.ll -ir-files-list=ir.txt \\\n" + " -txt-files-list=files.txt -o out \\\n" + " This command will produce several llvm IR files: out_0.ll, " + " out_1.ll...,\n" + " several text files containing spir kernel names out_0.txt, " + " out_1.txt,...,\n" + " and two filelists in ir.txt and files.txt.\n"); + + SMDiagnostic Err; + std::unique_ptr M = parseIRFile(InputFilename, Err, Context); + + if (!M.get()) { + Err.print(argv[0], errs()); + return 1; + } + + std::map> GlobalsSet; + + collectKernelsSet(*M.get(), GlobalsSet); + + std::vector> ResultModules; + std::vector ResultSymbolsLists; + + splitModule(*M.get(), GlobalsSet, ResultModules, ResultSymbolsLists); + + if (BaseOutputFilename == "-") + BaseOutputFilename = "a.out"; + + saveResults(ResultModules, ResultSymbolsLists); + + return 0; +} diff --git a/sycl/CMakeLists.txt b/sycl/CMakeLists.txt index 6dae4f8be5d15..09a222770af26 100644 --- a/sycl/CMakeLists.txt +++ b/sycl/CMakeLists.txt @@ -156,6 +156,7 @@ add_custom_target( sycl-toolchain llvm-link llvm-objcopy opt + sycl-post-link COMMENT "Building SYCL compiler toolchain..." )