1 //===--- SyncScope.h - Atomic synchronization scopes ------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Provides definitions for the atomic synchronization scopes.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_CLANG_BASIC_SYNCSCOPE_H
15 #define LLVM_CLANG_BASIC_SYNCSCOPE_H
16
17 #include "clang/Basic/LangOptions.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/StringRef.h"
20 #include <memory>
21
22 namespace clang {
23
24 /// Defines synch scope values used internally by clang.
25 ///
26 /// The enum values start from 0 and are contiguous. They are mainly used for
27 /// enumerating all supported synch scope values and mapping them to LLVM
28 /// synch scopes. Their numerical values may be different from the corresponding
29 /// synch scope enums used in source languages.
30 ///
31 /// In atomic builtin and expressions, language-specific synch scope enums are
32 /// used. Currently only OpenCL memory scope enums are supported and assumed
33 /// to be used by all languages. However, in the future, other languages may
34 /// define their own set of synch scope enums. The language-specific synch scope
35 /// values are represented by class AtomicScopeModel and its derived classes.
36 ///
37 /// To add a new enum value:
38 /// Add the enum value to enum class SyncScope.
39 /// Update enum value Last if necessary.
40 /// Update getAsString.
41 ///
42 enum class SyncScope {
43 SystemScope,
44 DeviceScope,
45 WorkgroupScope,
46 WavefrontScope,
47 SingleScope,
48 HIPSingleThread,
49 HIPWavefront,
50 HIPWorkgroup,
51 HIPAgent,
52 HIPSystem,
53 OpenCLWorkGroup,
54 OpenCLDevice,
55 OpenCLAllSVMDevices,
56 OpenCLSubGroup,
57 Last = OpenCLSubGroup
58 };
59
getAsString(SyncScope S)60 inline llvm::StringRef getAsString(SyncScope S) {
61 switch (S) {
62 case SyncScope::SystemScope:
63 return "system_scope";
64 case SyncScope::DeviceScope:
65 return "device_scope";
66 case SyncScope::WorkgroupScope:
67 return "workgroup_scope";
68 case SyncScope::WavefrontScope:
69 return "wavefront_scope";
70 case SyncScope::SingleScope:
71 return "single_scope";
72 case SyncScope::HIPSingleThread:
73 return "hip_singlethread";
74 case SyncScope::HIPWavefront:
75 return "hip_wavefront";
76 case SyncScope::HIPWorkgroup:
77 return "hip_workgroup";
78 case SyncScope::HIPAgent:
79 return "hip_agent";
80 case SyncScope::HIPSystem:
81 return "hip_system";
82 case SyncScope::OpenCLWorkGroup:
83 return "opencl_workgroup";
84 case SyncScope::OpenCLDevice:
85 return "opencl_device";
86 case SyncScope::OpenCLAllSVMDevices:
87 return "opencl_allsvmdevices";
88 case SyncScope::OpenCLSubGroup:
89 return "opencl_subgroup";
90 }
91 llvm_unreachable("Invalid synch scope");
92 }
93
94 /// Defines the kind of atomic scope models.
95 enum class AtomicScopeModelKind { None, OpenCL, HIP, Generic };
96
97 /// Defines the interface for synch scope model.
98 class AtomicScopeModel {
99 public:
~AtomicScopeModel()100 virtual ~AtomicScopeModel() {}
101 /// Maps language specific synch scope values to internal
102 /// SyncScope enum.
103 virtual SyncScope map(unsigned S) const = 0;
104
105 /// Check if the compile-time constant synch scope value
106 /// is valid.
107 virtual bool isValid(unsigned S) const = 0;
108
109 /// Get all possible synch scope values that might be
110 /// encountered at runtime for the current language.
111 virtual ArrayRef<unsigned> getRuntimeValues() const = 0;
112
113 /// If atomic builtin function is called with invalid
114 /// synch scope value at runtime, it will fall back to a valid
115 /// synch scope value returned by this function.
116 virtual unsigned getFallBackValue() const = 0;
117
118 /// Create an atomic scope model by AtomicScopeModelKind.
119 /// \return an empty std::unique_ptr for AtomicScopeModelKind::None.
120 static std::unique_ptr<AtomicScopeModel> create(AtomicScopeModelKind K);
121 };
122
123 /// Defines the synch scope model for OpenCL.
124 class AtomicScopeOpenCLModel : public AtomicScopeModel {
125 public:
126 /// The enum values match the pre-defined macros
127 /// __OPENCL_MEMORY_SCOPE_*, which are used to define memory_scope_*
128 /// enums in opencl-c-base.h.
129 enum ID {
130 WorkGroup = 1,
131 Device = 2,
132 AllSVMDevices = 3,
133 SubGroup = 4,
134 Last = SubGroup
135 };
136
AtomicScopeOpenCLModel()137 AtomicScopeOpenCLModel() {}
138
map(unsigned S)139 SyncScope map(unsigned S) const override {
140 switch (static_cast<ID>(S)) {
141 case WorkGroup:
142 return SyncScope::OpenCLWorkGroup;
143 case Device:
144 return SyncScope::OpenCLDevice;
145 case AllSVMDevices:
146 return SyncScope::OpenCLAllSVMDevices;
147 case SubGroup:
148 return SyncScope::OpenCLSubGroup;
149 }
150 llvm_unreachable("Invalid language synch scope value");
151 }
152
isValid(unsigned S)153 bool isValid(unsigned S) const override {
154 return S >= static_cast<unsigned>(WorkGroup) &&
155 S <= static_cast<unsigned>(Last);
156 }
157
getRuntimeValues()158 ArrayRef<unsigned> getRuntimeValues() const override {
159 static_assert(Last == SubGroup, "Does not include all synch scopes");
160 static const unsigned Scopes[] = {
161 static_cast<unsigned>(WorkGroup), static_cast<unsigned>(Device),
162 static_cast<unsigned>(AllSVMDevices), static_cast<unsigned>(SubGroup)};
163 return llvm::ArrayRef(Scopes);
164 }
165
getFallBackValue()166 unsigned getFallBackValue() const override {
167 return static_cast<unsigned>(AllSVMDevices);
168 }
169 };
170
171 /// Defines the synch scope model for HIP.
172 class AtomicScopeHIPModel : public AtomicScopeModel {
173 public:
174 /// The enum values match the pre-defined macros
175 /// __HIP_MEMORY_SCOPE_*, which are used to define memory_scope_*
176 /// enums in hip-c.h.
177 enum ID {
178 SingleThread = 1,
179 Wavefront = 2,
180 Workgroup = 3,
181 Agent = 4,
182 System = 5,
183 Last = System
184 };
185
AtomicScopeHIPModel()186 AtomicScopeHIPModel() {}
187
map(unsigned S)188 SyncScope map(unsigned S) const override {
189 switch (static_cast<ID>(S)) {
190 case SingleThread:
191 return SyncScope::HIPSingleThread;
192 case Wavefront:
193 return SyncScope::HIPWavefront;
194 case Workgroup:
195 return SyncScope::HIPWorkgroup;
196 case Agent:
197 return SyncScope::HIPAgent;
198 case System:
199 return SyncScope::HIPSystem;
200 }
201 llvm_unreachable("Invalid language synch scope value");
202 }
203
isValid(unsigned S)204 bool isValid(unsigned S) const override {
205 return S >= static_cast<unsigned>(SingleThread) &&
206 S <= static_cast<unsigned>(Last);
207 }
208
getRuntimeValues()209 ArrayRef<unsigned> getRuntimeValues() const override {
210 static_assert(Last == System, "Does not include all synch scopes");
211 static const unsigned Scopes[] = {
212 static_cast<unsigned>(SingleThread), static_cast<unsigned>(Wavefront),
213 static_cast<unsigned>(Workgroup), static_cast<unsigned>(Agent),
214 static_cast<unsigned>(System)};
215 return llvm::ArrayRef(Scopes);
216 }
217
getFallBackValue()218 unsigned getFallBackValue() const override {
219 return static_cast<unsigned>(System);
220 }
221 };
222
223 /// Defines the generic atomic scope model.
224 class AtomicScopeGenericModel : public AtomicScopeModel {
225 public:
226 /// The enum values match predefined built-in macros __ATOMIC_SCOPE_*.
227 enum ID {
228 System = 0,
229 Device = 1,
230 Workgroup = 2,
231 Wavefront = 3,
232 Single = 4,
233 Last = Single
234 };
235
236 AtomicScopeGenericModel() = default;
237
map(unsigned S)238 SyncScope map(unsigned S) const override {
239 switch (static_cast<ID>(S)) {
240 case Device:
241 return SyncScope::DeviceScope;
242 case System:
243 return SyncScope::SystemScope;
244 case Workgroup:
245 return SyncScope::WorkgroupScope;
246 case Wavefront:
247 return SyncScope::WavefrontScope;
248 case Single:
249 return SyncScope::SingleScope;
250 }
251 llvm_unreachable("Invalid language sync scope value");
252 }
253
isValid(unsigned S)254 bool isValid(unsigned S) const override {
255 return S <= static_cast<unsigned>(Last);
256 }
257
getRuntimeValues()258 ArrayRef<unsigned> getRuntimeValues() const override {
259 static_assert(Last == Single, "Does not include all sync scopes");
260 static const unsigned Scopes[] = {
261 static_cast<unsigned>(Device), static_cast<unsigned>(System),
262 static_cast<unsigned>(Workgroup), static_cast<unsigned>(Wavefront),
263 static_cast<unsigned>(Single)};
264 return llvm::ArrayRef(Scopes);
265 }
266
getFallBackValue()267 unsigned getFallBackValue() const override {
268 return static_cast<unsigned>(System);
269 }
270 };
271
272 inline std::unique_ptr<AtomicScopeModel>
create(AtomicScopeModelKind K)273 AtomicScopeModel::create(AtomicScopeModelKind K) {
274 switch (K) {
275 case AtomicScopeModelKind::None:
276 return std::unique_ptr<AtomicScopeModel>{};
277 case AtomicScopeModelKind::OpenCL:
278 return std::make_unique<AtomicScopeOpenCLModel>();
279 case AtomicScopeModelKind::HIP:
280 return std::make_unique<AtomicScopeHIPModel>();
281 case AtomicScopeModelKind::Generic:
282 return std::make_unique<AtomicScopeGenericModel>();
283 }
284 llvm_unreachable("Invalid atomic scope model kind");
285 }
286 } // namespace clang
287
288 #endif
289