| 1 | #pragma once |
| 2 | |
| 3 | /* |
| 4 | * Most of this header is a giant workaround for MSVC to make atomics into a |
| 5 | * somewhat unified interface with how GCC and Clang handle them. |
| 6 | * |
| 7 | * We don't use the absolutely disgusting C11 stdatomic.h header because it is |
| 8 | * unable to operate on non _Atomic types, which enforce implicit sequential |
| 9 | * consistency and alter the behavior of the standard C binary/unary operators. |
| 10 | * |
| 11 | * The strictness of the atomic helpers defined here is assumed to be at least |
| 12 | * acquire for loads and release for stores. Cmpxchg uses the standard acq/rel |
| 13 | * for success, acq for failure, and is assumed to be strong. |
| 14 | */ |
| 15 | |
| 16 | #ifdef UACPI_OVERRIDE_ATOMIC |
| 17 | #include "uacpi_atomic.h" |
| 18 | #else |
| 19 | |
| 20 | #include <uacpi/platform/compiler.h> |
| 21 | |
| 22 | #if defined(_MSC_VER) && !defined(__clang__) |
| 23 | |
| 24 | #include <intrin.h> |
| 25 | |
| 26 | // mimic __atomic_compare_exchange_n that doesn't exist on MSVC |
| 27 | #define UACPI_MAKE_MSVC_CMPXCHG(width, type, suffix) \ |
| 28 | static inline int uacpi_do_atomic_cmpxchg##width( \ |
| 29 | type volatile *ptr, type volatile *expected, type desired \ |
| 30 | ) \ |
| 31 | { \ |
| 32 | type current; \ |
| 33 | \ |
| 34 | current = _InterlockedCompareExchange##suffix(ptr, *expected, desired); \ |
| 35 | if (current != *expected) { \ |
| 36 | *expected = current; \ |
| 37 | return 0; \ |
| 38 | } \ |
| 39 | return 1; \ |
| 40 | } |
| 41 | |
| 42 | #define UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, width, type) \ |
| 43 | uacpi_do_atomic_cmpxchg##width( \ |
| 44 | (type volatile*)ptr, (type volatile*)expected, desired \ |
| 45 | ) |
| 46 | |
| 47 | #define UACPI_MSVC_ATOMIC_STORE(ptr, value, type, width) \ |
| 48 | _InterlockedExchange##width((type volatile*)(ptr), (type)(value)) |
| 49 | |
| 50 | #define UACPI_MSVC_ATOMIC_LOAD(ptr, type, width) \ |
| 51 | _InterlockedOr##width((type volatile*)(ptr), 0) |
| 52 | |
| 53 | #define UACPI_MSVC_ATOMIC_INC(ptr, type, width) \ |
| 54 | _InterlockedIncrement##width((type volatile*)(ptr)) |
| 55 | |
| 56 | #define UACPI_MSVC_ATOMIC_DEC(ptr, type, width) \ |
| 57 | _InterlockedDecrement##width((type volatile*)(ptr)) |
| 58 | |
| 59 | UACPI_MAKE_MSVC_CMPXCHG(64, __int64, 64) |
| 60 | UACPI_MAKE_MSVC_CMPXCHG(32, long,) |
| 61 | UACPI_MAKE_MSVC_CMPXCHG(16, short, 16) |
| 62 | |
| 63 | #define uacpi_atomic_cmpxchg16(ptr, expected, desired) \ |
| 64 | UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 16, short) |
| 65 | |
| 66 | #define uacpi_atomic_cmpxchg32(ptr, expected, desired) \ |
| 67 | UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 32, long) |
| 68 | |
| 69 | #define uacpi_atomic_cmpxchg64(ptr, expected, desired) \ |
| 70 | UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 64, __int64) |
| 71 | |
| 72 | #define uacpi_atomic_load8(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, char, 8) |
| 73 | #define uacpi_atomic_load16(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, short, 16) |
| 74 | #define uacpi_atomic_load32(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, long,) |
| 75 | #define uacpi_atomic_load64(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, __int64, 64) |
| 76 | |
| 77 | #define uacpi_atomic_store8(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, char, 8) |
| 78 | #define uacpi_atomic_store16(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, short, 16) |
| 79 | #define uacpi_atomic_store32(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, long,) |
| 80 | #define uacpi_atomic_store64(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, __int64, 64) |
| 81 | |
| 82 | #define uacpi_atomic_inc16(ptr) UACPI_MSVC_ATOMIC_INC(ptr, short, 16) |
| 83 | #define uacpi_atomic_inc32(ptr) UACPI_MSVC_ATOMIC_INC(ptr, long,) |
| 84 | #define uacpi_atomic_inc64(ptr) UACPI_MSVC_ATOMIC_INC(ptr, __int64, 64) |
| 85 | |
| 86 | #define uacpi_atomic_dec16(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, short, 16) |
| 87 | #define uacpi_atomic_dec32(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, long,) |
| 88 | #define uacpi_atomic_dec64(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, __int64, 64) |
| 89 | #elif defined(__WATCOMC__) |
| 90 | |
| 91 | #include <stdint.h> |
| 92 | |
| 93 | static int uacpi_do_atomic_cmpxchg16(volatile uint16_t *ptr, volatile uint16_t *expected, uint16_t desired); |
| 94 | #pragma aux uacpi_do_atomic_cmpxchg16 = \ |
| 95 | ".486" \ |
| 96 | "mov ax, [esi]" \ |
| 97 | "lock cmpxchg [edi], bx" \ |
| 98 | "mov [esi], ax" \ |
| 99 | "setz al" \ |
| 100 | "movzx eax, al" \ |
| 101 | parm [ edi ] [ esi ] [ ebx ] \ |
| 102 | value [ eax ] |
| 103 | |
| 104 | static int uacpi_do_atomic_cmpxchg32(volatile uint32_t *ptr, volatile uint32_t *expected, uint32_t desired); |
| 105 | #pragma aux uacpi_do_atomic_cmpxchg32 = \ |
| 106 | ".486" \ |
| 107 | "mov eax, [esi]" \ |
| 108 | "lock cmpxchg [edi], ebx" \ |
| 109 | "mov [esi], eax" \ |
| 110 | "setz al" \ |
| 111 | "movzx eax, al" \ |
| 112 | parm [ edi ] [ esi ] [ ebx ] \ |
| 113 | value [ eax ] |
| 114 | |
| 115 | static int uacpi_do_atomic_cmpxchg64_asm(volatile uint64_t *ptr, volatile uint64_t *expected, uint32_t low, uint32_t high); |
| 116 | #pragma aux uacpi_do_atomic_cmpxchg64_asm = \ |
| 117 | ".586" \ |
| 118 | "mov eax, [esi]" \ |
| 119 | "mov edx, [esi + 4]" \ |
| 120 | "lock cmpxchg8b [edi]" \ |
| 121 | "mov [esi], eax" \ |
| 122 | "mov [esi + 4], edx" \ |
| 123 | "setz al" \ |
| 124 | "movzx eax, al" \ |
| 125 | modify [ edx ] \ |
| 126 | parm [ edi ] [ esi ] [ ebx ] [ ecx ] \ |
| 127 | value [ eax ] |
| 128 | |
| 129 | static inline int uacpi_do_atomic_cmpxchg64(volatile uint64_t *ptr, volatile uint64_t *expected, uint64_t desired) { |
| 130 | return uacpi_do_atomic_cmpxchg64_asm(ptr, expected, desired, desired >> 32); |
| 131 | } |
| 132 | |
| 133 | #define uacpi_atomic_cmpxchg16(ptr, expected, desired) \ |
| 134 | uacpi_do_atomic_cmpxchg16((volatile uint16_t*)ptr, (volatile uint16_t*)expected, (uint16_t)desired) |
| 135 | #define uacpi_atomic_cmpxchg32(ptr, expected, desired) \ |
| 136 | uacpi_do_atomic_cmpxchg32((volatile uint32_t*)ptr, (volatile uint32_t*)expected, (uint32_t)desired) |
| 137 | #define uacpi_atomic_cmpxchg64(ptr, expected, desired) \ |
| 138 | uacpi_do_atomic_cmpxchg64((volatile uint64_t*)ptr, (volatile uint64_t*)expected, (uint64_t)desired) |
| 139 | |
| 140 | static uint8_t uacpi_do_atomic_load8(volatile uint8_t *ptr); |
| 141 | #pragma aux uacpi_do_atomic_load8 = \ |
| 142 | "mov al, [esi]" \ |
| 143 | parm [ esi ] \ |
| 144 | value [ al ] |
| 145 | |
| 146 | static uint16_t uacpi_do_atomic_load16(volatile uint16_t *ptr); |
| 147 | #pragma aux uacpi_do_atomic_load16 = \ |
| 148 | "mov ax, [esi]" \ |
| 149 | parm [ esi ] \ |
| 150 | value [ ax ] |
| 151 | |
| 152 | static uint32_t uacpi_do_atomic_load32(volatile uint32_t *ptr); |
| 153 | #pragma aux uacpi_do_atomic_load32 = \ |
| 154 | "mov eax, [esi]" \ |
| 155 | parm [ esi ] \ |
| 156 | value [ eax ] |
| 157 | |
| 158 | static void uacpi_do_atomic_load64_asm(volatile uint64_t *ptr, uint64_t *out); |
| 159 | #pragma aux uacpi_do_atomic_load64_asm = \ |
| 160 | ".586" \ |
| 161 | "xor eax, eax" \ |
| 162 | "xor ebx, ebx" \ |
| 163 | "xor ecx, ecx" \ |
| 164 | "xor edx, edx" \ |
| 165 | "lock cmpxchg8b [esi]" \ |
| 166 | "mov [edi], eax" \ |
| 167 | "mov [edi + 4], edx" \ |
| 168 | modify [ eax ebx ecx edx ] \ |
| 169 | parm [ esi ] [ edi ] |
| 170 | |
| 171 | static inline uint64_t uacpi_do_atomic_load64(volatile uint64_t *ptr) { |
| 172 | uint64_t value; |
| 173 | uacpi_do_atomic_load64_asm(ptr, &value); |
| 174 | return value; |
| 175 | } |
| 176 | |
| 177 | #define uacpi_atomic_load8(ptr) uacpi_do_atomic_load8((volatile uint8_t*)ptr) |
| 178 | #define uacpi_atomic_load16(ptr) uacpi_do_atomic_load16((volatile uint16_t*)ptr) |
| 179 | #define uacpi_atomic_load32(ptr) uacpi_do_atomic_load32((volatile uint32_t*)ptr) |
| 180 | #define uacpi_atomic_load64(ptr) uacpi_do_atomic_load64((volatile uint64_t*)ptr) |
| 181 | |
| 182 | static void uacpi_do_atomic_store8(volatile uint8_t *ptr, uint8_t value); |
| 183 | #pragma aux uacpi_do_atomic_store8 = \ |
| 184 | "mov [edi], al" \ |
| 185 | parm [ edi ] [ eax ] |
| 186 | |
| 187 | static void uacpi_do_atomic_store16(volatile uint16_t *ptr, uint16_t value); |
| 188 | #pragma aux uacpi_do_atomic_store16 = \ |
| 189 | "mov [edi], ax" \ |
| 190 | parm [ edi ] [ eax ] |
| 191 | |
| 192 | static void uacpi_do_atomic_store32(volatile uint32_t *ptr, uint32_t value); |
| 193 | #pragma aux uacpi_do_atomic_store32 = \ |
| 194 | "mov [edi], eax" \ |
| 195 | parm [ edi ] [ eax ] |
| 196 | |
| 197 | static void uacpi_do_atomic_store64_asm(volatile uint64_t *ptr, uint32_t low, uint32_t high); |
| 198 | #pragma aux uacpi_do_atomic_store64_asm = \ |
| 199 | ".586" \ |
| 200 | "xor eax, eax" \ |
| 201 | "xor edx, edx" \ |
| 202 | "retry: lock cmpxchg8b [edi]" \ |
| 203 | "jnz retry" \ |
| 204 | modify [ eax edx ] \ |
| 205 | parm [ edi ] [ ebx ] [ ecx ] |
| 206 | |
| 207 | static inline void uacpi_do_atomic_store64(volatile uint64_t *ptr, uint64_t value) { |
| 208 | uacpi_do_atomic_store64_asm(ptr, value, value >> 32); |
| 209 | } |
| 210 | |
| 211 | #define uacpi_atomic_store8(ptr, value) uacpi_do_atomic_store8((volatile uint8_t*)ptr, (uint8_t)value) |
| 212 | #define uacpi_atomic_store16(ptr, value) uacpi_do_atomic_store16((volatile uint16_t*)ptr, (uint16_t)value) |
| 213 | #define uacpi_atomic_store32(ptr, value) uacpi_do_atomic_store32((volatile uint32_t*)ptr, (uint32_t)value) |
| 214 | #define uacpi_atomic_store64(ptr, value) uacpi_do_atomic_store64((volatile uint64_t*)ptr, (uint64_t)value) |
| 215 | |
| 216 | static uint16_t uacpi_do_atomic_inc16(volatile uint16_t *ptr); |
| 217 | #pragma aux uacpi_do_atomic_inc16 = \ |
| 218 | ".486" \ |
| 219 | "mov ax, 1" \ |
| 220 | "lock xadd [edi], ax" \ |
| 221 | "add ax, 1" \ |
| 222 | parm [ edi ] \ |
| 223 | value [ ax ] |
| 224 | |
| 225 | static uint32_t uacpi_do_atomic_inc32(volatile uint32_t *ptr); |
| 226 | #pragma aux uacpi_do_atomic_inc32 = \ |
| 227 | ".486" \ |
| 228 | "mov eax, 1" \ |
| 229 | "lock xadd [edi], eax" \ |
| 230 | "add eax, 1" \ |
| 231 | parm [ edi ] \ |
| 232 | value [ eax ] |
| 233 | |
| 234 | static void uacpi_do_atomic_inc64_asm(volatile uint64_t *ptr, uint64_t *out); |
| 235 | #pragma aux uacpi_do_atomic_inc64_asm = \ |
| 236 | ".586" \ |
| 237 | "xor eax, eax" \ |
| 238 | "xor edx, edx" \ |
| 239 | "mov ebx, 1" \ |
| 240 | "mov ecx, 1" \ |
| 241 | "retry: lock cmpxchg8b [esi]" \ |
| 242 | "mov ebx, eax" \ |
| 243 | "mov ecx, edx" \ |
| 244 | "add ebx, 1" \ |
| 245 | "adc ecx, 0" \ |
| 246 | "jnz retry" \ |
| 247 | "mov [edi], ebx" \ |
| 248 | "mov [edi + 4], ecx" \ |
| 249 | modify [ eax ebx ecx edx ] \ |
| 250 | parm [ esi ] [ edi ] |
| 251 | |
| 252 | static inline uint64_t uacpi_do_atomic_inc64(volatile uint64_t *ptr) { |
| 253 | uint64_t value; |
| 254 | uacpi_do_atomic_inc64_asm(ptr, &value); |
| 255 | return value; |
| 256 | } |
| 257 | |
| 258 | #define uacpi_atomic_inc16(ptr) uacpi_do_atomic_inc16((volatile uint16_t*)ptr) |
| 259 | #define uacpi_atomic_inc32(ptr) uacpi_do_atomic_inc32((volatile uint32_t*)ptr) |
| 260 | #define uacpi_atomic_inc64(ptr) uacpi_do_atomic_inc64((volatile uint64_t*)ptr) |
| 261 | |
| 262 | static uint16_t uacpi_do_atomic_dec16(volatile uint16_t *ptr); |
| 263 | #pragma aux uacpi_do_atomic_dec16 = \ |
| 264 | ".486" \ |
| 265 | "mov ax, -1" \ |
| 266 | "lock xadd [edi], ax" \ |
| 267 | "add ax, -1" \ |
| 268 | parm [ edi ] \ |
| 269 | value [ ax ] |
| 270 | |
| 271 | static uint32_t uacpi_do_atomic_dec32(volatile uint32_t *ptr); |
| 272 | #pragma aux uacpi_do_atomic_dec32 = \ |
| 273 | ".486" \ |
| 274 | "mov eax, -1" \ |
| 275 | "lock xadd [edi], eax" \ |
| 276 | "add eax, -1" \ |
| 277 | parm [ edi ] \ |
| 278 | value [ eax ] |
| 279 | |
| 280 | static void uacpi_do_atomic_dec64_asm(volatile uint64_t *ptr, uint64_t *out); |
| 281 | #pragma aux uacpi_do_atomic_dec64_asm = \ |
| 282 | ".586" \ |
| 283 | "xor eax, eax" \ |
| 284 | "xor edx, edx" \ |
| 285 | "mov ebx, -1" \ |
| 286 | "mov ecx, -1" \ |
| 287 | "retry: lock cmpxchg8b [esi]" \ |
| 288 | "mov ebx, eax" \ |
| 289 | "mov ecx, edx" \ |
| 290 | "sub ebx, 1" \ |
| 291 | "sbb ecx, 0" \ |
| 292 | "jnz retry" \ |
| 293 | "mov [edi], ebx" \ |
| 294 | "mov [edi + 4], ecx" \ |
| 295 | modify [ eax ebx ecx edx ] \ |
| 296 | parm [ esi ] [ edi ] |
| 297 | |
| 298 | static inline uint64_t uacpi_do_atomic_dec64(volatile uint64_t *ptr) { |
| 299 | uint64_t value; |
| 300 | uacpi_do_atomic_dec64_asm(ptr, &value); |
| 301 | return value; |
| 302 | } |
| 303 | |
| 304 | #define uacpi_atomic_dec16(ptr) uacpi_do_atomic_dec16((volatile uint16_t*)ptr) |
| 305 | #define uacpi_atomic_dec32(ptr) uacpi_do_atomic_dec32((volatile uint32_t*)ptr) |
| 306 | #define uacpi_atomic_dec64(ptr) uacpi_do_atomic_dec64((volatile uint64_t*)ptr) |
| 307 | #else |
| 308 | |
| 309 | #define UACPI_DO_CMPXCHG(ptr, expected, desired) \ |
| 310 | __atomic_compare_exchange_n(ptr, expected, desired, 0, \ |
| 311 | __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) |
| 312 | |
| 313 | #define uacpi_atomic_cmpxchg16(ptr, expected, desired) \ |
| 314 | UACPI_DO_CMPXCHG(ptr, expected, desired) |
| 315 | #define uacpi_atomic_cmpxchg32(ptr, expected, desired) \ |
| 316 | UACPI_DO_CMPXCHG(ptr, expected, desired) |
| 317 | #define uacpi_atomic_cmpxchg64(ptr, expected, desired) \ |
| 318 | UACPI_DO_CMPXCHG(ptr, expected, desired) |
| 319 | |
| 320 | #define uacpi_atomic_load8(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) |
| 321 | #define uacpi_atomic_load16(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) |
| 322 | #define uacpi_atomic_load32(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) |
| 323 | #define uacpi_atomic_load64(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) |
| 324 | |
| 325 | #define uacpi_atomic_store8(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE) |
| 326 | #define uacpi_atomic_store16(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE) |
| 327 | #define uacpi_atomic_store32(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE) |
| 328 | #define uacpi_atomic_store64(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE) |
| 329 | |
| 330 | #define uacpi_atomic_inc16(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 331 | #define uacpi_atomic_inc32(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 332 | #define uacpi_atomic_inc64(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 333 | |
| 334 | #define uacpi_atomic_dec16(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 335 | #define uacpi_atomic_dec32(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 336 | #define uacpi_atomic_dec64(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL) |
| 337 | #endif |
| 338 | |
| 339 | #if UACPI_POINTER_SIZE == 4 |
| 340 | #define uacpi_atomic_load_ptr(ptr_to_ptr) uacpi_atomic_load32(ptr_to_ptr) |
| 341 | #define uacpi_atomic_store_ptr(ptr_to_ptr, value) uacpi_atomic_store32(ptr_to_ptr, value) |
| 342 | #else |
| 343 | #define uacpi_atomic_load_ptr(ptr_to_ptr) uacpi_atomic_load64(ptr_to_ptr) |
| 344 | #define uacpi_atomic_store_ptr(ptr_to_ptr, value) uacpi_atomic_store64(ptr_to_ptr, value) |
| 345 | #endif |
| 346 | |
| 347 | #endif |
| 348 | |