1 //
2 // Copyright (c) 2017 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //    http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #ifndef _HOST_ATOMICS_H_
17 #define _HOST_ATOMICS_H_
18 
19 #include "harness/testHarness.h"
20 
21 #ifdef WIN32
22 #include "Windows.h"
23 #endif
24 
25 //flag for test verification (good test should discover non-atomic functions and fail)
26 //#define NON_ATOMIC_FUNCTIONS
27 
28 enum TExplicitMemoryOrderType
29 {
30   MEMORY_ORDER_EMPTY,
31   MEMORY_ORDER_RELAXED,
32   MEMORY_ORDER_ACQUIRE,
33   MEMORY_ORDER_RELEASE,
34   MEMORY_ORDER_ACQ_REL,
35   MEMORY_ORDER_SEQ_CST
36 };
37 
38 // host atomic types (applicable for atomic functions supported on host OS)
39 #ifdef WIN32
40 #define HOST_ATOMIC_INT         unsigned long
41 #define HOST_ATOMIC_UINT        unsigned long
42 #define HOST_ATOMIC_LONG        unsigned long long
43 #define HOST_ATOMIC_ULONG       unsigned long long
44 #define HOST_ATOMIC_FLOAT       float
45 #define HOST_ATOMIC_DOUBLE      double
46 #else
47 #define HOST_ATOMIC_INT         cl_int
48 #define HOST_ATOMIC_UINT        cl_uint
49 #define HOST_ATOMIC_LONG        cl_long
50 #define HOST_ATOMIC_ULONG       cl_ulong
51 #define HOST_ATOMIC_FLOAT       cl_float
52 #define HOST_ATOMIC_DOUBLE      cl_double
53 #endif
54 
55 #define HOST_ATOMIC_INTPTR_T32  HOST_ATOMIC_INT
56 #define HOST_ATOMIC_UINTPTR_T32 HOST_ATOMIC_INT
57 #define HOST_ATOMIC_SIZE_T32    HOST_ATOMIC_UINT
58 #define HOST_ATOMIC_PTRDIFF_T32 HOST_ATOMIC_INT
59 
60 #define HOST_ATOMIC_INTPTR_T64  HOST_ATOMIC_LONG
61 #define HOST_ATOMIC_UINTPTR_T64 HOST_ATOMIC_LONG
62 #define HOST_ATOMIC_SIZE_T64    HOST_ATOMIC_ULONG
63 #define HOST_ATOMIC_PTRDIFF_T64 HOST_ATOMIC_LONG
64 
65 #define HOST_ATOMIC_FLAG        HOST_ATOMIC_INT
66 
67 // host regular types corresponding to atomic types
68 #define HOST_INT                cl_int
69 #define HOST_UINT               cl_uint
70 #define HOST_LONG               cl_long
71 #define HOST_ULONG              cl_ulong
72 #define HOST_FLOAT              cl_float
73 #define HOST_DOUBLE             cl_double
74 
75 #define HOST_INTPTR_T32         cl_int
76 #define HOST_UINTPTR_T32        cl_uint
77 #define HOST_SIZE_T32           cl_uint
78 #define HOST_PTRDIFF_T32        cl_int
79 
80 #define HOST_INTPTR_T64         cl_long
81 #define HOST_UINTPTR_T64        cl_ulong
82 #define HOST_SIZE_T64           cl_ulong
83 #define HOST_PTRDIFF_T64        cl_long
84 
85 #define HOST_FLAG               cl_uint
86 
87 // host atomic functions
88 void host_atomic_thread_fence(TExplicitMemoryOrderType order);
89 
90 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_add(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)91 CorrespondingType host_atomic_fetch_add(volatile AtomicType *a, CorrespondingType c,
92                                         TExplicitMemoryOrderType order)
93 {
94 #if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32))
95   return InterlockedExchangeAdd(a, c);
96 #elif defined(__GNUC__)
97   return __sync_fetch_and_add(a, c);
98 #else
99   log_info("Host function not implemented: atomic_fetch_add\n");
100   return 0;
101 #endif
102 }
103 
104 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_sub(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)105 CorrespondingType host_atomic_fetch_sub(volatile AtomicType *a, CorrespondingType c,
106                                         TExplicitMemoryOrderType order)
107 {
108 #if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32))
109   return InterlockedExchangeSubtract(a, c);
110 #elif defined(__GNUC__)
111   return __sync_fetch_and_sub(a, c);
112 #else
113   log_info("Host function not implemented: atomic_fetch_sub\n");
114   return 0;
115 #endif
116 }
117 
118 template <typename AtomicType, typename CorrespondingType>
host_atomic_exchange(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)119 CorrespondingType host_atomic_exchange(volatile AtomicType *a, CorrespondingType c,
120                                        TExplicitMemoryOrderType order)
121 {
122 #if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32))
123   return InterlockedExchange(a, c);
124 #elif defined(__GNUC__)
125   return __sync_lock_test_and_set(a, c);
126 #else
127   log_info("Host function not implemented: atomic_exchange\n");
128   return 0;
129 #endif
130 }
131 template <> HOST_FLOAT host_atomic_exchange(volatile HOST_ATOMIC_FLOAT *a, HOST_FLOAT c,
132                                             TExplicitMemoryOrderType order);
133 template <> HOST_DOUBLE host_atomic_exchange(volatile HOST_ATOMIC_DOUBLE *a, HOST_DOUBLE c,
134                                              TExplicitMemoryOrderType order);
135 
136 template <typename AtomicType, typename CorrespondingType>
host_atomic_compare_exchange(volatile AtomicType * a,CorrespondingType * expected,CorrespondingType desired,TExplicitMemoryOrderType order_success,TExplicitMemoryOrderType order_failure)137 bool host_atomic_compare_exchange(volatile AtomicType *a, CorrespondingType *expected, CorrespondingType desired,
138                                   TExplicitMemoryOrderType order_success,
139                                   TExplicitMemoryOrderType order_failure)
140 {
141   CorrespondingType tmp;
142 #if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32))
143   tmp = InterlockedCompareExchange(a, desired, *expected);
144 #elif defined(__GNUC__)
145   tmp = __sync_val_compare_and_swap(a, *expected, desired);
146 #else
147   log_info("Host function not implemented: atomic_compare_exchange\n");
148   tmp = 0;
149 #endif
150   if(tmp == *expected)
151     return true;
152   *expected = tmp;
153   return false;
154 }
155 
156 template <typename AtomicType, typename CorrespondingType>
host_atomic_load(volatile AtomicType * a,TExplicitMemoryOrderType order)157 CorrespondingType host_atomic_load(volatile AtomicType *a,
158                                    TExplicitMemoryOrderType order)
159 {
160 #if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32))
161   return InterlockedExchangeAdd(a, 0);
162 #elif defined(__GNUC__)
163   return __sync_add_and_fetch(a, 0);
164 #else
165   log_info("Host function not implemented: atomic_load\n");
166   return 0;
167 #endif
168 }
169 template <> HOST_FLOAT host_atomic_load(volatile HOST_ATOMIC_FLOAT *a,
170                                         TExplicitMemoryOrderType order);
171 template <> HOST_DOUBLE host_atomic_load(volatile HOST_ATOMIC_DOUBLE *a,
172                                          TExplicitMemoryOrderType order);
173 
174 template <typename AtomicType, typename CorrespondingType>
host_atomic_store(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)175 void host_atomic_store(volatile AtomicType* a, CorrespondingType c,
176                        TExplicitMemoryOrderType order)
177 {
178   host_atomic_exchange(a, c, order);
179 }
180 
181 template <typename AtomicType, typename CorrespondingType>
host_atomic_init(volatile AtomicType * a,CorrespondingType c)182 void host_atomic_init(volatile AtomicType* a, CorrespondingType c)
183 {
184   host_atomic_exchange(a, c, MEMORY_ORDER_RELAXED);
185 }
186 
187 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_or(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)188 CorrespondingType host_atomic_fetch_or(volatile AtomicType *a, CorrespondingType c,
189                                        TExplicitMemoryOrderType order)
190 {
191   CorrespondingType expected = host_atomic_load<AtomicType, CorrespondingType>(a, order);
192   CorrespondingType desired;
193   do
194   desired = expected | c;
195   while(!host_atomic_compare_exchange(a, &expected, desired, order, order));
196   return expected;
197 }
198 
199 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_and(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)200 CorrespondingType host_atomic_fetch_and(volatile AtomicType *a, CorrespondingType c,
201                                         TExplicitMemoryOrderType order)
202 {
203   CorrespondingType expected = host_atomic_load<AtomicType, CorrespondingType>(a, order);
204   CorrespondingType desired;
205   do
206   desired = expected & c;
207   while(!host_atomic_compare_exchange(a, &expected, desired, order, order));
208   return expected;
209 }
210 
211 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_xor(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)212 CorrespondingType host_atomic_fetch_xor(volatile AtomicType *a, CorrespondingType c,
213                                         TExplicitMemoryOrderType order)
214 {
215   CorrespondingType expected = host_atomic_load<AtomicType, CorrespondingType>(a, order);
216   CorrespondingType desired;
217   do
218   desired = expected ^ c;
219   while(!host_atomic_compare_exchange(a, &expected, desired, order, order));
220   return expected;
221 }
222 
223 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_min(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)224 CorrespondingType host_atomic_fetch_min(volatile AtomicType *a, CorrespondingType c,
225                                         TExplicitMemoryOrderType order)
226 {
227   CorrespondingType expected = host_atomic_load<AtomicType, CorrespondingType>(a, order);
228   CorrespondingType desired;
229   do
230   desired = expected < c ? expected : c;
231   while(!host_atomic_compare_exchange(a, &expected, desired, order, order));
232   return expected;
233 }
234 
235 template <typename AtomicType, typename CorrespondingType>
host_atomic_fetch_max(volatile AtomicType * a,CorrespondingType c,TExplicitMemoryOrderType order)236 CorrespondingType host_atomic_fetch_max(volatile AtomicType *a, CorrespondingType c,
237                                         TExplicitMemoryOrderType order)
238 {
239   CorrespondingType expected = host_atomic_load<AtomicType, CorrespondingType>(a, order);
240   CorrespondingType desired;
241   do
242   desired = expected > c ? expected : c;
243   while(!host_atomic_compare_exchange(a, &expected, desired, order, order));
244   return expected;
245 }
246 
247 bool host_atomic_flag_test_and_set(volatile HOST_ATOMIC_FLAG *a, TExplicitMemoryOrderType order);
248 void host_atomic_flag_clear(volatile HOST_ATOMIC_FLAG *a, TExplicitMemoryOrderType order);
249 
250 #endif //_HOST_ATOMICS_H_
251