1
2/* Program which uses a happens-before edge to coordinate an access to
3   variable 'shared_var' between two threads.  The h-b edge is created
4   by a custom (kludgesome!) mechanism and hence we need to use
5   ANNOTATES_HAPPEN_{BEFORE,AFTER} to explain to Helgrind what's going
6   on (else it reports a race). */
7
8#include <pthread.h>
9#include <stdio.h>
10#include <assert.h>
11
12#include "../../helgrind/helgrind.h"
13
14/* Todo: move all this do_acasW guff into a support library.  It's
15   useful for multiple tests, not just this one.
16
17   XXX: all the do_acasW routines assume the supplied address
18   is UWord (naturally) aligned. */
19
20
21typedef  unsigned long int  UWord;
22
23#if defined(VGA_ppc64)
24
25// ppc64
26/* return 1 if success, 0 if failure */
27UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
28{
29  UWord old, success;
30
31  /* Fetch the old value, and set the reservation */
32  __asm__ __volatile__ (
33     "ldarx  %0, 0,%1"     "\n"  // rD,rA,rB
34      : /*out*/   "=b"(old)
35      : /*in*/    "b"(addr)
36      : /*trash*/ "memory","cc"
37   );
38
39   /* If the old value isn't as expected, we've had it */
40   if (old != expected) return 0;
41
42   /* otherwise try to stuff the new value in */
43   __asm__ __volatile__(
44      "stdcx. %2, 0,%1"   "\n"      // rS,rA,rB
45      "mfcr   %0"         "\n\t"
46      "srdi   %0,%0,29"   "\n\t"
47      "andi.  %0,%0,1"    "\n"
48      : /*out*/ "=b"(success)
49      : /*in*/ "b"(addr), "b"(nyu)
50   );
51
52   assert(success == 0 || success == 1);
53   return success;
54}
55
56#elif defined(VGA_ppc32)
57
58// ppc32
59/* return 1 if success, 0 if failure */
60UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
61{
62  UWord old, success;
63
64  /* Fetch the old value, and set the reservation */
65  __asm__ __volatile__ (
66     "lwarx  %0, 0,%1"     "\n"  // rD,rA,rB
67      : /*out*/   "=b"(old)
68      : /*in*/    "b"(addr)
69      : /*trash*/ "memory","cc"
70   );
71
72   /* If the old value isn't as expected, we've had it */
73   if (old != expected) return 0;
74
75   /* otherwise try to stuff the new value in */
76   __asm__ __volatile__(
77      "stwcx. %2, 0,%1"   "\n"      // rS,rA,rB
78      "mfcr   %0"         "\n\t"
79      "srwi   %0,%0,29"   "\n\t"
80      "andi.  %0,%0,1"    "\n"
81      : /*out*/ "=b"(success)
82      : /*in*/ "b"(addr), "b"(nyu)
83   );
84
85   assert(success == 0 || success == 1);
86   return success;
87}
88
89#elif defined(VGA_amd64)
90
91// amd64
92/* return 1 if success, 0 if failure */
93UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
94{
95   UWord block[4] = { (UWord)addr, expected, nyu, 2 };
96   __asm__ __volatile__(
97      "movq 0(%%rsi),  %%rdi"         "\n\t" // addr
98      "movq 8(%%rsi),  %%rax"         "\n\t" // expected
99      "movq 16(%%rsi), %%rbx"         "\n\t" // nyu
100      "xorq %%rcx,%%rcx"              "\n\t"
101      "lock; cmpxchgq %%rbx,(%%rdi)"  "\n\t"
102      "setz %%cl"                     "\n\t"
103      "movq %%rcx, 24(%%rsi)"         "\n"
104      : /*out*/
105      : /*in*/ "S"(&block[0])
106      : /*trash*/"memory","cc","rdi","rax","rbx","rcx"
107   );
108   assert(block[3] == 0 || block[3] == 1);
109   return block[3] & 1;
110}
111
112#elif defined(VGA_x86)
113
114// x86
115/* return 1 if success, 0 if failure */
116UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
117{
118   UWord block[4] = { (UWord)addr, expected, nyu, 2 };
119   __asm__ __volatile__(
120      "pushl %%ebx"                   "\n\t"
121      "movl 0(%%esi),  %%edi"         "\n\t" // addr
122      "movl 4(%%esi),  %%eax"         "\n\t" // expected
123      "movl 8(%%esi),  %%ebx"         "\n\t" // nyu
124      "xorl %%ecx,%%ecx"              "\n\t"
125      "lock; cmpxchgl %%ebx,(%%edi)"  "\n\t"
126      "setz %%cl"                     "\n\t"
127      "movl %%ecx, 12(%%esi)"         "\n\t"
128      "popl %%ebx"                    "\n"
129      : /*out*/
130      : /*in*/ "S"(&block[0])
131      : /*trash*/"memory","cc","edi","eax","ecx"
132   );
133   assert(block[3] == 0 || block[3] == 1);
134   return block[3] & 1;
135}
136
137#elif defined(VGA_arm)
138
139// arm
140/* return 1 if success, 0 if failure */
141UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
142{
143  UWord old, success;
144  UWord block[2] = { (UWord)addr, nyu };
145
146  /* Fetch the old value, and set the reservation */
147  __asm__ __volatile__ (
148     "ldrex  %0, [%1]"    "\n"
149      : /*out*/   "=r"(old)
150      : /*in*/    "r"(addr)
151   );
152
153   /* If the old value isn't as expected, we've had it */
154   if (old != expected) return 0;
155
156   /* otherwise try to stuff the new value in */
157   __asm__ __volatile__(
158      "ldr    r4, [%1, #0]"      "\n\t"
159      "ldr    r5, [%1, #4]"      "\n\t"
160      "strex  r6, r5, [r4, #0]"  "\n\t"
161      "eor    %0, r6, #1"        "\n\t"
162      : /*out*/ "=r"(success)
163      : /*in*/ "r"(&block[0])
164      : /*trash*/ "r4","r5","r6","memory"
165   );
166   assert(success == 0 || success == 1);
167   return success;
168}
169
170#elif defined(VGA_arm64)
171
172// arm64
173/* return 1 if success, 0 if failure */
174UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
175{
176  UWord old, success;
177  UWord block[2] = { (UWord)addr, nyu };
178
179  /* Fetch the old value, and set the reservation */
180  __asm__ __volatile__ (
181     "ldxr  %0, [%1]"    "\n"
182      : /*out*/   "=r"(old)
183      : /*in*/    "r"(addr)
184   );
185
186   /* If the old value isn't as expected, we've had it */
187   if (old != expected) return 0;
188
189   /* otherwise try to stuff the new value in */
190   __asm__ __volatile__(
191      "ldr    x4, [%1, #0]"      "\n\t"
192      "ldr    x5, [%1, #8]"      "\n\t"
193      "stxr   w6, x5, [x4, #0]"  "\n\t"
194      "eor    %0, x6, #1"        "\n\t"
195      : /*out*/ "=r"(success)
196      : /*in*/ "r"(&block[0])
197      : /*trash*/ "x4","x5","x6","memory"
198   );
199   assert(success == 0 || success == 1);
200   return success;
201}
202
203#elif defined(VGA_s390x)
204
205// s390x
206/* return 1 if success, 0 if failure */
207UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
208{
209   int cc;
210
211   __asm__ __volatile__ (
212     "csg %2,%3,%1\n\t"
213     "ipm %0\n\t"
214     "srl %0,28\n\t"
215     : /* out */  "=r" (cc)
216     : /* in */ "Q" (*addr), "d" (expected), "d" (nyu)
217     : "memory", "cc"
218   );
219   return cc == 0;
220}
221
222#elif defined(VGA_mips32)
223
224// mips32
225/* return 1 if success, 0 if failure */
226UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
227{
228  UWord success;
229  UWord block[3] = { (UWord)addr, nyu, expected};
230
231   __asm__ __volatile__(
232      ".set noreorder"           "\n\t"
233      "lw     $t0, 0(%1)"        "\n\t"
234      "lw     $t2, 8(%1)"        "\n\t"
235      "lw     $t3, 4(%1)"        "\n\t"
236      "ll     $t1, 0($t0)"       "\n\t"
237      "bne    $t1, $t2, exit_0"  "\n\t"
238      "nop"                      "\n\t"
239      "sc     $t3, 0($t0)"       "\n\t"
240      "move   %0, $t3"           "\n\t"
241      "b exit"                   "\n\t"
242      "nop"                      "\n\t"
243      "exit_0:"                  "\n\t"
244      "move   %0, $zero"         "\n\t"
245      "exit:"                    "\n\t"
246      : /*out*/ "=r"(success)
247      : /*in*/ "r"(&block[0])
248      : /*trash*/ "t0", "t1", "t2", "t3", "memory"
249   );
250
251   assert(success == 0 || success == 1);
252   return success;
253}
254
255#elif defined(VGA_mips64)
256
257// mips64
258/* return 1 if success, 0 if failure */
259UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
260{
261  UWord success;
262  UWord block[3] = { (UWord)addr, nyu, expected};
263
264   __asm__ __volatile__(
265      ".set noreorder"           "\n\t"
266      "ld     $t0, 0(%1)"        "\n\t"
267      "ld     $t2, 16(%1)"       "\n\t"
268      "ld     $t3, 8(%1)"        "\n\t"
269      "ll     $t1, 0($t0)"       "\n\t"
270      "bne    $t1, $t2, exit_0"  "\n\t"
271      "nop"                      "\n\t"
272      "sc     $t3, 0($t0)"       "\n\t"
273      "move   %0, $t3"           "\n\t"
274      "b exit"                   "\n\t"
275      "nop"                      "\n\t"
276      "exit_0:"                  "\n\t"
277      "move   %0, $zero"         "\n\t"
278      "exit:"                    "\n\t"
279      : /*out*/ "=r"(success)
280      : /*in*/ "r"(&block[0])
281      : /*trash*/ "t0", "t1", "t2", "t3", "memory"
282   );
283
284   assert(success == 0 || success == 1);
285   return success;
286}
287
288#endif
289
290void atomic_incW ( UWord* w )
291{
292   while (1) {
293      UWord old = *w;
294      UWord nyu = old + 1;
295      UWord ok = do_acasW( w, old, nyu );
296      if (ok) break;
297   };
298}
299
300#if 0
301
302#define NNN 1000000
303
304void* thread_fn ( void* arg )
305{
306  UWord* w = (UWord*)arg;
307  int i;
308  for (i = 0; i < NNN; i++)
309     atomic_incW( w );
310  return NULL;
311}
312
313
314int main ( void )
315{
316   int r;
317  //ANNOTATE_HAPPENS_BEFORE(0);
318  //return 0;
319   UWord w = 0;
320  pthread_t t1, t2;
321
322  r= pthread_create( &t1, NULL, &thread_fn, (void*)&w );   assert(!r);
323  r= pthread_create( &t2, NULL, &thread_fn, (void*)&w );   assert(!r);
324
325  r= pthread_join( t1, NULL );   assert(!r);
326  r= pthread_join( t2, NULL );   assert(!r);
327
328  printf("result = %lu\n", w );
329  return 0;
330}
331
332#endif
333
334int shared_var = 0;  // is not raced upon
335
336
337void delayXms ( int i )
338{
339   struct timespec ts = { 0, 1 * 1000 * 1000 };
340   // We do the sleep in small pieces to have scheduling
341   // events ensuring a fair switch between threads, even
342   // without --fair-sched=yes. This is a.o. needed for
343   // running this test under an outer helgrind or an outer
344   // sgcheck.
345   while (i > 0) {
346      nanosleep(&ts, NULL);
347      i--;
348   }
349}
350
351void do_wait ( UWord* w )
352{
353  UWord w0 = *w;
354  UWord volatile * wV = w;
355  while (*wV == w0)
356    delayXms(1); // small sleeps, ensuring context switches
357  ANNOTATE_HAPPENS_AFTER(w);
358}
359
360void do_signal ( UWord* w )
361{
362  ANNOTATE_HAPPENS_BEFORE(w);
363  atomic_incW(w);
364}
365
366
367
368void* thread_fn1 ( void* arg )
369{
370  UWord* w = (UWord*)arg;
371  delayXms(500);    // ensure t2 gets to its wait first
372  shared_var = 1;  // first access
373  do_signal(w);    // cause h-b edge to second thread
374
375  delayXms(500);
376  return NULL;
377}
378
379void* thread_fn2 ( void* arg )
380{
381  UWord* w = (UWord*)arg;
382  do_wait(w);      // wait for h-b edge from first thread
383  shared_var = 2;  // second access
384
385  delayXms(500);
386  return NULL;
387}
388
389
390
391
392
393
394int main ( void )
395{
396   int r;
397   UWord w = 0;
398   pthread_t t1, t2;
399
400   r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w );   assert(!r);
401   r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w );   assert(!r);
402
403   r= pthread_join( t1, NULL );   assert(!r);
404   r= pthread_join( t2, NULL );   assert(!r);
405   return 0;
406}
407