===================================================================
@@ -747,4 +747,26 @@ proc dg-message { args } {
process-message saved-dg-warning "" $args
}
+# Check the existence of a gdb in the path, and return true if there
+# is one.
+#
+# Set env(GDB_FOR_GCC_TESTING) accordingly.
+
+proc gdb-exists { args } {
+ if ![info exists ::env(GDB_FOR_GCC_TESTING)] {
+ global GDB
+ if ![info exists ::env(GDB_FOR_GCC_TESTING)] {
+ if [info exists GDB] {
+ setenv GDB_FOR_GCC_TESTING "$GDB"
+ } else {
+ setenv GDB_FOR_GCC_TESTING "[transform gdb]"
+ }
+ }
+ }
+ if { [which $::env(GDB_FOR_GCC_TESTING)] != 0 } {
+ return 1;
+ }
+ return 0;
+}
+
set additional_prunes ""
===================================================================
@@ -1,77 +0,0 @@
-# Copyright (C) 2011 Free Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with GCC; see the file COPYING3. If not see
-# <http://www.gnu.org/licenses/>.
-
-# Utility for running a given test through the memmodel harness using gdb.
-# This is invoked via dg-final.
-#
-# Adapted from the guality harness.
-#
-# Call 'fail' if a given test printed "FAIL:", otherwise call 'pass'.
-
-proc memmodel-gdb-test { args } {
- if { ![isnative] || [is_remote target] } { return }
-
- if { [llength $args] == 1 } {
- switch [dg-process-target [lindex $args 0]] {
- "F" { setup_xfail "*-*-*" }
- }
- }
-
- # This assumes that we are three frames down from dg-test, and that
- # it still stores the filename of the testcase in a local variable "name".
- # A cleaner solution would require a new DejaGnu release.
- upvar 2 name testcase
- upvar 2 prog prog
- upvar 2 srcdir testsuite_dir
-
- set gdb_name $::env(GUALITY_GDB_NAME)
- set exec_file "[file rootname [file tail $prog]].exe"
- set cmd_file "$testsuite_dir/gcc.dg/memmodel/memmodel.gdb"
-
- if ![file exists $exec_file] {
- return
- }
-
- send_log "Spawning: $gdb_name -nx -nw -quiet -x $cmd_file ./$exec_file\n"
- set res [remote_spawn target "$gdb_name -nx -nw -x $cmd_file ./$exec_file"]
- if { $res < 0 || $res == "" } {
- unsupported "$testcase"
- return
- }
-
- remote_expect target [timeout_value] {
- -re "FAIL:" {
- fail "$testcase"
- remote_close target
- return
- }
- # Too old GDB
- -re "Unhandled dwarf expression|Error in sourced command file" {
- unsupported "$testcase"
- remote_close target
- return
- }
- timeout {
- unsupported "$testcase"
- remote_close target
- return
- }
- }
-
- remote_close target
- pass "$testcase"
- return
-}
===================================================================
@@ -0,0 +1,90 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Utility for running a given test through the simulate-thread harness
+# using gdb. This is invoked via dg-final.
+#
+# Adapted from the guality harness.
+#
+# Call 'fail' if a given test printed "FAIL:", otherwise call 'pass'.
+
+proc simulate-thread { args } {
+ if { ![isnative] || [is_remote target] } { return }
+
+ if { [llength $args] == 1 } {
+ switch [dg-process-target [lindex $args 0]] {
+ "F" { setup_xfail "*-*-*" }
+ }
+ }
+
+ # This assumes that we are three frames down from dg-test, and that
+ # it still stores the filename of the testcase in a local variable "name".
+ # A cleaner solution would require a new DejaGnu release.
+ upvar 2 name testcase
+ upvar 2 prog prog
+ upvar 2 srcdir testsuite_dir
+
+ set gdb_name $::env(GDB_FOR_GCC_TESTING)
+ set exec_file "[file rootname [file tail $prog]].exe"
+ set cmd_file "$testsuite_dir/gcc.dg/simulate-thread/simulate-thread.gdb"
+
+ if ![file exists $exec_file] {
+ return
+ }
+
+ send_log "Spawning: $gdb_name -nx -nw -quiet -x $cmd_file ./$exec_file\n"
+ set res [remote_spawn target "$gdb_name -nx -nw -x $cmd_file ./$exec_file"]
+ if { $res < 0 || $res == "" } {
+ unsupported "$testcase"
+ return
+ }
+
+ set gdb_worked 0
+ remote_expect target [timeout_value] {
+ # Too old GDB
+ -re "Unhandled dwarf expression|Error in sourced command file" {
+ unsupported "$testcase"
+ remote_close target
+ return
+ }
+ -re "FAIL:" {
+ fail "$testcase"
+ remote_close target
+ return
+ }
+ # If the gdb output contained simulate_thread_main, assume
+ # that at the very least, we had a working gdb that was able
+ # to break in simulate_thread_main.
+ -re "simulate_thread_main" {
+ set gdb_worked 1
+ exp_continue
+ }
+ timeout {
+ unsupported "$testcase"
+ remote_close target
+ return
+ }
+ }
+
+ remote_close target
+ if {$gdb_worked} {
+ pass "$testcase"
+ } else {
+ # Fail in the absence of a sane GDB.
+ fail "$testcase"
+ }
+ return
+}
===================================================================
@@ -0,0 +1,52 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-packed-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "simulate-thread.h"
+
+/* This test verifies writes to globals do not write to adjacent
+ globals. This mostly happens on strict-align targets that are not
+ byte addressable (old Alphas, etc). */
+
+char a = 0;
+char b = 77;
+
+void simulate_thread_other_threads()
+{
+}
+
+int simulate_thread_step_verify()
+{
+ if (b != 77)
+ {
+ printf("FAIL: Unexpected value. <b> is %d, should be 77\n", b);
+ return 1;
+ }
+ return 0;
+}
+
+/* Verify that every variable has the correct value. */
+int simulate_thread_final_verify()
+{
+ int ret = simulate_thread_step_verify ();
+ if (a != 66)
+ {
+ printf("FAIL: Unexpected value. <a> is %d, should be 66\n", a);
+ return 1;
+ }
+ return ret;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ a = 66;
+}
+
+int main ()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Your run of the mill dg test, but verify that we have a working GDB first.
+
+load_lib gcc-dg.exp
+load_lib gcc-simulate-thread.exp
+load_lib torture-options.exp
+
+dg-init
+torture-init
+set-torture-options [list \
+ { -O0 -g } \
+ { -O1 -g } \
+ { -O2 -g } \
+ { -O3 -g } \
+ { -Os -g } ]
+
+if [gdb-exists] {
+ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] ""
+ gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/simulate-thread/*.c]] ""
+}
+
+torture-finish
+dg-finish
===================================================================
@@ -0,0 +1,17 @@
+set height 0
+break simulate_thread_main
+disp/i $pc
+run
+
+set $ret = 0
+while (simulate_thread_fini != 1) && (! $ret)
+ call simulate_thread_wrapper_other_threads()
+ stepi
+ set $ret |= simulate_thread_step_verify()
+end
+
+if (! $ret)
+ set $ret |= simulate_thread_wrapper_final_verify()
+end
+continue
+quit $ret
===================================================================
@@ -0,0 +1,117 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_long_long } */
+/* { dg-options "" } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+ static int current = 0;
+
+ if (++current >= table_cycle_size)
+ current = 0;
+ value = table[current];
+}
+
+int simulate_thread_step_verify ()
+{
+ return verify_result ();
+}
+
+int simulate_thread_final_verify ()
+{
+ return verify_result ();
+}
+
+__attribute__((noinline))
+void simulate_thread_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);
+ }
+}
+
+main()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,117 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_long_long } */
+/* { dg-options "" } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+}
+
+int simulate_thread_step_verify ()
+{
+ if (value != zero && value != max)
+ {
+ printf ("FAIL: invalid intermediate result for value.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_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 simulate_thread_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. */
+__attribute__((noinline))
+void simulate_thread_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 ();
+}
+
+int main ()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,118 @@
+OVERVIEW
+--------
+
+This is a harness to test the atomicity of certain operations, and to
+make sure the compiler does not introduce data races in a
+multi-threaded environment.
+
+The basic premise is that we set up testcases such that the thing we
+want test, say an atomic instruction which stores a double word is in
+a function of its own. We then run this testcase within GDB,
+controlled by a gdb script (simulate-thread.gdb). The gdb script will
+break on the function to be tested, and then single step through every
+machine instruction in the function. We set this up so GDB can make a
+couple of inferior function calls before and after each of these
+single step instructions for a couple of purposes:
+
+ 1. One of the calls simulates another thread running in the
+ process which changes or access memory.
+
+ 2. The other calls are used to verify that we always get the
+ expected behavior.
+
+For example, in the case of an atomic store, anyone looking at the
+memory associated with an atomic variable should never see any in
+between states. If you have an atomic long long int, and it starts
+with the value 0, and you write the value MAX_LONG_LONG, any other
+thread looking at that variable should never see anything other than 0
+or MAX_LONG_LONG. If you implement the atomic write as a sequence of
+2 stores, it is possible for another thread to read the location after
+the first store, but before the second one is complete. That thread
+would then see an in-between state (one word would still be 0).
+
+We simulate this in the testcase by having GDB step through the
+program, instruction by instruction, and after each step, making an
+inferior function call which looks at the value of the atomic variable
+and verifies that it sees either 0 or MAX_LONG_LONG. If it sees any
+other value, it fails the testcase.
+
+This way, we are *sure* there is no in between state because we
+effectively acted like an OS and switched to another thread after
+every single instruction of the routine is executed and looked at the
+results each time.
+
+We use the same idea to test for data races to see if an illegal load
+has been hoisted, or that two parallel bitfield writes don't overlap
+in a data race.
+
+Below is a skeleton of how a test should look like. For more details,
+look at the tests themselves.
+
+ANATOMY OF A TEST
+-----------------
+
+/* { dg-do link } */
+/* { dg-options "-some-flags" } */
+/* { dg-final { simulate-thread } } */
+
+/* NOTE: Any failure must be indicated by displaying "FAIL:". */
+
+#include "simulate-thread.h"
+
+/* Called before each instruction, simulating another thread executing. */
+void simulate_thread_other_threads()
+{
+}
+
+/* Called after each instruction. Returns 1 if any inconsistency is
+ found, 0 otherwise. */
+int simulate_thread_step_verify()
+{
+ if (some_problem)
+ {
+ printf("FAIL: reason\n");
+ return 1;
+ }
+ return 0;
+}
+
+/* Called at the end of the program (simulate_thread_fini == 1). Verifies
+ the state of the program and returns 1 if any inconsistency is
+ found, 0 otherwise. */
+int simulate_thread_final_verify()
+{
+ if (some_problem)
+ {
+ printf("FAIL: reason\n");
+ return 1;
+ }
+ return 0;
+}
+
+/* The gdb script will break on simulate_thread_main(), so make sure
+ GCC does not inline it, thus making the break point fail. */
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ /* Do stuff. */
+}
+
+int main()
+{
+
+ /* Perform any setup code that will run outside of the testing
+ harness. Put code here that you do NOT want to be interrupted on
+ an instruction basis. E.g., setup code, and system library
+ calls. */
+
+ /* Do un-instrumented stuff. */
+ /* ... */
+
+ /* Start the instrumented show. */
+ simulate_thread_main();
+
+ /* Must be called at the end of the test. */
+ simulate_thread_done();
+
+ return 0;
+}
===================================================================
@@ -0,0 +1,132 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_int_128 } */
+/* { dg-options "-mcx16" { target { x86_64-*-* } } } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+ static int current = 0;
+
+ if (++current >= table_cycle_size)
+ current = 0;
+ value = table[current];
+}
+
+int simulate_thread_step_verify ()
+{
+ return verify_result ();
+}
+
+int simulate_thread_final_verify ()
+{
+ return verify_result ();
+}
+
+__attribute__((noinline))
+void simulate_thread_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);
+ }
+}
+
+main()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,57 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "simulate-thread.h"
+
+/* This file tests that speculative store movement out of a loop doesn't
+ happen. This is disallowed when --param allow-store-data-races is 0. */
+
+int global = 100;
+
+/* Other thread makes sure global is 100 before the next instruction is
+ * exceuted. */
+void simulate_thread_other_threads()
+{
+ global = 100;
+}
+
+int simulate_thread_step_verify()
+{
+ if (global != 100)
+ {
+ printf("FAIL: global variable was assigned to. \n");
+ return 1;
+ }
+}
+
+int simulate_thread_final_verify()
+{
+ return 0;
+}
+
+/* The variable global should never be assigned if func(0) is called.
+ This tests store movement out of loop thats never executed. */
+void test (int y)
+{
+ int x;
+ for (x=0; x< y; x++)
+ {
+ global = y; /* This should never speculatively execute. */
+ }
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ test(0);
+ simulate_thread_done();
+}
+
+__attribute__((noinline))
+int main()
+{
+ simulate_thread_main();
+ return 0;
+}
===================================================================
@@ -0,0 +1,116 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_int_128 } */
+/* { dg-options "-mcx16" { target { x86_64-*-* } } } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+}
+
+int simulate_thread_step_verify ()
+{
+ if (value != zero && value != max)
+ {
+ printf ("FAIL: invalid intermediate result for value.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_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 simulate_thread_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. */
+__attribute__((noinline))
+void simulate_thread_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 ();
+}
+
+int main()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,93 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-packed-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "simulate-thread.h"
+
+/* This test verifies that data races aren't introduced by structure subfield
+ stores. */
+
+struct test_struct {
+ char a;
+ char b;
+ char c;
+ char d;
+} var = {0,0,0,0};
+
+
+/* This routine sets field a to 'x'. If executed properly, it will
+ not affect any of the other fields in the structure. An improper
+ implementation may load an entire word, change the 8 bits for field
+ 'a' and write the entire word back out. */
+__attribute__((noinline))
+void set_a(char x)
+{
+ var.a = x;
+}
+
+static int global = 0;
+
+/* The other thread increments the value of each of the other fields
+ in the structure every cycle. If the store to the 'a' field does
+ an incorrect full or partial word load, mask and store, it will
+ write back an incorrect value to one or more of the other
+ fields. */
+void simulate_thread_other_threads()
+{
+ global++;
+ var.b = global;
+ var.c = global;
+ var.d = global;
+}
+
+
+/* Make sure that none of the other fields have been changed. */
+int simulate_thread_step_verify()
+{
+ int ret = 0;
+ if (var.b != global)
+ {
+ printf("FAIL: Unexpected value. var.b is %d, should be %d\n",
+ var.b, global);
+ ret = 1;
+ }
+ if (var.c != global)
+ {
+ printf("FAIL: Unexpected value. var.c is %d, should be %d\n",
+ var.c, global);
+ ret = 1;
+ }
+ if (var.d != global)
+ {
+ printf("FAIL: Unexpected value. var.d is %d, should be %d\n",
+ var.d, global);
+ ret = 1;
+ }
+ return ret;
+}
+
+/* Verify that every variable has the correct value. */
+int simulate_thread_final_verify()
+{
+ int ret = simulate_thread_step_verify();
+ if (var.a != 1)
+ {
+ printf("FAIL: Unexpected value. var.a is %d, should be %d\n", var.a, 1);
+ ret = 1;
+ }
+ return ret;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ set_a(1);
+}
+
+int main ()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,116 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_int_long } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+ static int current = 0;
+
+ if (++current >= table_cycle_size)
+ current = 0;
+ value = table[current];
+}
+
+int simulate_thread_step_verify ()
+{
+ return verify_result ();
+}
+
+int simulate_thread_final_verify ()
+{
+ return verify_result ();
+}
+
+__attribute__((noinline))
+void simulate_thread_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);
+ }
+}
+
+main()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,118 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_int_long } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+}
+
+int simulate_thread_step_verify ()
+{
+ if (value != zero && value != max)
+ {
+ printf ("FAIL: invalid intermediate result for value.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_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 simulate_thread_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. */
+__attribute__((noinline))
+void simulate_thread_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 ();
+}
+
+main ()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,116 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_char_short } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+ static int current = 0;
+
+ if (++current >= table_cycle_size)
+ current = 0;
+ value = table[current];
+}
+
+int simulate_thread_step_verify ()
+{
+ return verify_result ();
+}
+
+int simulate_thread_final_verify ()
+{
+ return verify_result ();
+}
+
+__attribute__((noinline))
+void simulate_thread_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);
+ }
+}
+
+main()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,117 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_char_short } */
+/* { dg-final { simulate-thread } } */
+
+
+#include <stdio.h>
+#include "simulate-thread.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 simulate_thread_other_threads ()
+{
+}
+
+int simulate_thread_step_verify ()
+{
+ if (value != zero && value != max)
+ {
+ printf ("FAIL: invalid intermediate result for value.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_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 simulate_thread_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. */
+__attribute__((noinline))
+void simulate_thread_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 ();
+}
+
+int main ()
+{
+ simulate_thread_main ();
+ simulate_thread_done ();
+ return 0;
+}
===================================================================
@@ -0,0 +1,111 @@
+int simulate_thread_fini = 0;
+
+void __attribute__((noinline))
+simulate_thread_done ()
+{
+ simulate_thread_fini = 1;
+}
+
+/* A hostile thread is one which changes a memory location so quickly
+ that another thread may never see the same value again. This is
+ simulated when simulate_thread_other_thread() is defined to modify
+ a memory location every cycle.
+
+ A process implementing a dependency on this value can run into
+ difficulties with such a hostile thread. For instance,
+ implementing an add with a compare_and_swap loop goes something
+ like:
+
+ expected = *mem;
+ loop:
+ new = expected += value;
+ if (!succeed (expected = compare_and_swap (mem, expected, new)))
+ goto loop;
+
+ If the content of 'mem' are changed every cycle by
+ simulate_thread_other_thread () this will become an infinite loop
+ since the value *mem will never be 'expected' by the time the
+ compare_and_swap is executed.
+
+ HOSTILE_THREAD_THRESHOLD defines the number of intructions which a
+ program will execute before triggering the hostile thread
+ pause. The pause will last for HOSTILE_THREAD_PAUSE instructions,
+ and then the counter will reset and begin again. During the pause
+ period, simulate_thread_other_thread will not be called.
+
+ This provides a chance for forward progress to be made and the
+ infinite loop to be avoided.
+
+ If the testcase defines HOSTILE_PAUSE_ERROR, then it will be
+ considered an RUNTIME FAILURE if the hostile pause is triggered.
+ This will allow to test for guaranteed forward progress routines.
+
+ If the default values for HOSTILE_THREAD_THRESHOLD or
+ HOSTILE_THREAD_PAUSE are insufficient, then the testcase may
+ override these by defining the values before including this file.
+
+ Most testcase are intended to run for very short periods of time,
+ so these defaults are considered to be high enough to not trigger
+ on a typical case, but not drag the test time out too much if a
+ hostile condition is interferring. */
+
+
+/* Define the threshold to start pausing the hostile thread. */
+#if !defined (HOSTILE_THREAD_THRESHOLD)
+#define HOSTILE_THREAD_THRESHOLD 500
+#endif
+
+/* Define the length of pause in cycles for the hostile thread to pause to
+ allow forward progress to be made. */
+#if !defined (HOSTILE_THREAD_PAUSE)
+#define HOSTILE_THREAD_PAUSE 20
+#endif
+
+void simulate_thread_other_threads (void);
+int simulate_thread_final_verify (void);
+
+static int simulate_thread_hostile_pause = 0;
+
+/* This function wraps simulate_thread_other_threads an monitors for
+ an infinite loop. If the threshold value HOSTILE_THREAD_THRESHOLD
+ is reached, the other_thread process is paused for
+ HOSTILE_THREAD_PAUSE cycles before resuming, and the counters start
+ again. */
+void
+simulate_thread_wrapper_other_threads()
+{
+ static int count = 0;
+ static int pause = 0;
+
+ if (++count >= HOSTILE_THREAD_THRESHOLD)
+ {
+ if (!simulate_thread_hostile_pause)
+ simulate_thread_hostile_pause = 1;
+
+ /* Count cycles before calling the hostile thread again. */
+ if (pause++ < HOSTILE_THREAD_PAUSE)
+ return;
+
+ /* Reset the pause counter, as well as the thread counter. */
+ pause = 0;
+ count = 0;
+ }
+ simulate_thread_other_threads ();
+}
+
+
+/* If the test case defines HOSTILE_PAUSE_ERROR, then the test case
+ will fail execution if it had a hostile pause. */
+int
+simulate_thread_wrapper_final_verify ()
+{
+ int ret = simulate_thread_final_verify ();
+#if defined (HOSTILE_PAUSE_ERROR)
+ if (simulate_thread_hostile_pause)
+ {
+ printf ("FAIL: Forward progress made only by pausing hostile thread\n");
+ ret = ret | 1; /* 0 indicates proper comnpletion. */
+ }
+#endif
+ return ret;
+}
===================================================================
@@ -1,46 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "--param allow-packed-store-data-races=0" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-#include <stdio.h>
-#include "memmodel.h"
-
-/* This test verifies writes to globals do not write to adjacent
- globals. This mostly happens on strict-align targets that are not
- byte addressable (old Alphas, etc). */
-
-char a = 0;
-char b = 77;
-
-void memmodel_other_threads()
-{
-}
-
-int memmodel_step_verify()
-{
- if (b != 77)
- {
- printf("FAIL: Unexpected value. <b> is %d, should be 77\n", b);
- return 1;
- }
- return 0;
-}
-
-/* Verify that every variable has the correct value. */
-int memmodel_final_verify()
-{
- int ret = memmodel_step_verify ();
- if (a != 66)
- {
- printf("FAIL: Unexpected value. <a> is %d, should be 66\n", a);
- return 1;
- }
- return ret;
-}
-
-int main ()
-{
- a = 66;
- memmodel_done();
- return 0;
-}
===================================================================
@@ -1,107 +0,0 @@
-int __gdb_memmodel_fini = 0;
-
-void __attribute__((noinline))
-memmodel_done ()
-{
- __gdb_memmodel_fini = 1;
-}
-
-
-/* A hostile thread is one which changes a memory location so quickly that
- another thread may never see the same value again. This is simulated when
- memmodel_other_thread() is defined to modify a memory location every cycle.
-
- A process implementing a dependency on this value can run into difficulties
- with such a hostile thread. for instance, implementing an add with a
- compare_and_swap loop goes something like:
- expected = *mem;
- loop:
- new = expected += value;
- if (!succeed (expected = compare_and_swap (mem, expected, new)))
- goto loop;
-
- if the content of 'mem' are changed every cycle by memodel_other_thread ()
- this will become an infinite loop since the value *mem will never be
- 'expected' by the time the compare_and_swap is executed.
-
- HOSTILE_THREAD_THRESHOLD defines the number of intructions which a program
- will execute before triggering the hostile threead pause. The pause will
- last for HOSTILE_THREAD_PAUSE instructions, and then the counter will reset
- and begin again. During the pause period, memodel_other_thread will not
- be called.
-
- This provides a chance for forward progress to be made and the infinite loop
- to be avoided.
-
- if the testcase defines HOSTILE_PAUSE_ERROR, then it will be considered an
- RUNTIME FAILURE if the hostile pause is triggered. This will allow to test
- for guaranteed forward progress routines.
-
- If the default values for HOSTILE_THREAD_THRESHOLD or HOSTILE_THREAD_PAUSE
- are insufficient, then the testcase may override these by defining the
- values before including this file.
-
- Most testcase are intended to run for very short periods of time, so these
- defaults are considered to be high enough to not trigger on a typical case,
- but not drag the test time out too much if a hostile condition is
- interferring. */
-
-
-/* Define the threshold to start pausing the hostile thread. */
-#if !defined (HOSTILE_THREAD_THRESHOLD)
-#define HOSTILE_THREAD_THRESHOLD 500
-#endif
-
-/* Define the length of pause in cycles for the hostile thread to pause to
- allow forward progress to be made. */
-#if !defined (HOSTILE_THREAD_PAUSE)
-#define HOSTILE_THREAD_PAUSE 20
-#endif
-
-void memmodel_other_threads (void);
-int memmodel_final_verify (void);
-
-static int __gdb_hostile_pause = 0;
-
-/* This function wraps memodel_other_threads an monitors for an infinite loop.
- If the threshold value HOSTILE_THREAD_THRESHOLD is reached, the other_thread
- process is paused for HOSTILE_THREAD_PAUSE cycles before resuming, and the
- counters start again. */
-void
-__gdb_wrapper_other_threads()
-{
- static int count = 0;
- static int pause = 0;
-
- if (++count >= HOSTILE_THREAD_THRESHOLD)
- {
- if (!__gdb_hostile_pause)
- __gdb_hostile_pause = 1;
-
- /* Count cycles before calling the hostile thread again. */
- if (pause++ < HOSTILE_THREAD_PAUSE)
- return;
-
- /* Reset the pause counter, as well as the thread counter. */
- pause = 0;
- count = 0;
- }
- memmodel_other_threads ();
-}
-
-
-/* If the test case defines HOSTILE_PAUSE_ERROR, then the test case will
- fail execution if it had a hostile pause. */
-int
-__gdb_wrapper_final_verify ()
-{
- int ret = memmodel_final_verify ();
-#if defined (HOSTILE_PAUSE_ERROR)
- if (__gdb_hostile_pause)
- {
- printf ("FAIL: Forward progress made only by pausing hostile thread\n");
- ret = ret | 1; /* 0 indicates proper comnpletion. */
- }
-#endif
- return ret;
-}
===================================================================
@@ -1,112 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -1,113 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -1,17 +0,0 @@
-set height 0
-break main
-disp/i $pc
-run
-
-set $ret = 0
-while (__gdb_memmodel_fini != 1) && (! $ret)
- call __gdb_wrapper_other_threads()
- stepi
- set $ret |= memmodel_step_verify()
-end
-
-if (! $ret)
- set $ret |= __gdb_wrapper_final_verify()
-end
-continue
-quit $ret
===================================================================
@@ -1,58 +0,0 @@
-# Your run of the mill dg test, but verify that we have a working GDB first.
-
-load_lib gcc-dg.exp
-load_lib gcc-memmodel-gdb-test.exp
-load_lib torture-options.exp
-
-proc check_guality {args} {
- set result [eval check_compile guality_check executable $args "-g -O0"]
- set lines [lindex $result 0]
- set output [lindex $result 1]
- set ret 0
- if {[string match "" $lines]} {
- set execout [gcc_load "./$output"]
- set ret [string match "*1 PASS, 0 FAIL, 0 UNRESOLVED*" $execout]
- }
- remote_file build delete $output
- return $ret
-}
-
-dg-init
-torture-init
-set-torture-options [list \
- { -O0 -g } \
- { -O1 -g } \
- { -O2 -g } \
- { -O3 -g } \
- { -Os -g } ]
-
-# Test the presence of gdb with the guality infrastructure.
-global GDB
-if ![info exists ::env(GUALITY_GDB_NAME)] {
- if [info exists GDB] {
- set guality_gdb_name "$GDB"
- } else {
- set guality_gdb_name "[transform gdb]"
- }
- setenv GUALITY_GDB_NAME "$guality_gdb_name"
-}
-if {[check_guality "
- #include \"$srcdir/$subdir/guality.h\"
- volatile long int varl = 6;
- int main (int argc, char *argv\[\])
- {
- GUALCHKVAL (varl);
- return 0;
- }
-"]} {
- gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] ""
-# Uncomment line below when we have common C/C++ tests.
-# gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/memmodel/*.c]] ""
-}
-
-if [info exists guality_gdb_name] {
- unsetenv GUALITY_GDB_NAME
-}
-
-torture-finish
-dg-finish
===================================================================
@@ -1,127 +0,0 @@
-/* { dg-do link } */
-/* { dg-require-effective-target sync_int_128 } */
-/* { dg-options "-mcx16" { target { x86_64-*-* } } } */
-/* { 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. */
-
-__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;
-}
===================================================================
@@ -1,102 +0,0 @@
-OVERVIEW
---------
-
-This is a harness to test the atomicity of certain operations, and to
-make sure the compiler does not introduce data races in a
-multi-threaded environment.
-
-The basic premise is that we set up testcases such that the thing we
-want test, say an atomic instruction which stores a double word is in
-a function of its own. We then run this testcase within GDB,
-controlled by a gdb script (memmodel.gdb). The gdb script will break
-on the function to be tested, and then single step through every
-machine instruction in the function. We set this up so GDB can make a
-couple of inferior function calls before and after each of these
-single step instructions for a couple of purposes:
-
- 1. One of the calls simulates another thread running in the
- process which changes or access memory.
-
- 2. The other calls are used to verify that we always get the
- expected behavior.
-
-For example, in the case of an atomic store, anyone looking at the
-memory associated with an atomic variable should never see any in
-between states. If you have an atomic long long int, and it starts
-with the value 0, and you write the value MAX_LONG_LONG, any other
-thread looking at that variable should never see anything other than 0
-or MAX_LONG_LONG. If you implement the atomic write as a sequence of
-2 stores, it is possible for another thread to read the location after
-the first store, but before the second one is complete. That thread
-would then see an in-between state (one word would still be 0).
-
-We simulate this in the testcase by having GDB step through the
-program, instruction by instruction, and after each step, making an
-inferior function call which looks at the value of the atomic variable
-and verifies that it sees either 0 or MAX_LONG_LONG. If it sees any
-other value, it fails the testcase.
-
-This way, we are *sure* there is no in between state because we
-effectively acted like an OS and switched to another thread after
-every single instruction of the routine is executed and looked at the
-results each time.
-
-We use the same idea to test for data races to see if an illegal load
-has been hoisted, or that two parallel bitfield writes don't overlap
-in a data race.
-
-Below is a skeleton of how a test should look like. For more details,
-look at the tests themselves.
-
-ANATOMY OF A TEST
------------------
-
-/* { dg-do link } */
-/* { dg-options "-some-flags" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-/* NOTE: Any failure must be indicated by displaying "FAIL:". */
-
-#include "memmodel.h"
-
-/* Called before each instruction, simulating another thread
- executing. */
-void memmodel_other_threads()
-{
-}
-
-/* Called after each instruction. Returns 1 if any inconsistency is
- found, 0 otherwise. */
-int memmodel_step_verify()
-{
- if (some_problem)
- {
- printf("FAIL: reason\n");
- return 1;
- }
- return 0;
-}
-
-/* Called at the end of the program (memmodel_fini == 1). Verifies
- the state of the program and returns 1 if any inconsistency is
- found, 0 otherwise. */
-int memmodel_final_verify()
-{
- if (some_problem)
- {
- printf("FAIL: reason\n");
- return 1;
- }
- return 0;
-}
-
-int main()
-{
- /* Do stuff. */
- /* ... */
-
- /* Must be called at the end of the test. */
- memmodel_done();
-
- return 0;
-}
===================================================================
@@ -1,111 +0,0 @@
-/* { dg-do link } */
-/* { dg-require-effective-target sync_int_128 } */
-/* { dg-options "-mcx16" { target { x86_64-*-* } } } */
-/* { dg-final { memmodel-gdb-test } } */
-
-#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;
-}
===================================================================
@@ -1,49 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "--param allow-store-data-races=0" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-#include <stdio.h>
-#include "memmodel.h"
-
-/* This file tests that speculative store movement out of a loop doesn't
- happen. This is disallowed when --param allow-store-data-races is 0. */
-
-int global = 100;
-
-/* Other thread makes sure global is 100 before the next instruction is
- * exceuted. */
-void memmodel_other_threads()
-{
- global = 100;
-}
-
-int memmodel_step_verify()
-{
- if (global != 100)
- {
- printf("FAIL: global variable was assigned to. \n");
- return 1;
- }
-}
-
-int memmodel_final_verify()
-{
- return 0;
-}
-
-/* The variable global should never be assigned if func(0) is called.
- This tests store movement out of loop thats never executed. */
-void test (int y)
-{
- int x;
- for (x=0; x< y; x++)
- {
- global = y; /* This should never speculatively execute. */
- }
-}
-
-int main()
-{
- test(0);
- memmodel_done();
-}
===================================================================
@@ -1,87 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "--param allow-packed-store-data-races=0" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-#include <stdio.h>
-#include "memmodel.h"
-
-/* This test verifies that data races aren't introduced by structure subfield
- stores. */
-
-struct test_struct {
- char a;
- char b;
- char c;
- char d;
-} var = {0,0,0,0};
-
-
-/* This routine sets field a to 'x'. If executed properly, it will
- not affect any of the other fields in the structure. An improper
- implementation may load an entire word, change the 8 bits for field
- 'a' and write the entire word back out. */
-__attribute__((noinline))
-void set_a(char x)
-{
- var.a = x;
-}
-
-static int global = 0;
-
-/* The other thread increments the value of each of the other fields
- in the structure every cycle. If the store to the 'a' field does
- an incorrect full or partial word load, mask and store, it will
- write back an incorrect value to one or more of the other
- fields. */
-void memmodel_other_threads()
-{
- global++;
- var.b = global;
- var.c = global;
- var.d = global;
-}
-
-
-/* Make sure that none of the other fields have been changed. */
-int memmodel_step_verify()
-{
- int ret = 0;
- if (var.b != global)
- {
- printf("FAIL: Unexpected value. var.b is %d, should be %d\n",
- var.b, global);
- ret = 1;
- }
- if (var.c != global)
- {
- printf("FAIL: Unexpected value. var.c is %d, should be %d\n",
- var.c, global);
- ret = 1;
- }
- if (var.d != global)
- {
- printf("FAIL: Unexpected value. var.d is %d, should be %d\n",
- var.d, global);
- ret = 1;
- }
- return ret;
-}
-
-/* Verify that every variable has the correct value. */
-int memmodel_final_verify()
-{
- int ret = memmodel_step_verify();
- if (var.a != 1)
- {
- printf("FAIL: Unexpected value. var.a is %d, should be %d\n", var.a, 1);
- ret = 1;
- }
- return ret;
-}
-
-int main ()
-{
- set_a(1);
- memmodel_done();
- return 0;
-}
===================================================================
@@ -1,111 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -1,112 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -1 +0,0 @@
-#include "../../gcc.dg/guality/guality.h"
===================================================================
@@ -1,111 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -1,112 +0,0 @@
-/* { 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;
-}
===================================================================
@@ -0,0 +1,58 @@
+/* { dg-do link } */
+/* { dg-options "-std=c++0x" } */
+/* { dg-final { simulate-thread } } */
+
+using namespace std;
+
+#include <atomic>
+#include <limits.h>
+#include <stdio.h>
+#include "simulate-thread.h"
+
+atomic_int atomi;
+
+/* Non-atomic. Use a type wide enough to possibly coerce GCC into
+ moving things around. */
+long double j;
+
+
+/* Test that an atomic store synchronizes with an atomic load.
+
+ In this case, test that the store to <j> happens-before the atomic
+ store to <atomi>. Make sure the compiler does not reorder the
+ stores. */
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ j = 13.0;
+ atomi.store(1);
+}
+
+int main ()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
+
+void simulate_thread_other_threads()
+{
+}
+
+/* Verify that side-effects before an atomic store are correctly
+ synchronized with the an atomic load to the same location. */
+int simulate_thread_step_verify()
+{
+ if (atomi.load() == 1 && j != 13.0)
+ {
+ printf ("FAIL: invalid synchronization for atomic load/store.\n");
+ return 1;
+ }
+ return 0;
+}
+
+
+int simulate_thread_final_verify()
+{
+ return simulate_thread_step_verify();
+}
===================================================================
@@ -0,0 +1,39 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+
+# Your run of the mill dg test, but verify that we have a working GDB first.
+
+load_lib g++-dg.exp
+load_lib gcc-simulate-thread.exp
+load_lib torture-options.exp
+
+dg-init
+torture-init
+set-torture-options [list \
+ { -O0 -g } \
+ { -O1 -g } \
+ { -O2 -g } \
+ { -O3 -g } \
+ { -Os -g } ]
+
+if [gdb-exists] {
+ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] ""
+ gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/simulate-thread/*.c]] ""
+}
+
+torture-finish
+dg-finish
===================================================================
@@ -0,0 +1 @@
+source ../../gcc.dg/simulate-thread/simulate-thread.gdb
===================================================================
@@ -0,0 +1,77 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-load-data-races=0 --param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+/* Test that setting <var.a> does not touch either <var.b> or <var.c>.
+ In the C++ memory model, non contiguous bitfields ("a" and "c"
+ here) should be considered as distinct memory locations, so we
+ can't use bit twiddling to set either one. */
+
+#include <stdio.h>
+#include "simulate-thread.h"
+
+#define CONSTA 12
+
+static int global;
+struct S
+{
+ unsigned int a : 4;
+ unsigned char b;
+ unsigned int c : 6;
+} var;
+
+__attribute__((noinline))
+void set_a()
+{
+ var.a = CONSTA;
+}
+
+void simulate_thread_other_threads()
+{
+ ++global;
+ var.b = global;
+ var.c = global;
+}
+
+int simulate_thread_step_verify()
+{
+ int ret = 0;
+ if (var.b != global)
+ {
+ printf ("FAIL: Unexpected value: var.b is %d, should be %d\n",
+ var.b, global);
+ ret = 1;
+ }
+ if (var.c != global)
+ {
+ printf ("FAIL: Unexpected value: var.c is %d, should be %d\n",
+ var.c, global);
+ ret = 1;
+ }
+ return ret;
+}
+
+int simulate_thread_final_verify()
+{
+ int ret = simulate_thread_step_verify();
+ if (var.a != CONSTA)
+ {
+ printf ("FAIL: Unexpected value: var.a is %d, should be %d\n",
+ var.a, CONSTA);
+ ret = 1;
+ }
+ return ret;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ set_a();
+}
+
+int main()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,80 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-load-data-races=0 --param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+/* Test that setting <var.a> does not touch either <var.b> or <var.c>.
+ In the C++ memory model, non contiguous bitfields ("a" and "c"
+ here) should be considered as distinct memory locations, so we
+ can't use bit twiddling to set either one. */
+
+#include <stdio.h>
+#include "simulate-thread.h"
+
+#define CONSTA 12
+
+static int global;
+struct S
+{
+ /* On x86-64, the volatile causes us to access <a> with a 32-bit
+ access, and thus trigger this test. */
+ volatile unsigned int a : 4;
+
+ unsigned char b;
+ unsigned int c : 6;
+} var;
+
+__attribute__((noinline))
+void set_a()
+{
+ var.a = CONSTA;
+}
+
+void simulate_thread_other_threads()
+{
+ ++global;
+ var.b = global;
+ var.c = global;
+}
+
+int simulate_thread_step_verify()
+{
+ int ret = 0;
+ if (var.b != global)
+ {
+ printf ("FAIL: Unexpected value: var.b is %d, should be %d\n",
+ var.b, global);
+ ret = 1;
+ }
+ if (var.c != global)
+ {
+ printf ("FAIL: Unexpected value: var.c is %d, should be %d\n",
+ var.c, global);
+ ret = 1;
+ }
+ return ret;
+}
+
+int simulate_thread_final_verify()
+{
+ int ret = simulate_thread_step_verify();
+ if (var.a != CONSTA)
+ {
+ printf ("FAIL: Unexpected value: var.a is %d, should be %d\n",
+ var.a, CONSTA);
+ ret = 1;
+ }
+ return ret;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ set_a();
+}
+
+int main ()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,73 @@
+/* { dg-do link } */
+/* { dg-options "-std=c++0x" } */
+/* { dg-final { simulate-thread } } */
+
+/* Test that atomic int and atomic char work properly. */
+
+using namespace std;
+
+#include <atomic>
+#include <limits.h>
+#include <stdio.h>
+#include "simulate-thread.h"
+
+atomic<int> atomi;
+atomic<char> atomc;
+
+/* No need for parallel threads to do anything */
+void simulate_thread_other_threads()
+{
+}
+
+/* Verify after every instruction is executed, that the atmoic int and
+ char have one of the 2 legitimate values. */
+int simulate_thread_step_verify()
+{
+ if (atomi != 0 && atomi != INT_MAX)
+ {
+ printf ("FAIL: invalid intermediate result for atomi (%d).\n",
+ (int)atomi);
+ return 1;
+ }
+ if (atomc != 0 && atomc != CHAR_MAX)
+ {
+ printf ("FAIL: invalid intermediate result for atomc (%d).\n",
+ (int)atomc);
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Verify that both atmoics have the corerct value. */
+int simulate_thread_final_verify()
+{
+ if (atomi != INT_MAX)
+ {
+ printf ("FAIL: invalid final result for atomi (%d).\n",
+ (int)atomi);
+ return 1;
+ }
+ if (atomc != CHAR_MAX)
+ {
+ printf ("FAIL: invalid final result for atomc (%d).\n",
+ (int)atomc);
+ return 1;
+ }
+ return 0;
+}
+
+/* Test a store to an atomic int and an atomic char. */
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ atomi = INT_MAX;
+ atomc = CHAR_MAX;
+}
+
+int main ()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1 @@
+#include "../../gcc.dg/simulate-thread/simulate-thread.h"
===================================================================
@@ -1,51 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "-std=c++0x" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-using namespace std;
-
-#include <atomic>
-#include <limits.h>
-#include <stdio.h>
-#include "memmodel.h"
-
-atomic_int atomi;
-
-/* Non-atomic. Use a type wide enough to possibly coerce GCC into
- moving things around. */
-long double j;
-
-
-/* Test that an atomic store synchronizes with an atomic load.
-
- In this case, test that the store to <j> happens-before the atomic
- store to <atomi>. Make sure the compiler does not reorder the
- stores. */
-main()
-{
- j = 13.0;
- atomi.store(1);
- memmodel_done();
-}
-
-void memmodel_other_threads()
-{
-}
-
-/* Verify that side-effects before an atomic store are correctly
- synchronized with the an atomic load to the same location. */
-int memmodel_step_verify()
-{
- if (atomi.load() == 1 && j != 13.0)
- {
- printf ("FAIL: invalid synchronization for atomic load/store.\n");
- return 1;
- }
- return 0;
-}
-
-
-int memmodel_final_verify()
-{
- return memmodel_step_verify();
-}
===================================================================
@@ -1 +0,0 @@
-#include "../../gcc.dg/guality/guality.h"
===================================================================
@@ -1 +0,0 @@
-#include "../../gcc.dg/memmodel/memmodel.h"
===================================================================
@@ -1,71 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "--param allow-load-data-races=0 --param allow-store-data-races=0" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-/* Test that setting <var.a> does not touch either <var.b> or <var.c>.
- In the C++ memory model, non contiguous bitfields ("a" and "c"
- here) should be considered as distinct memory locations, so we
- can't use bit twiddling to set either one. */
-
-#include <stdio.h>
-#include "memmodel.h"
-
-#define CONSTA 12
-
-static int global;
-struct S
-{
- unsigned int a : 4;
- unsigned char b;
- unsigned int c : 6;
-} var;
-
-__attribute__((noinline))
-void set_a()
-{
- var.a = CONSTA;
-}
-
-void memmodel_other_threads()
-{
- ++global;
- var.b = global;
- var.c = global;
-}
-
-int memmodel_step_verify()
-{
- int ret = 0;
- if (var.b != global)
- {
- printf ("FAIL: Unexpected value: var.b is %d, should be %d\n",
- var.b, global);
- ret = 1;
- }
- if (var.c != global)
- {
- printf ("FAIL: Unexpected value: var.c is %d, should be %d\n",
- var.c, global);
- ret = 1;
- }
- return ret;
-}
-
-int memmodel_final_verify()
-{
- int ret = memmodel_step_verify();
- if (var.a != CONSTA)
- {
- printf ("FAIL: Unexpected value: var.a is %d, should be %d\n",
- var.a, CONSTA);
- ret = 1;
- }
- return ret;
-}
-
-int main()
-{
- set_a();
- memmodel_done();
- return 0;
-}
===================================================================
@@ -1,74 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "--param allow-load-data-races=0 --param allow-store-data-races=0" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-/* Test that setting <var.a> does not touch either <var.b> or <var.c>.
- In the C++ memory model, non contiguous bitfields ("a" and "c"
- here) should be considered as distinct memory locations, so we
- can't use bit twiddling to set either one. */
-
-#include <stdio.h>
-#include "memmodel.h"
-
-#define CONSTA 12
-
-static int global;
-struct S
-{
- /* On x86-64, the volatile causes us to access <a> with a 32-bit
- access, and thus trigger this test. */
- volatile unsigned int a : 4;
-
- unsigned char b;
- unsigned int c : 6;
-} var;
-
-__attribute__((noinline))
-void set_a()
-{
- var.a = CONSTA;
-}
-
-void memmodel_other_threads()
-{
- ++global;
- var.b = global;
- var.c = global;
-}
-
-int memmodel_step_verify()
-{
- int ret = 0;
- if (var.b != global)
- {
- printf ("FAIL: Unexpected value: var.b is %d, should be %d\n",
- var.b, global);
- ret = 1;
- }
- if (var.c != global)
- {
- printf ("FAIL: Unexpected value: var.c is %d, should be %d\n",
- var.c, global);
- ret = 1;
- }
- return ret;
-}
-
-int memmodel_final_verify()
-{
- int ret = memmodel_step_verify();
- if (var.a != CONSTA)
- {
- printf ("FAIL: Unexpected value: var.a is %d, should be %d\n",
- var.a, CONSTA);
- ret = 1;
- }
- return ret;
-}
-
-int main()
-{
- set_a();
- memmodel_done();
- return 0;
-}
===================================================================
@@ -1 +0,0 @@
-source ../../gcc.dg/memmodel/memmodel.gdb
===================================================================
@@ -1,58 +0,0 @@
-# Your run of the mill dg test, but verify that we have a working GDB first.
-
-load_lib g++-dg.exp
-load_lib gcc-memmodel-gdb-test.exp
-load_lib torture-options.exp
-
-proc check_guality {args} {
- set result [eval check_compile guality_check executable $args "-g -O0"]
- set lines [lindex $result 0]
- set output [lindex $result 1]
- set ret 0
- if {[string match "" $lines]} {
- set execout [g++_load "./$output"]
- set ret [string match "*1 PASS, 0 FAIL, 0 UNRESOLVED*" $execout]
- }
- remote_file build delete $output
- return $ret
-}
-
-dg-init
-torture-init
-set-torture-options [list \
- { -O0 -g } \
- { -O1 -g } \
- { -O2 -g } \
- { -O3 -g } \
- { -Os -g } ]
-
-# Test the presence of gdb with the guality infrastructure.
-global GDB
-if ![info exists ::env(GUALITY_GDB_NAME)] {
- if [info exists GDB] {
- set guality_gdb_name "$GDB"
- } else {
- set guality_gdb_name "[transform gdb]"
- }
- setenv GUALITY_GDB_NAME "$guality_gdb_name"
-}
-if {[check_guality "
- #include \"$srcdir/$subdir/guality.h\"
- volatile long int varl = 6;
- int main (int argc, char *argv\[\])
- {
- GUALCHKVAL (varl);
- return 0;
- }
-"]} {
- gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.C]] ""
-# Uncomment line below when we have common C/C++ tests.
-# gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/memmodel/*.c]] ""
-}
-
-if [info exists guality_gdb_name] {
- unsetenv GUALITY_GDB_NAME
-}
-
-torture-finish
-dg-finish
===================================================================
@@ -1,66 +0,0 @@
-/* { dg-do link } */
-/* { dg-options "-std=c++0x" } */
-/* { dg-final { memmodel-gdb-test } } */
-
-/* Test that atomic int and atomic char work properly. */
-
-using namespace std;
-
-#include <atomic>
-#include <limits.h>
-#include <stdio.h>
-#include "memmodel.h"
-
-atomic<int> atomi;
-atomic<char> atomc;
-
-/* No need for parallel threads to do anything */
-void memmodel_other_threads()
-{
-}
-
-/* Verify after every instruction is executed, that the atmoic int and
- char have one of the 2 legitimate values. */
-int memmodel_step_verify()
-{
- if (atomi != 0 && atomi != INT_MAX)
- {
- printf ("FAIL: invalid intermediate result for atomi (%d).\n",
- (int)atomi);
- return 1;
- }
- if (atomc != 0 && atomc != CHAR_MAX)
- {
- printf ("FAIL: invalid intermediate result for atomc (%d).\n",
- (int)atomc);
- return 1;
- }
- return 0;
-}
-
-
-/* Verify that both atmoics have the corerct value. */
-int memmodel_final_verify()
-{
- if (atomi != INT_MAX)
- {
- printf ("FAIL: invalid final result for atomi (%d).\n",
- (int)atomi);
- return 1;
- }
- if (atomc != CHAR_MAX)
- {
- printf ("FAIL: invalid final result for atomc (%d).\n",
- (int)atomc);
- return 1;
- }
- return 0;
-}
-
-/* Test a store to an atomic int and an atomic char. */
-main()
-{
- atomi = INT_MAX;
- atomc = CHAR_MAX;
- memmodel_done();
-}
===================================================================
@@ -0,0 +1,71 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "../../gcc.dg/simulate-thread/simulate-thread.h"
+
+/* Test that we don't store past VAR.A. */
+
+struct S
+{
+ volatile unsigned int a : 4;
+ unsigned char b;
+ unsigned int c : 6;
+} var = { 1, 2, 3 };
+
+static int global = 0;
+
+/* Called before each instruction, simulating another thread
+ executing. */
+void simulate_thread_other_threads()
+{
+ global++;
+ var.b = global;
+ /* Don't go past the 6 bits var.c can hold. */
+ var.c = global % 64;
+}
+
+/* Called after each instruction. Returns 1 if any inconsistency is
+ found, 0 otherwise. */
+int simulate_thread_step_verify()
+{
+ int ret = 0;
+ if (var.b != global)
+ {
+ printf("FAIL: invalid intermediate value for <b>.\n");
+ ret = 1;
+ }
+ if (var.c != global % 64)
+ {
+ printf("FAIL: invalid intermediate value for <c>.\n");
+ ret = 1;
+ }
+ return ret;
+}
+
+/* Called at the end of the program (simulate_thread_fini == 1). Verifies
+ the state of the program and returns 1 if any inconsistency is
+ found, 0 otherwise. */
+int simulate_thread_final_verify()
+{
+ if (var.a != 12)
+ {
+ printf("FAIL: invalid final result for <a>.\n");
+ return 1;
+ }
+ return 0;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ var.a = 12;
+}
+
+int main()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,59 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "../../gcc.dg/simulate-thread/simulate-thread.h"
+
+/* Test that we don't store past VAR.K. */
+
+struct S
+{
+ volatile int i;
+ volatile int j: 32;
+ volatile int k: 15;
+ volatile unsigned char c[2];
+} var;
+
+static int global = 0;
+
+void simulate_thread_other_threads()
+{
+ global++;
+ var.c[0] = global % 256;
+ var.c[1] = global % 256;
+}
+
+int simulate_thread_step_verify()
+{
+ if (var.c[0] != global % 256
+ || var.c[1] != global % 256)
+ {
+ printf("FAIL: invalid intermediate result for <var.c[]>.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_final_verify()
+{
+ if (var.k != 13)
+ {
+ printf("FAIL: invalid final result\n");
+ return 1;
+ }
+ return 0;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ var.k = 13;
+}
+
+int main()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,63 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include "../../gcc.dg/simulate-thread/simulate-thread.h"
+
+/* Store into <c> should not clobber <d>. */
+
+struct bits
+{
+ char a;
+ int b:7;
+ int c:9;
+ unsigned char d;
+} var;
+
+static int global = 0;
+
+void simulate_thread_other_threads()
+{
+ global++;
+ var.d = global;
+}
+
+int simulate_thread_step_verify()
+{
+ if (var.d != global)
+ {
+ printf("FAIL: invalid intermediate result\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_final_verify()
+{
+ if (var.c != 5)
+ {
+ printf("FAIL: invalid final result\n");
+ return 1;
+ }
+ return 0;
+}
+
+__attribute__((noinline))
+void update_c(struct bits *p, int val)
+{
+ p -> c = val;
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ update_c(&var, 5);
+}
+
+int main()
+{
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -0,0 +1,60 @@
+/* { dg-do link } */
+/* { dg-options "--param allow-store-data-races=0" } */
+/* { dg-final { simulate-thread } } */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../gcc.dg/simulate-thread/simulate-thread.h"
+
+struct bits
+{
+ char a;
+ int b:7;
+ int c:9;
+ unsigned char d;
+} *p;
+
+static int global = 0;
+
+void simulate_thread_other_threads()
+{
+ global++;
+ p->d = global % 256;
+}
+
+int simulate_thread_step_verify()
+{
+ if (p->d != global % 256)
+ {
+ printf("FAIL: invalid intermediate result\n");
+ return 1;
+ }
+ return 0;
+}
+
+int simulate_thread_final_verify()
+{
+ if (p->c != 55)
+ {
+ printf("FAIL: invalid final result\n");
+ return 1;
+ }
+ return 0;
+}
+
+/* Store into <c> should not clobber <d>. */
+/* We should not use a 32-bit move to store into p->, but a smaller move. */
+__attribute__((noinline))
+void simulate_thread_main()
+{
+ p -> c = 55;
+}
+
+
+int main()
+{
+ p = (struct bits *) calloc (1, sizeof (struct bits));
+ simulate_thread_main();
+ simulate_thread_done();
+ return 0;
+}
===================================================================
@@ -1,19 +0,0 @@
-/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
-/* { dg-options "-O2 --param allow-store-data-races=0" } */
-
-/* Test that we don't store past VAR.K. */
-
-struct S
-{
- volatile int i;
- volatile int j: 32;
- volatile int k: 15;
- volatile char c[2];
-} var;
-
-void setit()
-{
- var.k = 13;
-}
-
-/* { dg-final { scan-assembler-not "movl.*, var" } } */
===================================================================
@@ -1,18 +0,0 @@
-/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
-/* { dg-options "-O2 --param allow-store-data-races=0" } */
-
-/* Test that we don't store past VAR.A. */
-
-struct S
-{
- volatile unsigned int a : 4;
- unsigned char b;
- unsigned int c : 6;
-} var;
-
-void set_a()
-{
- var.a = 12;
-}
-
-/* { dg-final { scan-assembler-not "movl.*, var" } } */
===================================================================
@@ -1,18 +0,0 @@
-/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
-/* { dg-options "-O2 --param allow-store-data-races=0" } */
-
-struct bits
-{
- char a;
- int b:7;
- int c:9;
- unsigned char d;
-} x;
-
-/* Store into <c> should not clobber <d>. */
-void update_c(struct bits *p, int val)
-{
- p -> c = val;
-}
-
-/* { dg-final { scan-assembler "mov\[bw\]" } } */
===================================================================
@@ -1,29 +0,0 @@
-/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
-/* { dg-options "-O2 --param allow-store-data-races=0" } */
-
-#include <stdlib.h>
-
-struct bits
-{
- char a;
- int b:7;
- int c:9;
- unsigned char d;
-} x;
-
-struct bits *p;
-
-static void allocit()
-{
- p = (struct bits *) malloc (sizeof (struct bits));
-}
-
-/* Store into <c> should not clobber <d>. */
-/* We should not use a 32-bit move to store into p->, but a smaller move. */
-void foo()
-{
- allocit();
- p -> c = 55;
-}
-
-/* { dg-final { scan-assembler "mov\[bw\]" } } */