diff options
Diffstat (limited to 'gcc/testsuite/gcc.dg/simulate-thread')
11 files changed, 1151 insertions, 0 deletions
diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int.c new file mode 100644 index 00000000000..d03e8318108 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int.c @@ -0,0 +1,116 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_long } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + + +/* Testing load for atomicity is a little trickier. + + Set up the atomic value so that it changes value after every instruction + is executed. + + Simply alternating between 2 values wouldn't be sufficient since a load of + one part, followed by the load of the second part 2 instructions later would + appear to be valid. + + set up a table of 16 values which change a bit in every byte of the value + each time, this will give us a 16 instruction cycle before repetition + kicks in, which should be sufficient to detect any issues. Just to be sure, + we also change the table cycle size during execution. + + The end result is that all loads should always get one of the values from + the table. Any other pattern means the load failed. */ + +unsigned int ret; +unsigned int value = 0; +unsigned int result = 0; +unsigned int table[16] = { +0x00000000, +0x11111111, +0x22222222, +0x33333333, +0x44444444, +0x55555555, +0x66666666, +0x77777777, +0x88888888, +0x99999999, +0xAAAAAAAA, +0xBBBBBBBB, +0xCCCCCCCC, +0xDDDDDDDD, +0xEEEEEEEE, +0xFFFFFFFF +}; + +int table_cycle_size = 16; + +/* Return 0 if 'result' is a valid value to have loaded. */ +int verify_result () +{ + int x; + int found = 0; + + /* Check entire table for valid values. */ + for (x = 0; x < 16 ; x++) + if (result == table[x]) + { + found = 1; + break; + } + + if (!found) + printf("FAIL: Invalid result returned from fetch\n"); + + return !found; +} + +/* Iterate VALUE through the different valid values. */ +void simulate_thread_other_threads () +{ + static int current = 0; + + if (++current >= table_cycle_size) + current = 0; + value = table[current]; +} + +int simulate_thread_step_verify () +{ + return verify_result (); +} + +int simulate_thread_final_verify () +{ + return verify_result (); +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + int x; + + /* Execute loads with value changing at various cyclic values. */ + for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) + { + ret = __atomic_load_n (&value, __ATOMIC_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. */ + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + + /* Execute the fetch/store a couple of times just to ensure the cycles + have a chance to be interesting. */ + ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST); + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + } +} + +main() +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int128.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int128.c new file mode 100644 index 00000000000..3ade0d6fad3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-int128.c @@ -0,0 +1,132 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_128 } */ +/* { dg-options "-mcx16" { target { x86_64-*-* i?86-*-* } } } */ +/* { dg-final { simulate-thread } } */ + +#include <stdio.h> +#include "simulate-thread.h" + + +/* Testing load for atomicity is a little trickier. + + Set up the atomic value so that it changes value after every instruction + is executed. + + Simply alternating between 2 values wouldn't be sufficient since a load of + one part, followed by the load of the second part 2 instructions later would + appear to be valid. + + set up a table of 16 values which change a bit in every byte of the value + each time, this will give us a 16 instruction cycle before repetition + kicks in, which should be sufficient to detect any issues. Just to be sure, + we also change the table cycle size during execution. + + The end result is that all loads should always get one of the values from + the table. Any other pattern means the load failed. */ + +__int128_t ret; +__int128_t value = 0; +__int128_t result = 0; +__int128_t table[16] = { +0x0000000000000000, +0x1111111111111111, +0x2222222222222222, +0x3333333333333333, +0x4444444444444444, +0x5555555555555555, +0x6666666666666666, +0x7777777777777777, +0x8888888888888888, +0x9999999999999999, +0xAAAAAAAAAAAAAAAA, +0xBBBBBBBBBBBBBBBB, +0xCCCCCCCCCCCCCCCC, +0xDDDDDDDDDDDDDDDD, +0xEEEEEEEEEEEEEEEE, +0xFFFFFFFFFFFFFFFF +}; + +int table_cycle_size = 16; + +/* Since we don't have 128 bit constants, we have to properly pad the table. */ +void fill_table() +{ + int x; + for (x = 0; x < 16; x++) + { + ret = table[x]; + ret = (ret << 64) | ret; + table[x] = ret; + } +} + +/* Return 0 if 'result' is a valid value to have loaded. */ +int verify_result () +{ + int x; + int found = 0; + + /* Check entire table for valid values. */ + for (x = 0; x < 16; x++) + if (result == table[x]) + { + found = 1; + break; + } + + if (!found) + printf("FAIL: Invalid result returned from fetch\n"); + + return !found; +} + +/* Iterate VALUE through the different valid values. */ +void simulate_thread_other_threads () +{ + static int current = 0; + + if (++current >= table_cycle_size) + current = 0; + value = table[current]; +} + +int simulate_thread_step_verify () +{ + return verify_result (); +} + +int simulate_thread_final_verify () +{ + return verify_result (); +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + int x; + + /* Make sure value starts with an atomic value now. */ + __atomic_store_n (&value, ret, __ATOMIC_SEQ_CST); + + /* Execute loads with value changing at various cyclic values. */ + for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) + { + ret = __atomic_load_n (&value, __ATOMIC_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. */ + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + + /* Execute the fetch/store a couple of times just to ensure the cycles + have a chance to be interesting. */ + ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST); + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + } +} + +main() +{ + fill_table (); + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-longlong.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-longlong.c new file mode 100644 index 00000000000..8bc2eaace65 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-longlong.c @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_long_long } */ +/* { dg-options "" } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + + +/* Testing load for atomicity is a little trickier. + + Set up the atomic value so that it changes value after every instruction + is executed. + + Simply alternating between 2 values wouldn't be sufficient since a load of + one part, followed by the load of the second part 2 instructions later would + appear to be valid. + + set up a table of 16 values which change a bit in every byte of the value + each time, this will give us a 16 instruction cycle before repetition + kicks in, which should be sufficient to detect any issues. Just to be sure, + we also change the table cycle size during execution. + + The end result is that all loads should always get one of the values from + the table. Any other pattern means the load failed. */ + +unsigned long long ret; +unsigned long long value = 0; +unsigned long long result = 0; +unsigned long long table[16] = { +0x0000000000000000, +0x1111111111111111, +0x2222222222222222, +0x3333333333333333, +0x4444444444444444, +0x5555555555555555, +0x6666666666666666, +0x7777777777777777, +0x8888888888888888, +0x9999999999999999, +0xAAAAAAAAAAAAAAAA, +0xBBBBBBBBBBBBBBBB, +0xCCCCCCCCCCCCCCCC, +0xDDDDDDDDDDDDDDDD, +0xEEEEEEEEEEEEEEEE, +0xFFFFFFFFFFFFFFFF +}; + +int table_cycle_size = 16; + +/* Return 0 if 'result' is a valid value to have loaded. */ +int verify_result () +{ + int x; + int found = 0; + + /* Check entire table for valid values. */ + for (x = 0; x < 16 ; x++) + if (result == table[x]) + { + found = 1; + break; + } + + if (!found) + printf("FAIL: Invalid result returned from fetch\n"); + + return !found; +} + +/* Iterate VALUE through the different valid values. */ +void simulate_thread_other_threads () +{ + static int current = 0; + + if (++current >= table_cycle_size) + current = 0; + value = table[current]; +} + +int simulate_thread_step_verify () +{ + return verify_result (); +} + +int simulate_thread_final_verify () +{ + return verify_result (); +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + int x; + + /* Execute loads with value changing at various cyclic values. */ + for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) + { + ret = __atomic_load_n (&value, __ATOMIC_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. */ + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + + /* Execute the fetch/store a couple of times just to ensure the cycles + have a chance to be interesting. */ + ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST); + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + } +} + +main() +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-short.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-short.c new file mode 100644 index 00000000000..e7b54c46bef --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-load-short.c @@ -0,0 +1,116 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_char_short } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + + +/* Testing load for atomicity is a little trickier. + + Set up the atomic value so that it changes value after every instruction + is executed. + + Simply alternating between 2 values wouldn't be sufficient since a load of + one part, followed by the load of the second part 2 instructions later would + appear to be valid. + + set up a table of 16 values which change a bit in every byte of the value + each time, this will give us a 16 instruction cycle before repetition + kicks in, which should be sufficient to detect any issues. Just to be sure, + we also change the table cycle size during execution. + + The end result is that all loads should always get one of the values from + the table. Any other pattern means the load failed. */ + +unsigned short ret; +unsigned short value = 0; +unsigned short result = 0; +unsigned short table[16] = { +0x0000, +0x1111, +0x2222, +0x3333, +0x4444, +0x5555, +0x6666, +0x7777, +0x8888, +0x9999, +0xAAAA, +0xBBBB, +0xCCCC, +0xDDDD, +0xEEEE, +0xFFFF +}; + +int table_cycle_size = 16; + +/* Return 0 if 'result' is a valid value to have loaded. */ +int verify_result () +{ + int x; + int found = 0; + + /* Check entire table for valid values. */ + for (x = 0; x < 16 ; x++) + if (result == table[x]) + { + found = 1; + break; + } + + if (!found) + printf("FAIL: Invalid result returned from fetch\n"); + + return !found; +} + +/* Iterate VALUE through the different valid values. */ +void simulate_thread_other_threads () +{ + static int current = 0; + + if (++current >= table_cycle_size) + current = 0; + value = table[current]; +} + +int simulate_thread_step_verify () +{ + return verify_result (); +} + +int simulate_thread_final_verify () +{ + return verify_result (); +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + int x; + + /* Execute loads with value changing at various cyclic values. */ + for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--) + { + ret = __atomic_load_n (&value, __ATOMIC_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. */ + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + + /* Execute the fetch/store a couple of times just to ensure the cycles + have a chance to be interesting. */ + ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST); + __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST); + } +} + +main() +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int.c new file mode 100644 index 00000000000..990310c0f0e --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int.c @@ -0,0 +1,118 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_long } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + +/* Test all the __sync routines for proper atomicity on 4 byte values. */ + +unsigned int zero = 0; +unsigned int max = ~0; + +unsigned int changing_value = 0; +unsigned int value = 0; +unsigned int ret; + +void test_abort() +{ + static int reported = 0; + if (!reported) + { + printf ("FAIL: improper execution of __sync builtin.\n"); + reported = 1; + } +} + +void simulate_thread_other_threads () +{ +} + +int simulate_thread_step_verify () +{ + if (value != zero && value != max) + { + printf ("FAIL: invalid intermediate result for value.\n"); + return 1; + } + return 0; +} + +int simulate_thread_final_verify () +{ + if (value != 0) + { + printf ("FAIL: invalid final result for value.\n"); + return 1; + } + return 0; +} + +/* All values written to 'value' alternate between 'zero' and + 'max'. Any other value detected by simulate_thread_step_verify() + between instructions would indicate that the value was only + partially written, and would thus fail this atomicity test. + + This function tests each different __atomic routine once, with + the exception of the load instruction which requires special + testing. */ +__attribute__((noinline)) +void simulate_thread_main() +{ + + ret = __atomic_exchange_n (&value, max, __ATOMIC_SEQ_CST); + if (ret != zero || value != max) + test_abort(); + + __atomic_store_n (&value, zero, __ATOMIC_SEQ_CST); + if (value != zero) + test_abort(); + + ret = __atomic_fetch_add (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_sub (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_fetch_or (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_and (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_fetch_xor (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_add_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_sub_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); + + ret = __atomic_or_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_and_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_xor_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); +} + +main () +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int128.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int128.c new file mode 100644 index 00000000000..67f84a14a00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-int128.c @@ -0,0 +1,116 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_int_128 } */ +/* { dg-options "-mcx16" { target { x86_64-*-* i?86-*-*] } } } */ +/* { dg-final { simulate-thread } } */ + +#include <stdio.h> +#include "simulate-thread.h" + +/* Test all the __sync routines for proper atomicity on 16 byte values. */ + +__int128_t zero = 0; +__int128_t max = ~0; +__int128_t changing_value = 0; +__int128_t value = 0; +__int128_t ret; + +void test_abort() +{ + static int reported = 0; + if (!reported) + { + printf ("FAIL: improper execution of __sync builtin.\n"); + reported = 1; + } +} + +void simulate_thread_other_threads () +{ +} + +int simulate_thread_step_verify () +{ + if (value != zero && value != max) + { + printf ("FAIL: invalid intermediate result for value.\n"); + return 1; + } + return 0; +} + +int simulate_thread_final_verify () +{ + if (value != 0) + { + printf ("FAIL: invalid final result for value.\n"); + return 1; + } + return 0; +} + +/* All values written to 'value' alternate between 'zero' and 'max'. Any other + value detected by simulate_thread_step_verify() between instructions would indicate + that the value was only partially written, and would thus fail this + atomicity test. + + This function tests each different __atomic routine once, with the + exception of the load instruction which requires special testing. */ +__attribute__((noinline)) +void simulate_thread_main() +{ + + ret = __atomic_exchange_n (&value, max, __ATOMIC_SEQ_CST); + if (ret != zero || value != max) + test_abort(); + + __atomic_store_n (&value, zero, __ATOMIC_SEQ_CST); + if (value != zero) + test_abort(); + + ret = __atomic_fetch_add (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_sub (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_fetch_or (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_and (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_fetch_xor (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_add_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_sub_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); + + ret = __atomic_or_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_and_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_xor_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); +} + +int main() +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-longlong.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-longlong.c new file mode 100644 index 00000000000..ac4330bd8a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-longlong.c @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_long_long } */ +/* { dg-options "" } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + +/* Test all the __sync routines for proper atomicity on 8 byte values. */ + +unsigned long long zero = 0; +unsigned long long max = ~0; + +unsigned long long changing_value = 0; +unsigned long long value = 0; +unsigned long long ret; + +void test_abort() +{ + static int reported = 0; + if (!reported) + { + printf ("FAIL: improper execution of __sync builtin.\n"); + reported = 1; + } +} + +void simulate_thread_other_threads () +{ +} + +int simulate_thread_step_verify () +{ + if (value != zero && value != max) + { + printf ("FAIL: invalid intermediate result for value.\n"); + return 1; + } + return 0; +} + +int simulate_thread_final_verify () +{ + if (value != 0) + { + printf ("FAIL: invalid final result for value.\n"); + return 1; + } + return 0; +} + +/* All values written to 'value' alternate between 'zero' and 'max'. Any other + value detected by simulate_thread_step_verify() between instructions would indicate + that the value was only partially written, and would thus fail this + atomicity test. + + This function tests each different __atomic routine once, with the + exception of the load instruction which requires special testing. */ +__attribute__((noinline)) +void simulate_thread_main() +{ + ret = __atomic_exchange_n (&value, max, __ATOMIC_SEQ_CST); + if (ret != zero || value != max) + test_abort(); + + __atomic_store_n (&value, zero, __ATOMIC_SEQ_CST); + if (value != zero) + test_abort(); + + ret = __atomic_fetch_add (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_sub (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_fetch_or (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_and (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_fetch_xor (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_add_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_sub_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); + + ret = __atomic_or_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_and_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_xor_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); +} + +int main () +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-short.c b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-short.c new file mode 100644 index 00000000000..d823e02fb47 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/atomic-other-short.c @@ -0,0 +1,117 @@ +/* { dg-do link } */ +/* { dg-require-effective-target sync_char_short } */ +/* { dg-final { simulate-thread } } */ + + +#include <stdio.h> +#include "simulate-thread.h" + +/* Test all the __sync routines for proper atomicity on 2 byte values. */ + +unsigned short zero = 0; +unsigned short max = ~0; + +unsigned short changing_value = 0; +unsigned short value = 0; +unsigned short ret; + +void test_abort() +{ + static int reported = 0; + if (!reported) + { + printf ("FAIL: improper execution of __sync builtin.\n"); + reported = 1; + } +} + +void simulate_thread_other_threads () +{ +} + +int simulate_thread_step_verify () +{ + if (value != zero && value != max) + { + printf ("FAIL: invalid intermediate result for value.\n"); + return 1; + } + return 0; +} + +int simulate_thread_final_verify () +{ + if (value != 0) + { + printf ("FAIL: invalid final result for value.\n"); + return 1; + } + return 0; +} + +/* All values written to 'value' alternate between 'zero' and + 'max'. Any other value detected by simulate_thread_step_verify() + between instructions would indicate that the value was only + partially written, and would thus fail this atomicity test. + + This function tests each different __atomic routine once, with + the exception of the load instruction which requires special + testing. */ +__attribute__((noinline)) +void simulate_thread_main() +{ + ret = __atomic_exchange_n (&value, max, __ATOMIC_SEQ_CST); + if (ret != zero || value != max) + test_abort(); + + __atomic_store_n (&value, zero, __ATOMIC_SEQ_CST); + if (value != zero) + test_abort(); + + ret = __atomic_fetch_add (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_sub (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_fetch_or (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != zero) + test_abort (); + + ret = __atomic_fetch_and (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_fetch_xor (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != max) + test_abort (); + + ret = __atomic_add_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_sub_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); + + ret = __atomic_or_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_and_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != max || ret != max) + test_abort (); + + ret = __atomic_xor_fetch (&value, max, __ATOMIC_SEQ_CST); + if (value != zero || ret != zero) + test_abort (); +} + +int main () +{ + simulate_thread_main (); + simulate_thread_done (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/speculative-store.c b/gcc/testsuite/gcc.dg/simulate-thread/speculative-store.c new file mode 100644 index 00000000000..71d1cca9dda --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/speculative-store.c @@ -0,0 +1,57 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include <stdio.h> +#include "simulate-thread.h" + +/* This file tests that speculative store movement out of a loop doesn't + happen. This is disallowed when --param allow-store-data-races is 0. */ + +int global = 100; + +/* Other thread makes sure global is 100 before the next instruction is + * exceuted. */ +void simulate_thread_other_threads() +{ + global = 100; +} + +int simulate_thread_step_verify() +{ + if (global != 100) + { + printf("FAIL: global variable was assigned to. \n"); + return 1; + } +} + +int simulate_thread_final_verify() +{ + return 0; +} + +/* The variable global should never be assigned if func(0) is called. + This tests store movement out of loop thats never executed. */ +void test (int y) +{ + int x; + for (x=0; x< y; x++) + { + global = y; /* This should never speculatively execute. */ + } +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + test(0); + simulate_thread_done(); +} + +__attribute__((noinline)) +int main() +{ + simulate_thread_main(); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/strict-align-global.c b/gcc/testsuite/gcc.dg/simulate-thread/strict-align-global.c new file mode 100644 index 00000000000..fdcd7f46af7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/strict-align-global.c @@ -0,0 +1,52 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-packed-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include <stdio.h> +#include "simulate-thread.h" + +/* This test verifies writes to globals do not write to adjacent + globals. This mostly happens on strict-align targets that are not + byte addressable (old Alphas, etc). */ + +char a = 0; +char b = 77; + +void simulate_thread_other_threads() +{ +} + +int simulate_thread_step_verify() +{ + if (b != 77) + { + printf("FAIL: Unexpected value. <b> is %d, should be 77\n", b); + return 1; + } + return 0; +} + +/* Verify that every variable has the correct value. */ +int simulate_thread_final_verify() +{ + int ret = simulate_thread_step_verify (); + if (a != 66) + { + printf("FAIL: Unexpected value. <a> is %d, should be 66\n", a); + return 1; + } + return ret; +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + a = 66; +} + +int main () +{ + simulate_thread_main(); + simulate_thread_done(); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/simulate-thread/subfields.c b/gcc/testsuite/gcc.dg/simulate-thread/subfields.c new file mode 100644 index 00000000000..2d931176e7d --- /dev/null +++ b/gcc/testsuite/gcc.dg/simulate-thread/subfields.c @@ -0,0 +1,93 @@ +/* { dg-do link } */ +/* { dg-options "--param allow-packed-store-data-races=0" } */ +/* { dg-final { simulate-thread } } */ + +#include <stdio.h> +#include "simulate-thread.h" + +/* This test verifies that data races aren't introduced by structure subfield + stores. */ + +struct test_struct { + char a; + char b; + char c; + char d; +} var = {0,0,0,0}; + + +/* This routine sets field a to 'x'. If executed properly, it will + not affect any of the other fields in the structure. An improper + implementation may load an entire word, change the 8 bits for field + 'a' and write the entire word back out. */ +__attribute__((noinline)) +void set_a(char x) +{ + var.a = x; +} + +static int global = 0; + +/* The other thread increments the value of each of the other fields + in the structure every cycle. If the store to the 'a' field does + an incorrect full or partial word load, mask and store, it will + write back an incorrect value to one or more of the other + fields. */ +void simulate_thread_other_threads() +{ + global++; + var.b = global; + var.c = global; + var.d = global; +} + + +/* Make sure that none of the other fields have been changed. */ +int simulate_thread_step_verify() +{ + int ret = 0; + if (var.b != global) + { + printf("FAIL: Unexpected value. var.b is %d, should be %d\n", + var.b, global); + ret = 1; + } + if (var.c != global) + { + printf("FAIL: Unexpected value. var.c is %d, should be %d\n", + var.c, global); + ret = 1; + } + if (var.d != global) + { + printf("FAIL: Unexpected value. var.d is %d, should be %d\n", + var.d, global); + ret = 1; + } + return ret; +} + +/* Verify that every variable has the correct value. */ +int simulate_thread_final_verify() +{ + int ret = simulate_thread_step_verify(); + if (var.a != 1) + { + printf("FAIL: Unexpected value. var.a is %d, should be %d\n", var.a, 1); + ret = 1; + } + return ret; +} + +__attribute__((noinline)) +void simulate_thread_main() +{ + set_a(1); +} + +int main () +{ + simulate_thread_main(); + simulate_thread_done(); + return 0; +} |