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   HIPSingleThread,
44   HIPWavefront,
45   HIPWorkgroup,
46   HIPAgent,
47   HIPSystem,
48   OpenCLWorkGroup,
49   OpenCLDevice,
50   OpenCLAllSVMDevices,
51   OpenCLSubGroup,
52   Last = OpenCLSubGroup
53 };
54 
55 inline llvm::StringRef getAsString(SyncScope S) {
56   switch (S) {
57   case SyncScope::HIPSingleThread:
58     return "hip_singlethread";
59   case SyncScope::HIPWavefront:
60     return "hip_wavefront";
61   case SyncScope::HIPWorkgroup:
62     return "hip_workgroup";
63   case SyncScope::HIPAgent:
64     return "hip_agent";
65   case SyncScope::HIPSystem:
66     return "hip_system";
67   case SyncScope::OpenCLWorkGroup:
68     return "opencl_workgroup";
69   case SyncScope::OpenCLDevice:
70     return "opencl_device";
71   case SyncScope::OpenCLAllSVMDevices:
72     return "opencl_allsvmdevices";
73   case SyncScope::OpenCLSubGroup:
74     return "opencl_subgroup";
75   }
76   llvm_unreachable("Invalid synch scope");
77 }
78 
79 /// Defines the kind of atomic scope models.
80 enum class AtomicScopeModelKind { None, OpenCL, HIP };
81 
82 /// Defines the interface for synch scope model.
83 class AtomicScopeModel {
84 public:
85   virtual ~AtomicScopeModel() {}
86   /// Maps language specific synch scope values to internal
87   /// SyncScope enum.
88   virtual SyncScope map(unsigned S) const = 0;
89 
90   /// Check if the compile-time constant synch scope value
91   /// is valid.
92   virtual bool isValid(unsigned S) const = 0;
93 
94   /// Get all possible synch scope values that might be
95   /// encountered at runtime for the current language.
96   virtual ArrayRef<unsigned> getRuntimeValues() const = 0;
97 
98   /// If atomic builtin function is called with invalid
99   /// synch scope value at runtime, it will fall back to a valid
100   /// synch scope value returned by this function.
101   virtual unsigned getFallBackValue() const = 0;
102 
103   /// Create an atomic scope model by AtomicScopeModelKind.
104   /// \return an empty std::unique_ptr for AtomicScopeModelKind::None.
105   static std::unique_ptr<AtomicScopeModel> create(AtomicScopeModelKind K);
106 };
107 
108 /// Defines the synch scope model for OpenCL.
109 class AtomicScopeOpenCLModel : public AtomicScopeModel {
110 public:
111   /// The enum values match the pre-defined macros
112   /// __OPENCL_MEMORY_SCOPE_*, which are used to define memory_scope_*
113   /// enums in opencl-c-base.h.
114   enum ID {
115     WorkGroup = 1,
116     Device = 2,
117     AllSVMDevices = 3,
118     SubGroup = 4,
119     Last = SubGroup
120   };
121 
122   AtomicScopeOpenCLModel() {}
123 
124   SyncScope map(unsigned S) const override {
125     switch (static_cast<ID>(S)) {
126     case WorkGroup:
127       return SyncScope::OpenCLWorkGroup;
128     case Device:
129       return SyncScope::OpenCLDevice;
130     case AllSVMDevices:
131       return SyncScope::OpenCLAllSVMDevices;
132     case SubGroup:
133       return SyncScope::OpenCLSubGroup;
134     }
135     llvm_unreachable("Invalid language synch scope value");
136   }
137 
138   bool isValid(unsigned S) const override {
139     return S >= static_cast<unsigned>(WorkGroup) &&
140            S <= static_cast<unsigned>(Last);
141   }
142 
143   ArrayRef<unsigned> getRuntimeValues() const override {
144     static_assert(Last == SubGroup, "Does not include all synch scopes");
145     static const unsigned Scopes[] = {
146         static_cast<unsigned>(WorkGroup), static_cast<unsigned>(Device),
147         static_cast<unsigned>(AllSVMDevices), static_cast<unsigned>(SubGroup)};
148     return llvm::makeArrayRef(Scopes);
149   }
150 
151   unsigned getFallBackValue() const override {
152     return static_cast<unsigned>(AllSVMDevices);
153   }
154 };
155 
156 /// Defines the synch scope model for HIP.
157 class AtomicScopeHIPModel : public AtomicScopeModel {
158 public:
159   /// The enum values match the pre-defined macros
160   /// __HIP_MEMORY_SCOPE_*, which are used to define memory_scope_*
161   /// enums in hip-c.h.
162   enum ID {
163     SingleThread = 1,
164     Wavefront = 2,
165     Workgroup = 3,
166     Agent = 4,
167     System = 5,
168     Last = System
169   };
170 
171   AtomicScopeHIPModel() {}
172 
173   SyncScope map(unsigned S) const override {
174     switch (static_cast<ID>(S)) {
175     case SingleThread:
176       return SyncScope::HIPSingleThread;
177     case Wavefront:
178       return SyncScope::HIPWavefront;
179     case Workgroup:
180       return SyncScope::HIPWorkgroup;
181     case Agent:
182       return SyncScope::HIPAgent;
183     case System:
184       return SyncScope::HIPSystem;
185     }
186     llvm_unreachable("Invalid language synch scope value");
187   }
188 
189   bool isValid(unsigned S) const override {
190     return S >= static_cast<unsigned>(SingleThread) &&
191            S <= static_cast<unsigned>(Last);
192   }
193 
194   ArrayRef<unsigned> getRuntimeValues() const override {
195     static_assert(Last == System, "Does not include all synch scopes");
196     static const unsigned Scopes[] = {
197         static_cast<unsigned>(SingleThread), static_cast<unsigned>(Wavefront),
198         static_cast<unsigned>(Workgroup), static_cast<unsigned>(Agent),
199         static_cast<unsigned>(System)};
200     return llvm::makeArrayRef(Scopes);
201   }
202 
203   unsigned getFallBackValue() const override {
204     return static_cast<unsigned>(System);
205   }
206 };
207 
208 inline std::unique_ptr<AtomicScopeModel>
209 AtomicScopeModel::create(AtomicScopeModelKind K) {
210   switch (K) {
211   case AtomicScopeModelKind::None:
212     return std::unique_ptr<AtomicScopeModel>{};
213   case AtomicScopeModelKind::OpenCL:
214     return std::make_unique<AtomicScopeOpenCLModel>();
215   case AtomicScopeModelKind::HIP:
216     return std::make_unique<AtomicScopeHIPModel>();
217   }
218   llvm_unreachable("Invalid atomic scope model kind");
219 }
220 } // namespace clang
221 
222 #endif
223