Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (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 // Author: Sasha Levitskiy
     32 // based on atomicops-internals by Sanjay Ghemawat
     33 //
     34 // This file is an internal atomic implementation, use base/atomicops.h instead.
     35 //
     36 // This code implements ARM atomics for architectures V6 and  newer.
     37 
     38 #ifndef BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
     39 #define BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
     40 
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include "base/abort.h"
     44 #include "base/basictypes.h"  // For COMPILE_ASSERT
     45 
     46 // The LDREXD and STREXD instructions in ARM all v7 variants or above.  In v6,
     47 // only some variants support it.  For simplicity, we only use exclusive
     48 // 64-bit load/store in V7 or above.
     49 #if defined(ARMV7)
     50 # define BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
     51 #endif
     52 
     53 typedef int32_t Atomic32;
     54 
     55 namespace base {
     56 namespace subtle {
     57 
     58 typedef int64_t Atomic64;
     59 
     60 // 32-bit low-level ops
     61 
     62 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
     63                                          Atomic32 old_value,
     64                                          Atomic32 new_value) {
     65   Atomic32 oldval, res;
     66   do {
     67     __asm__ __volatile__(
     68     "ldrex   %1, [%3]\n"
     69     "mov     %0, #0\n"
     70     "teq     %1, %4\n"
     71     // The following IT (if-then) instruction is needed for the subsequent
     72     // conditional instruction STREXEQ when compiling in THUMB mode.
     73     // In ARM mode, the compiler/assembler will not generate any code for it.
     74     "it      eq\n"
     75     "strexeq %0, %5, [%3]\n"
     76         : "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
     77         : "r" (ptr), "Ir" (old_value), "r" (new_value)
     78         : "cc");
     79   } while (res);
     80   return oldval;
     81 }
     82 
     83 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
     84                                          Atomic32 new_value) {
     85   Atomic32 tmp, old;
     86   __asm__ __volatile__(
     87       "1:\n"
     88       "ldrex  %1, [%2]\n"
     89       "strex  %0, %3, [%2]\n"
     90       "teq    %0, #0\n"
     91       "bne    1b"
     92       : "=&r" (tmp), "=&r" (old)
     93       : "r" (ptr), "r" (new_value)
     94       : "cc", "memory");
     95   return old;
     96 }
     97 
     98 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
     99                                           Atomic32 increment) {
    100   Atomic32 tmp, res;
    101   __asm__ __volatile__(
    102       "1:\n"
    103       "ldrex  %1, [%2]\n"
    104       "add    %1, %1, %3\n"
    105       "strex  %0, %1, [%2]\n"
    106       "teq    %0, #0\n"
    107       "bne    1b"
    108       : "=&r" (tmp), "=&r"(res)
    109       : "r" (ptr), "r"(increment)
    110       : "cc", "memory");
    111   return res;
    112 }
    113 
    114 inline void MemoryBarrier() {
    115   __asm__ __volatile__("dmb" : : : "memory");
    116 }
    117 
    118 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
    119                                         Atomic32 increment) {
    120   Atomic32 tmp, res;
    121   __asm__ __volatile__(
    122       "1:\n"
    123       "ldrex  %1, [%2]\n"
    124       "add    %1, %1, %3\n"
    125       "dmb\n"
    126       "strex  %0, %1, [%2]\n"
    127       "teq    %0, #0\n"
    128       "bne    1b"
    129       : "=&r" (tmp), "=&r"(res)
    130       : "r" (ptr), "r"(increment)
    131       : "cc", "memory");
    132   return res;
    133 }
    134 
    135 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
    136                                        Atomic32 old_value,
    137                                        Atomic32 new_value) {
    138   Atomic32 value = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
    139   MemoryBarrier();
    140   return value;
    141 }
    142 
    143 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
    144                                        Atomic32 old_value,
    145                                        Atomic32 new_value) {
    146   MemoryBarrier();
    147   return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
    148 }
    149 
    150 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
    151   *ptr = value;
    152 }
    153 
    154 inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
    155   *ptr = value;
    156   MemoryBarrier();
    157 }
    158 
    159 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
    160   MemoryBarrier();
    161   *ptr = value;
    162 }
    163 
    164 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
    165   return *ptr;
    166 }
    167 
    168 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
    169   Atomic32 value = *ptr;
    170   MemoryBarrier();
    171   return value;
    172 }
    173 
    174 inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
    175   MemoryBarrier();
    176   return *ptr;
    177 }
    178 
    179 // 64-bit versions are only available if LDREXD and STREXD instructions
    180 // are available.
    181 #ifdef BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
    182 
    183 #define BASE_HAS_ATOMIC64 1
    184 
    185 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
    186                                          Atomic64 old_value,
    187                                          Atomic64 new_value) {
    188   Atomic64 oldval, res;
    189   do {
    190     __asm__ __volatile__(
    191     "ldrexd   %1, [%3]\n"
    192     "mov      %0, #0\n"
    193     "teq      %Q1, %Q4\n"
    194     // The following IT (if-then) instructions are needed for the subsequent
    195     // conditional instructions when compiling in THUMB mode.
    196     // In ARM mode, the compiler/assembler will not generate any code for it.
    197     "it       eq\n"
    198     "teqeq    %R1, %R4\n"
    199     "it       eq\n"
    200     "strexdeq %0, %5, [%3]\n"
    201         : "=&r" (res), "=&r" (oldval), "+Q" (*ptr)
    202         : "r" (ptr), "Ir" (old_value), "r" (new_value)
    203         : "cc");
    204   } while (res);
    205   return oldval;
    206 }
    207 
    208 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
    209                                          Atomic64 new_value) {
    210   int store_failed;
    211   Atomic64 old;
    212   __asm__ __volatile__(
    213       "1:\n"
    214       "ldrexd  %1, [%2]\n"
    215       "strexd  %0, %3, [%2]\n"
    216       "teq     %0, #0\n"
    217       "bne     1b"
    218       : "=&r" (store_failed), "=&r" (old)
    219       : "r" (ptr), "r" (new_value)
    220       : "cc", "memory");
    221   return old;
    222 }
    223 
    224 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
    225                                           Atomic64 increment) {
    226   int store_failed;
    227   Atomic64 res;
    228   __asm__ __volatile__(
    229       "1:\n"
    230       "ldrexd  %1, [%2]\n"
    231       "adds    %Q1, %Q1, %Q3\n"
    232       "adc     %R1, %R1, %R3\n"
    233       "strexd  %0, %1, [%2]\n"
    234       "teq     %0, #0\n"
    235       "bne     1b"
    236       : "=&r" (store_failed), "=&r"(res)
    237       : "r" (ptr), "r"(increment)
    238       : "cc", "memory");
    239   return res;
    240 }
    241 
    242 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
    243                                         Atomic64 increment) {
    244   int store_failed;
    245   Atomic64 res;
    246   __asm__ __volatile__(
    247       "1:\n"
    248       "ldrexd  %1, [%2]\n"
    249       "adds    %Q1, %Q1, %Q3\n"
    250       "adc     %R1, %R1, %R3\n"
    251       "dmb\n"
    252       "strexd  %0, %1, [%2]\n"
    253       "teq     %0, #0\n"
    254       "bne     1b"
    255       : "=&r" (store_failed), "=&r"(res)
    256       : "r" (ptr), "r"(increment)
    257       : "cc", "memory");
    258   return res;
    259 }
    260 
    261 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
    262   int store_failed;
    263   Atomic64 dummy;
    264   __asm__ __volatile__(
    265       "1:\n"
    266       // Dummy load to lock cache line.
    267       "ldrexd  %1, [%3]\n"
    268       "strexd  %0, %2, [%3]\n"
    269       "teq     %0, #0\n"
    270       "bne     1b"
    271       : "=&r" (store_failed), "=&r"(dummy)
    272       : "r"(value), "r" (ptr)
    273       : "cc", "memory");
    274 }
    275 
    276 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
    277   Atomic64 res;
    278   __asm__ __volatile__(
    279   "ldrexd   %0, [%1]\n"
    280   "clrex\n"
    281       : "=r" (res)
    282       : "r"(ptr), "Q"(*ptr));
    283   return res;
    284 }
    285 
    286 #else // BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
    287 
    288 inline void NotImplementedFatalError(const char *function_name) {
    289   fprintf(stderr, "64-bit %s() not implemented on this platform\n",
    290           function_name);
    291   tcmalloc::Abort();
    292 }
    293 
    294 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
    295                                          Atomic64 old_value,
    296                                          Atomic64 new_value) {
    297   NotImplementedFatalError("NoBarrier_CompareAndSwap");
    298   return 0;
    299 }
    300 
    301 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
    302                                          Atomic64 new_value) {
    303   NotImplementedFatalError("NoBarrier_AtomicExchange");
    304   return 0;
    305 }
    306 
    307 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
    308                                           Atomic64 increment) {
    309   NotImplementedFatalError("NoBarrier_AtomicIncrement");
    310   return 0;
    311 }
    312 
    313 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
    314                                         Atomic64 increment) {
    315   NotImplementedFatalError("Barrier_AtomicIncrement");
    316   return 0;
    317 }
    318 
    319 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
    320   NotImplementedFatalError("NoBarrier_Store");
    321 }
    322 
    323 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
    324   NotImplementedFatalError("NoBarrier_Load");
    325   return 0;
    326 }
    327 
    328 #endif // BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
    329 
    330 inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
    331   NoBarrier_Store(ptr, value);
    332   MemoryBarrier();
    333 }
    334 
    335 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
    336   MemoryBarrier();
    337   NoBarrier_Store(ptr, value);
    338 }
    339 
    340 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
    341   Atomic64 value = NoBarrier_Load(ptr);
    342   MemoryBarrier();
    343   return value;
    344 }
    345 
    346 inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
    347   MemoryBarrier();
    348   return NoBarrier_Load(ptr);
    349 }
    350 
    351 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
    352                                        Atomic64 old_value,
    353                                        Atomic64 new_value) {
    354   Atomic64 value = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
    355   MemoryBarrier();
    356   return value;
    357 }
    358 
    359 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
    360                                        Atomic64 old_value,
    361                                        Atomic64 new_value) {
    362   MemoryBarrier();
    363   return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
    364 }
    365 
    366 }  // namespace subtle ends
    367 }  // namespace base ends
    368 
    369 #endif  // BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
    370