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