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.
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.
12 * See other synchronization_* tests in this directory.
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
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).
23 * GCC Legacy __sync Built-in Functions for Atomic Memory Access.
25 * http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/_005f_005fsync-Builtins.html
27 * Note: nand is ignored because it isn't in C11/C++11.
36 #define CONCAT_(A, B) A ## B
37 #define CONCAT(A, B) CONCAT_(A, B)
39 #define STR(A) STR_(A)
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) "` " \
54 * Test that using some synchronization primitives without using the
55 * return value works. Do this by modifying volatile globals.
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))
64 * Test that base+displacement as synchronization address also works,
65 * with small and big displacements (which should affect unrolling).
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)
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; }
104 * These built-in functions perform the operation suggested by the name,
105 * and return the new value:
106 * { *ptr op= value; return *ptr; }
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_* */ \
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 */ \
120 value = value_ = 41; \
121 res = CONCAT(CONCAT(__sync_, OPERATION_NAME), _and_fetch)( \
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)( \
132 { /* In a small array. */ \
133 volatile TYPE *small_sink = SMALL_SINK_ADDR(TYPE); \
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); \
142 { /* In a big array. */ \
143 volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE); \
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); \
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); \
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, ^);
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.
177 * Note: LLVM also has __sync_swap, which is ignored because it isn't
178 * C11/C++11 and isn't in GCC.
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 */ \
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"); \
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 */ \
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"); \
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); \
212 { /* In a small array. */ \
213 volatile TYPE *small_sink = SMALL_SINK_ADDR(TYPE); \
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); \
222 { /* In a big array. */ \
223 volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE); \
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); \
243 * This built-in function issues a full memory barrier. Simply test that
246 void test_synchronize(void) {
247 __sync_synchronize();
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.
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.
270 * This built-in function releases the lock acquired by
271 * __sync_lock_test_and_set. Normally this means writing the constant 0
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.
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. */ \
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); \
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); \
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]); \
307 { /* In a big array. */ \
308 volatile TYPE *big_sink = BIG_SINK_ADDR(TYPE); \
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]); \