atomic-arm.h revision 93b0cb40c18cae594c931677be2b9214420610b7
1/*
2 * Copyright (C) 2010 The Android Open Source Project
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
17#ifndef ANDROID_CUTILS_ATOMIC_ARM_H
18#define ANDROID_CUTILS_ATOMIC_ARM_H
19
20#include <stdint.h>
21#include <machine/cpu-features.h>
22
23extern inline void android_compiler_barrier(void)
24{
25    __asm__ __volatile__ ("" : : : "memory");
26}
27
28#if ANDROID_SMP == 0
29extern inline void android_memory_barrier(void)
30{
31  android_compiler_barrier();
32}
33#elif defined(__ARM_HAVE_DMB)
34extern inline void android_memory_barrier(void)
35{
36    __asm__ __volatile__ ("dmb" : : : "memory");
37}
38#elif defined(__ARM_HAVE_LDREX_STREX)
39extern inline void android_memory_barrier(void)
40{
41    __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5"
42                          : : "r" (0) : "memory");
43}
44#else
45extern inline void android_memory_barrier(void)
46{
47    typedef void (kuser_memory_barrier)(void);
48    (*(kuser_memory_barrier *)0xffff0fa0)();
49}
50#endif
51
52extern inline int32_t android_atomic_acquire_load(volatile int32_t *ptr)
53{
54    int32_t value = *ptr;
55    android_memory_barrier();
56    return value;
57}
58
59extern inline int32_t android_atomic_release_load(volatile int32_t *ptr)
60{
61    android_memory_barrier();
62    return *ptr;
63}
64
65extern inline void android_atomic_acquire_store(int32_t value,
66                                                volatile int32_t *ptr)
67{
68    *ptr = value;
69    android_memory_barrier();
70}
71
72extern inline void android_atomic_release_store(int32_t value,
73                                                volatile int32_t *ptr)
74{
75    android_memory_barrier();
76    *ptr = value;
77}
78
79#if defined(__thumb__)
80extern int android_atomic_cas(int32_t old_value, int32_t new_value,
81                              volatile int32_t *ptr);
82#elif defined(__ARM_HAVE_LDREX_STREX)
83extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
84                                     volatile int32_t *ptr)
85{
86    int32_t prev, status;
87    do {
88        __asm__ __volatile__ ("ldrex %0, [%3]\n"
89                              "mov %1, #0\n"
90                              "teq %0, %4\n"
91                              "strexeq %1, %5, [%3]"
92                              : "=&r" (prev), "=&r" (status), "+m"(*ptr)
93                              : "r" (ptr), "Ir" (old_value), "r" (new_value)
94                              : "cc");
95    } while (__builtin_expect(status != 0, 0));
96    return prev != old_value;
97}
98#else
99extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
100                                     volatile int32_t *ptr)
101{
102    typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *);
103    int32_t prev, status;
104    prev = *ptr;
105    do {
106        status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr);
107        if (__builtin_expect(status == 0, 1))
108            return 0;
109        prev = *ptr;
110    } while (prev == old_value);
111    return 1;
112}
113#endif
114
115extern inline int android_atomic_acquire_cas(int32_t old_value,
116                                             int32_t new_value,
117                                             volatile int32_t *ptr)
118{
119    int status = android_atomic_cas(old_value, new_value, ptr);
120    android_memory_barrier();
121    return status;
122}
123
124extern inline int android_atomic_release_cas(int32_t old_value,
125                                             int32_t new_value,
126                                             volatile int32_t *ptr)
127{
128    android_memory_barrier();
129    return android_atomic_cas(old_value, new_value, ptr);
130}
131
132
133#if defined(__thumb__)
134extern int32_t android_atomic_swap(int32_t new_value,
135                                   volatile int32_t *ptr);
136#elif defined(__ARM_HAVE_LDREX_STREX)
137extern inline int32_t android_atomic_swap(int32_t new_value,
138                                          volatile int32_t *ptr)
139{
140    int32_t prev, status;
141    do {
142        __asm__ __volatile__ ("ldrex %0, [%3]\n"
143                              "strex %1, %4, [%3]"
144                              : "=&r" (prev), "=&r" (status), "+m" (*ptr)
145                              : "r" (ptr), "r" (new_value)
146                              : "cc");
147    } while (__builtin_expect(status != 0, 0));
148    android_memory_barrier();
149    return prev;
150}
151#else
152extern inline int32_t android_atomic_swap(int32_t new_value,
153                                          volatile int32_t *ptr)
154{
155    int32_t prev;
156    __asm__ __volatile__ ("swp %0, %2, [%3]"
157                          : "=&r" (prev), "+m" (*ptr)
158                          : "r" (new_value), "r" (ptr)
159                          : "cc");
160    android_memory_barrier();
161    return prev;
162}
163#endif
164
165#if defined(__thumb__)
166extern int32_t android_atomic_add(int32_t increment,
167                                  volatile int32_t *ptr);
168#elif defined(__ARM_HAVE_LDREX_STREX)
169extern inline int32_t android_atomic_add(int32_t increment,
170                                         volatile int32_t *ptr)
171{
172    int32_t prev, tmp, status;
173    android_memory_barrier();
174    do {
175        __asm__ __volatile__ ("ldrex %0, [%4]\n"
176                              "add %1, %0, %5\n"
177                              "strex %2, %1, [%4]"
178                              : "=&r" (prev), "=&r" (tmp),
179                                "=&r" (status), "+m" (*ptr)
180                              : "r" (ptr), "Ir" (increment)
181                              : "cc");
182    } while (__builtin_expect(status != 0, 0));
183    return prev;
184}
185#else
186extern inline int32_t android_atomic_add(int32_t increment,
187                                         volatile int32_t *ptr)
188{
189    int32_t prev, status;
190    android_memory_barrier();
191    do {
192        prev = *ptr;
193        status = android_atomic_cas(prev, prev + increment, ptr);
194    } while (__builtin_expect(status != 0, 0));
195    return prev;
196}
197#endif
198
199extern inline int32_t android_atomic_inc(volatile int32_t *addr) {
200    return android_atomic_add(1, addr);
201}
202
203extern inline int32_t android_atomic_dec(volatile int32_t *addr) {
204    return android_atomic_add(-1, addr);
205}
206
207#if defined(__thumb__)
208extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr);
209#elif defined(__ARM_HAVE_LDREX_STREX)
210extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
211{
212    int32_t prev, tmp, status;
213    android_memory_barrier();
214    do {
215        __asm__ __volatile__ ("ldrex %0, [%4]\n"
216                              "and %1, %0, %5\n"
217                              "strex %2, %1, [%4]"
218                              : "=&r" (prev), "=&r" (tmp),
219                                "=&r" (status), "+m" (*ptr)
220                              : "r" (ptr), "Ir" (value)
221                              : "cc");
222    } while (__builtin_expect(status != 0, 0));
223    return prev;
224}
225#else
226extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
227{
228    int32_t prev, status;
229    android_memory_barrier();
230    do {
231        prev = *ptr;
232        status = android_atomic_cas(prev, prev & value, ptr);
233    } while (__builtin_expect(status != 0, 0));
234    return prev;
235}
236#endif
237
238#if defined(__thumb__)
239extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr);
240#elif defined(__ARM_HAVE_LDREX_STREX)
241extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
242{
243    int32_t prev, tmp, status;
244    android_memory_barrier();
245    do {
246        __asm__ __volatile__ ("ldrex %0, [%4]\n"
247                              "orr %1, %0, %5\n"
248                              "strex %2, %1, [%4]"
249                              : "=&r" (prev), "=&r" (tmp),
250                                "=&r" (status), "+m" (*ptr)
251                              : "r" (ptr), "Ir" (value)
252                              : "cc");
253    } while (__builtin_expect(status != 0, 0));
254    return prev;
255}
256#else
257extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
258{
259    int32_t prev, status;
260    android_memory_barrier();
261    do {
262        prev = *ptr;
263        status = android_atomic_cas(prev, prev | value, ptr);
264    } while (__builtin_expect(status != 0, 0));
265    return prev;
266}
267#endif
268
269#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */
270