1/* { dg-do link } */
2/* { dg-require-effective-target sync_int_128_runtime } */
3/* { dg-options "-mcx16" { target { x86_64-*-* i?86-*-* } } } */
4/* { dg-final { simulate-thread } } */
5
6#include <stdio.h>
7#include "simulate-thread.h"
8
9
10/* Testing load for atomicity is a little trickier.
11
12   Set up the atomic value so that it changes value after every instruction
13   is executed.
14
15   Simply alternating between 2 values wouldn't be sufficient since a load of
16   one part, followed by the load of the second part 2 instructions later would
17   appear to be valid.
18
19   set up a table of 16 values which change a bit in every byte of the value
20   each time, this will give us a 16 instruction cycle before repetition
21   kicks in, which should be sufficient to detect any issues.  Just to be sure,
22   we also change the table cycle size during execution.
23
24   The end result is that all loads should always get one of the values from
25   the table. Any other pattern means the load failed.  */
26
27__int128_t ret;
28__int128_t value = 0;
29__int128_t result = 0;
30__int128_t table[16] = {
310x0000000000000000,
320x1111111111111111,
330x2222222222222222,
340x3333333333333333,
350x4444444444444444,
360x5555555555555555,
370x6666666666666666,
380x7777777777777777,
390x8888888888888888,
400x9999999999999999,
410xAAAAAAAAAAAAAAAA,
420xBBBBBBBBBBBBBBBB,
430xCCCCCCCCCCCCCCCC,
440xDDDDDDDDDDDDDDDD,
450xEEEEEEEEEEEEEEEE,
460xFFFFFFFFFFFFFFFF
47};
48
49int table_cycle_size = 16;
50
51/* Since we don't have 128 bit constants, we have to properly pad the table.  */
52void fill_table()
53{
54  int x;
55  for (x = 0; x < 16; x++)
56    {
57      ret = table[x];
58      ret = (ret << 64) | ret;
59      table[x] = ret;
60    }
61}
62
63/* Return 0 if 'result' is a valid value to have loaded.  */
64int verify_result ()
65{
66  int x;
67  int found = 0;
68
69  /* Check entire table for valid values.  */
70  for (x = 0; x < 16; x++)
71    if (result == table[x])
72      {
73	found = 1;
74	break;
75      }
76
77  if (!found)
78    printf("FAIL: Invalid result returned from fetch\n");
79
80  return !found;
81}
82
83/* Iterate VALUE through the different valid values. */
84void simulate_thread_other_threads ()
85{
86  static int current = 0;
87
88  if (++current >= table_cycle_size)
89    current = 0;
90  value = table[current];
91}
92
93int simulate_thread_step_verify ()
94{
95  return verify_result ();
96}
97
98int simulate_thread_final_verify ()
99{
100  return verify_result ();
101}
102
103__attribute__((noinline))
104void simulate_thread_main()
105{
106  int x;
107
108  /* Execute loads with value changing at various cyclic values.  */
109  for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
110    {
111      ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST);
112      /* In order to verify the returned value (which is not atomic), it needs
113	 to be atomically stored into another variable and check that.  */
114      __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST);
115
116      /* Execute the fetch/store a couple of times just to ensure the cycles
117         have a chance to be interesting.  */
118      ret = __atomic_load_n (&value, __ATOMIC_SEQ_CST);
119      __atomic_store_n (&result, ret, __ATOMIC_SEQ_CST);
120    }
121}
122
123int
124main()
125{
126  fill_table ();
127
128  /* Make sure value starts with an atomic value from the table.  */
129  __atomic_store_n (&value, table[0], __ATOMIC_SEQ_CST);
130
131  simulate_thread_main ();
132  simulate_thread_done ();
133  return 0;
134}
135