Skip to content

Commit 44cbb8e

Browse files
committed
Modify names of SYCL/ESIMD shared functions before linkage, add test.
Signed-off-by: Konstantin S Bobrovsky <[email protected]>
1 parent a8d7b9a commit 44cbb8e

File tree

5 files changed

+166
-18
lines changed

5 files changed

+166
-18
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
; This test checks "integrated" invoke_simd support by the sycl-post-link tool:
2+
; - data flow analysis is able to determine actual target of
3+
; _Z33__regcall3____builtin_invoke_simd*
4+
; - functions shared by SYCL and ESIMD callgraphs get cloned and renamed, thus
5+
; making sure no functions are shared by the callgraphs (currently required by
6+
; IGC)
7+
8+
; RUN: sycl-post-link -lower-esimd -symbols -split=auto -S %s -o %t.table
9+
; RUN: FileCheck %s -input-file=%t.table
10+
; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-IR
11+
; RUN: FileCheck %s -input-file=%t_0.sym --check-prefixes CHECK-SYM
12+
13+
;------------- Verify generated table file.
14+
; CHECK: [Code|Properties|Symbols]
15+
; CHECK: {{.*}}_0.ll|{{.*}}_0.prop|{{.*}}_0.sym
16+
; CHECK-EMPTY:
17+
18+
;------------- Verify generated symbol file.
19+
; CHECK-SYM: SYCL_kernel
20+
; CHECK-SYM: _SIMD_CALLEE
21+
; CHECK-SYM: ESIMD_kernel
22+
; CHECK-SYM-EMPTY:
23+
24+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
25+
target triple = "spir64-unknown-unknown"
26+
27+
; Function Attrs: convergent
28+
29+
declare dso_local spir_func <4 x float> @__intrin(i64)
30+
31+
define dso_local spir_func <4 x float> @block_read(i64 %addr) noinline {
32+
;------------- This function is participates in both SYCL and ESIMD callgraphs -
33+
;------------- check that it is duplicated:
34+
; CHECK-IR-DAG: define dso_local spir_func <4 x float> @block_read.esimd
35+
%res = call spir_func <4 x float> @__intrin(i64 %addr)
36+
ret <4 x float> %res
37+
}
38+
39+
; Function Attrs: convergent
40+
declare dso_local spir_func float @_Z33__regcall3____builtin_invoke_simdXX(<4 x float> (float addrspace(4)*, <4 x float>, i32)*, float addrspace(4)*, float, i32) local_unnamed_addr
41+
42+
; Function Attrs: convergent mustprogress noinline norecurse optnone
43+
define dso_local x86_regcallcc <4 x float> @_SIMD_CALLEE(float addrspace(4)* %A, <4 x float> %non_uni_val, i32 %uni_val) #0 !sycl_explicit_simd !0 !intel_reqd_sub_group_size !0 {
44+
; Verify that correct attributes are attached to the function:
45+
; CHECK-IR-DAG: {{.*}} @_SIMD_CALLEE(float addrspace(4)* %A, <4 x float> %non_uni_val, i32 %uni_val) #[[ATTR1:[0-9]+]]
46+
entry:
47+
%AA = ptrtoint float addrspace(4)* %A to i64
48+
%ii = zext i32 %uni_val to i64
49+
%addr = add nuw nsw i64 %ii, %AA
50+
%data = call spir_func <4 x float> @block_read(i64 %addr)
51+
%add = fadd <4 x float> %non_uni_val, %data
52+
ret <4 x float> %add
53+
}
54+
55+
define internal spir_func float @foo(float addrspace(1)* %ptr) align 2 {
56+
entry:
57+
;------------- Typical data flow of the @_SIMD_CALLEE function address in worst
58+
;------------- case (-O0), when invoke_simd uses function name:
59+
;------------- float res = invoke_simd(sg, SIMD_CALLEE, uniform{ A }, x, uniform{ y });
60+
%f.addr.i = alloca <4 x float> (float addrspace(4)*, <4 x float>, i32)*, align 8
61+
%f.addr.ascast.i = addrspacecast <4 x float> (float addrspace(4)*, <4 x float>, i32)** %f.addr.i to <4 x float> (float addrspace(4)*, <4 x float>, i32)* addrspace(4)*
62+
store <4 x float> (float addrspace(4)*, <4 x float>, i32)* @_SIMD_CALLEE, <4 x float> (float addrspace(4)*, <4 x float>, i32)* addrspace(4)* %f.addr.ascast.i, align 8
63+
%FUNC_PTR = load <4 x float> (float addrspace(4)*, <4 x float>, i32)*, <4 x float> (float addrspace(4)*, <4 x float>, i32)* addrspace(4)* %f.addr.ascast.i, align 8
64+
65+
;------------- Data flow for the parameters of SIMD_CALLEE
66+
%param_A = addrspacecast float addrspace(1)* %ptr to float addrspace(4)*
67+
%param_non_uni_val = load float, float addrspace(4)* %param_A, align 4
68+
69+
;------------- The invoke_simd calls.
70+
%res1 = call spir_func float @_Z33__regcall3____builtin_invoke_simdXX(<4 x float> (float addrspace(4)*, <4 x float>, i32)* %FUNC_PTR, float addrspace(4)* %param_A, float %param_non_uni_val, i32 10)
71+
; Verify that %FUNC_PTR is replaced with @_SIMD_CALLEE:
72+
; CHECK-IR-DAG: %{{.*}} = call spir_func float @_Z33__regcall3____builtin_invoke_simdXX(<4 x float> (float addrspace(4)*, <4 x float>, i32)* @_SIMD_CALLEE, float addrspace(4)* %param_A, float %param_non_uni_val, i32 10)
73+
ret float %res1
74+
}
75+
76+
;------------- SYCL kernel, an entry point
77+
define dso_local spir_kernel void @SYCL_kernel(float addrspace(1)* %ptr) #2 {
78+
entry:
79+
%res1 = call spir_func float @foo(float addrspace(1)* %ptr)
80+
%ptri = ptrtoint float addrspace(1)* %ptr to i64
81+
%vec = call spir_func <4 x float> @block_read(i64 %ptri)
82+
%res2 = extractelement <4 x float> %vec, i32 0
83+
%res = fadd float %res1, %res2
84+
store float %res, float addrspace(1)* %ptr
85+
ret void
86+
}
87+
88+
;------------- ESIMD kernel, an entry point
89+
define dso_local spir_kernel void @ESIMD_kernel(float addrspace(1)* %ptr) #3 !sycl_explicit_simd !0 {
90+
entry:
91+
%ptr_as4 = addrspacecast float addrspace(1)* %ptr to float addrspace(4)*
92+
%res = call x86_regcallcc <4 x float> @_SIMD_CALLEE(float addrspace(4)* %ptr_as4, <4 x float> <float 1.0, float 1.0, float 1.0, float 1.0>, i32 0)
93+
%ptr_x16 = bitcast float addrspace(4)* %ptr_as4 to <4 x float> addrspace(4)*
94+
store <4 x float> %res, <4 x float> addrspace(4)* %ptr_x16
95+
ret void
96+
}
97+
98+
99+
; Check that VCStackCall attribute is added to the invoke_simd target functions:
100+
attributes #0 = { convergent mustprogress norecurse "sycl-module-id"="invoke_simd.cpp" }
101+
; CHECK-IR-DAG: attributes #[[ATTR1]] = { {{.*}} "VCStackCall" {{.*}}"sycl-module-id"="invoke_simd.cpp" }
102+
103+
attributes #2 = { "sycl-module-id"="a.cpp" }
104+
attributes #3 = { "sycl-module-id"="b.cpp" }
105+
106+
107+
!0 = !{}

