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