• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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