The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

881 lines
33KB

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