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>(System) &&
256            S <= static_cast<unsigned>(Last);
257   }
258 
getRuntimeValues()259   ArrayRef<unsigned> getRuntimeValues() const override {
260     static_assert(Last == Single, "Does not include all sync scopes");
261     static const unsigned Scopes[] = {
262         static_cast<unsigned>(Device), static_cast<unsigned>(System),
263         static_cast<unsigned>(Workgroup), static_cast<unsigned>(Wavefront),
264         static_cast<unsigned>(Single)};
265     return llvm::ArrayRef(Scopes);
266   }
267 
getFallBackValue()268   unsigned getFallBackValue() const override {
269     return static_cast<unsigned>(System);
270   }
271 };
272 
273 inline std::unique_ptr<AtomicScopeModel>
create(AtomicScopeModelKind K)274 AtomicScopeModel::create(AtomicScopeModelKind K) {
275   switch (K) {
276   case AtomicScopeModelKind::None:
277     return std::unique_ptr<AtomicScopeModel>{};
278   case AtomicScopeModelKind::OpenCL:
279     return std::make_unique<AtomicScopeOpenCLModel>();
280   case AtomicScopeModelKind::HIP:
281     return std::make_unique<AtomicScopeHIPModel>();
282   case AtomicScopeModelKind::Generic:
283     return std::make_unique<AtomicScopeGenericModel>();
284   }
285   llvm_unreachable("Invalid atomic scope model kind");
286 }
287 } // namespace clang
288 
289 #endif
290