Skip to content

Commit dd1f598

Browse files
authored
Support syncscope LLVM argument of atomic instructions (#2222)
This fixes #1728
1 parent 467edf9 commit dd1f598

File tree

6 files changed

+235
-18
lines changed

6 files changed

+235
-18
lines changed

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);

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

lib/SPIRV/SPIRVRegularizeLLVM.cpp

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

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

460460
// cmpxchg LLVM instruction returns a pair {i32, i1}: the original
461461
// value and a flag indicating success (true) or failure (false).
@@ -467,7 +467,16 @@ bool SPIRVRegularizeLLVMBase::regularize() {
467467
// comparator, which matches with semantics of the flag returned by
468468
// cmpxchg.
469469
Value *Ptr = Cmpxchg->getPointerOperand();
470-
Value *MemoryScope = getInt32(M, spv::ScopeDevice);
470+
SmallVector<StringRef> SSIDs;
471+
Cmpxchg->getContext().getSyncScopeNames(SSIDs);
472+
473+
spv::Scope S;
474+
// Fill unknown syncscope value to default Device scope.
475+
if (!OCLStrMemScopeMap::find(SSIDs[Cmpxchg->getSyncScopeID()].str(),
476+
&S)) {
477+
S = ScopeDevice;
478+
}
479+
Value *MemoryScope = getInt32(M, S);
471480
auto SuccessOrder = static_cast<OCLMemOrderKind>(
472481
llvm::toCABI(Cmpxchg->getSuccessOrdering()));
473482
auto FailureOrder = static_cast<OCLMemOrderKind>(

lib/SPIRV/SPIRVWriter.cpp

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

17301730
SPIRVValue *LLVMToSPIRVBase::transAtomicStore(StoreInst *ST,
17311731
SPIRVBasicBlock *BB) {
1732-
std::vector<Value *> Ops{ST->getPointerOperand(),
1733-
getUInt32(M, spv::ScopeDevice),
1732+
SmallVector<StringRef> SSIDs;
1733+
ST->getContext().getSyncScopeNames(SSIDs);
1734+
1735+
spv::Scope S;
1736+
// Fill unknown syncscope value to default Device scope.
1737+
if (!OCLStrMemScopeMap::find(SSIDs[ST->getSyncScopeID()].str(), &S)) {
1738+
S = ScopeDevice;
1739+
}
1740+
1741+
std::vector<Value *> Ops{ST->getPointerOperand(), getUInt32(M, S),
17341742
getUInt32(M, transAtomicOrdering(ST->getOrdering())),
17351743
ST->getValueOperand()};
17361744
std::vector<SPIRVValue *> SPIRVOps = transValue(Ops, BB);
@@ -1741,8 +1749,17 @@ SPIRVValue *LLVMToSPIRVBase::transAtomicStore(StoreInst *ST,
17411749

17421750
SPIRVValue *LLVMToSPIRVBase::transAtomicLoad(LoadInst *LD,
17431751
SPIRVBasicBlock *BB) {
1752+
SmallVector<StringRef> SSIDs;
1753+
LD->getContext().getSyncScopeNames(SSIDs);
1754+
1755+
spv::Scope S;
1756+
// Fill unknown syncscope value to default Device scope.
1757+
if (!OCLStrMemScopeMap::find(SSIDs[LD->getSyncScopeID()].str(), &S)) {
1758+
S = ScopeDevice;
1759+
}
1760+
17441761
std::vector<Value *> Ops{
1745-
LD->getPointerOperand(), getUInt32(M, spv::ScopeDevice),
1762+
LD->getPointerOperand(), getUInt32(M, S),
17461763
getUInt32(M, transAtomicOrdering(LD->getOrdering()))};
17471764
std::vector<SPIRVValue *> SPIRVOps = transValue(Ops, BB);
17481765

@@ -2302,13 +2319,22 @@ LLVMToSPIRVBase::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB,
23022319
auto MemSem = OCLMemOrderMap::map(static_cast<OCLMemOrderKind>(Ordering));
23032320
std::vector<Value *> Operands(4);
23042321
Operands[0] = ARMW->getPointerOperand();
2305-
// To get the memory scope argument we might use ARMW->getSyncScopeID(), but
2322+
// To get the memory scope argument we use ARMW->getSyncScopeID(), but
23062323
// atomicrmw LLVM instruction is not aware of OpenCL(or SPIR-V) memory scope
2307-
// enumeration. And assuming the produced SPIR-V module will be consumed in
2308-
// an OpenCL environment, we can use the same memory scope as OpenCL atomic
2309-
// functions that don't have memory_scope argument i.e. memory_scope_device.
2310-
// See the OpenCL C specification p6.13.11. "Atomic Functions"
2311-
Operands[1] = getUInt32(M, spv::ScopeDevice);
2324+
// enumeration. If the scope is not set and assuming the produced SPIR-V
2325+
// module will be consumed in an OpenCL environment, we can use the same
2326+
// memory scope as OpenCL atomic functions that don't have memory_scope
2327+
// argument i.e. memory_scope_device. See the OpenCL C specification
2328+
// p6.13.11. "Atomic Functions"
2329+
SmallVector<StringRef> SSIDs;
2330+
ARMW->getContext().getSyncScopeNames(SSIDs);
2331+
2332+
spv::Scope S;
2333+
// Fill unknown syncscope value to default Device scope.
2334+
if (!OCLStrMemScopeMap::find(SSIDs[ARMW->getSyncScopeID()].str(), &S)) {
2335+
S = ScopeDevice;
2336+
}
2337+
Operands[1] = getUInt32(M, S);
23122338
Operands[2] = getUInt32(M, MemSem);
23132339
Operands[3] = ARMW->getValOperand();
23142340
std::vector<SPIRVValue *> OpVals = transValue(Operands, BB);
@@ -4827,8 +4853,15 @@ SPIRVValue *LLVMToSPIRVBase::transFenceInst(FenceInst *FI,
48274853
}
48284854

48294855
Module *M = FI->getParent()->getModule();
4830-
// Treat all llvm.fence instructions as having CrossDevice scope:
4831-
SPIRVValue *RetScope = transConstant(getUInt32(M, ScopeCrossDevice));
4856+
SmallVector<StringRef> SSIDs;
4857+
FI->getContext().getSyncScopeNames(SSIDs);
4858+
spv::Scope S;
4859+
// Treat all llvm.fence instructions as having CrossDevice scope by default
4860+
if (!OCLStrMemScopeMap::find(SSIDs[FI->getSyncScopeID()].str(), &S)) {
4861+
S = ScopeCrossDevice;
4862+
}
4863+
4864+
SPIRVValue *RetScope = transConstant(getUInt32(M, S));
48324865
SPIRVValue *Val = transConstant(getUInt32(M, MemorySemantics));
48334866
assert(RetScope && Val && "RetScope and Value are not constants");
48344867
return BM->addMemoryBarrierInst(static_cast<Scope>(RetScope->getId()),
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"}

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)