Here's a set of 8 tests which test the atomicity of all the >1 byte __sync_mem routines. I didn't see the point of testing char for atomicity. Maybe there isn't much point to short either, but maybe there is some weird byte-load/store target out there now or later :-P It doesn't hurt to have it.

There are tests for __sync_mem_load and then all the rest.

It uses the new gdb framework to verify that no invalid intermediate values are ever detected.

The load test basically writes a cycling bit-pattern value into an atomic variable after every instruction is executed. The atomic load should only ever fetch one of the valid values which are in this cycle. the load is executed within a loop which varies the cycle of the pattern to ensure we don't find resonance with the instruction execution sequence. (ie, alternating 2 values when it takes 2 instructions to get the second half of a value would not detect a failure)

The other test simply executes all the remaining __sync_mem routines in such a way that they alternate writing 0 and MAX into the atomic value. The value is checked after every instruction that it contains one value or the other.

As expected, the 16 byte tests fail and I have XFAILed them.

Oh, and I modified the control script such that once a failed result is detected, the test-case can be terminated.

Andrew

        * gcc.dg/memmodel/memmodel.gdb: Short-circuit tests once a failure 
        happens.
        * gcc.dg/memmodel/sync-other-{short,int,longlong,int128}.c: New. Test
        for proper atomic operation of all __sync_mem routines except load.
        * gcc.dg/memmodel/sync-load-{short,int,longlong,int128}.c: New. Test
        for proper atomic operation of __sync_mem_load.

Index: gcc.dg/memmodel/memmodel.gdb
===================================================================
*** gcc.dg/memmodel/memmodel.gdb        (revision 177853)
--- gcc.dg/memmodel/memmodel.gdb        (working copy)
*************** disp/i $pc
*** 4,15 ****
  run
  
  set $ret = 0
! while memmodel_fini != 1
    call memmodel_other_threads()
    stepi
    set $ret |= memmodel_step_verify()
  end
  
! set $ret |= memmodel_final_verify()
  continue
  quit $ret
--- 4,17 ----
  run
  
  set $ret = 0
! while (memmodel_fini != 1) && (! $ret)
    call memmodel_other_threads()
    stepi
    set $ret |= memmodel_step_verify()
  end
  
! if (! $ret)
!   set $ret |= memmodel_final_verify()
! end
  continue
  quit $ret
