OpenShot Audio Library | OpenShotAudio 0.4.0
juce_SIMDRegister_test.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce::dsp
27{
28
29namespace SIMDRegister_test_internal
30{
31 template <typename type>
32 struct RandomPrimitive
33 {
34 static type next (Random& random)
35 {
36 if constexpr (std::is_floating_point_v<type>)
37 {
38 return static_cast<type> (std::is_signed_v<type> ? (random.nextFloat() * 16.0) - 8.0
39 : (random.nextFloat() * 8.0));
40 }
41 else if constexpr (std::is_integral_v<type>)
42 {
43 return static_cast<type> (random.nextInt64());
44 }
45 }
46 };
47
48 template <typename type>
49 struct RandomValue
50 {
51 static type next (Random& random)
52 {
53 return RandomPrimitive<type>::next (random);
54 }
55 };
56
57 template <typename type>
58 struct RandomValue<std::complex<type>>
59 {
60 static std::complex<type> next (Random& random)
61 {
62 return {RandomPrimitive<type>::next (random), RandomPrimitive<type>::next (random)};
63 }
64 };
65
66 template <typename type>
67 static void fillVec (type* dst, Random& random)
68 {
69 std::generate_n (dst, SIMDRegister<type>::SIMDNumElements, [&]
70 {
71 return RandomValue<type>::next (random);
72 });
73 }
74
75 // Avoid visual studio warning
76 template <typename type>
77 static type safeAbs (type a)
78 {
79 return static_cast<type> (std::abs (static_cast<double> (a)));
80 }
81
82 template <typename type>
83 static type safeAbs (std::complex<type> a)
84 {
85 return std::abs (a);
86 }
87
88 template <typename type>
89 static double difference (type a)
90 {
91 return static_cast<double> (safeAbs (a));
92 }
93
94 template <typename type>
95 static double difference (type a, type b)
96 {
97 return difference (a - b);
98 }
99} // namespace SIMDRegister_test_internal
100
101// These tests need to be strictly run on all platforms supported by JUCE as the
102// SIMD code is highly platform dependent.
103
104class SIMDRegisterUnitTests final : public UnitTest
105{
106public:
107 template <typename> struct Tag {};
108
109 SIMDRegisterUnitTests()
110 : UnitTest ("SIMDRegister UnitTests", UnitTestCategories::dsp)
111 {}
112
113 //==============================================================================
114 // Some helper classes
115 template <typename type>
116 static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar)
117 {
118 alignas (sizeof (SIMDRegister<type>)) type elements[SIMDRegister<type>::SIMDNumElements];
119
120 vec.copyToRawArray (elements);
121
122 // as we do not want to rely on the access operator we cast this to a primitive pointer
123 return std::all_of (std::begin (elements), std::end (elements), [scalar] (const auto x) { return exactlyEqual (x, scalar); });
124 }
125
126 template <typename type>
127 static bool vecEqualToArray (const SIMDRegister<type>& vec, const type* array)
128 {
129 HeapBlock<type> vecElementsStorage (SIMDRegister<type>::SIMDNumElements * 2);
130 auto* ptr = SIMDRegister<type>::getNextSIMDAlignedPtr (vecElementsStorage.getData());
131 vec.copyToRawArray (ptr);
132
133 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
134 {
135 double delta = SIMDRegister_test_internal::difference (ptr[i], array[i]);
136 if (delta > 1e-4)
137 {
138 DBG ("a: " << SIMDRegister_test_internal::difference (ptr[i]) << " b: " << SIMDRegister_test_internal::difference (array[i]) << " difference: " << delta);
139 return false;
140 }
141 }
142
143 return true;
144 }
145
146 template <typename type>
147 static void copy (SIMDRegister<type>& vec, const type* ptr)
148 {
150 {
152 }
153 else
154 {
155 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
156 vec[i] = ptr[i];
157 }
158 }
159
160 //==============================================================================
161 // Some useful operations to test
162 struct Addition
163 {
164 template <typename typeOne, typename typeTwo>
165 static void inplace (typeOne& a, const typeTwo& b)
166 {
167 a += b;
168 }
169
170 template <typename typeOne, typename typeTwo>
171 static typeOne outofplace (const typeOne& a, const typeTwo& b)
172 {
173 return a + b;
174 }
175 };
176
177 struct Subtraction
178 {
179 template <typename typeOne, typename typeTwo>
180 static void inplace (typeOne& a, const typeTwo& b)
181 {
182 a -= b;
183 }
184
185 template <typename typeOne, typename typeTwo>
186 static typeOne outofplace (const typeOne& a, const typeTwo& b)
187 {
188 return a - b;
189 }
190 };
191
192 struct Multiplication
193 {
194 template <typename typeOne, typename typeTwo>
195 static void inplace (typeOne& a, const typeTwo& b)
196 {
197 a *= b;
198 }
199
200 template <typename typeOne, typename typeTwo>
201 static typeOne outofplace (const typeOne& a, const typeTwo& b)
202 {
203 return a * b;
204 }
205 };
206
207 struct BitAND
208 {
209 template <typename typeOne, typename typeTwo>
210 static void inplace (typeOne& a, const typeTwo& b)
211 {
212 a &= b;
213 }
214
215 template <typename typeOne, typename typeTwo>
216 static typeOne outofplace (const typeOne& a, const typeTwo& b)
217 {
218 return a & b;
219 }
220 };
221
222 struct BitOR
223 {
224 template <typename typeOne, typename typeTwo>
225 static void inplace (typeOne& a, const typeTwo& b)
226 {
227 a |= b;
228 }
229
230 template <typename typeOne, typename typeTwo>
231 static typeOne outofplace (const typeOne& a, const typeTwo& b)
232 {
233 return a | b;
234 }
235 };
236
237 struct BitXOR
238 {
239 template <typename typeOne, typename typeTwo>
240 static void inplace (typeOne& a, const typeTwo& b)
241 {
242 a ^= b;
243 }
244
245 template <typename typeOne, typename typeTwo>
246 static typeOne outofplace (const typeOne& a, const typeTwo& b)
247 {
248 return a ^ b;
249 }
250 };
251
252 //==============================================================================
253 // the individual tests
254 struct InitializationTest
255 {
256 template <typename type>
257 static void run (UnitTest& u, Random& random, Tag<type>)
258 {
259 u.expect (allValuesEqualTo<type> (SIMDRegister<type>::expand (static_cast<type> (23)), 23));
260
261 {
262 #ifdef _MSC_VER
263 __declspec (align (sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
264 #else
265 type elements[SIMDRegister<type>::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister<type>))));
266 #endif
267 SIMDRegister_test_internal::fillVec (elements, random);
268 SIMDRegister<type> a (SIMDRegister<type>::fromRawArray (elements));
269
270 u.expect (vecEqualToArray (a, elements));
271
272 SIMDRegister<type> b (a);
273 a *= static_cast<type> (2);
274
275 u.expect (vecEqualToArray (b, elements));
276 }
277 }
278 };
279
280 struct AccessTest
281 {
282 template <typename type>
283 static void run (UnitTest& u, Random& random, Tag<type>)
284 {
285 // set-up
286 SIMDRegister<type> a;
288
289 SIMDRegister_test_internal::fillVec (array, random);
290
291 // Test non-const access operator
292 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
293 a[i] = array[i];
294
295 u.expect (vecEqualToArray (a, array));
296
297 // Test const access operator
298 const SIMDRegister<type>& b = a;
299
300 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
301 u.expect (exactlyEqual (b[i], array[i]));
302 }
303 };
304
305 template <class Operation>
306 struct OperatorTests
307 {
308 template <typename type>
309 static void run (UnitTest& u, Random& random, Tag<type>)
310 {
311 for (int n = 0; n < 100; ++n)
312 {
313 // set-up
314 SIMDRegister<type> a (static_cast<type> (0));
315 SIMDRegister<type> b (static_cast<type> (0));
316 SIMDRegister<type> c (static_cast<type> (0));
317
321
322 SIMDRegister_test_internal::fillVec (array_a, random);
323 SIMDRegister_test_internal::fillVec (array_b, random);
324 SIMDRegister_test_internal::fillVec (array_c, random);
325
326 copy (a, array_a); copy (b, array_b); copy (c, array_c);
327
328 // test in-place with both params being vectors
329 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
330 Operation::template inplace<type, type> (array_a[i], array_b[i]);
331
332 Operation::template inplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
333
334 u.expect (vecEqualToArray (a, array_a));
335 u.expect (vecEqualToArray (b, array_b));
336
337 SIMDRegister_test_internal::fillVec (array_a, random);
338 SIMDRegister_test_internal::fillVec (array_b, random);
339 SIMDRegister_test_internal::fillVec (array_c, random);
340
341 copy (a, array_a); copy (b, array_b); copy (c, array_c);
342
343 // test in-place with one param being scalar
344 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
345 Operation::template inplace<type, type> (array_b[i], static_cast<type> (2));
346
347 Operation::template inplace<SIMDRegister<type>, type> (b, 2);
348
349 u.expect (vecEqualToArray (a, array_a));
350 u.expect (vecEqualToArray (b, array_b));
351
352 // set-up again
353 SIMDRegister_test_internal::fillVec (array_a, random);
354 SIMDRegister_test_internal::fillVec (array_b, random);
355 SIMDRegister_test_internal::fillVec (array_c, random);
356 copy (a, array_a); copy (b, array_b); copy (c, array_c);
357
358 // test out-of-place with both params being vectors
359 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
360 array_c[i] = Operation::template outofplace<type, type> (array_a[i], array_b[i]);
361
362 c = Operation::template outofplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
363
364 u.expect (vecEqualToArray (a, array_a));
365 u.expect (vecEqualToArray (b, array_b));
366 u.expect (vecEqualToArray (c, array_c));
367
368 // test out-of-place with one param being scalar
369 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
370 array_c[i] = Operation::template outofplace<type, type> (array_b[i], static_cast<type> (2));
371
372 c = Operation::template outofplace<SIMDRegister<type>, type> (b, 2);
373
374 u.expect (vecEqualToArray (a, array_a));
375 u.expect (vecEqualToArray (b, array_b));
376 u.expect (vecEqualToArray (c, array_c));
377 }
378 }
379 };
380
381 template <class Operation>
382 struct BitOperatorTests
383 {
384 template <typename type>
385 static void run (UnitTest& u, Random& random, Tag<type>)
386 {
387 typedef typename SIMDRegister<type>::vMaskType vMaskType;
388 typedef typename SIMDRegister<type>::MaskType MaskType;
389
390 for (int n = 0; n < 100; ++n)
391 {
392 // Check flip sign bit and using as a union
393 {
395
396 union ConversionUnion
397 {
398 inline ConversionUnion() : floatVersion (static_cast<type> (0)) {}
399 inline ~ConversionUnion() {}
400 SIMDRegister<type> floatVersion;
401 vMaskType intVersion;
402 } a, b;
403
404 vMaskType bitmask = vMaskType::expand (static_cast<MaskType> (1) << (sizeof (MaskType) - 1));
405 SIMDRegister_test_internal::fillVec (array_a, random);
406 copy (a.floatVersion, array_a);
407 copy (b.floatVersion, array_a);
408
409 Operation::template inplace<SIMDRegister<type>, vMaskType> (a.floatVersion, bitmask);
410 Operation::template inplace<vMaskType, vMaskType> (b.intVersion, bitmask);
411
412 #ifdef _MSC_VER
413 __declspec (align (sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
414 #else
415 type elements[SIMDRegister<type>::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister<type>))));
416 #endif
417 b.floatVersion.copyToRawArray (elements);
418
419 u.expect (vecEqualToArray (a.floatVersion, elements));
420 }
421
422 // set-up
423 SIMDRegister<type> a, c;
424 vMaskType b;
425
429
432
433 SIMDRegister_test_internal::fillVec (float_a, random);
434 SIMDRegister_test_internal::fillVec (array_b, random);
435 SIMDRegister_test_internal::fillVec (float_c, random);
436
437 memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
438 memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
439 copy (a, float_a); copy (b, array_b); copy (c, float_c);
440
441 // test in-place with both params being vectors
442 for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
443 Operation::template inplace<MaskType, MaskType> (array_a[i], array_b[i]);
444 memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
445
446 Operation::template inplace<SIMDRegister<type>, vMaskType> (a, b);
447
448 u.expect (vecEqualToArray (a, float_a));
449 u.expect (vecEqualToArray (b, array_b));
450
451 SIMDRegister_test_internal::fillVec (float_a, random);
452 SIMDRegister_test_internal::fillVec (array_b, random);
453 SIMDRegister_test_internal::fillVec (float_c, random);
454 memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
455 memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
456 copy (a, float_a); copy (b, array_b); copy (c, float_c);
457
458 // test in-place with one param being scalar
459 for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
460 Operation::template inplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
461 memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
462
463 Operation::template inplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
464
465 u.expect (vecEqualToArray (a, float_a));
466 u.expect (vecEqualToArray (b, array_b));
467
468 // set-up again
469 SIMDRegister_test_internal::fillVec (float_a, random);
470 SIMDRegister_test_internal::fillVec (array_b, random);
471 SIMDRegister_test_internal::fillVec (float_c, random);
472 memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
473 memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
474 copy (a, float_a); copy (b, array_b); copy (c, float_c);
475
476 // test out-of-place with both params being vectors
477 for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
478 {
479 array_c[i] =
480 Operation::template outofplace<MaskType, MaskType> (array_a[i], array_b[i]);
481 }
482 memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
483 memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
484
485 c = Operation::template outofplace<SIMDRegister<type>, vMaskType> (a, b);
486
487 u.expect (vecEqualToArray (a, float_a));
488 u.expect (vecEqualToArray (b, array_b));
489 u.expect (vecEqualToArray (c, float_c));
490
491 // test out-of-place with one param being scalar
492 for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
493 array_c[i] = Operation::template outofplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
494 memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
495 memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
496
497 c = Operation::template outofplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
498
499 u.expect (vecEqualToArray (a, float_a));
500 u.expect (vecEqualToArray (b, array_b));
501 u.expect (vecEqualToArray (c, float_c));
502 }
503 }
504 };
505
506 struct CheckComparisonOps
507 {
508 template <typename type>
509 static void run (UnitTest& u, Random& random, Tag<type>)
510 {
511 typedef typename SIMDRegister<type>::vMaskType vMaskType;
512 typedef typename SIMDRegister<type>::MaskType MaskType;
513
514 for (int i = 0; i < 100; ++i)
515 {
516 // set-up
519 MaskType array_eq [SIMDRegister<type>::SIMDNumElements];
520 MaskType array_neq [SIMDRegister<type>::SIMDNumElements];
521 MaskType array_lt [SIMDRegister<type>::SIMDNumElements];
522 MaskType array_le [SIMDRegister<type>::SIMDNumElements];
523 MaskType array_gt [SIMDRegister<type>::SIMDNumElements];
524 MaskType array_ge [SIMDRegister<type>::SIMDNumElements];
525
526
527 SIMDRegister_test_internal::fillVec (array_a, random);
528 SIMDRegister_test_internal::fillVec (array_b, random);
529
530 // do check
531 for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
532 {
533 array_eq [j] = ( exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
534 array_neq [j] = (! exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
535 array_lt [j] = (array_a[j] < array_b[j]) ? static_cast<MaskType> (-1) : 0;
536 array_le [j] = (array_a[j] <= array_b[j]) ? static_cast<MaskType> (-1) : 0;
537 array_gt [j] = (array_a[j] > array_b[j]) ? static_cast<MaskType> (-1) : 0;
538 array_ge [j] = (array_a[j] >= array_b[j]) ? static_cast<MaskType> (-1) : 0;
539 }
540
541 SIMDRegister<type> a (static_cast<type> (0));
542 SIMDRegister<type> b (static_cast<type> (0));
543
544 vMaskType eq, neq, lt, le, gt, ge;
545
546 copy (a, array_a);
547 copy (b, array_b);
548
549 eq = SIMDRegister<type>::equal (a, b);
550 neq = SIMDRegister<type>::notEqual (a, b);
555
556 u.expect (vecEqualToArray (eq, array_eq ));
557 u.expect (vecEqualToArray (neq, array_neq));
558 u.expect (vecEqualToArray (lt, array_lt ));
559 u.expect (vecEqualToArray (le, array_le ));
560 u.expect (vecEqualToArray (gt, array_gt ));
561 u.expect (vecEqualToArray (ge, array_ge ));
562
563 do
564 {
565 SIMDRegister_test_internal::fillVec (array_a, random);
566 SIMDRegister_test_internal::fillVec (array_b, random);
567 } while (std::equal (array_a, array_a + SIMDRegister<type>::SIMDNumElements, array_b));
568
569 copy (a, array_a);
570 copy (b, array_b);
571 u.expect (a != b);
572 u.expect (b != a);
573 u.expect (! (a == b));
574 u.expect (! (b == a));
575
576 SIMDRegister_test_internal::fillVec (array_a, random);
577 copy (a, array_a);
578 copy (b, array_a);
579
580 u.expect (a == b);
581 u.expect (b == a);
582 u.expect (! (a != b));
583 u.expect (! (b != a));
584
585 type scalar = a[0];
586 a = SIMDRegister<type>::expand (scalar);
587
588 u.expect (a == scalar);
589 u.expect (! (a != scalar));
590
591 scalar--;
592
593 u.expect (a != scalar);
594 u.expect (! (a == scalar));
595 }
596 }
597 };
598
599 struct CheckMultiplyAdd
600 {
601 template <typename type>
602 static void run (UnitTest& u, Random& random, Tag<type>)
603 {
604 // set-up
609
610 SIMDRegister_test_internal::fillVec (array_a, random);
611 SIMDRegister_test_internal::fillVec (array_b, random);
612 SIMDRegister_test_internal::fillVec (array_c, random);
613 SIMDRegister_test_internal::fillVec (array_d, random);
614
615 // check
616 for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
617 array_d[i] = array_a[i] + (array_b[i] * array_c[i]);
618
619 SIMDRegister<type> a, b, c, d;
620
621 copy (a, array_a);
622 copy (b, array_b);
623 copy (c, array_c);
624
626
627 u.expect (vecEqualToArray (d, array_d));
628 }
629 };
630
631 struct CheckMinMax
632 {
633 template <typename type>
634 static void run (UnitTest& u, Random& random, Tag<type>)
635 {
636 for (int i = 0; i < 100; ++i)
637 {
642
643 for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
644 {
645 array_a[j] = static_cast<type> (random.nextInt (127));
646 array_b[j] = static_cast<type> (random.nextInt (127));
647 }
648
649 for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
650 {
651 array_min[j] = (array_a[j] < array_b[j]) ? array_a[j] : array_b[j];
652 array_max[j] = (array_a[j] > array_b[j]) ? array_a[j] : array_b[j];
653 }
654
655 SIMDRegister<type> a (static_cast<type> (0));
656 SIMDRegister<type> b (static_cast<type> (0));
657 SIMDRegister<type> vMin (static_cast<type> (0));
658 SIMDRegister<type> vMax (static_cast<type> (0));
659
660 copy (a, array_a);
661 copy (b, array_b);
662
663 vMin = jmin (a, b);
664 vMax = jmax (a, b);
665
666 u.expect (vecEqualToArray (vMin, array_min));
667 u.expect (vecEqualToArray (vMax, array_max));
668
669 copy (vMin, array_a);
670 copy (vMax, array_a);
671
672 vMin = SIMDRegister<type>::min (a, b);
673 vMax = SIMDRegister<type>::max (a, b);
674
675 u.expect (vecEqualToArray (vMin, array_min));
676 u.expect (vecEqualToArray (vMax, array_max));
677 }
678 }
679 };
680
681 struct CheckSum
682 {
683 template <typename type>
684 static void run (UnitTest& u, Random& random, Tag<type>)
685 {
687
688 SIMDRegister_test_internal::fillVec (array, random);
689
690 using AddedType = decltype (type{} + type{});
691 const auto sumCheck = (type) std::accumulate (std::begin (array), std::end (array), AddedType{});
692
693 SIMDRegister<type> a;
694 copy (a, array);
695
696 u.expect (SIMDRegister_test_internal::difference (sumCheck, a.sum()) < 1e-4);
697 }
698 };
699
700 struct CheckAbs
701 {
702 template <typename type>
703 static void run (UnitTest& u, Random& random, Tag<type>)
704 {
707
708 SIMDRegister_test_internal::fillVec (inArray, random);
709
710 SIMDRegister<type> a;
711 copy (a, inArray);
713
714 auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); };
715
716 for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
717 outArray[j] = calcAbs (inArray[j]);
718
719 u.expect (vecEqualToArray (a, outArray));
720 }
721 };
722
723 struct CheckTruncate
724 {
725 template <typename type>
726 static void run (UnitTest& u, Random& random, Tag<type>)
727 {
730
731 SIMDRegister_test_internal::fillVec (inArray, random);
732
733 SIMDRegister<type> a;
734 copy (a, inArray);
736
737 for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
738 outArray[j] = (type) (int) inArray[j];
739
740 u.expect (vecEqualToArray (a, outArray));
741 }
742 };
743
744 struct CheckBoolEquals
745 {
746 template <typename type>
747 static void run (UnitTest& u, Random& random, Tag<type>)
748 {
750
751 auto value = std::is_signed_v<type> ? static_cast<type> ((random.nextFloat() * 16.0) - 8.0)
752 : static_cast<type> (random.nextFloat() * 8.0);
753
754 std::fill (array, array + SIMDRegister<type>::SIMDNumElements, value);
755 SIMDRegister<type> a, b;
756 copy (a, array);
757
758 u.expect (a == value);
759 u.expect (! (a != value));
760 value += 1;
761
762 u.expect (a != value);
763 u.expect (! (a == value));
764
765 SIMDRegister_test_internal::fillVec (array, random);
766 copy (a, array);
767 copy (b, array);
768
769 u.expect (a == b);
770 u.expect (! (a != b));
771
772 SIMDRegister_test_internal::fillVec (array, random);
773 copy (b, array);
774
775 u.expect (a != b);
776 u.expect (! (a == b));
777 }
778 };
779
780 //==============================================================================
781 template <class TheTest>
782 void runTestFloatingPoint (const char* unitTestName, TheTest)
783 {
784 beginTest (unitTestName);
785
786 Random random = getRandom();
787
788 TheTest::run (*this, random, Tag<float> {});
789 TheTest::run (*this, random, Tag<double>{});
790 }
791
792 //==============================================================================
793 template <class TheTest>
794 void runTestForAllTypes (const char* unitTestName, TheTest)
795 {
796 beginTest (unitTestName);
797
798 Random random = getRandom();
799
800 TheTest::run (*this, random, Tag<float> {});
801 TheTest::run (*this, random, Tag<double> {});
802 TheTest::run (*this, random, Tag<int8_t> {});
803 TheTest::run (*this, random, Tag<uint8_t> {});
804 TheTest::run (*this, random, Tag<int16_t> {});
805 TheTest::run (*this, random, Tag<uint16_t> {});
806 TheTest::run (*this, random, Tag<int32_t> {});
807 TheTest::run (*this, random, Tag<uint32_t> {});
808 TheTest::run (*this, random, Tag<int64_t> {});
809 TheTest::run (*this, random, Tag<uint64_t> {});
810 TheTest::run (*this, random, Tag<std::complex<float>> {});
811 TheTest::run (*this, random, Tag<std::complex<double>>{});
812 }
813
814 template <class TheTest>
815 void runTestNonComplex (const char* unitTestName, TheTest)
816 {
817 beginTest (unitTestName);
818
819 Random random = getRandom();
820
821 TheTest::run (*this, random, Tag<float> {});
822 TheTest::run (*this, random, Tag<double> {});
823 TheTest::run (*this, random, Tag<int8_t> {});
824 TheTest::run (*this, random, Tag<uint8_t> {});
825 TheTest::run (*this, random, Tag<int16_t> {});
826 TheTest::run (*this, random, Tag<uint16_t>{});
827 TheTest::run (*this, random, Tag<int32_t> {});
828 TheTest::run (*this, random, Tag<uint32_t>{});
829 TheTest::run (*this, random, Tag<int64_t> {});
830 TheTest::run (*this, random, Tag<uint64_t>{});
831 }
832
833 template <class TheTest>
834 void runTestSigned (const char* unitTestName, TheTest)
835 {
836 beginTest (unitTestName);
837
838 Random random = getRandom();
839
840 TheTest::run (*this, random, Tag<float> {});
841 TheTest::run (*this, random, Tag<double> {});
842 TheTest::run (*this, random, Tag<int8_t> {});
843 TheTest::run (*this, random, Tag<int16_t>{});
844 TheTest::run (*this, random, Tag<int32_t>{});
845 TheTest::run (*this, random, Tag<int64_t>{});
846 }
847
848 void runTest() override
849 {
850 runTestForAllTypes ("InitializationTest", InitializationTest{});
851
852 runTestForAllTypes ("AccessTest", AccessTest{});
853
854 runTestForAllTypes ("AdditionOperators", OperatorTests<Addition>{});
855 runTestForAllTypes ("SubtractionOperators", OperatorTests<Subtraction>{});
856 runTestForAllTypes ("MultiplicationOperators", OperatorTests<Multiplication>{});
857
858 runTestForAllTypes ("BitANDOperators", BitOperatorTests<BitAND>{});
859 runTestForAllTypes ("BitOROperators", BitOperatorTests<BitOR>{});
860 runTestForAllTypes ("BitXOROperators", BitOperatorTests<BitXOR>{});
861
862 runTestNonComplex ("CheckComparisons", CheckComparisonOps{});
863 runTestNonComplex ("CheckBoolEquals", CheckBoolEquals{});
864 runTestNonComplex ("CheckMinMax", CheckMinMax{});
865
866 runTestForAllTypes ("CheckMultiplyAdd", CheckMultiplyAdd{});
867 runTestForAllTypes ("CheckSum", CheckSum{});
868
869 runTestSigned ("CheckAbs", CheckAbs{});
870
871 runTestFloatingPoint ("CheckTruncate", CheckTruncate{});
872 }
873};
874
875static SIMDRegisterUnitTests SIMDRegisterUnitTests;
876
877} // namespace juce::dsp
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
Random getRandom() const
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate(SIMDRegister a) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE expand(ElementType s) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE max(SIMDRegister a, SIMDRegister b) noexcept
static ElementType * getNextSIMDAlignedPtr(ElementType *ptr) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual(SIMDRegister a, SIMDRegister b) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan(SIMDRegister a, SIMDRegister b) noexcept
static bool isSIMDAligned(const ElementType *ptr) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE abs(SIMDRegister a) noexcept
SIMDRegister< MaskType > vMaskType
static vMaskType JUCE_VECTOR_CALLTYPE equal(SIMDRegister a, SIMDRegister b) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd(SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE lessThan(SIMDRegister a, SIMDRegister b) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE min(SIMDRegister a, SIMDRegister b) noexcept
static constexpr size_t SIMDNumElements
SIMDInternal::MaskType< ElementType > MaskType
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual(SIMDRegister a, SIMDRegister b) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray(const ElementType *a) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE notEqual(SIMDRegister a, SIMDRegister b) noexcept