Skip to content

Commit f5bc1ff

Browse files
AlexeySachkovstanleygambarin
authored andcommitted
[Backport to 9] [NFC] Refactor logic of memory scopes, orders and flags translation
There are several OpenCL built-ins which accept cl_mem_fence_flags, memory_scope and memory_order arguments (like barriers, fences or atomics) and representation of that info is different between OpenCL and SPIR-V: different values for enumerations and bitmasks, different arguments for OpenCL built-ins and SPIR-V instructions. To avoid code duplication, refactored all common code for translation aforementioned entities into helper functions. Simplified interface of getOrCreateSwithFunc helper: `llvm::Module` argument is now infered from `InsertPoint` argument.
1 parent c25fd6c commit f5bc1ff

File tree

6 files changed

+209
-107
lines changed

6 files changed

+209
-107
lines changed

lib/SPIRV/OCL20ToSPIRV.cpp

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -823,29 +823,13 @@ void OCL20ToSPIRV::transAtomicBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info) {
823823
const size_t ArgsCount = Args.size();
824824
const size_t ScopeIdx = ArgsCount - 1;
825825
const size_t OrderIdx = ScopeIdx - NumOrder;
826-
if (auto ScopeInt = dyn_cast_or_null<ConstantInt>(Args[ScopeIdx])) {
827-
Args[ScopeIdx] = mapUInt(M, ScopeInt, [](unsigned I) {
828-
return map<Scope>(static_cast<OCLScopeKind>(I));
829-
});
830-
} else {
831-
Args[ScopeIdx] =
832-
getOrCreateSwitchFunc(kSPIRVName::TranslateOCLMemScope,
833-
Args[ScopeIdx], OCLMemScopeMap::getMap(),
834-
false /*IsReverse*/, OCLMS_device, CI, M);
835-
}
826+
827+
Args[ScopeIdx] =
828+
transOCLMemScopeIntoSPIRVScope(Args[ScopeIdx], OCLMS_device, CI);
829+
836830
for (size_t I = 0; I < NumOrder; ++I) {
837-
if (auto OrderInt =
838-
dyn_cast_or_null<ConstantInt>(Args[OrderIdx + I])) {
839-
Args[OrderIdx + I] = mapUInt(M, OrderInt, [](unsigned Ord) {
840-
return mapOCLMemSemanticToSPIRV(
841-
0, static_cast<OCLMemOrderKind>(Ord));
842-
});
843-
} else {
844-
Args[OrderIdx + I] = getOrCreateSwitchFunc(
845-
kSPIRVName::TranslateOCLMemOrder, Args[OrderIdx + I],
846-
OCLMemOrderMap::getMap(), false /*IsReverse*/, OCLMO_seq_cst,
847-
CI, M);
848-
}
831+
Args[OrderIdx + I] = transOCLMemOrderIntoSPIRVMemorySemantics(
832+
Args[OrderIdx + I], OCLMO_seq_cst, CI);
849833
}
850834
// Order of args in SPIR-V:
851835
// object, scope, 1-2 order, 0-2 other args

lib/SPIRV/OCLUtil.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,105 @@ std::string getIntelSubgroupBlockDataPostfix(unsigned ElementBitSize,
952952
}
953953
} // namespace OCLUtil
954954