llvm/test/tools/sycl-post-link/sycl-esimd/sycl-esimd-split-per-source.ll

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
; RUN: sycl-post-link -split-esimd -split=source -S %s -o %t.table
22
; RUN: FileCheck %s -input-file=%t.table
3-
; RxUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-SYCL-IR-0
4-
; RxUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-SYCL-IR-1
5-
; RxUN: FileCheck %s -input-file=%t_esimd_0.ll --check-prefixes CHECK-ESIMD-IR-0
6-
; RxUN: FileCheck %s -input-file=%t_esimd_1.ll --check-prefixes CHECK-ESIMD-IR-1
3+
; RUN: FileCheck %s -input-file=%t_0.ll --check-prefixes CHECK-SYCL-IR-0
4+
; RUN: FileCheck %s -input-file=%t_1.ll --check-prefixes CHECK-SYCL-IR-1
5+
; RUN: FileCheck %s -input-file=%t_esimd_0.ll --check-prefixes CHECK-ESIMD-IR-0
6+
; RUN: FileCheck %s -input-file=%t_esimd_1.ll --check-prefixes CHECK-ESIMD-IR-1
77

88
; This test checks that after we split SYCL and ESIMD kernels into
99
; separate modules, we split those two modules further according to
@@ -71,16 +71,16 @@ attributes #1 = { "sycl-module-id"="b.cpp" }
7171
; CHECK-DAG: {{.*}}tmp_esimd_0.ll|{{.*}}_esimd_0.prop
7272
; CHECK-DAG: {{.*}}tmp_esimd_1.ll|{{.*}}_esimd_1.prop
7373

