Chris@102
|
1 /*
|
Chris@102
|
2 * Distributed under the Boost Software License, Version 1.0.
|
Chris@102
|
3 * (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@102
|
4 * http://www.boost.org/LICENSE_1_0.txt)
|
Chris@102
|
5 *
|
Chris@102
|
6 * Copyright (c) 2009 Helge Bahmann
|
Chris@102
|
7 * Copyright (c) 2013 Tim Blechmann
|
Chris@102
|
8 * Copyright (c) 2014 Andrey Semashev
|
Chris@102
|
9 */
|
Chris@102
|
10 /*!
|
Chris@102
|
11 * \file atomic/detail/ops_gcc_arm.hpp
|
Chris@102
|
12 *
|
Chris@102
|
13 * This header contains implementation of the \c operations template.
|
Chris@102
|
14 */
|
Chris@102
|
15
|
Chris@102
|
16 #ifndef BOOST_ATOMIC_DETAIL_OPS_GCC_ARM_HPP_INCLUDED_
|
Chris@102
|
17 #define BOOST_ATOMIC_DETAIL_OPS_GCC_ARM_HPP_INCLUDED_
|
Chris@102
|
18
|
Chris@102
|
19 #include <boost/cstdint.hpp>
|
Chris@102
|
20 #include <boost/memory_order.hpp>
|
Chris@102
|
21 #include <boost/atomic/detail/config.hpp>
|
Chris@102
|
22 #include <boost/atomic/detail/storage_type.hpp>
|
Chris@102
|
23 #include <boost/atomic/detail/operations_fwd.hpp>
|
Chris@102
|
24 #include <boost/atomic/detail/ops_extending_cas_based.hpp>
|
Chris@102
|
25 #include <boost/atomic/capabilities.hpp>
|
Chris@102
|
26
|
Chris@102
|
27 #ifdef BOOST_HAS_PRAGMA_ONCE
|
Chris@102
|
28 #pragma once
|
Chris@102
|
29 #endif
|
Chris@102
|
30
|
Chris@102
|
31 namespace boost {
|
Chris@102
|
32 namespace atomics {
|
Chris@102
|
33 namespace detail {
|
Chris@102
|
34
|
Chris@102
|
35 // From the ARM Architecture Reference Manual for architecture v6:
|
Chris@102
|
36 //
|
Chris@102
|
37 // LDREX{<cond>} <Rd>, [<Rn>]
|
Chris@102
|
38 // <Rd> Specifies the destination register for the memory word addressed by <Rd>
|
Chris@102
|
39 // <Rn> Specifies the register containing the address.
|
Chris@102
|
40 //
|
Chris@102
|
41 // STREX{<cond>} <Rd>, <Rm>, [<Rn>]
|
Chris@102
|
42 // <Rd> Specifies the destination register for the returned status value.
|
Chris@102
|
43 // 0 if the operation updates memory
|
Chris@102
|
44 // 1 if the operation fails to update memory
|
Chris@102
|
45 // <Rm> Specifies the register containing the word to be stored to memory.
|
Chris@102
|
46 // <Rn> Specifies the register containing the address.
|
Chris@102
|
47 // Rd must not be the same register as Rm or Rn.
|
Chris@102
|
48 //
|
Chris@102
|
49 // ARM v7 is like ARM v6 plus:
|
Chris@102
|
50 // There are half-word and byte versions of the LDREX and STREX instructions,
|
Chris@102
|
51 // LDREXH, LDREXB, STREXH and STREXB.
|
Chris@102
|
52 // There are also double-word versions, LDREXD and STREXD.
|
Chris@102
|
53 // (Actually it looks like these are available from version 6k onwards.)
|
Chris@102
|
54 // FIXME these are not yet used; should be mostly a matter of copy-and-paste.
|
Chris@102
|
55 // I think you can supply an immediate offset to the address.
|
Chris@102
|
56 //
|
Chris@102
|
57 // A memory barrier is effected using a "co-processor 15" instruction,
|
Chris@102
|
58 // though a separate assembler mnemonic is available for it in v7.
|
Chris@102
|
59 //
|
Chris@102
|
60 // "Thumb 1" is a subset of the ARM instruction set that uses a 16-bit encoding. It
|
Chris@102
|
61 // doesn't include all instructions and in particular it doesn't include the co-processor
|
Chris@102
|
62 // instruction used for the memory barrier or the load-locked/store-conditional
|
Chris@102
|
63 // instructions. So, if we're compiling in "Thumb 1" mode, we need to wrap all of our
|
Chris@102
|
64 // asm blocks with code to temporarily change to ARM mode.
|
Chris@102
|
65 //
|
Chris@102
|
66 // You can only change between ARM and Thumb modes when branching using the bx instruction.
|
Chris@102
|
67 // bx takes an address specified in a register. The least significant bit of the address
|
Chris@102
|
68 // indicates the mode, so 1 is added to indicate that the destination code is Thumb.
|
Chris@102
|
69 // A temporary register is needed for the address and is passed as an argument to these
|
Chris@102
|
70 // macros. It must be one of the "low" registers accessible to Thumb code, specified
|
Chris@102
|
71 // using the "l" attribute in the asm statement.
|
Chris@102
|
72 //
|
Chris@102
|
73 // Architecture v7 introduces "Thumb 2", which does include (almost?) all of the ARM
|
Chris@102
|
74 // instruction set. (Actually, there was an extension of v6 called v6T2 which supported
|
Chris@102
|
75 // "Thumb 2" mode, but its architecture manual is no longer available, referring to v7.)
|
Chris@102
|
76 // So in v7 we don't need to change to ARM mode; we can write "universal
|
Chris@102
|
77 // assembler" which will assemble to Thumb 2 or ARM code as appropriate. The only thing
|
Chris@102
|
78 // we need to do to make this "universal" assembler mode work is to insert "IT" instructions
|
Chris@102
|
79 // to annotate the conditional instructions. These are ignored in other modes (e.g. v6),
|
Chris@102
|
80 // so they can always be present.
|
Chris@102
|
81
|
Chris@102
|
82 // A note about memory_order_consume. Technically, this architecture allows to avoid
|
Chris@102
|
83 // unnecessary memory barrier after consume load since it supports data dependency ordering.
|
Chris@102
|
84 // However, some compiler optimizations may break a seemingly valid code relying on data
|
Chris@102
|
85 // dependency tracking by injecting bogus branches to aid out of order execution.
|
Chris@102
|
86 // This may happen not only in Boost.Atomic code but also in user's code, which we have no
|
Chris@102
|
87 // control of. See this thread: http://lists.boost.org/Archives/boost/2014/06/213890.php.
|
Chris@102
|
88 // For this reason we promote memory_order_consume to memory_order_acquire.
|
Chris@102
|
89
|
Chris@102
|
90 #if defined(__thumb__) && !defined(__thumb2__)
|
Chris@102
|
91 #define BOOST_ATOMIC_DETAIL_ARM_ASM_START(TMPREG) "adr " #TMPREG ", 8f\n" "bx " #TMPREG "\n" ".arm\n" ".align 4\n" "8:\n"
|
Chris@102
|
92 #define BOOST_ATOMIC_DETAIL_ARM_ASM_END(TMPREG) "adr " #TMPREG ", 9f + 1\n" "bx " #TMPREG "\n" ".thumb\n" ".align 2\n" "9:\n"
|
Chris@102
|
93 #define BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(var) "=&l" (var)
|
Chris@102
|
94 #else
|
Chris@102
|
95 // The tmpreg may be wasted in this case, which is non-optimal.
|
Chris@102
|
96 #define BOOST_ATOMIC_DETAIL_ARM_ASM_START(TMPREG)
|
Chris@102
|
97 #define BOOST_ATOMIC_DETAIL_ARM_ASM_END(TMPREG)
|
Chris@102
|
98 #define BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(var) "=&r" (var)
|
Chris@102
|
99 #endif
|
Chris@102
|
100
|
Chris@102
|
101 struct gcc_arm_operations_base
|
Chris@102
|
102 {
|
Chris@102
|
103 static BOOST_FORCEINLINE void fence_before(memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
104 {
|
Chris@102
|
105 if ((order & memory_order_release) != 0)
|
Chris@102
|
106 hardware_full_fence();
|
Chris@102
|
107 }
|
Chris@102
|
108
|
Chris@102
|
109 static BOOST_FORCEINLINE void fence_after(memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
110 {
|
Chris@102
|
111 if ((order & (memory_order_consume | memory_order_acquire)) != 0)
|
Chris@102
|
112 hardware_full_fence();
|
Chris@102
|
113 }
|
Chris@102
|
114
|
Chris@102
|
115 static BOOST_FORCEINLINE void fence_after_store(memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
116 {
|
Chris@102
|
117 if (order == memory_order_seq_cst)
|
Chris@102
|
118 hardware_full_fence();
|
Chris@102
|
119 }
|
Chris@102
|
120
|
Chris@102
|
121 static BOOST_FORCEINLINE void hardware_full_fence() BOOST_NOEXCEPT
|
Chris@102
|
122 {
|
Chris@102
|
123 #if defined(BOOST_ATOMIC_DETAIL_ARM_HAS_DMB)
|
Chris@102
|
124 // Older binutils (supposedly, older than 2.21.1) didn't support symbolic or numeric arguments of the "dmb" instruction such as "ish" or "#11".
|
Chris@102
|
125 // As a workaround we have to inject encoded bytes of the instruction. There are two encodings for the instruction: ARM and Thumb. See ARM Architecture Reference Manual, A8.8.43.
|
Chris@102
|
126 // Since we cannot detect binutils version at compile time, we'll have to always use this hack.
|
Chris@102
|
127 __asm__ __volatile__
|
Chris@102
|
128 (
|
Chris@102
|
129 #if defined(__thumb2__)
|
Chris@102
|
130 ".short 0xF3BF, 0x8F5B\n" // dmb ish
|
Chris@102
|
131 #else
|
Chris@102
|
132 ".word 0xF57FF05B\n" // dmb ish
|
Chris@102
|
133 #endif
|
Chris@102
|
134 :
|
Chris@102
|
135 :
|
Chris@102
|
136 : "memory"
|
Chris@102
|
137 );
|
Chris@102
|
138 #else
|
Chris@102
|
139 int tmp;
|
Chris@102
|
140 __asm__ __volatile__
|
Chris@102
|
141 (
|
Chris@102
|
142 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
143 "mcr\tp15, 0, r0, c7, c10, 5\n"
|
Chris@102
|
144 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
145 : "=&l" (tmp)
|
Chris@102
|
146 :
|
Chris@102
|
147 : "memory"
|
Chris@102
|
148 );
|
Chris@102
|
149 #endif
|
Chris@102
|
150 }
|
Chris@102
|
151 };
|
Chris@102
|
152
|
Chris@102
|
153
|
Chris@102
|
154 template< bool Signed >
|
Chris@102
|
155 struct operations< 4u, Signed > :
|
Chris@102
|
156 public gcc_arm_operations_base
|
Chris@102
|
157 {
|
Chris@102
|
158 typedef typename make_storage_type< 4u, Signed >::type storage_type;
|
Chris@102
|
159
|
Chris@102
|
160 static BOOST_FORCEINLINE void store(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
161 {
|
Chris@102
|
162 fence_before(order);
|
Chris@102
|
163 storage = v;
|
Chris@102
|
164 fence_after_store(order);
|
Chris@102
|
165 }
|
Chris@102
|
166
|
Chris@102
|
167 static BOOST_FORCEINLINE storage_type load(storage_type const volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
168 {
|
Chris@102
|
169 storage_type v = storage;
|
Chris@102
|
170 fence_after(order);
|
Chris@102
|
171 return v;
|
Chris@102
|
172 }
|
Chris@102
|
173
|
Chris@102
|
174 static BOOST_FORCEINLINE storage_type exchange(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
175 {
|
Chris@102
|
176 storage_type original;
|
Chris@102
|
177 fence_before(order);
|
Chris@102
|
178 uint32_t tmp;
|
Chris@102
|
179 __asm__ __volatile__
|
Chris@102
|
180 (
|
Chris@102
|
181 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
182 "1:\n"
|
Chris@102
|
183 "ldrex %[original], %[storage]\n" // load the original value
|
Chris@102
|
184 "strex %[tmp], %[value], %[storage]\n" // store the replacement, tmp = store failed
|
Chris@102
|
185 "teq %[tmp], #0\n" // check if store succeeded
|
Chris@102
|
186 "bne 1b\n"
|
Chris@102
|
187 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
188 : [tmp] "=&l" (tmp), [original] "=&r" (original), [storage] "+Q" (storage)
|
Chris@102
|
189 : [value] "r" (v)
|
Chris@102
|
190 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
191 );
|
Chris@102
|
192 fence_after(order);
|
Chris@102
|
193 return original;
|
Chris@102
|
194 }
|
Chris@102
|
195
|
Chris@102
|
196 static BOOST_FORCEINLINE bool compare_exchange_weak(
|
Chris@102
|
197 storage_type volatile& storage, storage_type& expected, storage_type desired, memory_order success_order, memory_order failure_order) BOOST_NOEXCEPT
|
Chris@102
|
198 {
|
Chris@102
|
199 fence_before(success_order);
|
Chris@102
|
200 uint32_t success;
|
Chris@102
|
201 uint32_t tmp;
|
Chris@102
|
202 storage_type original;
|
Chris@102
|
203 __asm__ __volatile__
|
Chris@102
|
204 (
|
Chris@102
|
205 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
206 "mov %[success], #0\n" // success = 0
|
Chris@102
|
207 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
208 "cmp %[original], %[expected]\n" // flags = original==expected
|
Chris@102
|
209 "itt eq\n" // [hint that the following 2 instructions are conditional on flags.equal]
|
Chris@102
|
210 "strexeq %[success], %[desired], %[storage]\n" // if (flags.equal) *(&storage) = desired, success = store failed
|
Chris@102
|
211 "eoreq %[success], %[success], #1\n" // if (flags.equal) success ^= 1 (i.e. make it 1 if store succeeded)
|
Chris@102
|
212 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
213 : [original] "=&r" (original), // %0
|
Chris@102
|
214 [success] "=&r" (success), // %1
|
Chris@102
|
215 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
216 [storage] "+Q" (storage) // %3
|
Chris@102
|
217 : [expected] "r" (expected), // %4
|
Chris@102
|
218 [desired] "r" (desired) // %5
|
Chris@102
|
219 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
220 );
|
Chris@102
|
221 if (success)
|
Chris@102
|
222 fence_after(success_order);
|
Chris@102
|
223 else
|
Chris@102
|
224 fence_after(failure_order);
|
Chris@102
|
225 expected = original;
|
Chris@102
|
226 return !!success;
|
Chris@102
|
227 }
|
Chris@102
|
228
|
Chris@102
|
229 static BOOST_FORCEINLINE bool compare_exchange_strong(
|
Chris@102
|
230 storage_type volatile& storage, storage_type& expected, storage_type desired, memory_order success_order, memory_order failure_order) BOOST_NOEXCEPT
|
Chris@102
|
231 {
|
Chris@102
|
232 fence_before(success_order);
|
Chris@102
|
233 uint32_t success;
|
Chris@102
|
234 uint32_t tmp;
|
Chris@102
|
235 storage_type original;
|
Chris@102
|
236 __asm__ __volatile__
|
Chris@102
|
237 (
|
Chris@102
|
238 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
239 "mov %[success], #0\n" // success = 0
|
Chris@102
|
240 "1:\n"
|
Chris@102
|
241 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
242 "cmp %[original], %[expected]\n" // flags = original==expected
|
Chris@102
|
243 "bne 2f\n" // if (!flags.equal) goto end
|
Chris@102
|
244 "strex %[success], %[desired], %[storage]\n" // *(&storage) = desired, success = store failed
|
Chris@102
|
245 "eors %[success], %[success], #1\n" // success ^= 1 (i.e. make it 1 if store succeeded); flags.equal = success == 0
|
Chris@102
|
246 "beq 1b\n" // if (flags.equal) goto retry
|
Chris@102
|
247 "2:\n"
|
Chris@102
|
248 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
249 : [original] "=&r" (original), // %0
|
Chris@102
|
250 [success] "=&r" (success), // %1
|
Chris@102
|
251 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
252 [storage] "+Q" (storage) // %3
|
Chris@102
|
253 : [expected] "r" (expected), // %4
|
Chris@102
|
254 [desired] "r" (desired) // %5
|
Chris@102
|
255 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
256 );
|
Chris@102
|
257 if (success)
|
Chris@102
|
258 fence_after(success_order);
|
Chris@102
|
259 else
|
Chris@102
|
260 fence_after(failure_order);
|
Chris@102
|
261 expected = original;
|
Chris@102
|
262 return !!success;
|
Chris@102
|
263 }
|
Chris@102
|
264
|
Chris@102
|
265 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
266 {
|
Chris@102
|
267 fence_before(order);
|
Chris@102
|
268 uint32_t tmp;
|
Chris@102
|
269 storage_type original, result;
|
Chris@102
|
270 __asm__ __volatile__
|
Chris@102
|
271 (
|
Chris@102
|
272 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
273 "1:\n"
|
Chris@102
|
274 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
275 "add %[result], %[original], %[value]\n" // result = original + value
|
Chris@102
|
276 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
277 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
278 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
279 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
280 : [original] "=&r" (original), // %0
|
Chris@102
|
281 [result] "=&r" (result), // %1
|
Chris@102
|
282 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
283 [storage] "+Q" (storage) // %3
|
Chris@102
|
284 : [value] "r" (v) // %4
|
Chris@102
|
285 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
286 );
|
Chris@102
|
287 fence_after(order);
|
Chris@102
|
288 return original;
|
Chris@102
|
289 }
|
Chris@102
|
290
|
Chris@102
|
291 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
292 {
|
Chris@102
|
293 fence_before(order);
|
Chris@102
|
294 uint32_t tmp;
|
Chris@102
|
295 storage_type original, result;
|
Chris@102
|
296 __asm__ __volatile__
|
Chris@102
|
297 (
|
Chris@102
|
298 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
299 "1:\n"
|
Chris@102
|
300 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
301 "sub %[result], %[original], %[value]\n" // result = original - value
|
Chris@102
|
302 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
303 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
304 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
305 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
306 : [original] "=&r" (original), // %0
|
Chris@102
|
307 [result] "=&r" (result), // %1
|
Chris@102
|
308 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
309 [storage] "+Q" (storage) // %3
|
Chris@102
|
310 : [value] "r" (v) // %4
|
Chris@102
|
311 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
312 );
|
Chris@102
|
313 fence_after(order);
|
Chris@102
|
314 return original;
|
Chris@102
|
315 }
|
Chris@102
|
316
|
Chris@102
|
317 static BOOST_FORCEINLINE storage_type fetch_and(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
318 {
|
Chris@102
|
319 fence_before(order);
|
Chris@102
|
320 uint32_t tmp;
|
Chris@102
|
321 storage_type original, result;
|
Chris@102
|
322 __asm__ __volatile__
|
Chris@102
|
323 (
|
Chris@102
|
324 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
325 "1:\n"
|
Chris@102
|
326 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
327 "and %[result], %[original], %[value]\n" // result = original & value
|
Chris@102
|
328 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
329 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
330 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
331 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
332 : [original] "=&r" (original), // %0
|
Chris@102
|
333 [result] "=&r" (result), // %1
|
Chris@102
|
334 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
335 [storage] "+Q" (storage) // %3
|
Chris@102
|
336 : [value] "r" (v) // %4
|
Chris@102
|
337 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
338 );
|
Chris@102
|
339 fence_after(order);
|
Chris@102
|
340 return original;
|
Chris@102
|
341 }
|
Chris@102
|
342
|
Chris@102
|
343 static BOOST_FORCEINLINE storage_type fetch_or(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
344 {
|
Chris@102
|
345 fence_before(order);
|
Chris@102
|
346 uint32_t tmp;
|
Chris@102
|
347 storage_type original, result;
|
Chris@102
|
348 __asm__ __volatile__
|
Chris@102
|
349 (
|
Chris@102
|
350 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
351 "1:\n"
|
Chris@102
|
352 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
353 "orr %[result], %[original], %[value]\n" // result = original | value
|
Chris@102
|
354 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
355 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
356 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
357 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
358 : [original] "=&r" (original), // %0
|
Chris@102
|
359 [result] "=&r" (result), // %1
|
Chris@102
|
360 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
361 [storage] "+Q" (storage) // %3
|
Chris@102
|
362 : [value] "r" (v) // %4
|
Chris@102
|
363 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
364 );
|
Chris@102
|
365 fence_after(order);
|
Chris@102
|
366 return original;
|
Chris@102
|
367 }
|
Chris@102
|
368
|
Chris@102
|
369 static BOOST_FORCEINLINE storage_type fetch_xor(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
370 {
|
Chris@102
|
371 fence_before(order);
|
Chris@102
|
372 uint32_t tmp;
|
Chris@102
|
373 storage_type original, result;
|
Chris@102
|
374 __asm__ __volatile__
|
Chris@102
|
375 (
|
Chris@102
|
376 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
377 "1:\n"
|
Chris@102
|
378 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
379 "eor %[result], %[original], %[value]\n" // result = original ^ value
|
Chris@102
|
380 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
381 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
382 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
383 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
384 : [original] "=&r" (original), // %0
|
Chris@102
|
385 [result] "=&r" (result), // %1
|
Chris@102
|
386 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
387 [storage] "+Q" (storage) // %3
|
Chris@102
|
388 : [value] "r" (v) // %4
|
Chris@102
|
389 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
390 );
|
Chris@102
|
391 fence_after(order);
|
Chris@102
|
392 return original;
|
Chris@102
|
393 }
|
Chris@102
|
394
|
Chris@102
|
395 static BOOST_FORCEINLINE bool test_and_set(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
396 {
|
Chris@102
|
397 return !!exchange(storage, (storage_type)1, order);
|
Chris@102
|
398 }
|
Chris@102
|
399
|
Chris@102
|
400 static BOOST_FORCEINLINE void clear(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
401 {
|
Chris@102
|
402 store(storage, 0, order);
|
Chris@102
|
403 }
|
Chris@102
|
404
|
Chris@102
|
405 static BOOST_FORCEINLINE bool is_lock_free(storage_type const volatile&) BOOST_NOEXCEPT
|
Chris@102
|
406 {
|
Chris@102
|
407 return true;
|
Chris@102
|
408 }
|
Chris@102
|
409 };
|
Chris@102
|
410
|
Chris@102
|
411
|
Chris@102
|
412 template< >
|
Chris@102
|
413 struct operations< 1u, false > :
|
Chris@102
|
414 public operations< 4u, false >
|
Chris@102
|
415 {
|
Chris@102
|
416 typedef operations< 4u, false > base_type;
|
Chris@102
|
417 typedef base_type::storage_type storage_type;
|
Chris@102
|
418
|
Chris@102
|
419 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
420 {
|
Chris@102
|
421 fence_before(order);
|
Chris@102
|
422 uint32_t tmp;
|
Chris@102
|
423 storage_type original, result;
|
Chris@102
|
424 __asm__ __volatile__
|
Chris@102
|
425 (
|
Chris@102
|
426 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
427 "1:\n"
|
Chris@102
|
428 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
429 "add %[result], %[original], %[value]\n" // result = original + value
|
Chris@102
|
430 "uxtb %[result], %[result]\n" // zero extend result from 8 to 32 bits
|
Chris@102
|
431 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
432 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
433 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
434 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
435 : [original] "=&r" (original), // %0
|
Chris@102
|
436 [result] "=&r" (result), // %1
|
Chris@102
|
437 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
438 [storage] "+Q" (storage) // %3
|
Chris@102
|
439 : [value] "r" (v) // %4
|
Chris@102
|
440 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
441 );
|
Chris@102
|
442 fence_after(order);
|
Chris@102
|
443 return original;
|
Chris@102
|
444 }
|
Chris@102
|
445
|
Chris@102
|
446 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
447 {
|
Chris@102
|
448 fence_before(order);
|
Chris@102
|
449 uint32_t tmp;
|
Chris@102
|
450 storage_type original, result;
|
Chris@102
|
451 __asm__ __volatile__
|
Chris@102
|
452 (
|
Chris@102
|
453 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
454 "1:\n"
|
Chris@102
|
455 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
456 "sub %[result], %[original], %[value]\n" // result = original - value
|
Chris@102
|
457 "uxtb %[result], %[result]\n" // zero extend result from 8 to 32 bits
|
Chris@102
|
458 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
459 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
460 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
461 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
462 : [original] "=&r" (original), // %0
|
Chris@102
|
463 [result] "=&r" (result), // %1
|
Chris@102
|
464 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
465 [storage] "+Q" (storage) // %3
|
Chris@102
|
466 : [value] "r" (v) // %4
|
Chris@102
|
467 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
468 );
|
Chris@102
|
469 fence_after(order);
|
Chris@102
|
470 return original;
|
Chris@102
|
471 }
|
Chris@102
|
472 };
|
Chris@102
|
473
|
Chris@102
|
474 template< >
|
Chris@102
|
475 struct operations< 1u, true > :
|
Chris@102
|
476 public operations< 4u, true >
|
Chris@102
|
477 {
|
Chris@102
|
478 typedef operations< 4u, true > base_type;
|
Chris@102
|
479 typedef base_type::storage_type storage_type;
|
Chris@102
|
480
|
Chris@102
|
481 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
482 {
|
Chris@102
|
483 fence_before(order);
|
Chris@102
|
484 uint32_t tmp;
|
Chris@102
|
485 storage_type original, result;
|
Chris@102
|
486 __asm__ __volatile__
|
Chris@102
|
487 (
|
Chris@102
|
488 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
489 "1:\n"
|
Chris@102
|
490 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
491 "add %[result], %[original], %[value]\n" // result = original + value
|
Chris@102
|
492 "sxtb %[result], %[result]\n" // sign extend result from 8 to 32 bits
|
Chris@102
|
493 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
494 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
495 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
496 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
497 : [original] "=&r" (original), // %0
|
Chris@102
|
498 [result] "=&r" (result), // %1
|
Chris@102
|
499 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
500 [storage] "+Q" (storage) // %3
|
Chris@102
|
501 : [value] "r" (v) // %4
|
Chris@102
|
502 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
503 );
|
Chris@102
|
504 fence_after(order);
|
Chris@102
|
505 return original;
|
Chris@102
|
506 }
|
Chris@102
|
507
|
Chris@102
|
508 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
509 {
|
Chris@102
|
510 fence_before(order);
|
Chris@102
|
511 uint32_t tmp;
|
Chris@102
|
512 storage_type original, result;
|
Chris@102
|
513 __asm__ __volatile__
|
Chris@102
|
514 (
|
Chris@102
|
515 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
516 "1:\n"
|
Chris@102
|
517 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
518 "sub %[result], %[original], %[value]\n" // result = original - value
|
Chris@102
|
519 "sxtb %[result], %[result]\n" // sign extend result from 8 to 32 bits
|
Chris@102
|
520 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
521 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
522 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
523 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
524 : [original] "=&r" (original), // %0
|
Chris@102
|
525 [result] "=&r" (result), // %1
|
Chris@102
|
526 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
527 [storage] "+Q" (storage) // %3
|
Chris@102
|
528 : [value] "r" (v) // %4
|
Chris@102
|
529 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
530 );
|
Chris@102
|
531 fence_after(order);
|
Chris@102
|
532 return original;
|
Chris@102
|
533 }
|
Chris@102
|
534 };
|
Chris@102
|
535
|
Chris@102
|
536
|
Chris@102
|
537 template< >
|
Chris@102
|
538 struct operations< 2u, false > :
|
Chris@102
|
539 public operations< 4u, false >
|
Chris@102
|
540 {
|
Chris@102
|
541 typedef operations< 4u, false > base_type;
|
Chris@102
|
542 typedef base_type::storage_type storage_type;
|
Chris@102
|
543
|
Chris@102
|
544 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
545 {
|
Chris@102
|
546 fence_before(order);
|
Chris@102
|
547 uint32_t tmp;
|
Chris@102
|
548 storage_type original, result;
|
Chris@102
|
549 __asm__ __volatile__
|
Chris@102
|
550 (
|
Chris@102
|
551 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
552 "1:\n"
|
Chris@102
|
553 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
554 "add %[result], %[original], %[value]\n" // result = original + value
|
Chris@102
|
555 "uxth %[result], %[result]\n" // zero extend result from 16 to 32 bits
|
Chris@102
|
556 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
557 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
558 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
559 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
560 : [original] "=&r" (original), // %0
|
Chris@102
|
561 [result] "=&r" (result), // %1
|
Chris@102
|
562 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
563 [storage] "+Q" (storage) // %3
|
Chris@102
|
564 : [value] "r" (v) // %4
|
Chris@102
|
565 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
566 );
|
Chris@102
|
567 fence_after(order);
|
Chris@102
|
568 return original;
|
Chris@102
|
569 }
|
Chris@102
|
570
|
Chris@102
|
571 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
572 {
|
Chris@102
|
573 fence_before(order);
|
Chris@102
|
574 uint32_t tmp;
|
Chris@102
|
575 storage_type original, result;
|
Chris@102
|
576 __asm__ __volatile__
|
Chris@102
|
577 (
|
Chris@102
|
578 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
579 "1:\n"
|
Chris@102
|
580 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
581 "sub %[result], %[original], %[value]\n" // result = original - value
|
Chris@102
|
582 "uxth %[result], %[result]\n" // zero extend result from 16 to 32 bits
|
Chris@102
|
583 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
584 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
585 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
586 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
587 : [original] "=&r" (original), // %0
|
Chris@102
|
588 [result] "=&r" (result), // %1
|
Chris@102
|
589 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
590 [storage] "+Q" (storage) // %3
|
Chris@102
|
591 : [value] "r" (v) // %4
|
Chris@102
|
592 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
593 );
|
Chris@102
|
594 fence_after(order);
|
Chris@102
|
595 return original;
|
Chris@102
|
596 }
|
Chris@102
|
597 };
|
Chris@102
|
598
|
Chris@102
|
599 template< >
|
Chris@102
|
600 struct operations< 2u, true > :
|
Chris@102
|
601 public operations< 4u, true >
|
Chris@102
|
602 {
|
Chris@102
|
603 typedef operations< 4u, true > base_type;
|
Chris@102
|
604 typedef base_type::storage_type storage_type;
|
Chris@102
|
605
|
Chris@102
|
606 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
607 {
|
Chris@102
|
608 fence_before(order);
|
Chris@102
|
609 uint32_t tmp;
|
Chris@102
|
610 storage_type original, result;
|
Chris@102
|
611 __asm__ __volatile__
|
Chris@102
|
612 (
|
Chris@102
|
613 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
614 "1:\n"
|
Chris@102
|
615 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
616 "add %[result], %[original], %[value]\n" // result = original + value
|
Chris@102
|
617 "sxth %[result], %[result]\n" // sign extend result from 16 to 32 bits
|
Chris@102
|
618 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
619 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
620 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
621 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
622 : [original] "=&r" (original), // %0
|
Chris@102
|
623 [result] "=&r" (result), // %1
|
Chris@102
|
624 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
625 [storage] "+Q" (storage) // %3
|
Chris@102
|
626 : [value] "r" (v) // %4
|
Chris@102
|
627 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
628 );
|
Chris@102
|
629 fence_after(order);
|
Chris@102
|
630 return original;
|
Chris@102
|
631 }
|
Chris@102
|
632
|
Chris@102
|
633 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
634 {
|
Chris@102
|
635 fence_before(order);
|
Chris@102
|
636 uint32_t tmp;
|
Chris@102
|
637 storage_type original, result;
|
Chris@102
|
638 __asm__ __volatile__
|
Chris@102
|
639 (
|
Chris@102
|
640 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%[tmp])
|
Chris@102
|
641 "1:\n"
|
Chris@102
|
642 "ldrex %[original], %[storage]\n" // original = *(&storage)
|
Chris@102
|
643 "sub %[result], %[original], %[value]\n" // result = original - value
|
Chris@102
|
644 "sxth %[result], %[result]\n" // sign extend result from 16 to 32 bits
|
Chris@102
|
645 "strex %[tmp], %[result], %[storage]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
646 "teq %[tmp], #0\n" // flags = tmp==0
|
Chris@102
|
647 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
648 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%[tmp])
|
Chris@102
|
649 : [original] "=&r" (original), // %0
|
Chris@102
|
650 [result] "=&r" (result), // %1
|
Chris@102
|
651 [tmp] "=&l" (tmp), // %2
|
Chris@102
|
652 [storage] "+Q" (storage) // %3
|
Chris@102
|
653 : [value] "r" (v) // %4
|
Chris@102
|
654 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC
|
Chris@102
|
655 );
|
Chris@102
|
656 fence_after(order);
|
Chris@102
|
657 return original;
|
Chris@102
|
658 }
|
Chris@102
|
659 };
|
Chris@102
|
660
|
Chris@102
|
661
|
Chris@102
|
662 #if defined(BOOST_ATOMIC_DETAIL_ARM_HAS_LDREXD_STREXD)
|
Chris@102
|
663
|
Chris@102
|
664 // Unlike 32-bit operations, for 64-bit loads and stores we must use ldrexd/strexd.
|
Chris@102
|
665 // Any other instructions result in a non-atomic sequence of 32-bit accesses.
|
Chris@102
|
666 // See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition",
|
Chris@102
|
667 // Section A3.5.3 "Atomicity in the ARM architecture".
|
Chris@102
|
668
|
Chris@102
|
669 // In the asm blocks below we have to use 32-bit register pairs to compose 64-bit values.
|
Chris@102
|
670 // In order to pass the 64-bit operands to/from asm blocks, we use undocumented gcc feature:
|
Chris@102
|
671 // the lower half (Rt) of the operand is accessible normally, via the numbered placeholder (e.g. %0),
|
Chris@102
|
672 // and the upper half (Rt2) - via the same placeholder with an 'H' after the '%' sign (e.g. %H0).
|
Chris@102
|
673 // See: http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/
|
Chris@102
|
674
|
Chris@102
|
675 template< bool Signed >
|
Chris@102
|
676 struct operations< 8u, Signed > :
|
Chris@102
|
677 public gcc_arm_operations_base
|
Chris@102
|
678 {
|
Chris@102
|
679 typedef typename make_storage_type< 8u, Signed >::type storage_type;
|
Chris@102
|
680
|
Chris@102
|
681 static BOOST_FORCEINLINE void store(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
682 {
|
Chris@102
|
683 exchange(storage, v, order);
|
Chris@102
|
684 }
|
Chris@102
|
685
|
Chris@102
|
686 static BOOST_FORCEINLINE storage_type load(storage_type const volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
687 {
|
Chris@102
|
688 storage_type original;
|
Chris@102
|
689 uint32_t tmp;
|
Chris@102
|
690 __asm__ __volatile__
|
Chris@102
|
691 (
|
Chris@102
|
692 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
693 "ldrexd %1, %H1, [%2]\n"
|
Chris@102
|
694 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
695 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
696 "=&r" (original) // %1
|
Chris@102
|
697 : "r" (&storage) // %2
|
Chris@102
|
698 );
|
Chris@102
|
699 fence_after(order);
|
Chris@102
|
700 return original;
|
Chris@102
|
701 }
|
Chris@102
|
702
|
Chris@102
|
703 static BOOST_FORCEINLINE storage_type exchange(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
704 {
|
Chris@102
|
705 storage_type original;
|
Chris@102
|
706 fence_before(order);
|
Chris@102
|
707 uint32_t tmp;
|
Chris@102
|
708 __asm__ __volatile__
|
Chris@102
|
709 (
|
Chris@102
|
710 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
711 "1:\n"
|
Chris@102
|
712 "ldrexd %1, %H1, [%3]\n" // load the original value
|
Chris@102
|
713 "strexd %0, %2, %H2, [%3]\n" // store the replacement, tmp = store failed
|
Chris@102
|
714 "teq %0, #0\n" // check if store succeeded
|
Chris@102
|
715 "bne 1b\n"
|
Chris@102
|
716 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
717 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
718 "=&r" (original) // %1
|
Chris@102
|
719 : "r" (v), // %2
|
Chris@102
|
720 "r" (&storage) // %3
|
Chris@102
|
721 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
722 );
|
Chris@102
|
723 fence_after(order);
|
Chris@102
|
724 return original;
|
Chris@102
|
725 }
|
Chris@102
|
726
|
Chris@102
|
727 static BOOST_FORCEINLINE bool compare_exchange_weak(
|
Chris@102
|
728 storage_type volatile& storage, storage_type& expected, storage_type desired, memory_order success_order, memory_order failure_order) BOOST_NOEXCEPT
|
Chris@102
|
729 {
|
Chris@102
|
730 fence_before(success_order);
|
Chris@102
|
731 uint32_t tmp;
|
Chris@102
|
732 storage_type original, old_val = expected;
|
Chris@102
|
733 __asm__ __volatile__
|
Chris@102
|
734 (
|
Chris@102
|
735 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
736 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
737 "cmp %1, %2\n" // flags = original.lo==old_val.lo
|
Chris@102
|
738 "ittt eq\n" // [hint that the following 3 instructions are conditional on flags.equal]
|
Chris@102
|
739 "cmpeq %H1, %H2\n" // if (flags.equal) flags = original.hi==old_val.hi
|
Chris@102
|
740 "strexdeq %0, %4, %H4, [%3]\n" // if (flags.equal) *(&storage) = desired, tmp = store failed
|
Chris@102
|
741 "teqeq %0, #0\n" // if (flags.equal) flags = tmp==0
|
Chris@102
|
742 "ite eq\n" // [hint that the following 2 instructions are conditional on flags.equal]
|
Chris@102
|
743 "moveq %2, #1\n" // if (flags.equal) old_val.lo = 1
|
Chris@102
|
744 "movne %2, #0\n" // if (!flags.equal) old_val.lo = 0
|
Chris@102
|
745 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
746 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
747 "=&r" (original), // %1
|
Chris@102
|
748 "+r" (old_val) // %2
|
Chris@102
|
749 : "r" (&storage), // %3
|
Chris@102
|
750 "r" (desired) // %4
|
Chris@102
|
751 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
752 );
|
Chris@102
|
753 const uint32_t success = (uint32_t)old_val;
|
Chris@102
|
754 if (success)
|
Chris@102
|
755 fence_after(success_order);
|
Chris@102
|
756 else
|
Chris@102
|
757 fence_after(failure_order);
|
Chris@102
|
758 expected = original;
|
Chris@102
|
759 return !!success;
|
Chris@102
|
760 }
|
Chris@102
|
761
|
Chris@102
|
762 static BOOST_FORCEINLINE bool compare_exchange_strong(
|
Chris@102
|
763 storage_type volatile& storage, storage_type& expected, storage_type desired, memory_order success_order, memory_order failure_order) BOOST_NOEXCEPT
|
Chris@102
|
764 {
|
Chris@102
|
765 fence_before(success_order);
|
Chris@102
|
766 uint32_t tmp;
|
Chris@102
|
767 storage_type original, old_val = expected;
|
Chris@102
|
768 __asm__ __volatile__
|
Chris@102
|
769 (
|
Chris@102
|
770 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
771 "1:\n"
|
Chris@102
|
772 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
773 "cmp %1, %2\n" // flags = original.lo==old_val.lo
|
Chris@102
|
774 "it eq\n" // [hint that the following instruction is conditional on flags.equal]
|
Chris@102
|
775 "cmpeq %H1, %H2\n" // if (flags.equal) flags = original.hi==old_val.hi
|
Chris@102
|
776 "bne 2f\n" // if (!flags.equal) goto end
|
Chris@102
|
777 "strexd %0, %4, %H4, [%3]\n" // *(&storage) = desired, tmp = store failed
|
Chris@102
|
778 "teq %0, #0\n" // flags.equal = tmp == 0
|
Chris@102
|
779 "bne 1b\n" // if (flags.equal) goto retry
|
Chris@102
|
780 "2:\n"
|
Chris@102
|
781 "ite eq\n" // [hint that the following 2 instructions are conditional on flags.equal]
|
Chris@102
|
782 "moveq %2, #1\n" // if (flags.equal) old_val.lo = 1
|
Chris@102
|
783 "movne %2, #0\n" // if (!flags.equal) old_val.lo = 0
|
Chris@102
|
784 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
785 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
786 "=&r" (original), // %1
|
Chris@102
|
787 "+r" (old_val) // %2
|
Chris@102
|
788 : "r" (&storage), // %3
|
Chris@102
|
789 "r" (desired) // %4
|
Chris@102
|
790 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
791 );
|
Chris@102
|
792 const uint32_t success = (uint32_t)old_val;
|
Chris@102
|
793 if (success)
|
Chris@102
|
794 fence_after(success_order);
|
Chris@102
|
795 else
|
Chris@102
|
796 fence_after(failure_order);
|
Chris@102
|
797 expected = original;
|
Chris@102
|
798 return !!success;
|
Chris@102
|
799 }
|
Chris@102
|
800
|
Chris@102
|
801 static BOOST_FORCEINLINE storage_type fetch_add(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
802 {
|
Chris@102
|
803 fence_before(order);
|
Chris@102
|
804 storage_type original, result;
|
Chris@102
|
805 uint32_t tmp;
|
Chris@102
|
806 __asm__ __volatile__
|
Chris@102
|
807 (
|
Chris@102
|
808 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
809 "1:\n"
|
Chris@102
|
810 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
811 "adds %2, %1, %4\n" // result = original + value
|
Chris@102
|
812 "adc %H2, %H1, %H4\n"
|
Chris@102
|
813 "strexd %0, %2, %H2, [%3]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
814 "teq %0, #0\n" // flags = tmp==0
|
Chris@102
|
815 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
816 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
817 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
818 "=&r" (original), // %1
|
Chris@102
|
819 "=&r" (result) // %2
|
Chris@102
|
820 : "r" (&storage), // %3
|
Chris@102
|
821 "r" (v) // %4
|
Chris@102
|
822 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
823 );
|
Chris@102
|
824 fence_after(order);
|
Chris@102
|
825 return original;
|
Chris@102
|
826 }
|
Chris@102
|
827
|
Chris@102
|
828 static BOOST_FORCEINLINE storage_type fetch_sub(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
829 {
|
Chris@102
|
830 fence_before(order);
|
Chris@102
|
831 storage_type original, result;
|
Chris@102
|
832 uint32_t tmp;
|
Chris@102
|
833 __asm__ __volatile__
|
Chris@102
|
834 (
|
Chris@102
|
835 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
836 "1:\n"
|
Chris@102
|
837 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
838 "subs %2, %1, %4\n" // result = original - value
|
Chris@102
|
839 "sbc %H2, %H1, %H4\n"
|
Chris@102
|
840 "strexd %0, %2, %H2, [%3]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
841 "teq %0, #0\n" // flags = tmp==0
|
Chris@102
|
842 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
843 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
844 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
845 "=&r" (original), // %1
|
Chris@102
|
846 "=&r" (result) // %2
|
Chris@102
|
847 : "r" (&storage), // %3
|
Chris@102
|
848 "r" (v) // %4
|
Chris@102
|
849 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
850 );
|
Chris@102
|
851 fence_after(order);
|
Chris@102
|
852 return original;
|
Chris@102
|
853 }
|
Chris@102
|
854
|
Chris@102
|
855 static BOOST_FORCEINLINE storage_type fetch_and(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
856 {
|
Chris@102
|
857 fence_before(order);
|
Chris@102
|
858 storage_type original, result;
|
Chris@102
|
859 uint32_t tmp;
|
Chris@102
|
860 __asm__ __volatile__
|
Chris@102
|
861 (
|
Chris@102
|
862 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
863 "1:\n"
|
Chris@102
|
864 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
865 "and %2, %1, %4\n" // result = original & value
|
Chris@102
|
866 "and %H2, %H1, %H4\n"
|
Chris@102
|
867 "strexd %0, %2, %H2, [%3]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
868 "teq %0, #0\n" // flags = tmp==0
|
Chris@102
|
869 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
870 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
871 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
872 "=&r" (original), // %1
|
Chris@102
|
873 "=&r" (result) // %2
|
Chris@102
|
874 : "r" (&storage), // %3
|
Chris@102
|
875 "r" (v) // %4
|
Chris@102
|
876 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
877 );
|
Chris@102
|
878 fence_after(order);
|
Chris@102
|
879 return original;
|
Chris@102
|
880 }
|
Chris@102
|
881
|
Chris@102
|
882 static BOOST_FORCEINLINE storage_type fetch_or(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
883 {
|
Chris@102
|
884 fence_before(order);
|
Chris@102
|
885 storage_type original, result;
|
Chris@102
|
886 uint32_t tmp;
|
Chris@102
|
887 __asm__ __volatile__
|
Chris@102
|
888 (
|
Chris@102
|
889 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
890 "1:\n"
|
Chris@102
|
891 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
892 "orr %2, %1, %4\n" // result = original | value
|
Chris@102
|
893 "orr %H2, %H1, %H4\n"
|
Chris@102
|
894 "strexd %0, %2, %H2, [%3]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
895 "teq %0, #0\n" // flags = tmp==0
|
Chris@102
|
896 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
897 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
898 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
899 "=&r" (original), // %1
|
Chris@102
|
900 "=&r" (result) // %2
|
Chris@102
|
901 : "r" (&storage), // %3
|
Chris@102
|
902 "r" (v) // %4
|
Chris@102
|
903 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
904 );
|
Chris@102
|
905 fence_after(order);
|
Chris@102
|
906 return original;
|
Chris@102
|
907 }
|
Chris@102
|
908
|
Chris@102
|
909 static BOOST_FORCEINLINE storage_type fetch_xor(storage_type volatile& storage, storage_type v, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
910 {
|
Chris@102
|
911 fence_before(order);
|
Chris@102
|
912 storage_type original, result;
|
Chris@102
|
913 uint32_t tmp;
|
Chris@102
|
914 __asm__ __volatile__
|
Chris@102
|
915 (
|
Chris@102
|
916 BOOST_ATOMIC_DETAIL_ARM_ASM_START(%0)
|
Chris@102
|
917 "1:\n"
|
Chris@102
|
918 "ldrexd %1, %H1, [%3]\n" // original = *(&storage)
|
Chris@102
|
919 "eor %2, %1, %4\n" // result = original ^ value
|
Chris@102
|
920 "eor %H2, %H1, %H4\n"
|
Chris@102
|
921 "strexd %0, %2, %H2, [%3]\n" // *(&storage) = result, tmp = store failed
|
Chris@102
|
922 "teq %0, #0\n" // flags = tmp==0
|
Chris@102
|
923 "bne 1b\n" // if (!flags.equal) goto retry
|
Chris@102
|
924 BOOST_ATOMIC_DETAIL_ARM_ASM_END(%0)
|
Chris@102
|
925 : BOOST_ATOMIC_DETAIL_ARM_ASM_TMPREG_CONSTRAINT(tmp), // %0
|
Chris@102
|
926 "=&r" (original), // %1
|
Chris@102
|
927 "=&r" (result) // %2
|
Chris@102
|
928 : "r" (&storage), // %3
|
Chris@102
|
929 "r" (v) // %4
|
Chris@102
|
930 : BOOST_ATOMIC_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
|
Chris@102
|
931 );
|
Chris@102
|
932 fence_after(order);
|
Chris@102
|
933 return original;
|
Chris@102
|
934 }
|
Chris@102
|
935
|
Chris@102
|
936 static BOOST_FORCEINLINE bool test_and_set(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
937 {
|
Chris@102
|
938 return !!exchange(storage, (storage_type)1, order);
|
Chris@102
|
939 }
|
Chris@102
|
940
|
Chris@102
|
941 static BOOST_FORCEINLINE void clear(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
942 {
|
Chris@102
|
943 store(storage, 0, order);
|
Chris@102
|
944 }
|
Chris@102
|
945
|
Chris@102
|
946 static BOOST_FORCEINLINE bool is_lock_free(storage_type const volatile&) BOOST_NOEXCEPT
|
Chris@102
|
947 {
|
Chris@102
|
948 return true;
|
Chris@102
|
949 }
|
Chris@102
|
950 };
|
Chris@102
|
951
|
Chris@102
|
952 #endif // defined(BOOST_ATOMIC_DETAIL_ARM_HAS_LDREXD_STREXD)
|
Chris@102
|
953
|
Chris@102
|
954
|
Chris@102
|
955 BOOST_FORCEINLINE void thread_fence(memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
956 {
|
Chris@102
|
957 if (order != memory_order_relaxed)
|
Chris@102
|
958 gcc_arm_operations_base::hardware_full_fence();
|
Chris@102
|
959 }
|
Chris@102
|
960
|
Chris@102
|
961 BOOST_FORCEINLINE void signal_fence(memory_order order) BOOST_NOEXCEPT
|
Chris@102
|
962 {
|
Chris@102
|
963 if (order != memory_order_relaxed)
|
Chris@102
|
964 __asm__ __volatile__ ("" ::: "memory");
|
Chris@102
|
965 }
|
Chris@102
|
966
|
Chris@102
|
967 } // namespace detail
|
Chris@102
|
968 } // namespace atomics
|
Chris@102
|
969 } // namespace boost
|
Chris@102
|
970
|
Chris@102
|
971 #endif // BOOST_ATOMIC_DETAIL_OPS_GCC_ARM_HPP_INCLUDED_
|