Library of Assembled Shared Sources
atomic_gcc_arm.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2011 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#include "atomic_poor_man.h"
44
45namespace lass
46{
47namespace util
48{
49namespace impl
50{
51
52#if LASS_KUSER_HELPER_VERSION >= 2
53#define LASS_HAVE_KUSER_CMPXCHG 1
54typedef int (kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
55#define lass_util_impl_kuser_cmpxchg (*(kuser_cmpxchg_t *)0xffff0fc0)
56#endif
57
58#if LASS_KUSER_HELPER_VERSION >= 5
59#define LASS_HAVE_KUSER_CMPXCHG64 1
60typedef int (kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr);
61#define lass_util_impl_kuser_cmpxchg64 (*(kuser_cmpxchg64_t *)0xffff0f60)
62#endif
63
64#if LASS_HAVE_BIG_ENDIAN
65# error "code below assumes little endian. can easily be adapted for big endian too, but that's not done yet ;-)"
66#endif
67
68
69// --- compareAndSwap ---------------------------------------------------------
70
71#if LASS_HAVE_LDREXB_STREXB
72template <>
73template <typename T>
74inline bool AtomicOperations<1>::compareAndSwap(volatile T& dest, T expected, T desired)
75{
76 uint8_t tmp;
77 bool failure = false;
78 __asm__ __volatile__(
79 "ldrexb %[tmp], [%[dest]]\n\t"
80 "mov %[failure], #1\n\t"
81 "teq %[tmp], %[expected]\n\t"
82 "it eq\n\t"
83 "strexbeq %[failure], %[desired], [%[dest]]\n\t"
84 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
85 : [dest]"r"(&dest), [expected]"r"(expected), [desired]"r"(desired)
86 : "cc", "memory");
87 return !failure;
88}
89#endif
90
91#if LASS_HAVE_LDREXH_STREXH
92template <>
93template <typename T>
94inline bool AtomicOperations<2>::compareAndSwap(volatile T& dest, T expected, T desired)
95{
96 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest) & 0x1) == 0); // requires half word alignment
97
98 uint16_t tmp;
99 bool failure = false;
100 __asm__ __volatile__(
101 "ldrexh %[tmp], [%[dest]]\n\t"
102 "mov %[failure], #1\n\t"
103 "teq %[tmp], %[expected]\n\t"
104 "it eq\n\t"
105 "strexheq %[failure], %[desired], [%[dest]]\n\t"
106 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
107 : [dest]"r"(&dest), [expected]"r"(expected), [desired]"r"(desired)
108 : "cc", "memory");
109 return !failure;
110}
111#endif
112
113#if LASS_HAVE_LDREX_STREX
114template <>
115template <typename T>
116inline bool AtomicOperations<4>::compareAndSwap(volatile T& dest, T expected, T desired)
117{
118 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest) & 0x3) == 0); // requires word alignment
119
120 uint32_t tmp;
121 bool failure = false;
122 __asm__ __volatile__(
123 "ldrex %[tmp], [%[dest]]\n\t"
124 "mov %[failure], #1\n\t"
125 "teq %[tmp], %[expected]\n\t"
126 "it eq\n\t"
127 "strexeq %[failure], %[desired], [%[dest]]\n\t"
128 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
129 : [dest]"r"(&dest), [expected]"r"(expected), [desired]"r"(desired)
130 : "cc", "memory");
131 return !failure;
132}
133#elif LASS_HAVE_KUSER_CMPXCHG
134template <>
135template <typename T>
136inline bool AtomicOperations<4>::compareAndSwap(volatile T& dest, T expected, T desired)
137{
138 return lass_util_impl_kuser_cmpxchg(
139 *reinterpret_cast<int32_t*>(&expected),
140 *reinterpret_cast<int32_t*>(&desired),
141 reinterpret_cast<volatile int32_t*>(&dest)
142 ) == 0;
143}
144#endif
145
146#if LASS_HAVE_LDREXD_STREXD
147template <>
148template <typename T>
149inline bool AtomicOperations<8>::compareAndSwap(volatile T& dest, T expected, T desired)
150{
151 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest) & 0x7) == 0); // requires double word alignment
152
153 uint64_t tmp;
154 bool failure = false;
155 __asm__ __volatile__(
156 "ldrexd %[tmp], %H[tmp], [%[dest]]\n\t"
157 "mov %[failure], #1\n\t"
158 "teq %[tmp], %[expected]\n\t"
159 "itt eq\n\t"
160 "teqeq %H[tmp], %H[expected]\n\t"
161 "strexdeq %[failure], %[desired], %H[desired], [%[dest]]\n\t"
162 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
163 : [dest]"r"(&dest), [expected]"r"(expected), [desired]"r"(desired)
164 : "cc", "memory");
165 return !failure;
166}
167#elif LASS_HAVE_KUSER_CMPXCHG64
168template <>
169template <typename T>
170inline bool AtomicOperations<8>::compareAndSwap(volatile T& dest, T expected, T desired)
171{
172 return lass_util_impl_kuser_cmpxchg64(
173 reinterpret_cast<const int64_t*>(&expected),
174 reinterpret_cast<const int64_t*>(&desired),
175 reinterpret_cast<volatile int64_t*>(&dest)
176 ) == 0;
177}
178#endif
179
180
181
182// --- adjacent compareAndSwap ------------------------------------------------
183
184#if LASS_HAVE_LDREXH_STREXH
185template <>
186template <typename T1, typename T2>
187inline bool AtomicOperations<1>::compareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 desired1, T2 desired2)
188{
189 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest1) & 0x1) == 0); // requires half word alignment
190
191 uint16_t tmp;
192 bool failure = false;
193 __asm__ __volatile__(
194 "orr %[expected1], %[expected1], %[expected2], lsl #8\n\t"
195 "orr %[desired1], %[desired1], %[desired2], lsl #8\n\t"
196 "ldrexh %[tmp], [%[dest]]\n\t"
197 "mov %[failure], #1\n\t"
198 "teq %[tmp], %[expected1]\n\t"
199 "it eq\n\t"
200 "strexheq %[failure], %[desired1], [%[dest]]\n\t"
201 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
202 : [dest]"r"(&dest1), [expected1]"r"(expected1), [expected2]"r"(expected2), [desired1]"r"(desired1), [desired2]"r"(desired2)
203 : "cc", "memory");
204 return !failure;
205}
206#endif
207
208#if LASS_HAVE_LDREX_STREX
209template <>
210template <typename T1, typename T2>
211inline bool AtomicOperations<2>::compareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 desired1, T2 desired2)
212{
213 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest1) & 0x3) == 0); // requires word alignment
214
215 uint32_t tmp;
216 bool failure = false;
217 __asm__ __volatile__(
218 "orr %[expected1], %[expected1], %[expected2], lsl #16\n\t"
219 "orr %[desired1], %[desired1], %[desired2], lsl #16\n\t"
220 "ldrex %[tmp], [%[dest]]\n\t"
221 "mov %[failure], #1\n\t"
222 "teq %[tmp], %[expected1]\n\t"
223 "it eq\n\t"
224 "strexeq %[failure], %[desired1], [%[dest]]\n\t"
225 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
226 : [dest]"r"(&dest1), [expected1]"r"(expected1), [expected2]"r"(expected2), [desired1]"r"(desired1), [desired2]"r"(desired2)
227 : "cc", "memory");
228 return !failure;
229}
230#elif LASS_HAVE_KUSER_CMPXCHG
231template <>
232template <typename T1, typename T2>
233inline bool AtomicOperations<2>::compareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 desired1, T2 desired2)
234{
235 const int32_t expected = static_cast<int32_t>(*reinterpret_cast<int16_t*>(&expected1)) | (static_cast<int32_t>(*reinterpret_cast<int16_t*>(&expected2)) << 16);
236 const int32_t desired = static_cast<int32_t>(*reinterpret_cast<int16_t*>(&desired1)) | (static_cast<int32_t>(*reinterpret_cast<int16_t*>(&desired2)) << 16);
237 return lass_util_impl_kuser_cmpxchg(expected, desired, reinterpret_cast<volatile int32_t*>(&dest1)) == 0;
238}
239#endif
240
241#if LASS_HAVE_LDREXD_STREXD
242template <>
243template <typename T1, typename T2>
244inline bool AtomicOperations<4>::compareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 desired1, T2 desired2)
245{
246 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&dest1) & 0x7) == 0); // requires double word alignment
247
248 uint64_t desired;
249 uint64_t tmp;
250 bool failure = false;
251 __asm__ __volatile__(
252 "mov %[desired], %[desired1]\n\t"
253 "mov %H[desired], %[desired2]\n\t"
254 "ldrexd %[tmp], %H[tmp], [%[dest]]\n\t"
255 "mov %[failure], #1\n\t"
256 "teq %[tmp], %[expected1]\n\t"
257 "itt eq\n\t"
258 "teqeq %H[tmp], %[expected2]\n\t"
259 "strexdeq %[failure], %[desired], %H[desired], [%[dest]]\n\t"
260 : [desired]"=&r"(desired), [tmp]"=&r"(tmp), [failure]"=&r"(failure)
261 : [dest]"r"(&dest1), [expected1]"r"(expected1), [expected2]"r"(expected2), [desired1]"r"(desired1), [desired2]"r"(desired2)
262 : "cc", "memory");
263 return !failure;
264}
265#elif LASS_HAVE_KUSER_CMPXCHG64
266template <>
267template <typename T1, typename T2>
268inline bool AtomicOperations<4>::compareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 desired1, T2 desired2)
269{
270 const int64_t expected = static_cast<int64_t>(*reinterpret_cast<int32_t*>(&expected1)) | (static_cast<int64_t>(*reinterpret_cast<int32_t*>(&expected2)) << 32);
271 const int64_t desired = static_cast<int64_t>(*reinterpret_cast<int32_t*>(&desired1)) | (static_cast<int64_t>(*reinterpret_cast<int32_t*>(&desired2)) << 32);
272 return lass_util_impl_kuser_cmpxchg64(&expected, &desired, reinterpret_cast<volatile int64_t*>(&dest1)) == 0;
273}
274#endif
275
276
277
278// --- increment --------------------------------------------------------------
279
280#if LASS_HAVE_LDREXB_STREXB
281template <>
282template <typename T>
283inline void AtomicOperations<1>::increment(volatile T& value)
284{
285 uint8_t tmp;
286 bool failure = false;
287 do
288 {
289 __asm__ __volatile__(
290 "ldrexb %[tmp], [%[value]]\n\t"
291 "add %[tmp], %[tmp], #1\n\t"
292 "strexb %[failure], %[tmp], [%[value]]\n\t"
293 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
294 : [value]"r"(&value)
295 : "cc", "memory");
296 }
297 while (failure);
298}
299#endif
300
301#if LASS_HAVE_LDREXH_STREXH
302template <>
303template <typename T>
304inline void AtomicOperations<2>::increment(volatile T& value)
305{
306 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x1) == 0); // requires half word alignment
307
308 uint16_t tmp;
309 bool failure = false;
310 do
311 {
312 __asm__ __volatile__(
313 "ldrexh %[tmp], [%[value]]\n\t"
314 "add %[tmp], %[tmp], #1\n\t"
315 "strexh %[failure], %[tmp], [%[value]]\n\t"
316 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
317 : [value]"r"(&value)
318 : "cc", "memory");
319 }
320 while (failure);
321}
322#endif
323
324#if LASS_HAVE_LDREX_STREX
325template <>
326template <typename T>
327inline void AtomicOperations<4>::increment(volatile T& value)
328{
329 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x3) == 0); // requires word alignment
330
331 uint32_t tmp;
332 bool failure = false;
333 do
334 {
335 __asm__ __volatile__(
336 "ldrex %[tmp], [%[value]]\n\t"
337 "add %[tmp], %[tmp], #1\n\t"
338 "strex %[failure], %[tmp], [%[value]]\n\t"
339 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
340 : [value]"r"(&value)
341 : "cc", "memory");
342 }
343 while (failure);
344}
345#endif
346
347#if LASS_HAVE_LDREXD_STREXD
348template <>
349template <typename T>
350inline void AtomicOperations<8>::increment(volatile T& value)
351{
352 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x7) == 0); // requires double word alignment
353
354 uint64_t tmp;
355 bool failure = false;
356 do
357 {
358 __asm__ __volatile__(
359 "ldrexd %[tmp], %H[tmp], [%[value]]\n\t"
360 "adds %[tmp], %[tmp], #1\n\t"
361 "adc %H[tmp], %H[tmp], #0\n\t"
362 "strexd %[failure], %[tmp], %H[tmp], [%[value]]\n\t"
363 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
364 : [value]"r"(&value)
365 : "cc", "memory");
366 }
367 while (failure);
368}
369#endif
370
371
372
373// --- decrement --------------------------------------------------------------
374
375#if LASS_HAVE_LDREXB_STREXB
376template <>
377template <typename T>
378inline void AtomicOperations<1>::decrement(volatile T& value)
379{
380 uint8_t tmp;
381 bool failure = false;
382 do
383 {
384 __asm__ __volatile__(
385 "ldrexb %[tmp], [%[value]]\n\t"
386 "sub %[tmp], %[tmp], #1\n\t"
387 "strexb %[failure], %[tmp], [%[value]]\n\t"
388 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
389 : [value]"r"(&value)
390 : "cc", "memory");
391 }
392 while (failure);
393}
394#endif
395
396#if LASS_HAVE_LDREXH_STREXH
397template <>
398template <typename T>
399inline void AtomicOperations<2>::decrement(volatile T& value)
400{
401 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x1) == 0); // requires half word alignment
402
403 uint16_t tmp;
404 bool failure = false;
405 do
406 {
407 __asm__ __volatile__(
408 "ldrexh %[tmp], [%[value]]\n\t"
409 "sub %[tmp], %[tmp], #1\n\t"
410 "strexh %[failure], %[tmp], [%[value]]\n\t"
411 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
412 : [value]"r"(&value)
413 : "cc", "memory");
414 }
415 while (failure);
416}
417#endif
418
419#if LASS_HAVE_LDREX_STREX
420template <>
421template <typename T>
422inline void AtomicOperations<4>::decrement(volatile T& value)
423{
424 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x3) == 0); // requires word alignment
425
426 uint32_t tmp;
427 bool failure = false;
428 do
429 {
430 __asm__ __volatile__(
431 "ldrex %[tmp], [%[value]]\n\t"
432 "sub %[tmp], %[tmp], #1\n\t"
433 "strex %[failure], %[tmp], [%[value]]\n\t"
434 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
435 : [value]"r"(&value)
436 : "cc", "memory");
437 }
438 while (failure);
439}
440#endif
441
442#if LASS_HAVE_LDREXD_STREXD
443template <>
444template <typename T>
445inline void AtomicOperations<8>::decrement(volatile T& value)
446{
447 LASS_ASSERT((reinterpret_cast<num::TuintPtr>(&value) & 0x7) == 0); // requires double word alignment
448
449 uint64_t tmp;
450 bool failure = false;
451 do
452 {
453 __asm__ __volatile__(
454 "ldrexd %[tmp], %H[tmp], [%[value]]\n\t"
455 "subs %[tmp], %[tmp], #1\n\t"
456 "sbc %H[tmp], %H[tmp], #0\n\t"
457 "strexd %[failure], %[tmp], %H[tmp], [%[value]]\n\t"
458 : [tmp]"=&r"(tmp), [failure]"=&r"(failure)
459 : [value]"r"(&value)
460 : "cc", "memory");
461 }
462 while (failure);
463}
464#endif
465
466}
467}
468}
469
470// EOF
471
general utility, debug facilities, ...
Library for Assembled Shared Sources.
Definition config.h:53