74-
; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel1()
75-
; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel2()
76-
; CHECK-SYCL-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()
77-
78-
; CHECK-SYCL-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel3()
74+
; CHECK-SYCL-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel1()
75+
; CHECK-SYCL-IR-1-DAG: define dso_local spir_kernel void @SYCL_kernel2()
7976
; CHECK-SYCL-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()
8077

81-
; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel1()
82-
; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel2()
83-
; CHECK-ESIMD-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()
78+
; CHECK-SYCL-IR-0-DAG: define dso_local spir_kernel void @SYCL_kernel3()
79+
; CHECK-SYCL-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()
8480

85-
; CHECK-ESIMD-IR-1-DAG: define dso_local spir_kernel void @ESIMD_kernel3()
81+
; CHECK-ESIMD-IR-1-DAG: define dso_local spir_kernel void @ESIMD_kernel1()
82+
; CHECK-ESIMD-IR-1-DAG: define dso_local spir_kernel void @ESIMD_kernel2()
8683
; CHECK-ESIMD-IR-1-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()
84+
85+
; CHECK-ESIMD-IR-0-DAG: define dso_local spir_kernel void @ESIMD_kernel3()
86+
; CHECK-ESIMD-IR-0-DAG: declare dso_local spir_func i64 @_Z28__spirv_GlobalInvocationId_xv()

llvm/tools/sycl-post-link/ModuleSplitter.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,39 @@ void dumpEntryPoints(const Module &M, bool OnlyKernelsAreEntryPoints,
472472
llvm::errs() << "}\n";
473473
}
474474

475-
// Updates Props.HasEsimd to ESIMDStatus::ESIMD_ONLY/SYCL_ONLY if this module
476-
// descriptor is a ESIMD/SYCL part of the ESIMD/SYCL module split. Otherwise
477-
// assumes the module has both SYCL and ESIMD.
475+
void ModuleDesc::renameDuplicatesOf(const Module &MA, StringRef Suff) {
476+
Module &MB = getModule();
477+
#ifndef _NDEBUG
478+
DenseSet<StringRef> EntryNamesB;
479+
auto It0 = entries().cbegin();
480+
auto It1 = entries().cend();
481+
std::for_each(It0, It1,
482+
[&](const Function *F) { EntryNamesB.insert(F->getName()); });
483+
#endif // _NDEBUG
484+
for (const GlobalObject &GoA : MA.global_objects()) {
485+
if (GoA.isDeclaration()) {
486+
continue;
487+
}
488+
StringRef Name = GoA.getName();
489+
auto *F = dyn_cast<Function>(&GoA);
490+
GlobalObject *GoB =
491+
F ? cast_or_null<GlobalObject>(MB.getFunction(Name))
492+
: cast_or_null<GlobalObject>(MB.getGlobalVariable(Name));
493+
494+
if (!GoB || GoB->isDeclaration()) {
495+
// function or variable is not shared or is a declaration in MB
496+
continue;
497+
}
498+
#ifndef _NDEBUG
499+
if (F) {
500+
// this is a shared function, must not be an entry point:
501+
assert(!EntryNamesB.contains(Name));
502+
}
503+
#endif // _NDEBUG
504+
// rename the global object in MB:
505+
GoB->setName(Name + Suff);
506+
}
507+
}
478508

