//===--- SyncScope.h - Atomic synchronization scopes ------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// Provides definitions for the atomic synchronization scopes. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_BASIC_SYNCSCOPE_H #define LLVM_CLANG_BASIC_SYNCSCOPE_H #include "clang/Basic/LangOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include namespace clang { /// Defines sync scope values used internally by clang. /// /// The enum values start from 0 and are contiguous. They are mainly used for /// enumerating all supported sync scope values and mapping them to LLVM /// sync scopes. Their numerical values may be different from the corresponding /// sync scope enums used in source languages. /// /// In atomic builtin and expressions, language-specific sync scope enums are /// used. Currently only OpenCL memory scope enums are supported and assumed /// to be used by all languages. However, in the future, other languages may /// define their own set of sync scope enums. The language-specific sync scope /// values are represented by class AtomicScopeModel and its derived classes. /// /// To add a new enum value: /// Add the enum value to enum class SyncScope. /// Update enum value Last if necessary. /// Update getAsString. /// enum class SyncScope { SystemScope, DeviceScope, WorkgroupScope, ClusterScope, WavefrontScope, SingleScope, HIPSingleThread, HIPWavefront, HIPWorkgroup, HIPCluster, HIPAgent, HIPSystem, OpenCLWorkGroup, OpenCLDevice, OpenCLAllSVMDevices, OpenCLSubGroup, Last = OpenCLSubGroup }; inline llvm::StringRef getAsString(SyncScope S) { switch (S) { case SyncScope::SystemScope: return "system_scope"; case SyncScope::DeviceScope: return "device_scope"; case SyncScope::WorkgroupScope: return "workgroup_scope"; case SyncScope::ClusterScope: return "cluster_scope"; case SyncScope::WavefrontScope: return "wavefront_scope"; case SyncScope::SingleScope: return "single_scope"; case SyncScope::HIPSingleThread: return "hip_singlethread"; case SyncScope::HIPWavefront: return "hip_wavefront"; case SyncScope::HIPWorkgroup: return "hip_workgroup"; case SyncScope::HIPCluster: return "hip_cluster"; case SyncScope::HIPAgent: return "hip_agent"; case SyncScope::HIPSystem: return "hip_system"; case SyncScope::OpenCLWorkGroup: return "opencl_workgroup"; case SyncScope::OpenCLDevice: return "opencl_device"; case SyncScope::OpenCLAllSVMDevices: return "opencl_allsvmdevices"; case SyncScope::OpenCLSubGroup: return "opencl_subgroup"; } llvm_unreachable("Invalid sync scope"); } /// Defines the kind of atomic scope models. enum class AtomicScopeModelKind { None, OpenCL, HIP, Generic }; /// Defines the interface for sync scope model. class AtomicScopeModel { public: virtual ~AtomicScopeModel() {} /// Maps language specific sync scope values to internal /// SyncScope enum. virtual SyncScope map(unsigned S) const = 0; /// Check if the compile-time constant sync scope value /// is valid. virtual bool isValid(unsigned S) const = 0; /// Get all possible sync scope values that might be /// encountered at runtime for the current language. virtual ArrayRef getRuntimeValues() const = 0; /// If atomic builtin function is called with invalid /// sync scope value at runtime, it will fall back to a valid /// sync scope value returned by this function. virtual unsigned getFallBackValue() const = 0; /// Create an atomic scope model by AtomicScopeModelKind. /// \return an empty std::unique_ptr for AtomicScopeModelKind::None. static std::unique_ptr create(AtomicScopeModelKind K); }; /// Defines the sync scope model for OpenCL. class AtomicScopeOpenCLModel : public AtomicScopeModel { public: /// The enum values match the pre-defined macros /// __OPENCL_MEMORY_SCOPE_*, which are used to define memory_scope_* /// enums in opencl-c-base.h. enum ID { WorkGroup = 1, Device = 2, AllSVMDevices = 3, SubGroup = 4, Last = SubGroup }; AtomicScopeOpenCLModel() {} SyncScope map(unsigned S) const override { switch (static_cast(S)) { case WorkGroup: return SyncScope::OpenCLWorkGroup; case Device: return SyncScope::OpenCLDevice; case AllSVMDevices: return SyncScope::OpenCLAllSVMDevices; case SubGroup: return SyncScope::OpenCLSubGroup; } llvm_unreachable("Invalid language sync scope value"); } bool isValid(unsigned S) const override { return S >= static_cast(WorkGroup) && S <= static_cast(Last); } ArrayRef getRuntimeValues() const override { static_assert(Last == SubGroup, "Does not include all sync scopes"); static const unsigned Scopes[] = { static_cast(WorkGroup), static_cast(Device), static_cast(AllSVMDevices), static_cast(SubGroup)}; return llvm::ArrayRef(Scopes); } unsigned getFallBackValue() const override { return static_cast(AllSVMDevices); } }; /// Defines the sync scope model for HIP. class AtomicScopeHIPModel : public AtomicScopeModel { public: /// The enum values match the pre-defined macros /// __HIP_MEMORY_SCOPE_*, which are used to define memory_scope_* /// enums in hip-c.h. /// These may be present in pch files or bitcode so preserve existing values /// when adding a new ID. enum ID { SingleThread = 1, Wavefront = 2, Workgroup = 3, Agent = 4, System = 5, Cluster = 6, End, Last = End - 1, Count = Last }; AtomicScopeHIPModel() {} SyncScope map(unsigned S) const override { switch (static_cast(S)) { case SingleThread: return SyncScope::HIPSingleThread; case Wavefront: return SyncScope::HIPWavefront; case Workgroup: return SyncScope::HIPWorkgroup; case Cluster: return SyncScope::HIPCluster; case Agent: return SyncScope::HIPAgent; case System: return SyncScope::HIPSystem; case End: break; } llvm_unreachable("Invalid language sync scope value"); } bool isValid(unsigned S) const override { return S >= static_cast(SingleThread) && S <= static_cast(Last); } ArrayRef getRuntimeValues() const override { static const unsigned Scopes[] = { static_cast(SingleThread), static_cast(Wavefront), static_cast(Workgroup), static_cast(Cluster), static_cast(System), static_cast(Agent)}; static_assert(sizeof(Scopes) / sizeof(Scopes[0]) == Count, "Does not include all sync scopes"); return llvm::ArrayRef(Scopes); } unsigned getFallBackValue() const override { return static_cast(System); } }; /// Defines the generic atomic scope model. class AtomicScopeGenericModel : public AtomicScopeModel { public: /// The enum values match predefined built-in macros __MEMORY_SCOPE_*. /// These may be present in pch files or bitcode so preserve existing values /// when adding a new ID. enum ID { System = 0, Device = 1, Workgroup = 2, Wavefront = 3, Single = 4, Cluster = 5, Count, Last = Count - 1 }; AtomicScopeGenericModel() = default; SyncScope map(unsigned S) const override { switch (static_cast(S)) { case Device: return SyncScope::DeviceScope; case System: return SyncScope::SystemScope; case Workgroup: return SyncScope::WorkgroupScope; case Cluster: return SyncScope::ClusterScope; case Wavefront: return SyncScope::WavefrontScope; case Single: return SyncScope::SingleScope; case Count: break; } llvm_unreachable("Invalid language sync scope value"); } bool isValid(unsigned S) const override { return S <= static_cast(Last); } ArrayRef getRuntimeValues() const override { static const unsigned Scopes[] = { static_cast(System), static_cast(Device), static_cast(Workgroup), static_cast(Cluster), static_cast(Wavefront), static_cast(Single)}; static_assert(sizeof(Scopes) / sizeof(Scopes[0]) == Count, "Does not include all sync scopes"); return llvm::ArrayRef(Scopes); } unsigned getFallBackValue() const override { return static_cast(System); } }; inline std::unique_ptr AtomicScopeModel::create(AtomicScopeModelKind K) { switch (K) { case AtomicScopeModelKind::None: return std::unique_ptr{}; case AtomicScopeModelKind::OpenCL: return std::make_unique(); case AtomicScopeModelKind::HIP: return std::make_unique(); case AtomicScopeModelKind::Generic: return std::make_unique(); } llvm_unreachable("Invalid atomic scope model kind"); } } // namespace clang #endif