1 
2 #include "config.h"
3 #include <pthread.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <assert.h>
7 
8 /* Simple test program, no race.  Parent and child both modify x and
9    use the hardware bus lock (implicitly, since XCHG r,m on x86/amd64
10    does not require an explicit LOCK prefix.). */
11 
12 #undef PLAT_x86_darwin
13 #undef PLAT_amd64_darwin
14 #undef PLAT_x86_linux
15 #undef PLAT_amd64_linux
16 #undef PLAT_ppc32_linux
17 #undef PLAT_ppc64be_linux
18 #undef PLAT_arm_linux
19 #undef PLAT_s390x_linux
20 #undef PLAT_mips32_linux
21 #undef PLAT_x86_solaris
22 #undef PLAT_amd64_solaris
23 
24 #if defined(__APPLE__) && defined(__i386__)
25 #  define PLAT_x86_darwin 1
26 #elif defined(__APPLE__) && defined(__x86_64__)
27 #  define PLAT_amd64_darwin 1
28 #elif defined(__linux__) && defined(__i386__)
29 #  define PLAT_x86_linux 1
30 #elif defined(__linux__) && defined(__x86_64__)
31 #  define PLAT_amd64_linux 1
32 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
33 #  define PLAT_ppc32_linux 1
34 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
35 #  define PLAT_ppc64_linux 1
36 #elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__)
37 #  define PLAT_arm_linux 1
38 #elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__)
39 #  define PLAT_arm64_linux 1
40 #elif defined(__linux__) && defined(__s390x__)
41 #  define PLAT_s390x_linux 1
42 #elif defined(__linux__) && defined(__mips__)
43 #  define PLAT_mips32_linux 1
44 #elif defined(__sun__) && defined(__i386__)
45 #  define PLAT_x86_solaris 1
46 #elif defined(__sun__) && defined(__x86_64__)
47 #  define PLAT_amd64_solaris 1
48 #endif
49 
50 
51 #if defined(PLAT_amd64_linux) || defined(PLAT_x86_linux) \
52     || defined(PLAT_amd64_darwin) || defined(PLAT_x86_darwin) \
53     || defined(PLAT_amd64_solaris) || defined(PLAT_x86_solaris)
54 #  define XCHG_M_R(_addr,_lval) \
55      __asm__ __volatile__( \
56         "xchgl %0, %1" \
57         : /*out*/ "+r"(_lval) \
58         : /*in*/  "m"(_addr) \
59         : "memory", "cc" \
60      )
61 #  define XCHG_M_R_with_redundant_LOCK(_addr,_lval) \
62      __asm__ __volatile__( \
63         "lock xchgl %0, %1" \
64         : /*out*/ "+r"(_lval) \
65         : /*in*/  "m"(_addr) \
66         : "memory", "cc" \
67      )
68 
69 #elif defined(PLAT_s390x_linux)
70 #  define XCHG_M_R(_addr,_lval)                              \
71      do {                                                    \
72         __asm__ __volatile__(                                \
73            "0: l   0,%[global]\n\t"                          \
74            "   cs  0,%[local],%[global]\n\t"                 \
75            "   bne 0b\n\t"                                   \
76            "   lr  %[local],0\n\t"                           \
77            : /*out*/ [global]"+m"(_addr), [local]"+d"(_lval) \
78            : /*in*/                                          \
79            : "0", "memory", "cc"                             \
80         );                                                   \
81      } while (0)
82 
83 #  define XCHG_M_R_with_redundant_LOCK(_addr,_lval) \
84       XCHG_M_R(_addr,_lval)
85 
86 #elif defined(PLAT_mips32_linux) || defined(PLAT_mips64_linux)
87 #  define XCHG_M_R(_addr,_lval)                              \
88      __asm__ __volatile__(                                   \
89         "move $12, %2\n"                                     \
90         "move $13, %1\n"                                     \
91         "ll $14, 0($13)\n"                                   \
92         "sc $12, 0($13)\n"                                   \
93         "move %0, $14\n"                                     \
94         : /*out*/ "=r"(_lval)                                \
95         : /*in*/  "r"(&_addr), "r"(_lval)                    \
96         : "$12", "$13", "$14", "memory", "cc"                \
97      )
98 
99 #  define XCHG_M_R_with_redundant_LOCK(_addr,_lval) \
100       XCHG_M_R(_addr,_lval)
101 
102 #elif defined(PLAT_ppc32_linux) || defined(PLAT_ppc64_linux) \
103       || defined(PLAT_arm_linux) || defined(PLAT_arm64_linux)
104 #  if defined(HAVE_BUILTIN_ATOMIC)
105 #    define XCHG_M_R(_addr,_lval)                                           \
106         do {                                                                \
107           int tmp;                                                          \
108           while ((tmp = *(int*)(& _addr)),                                  \
109                  ! __sync_bool_compare_and_swap((int*)&_addr, tmp, _lval))  \
110             ;                                                               \
111           _lval = tmp;                                                      \
112         } while (0)
113 #  else
114 #    warning "XCHG_M_R() implementation is missing. Either" \
115              "provide one or use a newer gcc version."
116 #    define XCHG_M_R(_addr,_lval) \
117         do { int tmp = *(int*)(& _addr); \
118              *(int*)(& _addr) = (_lval); \
119              _lval = tmp; \
120         } while (0)
121 #  endif
122 #  define XCHG_M_R_with_redundant_LOCK(_addr,_lval) \
123       XCHG_M_R(_addr,_lval)
124 
125 #else
126 #  error "Unsupported architecture"
127 
128 #endif
129 
130 int x = 0;
131 
132 void* child_fn ( void* arg )
133 {
134    int v = 12345;
135    XCHG_M_R_with_redundant_LOCK(x, v);
136    assert(v == 0 || v == 6789);
137    return NULL;
138 }
139 
140 int main ( void )
141 {
142    int v = 6789;
143    pthread_t child;
144 
145    if (pthread_create(&child, NULL, child_fn, NULL)) {
146       perror("pthread_create");
147       exit(1);
148    }
149 
150    XCHG_M_R(x, v);
151    assert(v == 0 || v == 12345);
152 
153    if (pthread_join(child, NULL)) {
154       perror("pthread join");
155       exit(1);
156    }
157 
158    if (v == 0 || v == 12345)
159       printf("success\n");
160    else
161       printf("failure\n");
162 
163    return v;
164 }
165