stdatomic.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. //replacement for gcc built-in functions
  7. #include "sdkconfig.h"
  8. #include <stdbool.h>
  9. #include <stdint.h>
  10. #include "soc/soc_caps.h"
  11. #include "freertos/FreeRTOS.h"
  12. #ifdef __XTENSA__
  13. #include "xtensa/config/core-isa.h"
  14. #ifndef XCHAL_HAVE_S32C1I
  15. #error "XCHAL_HAVE_S32C1I not defined, include correct header!"
  16. #endif
  17. #define HAS_ATOMICS_32 (XCHAL_HAVE_S32C1I == 1)
  18. // no 64-bit atomics on Xtensa
  19. #define HAS_ATOMICS_64 0
  20. #else // RISCV
  21. // GCC toolchain will define this pre-processor if "A" extension is supported
  22. #ifndef __riscv_atomic
  23. #define __riscv_atomic 0
  24. #endif
  25. #define HAS_ATOMICS_32 (__riscv_atomic == 1)
  26. #define HAS_ATOMICS_64 ((__riscv_atomic == 1) && (__riscv_xlen == 64))
  27. #endif // (__XTENSA__, __riscv)
  28. #if SOC_CPU_CORES_NUM == 1
  29. // Single core SoC: atomics can be implemented using portENTER_CRITICAL_NESTED
  30. // and portEXIT_CRITICAL_NESTED, which disable and enable interrupts.
  31. #define _ATOMIC_ENTER_CRITICAL() ({ \
  32. unsigned state = portENTER_CRITICAL_NESTED(); \
  33. state; \
  34. })
  35. #define _ATOMIC_EXIT_CRITICAL(state) do { \
  36. portEXIT_CRITICAL_NESTED(state); \
  37. } while (0)
  38. #else // SOC_CPU_CORES_NUM
  39. _Static_assert(HAS_ATOMICS_32, "32-bit atomics should be supported if SOC_CPU_CORES_NUM > 1");
  40. // Only need to implement 64-bit atomics here. Use a single global portMUX_TYPE spinlock
  41. // to emulate the atomics.
  42. static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED;
  43. // Return value is not used but kept for compatibility with the single-core version above.
  44. #define _ATOMIC_ENTER_CRITICAL() ({ \
  45. portENTER_CRITICAL_SAFE(&s_atomic_lock); \
  46. 0; \
  47. })
  48. #define _ATOMIC_EXIT_CRITICAL(state) do { \
  49. (void) (state); \
  50. portEXIT_CRITICAL_SAFE(&s_atomic_lock); \
  51. } while(0)
  52. #endif // SOC_CPU_CORES_NUM
  53. #ifdef __clang__
  54. // Clang doesn't allow to define "__sync_*" atomics. The workaround is to define function with name "__sync_*_builtin",
  55. // which implements "__sync_*" atomic functionality and use asm directive to set the value of symbol "__sync_*" to the name
  56. // of defined function.
  57. #define CLANG_ATOMIC_SUFFIX(name_) name_ ## _builtin
  58. #define CLANG_DECLARE_ALIAS(name_) \
  59. __asm__(".type " # name_ ", @function\n" \
  60. ".global " #name_ "\n" \
  61. ".equ " #name_ ", " #name_ "_builtin");
  62. #else // __clang__
  63. #define CLANG_ATOMIC_SUFFIX(name_) name_
  64. #define CLANG_DECLARE_ALIAS(name_)
  65. #endif // __clang__
  66. #define ATOMIC_LOAD(n, type) type __atomic_load_ ## n (const type* mem, int memorder) \
  67. { \
  68. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  69. type ret = *mem; \
  70. _ATOMIC_EXIT_CRITICAL(state); \
  71. return ret; \
  72. }
  73. #define ATOMIC_STORE(n, type) void __atomic_store_ ## n (type* mem, type val, int memorder) \
  74. { \
  75. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  76. *mem = val; \
  77. _ATOMIC_EXIT_CRITICAL(state); \
  78. }
  79. #define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \
  80. { \
  81. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  82. type ret = *mem; \
  83. *mem = val; \
  84. _ATOMIC_EXIT_CRITICAL(state); \
  85. return ret; \
  86. }
  87. #define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, bool weak, int success, int failure) \
  88. { \
  89. bool ret = false; \
  90. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  91. if (*mem == *expect) { \
  92. ret = true; \
  93. *mem = desired; \
  94. } else { \
  95. *expect = *mem; \
  96. } \
  97. _ATOMIC_EXIT_CRITICAL(state); \
  98. return ret; \
  99. }
  100. #define FETCH_ADD(n, type) type __atomic_fetch_add_ ## n (type* ptr, type value, int memorder) \
  101. { \
  102. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  103. type ret = *ptr; \
  104. *ptr = *ptr + value; \
  105. _ATOMIC_EXIT_CRITICAL(state); \
  106. return ret; \
  107. }
  108. #define FETCH_SUB(n, type) type __atomic_fetch_sub_ ## n (type* ptr, type value, int memorder) \
  109. { \
  110. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  111. type ret = *ptr; \
  112. *ptr = *ptr - value; \
  113. _ATOMIC_EXIT_CRITICAL(state); \
  114. return ret; \
  115. }
  116. #define FETCH_AND(n, type) type __atomic_fetch_and_ ## n (type* ptr, type value, int memorder) \
  117. { \
  118. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  119. type ret = *ptr; \
  120. *ptr = *ptr & value; \
  121. _ATOMIC_EXIT_CRITICAL(state); \
  122. return ret; \
  123. }
  124. #define FETCH_OR(n, type) type __atomic_fetch_or_ ## n (type* ptr, type value, int memorder) \
  125. { \
  126. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  127. type ret = *ptr; \
  128. *ptr = *ptr | value; \
  129. _ATOMIC_EXIT_CRITICAL(state); \
  130. return ret; \
  131. }
  132. #define FETCH_XOR(n, type) type __atomic_fetch_xor_ ## n (type* ptr, type value, int memorder) \
  133. { \
  134. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  135. type ret = *ptr; \
  136. *ptr = *ptr ^ value; \
  137. _ATOMIC_EXIT_CRITICAL(state); \
  138. return ret; \
  139. }
  140. #define SYNC_FETCH_OP(op, n, type) type CLANG_ATOMIC_SUFFIX(__sync_fetch_and_ ## op ##_ ## n) (type* ptr, type value) \
  141. { \
  142. return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \
  143. } \
  144. CLANG_DECLARE_ALIAS( __sync_fetch_and_ ## op ##_ ## n )
  145. #define SYNC_BOOL_CMP_EXCHANGE(n, type) bool CLANG_ATOMIC_SUFFIX(__sync_bool_compare_and_swap_ ## n) (type *ptr, type oldval, type newval) \
  146. { \
  147. bool ret = false; \
  148. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  149. if (*ptr == oldval) { \
  150. *ptr = newval; \
  151. ret = true; \
  152. } \
  153. _ATOMIC_EXIT_CRITICAL(state); \
  154. return ret; \
  155. } \
  156. CLANG_DECLARE_ALIAS( __sync_bool_compare_and_swap_ ## n )
  157. #define SYNC_VAL_CMP_EXCHANGE(n, type) type CLANG_ATOMIC_SUFFIX(__sync_val_compare_and_swap_ ## n) (type *ptr, type oldval, type newval) \
  158. { \
  159. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  160. type ret = *ptr; \
  161. if (*ptr == oldval) { \
  162. *ptr = newval; \
  163. } \
  164. _ATOMIC_EXIT_CRITICAL(state); \
  165. return ret; \
  166. } \
  167. CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n )
  168. #define SYNC_LOCK_TEST_AND_SET(n, type) type CLANG_ATOMIC_SUFFIX(__sync_lock_test_and_set_ ## n) (type *ptr, type val) \
  169. { \
  170. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  171. type ret = *ptr; \
  172. *ptr = val; \
  173. _ATOMIC_EXIT_CRITICAL(state); \
  174. return ret; \
  175. }
  176. CLANG_DECLARE_ALIAS( __sync_lock_test_and_set_ ## n )
  177. #define SYNC_LOCK_RELEASE(n, type) void CLANG_ATOMIC_SUFFIX(__sync_lock_release_ ## n) (type *ptr) \
  178. { \
  179. unsigned state = _ATOMIC_ENTER_CRITICAL(); \
  180. *ptr = 0; \
  181. _ATOMIC_EXIT_CRITICAL(state); \
  182. }
  183. CLANG_DECLARE_ALIAS( __sync_lock_release_ ## n )
  184. #if !HAS_ATOMICS_32
  185. ATOMIC_EXCHANGE(1, uint8_t)
  186. ATOMIC_EXCHANGE(2, uint16_t)
  187. ATOMIC_EXCHANGE(4, uint32_t)
  188. CMP_EXCHANGE(1, uint8_t)
  189. CMP_EXCHANGE(2, uint16_t)
  190. CMP_EXCHANGE(4, uint32_t)
  191. FETCH_ADD(1, uint8_t)
  192. FETCH_ADD(2, uint16_t)
  193. FETCH_ADD(4, uint32_t)
  194. FETCH_SUB(1, uint8_t)
  195. FETCH_SUB(2, uint16_t)
  196. FETCH_SUB(4, uint32_t)
  197. FETCH_AND(1, uint8_t)
  198. FETCH_AND(2, uint16_t)
  199. FETCH_AND(4, uint32_t)
  200. FETCH_OR(1, uint8_t)
  201. FETCH_OR(2, uint16_t)
  202. FETCH_OR(4, uint32_t)
  203. FETCH_XOR(1, uint8_t)
  204. FETCH_XOR(2, uint16_t)
  205. FETCH_XOR(4, uint32_t)
  206. SYNC_FETCH_OP(add, 1, uint8_t)
  207. SYNC_FETCH_OP(add, 2, uint16_t)
  208. SYNC_FETCH_OP(add, 4, uint32_t)
  209. SYNC_FETCH_OP(sub, 1, uint8_t)
  210. SYNC_FETCH_OP(sub, 2, uint16_t)
  211. SYNC_FETCH_OP(sub, 4, uint32_t)
  212. SYNC_FETCH_OP(and, 1, uint8_t)
  213. SYNC_FETCH_OP(and, 2, uint16_t)
  214. SYNC_FETCH_OP(and, 4, uint32_t)
  215. SYNC_FETCH_OP(or, 1, uint8_t)
  216. SYNC_FETCH_OP(or, 2, uint16_t)
  217. SYNC_FETCH_OP(or, 4, uint32_t)
  218. SYNC_FETCH_OP(xor, 1, uint8_t)
  219. SYNC_FETCH_OP(xor, 2, uint16_t)
  220. SYNC_FETCH_OP(xor, 4, uint32_t)
  221. SYNC_BOOL_CMP_EXCHANGE(1, uint8_t)
  222. SYNC_BOOL_CMP_EXCHANGE(2, uint16_t)
  223. SYNC_BOOL_CMP_EXCHANGE(4, uint32_t)
  224. SYNC_VAL_CMP_EXCHANGE(1, uint8_t)
  225. SYNC_VAL_CMP_EXCHANGE(2, uint16_t)
  226. SYNC_VAL_CMP_EXCHANGE(4, uint32_t)
  227. SYNC_LOCK_TEST_AND_SET(1, uint8_t)
  228. SYNC_LOCK_TEST_AND_SET(2, uint16_t)
  229. SYNC_LOCK_TEST_AND_SET(4, uint32_t)
  230. SYNC_LOCK_RELEASE(1, uint8_t)
  231. SYNC_LOCK_RELEASE(2, uint16_t)
  232. SYNC_LOCK_RELEASE(4, uint32_t)
  233. // LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension. LLVM thread: https://reviews.llvm.org/D47553.
  234. // Even though GCC does transform them, these libcalls need to be available for the case where a LLVM based project links against IDF.
  235. ATOMIC_LOAD(1, uint8_t)
  236. ATOMIC_LOAD(2, uint16_t)
  237. ATOMIC_LOAD(4, uint32_t)
  238. ATOMIC_STORE(1, uint8_t)
  239. ATOMIC_STORE(2, uint16_t)
  240. ATOMIC_STORE(4, uint32_t)
  241. #endif // !HAS_ATOMICS_32
  242. #if !HAS_ATOMICS_64
  243. ATOMIC_EXCHANGE(8, uint64_t)
  244. CMP_EXCHANGE(8, uint64_t)
  245. FETCH_ADD(8, uint64_t)
  246. FETCH_SUB(8, uint64_t)
  247. FETCH_AND(8, uint64_t)
  248. FETCH_OR(8, uint64_t)
  249. FETCH_XOR(8, uint64_t)
  250. SYNC_FETCH_OP(add, 8, uint64_t)
  251. SYNC_FETCH_OP(sub, 8, uint64_t)
  252. SYNC_FETCH_OP(and, 8, uint64_t)
  253. SYNC_FETCH_OP(or, 8, uint64_t)
  254. SYNC_FETCH_OP(xor, 8, uint64_t)
  255. SYNC_BOOL_CMP_EXCHANGE(8, uint64_t)
  256. SYNC_VAL_CMP_EXCHANGE(8, uint64_t)
  257. SYNC_LOCK_TEST_AND_SET(8, uint64_t)
  258. SYNC_LOCK_RELEASE(8, uint64_t)
  259. // LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension. LLVM thread: https://reviews.llvm.org/D47553.
  260. // Even though GCC does transform them, these libcalls need to be available for the case where a LLVM based project links against IDF.
  261. ATOMIC_LOAD(8, uint64_t)
  262. ATOMIC_STORE(8, uint64_t)
  263. #endif // !HAS_ATOMICS_64