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