Home | History | Annotate | Download | only in crosstest
      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[] = {
     37     0, 1, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff, 0x7ffe, 0x7fff, 0x8000, 0x8001,
     38     0xfffe, 0xffff, 0x007fffff /*Max subnormal + */, 0x00800000 /*Min+ */,
     39     0x7f7fffff /*Max+ */, 0x7f800000 /*+Inf*/, 0xff800000 /*-Inf*/,
     40     0x7fa00000 /*SNaN*/, 0x7fc00000 /*QNaN*/, 0x7ffffffe, 0x7fffffff,
     41     0x80000000, 0x80000001, 0xfffffffe, 0xffffffff, 0x100000000ll,
     42     0x100000001ll, 0x000fffffffffffffll /*Max subnormal + */,
     43     0x0010000000000000ll /*Min+ */, 0x7fefffffffffffffll /*Max+ */,
     44     0x7ff0000000000000ll /*+Inf*/, 0xfff0000000000000ll /*-Inf*/,
     45     0x7ff0000000000001ll /*SNaN*/, 0x7ff8000000000000ll /*QNaN*/,
     46     0x7ffffffffffffffell, 0x7fffffffffffffffll, 0x8000000000000000ll,
     47     0x8000000000000001ll, 0xfffffffffffffffell, 0xffffffffffffffffll};
     48 
     49 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
     50 
     51 struct {
     52   volatile uint8_t l8;
     53   volatile uint16_t l16;
     54   volatile uint32_t l32;
     55   volatile uint64 l64;
     56 } AtomicLocs;
     57 
     58 template <typename Type>
     59 void testAtomicRMW(volatile Type *AtomicLoc, size_t &TotalTests, size_t &Passes,
     60                    size_t &Failures) {
     61   typedef Type (*FuncType)(bool, volatile Type *, Type);
     62   static struct {
     63     const char *Name;
     64     FuncType FuncLlc;
     65     FuncType FuncSz;
     66   } Funcs[] = {
     67 #define X(inst)                                                                \
     68   { STR(inst), test_##inst, Subzero_::test_##inst }                            \
     69   , {STR(inst) "_alloca", test_alloca_##inst, Subzero_::test_alloca_##inst},   \
     70       {STR(inst) "_const", test_const_##inst, Subzero_::test_const_##inst},
     71       RMWOP_TABLE
     72 #undef X
     73   };
     74   const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
     75 
     76   for (size_t f = 0; f < NumFuncs; ++f) {
     77     for (size_t i = 0; i < NumValues; ++i) {
     78       Type Value1 = static_cast<Type>(Values[i]);
     79       for (size_t j = 0; j < NumValues; ++j) {
     80         Type Value2 = static_cast<Type>(Values[j]);
     81         for (size_t k = 0; k < 2; ++k) {
     82           bool fetch_first = k;
     83           ++TotalTests;
     84           *AtomicLoc = Value1;
     85           Type ResultSz1 = Funcs[f].FuncSz(fetch_first, AtomicLoc, Value2);
     86           Type ResultSz2 = *AtomicLoc;
     87           *AtomicLoc = Value1;
     88           Type ResultLlc1 = Funcs[f].FuncLlc(fetch_first, AtomicLoc, Value2);
     89           Type ResultLlc2 = *AtomicLoc;
     90           if (ResultSz1 == ResultLlc1 && ResultSz2 == ResultLlc2) {
     91             ++Passes;
     92           } else {
     93             ++Failures;
     94             std::cout << "test_" << Funcs[f].Name << (CHAR_BIT * sizeof(Type))
     95                       << "(" << fetch_first << ", "
     96                       << static_cast<uint64>(Value1) << ", "
     97                       << static_cast<uint64>(Value2)
     98                       << "): sz1=" << static_cast<uint64>(ResultSz1)
     99                       << " llc1=" << static_cast<uint64>(ResultLlc1)
    100                       << " sz2=" << static_cast<uint64>(ResultSz2)
    101                       << " llc2=" << static_cast<uint64>(ResultLlc2) << "\n";
    102           }
    103         }
    104       }
    105     }
    106   }
    107 }
    108 
    109 template <typename Type>
    110 void testValCompareAndSwap(volatile Type *AtomicLoc, size_t &TotalTests,
    111                            size_t &Passes, size_t &Failures) {
    112   typedef Type (*FuncType)(volatile Type *, Type, Type);
    113   static struct {
    114     const char *Name;
    115     FuncType FuncLlc;
    116     FuncType FuncSz;
    117   } Funcs[] = {{"val_cmp_swap", test_val_cmp_swap, Subzero_::test_val_cmp_swap},
    118                {"val_cmp_swap_loop", test_val_cmp_swap_loop,
    119                 Subzero_::test_val_cmp_swap_loop}};
    120   const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
    121   for (size_t f = 0; f < NumFuncs; ++f) {
    122     for (size_t i = 0; i < NumValues; ++i) {
    123       Type Value1 = static_cast<Type>(Values[i]);
    124       for (size_t j = 0; j < NumValues; ++j) {
    125         Type Value2 = static_cast<Type>(Values[j]);
    126         for (size_t f = 0; f < 2; ++f) {
    127           bool flip = f;
    128           ++TotalTests;
    129           *AtomicLoc = Value1;
    130           Type ResultSz1 =
    131               Funcs[f].FuncSz(AtomicLoc, flip ? Value2 : Value1, Value2);
    132           Type ResultSz2 = *AtomicLoc;
    133           *AtomicLoc = Value1;
    134           Type ResultLlc1 =
    135               Funcs[f].FuncLlc(AtomicLoc, flip ? Value2 : Value1, Value2);
    136           Type ResultLlc2 = *AtomicLoc;
    137           if (ResultSz1 == ResultLlc1 && ResultSz2 == ResultLlc2) {
    138             ++Passes;
    139           } else {
    140             ++Failures;
    141             std::cout << "test_" << Funcs[f].Name << (CHAR_BIT * sizeof(Type))
    142                       << "(" << static_cast<uint64>(Value1) << ", "
    143                       << static_cast<uint64>(Value2) << ", flip=" << flip
    144                       << "): sz1=" << static_cast<uint64>(ResultSz1)
    145                       << " llc1=" << static_cast<uint64>(ResultLlc1)
    146                       << " sz2=" << static_cast<uint64>(ResultSz2)
    147                       << " llc2=" << static_cast<uint64>(ResultLlc2) << "\n";
    148           }
    149         }
    150       }
    151     }
    152   }
    153 }
    154 
    155 template <typename Type> struct ThreadData {
    156   Type (*FuncPtr)(bool, volatile Type *, Type);
    157   bool Fetch;
    158   volatile Type *Ptr;
    159   Type Adjustment;
    160 };
    161 
    162 template <typename Type> void *threadWrapper(void *Data) {
    163 #if defined(ARM32) || defined(MIPS32)
    164   // Given that most of times these crosstests for ARM are run under qemu, we
    165   // set a lower NumReps to allow crosstests to complete within a reasonable
    166   // amount of time.
    167   static const size_t NumReps = 1000;
    168 #else  // ARM32 || MIPS32
    169   static const size_t NumReps = 8000;
    170 #endif // ARM32 || MIPS32
    171 
    172   ThreadData<Type> *TData = reinterpret_cast<ThreadData<Type> *>(Data);
    173   for (size_t i = 0; i < NumReps; ++i) {
    174     (void)TData->FuncPtr(TData->Fetch, TData->Ptr, TData->Adjustment);
    175   }
    176   return NULL;
    177 }
    178 
    179 template <typename Type>
    180 void testAtomicRMWThreads(volatile Type *AtomicLoc, size_t &TotalTests,
    181                           size_t &Passes, size_t &Failures) {
    182   typedef Type (*FuncType)(bool, volatile Type *, Type);
    183   static struct {
    184     const char *Name;
    185     FuncType FuncLlc;
    186     FuncType FuncSz;
    187   } Funcs[] = {
    188 #define X(inst)                                                                \
    189   { STR(inst), test_##inst, Subzero_::test_##inst }                            \
    190   , {STR(inst) "_alloca", test_alloca_##inst, Subzero_::test_alloca_##inst},
    191       RMWOP_TABLE
    192 #undef X
    193   };
    194   const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
    195 
    196   // Just test a few values, otherwise it takes a *really* long time.
    197   volatile uint64 ValuesSubset[] = {1, 0x7e, 0x000fffffffffffffffll};
    198   const size_t NumValuesSubset = sizeof(ValuesSubset) / sizeof(*ValuesSubset);
    199 
    200   for (size_t f = 0; f < NumFuncs; ++f) {
    201     for (size_t i = 0; i < NumValuesSubset; ++i) {
    202       Type Value1 = static_cast<Type>(ValuesSubset[i]);
    203       for (size_t j = 0; j < NumValuesSubset; ++j) {
    204         Type Value2 = static_cast<Type>(ValuesSubset[j]);
    205         bool fetch_first = true;
    206         ThreadData<Type> TDataSz = {Funcs[f].FuncSz, fetch_first, AtomicLoc,
    207                                     Value2};
    208         ThreadData<Type> TDataLlc = {Funcs[f].FuncLlc, fetch_first, AtomicLoc,
    209                                      Value2};
    210         ++TotalTests;
    211         const size_t NumThreads = 4;
    212         pthread_t t[NumThreads];
    213         pthread_attr_t attr[NumThreads];
    214 
    215         // Try N threads w/ just Llc.
    216         *AtomicLoc = Value1;
    217         for (size_t m = 0; m < NumThreads; ++m) {
    218           pthread_attr_init(&attr[m]);
    219           if (pthread_create(&t[m], &attr[m], &threadWrapper<Type>,
    220                              reinterpret_cast<void *>(&TDataLlc)) != 0) {
    221             std::cout << "pthread_create failed w/ " << strerror(errno) << "\n";
    222             abort();
    223           }
    224         }
    225         for (size_t m = 0; m < NumThreads; ++m) {
    226           pthread_join(t[m], NULL);
    227         }
    228         Type ResultLlc = *AtomicLoc;
    229 
    230         // Try N threads w/ both Sz and Llc.
    231         *AtomicLoc = Value1;
    232         for (size_t m = 0; m < NumThreads; ++m) {
    233           pthread_attr_init(&attr[m]);
    234           if (pthread_create(&t[m], &attr[m], &threadWrapper<Type>,
    235                              m % 2 == 0
    236                                  ? reinterpret_cast<void *>(&TDataLlc)
    237                                  : reinterpret_cast<void *>(&TDataSz)) != 0) {
    238             ++Failures;
    239             std::cout << "pthread_create failed w/ " << strerror(errno) << "\n";
    240             abort();
    241           }
    242         }
    243         for (size_t m = 0; m < NumThreads; ++m) {
    244           if (pthread_join(t[m], NULL) != 0) {
    245             ++Failures;
    246             std::cout << "pthread_join failed w/ " << strerror(errno) << "\n";
    247             abort();
    248           }
    249         }
    250         Type ResultMixed = *AtomicLoc;
    251 
    252         if (ResultLlc == ResultMixed) {
    253           ++Passes;
    254         } else {
    255           ++Failures;
    256           std::cout << "test_with_threads_" << Funcs[f].Name
    257                     << (8 * sizeof(Type)) << "(" << static_cast<uint64>(Value1)
    258                     << ", " << static_cast<uint64>(Value2)
    259                     << "): llc=" << static_cast<uint64>(ResultLlc)
    260                     << " mixed=" << static_cast<uint64>(ResultMixed) << "\n";
    261         }
    262       }
    263     }
    264   }
    265 }
    266 
    267 int main(int argc, char *argv[]) {
    268   size_t TotalTests = 0;
    269   size_t Passes = 0;
    270   size_t Failures = 0;
    271 
    272   testAtomicRMW<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
    273   testAtomicRMW<uint16_t>(&AtomicLocs.l16, TotalTests, Passes, Failures);
    274   testAtomicRMW<uint32_t>(&AtomicLocs.l32, TotalTests, Passes, Failures);
    275   testAtomicRMW<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
    276   testValCompareAndSwap<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
    277   testValCompareAndSwap<uint16_t>(&AtomicLocs.l16, TotalTests, Passes,
    278                                   Failures);
    279   testValCompareAndSwap<uint32_t>(&AtomicLocs.l32, TotalTests, Passes,
    280                                   Failures);
    281   testValCompareAndSwap<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
    282   testAtomicRMWThreads<uint8_t>(&AtomicLocs.l8, TotalTests, Passes, Failures);
    283   testAtomicRMWThreads<uint16_t>(&AtomicLocs.l16, TotalTests, Passes, Failures);
    284   testAtomicRMWThreads<uint32_t>(&AtomicLocs.l32, TotalTests, Passes, Failures);
    285   testAtomicRMWThreads<uint64>(&AtomicLocs.l64, TotalTests, Passes, Failures);
    286 
    287   std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
    288             << " Failures=" << Failures << "\n";
    289   return Failures;
    290 }
    291