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 
21 typedef  unsigned long int  UWord;
22 
23 #if defined(VGA_ppc64be) || defined(VGA_ppc64le)
24 
25 // ppc64
26 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)27 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)60 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)93 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)116 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)141 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)174 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)207 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)226 UWord 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 */
do_acasW(UWord * addr,UWord expected,UWord nyu)259 UWord 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 #elif defined(VGA_tilegx)
289 
290 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)291 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
292 {
293   /* Load the compare value into special register 0x2780 */
294   __insn_mtspr(0x2780, expected);
295   return __insn_cmpexch(addr, nyu);
296 }
297 
298 #endif
299 
atomic_incW(UWord * w)300 void atomic_incW ( UWord* w )
301 {
302    while (1) {
303       UWord old = *w;
304       UWord nyu = old + 1;
305       UWord ok = do_acasW( w, old, nyu );
306       if (ok) break;
307    };
308 }
309 
310 #if 0
311 
312 #define NNN 1000000
313 
314 void* thread_fn ( void* arg )
315 {
316   UWord* w = (UWord*)arg;
317   int i;
318   for (i = 0; i < NNN; i++)
319      atomic_incW( w );
320   return NULL;
321 }
322 
323 
324 int main ( void )
325 {
326    int r;
327   //ANNOTATE_HAPPENS_BEFORE(0);
328   //return 0;
329    UWord w = 0;
330   pthread_t t1, t2;
331 
332   r= pthread_create( &t1, NULL, &thread_fn, (void*)&w );   assert(!r);
333   r= pthread_create( &t2, NULL, &thread_fn, (void*)&w );   assert(!r);
334 
335   r= pthread_join( t1, NULL );   assert(!r);
336   r= pthread_join( t2, NULL );   assert(!r);
337 
338   printf("result = %lu\n", w );
339   return 0;
340 }
341 
342 #endif
343 
344 int shared_var = 0;  // is not raced upon
345 
346 
delayXms(int i)347 void delayXms ( int i )
348 {
349    struct timespec ts = { 0, 1 * 1000 * 1000 };
350    // We do the sleep in small pieces to have scheduling
351    // events ensuring a fair switch between threads, even
352    // without --fair-sched=yes. This is a.o. needed for
353    // running this test under an outer helgrind or an outer
354    // sgcheck.
355    while (i > 0) {
356       nanosleep(&ts, NULL);
357       i--;
358    }
359 }
360 
do_wait(UWord * w)361 void do_wait ( UWord* w )
362 {
363   UWord w0 = *w;
364   UWord volatile * wV = w;
365   while (*wV == w0)
366     delayXms(1); // small sleeps, ensuring context switches
367   ANNOTATE_HAPPENS_AFTER(w);
368 }
369 
do_signal(UWord * w)370 void do_signal ( UWord* w )
371 {
372   ANNOTATE_HAPPENS_BEFORE(w);
373   atomic_incW(w);
374 }
375 
376 
377 
thread_fn1(void * arg)378 void* thread_fn1 ( void* arg )
379 {
380   UWord* w = (UWord*)arg;
381   delayXms(500);    // ensure t2 gets to its wait first
382   shared_var = 1;  // first access
383   do_signal(w);    // cause h-b edge to second thread
384 
385   delayXms(500);
386   return NULL;
387 }
388 
thread_fn2(void * arg)389 void* thread_fn2 ( void* arg )
390 {
391   UWord* w = (UWord*)arg;
392   do_wait(w);      // wait for h-b edge from first thread
393   shared_var = 2;  // second access
394 
395   delayXms(500);
396   return NULL;
397 }
398 
399 
400 
401 
402 
403 
main(void)404 int main ( void )
405 {
406    int r;
407    UWord w = 0;
408    pthread_t t1, t2;
409 
410    r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w );   assert(!r);
411    r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w );   assert(!r);
412 
413    r= pthread_join( t1, NULL );   assert(!r);
414    r= pthread_join( t2, NULL );   assert(!r);
415    return 0;
416 }
417