1#!/usr/bin/env bash 2 3#does the toolchain need -latomic to support dword CAS? 4 5 6# repeat the HAS_MCX16 logic 7${CC:-cc} -x c -std=c11 -mcx16 -o /dev/null - &>/dev/null <<EOT 8int main(void) { 9 return 0; 10} 11EOT 12if [ $? -eq 0 ]; then 13 MCX16=-mcx16 14else 15 MCX16= 16fi 17 18# compile a program that operates on a double-word 19${CC:-cc} -x c -std=c11 ${MCX16} -o /dev/null - &>/dev/null <<EOT 20#include <stdbool.h> 21#include <stdint.h> 22 23// replicate what is in ../../rumur/resources/header.c 24 25#define THREADS 2 26 27#if __SIZEOF_POINTER__ <= 4 28 typedef uint64_t dword_t; 29#elif __SIZEOF_POINTER__ <= 8 30 typedef unsigned __int128 dword_t; 31#else 32 #error "unexpected pointer size; what scalar type to use for dword_t?" 33#endif 34 35static dword_t atomic_read(dword_t *p) { 36 37 if (THREADS == 1) { 38 return *p; 39 } 40 41#if defined(__x86_64__) || defined(__i386__) 42 /* x86-64: MOV is not guaranteed to be atomic on 128-bit naturally aligned 43 * memory. The way to work around this is apparently the following 44 * degenerate CMPXCHG16B. 45 * i386: __atomic_load_n emits code calling a libatomic function that takes a 46 * lock, making this no longer lock free. Force a CMPXCHG8B by using the 47 * __sync built-in instead. 48 */ 49 return __sync_val_compare_and_swap(p, 0, 0); 50#endif 51 52 return __atomic_load_n(p, __ATOMIC_SEQ_CST); 53} 54 55static void atomic_write(dword_t *p, dword_t v) { 56 57 if (THREADS == 1) { 58 *p = v; 59 return; 60 } 61 62#if defined(__x86_64__) || defined(__i386__) 63 /* As explained above, we need some extra gymnastics to avoid a call to 64 * libatomic on x86-64 and i386. 65 */ 66 dword_t expected; 67 dword_t old = 0; 68 do { 69 expected = old; 70 old = __sync_val_compare_and_swap(p, expected, v); 71 } while (expected != old); 72 return; 73#endif 74 75 __atomic_store_n(p, v, __ATOMIC_SEQ_CST); 76} 77 78static bool atomic_cas(dword_t *p, dword_t expected, dword_t new) { 79 80 if (THREADS == 1) { 81 if (*p == expected) { 82 *p = new; 83 return true; 84 } 85 return false; 86 } 87 88#if defined(__x86_64__) || defined(__i386__) 89 /* Make GCC >= 7.1 emit cmpxchg on x86-64 and i386. See 90 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878. 91 */ 92 return __sync_bool_compare_and_swap(p, expected, new); 93#endif 94 95 return __atomic_compare_exchange_n(p, &expected, new, false, __ATOMIC_SEQ_CST, 96 __ATOMIC_SEQ_CST); 97} 98 99static dword_t atomic_cas_val(dword_t *p, dword_t expected, dword_t new) { 100 101 if (THREADS == 1) { 102 dword_t old = *p; 103 if (old == expected) { 104 *p = new; 105 } 106 return old; 107 } 108 109#if defined(__x86_64__) || defined(__i386__) 110 /* Make GCC >= 7.1 emit cmpxchg on x86-64 and i386. See 111 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878. 112 */ 113 return __sync_val_compare_and_swap(p, expected, new); 114#endif 115 116 117 (void)__atomic_compare_exchange_n(p, &expected, new, false, __ATOMIC_SEQ_CST, 118 __ATOMIC_SEQ_CST); 119 return expected; 120} 121 122int main(void) { 123 dword_t target = 0; 124 125 target = atomic_read(&target); 126 127 atomic_write(&target, 42); 128 129 atomic_cas(&target, 42, 0); 130 131 return (int)atomic_cas_val(&target, 0, 42); 132} 133EOT 134 135# see if the compiler errored 136if [ $? -eq 0 ]; then 137 printf 'False\n' 138else 139 printf 'True\n' 140fi 141