479509
void ModuleDesc::assignESIMDProperty() {
480510
if (EntryPoints.isEsimd()) {

llvm/tools/sycl-post-link/ModuleSplitter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class ModuleDesc {
8080
rebuildEntryPoints(Names);
8181
}
8282

83+
bool isESIMD() const { return Props.HasEsimd == ESIMDStatus::ESIMD_ONLY; }
84+
bool isSYCL() const { return Props.HasEsimd == ESIMDStatus::SYCL_ONLY; }
8385
const EntryPointVec &entries() const { return EntryPoints.Functions; }
8486
EntryPointVec &entries() { return EntryPoints.Functions; }
8587
Module &getModule() { return *M; }
@@ -101,6 +103,8 @@ class ModuleDesc {
101103

102104
void rebuildEntryPoints(const Module &M) { EntryPoints.rebuild(M); }
103105

106+
void renameDuplicatesOf(const Module &M, StringRef Suff);
107+
104108
// Updates Props.HasEsimd to ESIMDStatus::ESIMD_ONLY/SYCL_ONLY if this module
105109
// descriptor is a ESIMD/SYCL part of the ESIMD/SYCL module split. Otherwise
106110
// assumes the module has both SYCL and ESIMD.

llvm/tools/sycl-post-link/sycl-post-link.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,8 @@ processInputModule(std::unique_ptr<Module> M) {
722722
// undergo different set of LLVMIR passes. After this they are linked back
723723
// together to form single module with disjoint SYCL and ESIMD call graphs
724724
// unless --split-esimd option is specified. The graphs become disjoint when
725-
// linked back because functions shared between graphs are cloned.
725+
// linked back because functions shared between graphs are cloned and
726+
// renamed.
726727
std::unique_ptr<module_split::ModuleSplitterBase> ESIMDSplitter =
727728
module_split::getSplitterByKernelType(
728729
std::move(MDesc.releaseModulePtr()), EmitOnlyKernelsAsEntryPoints,
@@ -750,8 +751,14 @@ processInputModule(std::unique_ptr<Module> M) {
750751
#endif // _NDEBUG
751752

752753
if (!SplitEsimd && (MMs.size() > 1)) {
753-
// SYCL/ESIMD splitting is not requested, link back into single module
754+
// SYCL/ESIMD splitting is not requested, link back into single module.
754755
assert(MMs.size() == 2);
756+
assert(MMs[0].isESIMD() && MMs[1].isSYCL() ||
757+
MMs[1].isESIMD() && MMs[0].isSYCL());
758+
int ESIMDInd = MMs[0].isESIMD() ? 0 : 1;
759+
int SYCLInd = MMs[0].isESIMD() ? 1 : 0;
760+
// ... but before that, make sure no link conflicts will occur.
761+
MMs[ESIMDInd].renameDuplicatesOf(MMs[SYCLInd].getModule(), ".esimd");
755762
module_split::ModuleDesc M2 = link(std::move(MMs[0]), std::move(MMs[1]));
756763
MMs.clear();
757764
MMs.emplace_back(std::move(M2));

0 commit comments

Comments
 (0)