Skip to content

Commit 7161327

Browse files
vmaksimosys-ce-bb
authored andcommitted
Support syncscope LLVM argument of atomic instructions (#2222)
This fixes #1728 Original commit: KhronosGroup/SPIRV-LLVM-Translator@dd1f598
1 parent 151938f commit 7161327

File tree

6 files changed

+235
-18
lines changed

6 files changed

+235
-18
lines changed

llvm-spirv/lib/SPIRV/OCLUtil.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ template <> void SPIRVMap<OCLScopeKind, Scope>::init() {
145145
add(OCLMS_sub_group, ScopeSubgroup);
146146
}
147147

148+
template <> void SPIRVMap<std::string, Scope>::init() {
149+
add("work_item", ScopeInvocation);
150+
add("workgroup", ScopeWorkgroup);
151+
add("device", ScopeDevice);
152+
add("all_svm_devices", ScopeCrossDevice);
153+
add("subgroup", ScopeSubgroup);
154+
}
155+
148156
template <> void SPIRVMap<std::string, SPIRVGroupOperationKind>::init() {
149157
add("reduce", GroupOperationReduce);
150158
add("scan_inclusive", GroupOperationInclusiveScan);

llvm-spirv/lib/SPIRV/OCLUtil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ typedef SPIRVMap<OCLMemOrderKind, unsigned, MemorySemanticsMask> OCLMemOrderMap;
127127

128128
typedef SPIRVMap<OCLScopeKind, Scope> OCLMemScopeMap;
129129

130+
typedef SPIRVMap<std::string, Scope> OCLStrMemScopeMap;
131+
130132
typedef SPIRVMap<std::string, SPIRVGroupOperationKind>
131133
SPIRSPIRVGroupOperationMap;
132134

llvm-spirv/lib/SPIRV/SPIRVRegularizeLLVM.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -511,13 +511,13 @@ bool SPIRVRegularizeLLVMBase::regularize() {
511511
// %1 = insertvalue { i32, i1 } undef, i32 %cmpxchg.res, 0
512512
// %2 = insertvalue { i32, i1 } %1, i1 %cmpxchg.success, 1
513513

514-
// To get memory scope argument we might use Cmpxchg->getSyncScopeID()
514+
// To get memory scope argument we use Cmpxchg->getSyncScopeID()
515515
// but LLVM's cmpxchg instruction is not aware of OpenCL(or SPIR-V)
516-
// memory scope enumeration. And assuming the produced SPIR-V module
517-
// will be consumed in an OpenCL environment, we can use the same
518-
// memory scope as OpenCL atomic functions that do not have
519-
// memory_scope argument, i.e. memory_scope_device. See the OpenCL C
520-
// specification p6.13.11. Atomic Functions
516+
// memory scope enumeration. If the scope is not set and assuming the
517+
// produced SPIR-V module will be consumed in an OpenCL environment,
518+
// we can use the same memory scope as OpenCL atomic functions that do
519+
// not have memory_scope argument, i.e. memory_scope_device. See the
520+
// OpenCL C specification p6.13.11. Atomic Functions
521521

522522
// cmpxchg LLVM instruction returns a pair {i32, i1}: the original
523523
// value and a flag indicating success (true) or failure (false).
@@ -529,7 +529,16 @@ bool SPIRVRegularizeLLVMBase::regularize() {
529529
// comparator, which matches with semantics of the flag returned by
530530
// cmpxchg.
531531
Value *Ptr = Cmpxchg->getPointerOperand();
532-
Value *MemoryScope = getInt32(M, spv::ScopeDevice);
532+
SmallVector<StringRef> SSIDs;
533+
Cmpxchg->getContext().getSyncScopeNames(SSIDs);
534+
535+
spv::Scope S;
536+
// Fill unknown syncscope value to default Device scope.
537+
if (!OCLStrMemScopeMap::find(SSIDs[Cmpxchg->getSyncScopeID()].str(),
538+
&S)) {
539+
S = ScopeDevice;
540+
}
541+
Value *MemoryScope = getInt32(M, S);
533542
auto SuccessOrder = static_cast<OCLMemOrderKind>(
534543
llvm::toCABI(Cmpxchg->getSuccessOrdering()));
535544
auto FailureOrder = static_cast<OCLMemOrderKind>(

llvm-spirv/lib/SPIRV/SPIRVWriter.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,8 +1744,16 @@ static int transAtomicOrdering(llvm::AtomicOrdering Ordering) {
17441744

17451745
SPIRVValue *LLVMToSPIRVBase::transAtomicStore(StoreInst *ST,
17461746
SPIRVBasicBlock *BB) {
1747-
std::vector<Value *> Ops{ST->getPointerOperand(),
1748-
getUInt32(M, spv::ScopeDevice),
1747+
SmallVector<StringRef> SSIDs;
1748+
ST->getContext().getSyncScopeNames(SSIDs);
1749+
1750+
spv::Scope S;
1751+
// Fill unknown syncscope value to default Device scope.
1752+
if (!OCLStrMemScopeMap::find(SSIDs[ST->getSyncScopeID()].str(), &S)) {
1753+
S = ScopeDevice;
1754+
}
1755+
1756+
std::vector<Value *> Ops{ST->getPointerOperand(), getUInt32(M, S),
17491757
getUInt32(M, transAtomicOrdering(ST->getOrdering())),
17501758
ST->getValueOperand()};
17511759
std::vector<SPIRVValue *> SPIRVOps = transValue(Ops, BB);
@@ -1756,8 +1764,17 @@ SPIRVValue *LLVMToSPIRVBase::transAtomicStore(StoreInst *ST,
17561764

17571765
SPIRVValue *LLVMToSPIRVBase::transAtomicLoad(LoadInst *LD,
17581766
SPIRVBasicBlock *BB) {
1767+
SmallVector<StringRef> SSIDs;
1768+
LD->getContext().getSyncScopeNames(SSIDs);
1769+
1770+
spv::Scope S;
1771+
// Fill unknown syncscope value to default Device scope.
1772+
if (!OCLStrMemScopeMap::find(SSIDs[LD->getSyncScopeID()].str(), &S)) {
1773+
S = ScopeDevice;
1774+
}
1775+
17591776
std::vector<Value *> Ops{
1760-
LD->getPointerOperand(), getUInt32(M, spv::ScopeDevice),
1777+
LD->getPointerOperand(), getUInt32(M, S),
17611778
getUInt32(M, transAtomicOrdering(LD->getOrdering()))};
17621779
std::vector<SPIRVValue *> SPIRVOps = transValue(Ops, BB);
17631780

@@ -2366,13 +2383,22 @@ LLVMToSPIRVBase::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB,
23662383
auto MemSem = OCLMemOrderMap::map(static_cast<OCLMemOrderKind>(Ordering));
23672384
std::vector<Value *> Operands(4);
23682385
Operands[0] = ARMW->getPointerOperand();
2369-
// To get the memory scope argument we might use ARMW->getSyncScopeID(), but
2386+
// To get the memory scope argument we use ARMW->getSyncScopeID(), but
23702387
// atomicrmw LLVM instruction is not aware of OpenCL(or SPIR-V) memory scope
2371-
// enumeration. And assuming the produced SPIR-V module will be consumed in
2372-
// an OpenCL environment, we can use the same memory scope as OpenCL atomic
2373-
// functions that don't have memory_scope argument i.e. memory_scope_device.
2374-
// See the OpenCL C specification p6.13.11. "Atomic Functions"
2375-
Operands[1] = getUInt32(M, spv::ScopeDevice);
2388+
// enumeration. If the scope is not set and assuming the produced SPIR-V
2389+
// module will be consumed in an OpenCL environment, we can use the same
2390+
// memory scope as OpenCL atomic functions that don't have memory_scope
2391+
// argument i.e. memory_scope_device. See the OpenCL C specification
2392+
// p6.13.11. "Atomic Functions"
2393+
SmallVector<StringRef> SSIDs;
2394+
ARMW->getContext().getSyncScopeNames(SSIDs);
2395+
2396+
spv::Scope S;
2397+
// Fill unknown syncscope value to default Device scope.
2398+
if (!OCLStrMemScopeMap::find(SSIDs[ARMW->getSyncScopeID()].str(), &S)) {
2399+
S = ScopeDevice;
2400+
}
2401+
Operands[1] = getUInt32(M, S);
23762402
Operands[2] = getUInt32(M, MemSem);
23772403
Operands[3] = ARMW->getValOperand();
23782404
std::vector<SPIRVValue *> OpVals = transValue(Operands, BB);
@@ -4881,8 +4907,15 @@ SPIRVValue *LLVMToSPIRVBase::transFenceInst(FenceInst *FI,
48814907
}
48824908

48834909
Module *M = FI->getParent()->getModule();
4884-
// Treat all llvm.fence instructions as having CrossDevice scope:
4885-
SPIRVValue *RetScope = transConstant(getUInt32(M, ScopeCrossDevice));
4910+
SmallVector<StringRef> SSIDs;
4911+
FI->getContext().getSyncScopeNames(SSIDs);
4912+
spv::Scope S;
4913+
// Treat all llvm.fence instructions as having CrossDevice scope by default
4914+
if (!OCLStrMemScopeMap::find(SSIDs[FI->getSyncScopeID()].str(), &S)) {
4915+
S = ScopeCrossDevice;
4916+
}
4917+
4918+
SPIRVValue *RetScope = transConstant(getUInt32(M, S));
48864919
SPIRVValue *Val = transConstant(getUInt32(M, MemorySemantics));
48874920
return BM->addMemoryBarrierInst(static_cast<Scope>(RetScope->getId()),
48884921
Val->getId(), BB);
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv %t.bc --spirv-ext=+SPV_EXT_shader_atomic_float_add -o %t.spv
3+
; RUN: spirv-val %t.spv
4+
; RUN: llvm-spirv %t.spv -to-text -o %t.spt
5+
; RUN: FileCheck < %t.spt %s -check-prefix=CHECK-SPIRV
6+
; RUN: llvm-spirv %t.spv -r --spirv-target-env=CL2.0 -o - | llvm-dis -o %t.rev.ll
7+
; RUN: FileCheck < %t.rev.ll %s -check-prefix=CHECK-LLVM
8+
9+
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
10+
target triple = "spir64"
11+
12+
@j = local_unnamed_addr addrspace(1) global i32 0, align 4
13+
14+
; SPIRV scopes
15+
; 0 - ScopeCrossDevice (all svm devices)
16+
; 1 - ScopeDevice
17+
; 2 - ScopeWorkGroup
18+
; 3 - ScopeSubgroup
19+
; 4 - ScopeInvocation (mapped from work item)
20+
21+
; OCL scopes
22+
; 0 - work_item
23+
; 1 - work_group
24+
; 2 - device
25+
; 3 - all_svm_devices
26+
; 4 - sub_group
27+
28+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt0:]] 0
29+
; CHECK-SPIRV-DAG: Constant [[#]] [[#SequentiallyConsistent:]] 16
30+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt1:]] 1
31+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt2:]] 2
32+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt3:]] 3
33+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt4:]] 4
34+
; CHECK-SPIRV-DAG: Constant [[#]] [[#Const2Power30:]] 1073741824
35+
; CHECK-SPIRV-DAG: Constant [[#]] [[#ConstInt42:]] 42
36+
37+
; AtomicLoad ResTypeId ResId PtrId MemScopeId MemSemanticsId
38+
; CHECK-SPIRV: AtomicLoad [[#]] [[#]] [[#]] [[#ConstInt2]] [[#SequentiallyConsistent]]
39+
; CHECK-SPIRV: AtomicLoad [[#]] [[#]] [[#]] [[#ConstInt1]] [[#SequentiallyConsistent]]
40+
; CHECK-SPIRV: AtomicLoad [[#]] [[#]] [[#]] [[#ConstInt1]] [[#SequentiallyConsistent]]
41+
; CHECK-SPIRV: AtomicLoad [[#]] [[#]] [[#]] [[#ConstInt3]] [[#SequentiallyConsistent]]
42+
43+
; CHECK-LLVM: call spir_func i32 @_Z20atomic_load_explicitPU3AS4VU7_Atomici12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 1)
44+
; CHECK-LLVM: call spir_func i32 @_Z20atomic_load_explicitPU3AS4VU7_Atomici12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 2)
45+
; CHECK-LLVM: call spir_func i32 @_Z20atomic_load_explicitPU3AS4VU7_Atomici12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 2)
46+
; CHECK-LLVM: call spir_func i32 @_Z20atomic_load_explicitPU3AS4VU7_Atomici12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 4)
47+
48+
define dso_local void @fi1(ptr addrspace(4) nocapture noundef readonly %i) local_unnamed_addr #0 {
49+
entry:
50+
%0 = load atomic i32, ptr addrspace(4) %i syncscope("workgroup") seq_cst, align 4
51+
%1 = load atomic i32, ptr addrspace(4) %i syncscope("device") seq_cst, align 4
52+
%2 = load atomic i32, ptr addrspace(4) %i seq_cst, align 4
53+
%3 = load atomic i32, ptr addrspace(4) %i syncscope("subgroup") seq_cst, align 4
54+
ret void
55+
}
56+
57+
; AtomicStore PtrId MemScopeId MemSemanticsId ValueId
58+
; CHECK-SPIRV: AtomicStore [[#]] [[#ConstInt3]] [[#SequentiallyConsistent]] [[#ConstInt1]]
59+
; CHECK-SPIRV: AtomicStore [[#]] [[#ConstInt2]] [[#SequentiallyConsistent]] [[#ConstInt1]]
60+
; CHECK-LLVM: call spir_func void @_Z21atomic_store_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 4)
61+
; CHECK-LLVM: call spir_func void @_Z21atomic_store_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 5, i32 1)
62+
63+
define dso_local void @test_addr(ptr addrspace(1) nocapture noundef writeonly %ig, ptr addrspace(3) nocapture noundef writeonly %il) local_unnamed_addr #0 {
64+
entry:
65+
store atomic i32 1, ptr addrspace(1) %ig syncscope("subgroup") seq_cst, align 4
66+
store atomic i32 1, ptr addrspace(3) %il syncscope("workgroup") seq_cst, align 4
67+
ret void
68+
}
69+
70+
; Atomic* ResTypeId ResId PtrId MemScopeId MemSemanticsId ValueId
71+
; CHECK-SPIRV: AtomicAnd [[#]] [[#]] [[#]] [[#ConstInt4]] [[#SequentiallyConsistent]] [[#ConstInt1]]
72+
; CHECK-SPIRV: AtomicSMin [[#]] [[#]] [[#]] [[#ConstInt0]] [[#SequentiallyConsistent]] [[#ConstInt1]]
73+
; CHECK-SPIRV: AtomicSMax [[#]] [[#]] [[#]] [[#ConstInt1]] [[#SequentiallyConsistent]] [[#ConstInt1]]
74+
; CHECK-SPIRV: AtomicUMin [[#]] [[#]] [[#]] [[#ConstInt2]] [[#SequentiallyConsistent]] [[#ConstInt1]]
75+
; CHECK-SPIRV: AtomicUMax [[#]] [[#]] [[#]] [[#ConstInt2]] [[#SequentiallyConsistent]] [[#ConstInt1]]
76+
77+
; CHECK-LLVM: call spir_func i32 @_Z25atomic_fetch_and_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 1, i32 5, i32 0)
78+
; CHECK-LLVM: call spir_func i32 @_Z25atomic_fetch_min_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 1, i32 5, i32 3)
79+
; CHECK-LLVM: call spir_func i32 @_Z25atomic_fetch_max_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 1, i32 5, i32 2)
80+
; CHECK-LLVM: call spir_func i32 @_Z25atomic_fetch_min_explicitPU3AS4VU7_Atomicjj12memory_order12memory_scope(ptr{{.*}}, i32 1, i32 5, i32 1)
81+
; CHECK-LLVM: call spir_func i32 @_Z25atomic_fetch_max_explicitPU3AS4VU7_Atomicjj12memory_order12memory_scope(ptr{{.*}}, i32 1, i32 5, i32 1)
82+
83+
define dso_local void @fi3(ptr nocapture noundef %i, ptr nocapture noundef %ui) local_unnamed_addr #0 {
84+
entry:
85+
%0 = atomicrmw and ptr %i, i32 1 syncscope("work_item") seq_cst, align 4
86+
%1 = atomicrmw min ptr %i, i32 1 syncscope("all_svm_devices") seq_cst, align 4
87+
%2 = atomicrmw max ptr %i, i32 1 syncscope("wrong_scope") seq_cst, align 4
88+
%3 = atomicrmw umin ptr %ui, i32 1 syncscope("workgroup") seq_cst, align 4
89+
%4 = atomicrmw umax ptr %ui, i32 1 syncscope("workgroup") seq_cst, align 4
90+
ret void
91+
}
92+
93+
; AtomicCompareExchange ResTypeId ResId PtrId MemScopeId MemSemEqualId MemSemUnequalId ValueId ComparatorId
94+
; CHECK-SPIRV: AtomicCompareExchange [[#]] [[#]] [[#]] [[#ConstInt2]] [[#ConstInt2]] [[#ConstInt2]] [[#ConstInt1]] [[#ConstInt0]]
95+
; CHECK-LLVM: call spir_func i1 @_Z39atomic_compare_exchange_strong_explicitPU3AS4VU7_AtomiciPU3AS4ii12memory_orderS4_12memory_scope(ptr{{.*}}, ptr{{.*}}, i32 1, i32 2, i32 2, i32 1)
96+
97+
define dso_local zeroext i1 @fi4(ptr nocapture noundef %i) local_unnamed_addr #0 {
98+
entry:
99+
%0 = cmpxchg ptr %i, i32 0, i32 1 syncscope("workgroup") acquire acquire, align 4
100+
%1 = extractvalue { i32, i1 } %0, 1
101+
ret i1 %1
102+
}
103+
104+
; AtomicExchange ResTypeId ResId PtrId MemScopeId MemSemanticsId ValueId
105+
; CHECK-SPIRV: AtomicExchange [[#]] [[#]] [[#]] [[#ConstInt2]] [[#SequentiallyConsistent]] [[#Const2Power30]]
106+
; CHECK-LLVM: call spir_func i32 @_Z24atomic_exchange_explicitPU3AS4VU7_Atomicii12memory_order12memory_scope(ptr{{.*}}, i32 1073741824, i32 5, i32 1)
107+
108+
define dso_local float @ff3(ptr nocapture noundef %d) local_unnamed_addr #0 {
109+
entry:
110+
%0 = atomicrmw xchg ptr %d, i32 1073741824 syncscope("workgroup") seq_cst, align 4
111+
%1 = bitcast i32 %0 to float
112+
ret float %1
113+
}
114+
115+
; AtomicFAddEXT ResTypeId ResId PtrId MemScopeId MemSemanticsId ValueId
116+
; CHECK-SPIRV: AtomicFAddEXT [[#]] [[#]] [[#]] [[#ConstInt2]] [[#ConstInt0]] [[#]]
117+
; CHECK-LLVM: call spir_func float @_Z25atomic_fetch_add_explicitPU3AS4VU7_Atomicff12memory_order12memory_scope(ptr{{.*}}, i32 0, i32 1)
118+
119+
define dso_local float @ff4(ptr addrspace(1) nocapture noundef %d, float noundef %a) local_unnamed_addr #0 {
120+
entry:
121+
%0 = atomicrmw fadd ptr addrspace(1) %d, float %a syncscope("workgroup") monotonic, align 4
122+
ret float %0
123+
}
124+
125+
; ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
126+
; define dso_local void @atomic_init_foo() local_unnamed_addr #1 {
127+
; entry:
128+
; store i32 42, ptr addrspace(1) @j, align 4
129+
; ret void
130+
; }
131+
132+
; Store PtrId ObjId MemOps+
133+
; CHECK-SPIRV: Store [[#]] [[#ConstInt42]]
134+
; CHECK-LLVM: store i32 42, ptr addrspace(1) @j
135+
136+
define dso_local void @atomic_init_foo() local_unnamed_addr #1 {
137+
entry:
138+
tail call spir_func void @_Z11atomic_initPU3AS4VU7_Atomicff(ptr addrspace(1) @j, i32 42)
139+
ret void
140+
}
141+
142+
; Function Attrs: convergent
143+
declare spir_func void @_Z11atomic_initPU3AS4VU7_Atomicff(ptr addrspace(1), i32) local_unnamed_addr
144+
145+
attributes #0 = { mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
146+
attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
147+
attributes #2 = { mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
148+
149+
!llvm.module.flags = !{!1}
150+
!opencl.ocl.version = !{!2}
151+
!llvm.ident = !{!3}
152+
153+
!1 = !{i32 1, !"wchar_size", i32 4}
154+
!2 = !{i32 2, i32 0}
155+
!3 = !{!"clang version 18.0.0 (https://github.com/llvm/llvm-project.git 7ce613fc77af092dd6e9db71ce3747b75bc5616e)"}
156+
!4 = !{!5, !5, i64 0}
157+
!5 = !{!"omnipotent char", !6, i64 0}
158+
!6 = !{!"Simple C/C++ TBAA"}

llvm-spirv/test/transcoding/fence_inst.ll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
; 0x0 CrossDevice
1515
; CHECK-SPIRV: Constant [[#UINT]] [[#CD:]] 0
1616

17+
; 0x2 Workgroup
1718
; CHECK-SPIRV: Constant [[#UINT]] [[#ID1:]] 2
1819
; CHECK-SPIRV: Constant [[#UINT]] [[#ID2:]] 4
1920
; CHECK-SPIRV: Constant [[#UINT]] [[#ID3:]] 8
@@ -23,6 +24,7 @@
2324
; CHECK-SPIRV: MemoryBarrier [[#CD]] [[#ID2]]
2425
; CHECK-SPIRV: MemoryBarrier [[#CD]] [[#ID3]]
2526
; CHECK-SPIRV: MemoryBarrier [[#CD]] [[#ID4]]
27+
; CHECK-SPIRV: MemoryBarrier [[#ID1]] [[#ID2]]
2628

2729

2830
; CHECK-LLVM: define spir_kernel void @fence_test_kernel1{{.*}} #0 {{.*}}
@@ -65,3 +67,8 @@ define spir_kernel void @fence_test_kernel4(ptr addrspace(1) noalias %s.ascast)
6567
ret void
6668
}
6769

70+
define spir_kernel void @fence_test_kernel5(ptr addrspace(1) noalias %s.ascast) {
71+
fence syncscope("workgroup") release
72+
ret void
73+
}
74+

0 commit comments

Comments
 (0)