Skip to content

Commit 63268eb

Browse files
authored
Add support for split barriers extension SPV_INTEL_split_barrier (#332)
This backports KhronosGroup/SPIRV-LLVM-Translator#1424 Signed-off-by: haonanya <[email protected]>
1 parent 3dd3742 commit 63268eb

File tree

1 file changed

+375
-0
lines changed

1 file changed

+375
-0
lines changed
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
From d231d4aea8c48e0c5d15cf247980361c1d6c57c5 Mon Sep 17 00:00:00 2001
2+
From: haonanya <[email protected]>
3+
Date: Mon, 28 Feb 2022 18:20:31 +0800
4+
Subject: [PATCH] Add support for split barriers extension
5+
SPV_INTEL_split_barrier
6+
7+
Signed-off-by: haonanya <[email protected]>
8+
---
9+
include/LLVMSPIRVExtensions.inc | 1 +
10+
lib/SPIRV/OCL20ToSPIRV.cpp | 38 +++++++++++++++++++++++++++
11+
lib/SPIRV/OCLUtil.cpp | 23 +++++++++++++++-
12+
lib/SPIRV/OCLUtil.h | 20 ++++++++++++++
13+
lib/SPIRV/SPIRVReader.cpp | 3 ++-
14+
lib/SPIRV/SPIRVToOCL.cpp | 4 +++
15+
lib/SPIRV/SPIRVToOCL.h | 5 ++++
16+
lib/SPIRV/SPIRVToOCL12.cpp | 17 ++++++++++++
17+
lib/SPIRV/SPIRVToOCL20.cpp | 26 ++++++++++++++++++
18+
lib/SPIRV/libSPIRV/SPIRVInstruction.h | 18 +++++++++++++
19+
lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h | 1 +
20+
lib/SPIRV/libSPIRV/SPIRVOpCode.h | 5 ++++
21+
lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h | 2 ++
22+
13 files changed, 161 insertions(+), 2 deletions(-)
23+
24+
diff --git a/include/LLVMSPIRVExtensions.inc b/include/LLVMSPIRVExtensions.inc
25+
index 7580e1f8..aa56b29b 100644
26+
--- a/include/LLVMSPIRVExtensions.inc
27+
+++ b/include/LLVMSPIRVExtensions.inc
28+
@@ -28,3 +28,4 @@ EXT(SPV_INTEL_optnone)
29+
EXT(SPV_INTEL_arbitrary_precision_integers)
30+
EXT(SPV_INTEL_variable_length_array)
31+
EXT(SPV_INTEL_memory_access_aliasing)
32+
+EXT(SPV_INTEL_split_barrier)
33+
diff --git a/lib/SPIRV/OCL20ToSPIRV.cpp b/lib/SPIRV/OCL20ToSPIRV.cpp
34+
index b62b433c..cda026ae 100644
35+
--- a/lib/SPIRV/OCL20ToSPIRV.cpp
36+
+++ b/lib/SPIRV/OCL20ToSPIRV.cpp
37+
@@ -279,6 +279,9 @@ public:
38+
StringRef MangledName,
39+
const std::string &DemangledName);
40+
41+
+ /// For cl_intel_split_work_group_barrier built-ins:
42+
+ void visitCallSplitBarrierINTEL(CallInst *CI, StringRef DemangledName);
43+
+
44+
void visitCallLdexp(CallInst *CI, StringRef MangledName,
45+
StringRef DemangledName);
46+
47+
@@ -546,6 +549,10 @@ void OCL20ToSPIRV::visitCallInst(CallInst &CI) {
48+
visitSubgroupImageMediaBlockINTEL(&CI, DemangledName);
49+
return;
50+
}
51+
+ if (DemangledName.find(kOCLBuiltinName::SplitBarrierINTELPrefix) == 0) {
52+
+ visitCallSplitBarrierINTEL(&CI, DemangledName);
53+
+ return;
54+
+ }
55+
// Handle 'cl_intel_device_side_avc_motion_estimation' extension built-ins
56+
if (DemangledName.find(kOCLSubgroupsAVCIntel::Prefix) == 0 ||
57+
// Workaround for a bug in the extension specification
58+
@@ -563,6 +570,37 @@ void OCL20ToSPIRV::visitCallInst(CallInst &CI) {
59+
visitCallBuiltinSimple(&CI, MangledName, DemangledName);
60+
}
61+
62+
+void OCL20ToSPIRV::visitCallSplitBarrierINTEL(CallInst *CI,
63+
+ StringRef DemangledName) {
64+
+ auto Lit = getBarrierLiterals(CI);
65+
+ AttributeList Attrs = CI->getCalledFunction()->getAttributes();
66+
+ Op OpCode =
67+
+ StringSwitch<Op>(DemangledName)
68+
+ .Case("intel_work_group_barrier_arrive", OpControlBarrierArriveINTEL)
69+
+ .Case("intel_work_group_barrier_wait", OpControlBarrierWaitINTEL)
70+
+ .Default(OpNop);
71+
+
72+
+ mutateCallInstSPIRV(
73+
+ M, CI,
74+
+ [=](CallInst *, std::vector<Value *> &Args) {
75+
+ Args.resize(3);
76+
+ // Execution scope
77+
+ Args[0] = addInt32(map<Scope>(std::get<2>(Lit)));
78+
+ // Memory scope
79+
+ Args[1] = addInt32(map<Scope>(std::get<1>(Lit)));
80+
+ // Memory semantics
81+
+ // OpControlBarrierArriveINTEL -> Release,
82+
+ // OpControlBarrierWaitINTEL -> Acquire
83+
+ unsigned MemFenceFlag = std::get<0>(Lit);
84+
+ OCLMemOrderKind MemOrder = OpCode == OpControlBarrierArriveINTEL
85+
+ ? OCLMO_release
86+
+ : OCLMO_acquire;
87+
+ Args[2] = addInt32(mapOCLMemSemanticToSPIRV(MemFenceFlag, MemOrder));
88+
+ return getSPIRVFuncName(OpCode);
89+
+ },
90+
+ &Attrs);
91+
+}
92+
+
93+
void OCL20ToSPIRV::visitCallNDRange(CallInst *CI,
94+
const std::string &DemangledName) {
95+
assert(DemangledName.find(kOCLBuiltinName::NDRangePrefix) == 0);
96+
diff --git a/lib/SPIRV/OCLUtil.cpp b/lib/SPIRV/OCLUtil.cpp
97+
index fcdd16eb..f93338eb 100644
98+
--- a/lib/SPIRV/OCLUtil.cpp
99+
+++ b/lib/SPIRV/OCLUtil.cpp
100+
@@ -471,7 +471,9 @@ public:
101+
} else if (UnmangledName.find("barrier") != std::string::npos) {
102+
addUnsignedArg(0);
103+
if (UnmangledName == "work_group_barrier" ||
104+
- UnmangledName == "sub_group_barrier")
105+
+ UnmangledName == "sub_group_barrier" ||
106+
+ UnmangledName == "intel_work_group_barrier_arrive" ||
107+
+ UnmangledName == "intel_work_group_barrier_wait")
108+
setEnumArg(1, SPIR::PRIMITIVE_MEMORY_SCOPE);
109+
} else if (UnmangledName.find("atomic_work_item_fence") == 0) {
110+
addUnsignedArg(0);
111+
@@ -985,6 +987,25 @@ void insertImageNameAccessQualifier(SPIRVAccessQualifierKind Acc,
112+
}
113+
} // namespace OCLUtil
114+
115+
+Value *SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
116+
+ Value *MemorySemantics, Instruction *InsertBefore) {
117+
+ if (auto *C = dyn_cast<ConstantInt>(MemorySemantics)) {
118+
+ return ConstantInt::get(C->getType(),
119+
+ mapSPIRVMemSemanticToOCL(C->getZExtValue()).first);
120+
+ }
121+
+
122+
+ // TODO: any possible optimizations?
123+
+ // SPIR-V MemorySemantics contains both OCL mem_fence_flags and mem_order and
124+
+ // therefore, we need to apply mask
125+
+ int Mask = MemorySemanticsWorkgroupMemoryMask |
126+
+ MemorySemanticsCrossWorkgroupMemoryMask |
127+
+ MemorySemanticsImageMemoryMask;
128+
+ return getOrCreateSwitchFunc(kSPIRVName::TranslateSPIRVMemFence,
129+
+ MemorySemantics,
130+
+ OCLMemFenceExtendedMap::getRMap(),
131+
+ /* IsReverse */ true, None, InsertBefore, InsertBefore->getModule(), Mask);
132+
+}
133+
+
134+
void llvm::mangleOpenClBuiltin(const std::string &UniqName,
135+
ArrayRef<Type *> ArgTypes,
136+
std::string &MangledName) {
137+
diff --git a/lib/SPIRV/OCLUtil.h b/lib/SPIRV/OCLUtil.h
138+
index 9100b5d2..4e585cee 100644
139+
--- a/lib/SPIRV/OCLUtil.h
140+
+++ b/lib/SPIRV/OCLUtil.h
141+
@@ -238,6 +238,7 @@ const static char SubgroupBlockWriteINTELPrefix[] =
142+
"intel_sub_group_block_write";
143+
const static char SubgroupImageMediaBlockINTELPrefix[] =
144+
"intel_sub_group_media_block";
145+
+const static char SplitBarrierINTELPrefix[] = "intel_work_group_barrier_";
146+
const static char LDEXP[] = "ldexp";
147+
} // namespace kOCLBuiltinName
148+
149+
@@ -545,6 +546,22 @@ getOrCreateSwitchFunc(StringRef MapName, Value *V,
150+
return addCallInst(M, MapName, Ty, V, nullptr, InsertPoint);
151+
}
152+
153+
+/// Performs conversion from SPIR-V Memory Semantics into OpenCL
154+
+/// mem_fence_flags.
155+
+///
156+
+/// Supports both constant and non-constant values. To handle the latter case,
157+
+/// function with switch..case statement will be inserted into module which
158+
+/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
159+
+///
160+
+/// \param [in] MemorySemantics Memory Semantics value which needs to be
161+
+/// translated
162+
+/// \param [in] InsertBefore insertion point for call into conversion function
163+
+/// which is generated if \arg MemorySemantics is not a constant
164+
+/// \returns \c Value corresponding to OpenCL mem_fence_flags equivalent to
165+
+/// SPIR-V Memory Semantics passed in \arg MemorySemantics
166+
+Value *transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Value *MemorySemantics,
167+
+ Instruction *InsertBefore);
168+
+
169+
template <> inline void SPIRVMap<std::string, SPIRVGroupOperationKind>::init() {
170+
add("reduce", GroupOperationReduce);
171+
add("scan_inclusive", GroupOperationInclusiveScan);
172+
@@ -815,6 +832,9 @@ template <> inline void SPIRVMap<std::string, Op, SPIRVInstruction>::init() {
173+
// cl_khr_subgroup_shuffle_relative
174+
_SPIRV_OP(group_shuffle_up, GroupNonUniformShuffleUp)
175+
_SPIRV_OP(group_shuffle_down, GroupNonUniformShuffleDown)
176+
+ // cl_khr_split_work_group_barrier
177+
+ _SPIRV_OP(intel_work_group_barrier_arrive, ControlBarrierArriveINTEL)
178+
+ _SPIRV_OP(intel_work_group_barrier_wait, ControlBarrierWaitINTEL)
179+
#undef _SPIRV_OP
180+
}
181+
182+
diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp
183+
index cd90d979..d26411a9 100644
184+
--- a/lib/SPIRV/SPIRVReader.cpp
185+
+++ b/lib/SPIRV/SPIRVReader.cpp
186+
@@ -2059,7 +2059,8 @@ Instruction *SPIRVToLLVM::transBuiltinFromInst(const std::string &FuncName,
187+
if (isFuncNoUnwind())
188+
Func->addFnAttr(Attribute::NoUnwind);
189+
auto OC = BI->getOpCode();
190+
- if (isGroupOpCode(OC) || isIntelSubgroupOpCode(OC))
191+
+ if (isGroupOpCode(OC) || isIntelSubgroupOpCode(OC) ||
192+
+ isSplitBarrierINTELOpCode(OC))
193+
Func->addFnAttr(Attribute::Convergent);
194+
}
195+
auto Call =
196+
diff --git a/lib/SPIRV/SPIRVToOCL.cpp b/lib/SPIRV/SPIRVToOCL.cpp
197+
index 42b539c7..c485fb0d 100644
198+
--- a/lib/SPIRV/SPIRVToOCL.cpp
199+
+++ b/lib/SPIRV/SPIRVToOCL.cpp
200+
@@ -106,6 +106,10 @@ void SPIRVToOCL::visitCallInst(CallInst &CI) {
201+
if (OC == OpControlBarrier) {
202+
visitCallSPIRVControlBarrier(&CI);
203+
}
204+
+ if (isSplitBarrierINTELOpCode(OC)) {
205+
+ visitCallSPIRVSplitBarrierINTEL(&CI, OC);
206+
+ return;
207+
+ }
208+
if (isAtomicOpCode(OC)) {
209+
visitCallSPIRVAtomicBuiltin(&CI, OC);
210+
return;
211+
diff --git a/lib/SPIRV/SPIRVToOCL.h b/lib/SPIRV/SPIRVToOCL.h
212+
index 127e8dd8..3577496b 100644
213+
--- a/lib/SPIRV/SPIRVToOCL.h
214+
+++ b/lib/SPIRV/SPIRVToOCL.h
215+
@@ -211,6 +211,11 @@ public:
216+
/// - OCL1.2: barrier
217+
virtual void visitCallSPIRVControlBarrier(CallInst *CI) = 0;
218+
219+
+ /// Transform split __spirv_ControlBarrier barrier to:
220+
+ /// - OCL2.0: overload with a memory_scope argument
221+
+ /// - OCL1.2: overload with no memory_scope argument
222+
+ virtual void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) = 0;
223+
+
224+
/// Transform __spirv_EnqueueKernel to __enqueue_kernel
225+
virtual void visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) = 0;
226+
227+
diff --git a/lib/SPIRV/SPIRVToOCL12.cpp b/lib/SPIRV/SPIRVToOCL12.cpp
228+
index 2b8b2967..f8084901 100644
229+
--- a/lib/SPIRV/SPIRVToOCL12.cpp
230+
+++ b/lib/SPIRV/SPIRVToOCL12.cpp
231+
@@ -60,6 +60,10 @@ public:
232+
/// barrier(flag(sema))
233+
void visitCallSPIRVControlBarrier(CallInst *CI) override;
234+
235+
+ /// Transform split __spirv_ControlBarrier barrier to overloads without a
236+
+ /// memory_scope argument.
237+
+ void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) override;
238+
+
239+
/// Transform __spirv_OpAtomic functions. It firstly conduct generic
240+
/// mutations for all builtins and then mutate some of them seperately
241+
Instruction *visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) override;
242+
@@ -202,6 +206,19 @@ void SPIRVToOCL12::visitCallSPIRVControlBarrier(CallInst *CI) {
243+
&Attrs);
244+
}
245+
246+
+void SPIRVToOCL12::visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) {
247+
+ AttributeList Attrs = CI->getCalledFunction()->getAttributes();
248+
+ mutateCallInstOCL(
249+
+ M, CI,
250+
+ [=](CallInst *, std::vector<Value *> &Args) {
251+
+ Value *MemFenceFlags =
252+
+ SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Args[2], CI);
253+
+ Args.assign(1, MemFenceFlags);
254+
+ return OCLSPIRVBuiltinMap::rmap(OC);
255+
+ },
256+
+ &Attrs);
257+
+}
258+
+
259+
Instruction *SPIRVToOCL12::visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) {
260+
AttributeList Attrs = CI->getCalledFunction()->getAttributes();
261+
return mutateCallInstOCL(
262+
diff --git a/lib/SPIRV/SPIRVToOCL20.cpp b/lib/SPIRV/SPIRVToOCL20.cpp
263+
index b7050246..fbe632ea 100644
264+
--- a/lib/SPIRV/SPIRVToOCL20.cpp
265+
+++ b/lib/SPIRV/SPIRVToOCL20.cpp
266+
@@ -63,6 +63,10 @@ public:
267+
/// sub_group_barrier(flag(sema), map(memScope))
268+
void visitCallSPIRVControlBarrier(CallInst *CI) override;
269+
270+
+ /// Transform split __spirv_ControlBarrier barrier to overloads with a
271+
+ /// memory_scope argument.
272+
+ void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) override;
273+
+
274+
/// Transform __spirv_Atomic* to atomic_*.
275+
/// __spirv_Atomic*(atomic_op, scope, sema, ops, ...) =>
276+
/// atomic_*(generic atomic_op, ops, ..., order(sema), map(scope))
277+
@@ -158,6 +162,28 @@ void SPIRVToOCL20::visitCallSPIRVControlBarrier(CallInst *CI) {
278+
&Attrs);
279+
}
280+
281+
+void SPIRVToOCL20::visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) {
282+
+ AttributeList Attrs = CI->getCalledFunction()->getAttributes();
283+
+ mutateCallInstOCL(
284+
+ M, CI,
285+
+ [=](CallInst *, std::vector<Value *> &Args) {
286+
+ auto GetArg = [=](unsigned I) {
287+
+ return cast<ConstantInt>(Args[I])->getZExtValue();
288+
+ };
289+
+ Value *MemScope =
290+
+ getInt32(M, rmap<OCLScopeKind>(static_cast<Scope>(GetArg(1))));
291+
+ Value *MemFenceFlags =
292+
+ SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Args[2], CI);
293+
+
294+
+ Args.resize(2);
295+
+ Args[0] = MemFenceFlags;
296+
+ Args[1] = MemScope;
297+
+
298+
+ return OCLSPIRVBuiltinMap::rmap(OC);
299+
+ },
300+
+ &Attrs);
301+
+}
302+
+
303+
std::string SPIRVToOCL20::mapFPAtomicName(Op OC) {
304+
assert(isFPAtomicOpCode(OC) && "Not intended to handle other opcodes than "
305+
"AtomicF{Add/Min/Max}EXT!");
306+
diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
307+
index 7ae7c96d..2e60dc0e 100644
308+
--- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h
309+
+++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
310+
@@ -2864,6 +2864,24 @@ _SPIRV_OP(VariableLengthArray, true, 4)
311+
_SPIRV_OP(SaveMemory, true, 3)
312+
_SPIRV_OP(RestoreMemory, false, 2)
313+
#undef _SPIRV_OP
314+
+
315+
+class SPIRVSplitBarrierINTELBase : public SPIRVInstTemplateBase {
316+
+protected:
317+
+ SPIRVCapVec getRequiredCapability() const override {
318+
+ return getVec(CapabilitySplitBarrierINTEL);
319+
+ }
320+
+
321+
+ SPIRVExtSet getRequiredExtensions() const override {
322+
+ return getSet(ExtensionID::SPV_INTEL_split_barrier);
323+
+ }
324+
+};
325+
+
326+
+#define _SPIRV_OP(x, ...) \
327+
+ typedef SPIRVInstTemplate<SPIRVSplitBarrierINTELBase, Op##x, __VA_ARGS__> \
328+
+ SPIRV##x;
329+
+_SPIRV_OP(ControlBarrierArriveINTEL, false, 4)
330+
+_SPIRV_OP(ControlBarrierWaitINTEL, false, 4)
331+
+#undef _SPIRV_OP
332+
} // namespace SPIRV
333+
334+
#endif // SPIRV_LIBSPIRV_SPIRVINSTRUCTION_H
335+
diff --git a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h
336+
index 3509bc34..3a391362 100644
337+
--- a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h
338+
+++ b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h
339+
@@ -568,6 +568,7 @@ template <> inline void SPIRVMap<Capability, std::string>::init() {
340+
"ArbitraryPrecisionIntegersINTEL");
341+
add(internal::CapabilityMemoryAccessAliasingINTEL,
342+
"MemoryAccessAliasingINTEL");
343+
+ add(CapabilitySplitBarrierINTEL, "SplitBarrierINTEL");
344+
345+
}
346+
SPIRV_DEF_NAMEMAP(Capability, SPIRVCapabilityNameMap)
347+
diff --git a/lib/SPIRV/libSPIRV/SPIRVOpCode.h b/lib/SPIRV/libSPIRV/SPIRVOpCode.h
348+
index c0f2b650..2a4012db 100644
349+
--- a/lib/SPIRV/libSPIRV/SPIRVOpCode.h
350+
+++ b/lib/SPIRV/libSPIRV/SPIRVOpCode.h
351+
@@ -242,6 +242,11 @@ inline bool isEventOpCode(Op OpCode) {
352+
return OpRetainEvent <= OpCode && OpCode <= OpCaptureEventProfilingInfo;
353+
}
354+
355+
+inline bool isSplitBarrierINTELOpCode(Op OpCode) {
356+
+ return OpCode == OpControlBarrierArriveINTEL ||
357+
+ OpCode == OpControlBarrierWaitINTEL;
358+
+}
359+
+
360+
} // namespace SPIRV
361+
362+
#endif // SPIRV_LIBSPIRV_SPIRVOPCODE_H
363+
diff --git a/lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h b/lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h
364+
index d2bf96cb..e9897dce 100644
365+
--- a/lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h
366+
+++ b/lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h
367+
@@ -486,3 +486,5 @@ _SPIRV_OP(WritePipeBlockingINTEL, 5947)
368+
_SPIRV_OP(FPGARegINTEL, 5949)
369+
_SPIRV_OP(AtomicFAddEXT, 6035)
370+
_SPIRV_OP(TypeBufferSurfaceINTEL, 6086)
371+
+_SPIRV_OP(ControlBarrierArriveINTEL, 6142)
372+
+_SPIRV_OP(ControlBarrierWaitINTEL, 6143)
373+
--
374+
2.17.1
375+

0 commit comments

Comments
 (0)