diff --git a/llvm/test/tools/sycl-post-link/ir-output-only.ll b/llvm/test/tools/sycl-post-link/ir-output-only.ll new file mode 100644 index 0000000000000..11e72debb78d6 --- /dev/null +++ b/llvm/test/tools/sycl-post-link/ir-output-only.ll @@ -0,0 +1,36 @@ +; RUN: sycl-post-link --ir-output-only -split=auto -S %s -o %t.ll +; RUN: FileCheck %s -input-file=%t.ll + +; This test checks that the --ir-output-only option writes a LLVM IR +; file instead of a table. In comparison with other tests, this one +; checks that the option works OK with -split=auto. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @kernel1() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @kernel2() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} + +; CHECK: define dso_local spir_kernel void @kernel1() +; CHECK: define dso_local spir_kernel void @kernel2() diff --git a/llvm/test/tools/sycl-post-link/sycl-esimd/basic-sycl-esimd-split.ll b/llvm/test/tools/sycl-post-link/sycl-esimd/basic-sycl-esimd-split.ll new file mode 100644 index 0000000000000..94f52ff519d0d --- /dev/null +++ b/llvm/test/tools/sycl-post-link/sycl-esimd/basic-sycl-esimd-split.ll @@ -0,0 +1,45 @@ +; RUN: sycl-post-link -split-esimd -S %s -o %t.table +; RUN: FileCheck %s -input-file=%t.table +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-SYCL-IR +; RUN: FileCheck %s -input-file=%t_esimd_0.ll --check-prefixes CHECK-ESIMD-IR + +; This is basic test of splitting SYCL and ESIMD kernels into separate +; modules. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @ESIMD_kernel() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} +!3 = !{} + +; CHECK: [Code|Properties] +; CHECK: {{.*}}_0.ll|{{.*}}_0.prop +; CHECK: {{.*}}_esimd_0.ll|{{.*}}_esimd_0.prop + +; CHECK-SYCL-IR-DAG: define dso_local spir_kernel void @SYCL_kernel() +; CHECK-SYCL-IR-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-ESIMD-IR-DAG: define dso_local spir_kernel void @ESIMD_kernel() +; CHECK-ESIMD-IR-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() diff --git a/llvm/test/tools/sycl-post-link/sycl-esimd/no-sycl-esimd-split.ll b/llvm/test/tools/sycl-post-link/sycl-esimd/no-sycl-esimd-split.ll new file mode 100644 index 0000000000000..854f600b2bd8d --- /dev/null +++ b/llvm/test/tools/sycl-post-link/sycl-esimd/no-sycl-esimd-split.ll @@ -0,0 +1,55 @@ +; RUN: sycl-post-link -split=source -S %s -o %t.table +; RUN: FileCheck %s -input-file=%t.table +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-IR-0 +; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-IR-1 + +; This test checks that if no '-split-esimd' provided, ther is no +; splitting of SYCL and ESIMD kernels into separate modules. +; However, the rest of the splitting still happens according to +; the '-split=' option. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @ESIMD_kernel() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel1() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel2() #1 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } +attributes #1 = { "sycl-module-id"="b.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} +!3 = !{} + +; CHECK: [Code|Properties] +; CHECK: {{.*}}_0.ll|{{.*}}_0.prop +; CHECK: {{.*}}_1.ll|{{.*}}_1.prop + +; CHECK-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel1() +; CHECK-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel() +; CHECK-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel2() +; CHECK-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() diff --git a/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-kernel.ll b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-kernel.ll new file mode 100644 index 0000000000000..9e27cfc50898f --- /dev/null +++ b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-kernel.ll @@ -0,0 +1,70 @@ +; RUN: sycl-post-link -split-esimd -split=kernel -S %s -o %t.table +; RUN: FileCheck %s -input-file=%t.table +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-SYCL-IR-0 +; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-SYCL-IR-1 +; RUN: FileCheck %s -input-file=%t_esimd_0.ll --check-prefixes CHECK-ESIMD-IR-0 +; RUN: FileCheck %s -input-file=%t_esimd_1.ll --check-prefixes CHECK-ESIMD-IR-1 + +; This test checks that after we split SYCL and ESIMD kernels into +; separate modules, we split those two modules further according to +; -split option. In this case we have 2 SYCL and 2 ESIMD kernels, which +; are split into a total of 4 separate modules. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @ESIMD_kernel1() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @ESIMD_kernel2() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel1() #1 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel2() #1 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } +attributes #1 = { "sycl-module-id"="a.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} +!3 = !{} + +; CHECK: [Code|Properties] +; CHECK: {{.*}}_0.ll|{{.*}}_0.prop +; CHECK: {{.*}}_1.ll|{{.*}}_1.prop +; CHECK: {{.*}}_esimd_0.ll|{{.*}}_esimd_0.prop +; CHECK: {{.*}}_esimd_1.ll|{{.*}}_esimd_1.prop + +; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel1() +; CHECK-SYCL-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-SYCL-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel2() +; CHECK-SYCL-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel1() +; CHECK-ESIMD-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-ESIMD-IR-1-DAG: define dso_local spir_kernel void @ESIMD_kernel2() +; CHECK-ESIMD-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() diff --git a/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-source.ll b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-source.ll new file mode 100644 index 0000000000000..87b99efa01fe5 --- /dev/null +++ b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-source.ll @@ -0,0 +1,86 @@ +; RUN: sycl-post-link -split-esimd -split=source -S %s -o %t.table +; RUN: FileCheck %s -input-file=%t.table +; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-SYCL-IR-0 +; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-SYCL-IR-1 +; RUN: FileCheck %s -input-file=%t_esimd_0.ll --check-prefixes CHECK-ESIMD-IR-0 +; RUN: FileCheck %s -input-file=%t_esimd_1.ll --check-prefixes CHECK-ESIMD-IR-1 + +; This test checks that after we split SYCL and ESIMD kernels into +; separate modules, we split those two modules further according to +; -split option. In this case we have: +; - 3 SYCL kernels: 2 in a.cpp, 1 in b.cpp +; - 3 ESIMD kernels: 2 in a.cpp, 1 in b.cpp +; The module will be split into a total of 4 separate modules. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @ESIMD_kernel1() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @ESIMD_kernel2() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @ESIMD_kernel3() #1 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel1() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel2() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel3() #1 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } +attributes #1 = { "sycl-module-id"="b.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} +!3 = !{} + +; CHECK: [Code|Properties] +; CHECK: {{.*}}_0.ll|{{.*}}_0.prop +; CHECK: {{.*}}_1.ll|{{.*}}_1.prop +; CHECK: {{.*}}_esimd_0.ll|{{.*}}_esimd_0.prop +; CHECK: {{.*}}_esimd_1.ll|{{.*}}_esimd_1.prop + +; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel1() +; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel2() +; CHECK-SYCL-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-SYCL-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel3() +; CHECK-SYCL-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel1() +; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel2() +; CHECK-ESIMD-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +; CHECK-ESIMD-IR-1-DAG: define dso_local spir_kernel void @ESIMD_kernel3() +; CHECK-ESIMD-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() diff --git a/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-symbols.ll b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-symbols.ll new file mode 100644 index 0000000000000..605c814d756e1 --- /dev/null +++ b/llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-symbols.ll @@ -0,0 +1,58 @@ +; RUN: sycl-post-link -split-esimd -symbols -S %s -o %t.table +; RUN: FileCheck %s -input-file=%t.table +; RUN: FileCheck %s -input-file=%t_0.sym --check-prefixes CHECK-SYCL-SYM +; RUN: FileCheck %s -input-file=%t_esimd_0.sym --check-prefixes CHECK-ESIMD-SYM + +; This test checks symbols generation when we split SYCL and ESIMD kernels into +; separate modules. + +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" + +declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + +define dso_local spir_kernel void @ESIMD_kernel1() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @ESIMD_kernel2() #0 !sycl_explicit_simd !3{ +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel1() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +define dso_local spir_kernel void @SYCL_kernel2() #0 { +entry: + %call = tail call spir_func i64 @_Z28__spirv_GlobalInvocationId_xv() + ret void +} + +attributes #0 = { "sycl-module-id"="a.cpp" } +attributes #1 = { "sycl-module-id"="b.cpp" } + +!llvm.module.flags = !{!0} +!opencl.spir.version = !{!1} +!spirv.Source = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, i32 2} +!2 = !{i32 0, i32 100000} +!3 = !{} + +; CHECK: [Code|Properties|Symbols] +; CHECK: {{.*}}_0.ll|{{.*}}_0.prop|{{.*}}_0.sym +; CHECK: {{.*}}_esimd_0.ll|{{.*}}_esimd_0.prop|{{.*}}_esimd_0.sym + +; CHECK-SYCL-SYM: SYCL_kernel1 +; CHECK-SYCL-SYM: SYCL_kernel2 + +; CHECK-ESIMD-SYM: ESIMD_kernel1 +; CHECK-ESIMD-SYM: ESIMD_kernel2 diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index 475d86e4dc4aa..2e16ed5612539 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -88,6 +88,10 @@ static cl::opt OutputAssembly{"S", cl::desc("Write output as LLVM assembly"), cl::Hidden, cl::cat(PostLinkCat)}; +static cl::opt SplitEsimd{"split-esimd", + cl::desc("Split SYCL and ESIMD kernels"), + cl::cat(PostLinkCat)}; + enum IRSplitMode { SPLIT_PER_TU, // one module per translation unit SPLIT_PER_KERNEL, // one module per kernel @@ -130,6 +134,7 @@ struct ImagePropSaveInfo { bool SetSpecConstAtRT; bool SpecConstsMet; bool EmitKernelParamInfo; + bool IsEsimdKernel; }; // Please update DeviceLibFuncMap if any item is added to or removed from // fallback device libraries in libdevice. @@ -445,7 +450,7 @@ splitModule(Module &M, } } -static std::string makeResultFileName(Twine Ext, int I) { +static std::string makeResultFileName(Twine Ext, int I, StringRef Suffix) { const StringRef Dir0 = OutputDir.getNumOccurrences() > 0 ? OutputDir : sys::path::parent_path(OutputFilename); @@ -453,9 +458,8 @@ static std::string makeResultFileName(Twine Ext, int I) { std::string Dir = Dir0.str(); if (!Dir0.empty() && !Dir0.endswith(Sep)) Dir += Sep.str(); - return (Dir + Twine(sys::path::stem(OutputFilename)) + "_" + - std::to_string(I) + Ext) - .str(); + return Dir + sys::path::stem(OutputFilename).str() + "_" + Suffix.str() + + std::to_string(I) + Ext.str(); } static void saveModule(Module &M, StringRef OutFilename) { @@ -476,13 +480,14 @@ static void saveModule(Module &M, StringRef OutFilename) { // Saves specified collection of llvm IR modules to files. // Saves file list if user specified corresponding filename. static string_vector -saveResultModules(std::vector> &ResModules) { +saveResultModules(std::vector> &ResModules, + StringRef Suffix) { string_vector Res; for (size_t I = 0; I < ResModules.size(); ++I) { std::error_code EC; StringRef FileExt = (OutputAssembly) ? ".ll" : ".bc"; - std::string CurOutFileName = makeResultFileName(FileExt, I); + std::string CurOutFileName = makeResultFileName(FileExt, I, Suffix); saveModule(*ResModules[I].get(), CurOutFileName); Res.emplace_back(std::move(CurOutFileName)); } @@ -582,7 +587,8 @@ static string_vector saveDeviceImageProperty( } } std::error_code EC; - std::string SCFile = makeResultFileName(".prop", I); + std::string SCFile = + makeResultFileName(".prop", I, ImgPSInfo.IsEsimdKernel ? "esimd_" : ""); raw_fd_ostream SCOut(SCFile, EC); PropSet.write(SCOut); Res.emplace_back(std::move(SCFile)); @@ -593,12 +599,13 @@ static string_vector saveDeviceImageProperty( // Saves specified collection of symbols lists to files. // Saves file list if user specified corresponding filename. -static string_vector saveResultSymbolsLists(string_vector &ResSymbolsLists) { +static string_vector saveResultSymbolsLists(string_vector &ResSymbolsLists, + StringRef Suffix) { string_vector Res; std::string TxtFilesList; for (size_t I = 0; I < ResSymbolsLists.size(); ++I) { - std::string CurOutFileName = makeResultFileName(".sym", I); + std::string CurOutFileName = makeResultFileName(".sym", I, Suffix); writeToFile(CurOutFileName, ResSymbolsLists[I]); Res.emplace_back(std::move(CurOutFileName)); } @@ -616,8 +623,12 @@ static string_vector saveResultSymbolsLists(string_vector &ResSymbolsLists) { using TableFiles = std::map; -static TableFiles processOneModule(std::unique_ptr M) { +static TableFiles processOneModule(std::unique_ptr M, bool IsEsimd, + bool SyclAndEsimdKernels) { TableFiles TblFiles; + if (!M) + return TblFiles; + std::map> GlobalsSet; bool DoSplit = SplitMode.getNumOccurrences() > 0; @@ -671,17 +682,19 @@ static TableFiles processOneModule(std::unique_ptr M) { { // reuse input module if there were no spec constants and no splitting - string_vector Files = SpecConstsMet || (ResultModules.size() > 1) - ? saveResultModules(ResultModules) - : string_vector{InputFilename}; + string_vector Files = + SpecConstsMet || (ResultModules.size() > 1) || SyclAndEsimdKernels + ? saveResultModules(ResultModules, IsEsimd ? "esimd_" : "") + : string_vector{InputFilename}; // "Code" column is always output std::copy(Files.begin(), Files.end(), std::back_inserter(TblFiles[COL_CODE])); } { - ImagePropSaveInfo ImgPSInfo = {true, DoSpecConst, SetSpecConstAtRT, - SpecConstsMet, EmitKernelParamInfo}; + ImagePropSaveInfo ImgPSInfo = { + true, DoSpecConst, SetSpecConstAtRT, + SpecConstsMet, EmitKernelParamInfo, IsEsimd}; string_vector Files = saveDeviceImageProperty(ResultModules, ImgPSInfo); std::copy(Files.begin(), Files.end(), std::back_inserter(TblFiles[COL_PROPS])); @@ -694,13 +707,81 @@ static TableFiles processOneModule(std::unique_ptr M) { assert(ResultModules.size() == 1); ResultSymbolsLists.push_back(""); } - string_vector Files = saveResultSymbolsLists(ResultSymbolsLists); + string_vector Files = + saveResultSymbolsLists(ResultSymbolsLists, IsEsimd ? "esimd_" : ""); std::copy(Files.begin(), Files.end(), std::back_inserter(TblFiles[COL_SYM])); } return TblFiles; } +using ModulePair = std::pair, std::unique_ptr>; + +// This function splits a module with a mix of SYCL and ESIMD kernels +// into two separate modules. +static ModulePair splitSyclEsimd(std::unique_ptr M) { + // Collect information about the SYCL and ESIMD kernels in the module. + std::vector SyclKernels; + std::vector EsimdKernels; + for (auto &F : M->functions()) { + if (F.getCallingConv() == CallingConv::SPIR_KERNEL) { + if (F.getMetadata("sycl_explicit_simd")) + EsimdKernels.push_back(&F); + else + SyclKernels.push_back(&F); + } + } + + // If only SYCL kernels or only ESIMD kernels, no splitting needed. + if (EsimdKernels.empty()) + return std::make_pair(std::move(M), std::unique_ptr(nullptr)); + + if (SyclKernels.empty()) + return std::make_pair(std::unique_ptr(nullptr), std::move(M)); + + // Key values in KernelModuleMap are not significant, but they define the + // order, in which kernels are processed in the splitModule function. The + // caller of the splitSyclEsimd function expects a pair of 1-Sycl and 2-Esimd + // modules, hence the strings names below. + std::map> KernelModuleMap( + {{"1-SYCL", SyclKernels}, {"2-ESIMD", EsimdKernels}}); + std::vector> ResultModules; + splitModule(*M, KernelModuleMap, ResultModules); + assert(ResultModules.size() == 2); + return std::make_pair(std::move(ResultModules[0]), + std::move(ResultModules[1])); +} + +static TableFiles processInputModule(std::unique_ptr M) { + if (!SplitEsimd) + return processOneModule(std::move(M), false, false); + + std::unique_ptr SyclModule; + std::unique_ptr EsimdModule; + std::tie(SyclModule, EsimdModule) = splitSyclEsimd(std::move(M)); + + // Do we have both Sycl and Esimd kernels? + bool SyclAndEsimdKernels = SyclModule && EsimdModule; + + TableFiles SyclTblFiles = + processOneModule(std::move(SyclModule), false, SyclAndEsimdKernels); + TableFiles EsimdTblFiles = + processOneModule(std::move(EsimdModule), true, SyclAndEsimdKernels); + + // Merge the two resulting file maps + TableFiles MergedTblFiles; + for (auto &ColumnStr : {COL_CODE, COL_PROPS, COL_SYM}) { + auto &SyclFiles = SyclTblFiles[ColumnStr]; + auto &EsimdFiles = EsimdTblFiles[ColumnStr]; + auto &MergedFiles = MergedTblFiles[ColumnStr]; + std::copy(SyclFiles.begin(), SyclFiles.end(), + std::back_inserter(MergedFiles)); + std::copy(EsimdFiles.begin(), EsimdFiles.end(), + std::back_inserter(MergedFiles)); + } + return MergedTblFiles; +} + int main(int argc, char **argv) { InitLLVM X{argc, argv}; @@ -712,6 +793,10 @@ int main(int argc, char **argv) { "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" + "- SYCL and ESIMD kernels can be split into separate modules with\n" + " '-split-esimd' option. The option has no effect when there is only\n" + " one type of kernels in the input module. Functions unreachable from\n" + " any kernel are dropped from the resulting module(s).\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" @@ -719,6 +804,9 @@ int main(int argc, char **argv) { " one module per kernel will be emitted.\n" " '-split=auto' mode automatically selects the best way of splitting\n" " kernels into modules based on some heuristic.\n" + " The '-split' option is compatible with '-split-esimd'. In this case,\n" + " first input module will be split into SYCL and ESIMD modules. Then\n" + " both modules will be further split according to the '-split' option.\n" "- If -symbols options is also specified, then for each produced module\n" " a text file containing names of all spir kernels in it is generated.\n" "- Specialization constant intrinsic transformer. Replaces symbolic\n" @@ -743,10 +831,11 @@ int main(int argc, char **argv) { "than 'auto'.\n"); bool DoSplit = SplitMode.getNumOccurrences() > 0; + bool DoSplitEsimd = SplitEsimd.getNumOccurrences() > 0; bool DoSpecConst = SpecConstLower.getNumOccurrences() > 0; bool DoParamInfo = EmitKernelParamInfo.getNumOccurrences() > 0; - if (!DoSplit && !DoSpecConst && !DoSymGen && !DoParamInfo) { + if (!DoSplit && !DoSpecConst && !DoSymGen && !DoParamInfo && !DoSplitEsimd) { errs() << "no actions specified; try --help for usage info\n"; return 1; } @@ -755,6 +844,11 @@ int main(int argc, char **argv) { << " can't be used with -" << IROutputOnly.ArgStr << "\n"; return 1; } + if (IROutputOnly && DoSplitEsimd) { + errs() << "error: -" << SplitEsimd.ArgStr << " can't be used with -" + << IROutputOnly.ArgStr << "\n"; + return 1; + } if (IROutputOnly && DoSymGen) { errs() << "error: -" << DoSymGen.ArgStr << " can't be used with -" << IROutputOnly.ArgStr << "\n"; @@ -791,34 +885,22 @@ int main(int argc, char **argv) { if (OutputFilename.getNumOccurrences() == 0) OutputFilename = (Twine(sys::path::stem(InputFilename)) + ".files").str(); - TableFiles TblFiles = processOneModule(std::move(M)); + TableFiles TblFiles = processInputModule(std::move(M)); + // Input module was processed and a single output file was requested. if (IROutputOnly) return 0; + // Populate and emit the resulting table util::SimpleTable Table; - auto addTableColumn = [&Table, &TblFiles](std::string Str) { - auto &Files = TblFiles[Str]; - if (Files.empty()) - return 0; - Error Err = Table.addColumn(Str, Files); - CHECK_AND_EXIT(Err); - return 0; - }; + for (auto &ColumnStr : {COL_CODE, COL_PROPS, COL_SYM}) + if (!TblFiles[ColumnStr].empty()) + CHECK_AND_EXIT(Table.addColumn(ColumnStr, TblFiles[ColumnStr])); - int Res; - if ((Res = addTableColumn(COL_CODE)) != 0) - return Res; - if ((Res = addTableColumn(COL_PROPS)) != 0) - return Res; - if ((Res = addTableColumn(COL_SYM)) != 0) - return Res; + std::error_code EC; + raw_fd_ostream Out{OutputFilename, EC, sys::fs::OF_None}; + checkError(EC, "error opening file '" + OutputFilename + "'"); + Table.write(Out); - { - std::error_code EC; - raw_fd_ostream Out{OutputFilename, EC, sys::fs::OF_None}; - checkError(EC, "error opening file '" + OutputFilename + "'"); - Table.write(Out); - } return 0; }