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.

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