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
22namespace 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///
42enum 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
60inline 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.
95enum class AtomicScopeModelKind { None, OpenCL, HIP, Generic };
96
97/// Defines the interface for synch scope model.
98class AtomicScopeModel {
99public:
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.
124class AtomicScopeOpenCLModel : public AtomicScopeModel {
125public:
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
137  AtomicScopeOpenCLModel() {}
138
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
153  bool isValid(unsigned S) const override {
154    return S >= static_cast<unsigned>(WorkGroup) &&
155           S <= static_cast<unsigned>(Last);
156  }
157
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
166  unsigned getFallBackValue() const override {
167    return static_cast<unsigned>(AllSVMDevices);
168  }
169};
170
171/// Defines the synch scope model for HIP.
172class AtomicScopeHIPModel : public AtomicScopeModel {
173public:
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
186  AtomicScopeHIPModel() {}
187
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
204  bool isValid(unsigned S) const override {
205    return S >= static_cast<unsigned>(SingleThread) &&
206           S <= static_cast<unsigned>(Last);
207  }
208
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
218  unsigned getFallBackValue() const override {
219    return static_cast<unsigned>(System);
220  }
221};
222
223/// Defines the generic atomic scope model.
224class AtomicScopeGenericModel : public AtomicScopeModel {
225public:
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
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
254  bool isValid(unsigned S) const override {
255    return S >= static_cast<unsigned>(System) &&
256           S <= static_cast<unsigned>(Last);
257  }
258
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
268  unsigned getFallBackValue() const override {
269    return static_cast<unsigned>(System);
270  }
271};
272
273inline std::unique_ptr<AtomicScopeModel>
274AtomicScopeModel::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