Updated copyright
[libcds.git] / cds / compiler / gcc / x86 / cxx11_atomic32.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #ifndef CDSLIB_COMPILER_GCC_X86_CXX11_ATOMIC32_H
32 #define CDSLIB_COMPILER_GCC_X86_CXX11_ATOMIC32_H
33
34 #include <cstdint>
35 #include <cds/details/is_aligned.h>
36
37 //@cond
38 namespace cds { namespace cxx11_atomic {
39     namespace platform { CDS_CXX11_INLINE_NAMESPACE namespace gcc { CDS_CXX11_INLINE_NAMESPACE namespace x86 {
40
41         static inline void fence_before( memory_order order ) CDS_NOEXCEPT
42         {
43             switch(order) {
44             case memory_order_relaxed:
45             case memory_order_acquire:
46             case memory_order_consume:
47                 break;
48             case memory_order_release:
49             case memory_order_acq_rel:
50                 CDS_COMPILER_RW_BARRIER;
51                 break;
52             case memory_order_seq_cst:
53                 CDS_COMPILER_RW_BARRIER;
54                 break;
55             }
56         }
57
58         static inline void fence_after( memory_order order ) CDS_NOEXCEPT
59         {
60             switch(order) {
61             case memory_order_acquire:
62             case memory_order_acq_rel:
63                 CDS_COMPILER_RW_BARRIER;
64                 break;
65             case memory_order_relaxed:
66             case memory_order_consume:
67             case memory_order_release:
68                 break;
69             case memory_order_seq_cst:
70                 CDS_COMPILER_RW_BARRIER;
71                 break;
72             }
73         }
74
75
76         static inline void fence_after_load(memory_order order) CDS_NOEXCEPT
77         {
78             switch(order) {
79             case memory_order_relaxed:
80             case memory_order_release:
81                 break;
82             case memory_order_acquire:
83             case memory_order_acq_rel:
84                 CDS_COMPILER_RW_BARRIER;
85                 break;
86             case memory_order_consume:
87                 break;
88             case memory_order_seq_cst:
89                 __asm__ __volatile__ ( "mfence" ::: "memory"  );
90                 break;
91             default:;
92             }
93         }
94
95         //-----------------------------------------------------------------------------
96         // fences
97         //-----------------------------------------------------------------------------
98         static inline void thread_fence(memory_order order) CDS_NOEXCEPT
99         {
100             switch(order)
101             {
102                 case memory_order_relaxed:
103                 case memory_order_consume:
104                     break;
105                 case memory_order_release:
106                 case memory_order_acquire:
107                 case memory_order_acq_rel:
108                     CDS_COMPILER_RW_BARRIER;
109                     break;
110                 case memory_order_seq_cst:
111                     __asm__ __volatile__ ( "mfence" ::: "memory"  );
112                     break;
113                 default:;
114             }
115         }
116
117         static inline void signal_fence(memory_order order) CDS_NOEXCEPT
118         {
119             // C++11: 29.8.8: only compiler optimization, no hardware instructions
120             switch(order)
121             {
122                 case memory_order_relaxed:
123                     break;
124                 case memory_order_consume:
125                 case memory_order_release:
126                 case memory_order_acquire:
127                 case memory_order_acq_rel:
128                 case memory_order_seq_cst:
129                     CDS_COMPILER_RW_BARRIER;
130                     break;
131                 default:;
132             }
133         }
134
135         //-----------------------------------------------------------------------------
136         // 8bit primitives
137         //-----------------------------------------------------------------------------
138
139         template <typename T>
140         static inline bool cas8_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
141         {
142             static_assert( sizeof(T) == 1, "Illegal size of operand" );
143
144             T prev = expected;
145             fence_before(mo_success);
146             __asm__ __volatile__  (
147                 "lock ; cmpxchgb %[desired], %[pDest]"
148                 : [prev] "+a" (prev), [pDest] "+m" (*pDest)
149                 : [desired] "q" (desired)
150                 );
151             bool success = (prev == expected);
152             expected = prev;
153             if (success)
154                 fence_after(mo_success);
155             else
156                 fence_after(mo_fail);
157             return success;
158         }
159
160         template <typename T>
161         static inline bool cas8_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
162         {
163             return cas8_strong( pDest, expected, desired, mo_success, mo_fail );
164         }
165
166         template <typename T>
167         static inline T exchange8( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
168         {
169             static_assert( sizeof(T) == 1, "Illegal size of operand" );
170
171             fence_before(order);
172             __asm__ __volatile__  (
173                 "xchgb %[v], %[pDest]"
174                 : [v] "+q" (v), [pDest] "+m" (*pDest)
175                 );
176             fence_after(order);
177             return v;
178         }
179
180         template <typename T>
181         static inline void store8( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
182         {
183             static_assert( sizeof(T) == 1, "Illegal size of operand" );
184             assert( order ==  memory_order_relaxed
185                 || order ==  memory_order_release
186                 || order == memory_order_seq_cst
187                 );
188             assert( pDest != NULL );
189
190             if ( order != memory_order_seq_cst ) {
191                 fence_before( order );
192                 *pDest = src;
193             }
194             else {
195                 exchange8( pDest, src, order );
196             }
197         }
198
199         template <typename T>
200         static inline T load8( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
201         {
202             static_assert( sizeof(T) == 1, "Illegal size of operand" );
203             assert( order ==  memory_order_relaxed
204                 || order ==  memory_order_consume
205                 || order ==  memory_order_acquire
206                 || order == memory_order_seq_cst
207                 );
208             assert( pSrc != NULL );
209
210             T v = *pSrc;
211             fence_after_load( order );
212             return v;
213         }
214
215 #       define CDS_ATOMIC_fetch8_add_defined
216         template <typename T>
217         static inline T fetch8_add( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
218         {
219             fence_before(order);
220             __asm__ __volatile__  (
221                 "lock ; xaddb %[val], %[pDest]"
222                 : [val] "+q" (val), [pDest] "+m" (*pDest)
223                 );
224             fence_after(order);
225             return val;
226         }
227
228 #       define CDS_ATOMIC_fetch8_sub_defined
229         template <typename T>
230         static inline T fetch8_sub( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
231         {
232             fence_before(order);
233             __asm__ __volatile__  (
234                 "negb %[val] ; \n"
235                 "lock ; xaddb %[val], %[pDest]"
236                 : [val] "+q" (val), [pDest] "+m" (*pDest)
237                 );
238             fence_after(order);
239             return val;
240         }
241
242         //-----------------------------------------------------------------------------
243         // atomic flag primitives
244         //-----------------------------------------------------------------------------
245
246         typedef bool atomic_flag_type;
247         static inline bool atomic_flag_tas( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
248         {
249             return exchange8( pFlag, true, order );
250         }
251
252         static inline void atomic_flag_clear( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
253         {
254             store8( pFlag, false, order );
255         }
256
257         //-----------------------------------------------------------------------------
258         // 16bit primitives
259         //-----------------------------------------------------------------------------
260
261         template <typename T>
262         static inline T exchange16( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
263         {
264             static_assert( sizeof(T) == 2, "Illegal size of operand" );
265             assert( cds::details::is_aligned( pDest, 2 ));
266
267             fence_before(order);
268             __asm__ __volatile__  (
269                 "xchgw %[v], %[pDest]"
270                 : [v] "+q" (v), [pDest] "+m" (*pDest)
271                 );
272             fence_after(order);
273             return v;
274         }
275
276         template <typename T>
277         static inline void store16( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
278         {
279             static_assert( sizeof(T) == 2, "Illegal size of operand" );
280             assert( order ==  memory_order_relaxed
281                 || order ==  memory_order_release
282                 || order == memory_order_seq_cst
283                 );
284             assert( pDest != NULL );
285             assert( cds::details::is_aligned( pDest, 2 ));
286
287             if ( order != memory_order_seq_cst ) {
288                 fence_before( order );
289                 *pDest = src;
290             }
291             else {
292                 exchange16( pDest, src, order );
293             }
294         }
295
296         template <typename T>
297         static inline T load16( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
298         {
299             static_assert( sizeof(T) == 2, "Illegal size of operand" );
300             assert( order ==  memory_order_relaxed
301                 || order ==  memory_order_consume
302                 || order ==  memory_order_acquire
303                 || order ==  memory_order_seq_cst
304                 );
305             assert( pSrc != NULL );
306             assert( cds::details::is_aligned( pSrc, 2 ));
307
308             T v = *pSrc;
309             fence_after_load( order );
310             return v;
311         }
312
313         template <typename T>
314         static inline bool cas16_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
315         {
316             static_assert( sizeof(T) == 2, "Illegal size of operand" );
317             assert( cds::details::is_aligned( pDest, 2 ));
318
319             T prev = expected;
320             fence_before(mo_success);
321             __asm__ __volatile__  (
322                 "lock ; cmpxchgw %[desired], %[pDest]"
323                 : [prev] "+a" (prev), [pDest] "+m" (*pDest)
324                 : [desired] "q" (desired)
325                 );
326             bool success = prev == expected;
327             if (success)
328                 fence_after(mo_success);
329             else {
330                 fence_after(mo_fail);
331                 expected = prev;
332             }
333
334             return success;
335         }
336
337         template <typename T>
338         static inline bool cas16_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
339         {
340             return cas16_strong( pDest, expected, desired, mo_success, mo_fail );
341         }
342
343 #       define CDS_ATOMIC_fetch16_add_defined
344         template <typename T>
345         static inline T fetch16_add( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
346         {
347             static_assert( sizeof(T) == 2, "Illegal size of operand" );
348             assert( cds::details::is_aligned( pDest, 2 ));
349
350             fence_before(order);
351             __asm__ __volatile__  (
352                 "lock ; xaddw %[val], %[pDest]"
353                 : [val] "+q" (val), [pDest] "+m" (*pDest)
354                 );
355             fence_after(order);
356             return val;
357         }
358
359 #       define CDS_ATOMIC_fetch16_sub_defined
360         template <typename T>
361         static inline T fetch16_sub( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
362         {
363             static_assert( sizeof(T) == 2, "Illegal size of operand" );
364             assert( cds::details::is_aligned( pDest, 2 ));
365
366             fence_before(order);
367             __asm__ __volatile__  (
368                 "negw %[val] ; \n"
369                 "lock ; xaddw %[val], %[pDest]"
370                 : [val] "+q" (val), [pDest] "+m" (*pDest)
371                 );
372             fence_after(order);
373             return val;
374         }
375
376         //-----------------------------------------------------------------------------
377         // 32bit primitives
378         //-----------------------------------------------------------------------------
379
380         template <typename T>
381         static inline T exchange32( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
382         {
383             static_assert( sizeof(T) == 4, "Illegal size of operand" );
384             assert( cds::details::is_aligned( pDest, 4 ));
385
386             fence_before(order);
387             __asm__ __volatile__  (
388                 "xchgl %[v], %[pDest]"
389                 : [v] "+r" (v), [pDest] "+m" (*pDest)
390                 );
391             fence_after(order);
392             return v;
393         }
394
395         template <typename T>
396         static inline void store32( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
397         {
398             static_assert( sizeof(T) == 4, "Illegal size of operand" );
399             assert( order ==  memory_order_relaxed
400                 || order ==  memory_order_release
401                 || order == memory_order_seq_cst
402                 );
403             assert( pDest != NULL );
404             assert( cds::details::is_aligned( pDest, 4 ));
405
406             if ( order != memory_order_seq_cst ) {
407                 fence_before( order );
408                 *pDest = src;
409             }
410             else {
411                 exchange32( pDest, src, order );
412             }
413         }
414
415         template <typename T>
416         static inline T load32( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
417         {
418             static_assert( sizeof(T) == 4, "Illegal size of operand" );
419             assert( order ==  memory_order_relaxed
420                 || order ==  memory_order_consume
421                 || order ==  memory_order_acquire
422                 || order ==  memory_order_seq_cst
423                 );
424             assert( pSrc != NULL );
425             assert( cds::details::is_aligned( pSrc, 4 ));
426
427             T v = *pSrc;
428             fence_after_load( order );
429             return v;
430         }
431
432         template <typename T>
433         static inline bool cas32_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
434         {
435             static_assert( sizeof(T) == 4, "Illegal size of operand" );
436             assert( cds::details::is_aligned( pDest, 4 ));
437
438             T prev = expected;
439             fence_before(mo_success);
440             __asm__ __volatile__  (
441                 "lock ; cmpxchgl %[desired], %[pDest]"
442                 : [prev] "+a" (prev), [pDest] "+m" (*pDest)
443                 : [desired] "r" (desired)
444                 );
445             bool success = prev == expected;
446             if (success)
447                 fence_after(mo_success);
448             else {
449                 fence_after(mo_fail);
450                 expected = prev;
451             }
452             return success;
453         }
454
455         template <typename T>
456         static inline bool cas32_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
457         {
458             return cas32_strong( pDest, expected, desired, mo_success, mo_fail );
459         }
460
461         // fetch_xxx may be emulated via cas32
462         // If the platform has special fetch_xxx instruction
463         // then it should define CDS_ATOMIC_fetch32_xxx_defined macro
464
465 #       define CDS_ATOMIC_fetch32_add_defined
466         template <typename T>
467         static inline T fetch32_add( T volatile * pDest, T v, memory_order order) CDS_NOEXCEPT
468         {
469             static_assert( sizeof(T) == 4, "Illegal size of operand" );
470             assert( cds::details::is_aligned( pDest, 4 ));
471
472             fence_before(order);
473             __asm__ __volatile__  (
474                 "lock ; xaddl %[v], %[pDest]"
475                 : [v] "+r" (v), [pDest] "+m" (*pDest)
476                 );
477             fence_after(order);
478             return v;
479         }
480
481 #       define CDS_ATOMIC_fetch32_sub_defined
482         template <typename T>
483         static inline T fetch32_sub( T volatile * pDest, T v, memory_order order) CDS_NOEXCEPT
484         {
485             static_assert( sizeof(T) == 4, "Illegal size of operand" );
486             assert( cds::details::is_aligned( pDest, 4 ));
487
488             fence_before(order);
489             __asm__ __volatile__  (
490                 "negl   %[v] ; \n"
491                 "lock ; xaddl %[v], %[pDest]"
492                 : [v] "+r" (v), [pDest] "+m" (*pDest)
493                 );
494             fence_after(order);
495             return v;
496         }
497
498     }}} // namespace platform::gcc::x86
499 }}  // namespace cds::cxx11_atomic
500 //@endcond
501
502 #endif // #ifndef CDSLIB_COMPILER_GCC_X86_CXX11_ATOMIC32_H