Index: gcc.dg/memmodel/sync-other-short.c
===================================================================
*** gcc.dg/memmodel/sync-other-short.c  (revision 0)
--- gcc.dg/memmodel/sync-other-short.c  (revision 0)
***************
*** 0 ****
--- 1,112 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_char_short } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ /* Test all the __sync routines for proper atomicity on 2 byte values.  */
+ 
+ unsigned short zero = 0;
+ unsigned short max = ~0;
+ 
+ unsigned short changing_value = 0;
+ unsigned short value = 0;
+ unsigned short ret;
+ 
+ void test_abort()
+ {
+   static int reported = 0;
+   if (!reported)
+     {
+       printf ("FAIL: improper execution of __sync builtin.\n");
+       reported = 1;
+     }
+ }
+ 
+ void memmodel_other_threads ()
+ {
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   if (value != zero && value != max)
+     {
+       printf ("FAIL: invalid intermediate result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   if (value != 0)
+     {
+       printf ("FAIL: invalid final result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ /* All values written to 'value' alternate between 'zero' and 'max'. Any other
+    value detected by memmodel_step_verify() between instructions would 
indicate
+    that the value was only partially written, and would thus fail this 
+    atomicity test.  
+ 
+    This function tests each different __sync_mem routine once, with the
+    exception of the load instruction which requires special testing.  */
+ main()
+ {
+   
+   ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST);
+   if (ret != zero || value != max)
+     test_abort();
+ 
+   __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST);
+   if (value != zero)
+     test_abort();
+ 
+   ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-other-int.c
===================================================================
*** gcc.dg/memmodel/sync-other-int.c    (revision 0)
--- gcc.dg/memmodel/sync-other-int.c    (revision 0)
***************
*** 0 ****
--- 1,112 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_int_long } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ /* Test all the __sync routines for proper atomicity on 4 byte values.  */
+ 
+ unsigned int zero = 0;
+ unsigned int max = ~0;
+ 
+ unsigned int changing_value = 0;
+ unsigned int value = 0;
+ unsigned int ret;
+ 
+ void test_abort()
+ {
+   static int reported = 0;
+   if (!reported)
+     {
+       printf ("FAIL: improper execution of __sync builtin.\n");
+       reported = 1;
+     }
+ }
+ 
+ void memmodel_other_threads ()
+ {
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   if (value != zero && value != max)
+     {
+       printf ("FAIL: invalid intermediate result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   if (value != 0)
+     {
+       printf ("FAIL: invalid final result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ /* All values written to 'value' alternate between 'zero' and 'max'. Any other
+    value detected by memmodel_step_verify() between instructions would 
indicate
+    that the value was only partially written, and would thus fail this 
+    atomicity test.  
+ 
+    This function tests each different __sync_mem routine once, with the
+    exception of the load instruction which requires special testing.  */
+ main()
+ {
+   
+   ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST);
+   if (ret != zero || value != max)
+     test_abort();
+ 
+   __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST);
+   if (value != zero)
+     test_abort();
+ 
+   ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-other-longlong.c
===================================================================
*** gcc.dg/memmodel/sync-other-longlong.c       (revision 0)
--- gcc.dg/memmodel/sync-other-longlong.c       (revision 0)
***************
*** 0 ****
--- 1,113 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_long_long } */
+ /* { dg-options "" } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ /* Test all the __sync routines for proper atomicity on 8 byte values.  */
+ 
+ unsigned long long zero = 0;
+ unsigned long long max = ~0;
+ 
+ unsigned long long changing_value = 0;
+ unsigned long long value = 0;
+ unsigned long long ret;
+ 
+ void test_abort()
+ {
+   static int reported = 0;
+   if (!reported)
+     {
+       printf ("FAIL: improper execution of __sync builtin.\n");
+       reported = 1;
+     }
+ }
+ 
+ void memmodel_other_threads ()
+ {
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   if (value != zero && value != max)
+     {
+       printf ("FAIL: invalid intermediate result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   if (value != 0)
+     {
+       printf ("FAIL: invalid final result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ /* All values written to 'value' alternate between 'zero' and 'max'. Any other
+    value detected by memmodel_step_verify() between instructions would 
indicate
+    that the value was only partially written, and would thus fail this 
+    atomicity test.  
+ 
+    This function tests each different __sync_mem routine once, with the
+    exception of the load instruction which requires special testing.  */
+ main()
+ {
+   
+   ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST);
+   if (ret != zero || value != max)
+     test_abort();
+ 
+   __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST);
+   if (value != zero)
+     test_abort();
+ 
+   ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-other-int128.c
===================================================================
*** gcc.dg/memmodel/sync-other-int128.c (revision 0)
--- gcc.dg/memmodel/sync-other-int128.c (revision 0)
***************
*** 0 ****
--- 1,114 ----
+ /* { dg-do link } */
+ 
+ /* { dg-require-effective-target sync_int_128 } */
+ /* { dg-options "-mcx16" { target { x86_64-*-* } } } */
+ 
+ /* { dg-final { memmodel-gdb-test { xfail *-*-* } } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ /* Test all the __sync routines for proper atomicity on 16 byte values.  */
+ 
+ __int128_t zero = 0;
+ __int128_t max = ~0;
+ __int128_t changing_value = 0;
+ __int128_t value = 0;
+ __int128_t ret;
+ 
+ void test_abort()
+ {
+   static int reported = 0;
+   if (!reported)
+     {
+       printf ("FAIL: improper execution of __sync builtin.\n");
+       reported = 1;
+     }
+ }
+ 
+ void memmodel_other_threads ()
+ {
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   if (value != zero && value != max)
+     {
+       printf ("FAIL: invalid intermediate result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   if (value != 0)
+     {
+       printf ("FAIL: invalid final result for value.\n");
+       return 1;
+     }
+   return 0;
+ }
+ 
+ /* All values written to 'value' alternate between 'zero' and 'max'. Any other
+    value detected by memmodel_step_verify() between instructions would 
indicate
+    that the value was only partially written, and would thus fail this 
+    atomicity test.  
+ 
+    This function tests each different __sync_mem routine once, with the
+    exception of the load instruction which requires special testing.  */
+ main()
+ {
+   
+   ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST);
+   if (ret != zero || value != max)
+     test_abort();
+ 
+   __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST);
+   if (value != zero)
+     test_abort();
+ 
+   ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != max || ret != max)
+     test_abort ();
+ 
+   ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST);
+   if (value != zero || ret != zero)
+     test_abort ();
+ 
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-load-short.c
===================================================================
*** gcc.dg/memmodel/sync-load-short.c   (revision 0)
--- gcc.dg/memmodel/sync-load-short.c   (revision 0)
***************
*** 0 ****
--- 1,111 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_char_short } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ 
+ /* Testing load for atomicity is a little trickier.  
+ 
+    Set up the atomic value so that it changes value after every instruction 
+    is executed.
+ 
+    Simply alternating between 2 values wouldn't be sufficient since a load of
+    one part, followed by the load of the second part 2 instructions later 
would
+    appear to be valid.
+ 
+    set up a table of 16 values which change a bit in every byte of the value 
+    each time, this will give us a 16 instruction cycle before repetition
+    kicks in, which should be sufficient to detect any issues.  Just to be 
sure,
+    we also change the table cycle size during execution. 
+    
+    The end result is that all loads should always get one of the values from
+    the table. Any other pattern means the load failed.  */
+ 
+ unsigned short ret;
+ unsigned short value = 0;
+ unsigned short result = 0;
+ unsigned short table[16] = {
+ 0x0000, 
+ 0x1111, 
+ 0x2222, 
+ 0x3333,
+ 0x4444,
+ 0x5555,
+ 0x6666,
+ 0x7777,
+ 0x8888,
+ 0x9999,
+ 0xAAAA,
+ 0xBBBB,
+ 0xCCCC,
+ 0xDDDD,
+ 0xEEEE,
+ 0xFFFF
+ };
+ 
+ int table_cycle_size = 16;
+ 
+ /* Return 0 if 'result' is a valid value to have loaded.  */
+ int verify_result ()
+ {
+   int x;
+   int found = 0;
+ 
+   /* Check entire table for valid values.  */
+   for (x = 0; x < 16 ; x++)
+     if (result == table[x])
+       {
+       found = 1;
+       break;
+       }
+ 
+   if (!found)
+     printf("FAIL: Invalid result returned from fetch\n");
+ 
+   return !found;
+ }
+ 
+ /* Iterate VALUE through the different valid values. */
+ void memmodel_other_threads ()
+ {
+   static int current = 0;
+ 
+   if (++current >= table_cycle_size)
+     current = 0;
+   value = table[current];
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ main()
+ {
+   int x;
+ 
+   /* Execute loads with value changing at various cyclic values.  */
+   for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
+     {
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       /* In order to verify the returned value (which is not atomic), it needs
+        to be atomically stored into another variable and check that.  */
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+ 
+       /* Execute the fetch/store a couple of times just to ensure the cycles
+          have a chance to be interesting.  */
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+     }
+   
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-load-int.c
===================================================================
*** gcc.dg/memmodel/sync-load-int.c     (revision 0)
--- gcc.dg/memmodel/sync-load-int.c     (revision 0)
***************
*** 0 ****
--- 1,111 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_int_long } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ 
+ /* Testing load for atomicity is a little trickier.  
+ 
+    Set up the atomic value so that it changes value after every instruction 
+    is executed.
+ 
+    Simply alternating between 2 values wouldn't be sufficient since a load of
+    one part, followed by the load of the second part 2 instructions later 
would
+    appear to be valid.
+ 
+    set up a table of 16 values which change a bit in every byte of the value 
+    each time, this will give us a 16 instruction cycle before repetition
+    kicks in, which should be sufficient to detect any issues.  Just to be 
sure,
+    we also change the table cycle size during execution. 
+    
+    The end result is that all loads should always get one of the values from
+    the table. Any other pattern means the load failed.  */
+ 
+ unsigned int ret;
+ unsigned int value = 0;
+ unsigned int result = 0;
+ unsigned int table[16] = {
+ 0x00000000, 
+ 0x11111111, 
+ 0x22222222, 
+ 0x33333333,
+ 0x44444444,
+ 0x55555555,
+ 0x66666666,
+ 0x77777777,
+ 0x88888888,
+ 0x99999999,
+ 0xAAAAAAAA,
+ 0xBBBBBBBB,
+ 0xCCCCCCCC,
+ 0xDDDDDDDD,
+ 0xEEEEEEEE,
+ 0xFFFFFFFF
+ };
+ 
+ int table_cycle_size = 16;
+ 
+ /* Return 0 if 'result' is a valid value to have loaded.  */
+ int verify_result ()
+ {
+   int x;
+   int found = 0;
+ 
+   /* Check entire table for valid values.  */
+   for (x = 0; x < 16 ; x++)
+     if (result == table[x])
+       {
+       found = 1;
+       break;
+       }
+ 
+   if (!found)
+     printf("FAIL: Invalid result returned from fetch\n");
+ 
+   return !found;
+ }
+ 
+ /* Iterate VALUE through the different valid values. */
+ void memmodel_other_threads ()
+ {
+   static int current = 0;
+ 
+   if (++current >= table_cycle_size)
+     current = 0;
+   value = table[current];
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ main()
+ {
+   int x;
+ 
+   /* Execute loads with value changing at various cyclic values.  */
+   for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
+     {
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       /* In order to verify the returned value (which is not atomic), it needs
+        to be atomically stored into another variable and check that.  */
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+ 
+       /* Execute the fetch/store a couple of times just to ensure the cycles
+          have a chance to be interesting.  */
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+     }
+   
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-load-longlong.c
===================================================================
*** gcc.dg/memmodel/sync-load-longlong.c        (revision 0)
--- gcc.dg/memmodel/sync-load-longlong.c        (revision 0)
***************
*** 0 ****
--- 1,112 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_long_long } */
+ /* { dg-options "" } */
+ /* { dg-final { memmodel-gdb-test } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ 
+ /* Testing load for atomicity is a little trickier.  
+ 
+    Set up the atomic value so that it changes value after every instruction 
+    is executed.
+ 
+    Simply alternating between 2 values wouldn't be sufficient since a load of
+    one part, followed by the load of the second part 2 instructions later 
would
+    appear to be valid.
+ 
+    set up a table of 16 values which change a bit in every byte of the value 
+    each time, this will give us a 16 instruction cycle before repetition
+    kicks in, which should be sufficient to detect any issues.  Just to be 
sure,
+    we also change the table cycle size during execution. 
+    
+    The end result is that all loads should always get one of the values from
+    the table. Any other pattern means the load failed.  */
+ 
+ unsigned long long ret;
+ unsigned long long value = 0;
+ unsigned long long result = 0;
+ unsigned long long table[16] = {
+ 0x0000000000000000, 
+ 0x1111111111111111, 
+ 0x2222222222222222, 
+ 0x3333333333333333,
+ 0x4444444444444444,
+ 0x5555555555555555,
+ 0x6666666666666666,
+ 0x7777777777777777,
+ 0x8888888888888888,
+ 0x9999999999999999,
+ 0xAAAAAAAAAAAAAAAA,
+ 0xBBBBBBBBBBBBBBBB,
+ 0xCCCCCCCCCCCCCCCC,
+ 0xDDDDDDDDDDDDDDDD,
+ 0xEEEEEEEEEEEEEEEE,
+ 0xFFFFFFFFFFFFFFFF
+ };
+ 
+ int table_cycle_size = 16;
+ 
+ /* Return 0 if 'result' is a valid value to have loaded.  */
+ int verify_result ()
+ {
+   int x;
+   int found = 0;
+ 
+   /* Check entire table for valid values.  */
+   for (x = 0; x < 16 ; x++)
+     if (result == table[x])
+       {
+       found = 1;
+       break;
+       }
+ 
+   if (!found)
+     printf("FAIL: Invalid result returned from fetch\n");
+ 
+   return !found;
+ }
+ 
+ /* Iterate VALUE through the different valid values. */
+ void memmodel_other_threads ()
+ {
+   static int current = 0;
+ 
+   if (++current >= table_cycle_size)
+     current = 0;
+   value = table[current];
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ main()
+ {
+   int x;
+ 
+   /* Execute loads with value changing at various cyclic values.  */
+   for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
+     {
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       /* In order to verify the returned value (which is not atomic), it needs
+        to be atomically stored into another variable and check that.  */
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+ 
+       /* Execute the fetch/store a couple of times just to ensure the cycles
+          have a chance to be interesting.  */
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+     }
+   
+   memmodel_done ();
+   return 0;
+ }
Index: gcc.dg/memmodel/sync-load-int128.c
===================================================================
*** gcc.dg/memmodel/sync-load-int128.c  (revision 0)
--- gcc.dg/memmodel/sync-load-int128.c  (revision 0)
***************
*** 0 ****
--- 1,128 ----
+ /* { dg-do link } */
+ /* { dg-require-effective-target sync_int_128 } */
+ /* { dg-options "-mcx16" { target { x86_64-*-* } } } */
+ /* { dg-final { memmodel-gdb-test { xfail *-*-* } } } */
+ 
+ 
+ #include <stdio.h>
+ #include "memmodel.h"
+ 
+ 
+ /* Testing load for atomicity is a little trickier.  
+ 
+    Set up the atomic value so that it changes value after every instruction 
+    is executed.
+ 
+    Simply alternating between 2 values wouldn't be sufficient since a load of
+    one part, followed by the load of the second part 2 instructions later 
would
+    appear to be valid.
+ 
+    set up a table of 16 values which change a bit in every byte of the value 
+    each time, this will give us a 16 instruction cycle before repetition
+    kicks in, which should be sufficient to detect any issues.  Just to be 
sure,
+    we also change the table cycle size during execution. 
+    
+    The end result is that all loads should always get one of the values from
+    the table. Any other pattern means the load failed.  */
+ 
+ __int128_t ret;
+ __int128_t value = 0;
+ __int128_t result = 0;
+ __int128_t table[16] = {
+ 0x0000000000000000, 
+ 0x1111111111111111, 
+ 0x2222222222222222, 
+ 0x3333333333333333,
+ 0x4444444444444444,
+ 0x5555555555555555,
+ 0x6666666666666666,
+ 0x7777777777777777,
+ 0x8888888888888888,
+ 0x9999999999999999,
+ 0xAAAAAAAAAAAAAAAA,
+ 0xBBBBBBBBBBBBBBBB,
+ 0xCCCCCCCCCCCCCCCC,
+ 0xDDDDDDDDDDDDDDDD,
+ 0xEEEEEEEEEEEEEEEE,
+ 0xFFFFFFFFFFFFFFFF
+ };
+ 
+ int table_cycle_size = 16;
+ 
+ /* Since we don't have 128 bit constants, we have to properly pad the table.  
*/
+ void fill_table()
+ {
+   int x;
+   for (x = 0; x < 16; x++)
+     {
+       ret = table[x];
+       ret = (ret << 64) | ret;
+       table[x] = ret;
+     }
+ }
+ 
+ /* Return 0 if 'result' is a valid value to have loaded.  */
+ int verify_result ()
+ {
+   int x;
+   int found = 0;
+ 
+   /* Check entire table for valid values.  */
+   for (x = 0; x < 16; x++)
+     if (result == table[x])
+       {
+       found = 1;
+       break;
+       }
+ 
+   if (!found)
+     printf("FAIL: Invalid result returned from fetch\n");
+ 
+   return !found;
+ }
+ 
+ /* Iterate VALUE through the different valid values. */
+ void memmodel_other_threads ()
+ {
+   static int current = 0;
+ 
+   if (++current >= table_cycle_size)
+     current = 0;
+   value = table[current];
+ }
+ 
+ int memmodel_step_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ int memmodel_final_verify ()
+ {
+   return verify_result ();
+ }
+ 
+ main()
+ {
+   int x;
+ 
+   fill_table ();
+   /* Make sure value starts with an atomic value now.  */
+   __sync_mem_store (&value, ret, __SYNC_MEM_SEQ_CST);
+ 
+   /* Execute loads with value changing at various cyclic values.  */
+   for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
+     {
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       /* In order to verify the returned value (which is not atomic), it needs
+        to be atomically stored into another variable and check that.  */
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+ 
+       /* Execute the fetch/store a couple of times just to ensure the cycles
+          have a chance to be interesting.  */
+       ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST);
+       __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST);
+     }
+   
+   memmodel_done ();
+   return 0;
+ }

Reply via email to