From patchwork Mon Oct 10 15:37:40 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aldy Hernandez X-Patchwork-Id: 118762 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id D1E56B70FE for ; Tue, 11 Oct 2011 02:38:45 +1100 (EST) Received: (qmail 771 invoked by alias); 10 Oct 2011 15:38:42 -0000 Received: (qmail 31463 invoked by uid 22791); 10 Oct 2011 15:38:24 -0000 X-SWARE-Spam-Status: No, hits=-6.6 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, SPF_HELO_PASS X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 10 Oct 2011 15:37:42 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p9AFbfNi020222 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 10 Oct 2011 11:37:41 -0400 Received: from houston.quesejoda.com (vpn-237-42.phx2.redhat.com [10.3.237.42]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p9AFbeXo025439; Mon, 10 Oct 2011 11:37:40 -0400 Message-ID: <4E931144.6010702@redhat.com> Date: Mon, 10 Oct 2011 10:37:40 -0500 From: Aldy Hernandez User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:6.0.2) Gecko/20110906 Thunderbird/6.0.2 MIME-Version: 1.0 To: gcc-patches , Andrew MacLeod Subject: [cxx-mem-model] merge simulate-thread changes to branch Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org I have merged the simulate-thread changes from trunk into the branch. And yes, I merged in your infinite loop changes to the testing harness. I did, however, choose to keep the "simulate_thread_memmodel_fini" name as opposed to your "__gdb_*_fini" changes, so we can be consistent throughout. I also updated all the tests in the branch to use the simulate_thread_main() stub. Committing to branch. Merge from mainline: 2011-10-10 Aldy Hernandez * lib/gcc-simulate-thread.exp: New. * gcc.dg/simulate-thread/guality.h: New. * gcc.dg/simulate-thread/simulate-thread.h: New. * gcc.dg/simulate-thread/simulate-thread.exp: New. * gcc.dg/simulate-thread/simulate-thread.gdb: New. * gcc.dg/simulate-thread/README: New. * g++.dg/simulate-thread/guality.h: New. * g++.dg/simulate-thread/simulate-thread.h: New. * g++.dg/simulate-thread/simulate-thread.exp: New. * g++.dg/simulate-thread/simulate-thread.gdb: New. * c-c++-common/cxxbitfields-2.c: Remove. * c-c++-common/cxxbitfields.c: Remove. * c-c++-common/cxxbitfields-4.c: Remove. * c-c++-common/cxxbitfields-5.c: Remove. * c-c++-common/simulate-thread/bitfields-1.c: New. * c-c++-common/simulate-thread/bitfields-2.c: New. * c-c++-common/simulate-thread/bitfields-3.c: New. * c-c++-common/simulate-thread/bitfields-4.c: New. Index: lib/gcc-dg.exp =================================================================== --- lib/gcc-dg.exp (revision 179750) +++ lib/gcc-dg.exp (working copy) @@ -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 "" Index: lib/gcc-memmodel-gdb-test.exp =================================================================== --- lib/gcc-memmodel-gdb-test.exp (revision 179750) +++ lib/gcc-memmodel-gdb-test.exp (working copy) @@ -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 -# . - -# 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 -} Index: lib/gcc-simulate-thread.exp =================================================================== --- lib/gcc-simulate-thread.exp (revision 0) +++ lib/gcc-simulate-thread.exp (revision 0) @@ -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 +# . + +# 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 +} Index: gcc.dg/simulate-thread/strict-align-global.c =================================================================== --- gcc.dg/simulate-thread/strict-align-global.c (revision 0) +++ gcc.dg/simulate-thread/strict-align-global.c (revision 0) @@ -0,0 +1,52 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-packed-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#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. 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. 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; +} Index: gcc.dg/simulate-thread/simulate-thread.exp =================================================================== --- gcc.dg/simulate-thread/simulate-thread.exp (revision 0) +++ gcc.dg/simulate-thread/simulate-thread.exp (revision 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 +# . + +# 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 Index: gcc.dg/simulate-thread/simulate-thread.gdb =================================================================== --- gcc.dg/simulate-thread/simulate-thread.gdb (revision 0) +++ gcc.dg/simulate-thread/simulate-thread.gdb (revision 0) @@ -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 Index: gcc.dg/simulate-thread/sync-load-longlong.c =================================================================== --- gcc.dg/simulate-thread/sync-load-longlong.c (revision 0) +++ gcc.dg/simulate-thread/sync-load-longlong.c (revision 0) @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_long_long } */ +/* { dg-options "" } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-other-longlong.c =================================================================== --- gcc.dg/simulate-thread/sync-other-longlong.c (revision 0) +++ gcc.dg/simulate-thread/sync-other-longlong.c (revision 0) @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_long_long } */ +/* { dg-options "" } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/README =================================================================== --- gcc.dg/simulate-thread/README (revision 0) +++ gcc.dg/simulate-thread/README (revision 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; +} Index: gcc.dg/simulate-thread/sync-load-int128.c =================================================================== --- gcc.dg/simulate-thread/sync-load-int128.c (revision 0) +++ gcc.dg/simulate-thread/sync-load-int128.c (revision 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 +#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; +} Index: gcc.dg/simulate-thread/speculative-store.c =================================================================== --- gcc.dg/simulate-thread/speculative-store.c (revision 0) +++ gcc.dg/simulate-thread/speculative-store.c (revision 0) @@ -0,0 +1,57 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-other-int128.c =================================================================== --- gcc.dg/simulate-thread/sync-other-int128.c (revision 0) +++ gcc.dg/simulate-thread/sync-other-int128.c (revision 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 +#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; +} Index: gcc.dg/simulate-thread/subfields.c =================================================================== --- gcc.dg/simulate-thread/subfields.c (revision 0) +++ gcc.dg/simulate-thread/subfields.c (revision 0) @@ -0,0 +1,93 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-packed-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-load-int.c =================================================================== --- gcc.dg/simulate-thread/sync-load-int.c (revision 0) +++ gcc.dg/simulate-thread/sync-load-int.c (revision 0) @@ -0,0 +1,116 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_long } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-other-int.c =================================================================== --- gcc.dg/simulate-thread/sync-other-int.c (revision 0) +++ gcc.dg/simulate-thread/sync-other-int.c (revision 0) @@ -0,0 +1,118 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_long } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-load-short.c =================================================================== --- gcc.dg/simulate-thread/sync-load-short.c (revision 0) +++ gcc.dg/simulate-thread/sync-load-short.c (revision 0) @@ -0,0 +1,116 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_char_short } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/sync-other-short.c =================================================================== --- gcc.dg/simulate-thread/sync-other-short.c (revision 0) +++ gcc.dg/simulate-thread/sync-other-short.c (revision 0) @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_char_short } */ +/* { dg-final { simulate-thread } } */ + + +#include +#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; +} Index: gcc.dg/simulate-thread/simulate-thread.h =================================================================== --- gcc.dg/simulate-thread/simulate-thread.h (revision 0) +++ gcc.dg/simulate-thread/simulate-thread.h (revision 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; +} Index: gcc.dg/memmodel/strict-align-global.c =================================================================== --- gcc.dg/memmodel/strict-align-global.c (revision 179750) +++ gcc.dg/memmodel/strict-align-global.c (working copy) @@ -1,46 +0,0 @@ -/* { dg-do link } */ -/* { dg-options "--param allow-packed-store-data-races=0" } */ -/* { dg-final { memmodel-gdb-test } } */ - -#include -#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. 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. is %d, should be 66\n", a); - return 1; - } - return ret; -} - -int main () -{ - a = 66; - memmodel_done(); - return 0; -} Index: gcc.dg/memmodel/memmodel.h =================================================================== --- gcc.dg/memmodel/memmodel.h (revision 179750) +++ gcc.dg/memmodel/memmodel.h (working copy) @@ -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; -} Index: gcc.dg/memmodel/sync-load-longlong.c =================================================================== --- gcc.dg/memmodel/sync-load-longlong.c (revision 179750) +++ gcc.dg/memmodel/sync-load-longlong.c (working copy) @@ -1,112 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_long_long } */ -/* { dg-options "" } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - - -/* Testing load for atomicity is a little trickier. - - Set up the atomic value so that it changes value after every instruction - is executed. - - Simply alternating between 2 values wouldn't be sufficient since a load of - one part, followed by the load of the second part 2 instructions later would - appear to be valid. - - set up a table of 16 values which change a bit in every byte of the value - each time, this will give us a 16 instruction cycle before repetition - kicks in, which should be sufficient to detect any issues. Just to be sure, - we also change the table cycle size during execution. - - The end result is that all loads should always get one of the values from - the table. Any other pattern means the load failed. */ - -unsigned long long ret; -unsigned long long value = 0; -unsigned long long result = 0; -unsigned long long table[16] = { -0x0000000000000000, -0x1111111111111111, -0x2222222222222222, -0x3333333333333333, -0x4444444444444444, -0x5555555555555555, -0x6666666666666666, -0x7777777777777777, -0x8888888888888888, -0x9999999999999999, -0xAAAAAAAAAAAAAAAA, -0xBBBBBBBBBBBBBBBB, -0xCCCCCCCCCCCCCCCC, -0xDDDDDDDDDDDDDDDD, -0xEEEEEEEEEEEEEEEE, -0xFFFFFFFFFFFFFFFF -}; - -int table_cycle_size = 16; - -/* Return 0 if 'result' is a valid value to have loaded. */ -int verify_result () -{ - int x; - int found = 0; - - /* Check entire table for valid values. */ - for (x = 0; x < 16 ; x++) - if (result == table[x]) - { - found = 1; - break; - } - - if (!found) - printf("FAIL: Invalid result returned from fetch\n"); - - return !found; -} - -/* Iterate VALUE through the different valid values. */ -void memmodel_other_threads () -{ - static int current = 0; - - if (++current >= table_cycle_size) - current = 0; - value = table[current]; -} - -int memmodel_step_verify () -{ - return verify_result (); -} - -int memmodel_final_verify () -{ - return verify_result (); -} - -main() -{ - int x; - - /* Execute loads with value changing at various cyclic values. */ - for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) - { - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - /* In order to verify the returned value (which is not atomic), it needs - to be atomically stored into another variable and check that. */ - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - - /* Execute the fetch/store a couple of times just to ensure the cycles - have a chance to be interesting. */ - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - } - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/sync-other-longlong.c =================================================================== --- gcc.dg/memmodel/sync-other-longlong.c (revision 179750) +++ gcc.dg/memmodel/sync-other-longlong.c (working copy) @@ -1,113 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_long_long } */ -/* { dg-options "" } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - -/* Test all the __sync routines for proper atomicity on 8 byte values. */ - -unsigned long long zero = 0; -unsigned long long max = ~0; - -unsigned long long changing_value = 0; -unsigned long long value = 0; -unsigned long long ret; - -void test_abort() -{ - static int reported = 0; - if (!reported) - { - printf ("FAIL: improper execution of __sync builtin.\n"); - reported = 1; - } -} - -void memmodel_other_threads () -{ -} - -int memmodel_step_verify () -{ - if (value != zero && value != max) - { - printf ("FAIL: invalid intermediate result for value.\n"); - return 1; - } - return 0; -} - -int memmodel_final_verify () -{ - if (value != 0) - { - printf ("FAIL: invalid final result for value.\n"); - return 1; - } - return 0; -} - -/* All values written to 'value' alternate between 'zero' and 'max'. Any other - value detected by memmodel_step_verify() between instructions would indicate - that the value was only partially written, and would thus fail this - atomicity test. - - This function tests each different __sync_mem routine once, with the - exception of the load instruction which requires special testing. */ -main() -{ - - ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST); - if (ret != zero || value != max) - test_abort(); - - __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST); - if (value != zero) - test_abort(); - - ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/memmodel.gdb =================================================================== --- gcc.dg/memmodel/memmodel.gdb (revision 179750) +++ gcc.dg/memmodel/memmodel.gdb (working copy) @@ -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 Index: gcc.dg/memmodel/memmodel.exp =================================================================== --- gcc.dg/memmodel/memmodel.exp (revision 179750) +++ gcc.dg/memmodel/memmodel.exp (working copy) @@ -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 Index: gcc.dg/memmodel/sync-load-int128.c =================================================================== --- gcc.dg/memmodel/sync-load-int128.c (revision 179750) +++ gcc.dg/memmodel/sync-load-int128.c (working copy) @@ -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 -#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; -} Index: gcc.dg/memmodel/README =================================================================== --- gcc.dg/memmodel/README (revision 179750) +++ gcc.dg/memmodel/README (working copy) @@ -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; -} Index: gcc.dg/memmodel/sync-other-int128.c =================================================================== --- gcc.dg/memmodel/sync-other-int128.c (revision 179750) +++ gcc.dg/memmodel/sync-other-int128.c (working copy) @@ -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 -#include "memmodel.h" - -/* Test all the __sync routines for proper atomicity on 16 byte values. */ - -__int128_t zero = 0; -__int128_t max = ~0; -__int128_t changing_value = 0; -__int128_t value = 0; -__int128_t ret; - -void test_abort() -{ - static int reported = 0; - if (!reported) - { - printf ("FAIL: improper execution of __sync builtin.\n"); - reported = 1; - } -} - -void memmodel_other_threads () -{ -} - -int memmodel_step_verify () -{ - if (value != zero && value != max) - { - printf ("FAIL: invalid intermediate result for value.\n"); - return 1; - } - return 0; -} - -int memmodel_final_verify () -{ - if (value != 0) - { - printf ("FAIL: invalid final result for value.\n"); - return 1; - } - return 0; -} - -/* All values written to 'value' alternate between 'zero' and 'max'. Any other - value detected by memmodel_step_verify() between instructions would indicate - that the value was only partially written, and would thus fail this - atomicity test. - - This function tests each different __sync_mem routine once, with the - exception of the load instruction which requires special testing. */ -main() -{ - - ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST); - if (ret != zero || value != max) - test_abort(); - - __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST); - if (value != zero) - test_abort(); - - ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/speculative-store.c =================================================================== --- gcc.dg/memmodel/speculative-store.c (revision 179750) +++ gcc.dg/memmodel/speculative-store.c (working copy) @@ -1,49 +0,0 @@ -/* { dg-do link } */ -/* { dg-options "--param allow-store-data-races=0" } */ -/* { dg-final { memmodel-gdb-test } } */ - -#include -#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(); -} Index: gcc.dg/memmodel/subfields.c =================================================================== --- gcc.dg/memmodel/subfields.c (revision 179750) +++ gcc.dg/memmodel/subfields.c (working copy) @@ -1,87 +0,0 @@ -/* { dg-do link } */ -/* { dg-options "--param allow-packed-store-data-races=0" } */ -/* { dg-final { memmodel-gdb-test } } */ - -#include -#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; -} Index: gcc.dg/memmodel/sync-load-int.c =================================================================== --- gcc.dg/memmodel/sync-load-int.c (revision 179750) +++ gcc.dg/memmodel/sync-load-int.c (working copy) @@ -1,111 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_int_long } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - - -/* Testing load for atomicity is a little trickier. - - Set up the atomic value so that it changes value after every instruction - is executed. - - Simply alternating between 2 values wouldn't be sufficient since a load of - one part, followed by the load of the second part 2 instructions later would - appear to be valid. - - set up a table of 16 values which change a bit in every byte of the value - each time, this will give us a 16 instruction cycle before repetition - kicks in, which should be sufficient to detect any issues. Just to be sure, - we also change the table cycle size during execution. - - The end result is that all loads should always get one of the values from - the table. Any other pattern means the load failed. */ - -unsigned int ret; -unsigned int value = 0; -unsigned int result = 0; -unsigned int table[16] = { -0x00000000, -0x11111111, -0x22222222, -0x33333333, -0x44444444, -0x55555555, -0x66666666, -0x77777777, -0x88888888, -0x99999999, -0xAAAAAAAA, -0xBBBBBBBB, -0xCCCCCCCC, -0xDDDDDDDD, -0xEEEEEEEE, -0xFFFFFFFF -}; - -int table_cycle_size = 16; - -/* Return 0 if 'result' is a valid value to have loaded. */ -int verify_result () -{ - int x; - int found = 0; - - /* Check entire table for valid values. */ - for (x = 0; x < 16 ; x++) - if (result == table[x]) - { - found = 1; - break; - } - - if (!found) - printf("FAIL: Invalid result returned from fetch\n"); - - return !found; -} - -/* Iterate VALUE through the different valid values. */ -void memmodel_other_threads () -{ - static int current = 0; - - if (++current >= table_cycle_size) - current = 0; - value = table[current]; -} - -int memmodel_step_verify () -{ - return verify_result (); -} - -int memmodel_final_verify () -{ - return verify_result (); -} - -main() -{ - int x; - - /* Execute loads with value changing at various cyclic values. */ - for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) - { - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - /* In order to verify the returned value (which is not atomic), it needs - to be atomically stored into another variable and check that. */ - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - - /* Execute the fetch/store a couple of times just to ensure the cycles - have a chance to be interesting. */ - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - } - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/sync-other-int.c =================================================================== --- gcc.dg/memmodel/sync-other-int.c (revision 179750) +++ gcc.dg/memmodel/sync-other-int.c (working copy) @@ -1,112 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_int_long } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - -/* Test all the __sync routines for proper atomicity on 4 byte values. */ - -unsigned int zero = 0; -unsigned int max = ~0; - -unsigned int changing_value = 0; -unsigned int value = 0; -unsigned int ret; - -void test_abort() -{ - static int reported = 0; - if (!reported) - { - printf ("FAIL: improper execution of __sync builtin.\n"); - reported = 1; - } -} - -void memmodel_other_threads () -{ -} - -int memmodel_step_verify () -{ - if (value != zero && value != max) - { - printf ("FAIL: invalid intermediate result for value.\n"); - return 1; - } - return 0; -} - -int memmodel_final_verify () -{ - if (value != 0) - { - printf ("FAIL: invalid final result for value.\n"); - return 1; - } - return 0; -} - -/* All values written to 'value' alternate between 'zero' and 'max'. Any other - value detected by memmodel_step_verify() between instructions would indicate - that the value was only partially written, and would thus fail this - atomicity test. - - This function tests each different __sync_mem routine once, with the - exception of the load instruction which requires special testing. */ -main() -{ - - ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST); - if (ret != zero || value != max) - test_abort(); - - __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST); - if (value != zero) - test_abort(); - - ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/guality.h =================================================================== --- gcc.dg/memmodel/guality.h (revision 179750) +++ gcc.dg/memmodel/guality.h (working copy) @@ -1 +0,0 @@ -#include "../../gcc.dg/guality/guality.h" Index: gcc.dg/memmodel/sync-load-short.c =================================================================== --- gcc.dg/memmodel/sync-load-short.c (revision 179750) +++ gcc.dg/memmodel/sync-load-short.c (working copy) @@ -1,111 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_char_short } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - - -/* Testing load for atomicity is a little trickier. - - Set up the atomic value so that it changes value after every instruction - is executed. - - Simply alternating between 2 values wouldn't be sufficient since a load of - one part, followed by the load of the second part 2 instructions later would - appear to be valid. - - set up a table of 16 values which change a bit in every byte of the value - each time, this will give us a 16 instruction cycle before repetition - kicks in, which should be sufficient to detect any issues. Just to be sure, - we also change the table cycle size during execution. - - The end result is that all loads should always get one of the values from - the table. Any other pattern means the load failed. */ - -unsigned short ret; -unsigned short value = 0; -unsigned short result = 0; -unsigned short table[16] = { -0x0000, -0x1111, -0x2222, -0x3333, -0x4444, -0x5555, -0x6666, -0x7777, -0x8888, -0x9999, -0xAAAA, -0xBBBB, -0xCCCC, -0xDDDD, -0xEEEE, -0xFFFF -}; - -int table_cycle_size = 16; - -/* Return 0 if 'result' is a valid value to have loaded. */ -int verify_result () -{ - int x; - int found = 0; - - /* Check entire table for valid values. */ - for (x = 0; x < 16 ; x++) - if (result == table[x]) - { - found = 1; - break; - } - - if (!found) - printf("FAIL: Invalid result returned from fetch\n"); - - return !found; -} - -/* Iterate VALUE through the different valid values. */ -void memmodel_other_threads () -{ - static int current = 0; - - if (++current >= table_cycle_size) - current = 0; - value = table[current]; -} - -int memmodel_step_verify () -{ - return verify_result (); -} - -int memmodel_final_verify () -{ - return verify_result (); -} - -main() -{ - int x; - - /* Execute loads with value changing at various cyclic values. */ - for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) - { - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - /* In order to verify the returned value (which is not atomic), it needs - to be atomically stored into another variable and check that. */ - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - - /* Execute the fetch/store a couple of times just to ensure the cycles - have a chance to be interesting. */ - ret = __sync_mem_load (&value, __SYNC_MEM_SEQ_CST); - __sync_mem_store (&result, ret, __SYNC_MEM_SEQ_CST); - } - - memmodel_done (); - return 0; -} Index: gcc.dg/memmodel/sync-other-short.c =================================================================== --- gcc.dg/memmodel/sync-other-short.c (revision 179750) +++ gcc.dg/memmodel/sync-other-short.c (working copy) @@ -1,112 +0,0 @@ -/* { dg-do link } */ -/* { dg-require-effective-target sync_char_short } */ -/* { dg-final { memmodel-gdb-test } } */ - - -#include -#include "memmodel.h" - -/* Test all the __sync routines for proper atomicity on 2 byte values. */ - -unsigned short zero = 0; -unsigned short max = ~0; - -unsigned short changing_value = 0; -unsigned short value = 0; -unsigned short ret; - -void test_abort() -{ - static int reported = 0; - if (!reported) - { - printf ("FAIL: improper execution of __sync builtin.\n"); - reported = 1; - } -} - -void memmodel_other_threads () -{ -} - -int memmodel_step_verify () -{ - if (value != zero && value != max) - { - printf ("FAIL: invalid intermediate result for value.\n"); - return 1; - } - return 0; -} - -int memmodel_final_verify () -{ - if (value != 0) - { - printf ("FAIL: invalid final result for value.\n"); - return 1; - } - return 0; -} - -/* All values written to 'value' alternate between 'zero' and 'max'. Any other - value detected by memmodel_step_verify() between instructions would indicate - that the value was only partially written, and would thus fail this - atomicity test. - - This function tests each different __sync_mem routine once, with the - exception of the load instruction which requires special testing. */ -main() -{ - - ret = __sync_mem_exchange (&value, max, __SYNC_MEM_SEQ_CST); - if (ret != zero || value != max) - test_abort(); - - __sync_mem_store (&value, zero, __SYNC_MEM_SEQ_CST); - if (value != zero) - test_abort(); - - ret = __sync_mem_fetch_add (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_sub (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_fetch_or (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != zero) - test_abort (); - - ret = __sync_mem_fetch_and (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_fetch_xor (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != max) - test_abort (); - - ret = __sync_mem_add_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_sub_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - ret = __sync_mem_or_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_and_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != max || ret != max) - test_abort (); - - ret = __sync_mem_xor_fetch (&value, max, __SYNC_MEM_SEQ_CST); - if (value != zero || ret != zero) - test_abort (); - - memmodel_done (); - return 0; -} Index: g++.dg/simulate-thread/atomics-2.C =================================================================== --- g++.dg/simulate-thread/atomics-2.C (revision 0) +++ g++.dg/simulate-thread/atomics-2.C (revision 0) @@ -0,0 +1,58 @@ +/* { dg-do link } */ +/* { dg-options "-std=c++0x" } */ +/* { dg-final { simulate-thread } } */ + +using namespace std; + +#include +#include +#include +#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 happens-before the atomic + store to . 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(); +} Index: g++.dg/simulate-thread/simulate-thread.exp =================================================================== --- g++.dg/simulate-thread/simulate-thread.exp (revision 0) +++ g++.dg/simulate-thread/simulate-thread.exp (revision 0) @@ -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 +# . + + +# 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 Index: g++.dg/simulate-thread/simulate-thread.gdb =================================================================== --- g++.dg/simulate-thread/simulate-thread.gdb (revision 0) +++ g++.dg/simulate-thread/simulate-thread.gdb (revision 0) @@ -0,0 +1 @@ +source ../../gcc.dg/simulate-thread/simulate-thread.gdb Index: g++.dg/simulate-thread/bitfields-2.C =================================================================== --- g++.dg/simulate-thread/bitfields-2.C (revision 0) +++ g++.dg/simulate-thread/bitfields-2.C (revision 0) @@ -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 does not touch either or . + 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 +#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; +} Index: g++.dg/simulate-thread/bitfields.C =================================================================== --- g++.dg/simulate-thread/bitfields.C (revision 0) +++ g++.dg/simulate-thread/bitfields.C (revision 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 does not touch either or . + 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 +#include "simulate-thread.h" + +#define CONSTA 12 + +static int global; +struct S +{ + /* On x86-64, the volatile causes us to access 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; +} Index: g++.dg/simulate-thread/atomics-1.C =================================================================== --- g++.dg/simulate-thread/atomics-1.C (revision 0) +++ g++.dg/simulate-thread/atomics-1.C (revision 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 +#include +#include +#include "simulate-thread.h" + +atomic atomi; +atomic 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; +} Index: g++.dg/simulate-thread/simulate-thread.h =================================================================== --- g++.dg/simulate-thread/simulate-thread.h (revision 0) +++ g++.dg/simulate-thread/simulate-thread.h (revision 0) @@ -0,0 +1 @@ +#include "../../gcc.dg/simulate-thread/simulate-thread.h" Index: g++.dg/memmodel/atomics-2.C =================================================================== --- g++.dg/memmodel/atomics-2.C (revision 179750) +++ g++.dg/memmodel/atomics-2.C (working copy) @@ -1,51 +0,0 @@ -/* { dg-do link } */ -/* { dg-options "-std=c++0x" } */ -/* { dg-final { memmodel-gdb-test } } */ - -using namespace std; - -#include -#include -#include -#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 happens-before the atomic - store to . 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(); -} Index: g++.dg/memmodel/guality.h =================================================================== --- g++.dg/memmodel/guality.h (revision 179750) +++ g++.dg/memmodel/guality.h (working copy) @@ -1 +0,0 @@ -#include "../../gcc.dg/guality/guality.h" Index: g++.dg/memmodel/memmodel.h =================================================================== --- g++.dg/memmodel/memmodel.h (revision 179750) +++ g++.dg/memmodel/memmodel.h (working copy) @@ -1 +0,0 @@ -#include "../../gcc.dg/memmodel/memmodel.h" Index: g++.dg/memmodel/bitfields-2.C =================================================================== --- g++.dg/memmodel/bitfields-2.C (revision 179750) +++ g++.dg/memmodel/bitfields-2.C (working copy) @@ -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 does not touch either or . - 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 -#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; -} Index: g++.dg/memmodel/bitfields.C =================================================================== --- g++.dg/memmodel/bitfields.C (revision 179750) +++ g++.dg/memmodel/bitfields.C (working copy) @@ -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 does not touch either or . - 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 -#include "memmodel.h" - -#define CONSTA 12 - -static int global; -struct S -{ - /* On x86-64, the volatile causes us to access 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; -} Index: g++.dg/memmodel/memmodel.gdb =================================================================== --- g++.dg/memmodel/memmodel.gdb (revision 179750) +++ g++.dg/memmodel/memmodel.gdb (working copy) @@ -1 +0,0 @@ -source ../../gcc.dg/memmodel/memmodel.gdb Index: g++.dg/memmodel/memmodel.exp =================================================================== --- g++.dg/memmodel/memmodel.exp (revision 179750) +++ g++.dg/memmodel/memmodel.exp (working copy) @@ -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 Index: g++.dg/memmodel/atomics-1.C =================================================================== --- g++.dg/memmodel/atomics-1.C (revision 179750) +++ g++.dg/memmodel/atomics-1.C (working copy) @@ -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 -#include -#include -#include "memmodel.h" - -atomic atomi; -atomic 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(); -} Index: c-c++-common/simulate-thread/bitfields-1.c =================================================================== --- c-c++-common/simulate-thread/bitfields-1.c (revision 0) +++ c-c++-common/simulate-thread/bitfields-1.c (revision 0) @@ -0,0 +1,71 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#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 .\n"); + ret = 1; + } + if (var.c != global % 64) + { + printf("FAIL: invalid intermediate value for .\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 .\n"); + return 1; + } + return 0; +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + var.a = 12; +} + +int main() +{ + simulate_thread_main(); + simulate_thread_done(); + return 0; +} Index: c-c++-common/simulate-thread/bitfields-2.c =================================================================== --- c-c++-common/simulate-thread/bitfields-2.c (revision 0) +++ c-c++-common/simulate-thread/bitfields-2.c (revision 0) @@ -0,0 +1,59 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#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 .\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; +} Index: c-c++-common/simulate-thread/bitfields-3.c =================================================================== --- c-c++-common/simulate-thread/bitfields-3.c (revision 0) +++ c-c++-common/simulate-thread/bitfields-3.c (revision 0) @@ -0,0 +1,63 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#include "../../gcc.dg/simulate-thread/simulate-thread.h" + +/* Store into should not clobber . */ + +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; +} Index: c-c++-common/simulate-thread/bitfields-4.c =================================================================== --- c-c++-common/simulate-thread/bitfields-4.c (revision 0) +++ c-c++-common/simulate-thread/bitfields-4.c (revision 0) @@ -0,0 +1,60 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include +#include +#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 should not clobber . */ +/* 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; +} Index: c-c++-common/cxxbitfields-2.c =================================================================== --- c-c++-common/cxxbitfields-2.c (revision 179750) +++ c-c++-common/cxxbitfields-2.c (working copy) @@ -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" } } */ Index: c-c++-common/cxxbitfields.c =================================================================== --- c-c++-common/cxxbitfields.c (revision 179750) +++ c-c++-common/cxxbitfields.c (working copy) @@ -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" } } */ Index: c-c++-common/cxxbitfields-4.c =================================================================== --- c-c++-common/cxxbitfields-4.c (revision 179750) +++ c-c++-common/cxxbitfields-4.c (working copy) @@ -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 should not clobber . */ -void update_c(struct bits *p, int val) -{ - p -> c = val; -} - -/* { dg-final { scan-assembler "mov\[bw\]" } } */ Index: c-c++-common/cxxbitfields-5.c =================================================================== --- c-c++-common/cxxbitfields-5.c (revision 179750) +++ c-c++-common/cxxbitfields-5.c (working copy) @@ -1,29 +0,0 @@ -/* { dg-do compile { target i?86-*-* x86_64-*-* } } */ -/* { dg-options "-O2 --param allow-store-data-races=0" } */ - -#include - -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 should not clobber . */ -/* 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\]" } } */