955+
Value *SPIRV::transOCLMemScopeIntoSPIRVScope(Value *MemScope,
956+
Optional<int> DefaultCase,
957+
Instruction *InsertBefore) {
958+
if (auto *C = dyn_cast<ConstantInt>(MemScope)) {
959+
return ConstantInt::get(
960+
C->getType(), map<Scope>(static_cast<OCLScopeKind>(C->getZExtValue())));
961+
}
962+
963+
// If memory_scope is not a constant, then we have to insert dynamic mapping:
964+
return getOrCreateSwitchFunc(kSPIRVName::TranslateOCLMemScope, MemScope,
965+
OCLMemScopeMap::getMap(), /* IsReverse */ false,
966+
DefaultCase, InsertBefore);
967+
}
968+
969+
Value *SPIRV::transOCLMemOrderIntoSPIRVMemorySemantics(
970+
Value *MemOrder, Optional<int> DefaultCase, Instruction *InsertBefore) {
971+
if (auto *C = dyn_cast<ConstantInt>(MemOrder)) {
972+
return ConstantInt::get(
973+
C->getType(), mapOCLMemSemanticToSPIRV(
974+
0, static_cast<OCLMemOrderKind>(C->getZExtValue())));
975+
}
976+
977+
return getOrCreateSwitchFunc(kSPIRVName::TranslateOCLMemOrder, MemOrder,
978+
OCLMemOrderMap::getMap(), /* IsReverse */ false,
979+
DefaultCase, InsertBefore);
980+
}
981+
982+
Value *
983+
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(Value *MemScope,
984+
Instruction *InsertBefore) {
985+
if (auto *C = dyn_cast<ConstantInt>(MemScope)) {
986+
return ConstantInt::get(C->getType(), rmap<OCLScopeKind>(static_cast<Scope>(
987+
C->getZExtValue())));
988+
}
989+
990+
if (auto *CI = dyn_cast<CallInst>(MemScope)) {
991+
Function *F = CI->getCalledFunction();
992+
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemScope)) {
993+
// In case the SPIR-V module was created from an OpenCL program by
994+
// *this* SPIR-V generator, we know that the value passed to
995+
// __translate_ocl_memory_scope is what we should pass to the
996+
// OpenCL builtin now.
997+
return CI->getArgOperand(0);
998+
}
999+
}
1000+
1001+
return getOrCreateSwitchFunc(kSPIRVName::TranslateSPIRVMemScope, MemScope,
1002+
OCLMemScopeMap::getRMap(),
1003+
/* IsReverse */ true, None, InsertBefore);
1004+
}
1005+
1006+
Value *
1007+
SPIRV::transSPIRVMemorySemanticsIntoOCLMemoryOrder(Value *MemorySemantics,
1008+
Instruction *InsertBefore) {
1009+
if (auto *C = dyn_cast<ConstantInt>(MemorySemantics)) {
1010+
return ConstantInt::get(C->getType(),
1011+
mapSPIRVMemSemanticToOCL(C->getZExtValue()).second);
1012+
}
1013+
1014+
if (auto *CI = dyn_cast<CallInst>(MemorySemantics)) {
1015+
Function *F = CI->getCalledFunction();
1016+
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemOrder)) {
1017+
// In case the SPIR-V module was created from an OpenCL program by
1018+
// *this* SPIR-V generator, we know that the value passed to
1019+
// __translate_ocl_memory_order is what we should pass to the
1020+
// OpenCL builtin now.
1021+
return CI->getArgOperand(0);
1022+
}
1023+
}
1024+
1025+
// SPIR-V MemorySemantics contains both OCL mem_fence_flags and mem_order and
1026+
// therefore, we need to apply mask
1027+
int Mask = MemorySemanticsMaskNone | MemorySemanticsAcquireMask |
1028+
MemorySemanticsReleaseMask | MemorySemanticsAcquireReleaseMask |
1029+
MemorySemanticsSequentiallyConsistentMask;
1030+
return getOrCreateSwitchFunc(kSPIRVName::TranslateSPIRVMemOrder,
1031+
MemorySemantics, OCLMemOrderMap::getRMap(),
1032+
/* IsReverse */ true, None, InsertBefore, Mask);
1033+
}
1034+
1035+
Value *SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
1036+
Value *MemorySemantics, Instruction *InsertBefore) {
1037+
if (auto *C = dyn_cast<ConstantInt>(MemorySemantics)) {
1038+
return ConstantInt::get(C->getType(),
1039+
mapSPIRVMemSemanticToOCL(C->getZExtValue()).first);
1040+
}
1041+
1042+
// TODO: any possible optimizations?
1043+
// SPIR-V MemorySemantics contains both OCL mem_fence_flags and mem_order and
1044+
// therefore, we need to apply mask
1045+
int Mask = MemorySemanticsWorkgroupMemoryMask |
1046+
MemorySemanticsCrossWorkgroupMemoryMask |
1047+
MemorySemanticsImageMemoryMask;
1048+
return getOrCreateSwitchFunc(kSPIRVName::TranslateSPIRVMemFence,
1049+
MemorySemantics,
1050+
OCLMemFenceExtendedMap::getRMap(),
1051+
/* IsReverse */ true, None, InsertBefore, Mask);
1052+
}
1053+
9551054
void llvm::mangleOpenClBuiltin(const std::string &UniqName,
9561055
ArrayRef<Type *> ArgTypes,
9571056
std::string &MangledName) {

lib/SPIRV/OCLUtil.h

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,12 +491,13 @@ Instruction *
491491
getOrCreateSwitchFunc(StringRef MapName, Value *V,
492492
const SPIRVMap<KeyTy, ValTy, Identifier> &Map,
493493
bool IsReverse, Optional<int> DefaultCase,
494-
Instruction *InsertPoint, Module *M, int KeyMask = 0) {
494+
Instruction *InsertPoint, int KeyMask = 0) {
495495
static_assert(std::is_convertible<KeyTy, int>::value &&
496496
std::is_convertible<ValTy, int>::value,
497497
"Can map only integer values");
498498
Type *Ty = V->getType();
499499
assert(Ty && Ty->isIntegerTy() && "Can't map non-integer types");
500+
Module *M = InsertPoint->getModule();
500501
Function *F = getOrCreateFunction(M, Ty, Ty, MapName);
501502
if (!F->empty()) // The switch function already exists. just call it.
502503
return addCallInst(M, MapName, Ty, V, nullptr, InsertPoint);
@@ -538,6 +539,85 @@ getOrCreateSwitchFunc(StringRef MapName, Value *V,
538539
return addCallInst(M, MapName, Ty, V, nullptr, InsertPoint);
539540
}
540541

542+
/// Performs conversion from OpenCL memory_scope into SPIR-V Scope.
543+
///
544+
/// Supports both constant and non-constant values. To handle the latter case,
545+
/// function with switch..case statement will be inserted into module which
546+
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
547+
///
548+
/// \param [in] MemScope memory_scope value which needs to be translated
549+
/// \param [in] DefaultCase default value for switch..case construct if
550+
/// dynamic mapping is used
551+
/// \param [in] InsertBefore insertion point for call into conversion function
552+
/// which is generated if \arg MemScope is not a constant
553+
/// \returns \c Value corresponding to SPIR-V Scope equivalent to OpenCL
554+
/// memory_scope passed in \arg MemScope
555+
Value *transOCLMemScopeIntoSPIRVScope(Value *MemScope,
556+
Optional<int> DefaultCase,
557+
Instruction *InsertBefore);
558+
559+
/// Performs conversion from OpenCL memory_order into SPIR-V Memory Semantics.
560+
///
561+
/// Supports both constant and non-constant values. To handle the latter case,
562+
/// function with switch..case statement will be inserted into module which
563+
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
564+
///
565+
/// \param [in] MemOrder memory_scope value which needs to be translated
566+
/// \param [in] DefaultCase default value for switch..case construct if
567+
/// dynamic mapping is used
568+
/// \param [in] InsertBefore insertion point for call into conversion function
569+
/// which is generated if \arg MemOrder is not a constant
570+
/// \returns \c Value corresponding to SPIR-V Memory Semantics equivalent to
571+
/// OpenCL memory_order passed in \arg MemOrder
572+
Value *transOCLMemOrderIntoSPIRVMemorySemantics(Value *MemOrder,
573+
Optional<int> DefaultCase,
574+
Instruction *InsertBefore);
575+
576+
/// Performs conversion from SPIR-V Scope into OpenCL memory_scope.
577+
///
578+
/// Supports both constant and non-constant values. To handle the latter case,
579+
/// function with switch..case statement will be inserted into module which
580+
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
581+
///
582+
/// \param [in] MemScope Scope value which needs to be translated
583+
/// \param [in] InsertBefore insertion point for call into conversion function
584+
/// which is generated if \arg MemScope is not a constant
585+
/// \returns \c Value corresponding to OpenCL memory_scope equivalent to SPIR-V
586+
/// Scope passed in \arg MemScope
587+
Value *transSPIRVMemoryScopeIntoOCLMemoryScope(Value *MemScope,
588+
Instruction *InsertBefore);
589+
590+
/// Performs conversion from SPIR-V Memory Semantics into OpenCL memory_order.
591+
///
592+
/// Supports both constant and non-constant values. To handle the latter case,
593+
/// function with switch..case statement will be inserted into module which
594+
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
595+
///
596+
/// \param [in] MemorySemantics Memory Semantics value which needs to be
597+
/// translated
598+
/// \param [in] InsertBefore insertion point for call into conversion function
599+
/// which is generated if \arg MemorySemantics is not a constant
600+
/// \returns \c Value corresponding to OpenCL memory_order equivalent to SPIR-V
601+
/// Memory Semantics passed in \arg MemorySemantics
602+
Value *transSPIRVMemorySemanticsIntoOCLMemoryOrder(Value *MemorySemantics,
603+
Instruction *InsertBefore);
604+
605+
/// Performs conversion from SPIR-V Memory Semantics into OpenCL
606+
/// mem_fence_flags.
607+
///
608+
/// Supports both constant and non-constant values. To handle the latter case,
609+
/// function with switch..case statement will be inserted into module which
610+
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
611+
///
612+
/// \param [in] MemorySemantics Memory Semantics value which needs to be
613+
/// translated
614+
/// \param [in] InsertBefore insertion point for call into conversion function
615+
/// which is generated if \arg MemorySemantics is not a constant
616+
/// \returns \c Value corresponding to OpenCL mem_fence_flags equivalent to
617+
/// SPIR-V Memory Semantics passed in \arg MemorySemantics
618+
Value *transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Value *MemorySemantics,
619+
Instruction *InsertBefore);
620+
541621
template <> inline void SPIRVMap<std::string, SPIRVGroupOperationKind>::init() {
542622
add("reduce", GroupOperationReduce);
543623
add("scan_inclusive", GroupOperationInclusiveScan);

lib/SPIRV/SPIRVToOCL12.cpp

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -131,26 +131,9 @@ void SPIRVToOCL12::visitCallSPIRVMemoryBarrier(CallInst *CI) {
131131
mutateCallInstOCL(
132132
M, CI,
133133
[=](CallInst *, std::vector<Value *> &Args) {
134-
if (auto Arg = dyn_cast<ConstantInt>(Args[1])) {
135-
auto Sema = mapSPIRVMemSemanticToOCL(Arg->getZExtValue());
136-
Args.resize(1);
137-
Args[0] = getInt32(M, Sema.first);
138-
} else {
139-
CallInst *TransCall = dyn_cast<CallInst>(Args[1]);
140-
Function *F = TransCall ? TransCall->getCalledFunction() : nullptr;
141-
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemScope)) {
142-
Args[0] = TransCall->getArgOperand(0);
143-
} else {
144-
int ClMemFenceMask = MemorySemanticsWorkgroupMemoryMask |
145-
MemorySemanticsCrossWorkgroupMemoryMask |
146-
MemorySemanticsImageMemoryMask;
147-
Args[0] = getOrCreateSwitchFunc(
148-
kSPIRVName::TranslateSPIRVMemFence, Args[1],
149-
OCLMemFenceExtendedMap::getRMap(), true /*IsReverse*/, None, CI,
150-
M, ClMemFenceMask);
151-
}
152-
Args.resize(1);
153-
}
134+
Value *MemFenceFlags =
135+
transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Args[1], CI);
136+
Args.assign(1, MemFenceFlags);
154137
return kOCLBuiltinName::MemFence;
155138
},
156139
&Attrs);
@@ -163,26 +146,9 @@ void SPIRVToOCL12::visitCallSPIRVControlBarrier(CallInst *CI) {
163146
mutateCallInstOCL(
164147
M, CI,
165148
[=](CallInst *, std::vector<Value *> &Args) {
166-
if (auto Arg = dyn_cast<ConstantInt>(Args[2])) {
167-
auto Sema = mapSPIRVMemSemanticToOCL(Arg->getZExtValue());
168-
Args.resize(1);
169-
Args[0] = getInt32(M, Sema.first);
170-
} else {
171-
CallInst *TransCall = dyn_cast<CallInst>(Args[2]);
172-
Function *F = TransCall ? TransCall->getCalledFunction() : nullptr;
173-
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemScope)) {
174-
Args[0] = TransCall->getArgOperand(0);
175-
} else {
176-
int ClMemFenceMask = MemorySemanticsWorkgroupMemoryMask |
177-
MemorySemanticsCrossWorkgroupMemoryMask |
178-
MemorySemanticsImageMemoryMask;
179-
Args[0] = getOrCreateSwitchFunc(
180-
kSPIRVName::TranslateSPIRVMemFence, Args[2],
181-
OCLMemFenceExtendedMap::getRMap(), true /*IsReverse*/, None, CI,
182-
M, ClMemFenceMask);
183-
}
184-
Args.resize(1);
185-
}
149+
auto *MemFenceFlags =
150+
transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Args[2], CI);
151+
Args.assign(1, MemFenceFlags);
186152
return kOCLBuiltinName::Barrier;
187153
},
188154
&Attrs);

