From f6280b9fddbc9f20ab9dd81a50e0b9bf959e72c4 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Mon, 30 Sep 2019 19:03:11 +0300 Subject: [PATCH 1/5] [SYCL] Implement sycl-post-link tool The tool will split single big linked device module into smaller ones using 'module-id' attribute applied to kernels. Signed-off-by: Mariya Podchishchaeva Signed-off-by: Sergey Semenov --- .../test/tools/sycl-split/basic-sycl-split.ll | 123 ++++++++ llvm/tools/LLVMBuild.txt | 1 + llvm/tools/sycl-split/CMakeLists.txt | 14 + llvm/tools/sycl-split/LLVMBuild.txt | 21 ++ llvm/tools/sycl-split/sycl-split.cpp | 263 ++++++++++++++++++ sycl/CMakeLists.txt | 1 + 6 files changed, 423 insertions(+) create mode 100644 llvm/test/tools/sycl-split/basic-sycl-split.ll create mode 100644 llvm/tools/sycl-split/CMakeLists.txt create mode 100644 llvm/tools/sycl-split/LLVMBuild.txt create mode 100644 llvm/tools/sycl-split/sycl-split.cpp diff --git a/llvm/test/tools/sycl-split/basic-sycl-split.ll b/llvm/test/tools/sycl-split/basic-sycl-split.ll new file mode 100644 index 0000000000000..ab0138c89408d --- /dev/null +++ b/llvm/test/tools/sycl-split/basic-sycl-split.ll @@ -0,0 +1,123 @@ +; RUN: sycl-split -S %s -o %out +; RUN: FileCheck %s -input-file=%out_0.ll --check-prefixes CHECK-TU0,CHECK +; RUN: FileCheck %s -input-file=%out_1.ll --check-prefixes CHECK-TU1,CHECK +; RUN: FileCheck %s -input-file=%out_0.txt --check-prefixes CHECK-TU0-TXT +; RUN: FileCheck %s -input-file=%out_1.txt --check-prefixes CHECK-TU1-TXT +; ModuleID = 'basic-sycl-split.ll' +source_filename = "basic-sycl-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() #1 { +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) #2 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() #2 { +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() #3 { +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() #2 { +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 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "module-id"="TU1.cpp" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "module-id"="TU2.cpp" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" } + +; 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..7ef5fdcc634a2 100644 --- a/llvm/tools/LLVMBuild.txt +++ b/llvm/tools/LLVMBuild.txt @@ -56,6 +56,7 @@ subdirectories = llvm-split llvm-undname opt + sycl-split verify-uselistorder [component_0] diff --git a/llvm/tools/sycl-split/CMakeLists.txt b/llvm/tools/sycl-split/CMakeLists.txt new file mode 100644 index 0000000000000..0dc226cc43551 --- /dev/null +++ b/llvm/tools/sycl-split/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS + BitWriter + Core + IPO + IRReader + Support + ) + +add_llvm_tool(sycl-split + sycl-split.cpp + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/tools/sycl-split/LLVMBuild.txt b/llvm/tools/sycl-split/LLVMBuild.txt new file mode 100644 index 0000000000000..862ec806f8596 --- /dev/null +++ b/llvm/tools/sycl-split/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/sycl-split/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-split +parent = Tools +required_libraries = AsmParser BitReader BitWriter IRReader IPO diff --git a/llvm/tools/sycl-split/sycl-split.cpp b/llvm/tools/sycl-split/sycl-split.cpp new file mode 100644 index 0000000000000..3c5d65c204191 --- /dev/null +++ b/llvm/tools/sycl-split/sycl-split.cpp @@ -0,0 +1,263 @@ +//===- sycl-split.cpp - SYCL module splitter 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 utility splits an input module into smaller ones. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SetVector.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/IR/IRPrintingPasses.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-split 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, 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)); + +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)); + +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 void error(const Twine &Msg) { + errs() << "sycl-split: " << 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) + continue; + + if (F.hasFnAttribute("module-id")) { + Attribute Id = F.getFnAttribute("module-id"); + std::string Val = Id.getValueAsString(); + ResKernelsSet[Val].push_back(&F); + } + } +} + +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 &BB : *F) { + for (auto &I : BB) { + CallBase *CB = dyn_cast(&I); + if (!CB) + continue; + Function *CF = CB->getCalledFunction(); + if (!CF) + continue; + if (CF->isDeclaration() || GVs.count(CF)) + continue; + 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(SymbolsList); + } +} + +static void saveResults(std::vector> &ResModules, + std::vector &ResSymbolsLists) { + int NumOfFile = 0; + std::error_code EC; + std::string IRFilesList; + std::string TxtFilesList; + for (size_t I = 0; I < ResModules.size(); ++I) { + 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-specific module splitter.\n" + "Splits fully linked module into smaller ones. Groups kernels\n" + "using function attribute 'module-id' , i.e. kernels \n" + "with same values in the 'module-id' attribute will be put into the \n" + "same module. For each produced module generates a text file contains \n" + "names of spir kernels presented in this module. Optionally can generate " + "\n" + "lists of probuced files. Usage: \n" + "sycl-split -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..5e6d34ca75d67 100644 --- a/sycl/CMakeLists.txt +++ b/sycl/CMakeLists.txt @@ -156,6 +156,7 @@ add_custom_target( sycl-toolchain llvm-link llvm-objcopy opt + sycl-split COMMENT "Building SYCL compiler toolchain..." ) From 37358269e7a4d93b4f5a15d8cc2bfe397cca66c6 Mon Sep 17 00:00:00 2001 From: Sergey Semenov Date: Mon, 7 Oct 2019 12:02:10 +0300 Subject: [PATCH 2/5] Address code review comments Signed-off-by: Sergey Semenov --- .../basic-module-split.ll} | 22 +++-- llvm/tools/LLVMBuild.txt | 2 +- .../CMakeLists.txt | 4 +- .../LLVMBuild.txt | 4 +- .../sycl-post-link.cpp} | 84 +++++++++---------- sycl/CMakeLists.txt | 2 +- 6 files changed, 55 insertions(+), 63 deletions(-) rename llvm/test/tools/{sycl-split/basic-sycl-split.ll => sycl-post-link/basic-module-split.ll} (64%) rename llvm/tools/{sycl-split => sycl-post-link}/CMakeLists.txt (68%) rename llvm/tools/{sycl-split => sycl-post-link}/LLVMBuild.txt (88%) rename llvm/tools/{sycl-split/sycl-split.cpp => sycl-post-link/sycl-post-link.cpp} (78%) diff --git a/llvm/test/tools/sycl-split/basic-sycl-split.ll b/llvm/test/tools/sycl-post-link/basic-module-split.ll similarity index 64% rename from llvm/test/tools/sycl-split/basic-sycl-split.ll rename to llvm/test/tools/sycl-post-link/basic-module-split.ll index ab0138c89408d..6519a1f9fa4b6 100644 --- a/llvm/test/tools/sycl-split/basic-sycl-split.ll +++ b/llvm/test/tools/sycl-post-link/basic-module-split.ll @@ -1,10 +1,10 @@ -; RUN: sycl-split -S %s -o %out +; RUN: sycl-post-link -S %s -o %out ; RUN: FileCheck %s -input-file=%out_0.ll --check-prefixes CHECK-TU0,CHECK ; RUN: FileCheck %s -input-file=%out_1.ll --check-prefixes CHECK-TU1,CHECK ; RUN: FileCheck %s -input-file=%out_0.txt --check-prefixes CHECK-TU0-TXT ; RUN: FileCheck %s -input-file=%out_1.txt --check-prefixes CHECK-TU1-TXT -; ModuleID = 'basic-sycl-split.ll' -source_filename = "basic-sycl-split.ll" +; 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" @@ -32,7 +32,7 @@ entry: ; CHECK-TU0: call spir_func i32 @{{.*}}bar{{.*}}(i32 1) -define dso_local spir_func void @_Z3foov() #1 { +define dso_local spir_func void @_Z3foov() { entry: %a = alloca i32, align 4 %call = call spir_func i32 @_Z3barIiET_S0_(i32 1) @@ -45,7 +45,7 @@ entry: ; 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) #2 comdat { +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 @@ -70,7 +70,7 @@ entry: ; CHECK-TU1-NOT: define dso_local spir_func void @{{.*}}foo1{{.*}}() ; Function Attrs: nounwind -define dso_local spir_func void @_Z4foo1v() #2 { +define dso_local spir_func void @_Z4foo1v() { entry: %a = alloca i32, align 4 store i32 2, i32* %a, align 4 @@ -84,7 +84,7 @@ entry: ; CHECK-TU1: call spir_func void @{{.*}}foo2{{.*}}() -define dso_local spir_kernel void @_ZTSZ4mainE10TU1_kernel() #3 { +define dso_local spir_kernel void @_ZTSZ4mainE10TU1_kernel() #1 { entry: call spir_func void @_Z4foo2v() ret void @@ -94,7 +94,7 @@ entry: ; CHECK-TU1: define dso_local spir_func void @{{.*}}foo2{{.*}}() ; Function Attrs: nounwind -define dso_local spir_func void @_Z4foo2v() #2 { +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 @@ -104,10 +104,8 @@ entry: ret void } -attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "module-id"="TU1.cpp" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "module-id"="TU2.cpp" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" } +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} diff --git a/llvm/tools/LLVMBuild.txt b/llvm/tools/LLVMBuild.txt index 7ef5fdcc634a2..6674783ba96ec 100644 --- a/llvm/tools/LLVMBuild.txt +++ b/llvm/tools/LLVMBuild.txt @@ -56,7 +56,7 @@ subdirectories = llvm-split llvm-undname opt - sycl-split + sycl-post-link verify-uselistorder [component_0] diff --git a/llvm/tools/sycl-split/CMakeLists.txt b/llvm/tools/sycl-post-link/CMakeLists.txt similarity index 68% rename from llvm/tools/sycl-split/CMakeLists.txt rename to llvm/tools/sycl-post-link/CMakeLists.txt index 0dc226cc43551..0d93c9330065e 100644 --- a/llvm/tools/sycl-split/CMakeLists.txt +++ b/llvm/tools/sycl-post-link/CMakeLists.txt @@ -6,8 +6,8 @@ set(LLVM_LINK_COMPONENTS Support ) -add_llvm_tool(sycl-split - sycl-split.cpp +add_llvm_tool(sycl-post-link + sycl-post-link.cpp DEPENDS intrinsics_gen diff --git a/llvm/tools/sycl-split/LLVMBuild.txt b/llvm/tools/sycl-post-link/LLVMBuild.txt similarity index 88% rename from llvm/tools/sycl-split/LLVMBuild.txt rename to llvm/tools/sycl-post-link/LLVMBuild.txt index 862ec806f8596..ad208216383be 100644 --- a/llvm/tools/sycl-split/LLVMBuild.txt +++ b/llvm/tools/sycl-post-link/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./tools/sycl-split/LLVMBuild.txt -------------------------*- Conf -*--===; +;===- ./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. @@ -16,6 +16,6 @@ [component_0] type = Tool -name = sycl-split +name = sycl-post-link parent = Tools required_libraries = AsmParser BitReader BitWriter IRReader IPO diff --git a/llvm/tools/sycl-split/sycl-split.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp similarity index 78% rename from llvm/tools/sycl-split/sycl-split.cpp rename to llvm/tools/sycl-post-link/sycl-post-link.cpp index 3c5d65c204191..00a0f935a2116 100644 --- a/llvm/tools/sycl-split/sycl-split.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -1,4 +1,4 @@ -//===- sycl-split.cpp - SYCL module splitter tool -------------------------===// +//===- 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. @@ -27,37 +27,36 @@ using namespace llvm; -cl::OptionCategory ExtractCat("sycl-split Options"); +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 InputFilename{ + cl::Positional, cl::desc(""), cl::init("-"), + cl::value_desc("filename")}; -static cl::opt BaseOutputFilename( +static cl::opt BaseOutputFilename{ "o", cl::desc("Specify base output filename, 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)); + cl::value_desc("filename"), cl::init("-"), cl::cat(ExtractCat)}; -static cl::opt OutputIRFilesList( +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)); + cl::value_desc("filename"), cl::init("-"), cl::cat(ExtractCat)}; -static cl::opt OutputTxtFilesList( +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)); + 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 Force{"f", cl::desc("Enable binary output on terminals"), + cl::cat(ExtractCat)}; -static cl::opt OutputAssembly("S", +static cl::opt OutputAssembly{"S", cl::desc("Write output as LLVM assembly"), - cl::Hidden, cl::cat(ExtractCat)); + cl::Hidden, cl::cat(ExtractCat)}; static void error(const Twine &Msg) { - errs() << "sycl-split: " << Msg << '\n'; + errs() << "sycl-post-link: " << Msg << '\n'; exit(1); } @@ -68,7 +67,7 @@ static void error(std::error_code EC, const Twine &Prefix) { static void writeToFile(std::string Filename, std::string Content) { std::error_code EC; - raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None); + 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(); @@ -78,12 +77,10 @@ static void collectKernelsSet( Module &M, std::map> &ResKernelsSet) { for (auto &F : M.functions()) { - if (F.getCallingConv() != CallingConv::SPIR_KERNEL) - continue; - - if (F.hasFnAttribute("module-id")) { - Attribute Id = F.getFnAttribute("module-id"); - std::string Val = Id.getValueAsString(); + if (F.getCallingConv() == CallingConv::SPIR_KERNEL && + F.hasFnAttribute("sycl-module-id")) { + auto Id = F.getFnAttribute("sycl-module-id"); + auto Val = Id.getValueAsString(); ResKernelsSet[Val].push_back(&F); } } @@ -112,16 +109,14 @@ splitModule(Module &M, Workqueue.pop_back(); for (auto &BB : *F) { for (auto &I : BB) { - CallBase *CB = dyn_cast(&I); - if (!CB) - continue; - Function *CF = CB->getCalledFunction(); - if (!CF) - continue; - if (CF->isDeclaration() || GVs.count(CF)) - continue; - GVs.insert(CF); - Workqueue.push_back(CF); + if (CallBase *CB = dyn_cast(&I)) { + if (Function *CF = CB->getCalledFunction()) { + if (!CF->isDeclaration() && !GVs.count(CF)) { + GVs.insert(CF); + Workqueue.push_back(CF); + } + } + } } } } @@ -183,7 +178,7 @@ static void saveResults(std::vector> &ResModules, std::to_string(NumOfFile) + ((OutputAssembly) ? ".ll" : ".bc"); - raw_fd_ostream Out(CurOutFileName, EC, sys::fs::OF_None); + raw_fd_ostream Out{CurOutFileName, EC, sys::fs::OF_None}; error(EC, "error opening the file '" + CurOutFileName + "'"); // TODO: Use the new PassManager instead? @@ -215,21 +210,20 @@ static void saveResults(std::vector> &ResModules, } int main(int argc, char **argv) { - InitLLVM X(argc, argv); + InitLLVM X{argc, argv}; LLVMContext Context; cl::HideUnrelatedOptions(ExtractCat); cl::ParseCommandLineOptions( argc, argv, - "SYCL-specific module splitter.\n" - "Splits fully linked module into smaller ones. Groups kernels\n" - "using function attribute 'module-id' , i.e. kernels \n" - "with same values in the 'module-id' attribute will be put into the \n" - "same module. For each produced module generates a text file contains \n" - "names of spir kernels presented in this module. Optionally can generate " - "\n" - "lists of probuced files. Usage: \n" - "sycl-split -S linked.ll -ir-files-list=ir.txt \\\n" + "SYCL post-link device code processing tool.\n" + "Splits a fully linked module into smaller ones. Groups kernels\n" + "using function attribute 'sycl-module-id', i.e. kernels with the same\n" + "values of the 'sycl-module-id' attribute will be put into the same\n" + "module. For each produced module a text file containing the names of\n" + "all spir kernels in it is generated. Optionally can generate lists of\n" + "produced files. 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" diff --git a/sycl/CMakeLists.txt b/sycl/CMakeLists.txt index 5e6d34ca75d67..09a222770af26 100644 --- a/sycl/CMakeLists.txt +++ b/sycl/CMakeLists.txt @@ -156,7 +156,7 @@ add_custom_target( sycl-toolchain llvm-link llvm-objcopy opt - sycl-split + sycl-post-link COMMENT "Building SYCL compiler toolchain..." ) From 2c832f8d5dbd619c5c5c3090e4ae894e69ea912c Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Tue, 15 Oct 2019 13:41:31 +0300 Subject: [PATCH 3/5] Apply CR suggestions Signed-off-by: Mariya Podchishchaeva --- .../sycl-post-link/one-kernel-per-module.ll | 132 ++++++++++++++++++ llvm/tools/sycl-post-link/sycl-post-link.cpp | 97 ++++++++----- 2 files changed, 195 insertions(+), 34 deletions(-) create mode 100644 llvm/test/tools/sycl-post-link/one-kernel-per-module.ll 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..4dfe7ed7fcbfa --- /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 %out +; RUN: FileCheck %s -input-file=%out_0.ll --check-prefixes CHECK-MODULE0,CHECK +; RUN: FileCheck %s -input-file=%out_1.ll --check-prefixes CHECK-MODULE1,CHECK +; RUN: FileCheck %s -input-file=%out_2.ll --check-prefixes CHECK-MODULE2,CHECK +; RUN: FileCheck %s -input-file=%out_0.txt --check-prefixes CHECK-MODULE0-TXT +; RUN: FileCheck %s -input-file=%out_1.txt --check-prefixes CHECK-MODULE1-TXT +; RUN: FileCheck %s -input-file=%out_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/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index 00a0f935a2116..228c0f95f1129 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -6,13 +6,17 @@ // //===----------------------------------------------------------------------===// // -// This utility splits an input module into smaller ones. +// 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" @@ -36,14 +40,20 @@ static cl::opt InputFilename{ static cl::opt BaseOutputFilename{ "o", - cl::desc("Specify base output filename, output filenames will be saved " + 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)}; @@ -55,6 +65,10 @@ 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); @@ -74,18 +88,28 @@ static void writeToFile(std::string Filename, std::string Content) { } static void collectKernelsSet( - Module &M, - std::map> &ResKernelsSet) { + Module &M, std::map> &ResKernelsSet) { for (auto &F : M.functions()) { - if (F.getCallingConv() == CallingConv::SPIR_KERNEL && - F.hasFnAttribute("sycl-module-id")) { - auto Id = F.getFnAttribute("sycl-module-id"); - auto Val = Id.getValueAsString(); - ResKernelsSet[Val].push_back(&F); + 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, @@ -107,17 +131,13 @@ splitModule(Module &M, while (!Workqueue.empty()) { Function *F = &*Workqueue.back(); Workqueue.pop_back(); - for (auto &BB : *F) { - for (auto &I : BB) { - if (CallBase *CB = dyn_cast(&I)) { - if (Function *CF = CB->getCalledFunction()) { - if (!CF->isDeclaration() && !GVs.count(CF)) { - GVs.insert(CF); - Workqueue.push_back(CF); - } + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + if (CallBase *CB = dyn_cast(&*I)) + if (Function *CF = CB->getCalledFunction()) + if (!CF->isDeclaration() && !GVs.count(CF)) { + GVs.insert(CF); + Workqueue.push_back(CF); } - } - } } } @@ -163,17 +183,20 @@ splitModule(Module &M, // Save results. ResModules.push_back(std::move(MClone)); - ResSymbolsLists.push_back(SymbolsList); + 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::error_code EC; 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"); @@ -217,19 +240,25 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions( argc, argv, "SYCL post-link device code processing tool.\n" - "Splits a fully linked module into smaller ones. Groups kernels\n" - "using function attribute 'sycl-module-id', i.e. kernels with the same\n" - "values of the 'sycl-module-id' attribute will be put into the same\n" - "module. For each produced module a text file containing the names of\n" - "all spir kernels in it is generated. Optionally can generate lists of\n" - "produced files. 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"); + "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); From d58ddd7d178d503c1b338e26bd5ffd31923ea10c Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Wed, 16 Oct 2019 11:45:56 +0300 Subject: [PATCH 4/5] Use %t substitution in the tests to avoid conflicts Signed-off-by: Mariya Podchishchaeva --- .../tools/sycl-post-link/basic-module-split.ll | 10 +++++----- .../tools/sycl-post-link/one-kernel-per-module.ll | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/llvm/test/tools/sycl-post-link/basic-module-split.ll b/llvm/test/tools/sycl-post-link/basic-module-split.ll index 6519a1f9fa4b6..e26fd509bc84f 100644 --- a/llvm/test/tools/sycl-post-link/basic-module-split.ll +++ b/llvm/test/tools/sycl-post-link/basic-module-split.ll @@ -1,8 +1,8 @@ -; RUN: sycl-post-link -S %s -o %out -; RUN: FileCheck %s -input-file=%out_0.ll --check-prefixes CHECK-TU0,CHECK -; RUN: FileCheck %s -input-file=%out_1.ll --check-prefixes CHECK-TU1,CHECK -; RUN: FileCheck %s -input-file=%out_0.txt --check-prefixes CHECK-TU0-TXT -; RUN: FileCheck %s -input-file=%out_1.txt --check-prefixes CHECK-TU1-TXT +; 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" 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 index 4dfe7ed7fcbfa..1ad0f0bf28b9f 100644 --- a/llvm/test/tools/sycl-post-link/one-kernel-per-module.ll +++ b/llvm/test/tools/sycl-post-link/one-kernel-per-module.ll @@ -1,10 +1,10 @@ -; RUN: sycl-post-link --one-kernel -S %s -o %out -; RUN: FileCheck %s -input-file=%out_0.ll --check-prefixes CHECK-MODULE0,CHECK -; RUN: FileCheck %s -input-file=%out_1.ll --check-prefixes CHECK-MODULE1,CHECK -; RUN: FileCheck %s -input-file=%out_2.ll --check-prefixes CHECK-MODULE2,CHECK -; RUN: FileCheck %s -input-file=%out_0.txt --check-prefixes CHECK-MODULE0-TXT -; RUN: FileCheck %s -input-file=%out_1.txt --check-prefixes CHECK-MODULE1-TXT -; RUN: FileCheck %s -input-file=%out_2.txt --check-prefixes CHECK-MODULE2-TXT +; 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" From 90abbfe7bc2ea83a80d62aebd2cec1b3e8fbbe59 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Wed, 16 Oct 2019 11:46:40 +0300 Subject: [PATCH 5/5] Apply CR suggestion Signed-off-by: Mariya Podchishchaeva --- llvm/tools/sycl-post-link/sycl-post-link.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index 228c0f95f1129..52dbecaf2a827 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -131,8 +131,8 @@ splitModule(Module &M, while (!Workqueue.empty()) { Function *F = &*Workqueue.back(); Workqueue.pop_back(); - for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { - if (CallBase *CB = dyn_cast(&*I)) + 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);