gcc-armv6plus.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #ifndef BOOST_ATOMIC_DETAIL_GCC_ARMV6PLUS_HPP
  2. #define BOOST_ATOMIC_DETAIL_GCC_ARMV6PLUS_HPP
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // Copyright (c) 2009 Helge Bahmann
  8. // Copyright (c) 2009 Phil Endecott
  9. // Copyright (c) 2013 Tim Blechmann
  10. // ARM Code by Phil Endecott, based on other architectures.
  11. #include <boost/cstdint.hpp>
  12. #include <boost/atomic/detail/config.hpp>
  13. #ifdef BOOST_HAS_PRAGMA_ONCE
  14. #pragma once
  15. #endif
  16. // From the ARM Architecture Reference Manual for architecture v6:
  17. //
  18. // LDREX{<cond>} <Rd>, [<Rn>]
  19. // <Rd> Specifies the destination register for the memory word addressed by <Rd>
  20. // <Rn> Specifies the register containing the address.
  21. //
  22. // STREX{<cond>} <Rd>, <Rm>, [<Rn>]
  23. // <Rd> Specifies the destination register for the returned status value.
  24. // 0 if the operation updates memory
  25. // 1 if the operation fails to update memory
  26. // <Rm> Specifies the register containing the word to be stored to memory.
  27. // <Rn> Specifies the register containing the address.
  28. // Rd must not be the same register as Rm or Rn.
  29. //
  30. // ARM v7 is like ARM v6 plus:
  31. // There are half-word and byte versions of the LDREX and STREX instructions,
  32. // LDREXH, LDREXB, STREXH and STREXB.
  33. // There are also double-word versions, LDREXD and STREXD.
  34. // (Actually it looks like these are available from version 6k onwards.)
  35. // FIXME these are not yet used; should be mostly a matter of copy-and-paste.
  36. // I think you can supply an immediate offset to the address.
  37. //
  38. // A memory barrier is effected using a "co-processor 15" instruction,
  39. // though a separate assembler mnemonic is available for it in v7.
  40. namespace boost {
  41. namespace atomics {
  42. namespace detail {
  43. // "Thumb 1" is a subset of the ARM instruction set that uses a 16-bit encoding. It
  44. // doesn't include all instructions and in particular it doesn't include the co-processor
  45. // instruction used for the memory barrier or the load-locked/store-conditional
  46. // instructions. So, if we're compiling in "Thumb 1" mode, we need to wrap all of our
  47. // asm blocks with code to temporarily change to ARM mode.
  48. //
  49. // You can only change between ARM and Thumb modes when branching using the bx instruction.
  50. // bx takes an address specified in a register. The least significant bit of the address
  51. // indicates the mode, so 1 is added to indicate that the destination code is Thumb.
  52. // A temporary register is needed for the address and is passed as an argument to these
  53. // macros. It must be one of the "low" registers accessible to Thumb code, specified
  54. // using the "l" attribute in the asm statement.
  55. //
  56. // Architecture v7 introduces "Thumb 2", which does include (almost?) all of the ARM
  57. // instruction set. So in v7 we don't need to change to ARM mode; we can write "universal
  58. // assembler" which will assemble to Thumb 2 or ARM code as appropriate. The only thing
  59. // we need to do to make this "universal" assembler mode work is to insert "IT" instructions
  60. // to annotate the conditional instructions. These are ignored in other modes (e.g. v6),
  61. // so they can always be present.
  62. #if defined(__thumb__) && !defined(__thumb2__)
  63. #define BOOST_ATOMIC_ARM_ASM_START(TMPREG) "adr " #TMPREG ", 1f\n" "bx " #TMPREG "\n" ".arm\n" ".align 4\n" "1: "
  64. #define BOOST_ATOMIC_ARM_ASM_END(TMPREG) "adr " #TMPREG ", 1f + 1\n" "bx " #TMPREG "\n" ".thumb\n" ".align 2\n" "1: "
  65. #else
  66. // The tmpreg is wasted in this case, which is non-optimal.
  67. #define BOOST_ATOMIC_ARM_ASM_START(TMPREG)
  68. #define BOOST_ATOMIC_ARM_ASM_END(TMPREG)
  69. #endif
  70. #if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7S__)
  71. #define BOOST_ATOMIC_ARM_DMB "dmb\n"
  72. #else
  73. #define BOOST_ATOMIC_ARM_DMB "mcr\tp15, 0, r0, c7, c10, 5\n"
  74. #endif
  75. inline void
  76. arm_barrier(void) BOOST_NOEXCEPT
  77. {
  78. int brtmp;
  79. __asm__ __volatile__
  80. (
  81. BOOST_ATOMIC_ARM_ASM_START(%0)
  82. BOOST_ATOMIC_ARM_DMB
  83. BOOST_ATOMIC_ARM_ASM_END(%0)
  84. : "=&l" (brtmp) :: "memory"
  85. );
  86. }
  87. inline void
  88. platform_fence_before(memory_order order) BOOST_NOEXCEPT
  89. {
  90. switch(order)
  91. {
  92. case memory_order_release:
  93. case memory_order_acq_rel:
  94. case memory_order_seq_cst:
  95. arm_barrier();
  96. case memory_order_consume:
  97. default:;
  98. }
  99. }
  100. inline void
  101. platform_fence_after(memory_order order) BOOST_NOEXCEPT
  102. {
  103. switch(order)
  104. {
  105. case memory_order_acquire:
  106. case memory_order_acq_rel:
  107. case memory_order_seq_cst:
  108. arm_barrier();
  109. default:;
  110. }
  111. }
  112. inline void
  113. platform_fence_before_store(memory_order order) BOOST_NOEXCEPT
  114. {
  115. platform_fence_before(order);
  116. }
  117. inline void
  118. platform_fence_after_store(memory_order order) BOOST_NOEXCEPT
  119. {
  120. if (order == memory_order_seq_cst)
  121. arm_barrier();
  122. }
  123. inline void
  124. platform_fence_after_load(memory_order order) BOOST_NOEXCEPT
  125. {
  126. platform_fence_after(order);
  127. }
  128. template<typename T>
  129. inline bool
  130. platform_cmpxchg32(T & expected, T desired, volatile T * ptr) BOOST_NOEXCEPT
  131. {
  132. int success;
  133. int tmp;
  134. __asm__ __volatile__
  135. (
  136. BOOST_ATOMIC_ARM_ASM_START(%2)
  137. "mov %1, #0\n" // success = 0
  138. "ldrex %0, %3\n" // expected' = *(&i)
  139. "teq %0, %4\n" // flags = expected'==expected
  140. "ittt eq\n"
  141. "strexeq %2, %5, %3\n" // if (flags.equal) *(&i) = desired, tmp = !OK
  142. "teqeq %2, #0\n" // if (flags.equal) flags = tmp==0
  143. "moveq %1, #1\n" // if (flags.equal) success = 1
  144. BOOST_ATOMIC_ARM_ASM_END(%2)
  145. : "=&r" (expected), // %0
  146. "=&r" (success), // %1
  147. "=&l" (tmp), // %2
  148. "+Q" (*ptr) // %3
  149. : "r" (expected), // %4
  150. "r" (desired) // %5
  151. : "cc"
  152. );
  153. return success;
  154. }
  155. }
  156. }
  157. #define BOOST_ATOMIC_THREAD_FENCE 2
  158. inline void
  159. atomic_thread_fence(memory_order order)
  160. {
  161. switch(order)
  162. {
  163. case memory_order_acquire:
  164. case memory_order_release:
  165. case memory_order_acq_rel:
  166. case memory_order_seq_cst:
  167. atomics::detail::arm_barrier();
  168. default:;
  169. }
  170. }
  171. #define BOOST_ATOMIC_SIGNAL_FENCE 2
  172. inline void
  173. atomic_signal_fence(memory_order)
  174. {
  175. __asm__ __volatile__ ("" ::: "memory");
  176. }
  177. class atomic_flag
  178. {
  179. private:
  180. uint32_t v_;
  181. public:
  182. BOOST_CONSTEXPR atomic_flag(void) BOOST_NOEXCEPT : v_(0) {}
  183. void
  184. clear(memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT
  185. {
  186. atomics::detail::platform_fence_before_store(order);
  187. const_cast<volatile uint32_t &>(v_) = 0;
  188. atomics::detail::platform_fence_after_store(order);
  189. }
  190. bool
  191. test_and_set(memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT
  192. {
  193. atomics::detail::platform_fence_before(order);
  194. uint32_t expected = v_;
  195. do {
  196. if (expected == 1)
  197. break;
  198. } while (!atomics::detail::platform_cmpxchg32(expected, (uint32_t)1, &v_));
  199. atomics::detail::platform_fence_after(order);
  200. return expected;
  201. }
  202. BOOST_DELETED_FUNCTION(atomic_flag(const atomic_flag &))
  203. BOOST_DELETED_FUNCTION(atomic_flag& operator=(const atomic_flag &))
  204. };
  205. #define BOOST_ATOMIC_FLAG_LOCK_FREE 2
  206. }
  207. #undef BOOST_ATOMIC_ARM_ASM_START
  208. #undef BOOST_ATOMIC_ARM_ASM_END
  209. #include <boost/atomic/detail/base.hpp>
  210. #if !defined(BOOST_ATOMIC_FORCE_FALLBACK)
  211. #define BOOST_ATOMIC_CHAR_LOCK_FREE 2
  212. #define BOOST_ATOMIC_CHAR16_T_LOCK_FREE 2
  213. #define BOOST_ATOMIC_CHAR32_T_LOCK_FREE 2
  214. #define BOOST_ATOMIC_WCHAR_T_LOCK_FREE 2
  215. #define BOOST_ATOMIC_SHORT_LOCK_FREE 2
  216. #define BOOST_ATOMIC_INT_LOCK_FREE 2
  217. #define BOOST_ATOMIC_LONG_LOCK_FREE 2
  218. #define BOOST_ATOMIC_LLONG_LOCK_FREE 0
  219. #define BOOST_ATOMIC_POINTER_LOCK_FREE 2
  220. #define BOOST_ATOMIC_BOOL_LOCK_FREE 2
  221. #include <boost/atomic/detail/cas32weak.hpp>
  222. #endif /* !defined(BOOST_ATOMIC_FORCE_FALLBACK) */
  223. #endif