Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client / tests / toolchain / synchronization_sync.c
1 /*
2  * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 /*
8  * This test ensures that the PNaCl backends can deal with compiler
9  * intrinsics as would be written by regular users, including PNaCl's
10  * ABI stabilization and target lowering.
11  *
12  * See other synchronization_* tests in this directory.
13  *
14  * This is a syntactical check as we do not run this multithreaded. Some
15  * real testing is done here: tests/threads/thread_test.c
16  *
17  * PNaCl's atomic support is based on C11/C++11's, and also supports
18  * GCC's legacy __sync_* intrinsics which internally map to the same IR
19  * as C11/C++11's atomics. PNaCl only supports 8, 16, 32 and 64-bit
20  * atomics, and should be able to handle any type that can map to these
21  * (e.g. 32- and 64-bit atomic floating point load/store).
22  *
23  * GCC Legacy __sync Built-in Functions for Atomic Memory Access.
24  * See:
25  * http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/_005f_005fsync-Builtins.html
26  *
27  * Note: nand is ignored because it isn't in C11/C++11.
28  */
29
30 #include <inttypes.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34
35
36 #define CONCAT_(A, B) A ## B
37 #define CONCAT(A, B) CONCAT_(A, B)
38 #define STR_(A) #A
39 #define STR(A) STR_(A)
40
41 #define CHECK_EQ(LHS, RHS, MSG) do {            \
42     printf("\t" MSG ":\t" STR(LHS) "=%" PRIu64  \
43            " and " STR(RHS) "=%" PRIu64 "\n",   \
44            (uint64_t)(LHS), (uint64_t)(RHS));   \
45     if ((LHS) != (RHS)) {                       \
46       fprintf(stderr, "ERROR: " MSG ": `"       \
47               STR(LHS) " != " STR(RHS) "` "     \
48               "\n");                            \
49       exit(1);                                  \
50     }                                           \
51   } while (0)
52
53 /*
54  * Test that using some synchronization primitives without using the
55  * return value works. Do this by modifying volatile globals.
56  */
57 volatile uint8_t g_uint8_t = 0;
58 volatile uint16_t g_uint16_t = 0;
59 volatile uint32_t g_uint32_t = 0;
60 volatile uint64_t g_uint64_t = 0;
61 #define SINK_ADDR(TYPE) (&CONCAT(g_, TYPE))
62
63 /*
64  * Test that base+displacement as synchronization address also works,
65  * with small and big displacements (which should affect unrolling).
66  */
67 /* Small displacement. */
68 enum { gsmall_ArrSize = 8 };
69 volatile struct GS8 { void *ptr; uint8_t arr[gsmall_ArrSize]; }
70   gsmall_uint8_t = { 0, { 0 } };
71 volatile struct GS16 { void *ptr; uint16_t arr[gsmall_ArrSize]; }
72   gsmall_uint16_t = { 0, { 0 } };
73 volatile struct GS32 { void *ptr; uint32_t arr[gsmall_ArrSize]; }
74   gsmall_uint32_t = { 0, { 0 } };
75 volatile struct GS64 { void *ptr; uint64_t arr[gsmall_ArrSize]; }
76   gsmall_uint64_t = { 0, { 0 } };
77 #define SMALL_SINK_ADDR(TYPE) (                                         \
78     CONCAT(gsmall_, TYPE).ptr = (void*) CONCAT(gsmall_, TYPE).arr,      \
79     (TYPE*) CONCAT(gsmall_, TYPE).ptr)
80 /* Big displacement. */
81 enum { gbig_ArrSize = 128 };
82 volatile struct GB8 { void *ptr; uint8_t arr[gbig_ArrSize]; }
83   gbig_uint8_t = { 0, { 0 } };
84 volatile struct GB16 { void *ptr; uint16_t arr[gbig_ArrSize]; }
85   gbig_uint16_t = { 0, { 0 } };
86 volatile struct GB32 { void *ptr; uint32_t arr[gbig_ArrSize]; }
87   gbig_uint32_t = { 0, { 0 } };
88 volatile struct GB64 { void *ptr; uint64_t arr[gbig_ArrSize]; }
89   gbig_uint64_t = { 0, { 0 } };
90 #define BIG_SINK_ADDR(TYPE) (                                   \
91     CONCAT(gbig_, TYPE).ptr = (void*) CONCAT(gbig_, TYPE).arr,  \
92     (TYPE*) CONCAT(gbig_, TYPE).ptr)
93
94
95 /*
96  * fetch_and_*
97  *
98  * These built-in functions perform the operation suggested by the name,
99  * and returns the value that had previously been in memory:
100  *   { tmp = *ptr; *ptr op= value; return tmp; }
101  *
102  * *_and_fetch
103  *
104  * These built-in functions perform the operation suggested by the name,
105  * and return the new value:
106  *   { *ptr op= value; return *ptr; }
107  */
108 void test_fetch(void) {
109 # define FETCH_TEST(OPERATION_NAME, OPERATION, TYPE)  do {              \
110     TYPE res, loc, value, res_, loc_, value_;                           \
111     printf("Testing " STR(OPERATION_NAME) " with " STR(TYPE) "\n");     \
112     /* Test __sync_fetch_and_* */                                       \
113     loc = loc_ = 5;                                                     \
114     value = value_ = 41;                                                \
115     res = CONCAT(__sync_fetch_and_, OPERATION_NAME)(&loc, value);       \
116     res_ = loc_; loc_ = loc_ OPERATION value_;                          \
117     CHECK_EQ(res, res_, "__sync_fetch_and_" STR(OPERATION_NAME));       \
118     /* Test __sync_*_and_fetch */                                       \
119     loc = loc_ = 5;                                                     \
120     value = value_ = 41;                                                \
121     res = CONCAT(CONCAT(__sync_, OPERATION_NAME), _and_fetch)(          \
122         &loc, value);                                                   \
123     loc_ = loc_ OPERATION value_; res_ = loc_;                          \
124     CHECK_EQ(res, res_, "__sync_" STR(OPERATION_NAME) "_and_fetch");    \
125     /* Test the above two variants, without reading the result back. */ \
126     { /* In just one variable. */                                       \
127       volatile TYPE *sink = SINK_ADDR(TYPE);                            \
128       (void) CONCAT(__sync_fetch_and_, OPERATION_NAME)(sink, value);    \
129       (void) CONCAT(CONCAT(__sync_, OPERATION_NAME), _and_fetch)(       \
130           sink, value);                                                 \
131     }                                                                   \
132     { /* In a small array. */                                           \
133       volatile TYPE *small_sink = SMALL_SINK_ADDR(TYPE);                \
134       size_t i;                                                         \
135       for (i = 0; i != gsmall_ArrSize; ++i) {                           \
136         (void) CONCAT(__sync_fetch_and_, OPERATION_NAME)(               \
137             &small_sink[i], value);                                     \
138         (void) CONCAT(CONCAT(__sync_, OPERATION_NAME), _and_fetch)(     \
139             &small_sink[i], value);                                     \
140       }                                                                 \
141     }                                                                   \
142     { /* In a big array. */                                             \
143       volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE);                    \
144       size_t i;                                                         \
145       for (i = 0; i != gbig_ArrSize; ++i) {                             \
146         (void) CONCAT(__sync_fetch_and_, OPERATION_NAME)(               \
147             &big_sink[i], value);                                       \
148         (void) CONCAT(CONCAT(__sync_, OPERATION_NAME), _and_fetch)(     \
149             &big_sink[i], value);                                       \
150       }                                                                 \
151     }                                                                   \
152   } while (0)
153
154 # define FETCH_TEST_ALL_TYPES(OPERATION_NAME, OPERATION) do {   \
155     FETCH_TEST(OPERATION_NAME, OPERATION, uint8_t);             \
156     FETCH_TEST(OPERATION_NAME, OPERATION, uint16_t);            \
157     FETCH_TEST(OPERATION_NAME, OPERATION, uint32_t);            \
158     FETCH_TEST(OPERATION_NAME, OPERATION, uint64_t);            \
159   } while (0)
160
161   FETCH_TEST_ALL_TYPES(add, +);
162   FETCH_TEST_ALL_TYPES(sub, -);
163   FETCH_TEST_ALL_TYPES(or, |);
164   FETCH_TEST_ALL_TYPES(and, &);
165   FETCH_TEST_ALL_TYPES(xor, ^);
166 }
167
168 /*
169  * *_compare_and_swap
170  *
171  * These built-in functions perform an atomic compare and swap. That is,
172  * if the current value of *ptr is oldval, then write newval into
173  * *ptr. The “bool” version returns true if the comparison is successful
174  * and newval is written. The "val" version returns the contents of *ptr
175  * before the operation.
176  *
177  * Note: LLVM also has __sync_swap, which is ignored because it isn't
178  * C11/C++11 and isn't in GCC.
179  */
180 void test_cas(void) {
181 # define CAS_TEST(TYPE) do {                                            \
182     TYPE res, loc, oldval, newval;                                      \
183     printf("Testing CAS with " STR(TYPE) "\n");                         \
184     /* Test __sync_bool_compare_and_swap */                             \
185     loc = 5;                                                            \
186     oldval = 29;                                                        \
187     newval = 41;                                                        \
188     res = __sync_bool_compare_and_swap(&loc, oldval, newval);           \
189     CHECK_EQ(res, 0, "__sync_bool_compare_and_swap");                   \
190     CHECK_EQ(loc, 5, "__sync_bool_compare_and_swap");                   \
191     loc = oldval;                                                       \
192     res = __sync_bool_compare_and_swap(&loc, oldval, newval);           \
193     CHECK_EQ(res, 1, "__sync_bool_compare_and_swap");                   \
194     CHECK_EQ(loc, 41, "__sync_bool_compare_and_swap");                  \
195     /* Test __sync_val_compare_and_swap */                              \
196     loc = 5;                                                            \
197     oldval = 29;                                                        \
198     newval = 41;                                                        \
199     res = __sync_val_compare_and_swap(&loc, oldval, newval);            \
200     CHECK_EQ(res, 5, "__sync_val_compare_and_swap");                    \
201     CHECK_EQ(loc, 5, "__sync_val_compare_and_swap");                    \
202     loc = oldval;                                                       \
203     res = __sync_val_compare_and_swap(&loc, oldval, newval);            \
204     CHECK_EQ(res, oldval, "__sync_val_compare_and_swap");               \
205     CHECK_EQ(loc, 41, "__sync_val_compare_and_swap");                   \
206     /* Test the above two variants, without reading the result back. */ \
207     { /* In just one variable. */                                       \
208       volatile TYPE *sink = SINK_ADDR(TYPE);                            \
209       (void) __sync_bool_compare_and_swap(sink, oldval, newval);        \
210       (void) __sync_val_compare_and_swap(sink, oldval, newval);         \
211     }                                                                   \
212     { /* In a small array. */                                           \
213       volatile TYPE *small_sink = SMALL_SINK_ADDR(TYPE);                \
214       size_t i;                                                         \
215       for (i = 0; i != gsmall_ArrSize; ++i) {                           \
216         (void) __sync_bool_compare_and_swap(                            \
217             &small_sink[i], oldval, newval);                            \
218         (void) __sync_val_compare_and_swap(                             \
219             &small_sink[i], oldval, newval);                            \
220       }                                                                 \
221     }                                                                   \
222     { /* In a big array. */                                             \
223       volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE);                    \
224       size_t i;                                                         \
225       for (i = 0; i != gbig_ArrSize; ++i) {                             \
226         (void) __sync_bool_compare_and_swap(                            \
227             &big_sink[i], oldval, newval);                              \
228         (void) __sync_val_compare_and_swap(                             \
229             &big_sink[i], oldval, newval);                              \
230       }                                                                 \
231     }                                                                   \
232   } while (0)
233
234   CAS_TEST(uint8_t);
235   CAS_TEST(uint16_t);
236   CAS_TEST(uint32_t);
237   CAS_TEST(uint64_t);
238 }
239
240 /*
241  * synchronize
242  *
243  * This built-in function issues a full memory barrier. Simply test that
244  * it compiles fine.
245  */
246 void test_synchronize(void) {
247   __sync_synchronize();
248 }
249
250 /*
251  * lock_test_and_set
252  *
253  * This built-in function, as described by Intel, is not a traditional
254  * test-and-set operation, but rather an atomic exchange operation. It
255  * writes value into *ptr, and returns the previous contents of *ptr.
256  * Many targets have only minimal support for such locks, and do not
257  * support a full exchange operation. In this case, a target may support
258  * reduced functionality here by which the only valid value to store is
259  * the immediate constant 1. The exact value actually stored in *ptr is
260  * implementation defined.
261  *
262  * This built-in function is not a full barrier, but rather an acquire
263  * barrier. This means that references after the operation cannot move
264  * to (or be speculated to) before the operation, but previous memory
265  * stores may not be globally visible yet, and previous memory loads may
266  * not yet be satisfied.
267  *
268  * lock_release
269  *
270  * This built-in function releases the lock acquired by
271  * __sync_lock_test_and_set. Normally this means writing the constant 0
272  * to *ptr.
273  *
274  * This built-in function is not a full barrier, but rather a release
275  * barrier. This means that all previous memory stores are globally
276  * visible, and all previous memory loads have been satisfied, but
277  * following memory reads are not prevented from being speculated to
278  * before the barrier.
279  */
280 void test_tas(void) {
281 # define TAS_TEST(TYPE) do {                                            \
282     TYPE res, loc, value;                                               \
283     printf("Testing TAS with " STR(TYPE) "\n");                         \
284     /* Test __sync_lock_test_and_set. */                                \
285     loc = 5;                                                            \
286     value = 41;                                                         \
287     res = __sync_lock_test_and_set(&loc, value);                        \
288     CHECK_EQ(res, 5, "__sync_lock_test_and_set");                       \
289     CHECK_EQ(loc, 41, "__sync_lock_test_and_set");                      \
290     /* Test __sync_lock_release. */                                     \
291     __sync_lock_release(&loc);                                          \
292     CHECK_EQ(loc, 0,  "__sync_lock_release");                           \
293     { /* In just one variable. */                                       \
294       volatile TYPE *sink = SINK_ADDR(TYPE);                            \
295       (void) __sync_lock_test_and_set(sink, value);                     \
296       __sync_lock_release(sink);                                        \
297     }                                                                   \
298     /* Test the above two variants, without reading the result back. */ \
299     { /* In a small array. */                                           \
300       volatile TYPE *small_sink = SMALL_SINK_ADDR(TYPE);                \
301       size_t i;                                                         \
302       for (i = 0; i != gsmall_ArrSize; ++i) {                           \
303         (void) __sync_lock_test_and_set(&small_sink[i], value);         \
304         __sync_lock_release(&small_sink[i]);                            \
305       }                                                                 \
306     }                                                                   \
307     { /* In a big array. */                                             \
308       volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE);                    \
309       size_t i;                                                         \
310       for (i = 0; i != gbig_ArrSize; ++i) {                             \
311         (void) __sync_lock_test_and_set(&big_sink[i], value);           \
312         __sync_lock_release(&big_sink[i]);                              \
313       }                                                                 \
314     }                                                                   \
315   } while (0)
316
317   TAS_TEST(uint8_t);
318   TAS_TEST(uint16_t);
319   TAS_TEST(uint32_t);
320   TAS_TEST(uint64_t);
321 }
322
323
324 int main(void) {
325   test_fetch();
326   test_cas();
327   test_synchronize();
328   test_tas();
329   return 0;
330 }