1 //===- subzero/crosstest/test_sync_atomic_main.cpp - Driver for tests -----===//
2 //
3 // The Subzero Code Generator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Driver for cross testing atomic intrinsics, via the sync builtins.
11 //
12 //===----------------------------------------------------------------------===//
13
14 /* crosstest.py --test=test_sync_atomic.cpp --crosstest-bitcode=0 \
15 --driver=test_sync_atomic_main.cpp --prefix=Subzero_ \
16 --output=test_sync_atomic */
17
18 #include <pthread.h>
19 #include <stdint.h>
20
21 #include <cerrno>
22 #include <climits>
23 #include <cstdlib>
24 #include <cstring>
25 #include <iostream>
26
27 // Include test_sync_atomic.h twice - once normally, and once within the
28 // Subzero_ namespace, corresponding to the llc and Subzero translated
29 // object files, respectively.
30 #include "test_sync_atomic.h"
31 #include "xdefs.h"
32 namespace Subzero_ {
33 #include "test_sync_atomic.h"
34 }
35
36 volatile uint64 Values[] = {0,
37 1,
38 0x7e,
39 0x7f,
40 0x80,
41 0x81,
42 0xfe,
43 0xff,
44 0x7ffe,
45 0x7fff,
46 0x8000,
47 0x8001,
48 0xfffe,
49 0xffff,
50 0x007fffff /*Max subnormal + */,
51 0x00800000 /*Min+ */,
52 0x7f7fffff /*Max+ */,
53 0x7f800000 /*+Inf*/,
54 0xff800000 /*-Inf*/,
55 0x7fa00000 /*SNaN*/,
56 0x7fc00000 /*QNaN*/,
57 0x7ffffffe,
58 0x7fffffff,
59 0x80000000,
60 0x80000001,
61 0xfffffffe,
62 0xffffffff,
63 0x100000000ll,
64 0x100000001ll,
65 0x000fffffffffffffll /*Max subnormal + */,
66 0x0010000000000000ll /*Min+ */,
67 0x7fefffffffffffffll /*Max+ */,
68 0x7ff0000000000000ll /*+Inf*/,
69 0xfff0000000000000ll /*-Inf*/,
70 0x7ff0000000000001ll /*SNaN*/,
71 0x7ff8000000000000ll /*QNaN*/,
72 0x7ffffffffffffffell,
73 0x7fffffffffffffffll,
74 0x8000000000000000ll,
75 0x8000000000000001ll,
76 0xfffffffffffffffell,
77 0xffffffffffffffffll};
78
79 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
80
81 struct {
82 volatile uint8_t l8;
83 volatile uint16_t l16;
84 volatile uint32_t l32;
85 volatile uint64 l64;
86 } AtomicLocs;
87
88 template <typename Type>
testAtomicRMW(volatile Type * AtomicLoc,size_t & TotalTests,size_t & Passes,size_t & Failures)89 void testAtomicRMW(volatile Type *AtomicLoc, size_t &TotalTests, size_t &Passes,
90 size_t &Failures) {
91 typedef Type (*FuncType)(bool, volatile Type *, Type);
92 static struct {
93 const char *Name;
94 FuncType FuncLlc;
95 FuncType FuncSz;
96 } Funcs[] = {
97 #define X(inst) \
98 {STR(inst), test_##inst, Subzero_::test_##inst}, \
99 {STR(inst) "_alloca", test_alloca_##inst, Subzero_::test_alloca_##inst}, \
100 {STR(inst) "_const", test_const_##inst, Subzero_::test_const_##inst},
101 RMWOP_TABLE
102 #undef X
103 };
104 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
105
106 for (size_t f = 0; f < NumFuncs; ++f) {
107 for (size_t i = 0; i < NumValues; ++i) {
108 Type Value1 = static_cast<Type>(Values[i]);
109 for (size_t j = 0; j < NumValues; ++j) {
110 Type Value2 = static_cast<Type>(Values[j]);
111 for (size_t k = 0; k < 2; ++k) {
112 bool fetch_first = k;
113 ++TotalTests;
114 *AtomicLoc = Value1;
115 Type ResultSz1 = Funcs[f].FuncSz(fetch_first, AtomicLoc, Value2);
116 Type ResultSz2 = *AtomicLoc;
117 *AtomicLoc = Value1;
118 Type ResultLlc1 = Funcs[f].FuncLlc(fetch_first, AtomicLoc, Value2);
119 Type ResultLlc2 = *AtomicLoc;
120 if (ResultSz1 == ResultLlc1 && ResultSz2 == ResultLlc2) {
121 ++Passes;
122 } else {
123 ++Failures;
124 std::cout << "test_" << Funcs[f].Name << (CHAR_BIT * sizeof(Type))
125 << "(" << fetch_first << ", "
126 << static_cast<uint64>(Value1) << ", "
127 << static_cast<uint64>(Value2)
128 << "): sz1=" << static_cast<uint64>(ResultSz1)
129 << " llc1=" << static_cast<uint64>(ResultLlc1)
130 << " sz2=" << static_cast<uint64>(ResultSz2)
131 << " llc2=" << static_cast<uint64>(ResultLlc2) << "\n";
132 }
133 }
134 }
135 }
136 }
137 }
138
139 template <typename Type>
testValCompareAndSwap(volatile Type * AtomicLoc,size_t & TotalTests,size_t & Passes,size_t & Failures)140 void testValCompareAndSwap(volatile Type *AtomicLoc, size_t &TotalTests,
141 size_t &Passes, size_t &Failures) {
142 typedef Type (*FuncType)(volatile Type *, Type, Type);
143 static struct {
144 const char *Name;
145 FuncType FuncLlc;
146 FuncType FuncSz;
147 } Funcs[] = {{"val_cmp_swap", test_val_cmp_swap, Subzero_::test_val_cmp_swap},
148 {"val_cmp_swap_loop", test_val_cmp_swap_loop,
149 Subzero_::test_val_cmp_swap_loop}};
150 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
151 for (size_t f = 0; f < NumFuncs; ++f) {
152 for (size_t i = 0; i < NumValues; ++i) {
153 Type Value1 = static_cast<Type>(Values[i]);
154 for (size_t j = 0; j < NumValues; ++j) {
155 Type Value2 = static_cast<Type>(Values[j]);
156 for (size_t f = 0; f < 2; ++f) {
157 bool flip = f;
158 ++TotalTests;
159 *AtomicLoc = Value1;
160 Type ResultSz1 =
161 Funcs[f].FuncSz(AtomicLoc, flip ? Value2 : Value1, Value2);
162 Type ResultSz2 = *AtomicLoc;
163 *AtomicLoc = Value1;
164 Type ResultLlc1 =
165 Funcs[f].FuncLlc(AtomicLoc, flip ? Value2 : Value1, Value2);
166 Type ResultLlc2 = *AtomicLoc;
167 if (ResultSz1 == ResultLlc1 && ResultSz2 == ResultLlc2) {
168 ++Passes;
169 } else {
170 ++Failures;
171 std::cout << "test_" << Funcs[f].Name << (CHAR_BIT * sizeof(Type))
172 << "(" << static_cast<uint64>(Value1) << ", "
173 << static_cast<uint64>(Value2) << ", flip=" << flip
174 << "): sz1=" << static_cast<uint64>(ResultSz1)
175 << " llc1=" << static_cast<uint64>(ResultLlc1)
176 << " sz2=" << static_cast<uint64>(ResultSz2)
177 << " llc2=" << static_cast<uint64>(ResultLlc2) << "\n";
178 }
179 }
180 }
181 }
182 }
183 }
184
185 template <typename Type> struct ThreadData {
186 Type (*FuncPtr)(bool, volatile Type *, Type);
187 bool Fetch;
188 volatile Type *Ptr;
189 Type Adjustment;
190 };
191
threadWrapper(void * Data)192 template <typename Type> void *threadWrapper(void *Data) {
193 #if defined(ARM32) || defined(MIPS32)
194 // Given that most of times these crosstests for ARM are run under qemu, we
195 // set a lower NumReps to allow crosstests to complete within a reasonable
196 // amount of time.
197 static const size_t NumReps = 1000;
198 #else // ARM32 || MIPS32
199 static const size_t NumReps = 8000;
200 #endif // ARM32 || MIPS32
201
202 ThreadData<Type> *TData = reinterpret_cast<ThreadData<Type> *>(Data);
203 for (size_t i = 0; i < NumReps; ++i) {
204 (void)TData->FuncPtr(TData->Fetch, TData->Ptr, TData->Adjustment);
205 }
206 return NULL;
207 }
208
209 template <typename Type>
testAtomicRMWThreads(volatile Type * AtomicLoc,size_t & TotalTests,size_t & Passes,size_t & Failures)210 void testAtomicRMWThreads(volatile Type *AtomicLoc, size_t &TotalTests,
211 size_t &Passes, size_t &Failures) {
212 typedef Type (*FuncType)(bool, volatile Type *, Type);
213 static struct {
214 const char *Name;
215 FuncType FuncLlc;
216 FuncType FuncSz;
217 } Funcs[] = {
218 #define X(inst) \
219 {STR(inst), test_##inst, Subzero_::test_##inst}, \
220 {STR(inst) "_alloca", test_alloca_##inst, Subzero_::test_alloca_##inst},
221 RMWOP_TABLE
222 #undef X
223 };
224 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
225
226 // Just test a few values, otherwise it takes a *really* long time.
227 volatile uint64 ValuesSubset[] = {1, 0x7e, 0x000fffffffffffffffll};
228 const size_t NumValuesSubset = sizeof(ValuesSubset) / sizeof(*ValuesSubset);
229
230 for (size_t f = 0; f < NumFuncs; ++f) {
231 for (size_t i = 0; i < NumValuesSubset; ++i) {
232 Type Value1 = static_cast<Type>(ValuesSubset[i]);
233 for (size_t j = 0; j < NumValuesSubset; ++j) {
234 Type Value2 = static_cast<Type>(ValuesSubset[j]);
235 bool fetch_first = true;
236 ThreadData<Type> TDataSz = {Funcs[f].FuncSz, fetch_first, AtomicLoc,
237 Value2};
238 ThreadData<Type> TDataLlc = {Funcs[f].FuncLlc, fetch_first, AtomicLoc,
239 Value2};
240 ++TotalTests;
241 const size_t NumThreads = 4;
242 pthread_t t[NumThreads];
243 pthread_attr_t attr[NumThreads];
244
245 // Try N threads w/ just Llc.
246 *AtomicLoc = Value1;
247 for (size_t m = 0; m < NumThreads; ++m) {
248 pthread_attr_init(&attr[m]);
249 if (pthread_create(&t[m], &attr[m], &threadWrapper<Type>,
250 reinterpret_cast<void *>(&TDataLlc)) != 0) {
251 std::cout << "pthread_create failed w/ " << strerror(errno) << "\n";
252 abort();
253 }
254 }
255 for (size_t m = 0; m < NumThreads; ++m) {
256 pthread_join(t[m], NULL);
257 }
258 Type ResultLlc = *AtomicLoc;
259
260 // Try N threads w/ both Sz and Llc.
261 *AtomicLoc = Value1;
262 for (size_t m = 0; m < NumThreads; ++m) {
263 pthread_attr_init(&attr[m]);
264 if (pthread_create(&t[m], &attr[m], &threadWrapper<Type>,
265 m % 2 == 0
266 ? reinterpret_cast<void *>(&TDataLlc)
267 : reinterpret_cast<void *>(&TDataSz)) != 0) {
268 ++Failures;
269 std::cout << "pthread_create failed w/ " << strerror(errno) << "\n";
270 abort();
271 }
272 }
273 for (size_t m = 0; m < NumThreads; ++m) {
274 if (pthread_join(t[m], NULL) != 0) {
275 ++Failures;
276 std::cout << "pthread_join failed w/ " << strerror(errno) << "\n";
277 abort();
278 }
279 }
280 Type ResultMixed = *AtomicLoc;
281
282 if (ResultLlc == ResultMixed) {
283 ++Passes;
284 } else {
285 ++Failures;
286 std::cout << "test_with_threads_" << Funcs[f].Name
287 << (8 * sizeof(Type)) << "(" << static_cast<uint64>(Value1)
288 << ", " << static_cast<uint64>(Value2)
289 << "): llc=" << static_cast<uint64>(ResultLlc)
290 << " mixed=" << static_cast<uint64>(ResultMixed) << "\n";
291 }
292 }
293 }
294 }
295 }
296
main(int argc,char * argv[])297 int main(int argc, char *argv[]) {
298 size_t TotalTests = 0;
299 size_t Passes = 0;
300 size_t Failures = 0;
301
302 testAtomicRMW<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
303 testAtomicRMW<uint16_t>(&AtomicLocs.l16, TotalTests, Passes, Failures);
304 testAtomicRMW<uint32_t>(&AtomicLocs.l32, TotalTests, Passes, Failures);
305 testAtomicRMW<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
306 testValCompareAndSwap<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
307 testValCompareAndSwap<uint16_t>(&AtomicLocs.l16, TotalTests, Passes,
308 Failures);
309 testValCompareAndSwap<uint32_t>(&AtomicLocs.l32, TotalTests, Passes,
310 Failures);
311 testValCompareAndSwap<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
312 testAtomicRMWThreads<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
313 testAtomicRMWThreads<uint16_t>(&AtomicLocs.l16, TotalTests, Passes, Failures);
314 testAtomicRMWThreads<uint32_t>(&AtomicLocs.l32, TotalTests, Passes, Failures);
315 testAtomicRMWThreads<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
316
317 std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
318 << " Failures=" << Failures << "\n";
319 return Failures;
320 }
321