Merged branch 'master' of https://github.com/Nemo1369/libcds
[libcds.git] / cds / compiler / gcc / sparc / cxx11_atomic.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_SPARC_CXX11_ATOMIC_H
32 #define CDSLIB_COMPILER_GCC_SPARC_CXX11_ATOMIC_H
33
34 #include <cstdint>
35
36 /*
37     Source:
38
39     1. [Doug Lea "JSR-133 Cookbook for Compiler Writers]:
40
41         Acquire semantics: load; LoadLoad+LoadStore
42         Release semantics: LoadStore+StoreStore; store
43
44     2. boost::atomic library by Helge Bahman
45     3. OpenSparc source code
46 */
47
48 #if CDS_OS_TYPE == CDS_OS_LINUX
49 #   define CDS_SPARC_RMO_MEMORY_MODEL
50 #endif
51
52 #define CDS_SPARC_MB_FULL    "membar #Sync \n\t"
53 #ifdef CDS_SPARC_RMO_MEMORY_MODEL
54         // RMO memory model (Linux only?..) Untested
55 #   define CDS_SPARC_MB_LL_LS       "membar #LoadLoad|#LoadStore \n\t"
56 #   define CDS_SPARC_MB_LS_SS       "membar #LoadStore|#StoreStore \n\t"
57 #   define CDS_SPARC_MB_LL_LS_SS    "membar #LoadLoad|#LoadStore|#StoreStore \n\t"
58 #else
59         // TSO memory model (default; Solaris uses this model)
60 #   define CDS_SPARC_MB_LL_LS
61 #   define CDS_SPARC_MB_LS_SS
62 #   define CDS_SPARC_MB_LL_LS_SS
63 #endif
64
65 #define CDS_SPARC_MB_ACQ        CDS_SPARC_MB_LL_LS
66 #define CDS_SPARC_MB_REL        CDS_SPARC_MB_LS_SS
67 #define CDS_SPARC_MB_ACQ_REL    CDS_SPARC_MB_LL_LS_SS
68 #define CDS_SPARC_MB_SEQ_CST    CDS_SPARC_MB_FULL
69
70 //@cond
71 namespace cds { namespace cxx11_atomic {
72     namespace platform { CDS_CXX11_INLINE_NAMESPACE namespace gcc { CDS_CXX11_INLINE_NAMESPACE namespace Sparc {
73
74         static inline void fence_before( memory_order order ) CDS_NOEXCEPT
75         {
76             switch(order) {
77             case memory_order_relaxed:
78             case memory_order_acquire:
79             case memory_order_consume:
80                 break;
81             case memory_order_release:
82             case memory_order_acq_rel:
83                 __asm__ __volatile__ ( "" CDS_SPARC_MB_REL ::: "memory" );
84                 break;
85             case memory_order_seq_cst:
86                 __asm__ __volatile__ ( "" CDS_SPARC_MB_FULL ::: "memory" );
87                 break;
88             }
89         }
90
91         static inline void fence_after( memory_order order ) CDS_NOEXCEPT
92         {
93             switch(order) {
94             case memory_order_relaxed:
95             case memory_order_consume:
96             case memory_order_release:
97                 break;
98             case memory_order_acquire:
99             case memory_order_acq_rel:
100                 __asm__ __volatile__ ( "" CDS_SPARC_MB_ACQ ::: "memory" );
101                 break;
102             case memory_order_seq_cst:
103                 __asm__ __volatile__ ( "" CDS_SPARC_MB_FULL ::: "memory" );
104                 break;
105             }
106         }
107
108
109         //-----------------------------------------------------------------------------
110         // fences
111         //-----------------------------------------------------------------------------
112         static inline void thread_fence(memory_order order) CDS_NOEXCEPT
113         {
114             switch(order)
115             {
116                 case memory_order_relaxed:
117                 case memory_order_consume:
118                     break;
119                 case memory_order_acquire:
120                     __asm__ __volatile__ ( "" CDS_SPARC_MB_ACQ ::: "memory" );
121                     break;
122                 case memory_order_release:
123                     __asm__ __volatile__ ( "" CDS_SPARC_MB_REL ::: "memory" );
124                     break;
125                 case memory_order_acq_rel:
126                     __asm__ __volatile__ ( "" CDS_SPARC_MB_ACQ_REL ::: "memory" );
127                     break;
128                 case memory_order_seq_cst:
129                     __asm__ __volatile__ ( "" CDS_SPARC_MB_SEQ_CST ::: "memory"  );
130                     break;
131                 default:;
132             }
133         }
134
135         static inline void signal_fence(memory_order order) CDS_NOEXCEPT
136         {
137             // C++11: 29.8.8: only compiler optimization, no hardware instructions
138             switch(order)
139             {
140                 case memory_order_relaxed:
141                     break;
142                 case memory_order_consume:
143                 case memory_order_release:
144                 case memory_order_acquire:
145                 case memory_order_acq_rel:
146                 case memory_order_seq_cst:
147                     CDS_COMPILER_RW_BARRIER;
148                     break;
149                 default:;
150             }
151         }
152
153         //-----------------------------------------------------------------------------
154         // atomic flag primitives
155         //-----------------------------------------------------------------------------
156
157         typedef unsigned char atomic_flag_type;
158         static inline bool atomic_flag_tas( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
159         {
160             atomic_flag_type fCur;
161             fence_before( order );
162             __asm__ __volatile__(
163                 "ldstub    [%[pFlag]], %[fCur] \n\t"
164                 : [fCur] "=r"(fCur)
165                 : [pFlag] "r"(pFlag)
166                 : "memory", "cc"
167                 );
168             fence_after( order );
169             return fCur != 0;
170         }
171
172         static inline void atomic_flag_clear( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
173         {
174             fence_before( order );
175             __asm__ __volatile__(
176                 CDS_SPARC_MB_REL
177                 "stub    %%g0, [%[pFlag]] \n\t"
178                 :: [pFlag] "r"(pFlag)
179                 : "memory"
180             );
181             fence_after( order );
182         }
183
184         //-----------------------------------------------------------------------------
185         // 32bit primitives
186         //-----------------------------------------------------------------------------
187
188         template <typename T>
189         static inline void store32( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
190         {
191             static_assert( sizeof(T) == 4, "Illegal size of operand" );
192             assert( order ==  memory_order_relaxed
193                 || order ==  memory_order_release
194                 || order == memory_order_seq_cst
195                 );
196             assert( pDest );
197
198             fence_before(order);
199             *pDest = src;
200             fence_after(order);
201         }
202
203         template <typename T>
204         static inline T load32( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
205         {
206             static_assert( sizeof(T) == 4, "Illegal size of operand" );
207             assert( order ==  memory_order_relaxed
208                 || order ==  memory_order_consume
209                 || order ==  memory_order_acquire
210                 || order ==  memory_order_seq_cst
211                 );
212             assert( pSrc );
213
214             fence_before(order);
215             T v = *pSrc;
216             fence_after(order);
217             return v;
218         }
219
220         template <typename T>
221         static inline bool cas32_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
222         {
223             static_assert( sizeof(T) == 4, "Illegal size of operand" );
224             assert( pDest );
225
226             fence_before( mo_success );
227             __asm__ __volatile__(
228                 "cas [%[pDest]], %[expected], %[desired]"
229                 : [desired] "+r" (desired)
230                 : [pDest] "r" (pDest), [expected] "r" (expected)
231                 : "memory"
232                 );
233
234             // desired contains current value
235
236             bool bSuccess = desired == expected;
237             if ( bSuccess )
238                 fence_after( mo_success );
239             else {
240                 fence_after(mo_fail);
241                 expected = desired;
242             }
243
244             return bSuccess;
245         }
246
247         template <typename T>
248         static inline bool cas32_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
249         {
250             return cas32_strong( pDest, expected, desired, mo_success, mo_fail );
251         }
252
253         template <typename T>
254         static inline T exchange32( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
255         {
256             static_assert( sizeof(T) == 4, "Illegal size of operand" );
257             assert( pDest );
258
259             // This primitive could be implemented via "swap" instruction but "swap" is deprecated in UltraSparc
260
261             T cur = load32( pDest, memory_order_relaxed );
262             do {} while ( !cas32_strong( pDest, cur, v, order, memory_order_relaxed ));
263             return cur;
264         }
265
266         //-----------------------------------------------------------------------------
267         // 64bit primitives
268         //-----------------------------------------------------------------------------
269
270         template <typename T>
271         static inline T load64( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
272         {
273             static_assert( sizeof(T) == 8, "Illegal size of operand" );
274             assert( order ==  memory_order_relaxed
275                 || order ==  memory_order_consume
276                 || order ==  memory_order_acquire
277                 || order ==  memory_order_seq_cst
278                 );
279             assert( pSrc );
280
281             fence_before(order);
282             T v = *pSrc;
283             fence_after(order);
284             return v;
285         }
286
287         template <typename T>
288         static inline void store64( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
289         {
290             static_assert( sizeof(T) == 8, "Illegal size of operand" );
291             assert( order ==  memory_order_relaxed
292                 || order ==  memory_order_release
293                 || order == memory_order_seq_cst
294                 );
295             assert( pDest );
296
297             fence_before(order);
298             *pDest = val;
299             fence_after(order);
300
301         }
302
303         template <typename T>
304         static inline bool cas64_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
305         {
306             static_assert( sizeof(T) == 8, "Illegal size of operand" );
307             assert( pDest );
308
309             fence_before( mo_success );
310             __asm__ __volatile__(
311                 "casx [%[pDest]], %[expected], %[desired]"
312                 : [desired] "+r" (desired)
313                 : [pDest] "r" (pDest), [expected] "r" (expected)
314                 : "memory"
315                 );
316
317             // desired contains current value
318
319             bool bSuccess = desired == expected;
320             if ( bSuccess ) {
321                 fence_after( mo_success );
322             }
323             else {
324                 fence_after(mo_fail);
325                 expected = desired;
326             }
327
328             return bSuccess;
329         }
330
331         template <typename T>
332         static inline bool cas64_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
333         {
334             return cas64_strong( pDest, expected, desired, mo_success, mo_fail );
335         }
336
337         template <typename T>
338         static inline T exchange64( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
339         {
340             static_assert( sizeof(T) == 8, "Illegal size of operand" );
341             assert( pDest );
342
343             T cur = load64( pDest, memory_order_relaxed );
344             do {} while ( !cas64_strong( pDest, cur, v, order, memory_order_relaxed ));
345             return cur;
346         }
347
348         //-----------------------------------------------------------------------------
349         // 8bit primitives
350         //-----------------------------------------------------------------------------
351
352         template <typename T>
353         static inline void store8( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
354         {
355             static_assert( sizeof(T) == 1, "Illegal size of operand" );
356             assert( order ==  memory_order_relaxed
357                 || order ==  memory_order_release
358                 || order == memory_order_seq_cst
359                 );
360             assert( pDest );
361
362             fence_before( order );
363             *pDest = src;
364             fence_after( order );
365         }
366
367         template <typename T>
368         static inline T load8( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
369         {
370             static_assert( sizeof(T) == 1, "Illegal size of operand" );
371             assert( order ==  memory_order_relaxed
372                 || order ==  memory_order_consume
373                 || order ==  memory_order_acquire
374                 || order == memory_order_seq_cst
375                 );
376             assert( pSrc );
377
378             fence_before( order );
379             T v = *pSrc;
380             fence_after( order );
381             return v;
382         }
383
384         template <typename T>
385         static inline bool cas8_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
386         {
387             static_assert( sizeof(T) == 1, "Illegal size of operand" );
388             assert( pDest );
389
390             union u32 {
391                 uint32_t    w;
392                 T           c[4];
393             };
394             static_assert( sizeof(u32) == sizeof(uint32_t), "Argument size error" );
395
396             u32 volatile *  pDest32 = (u32 *)( uintptr_t( pDest ) & ~0x03 );
397             size_t const  nCharIdx = (size_t)( uintptr_t( pDest ) & 0x03 );
398             u32 uExpected;
399             u32 uDesired;
400
401             bool bSuccess;
402             for (;;) {
403                 uExpected.w =
404                     uDesired.w = pDest32->w;
405                 uExpected.c[nCharIdx] = expected;
406                 uDesired.c[nCharIdx] = desired;
407
408                 bSuccess = cas32_weak( reinterpret_cast<uint32_t volatile *>(pDest32), uExpected.w, uDesired.w, mo_success, mo_fail );
409                 if ( bSuccess || uExpected.c[nCharIdx] != expected )
410                     break;
411             }
412
413             expected = uExpected.c[nCharIdx];
414             return bSuccess;
415         }
416
417         template <typename T>
418         static inline bool cas8_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
419         {
420             static_assert( sizeof(T) == 1, "Illegal size of operand" );
421             assert( pDest );
422
423             union u32 {
424                 uint32_t    w;
425                 T           c[4];
426             };
427             static_assert( sizeof(u32) == sizeof(uint32_t), "Argument size error" );
428
429             u32 volatile * pDest32 = (u32 *)( uintptr_t( pDest ) & ~0x03 );
430             size_t const  nCharIdx = (size_t)( uintptr_t( pDest ) & 0x03 );
431             u32 uExpected;
432             u32 uDesired;
433
434             uExpected.w =
435                 uDesired.w = pDest32->w;
436             uExpected.c[nCharIdx] = expected;
437             uDesired.c[nCharIdx] = desired;
438
439             bool bSuccess = cas32_weak( reinterpret_cast<uint32_t volatile *>(pDest32), uExpected.w, uDesired.w, mo_success, mo_fail );
440
441             expected = uExpected.c[nCharIdx];
442             return bSuccess;
443         }
444
445         template <typename T>
446         static inline T exchange8( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
447         {
448             static_assert( sizeof(T) == 1, "Illegal size of operand" );
449             assert( pDest );
450
451             T cur = load8( pDest, memory_order_relaxed );
452             do {} while ( !cas8_strong( pDest, cur, v, order, memory_order_relaxed ));
453             return cur;
454         }
455
456         //-----------------------------------------------------------------------------
457         // 16bit primitives
458         //-----------------------------------------------------------------------------
459
460         template <typename T>
461         static inline T load16( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
462         {
463             static_assert( sizeof(T) == 2, "Illegal size of operand" );
464             assert( order ==  memory_order_relaxed
465                 || order ==  memory_order_consume
466                 || order ==  memory_order_acquire
467                 || order ==  memory_order_seq_cst
468                 );
469             assert( pSrc );
470
471             fence_before( order );
472             T v = *pSrc;
473             fence_after( order );
474             return v;
475         }
476
477         template <typename T>
478         static inline void store16( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
479         {
480             static_assert( sizeof(T) == 2, "Illegal size of operand" );
481             assert( order ==  memory_order_relaxed
482                 || order ==  memory_order_release
483                 || order == memory_order_seq_cst
484                 );
485             assert( pDest );
486
487             fence_before(order);
488             *pDest = src;
489             fence_after(order);
490         }
491
492         template <typename T>
493         static inline bool cas16_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
494         {
495             static_assert( sizeof(T) == 2, "Illegal size of operand" );
496             assert( pDest );
497
498             union u32 {
499                 uint32_t    w;
500                 T           c[2];
501             };
502             static_assert( sizeof(u32) == sizeof(uint32_t), "Argument size error" );
503
504             u32 volatile *  pDest32 = (u32 *)( uintptr_t( pDest ) & ~0x03 );
505             size_t const  nIdx = (size_t)( (uintptr_t( pDest ) >> 1) & 0x01 );
506             u32 uExpected;
507             u32 uDesired;
508
509             bool bSuccess;
510             for (;;) {
511                 uExpected.w =
512                     uDesired.w = pDest32->w;
513                 uExpected.c[nIdx] = expected;
514                 uDesired.c[nIdx] = desired;
515
516                 bSuccess = cas32_weak( reinterpret_cast<uint32_t volatile *>(pDest32), uExpected.w, uDesired.w, mo_success, mo_fail );
517                 if ( bSuccess || uExpected.c[nIdx] != expected )
518                     break;
519             }
520
521             expected = uExpected.c[nIdx];
522             return bSuccess;
523         }
524
525         template <typename T>
526         static inline bool cas16_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
527         {
528             static_assert( sizeof(T) == 2, "Illegal size of operand" );
529             assert( pDest );
530
531             union u32 {
532                 uint32_t    w;
533                 T           c[2];
534             };
535             static_assert( sizeof(u32) == sizeof(uint32_t), "Argument size error" );
536
537             u32 volatile * pDest32 = (u32 *)( uintptr_t( pDest ) & ~0x03 );
538             size_t const  nIdx = (size_t)( (uintptr_t( pDest ) >> 1) & 0x01 );
539             u32 uExpected;
540             u32 uDesired;
541
542             uExpected.w =
543                 uDesired.w = pDest32->w;
544             uExpected.c[nIdx] = expected;
545             uDesired.c[nIdx] = desired;
546
547             bool bSuccess = cas32_weak( reinterpret_cast<uint32_t volatile *>(pDest32), uExpected.w, uDesired.w, mo_success, mo_fail );
548
549             expected = uExpected.c[nIdx];
550             return bSuccess;
551         }
552
553         template <typename T>
554         static inline T exchange16( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
555         {
556             static_assert( sizeof(T) == 2, "Illegal size of operand" );
557             assert( pDest );
558
559             T cur = load16( pDest, memory_order_relaxed );
560             do {} while ( !cas16_strong( pDest, cur, v, order, memory_order_relaxed ));
561             return cur;
562         }
563
564         //-----------------------------------------------------------------------------
565         // pointer primitives
566         //-----------------------------------------------------------------------------
567
568         template <typename T>
569         static inline void store_ptr( T * volatile * pDest, T * src, memory_order order ) CDS_NOEXCEPT
570         {
571             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
572             assert( order ==  memory_order_relaxed
573                 || order ==  memory_order_release
574                 || order == memory_order_seq_cst
575                 );
576             assert( pDest );
577
578             fence_before(order);
579             *pDest = src;
580             fence_after(order);
581         }
582
583         template <typename T>
584         static inline T * load_ptr( T * volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
585         {
586             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
587             assert( order ==  memory_order_relaxed
588                 || order ==  memory_order_consume
589                 || order ==  memory_order_acquire
590                 || order ==  memory_order_seq_cst
591                 );
592             assert( pSrc );
593
594             fence_before( order );
595             T * v = *pSrc;
596             fence_after( order );
597             return v;
598         }
599
600         template <typename T>
601         static inline bool cas_ptr_strong( T * volatile * pDest, T *& expected, T * desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
602         {
603             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
604
605             return cas64_strong( (uint64_t volatile *) pDest, *reinterpret_cast<uint64_t *>( &expected ), (uint64_t) desired, mo_success, mo_fail );
606         }
607
608         template <typename T>
609         static inline bool cas_ptr_weak( T * volatile * pDest, T *& expected, T * desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
610         {
611             return cas_ptr_strong( pDest, expected, desired, mo_success, mo_fail );
612         }
613
614         template <typename T>
615         static inline T * exchange_ptr( T * volatile * pDest, T * v, memory_order order ) CDS_NOEXCEPT
616         {
617             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
618             return (T *) exchange64( (uint64_t volatile *) pDest, (uint64_t) v, order );
619         }
620
621     }} // namespace gcc::Sparc
622
623 #ifndef CDS_CXX11_INLINE_NAMESPACE_SUPPORT
624     using namespace gcc::Sparc;
625 #endif
626     }   // namespace platform
627 }}  // namespace cds::cxx11_atomic
628 //@endcond
629
630 #undef CDS_SPARC_MB_ACQ
631 #undef CDS_SPARC_MB_REL
632 #undef CDS_SPARC_MB_SEQ_CST
633 #undef CDS_SPARC_MB_FULL
634 #undef CDS_SPARC_MB_LL_LS
635 #undef CDS_SPARC_MB_LS_SS
636 #undef CDS_SPARC_MB_LL_LS_SS
637
638 #endif // #ifndef CDSLIB_COMPILER_GCC_AMD64_CXX11_ATOMIC_H