lib/SPIRV/SPIRVToOCL20.cpp

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
//===----------------------------------------------------------------------===//
3838
#define DEBUG_TYPE "spvtocl20"
3939

40+
#include "OCLUtil.h"
4041
#include "SPIRVToOCL.h"
4142
#include "llvm/IR/Verifier.h"
4243

@@ -136,11 +137,15 @@ void SPIRVToOCL20::visitCallSPIRVControlBarrier(CallInst *CI) {
136137
return cast<ConstantInt>(Args[I])->getZExtValue();
137138
};
138139
auto ExecScope = static_cast<Scope>(GetArg(0));
139-
auto MScope = static_cast<Scope>(GetArg(1));
140-
auto Sema = mapSPIRVMemSemanticToOCL(GetArg(2));
140+
Value *MemScope =
141+
getInt32(M, rmap<OCLScopeKind>(static_cast<Scope>(GetArg(1))));
142+
Value *MemFenceFlags =
143+
SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Args[2], CI);
144+
141145
Args.resize(2);
142-
Args[0] = getInt32(M, Sema.first);
143-
Args[1] = getInt32(M, rmap<OCLScopeKind>(MScope));
146+
Args[0] = MemFenceFlags;
147+
Args[1] = MemScope;
148+
144149
return (ExecScope == ScopeWorkgroup) ? kOCLBuiltinName::WorkGroupBarrier
145150
: kOCLBuiltinName::SubGroupBarrier;
146151
},
@@ -222,46 +227,13 @@ CallInst *SPIRVToOCL20::mutateCommonAtomicArguments(CallInst *CI, Op OC) {
222227
auto NumOrder = getSPIRVAtomicBuiltinNumMemoryOrderArgs(OC);
223228
auto ScopeIdx = Ptr + 1;
224229
auto OrderIdx = Ptr + 2;
225-
if (auto *ScopeInt = dyn_cast_or_null<ConstantInt>(Args[ScopeIdx])) {
226-
Args[ScopeIdx] = mapUInt(M, ScopeInt, [](unsigned I) {
227-
return rmap<OCLScopeKind>(static_cast<Scope>(I));
228-
});
229-
} else {
230-
CallInst *TransCall = dyn_cast<CallInst>(Args[ScopeIdx]);
231-
Function *F = TransCall ? TransCall->getCalledFunction() : nullptr;
232-
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemScope)) {
233-
// In case the SPIR-V module was created from an OpenCL program by
234-
// *this* SPIR-V generator, we know that the value passed to
235-
// __translate_ocl_memory_scope is what we should pass to the OpenCL
236-
// builtin now.
237-
Args[ScopeIdx] = TransCall->getArgOperand(0);
238-
} else {
239-
Args[ScopeIdx] = getOrCreateSwitchFunc(
240-
kSPIRVName::TranslateSPIRVMemScope, Args[ScopeIdx],
241-
OCLMemScopeMap::getRMap(), true /*IsReverse*/, None, CI, M);
242-
}
243-
}
230+
231+
Args[ScopeIdx] =
232+
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(Args[ScopeIdx], CI);
244233
for (size_t I = 0; I < NumOrder; ++I) {
245-
if (auto OrderInt =
246-
dyn_cast_or_null<ConstantInt>(Args[OrderIdx + I])) {
247-
Args[OrderIdx + I] = mapUInt(M, OrderInt, [](unsigned Ord) {
248-
return mapSPIRVMemOrderToOCL(Ord);
249-
});
250-
} else {
251-
CallInst *TransCall = dyn_cast<CallInst>(Args[OrderIdx + I]);
252-
Function *F = TransCall ? TransCall->getCalledFunction() : nullptr;
253-
if (F && F->getName().equals(kSPIRVName::TranslateOCLMemOrder)) {
254-
// In case the SPIR-V module was created from an OpenCL program by
255-
// *this* SPIR-V generator, we know that the value passed to
256-
// __translate_ocl_memory_order is what we should pass to the
257-
// OpenCL builtin now.
258-
Args[OrderIdx + I] = TransCall->getArgOperand(0);
259-
} else {
260-
Args[OrderIdx + I] = getOrCreateSwitchFunc(
261-
kSPIRVName::TranslateSPIRVMemOrder, Args[OrderIdx + I],
262-
OCLMemOrderMap::getRMap(), true /*IsReverse*/, None, CI, M);
263-
}
264-
}
234+
Args[OrderIdx + I] =
235+
SPIRV::transSPIRVMemorySemanticsIntoOCLMemoryOrder(
236+
Args[OrderIdx + I], CI);
265237
}
266238
std::swap(Args[ScopeIdx], Args.back());
267239
return Name;

test/atomic_explicit_arguments.spt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@
6767

6868
; CHECK: define private spir_func i32 @__translate_spirv_memory_order(i32 %key) {
6969
; CHECK: entry:
70-
; CHECK: switch i32 %key, label %default [
70+
; CHECK: %key.masked = and i32 30, %key
71+
; CHECK: switch i32 %key.masked, label %default [
7172
; CHECK: i32 0, label %case.0
7273
; CHECK: i32 2, label %case.2
7374
; CHECK: i32 4, label %case.4

0 commit comments

Comments
 (0)