1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <audio_utils/intrinsic_utils.h>
18
19 #include <gtest/gtest.h>
20 #include <random>
21 #include <vector>
22
23 static constexpr float kFloatTolerance = 1e-3;
24 static constexpr size_t kStandardSize = 8;
25 static constexpr float kRangeMin = -10.f;
26 static constexpr float kRangeMax = 10.f;
27
28 // see also std::seed_seq
29 static size_t seedCounter = 42;
30
31 // create uniform distribution
32 template <typename T, typename V>
initUniform(V & v,T rangeMin,T rangeMax)33 static void initUniform(V& v, T rangeMin, T rangeMax) {
34 std::minstd_rand gen(++seedCounter);
35 std::uniform_real_distribution<T> dis(rangeMin, rangeMax);
36
37 android::audio_utils::intrinsics::vapply([&]() { return dis(gen); }, v);
38 }
39
40 using android::audio_utils::intrinsics::veval;
41
42 // constexpr method tests can use static_assert.
43
44 static constexpr android::audio_utils::intrinsics::internal_array_t<float, 3> xyzzy =
45 { 10, 10, 10 };
46
47 static_assert(android::audio_utils::intrinsics::internal_array_t<float, 3>(10) ==
48 android::audio_utils::intrinsics::internal_array_t<float, 3>(
49 { 10, 10, 10 }));
50
51 static_assert(android::audio_utils::intrinsics::internal_array_t<float, 3>(10) !=
52 android::audio_utils::intrinsics::internal_array_t<float, 3>(
53 { 10, 10, 20 }));
54
55 static_assert(android::audio_utils::intrinsics::internal_array_t<float, 3>(10) !=
56 android::audio_utils::intrinsics::internal_array_t<float, 3>(
57 { 10, 10 })); // implicit zero fill at end.
58
59 static_assert(android::audio_utils::intrinsics::internal_array_t<float, 3>( { 10, 10, 0 }) ==
60 android::audio_utils::intrinsics::internal_array_t<float, 3>(
61 { 10, 10 })); // implicit zero fill at end.
62
63
64 static_assert(android::audio_utils::intrinsics::internal_array_t<float, 3>(3) ==
__anon182793810202() 65 []() { android::audio_utils::intrinsics::internal_array_t<float, 3> temp;
66 vapply(3, temp);
67 return temp; }());
68
TEST(IntrisicUtilsTest,vector_hw_ctor_compatibility)69 TEST(IntrisicUtilsTest, vector_hw_ctor_compatibility) {
70 const android::audio_utils::intrinsics::vector_hw_t<float, 3> a{ 1, 2, 3 };
71 const android::audio_utils::intrinsics::vector_hw_t<float, 3> b(
72 android::audio_utils::intrinsics::internal_array_t<float, 3>{ 1, 2, 3 });
73 const android::audio_utils::intrinsics::vector_hw_t<float, 3> c(
74 android::audio_utils::intrinsics::internal_array_t<float, 3>{ 1, 2, 2 });
75 EXPECT_TRUE(android::audio_utils::intrinsics::veq(a, b));
76 EXPECT_FALSE(android::audio_utils::intrinsics::veq(a, c));
77 }
78
TEST(IntrisicUtilsTest,veq_nan)79 TEST(IntrisicUtilsTest, veq_nan) {
80 const android::audio_utils::intrinsics::vector_hw_t<float, 3> a(std::nanf(""));
81 EXPECT_EQ(0, std::memcmp(&a, &a, sizeof(a))); // bitwise equal.
82 EXPECT_FALSE(android::audio_utils::intrinsics::veq(a, a)); // logically nan is not.
83 }
84
TEST(IntrisicUtilsTest,veq_zero)85 TEST(IntrisicUtilsTest, veq_zero) {
86 int32_t neg = 0x8000'0000;
87 int32_t pos = 0;
88 float negzero, poszero;
89 memcpy(&negzero, &neg, sizeof(neg)); // float negative zero.
90 memcpy(&poszero, &pos, sizeof(pos)); // float positive zero.
91 const android::audio_utils::intrinsics::vector_hw_t<float, 3> a(negzero);
92 const android::audio_utils::intrinsics::vector_hw_t<float, 3> b(poszero);
93 EXPECT_NE(0, std::memcmp(&a, &b, sizeof(a))); // bitwise not-equal.
94 EXPECT_TRUE(android::audio_utils::intrinsics::veq(a, b)); // logically equal.
95 }
96
97 template <typename D>
98 class IntrisicUtilsTest : public ::testing::Test {
99 };
100
101 // Basic intrinsic tests.
102 using FloatTypes = ::testing::Types<float, double,
103 android::audio_utils::intrinsics::internal_array_t<float, kStandardSize>,
104 android::audio_utils::intrinsics::internal_array_t<float, 1>,
105 android::audio_utils::intrinsics::internal_array_t<double, kStandardSize>,
106 android::audio_utils::intrinsics::vector_hw_t<float, kStandardSize>,
107 android::audio_utils::intrinsics::vector_hw_t<float, 1>,
108 android::audio_utils::intrinsics::vector_hw_t<float, 2>,
109 android::audio_utils::intrinsics::vector_hw_t<float, 4>,
110 android::audio_utils::intrinsics::vector_hw_t<float, 7>,
111 android::audio_utils::intrinsics::vector_hw_t<float, 15>
112 >;
113 TYPED_TEST_CASE(IntrisicUtilsTest, FloatTypes);
114
TYPED_TEST(IntrisicUtilsTest,vector_hw_ctor)115 TYPED_TEST(IntrisicUtilsTest, vector_hw_ctor) {
116 if constexpr (!std::is_arithmetic_v<TypeParam>) {
117 if constexpr(std::is_same_v<float, typename TypeParam::element_t>) {
118 android::audio_utils::intrinsics::vector_hw_t<float, TypeParam::size()>
119 a(TypeParam(0.5));
120 }
121 }
122 }
123
TYPED_TEST(IntrisicUtilsTest,vabs_constant)124 TYPED_TEST(IntrisicUtilsTest, vabs_constant) {
125 const TypeParam value(-3.125f);
126 const TypeParam result = veval([](auto v) { return std::abs(v); }, value);
127 ASSERT_EQ(result, android::audio_utils::intrinsics::vabs(value));
128 }
129
TYPED_TEST(IntrisicUtilsTest,vabs_random)130 TYPED_TEST(IntrisicUtilsTest, vabs_random) {
131 TypeParam value;
132 initUniform(value, kRangeMin, kRangeMax);
133 const TypeParam result = veval([](auto v) { return std::abs(v); }, value);
134 ASSERT_EQ(result, android::audio_utils::intrinsics::vabs(value));
135 }
136
TYPED_TEST(IntrisicUtilsTest,vadd_constant)137 TYPED_TEST(IntrisicUtilsTest, vadd_constant) {
138 const TypeParam a(0.25f);
139 const TypeParam b(0.5f);
140 const TypeParam result = veval(
141 [](auto x, auto y) { return x + y; }, a, b);
142 EXPECT_EQ(result, android::audio_utils::intrinsics::vadd(a, b));
143 }
144
TYPED_TEST(IntrisicUtilsTest,vadd_random)145 TYPED_TEST(IntrisicUtilsTest, vadd_random) {
146 TypeParam a, b;
147 initUniform(a, kRangeMin, kRangeMax);
148 initUniform(b, kRangeMin, kRangeMax);
149 const TypeParam result = veval(
150 [](auto x, auto y) { return x + y; }, a, b);
151 EXPECT_EQ(result, android::audio_utils::intrinsics::vadd(a, b));
152 }
153
TYPED_TEST(IntrisicUtilsTest,vaddv_random)154 TYPED_TEST(IntrisicUtilsTest, vaddv_random) {
155 TypeParam a;
156 initUniform(a, kRangeMin, kRangeMax);
157 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
158 element_t result{};
159 android::audio_utils::intrinsics::vapply([&result] (element_t value) { result += value; }, a);
160 EXPECT_NEAR(result, android::audio_utils::intrinsics::vaddv(a), kFloatTolerance);
161 }
162
TYPED_TEST(IntrisicUtilsTest,vdupn)163 TYPED_TEST(IntrisicUtilsTest, vdupn) {
164 constexpr float ref = 1.f;
165 const TypeParam value(ref);
166 EXPECT_EQ(value, android::audio_utils::intrinsics::vdupn<TypeParam>(ref));
167 }
168
TYPED_TEST(IntrisicUtilsTest,vld1)169 TYPED_TEST(IntrisicUtilsTest, vld1) {
170 const TypeParam value(2.f);
171 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(value));
172 EXPECT_EQ(value, android::audio_utils::intrinsics::vld1<TypeParam>(
173 reinterpret_cast<const element_t*>(&value)));
174 }
175
TYPED_TEST(IntrisicUtilsTest,vmax_constant)176 TYPED_TEST(IntrisicUtilsTest, vmax_constant) {
177 const TypeParam a(0.25f);
178 const TypeParam b(0.5f);
179 const TypeParam result = veval(
180 [](auto x, auto y) { return std::max(x, y); }, a, b);
181 ASSERT_EQ(result, android::audio_utils::intrinsics::vmax(a, b));
182 }
183
TYPED_TEST(IntrisicUtilsTest,vmax_random)184 TYPED_TEST(IntrisicUtilsTest, vmax_random) {
185 TypeParam a, b;
186 initUniform(a, kRangeMin, kRangeMax);
187 initUniform(b, kRangeMin, kRangeMax);
188 const TypeParam result = veval(
189 [](auto x, auto y) { return std::max(x, y); }, a, b);
190 ASSERT_EQ(result, android::audio_utils::intrinsics::vmax(a, b));
191 }
192
TYPED_TEST(IntrisicUtilsTest,vmaxv_random)193 TYPED_TEST(IntrisicUtilsTest, vmaxv_random) {
194 TypeParam a;
195 initUniform(a, kRangeMin, kRangeMax);
196 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
197 element_t result = android::audio_utils::intrinsics::first_element_of(a);
198 android::audio_utils::intrinsics::vapply(
199 [&result] (element_t value) { result = std::max(result, value); }, a);
200 ASSERT_EQ(result, android::audio_utils::intrinsics::vmaxv(a));
201 }
202
TYPED_TEST(IntrisicUtilsTest,vmax_random_scalar)203 TYPED_TEST(IntrisicUtilsTest, vmax_random_scalar) {
204 TypeParam a;
205 initUniform(a, kRangeMin, kRangeMax);
206 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
207 const element_t scalar = 3.f;
208 TypeParam b(scalar);
209 const TypeParam result = veval(
210 [](auto x, auto y) { return std::max(x, y); }, a, b);
211 EXPECT_EQ(result, android::audio_utils::intrinsics::vmax(a, scalar));
212 EXPECT_EQ(result, android::audio_utils::intrinsics::vmax(scalar, a));
213 EXPECT_EQ(result, android::audio_utils::intrinsics::vmax(a, b));
214 }
215
TYPED_TEST(IntrisicUtilsTest,vmin_constant)216 TYPED_TEST(IntrisicUtilsTest, vmin_constant) {
217 const TypeParam a(0.25f);
218 const TypeParam b(0.5f);
219 const TypeParam result = veval(
220 [](auto x, auto y) { return std::min(x, y); }, a, b);
221 ASSERT_EQ(result, android::audio_utils::intrinsics::vmin(a, b));
222 }
223
TYPED_TEST(IntrisicUtilsTest,vmin_random)224 TYPED_TEST(IntrisicUtilsTest, vmin_random) {
225 TypeParam a, b;
226 initUniform(a, kRangeMin, kRangeMax);
227 initUniform(b, kRangeMin, kRangeMax);
228 const TypeParam result = veval(
229 [](auto x, auto y) { return std::min(x, y); }, a, b);
230 ASSERT_EQ(result, android::audio_utils::intrinsics::vmin(a, b));
231 }
232
TYPED_TEST(IntrisicUtilsTest,vminv_random)233 TYPED_TEST(IntrisicUtilsTest, vminv_random) {
234 TypeParam a;
235 initUniform(a, kRangeMin, kRangeMax);
236 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
237 element_t result = android::audio_utils::intrinsics::first_element_of(a);
238 android::audio_utils::intrinsics::vapply(
239 [&result] (element_t value) { result = std::min(result, value); }, a);
240 ASSERT_EQ(result, android::audio_utils::intrinsics::vminv(a));
241 }
242
TYPED_TEST(IntrisicUtilsTest,vmin_random_scalar)243 TYPED_TEST(IntrisicUtilsTest, vmin_random_scalar) {
244 TypeParam a;
245 initUniform(a, kRangeMin, kRangeMax);
246 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
247 const element_t scalar = 3.f;
248 TypeParam b(scalar);
249 const TypeParam result = veval(
250 [](auto x, auto y) { return std::min(x, y); }, a, b);
251 EXPECT_EQ(result, android::audio_utils::intrinsics::vmin(a, scalar));
252 EXPECT_EQ(result, android::audio_utils::intrinsics::vmin(scalar, a));
253 EXPECT_EQ(result, android::audio_utils::intrinsics::vmin(a, b));
254 }
255
TYPED_TEST(IntrisicUtilsTest,vmla_constant)256 TYPED_TEST(IntrisicUtilsTest, vmla_constant) {
257 const TypeParam a(2.125f);
258 const TypeParam b(2.25f);
259 const TypeParam c(2.5f);
260 const TypeParam result = veval(
261 [](auto x, auto y, auto z) { return x + y * z; }, a, b, c);
262 EXPECT_EQ(result, android::audio_utils::intrinsics::vmla(a, b, c));
263 }
264
TYPED_TEST(IntrisicUtilsTest,vmla_random)265 TYPED_TEST(IntrisicUtilsTest, vmla_random) {
266 TypeParam a, b, c;
267 initUniform(a, kRangeMin, kRangeMax);
268 initUniform(b, kRangeMin, kRangeMax);
269 initUniform(c, kRangeMin, kRangeMax);
270 const TypeParam result = veval(
271 [](auto x, auto y, auto z) { return x + y * z; }, a, b, c);
272 EXPECT_EQ(result, android::audio_utils::intrinsics::vmla(a, b, c));
273 }
274
TYPED_TEST(IntrisicUtilsTest,vmla_random_scalar)275 TYPED_TEST(IntrisicUtilsTest, vmla_random_scalar) {
276 TypeParam a, b;
277 initUniform(a, kRangeMin, kRangeMax);
278 initUniform(b, kRangeMin, kRangeMax);
279 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
280 const element_t scalar = 3.f;
281 const TypeParam c(scalar);
282 const TypeParam result = veval(
283 [](auto x, auto y, auto z) { return x + y * z; }, a, b, c);
284 EXPECT_EQ(result, android::audio_utils::intrinsics::vmla(a, scalar, b));
285 EXPECT_EQ(result, android::audio_utils::intrinsics::vmla(a, b, scalar));
286 EXPECT_EQ(result, android::audio_utils::intrinsics::vmla(a, b, c));
287 }
288
TYPED_TEST(IntrisicUtilsTest,vmul_constant)289 TYPED_TEST(IntrisicUtilsTest, vmul_constant) {
290 const TypeParam a(2.25f);
291 const TypeParam b(2.5f);
292 const TypeParam result = veval(
293 [](auto x, auto y) { return x * y; }, a, b);
294 EXPECT_EQ(result, android::audio_utils::intrinsics::vmul(a, b));
295 }
296
TYPED_TEST(IntrisicUtilsTest,vmul_random)297 TYPED_TEST(IntrisicUtilsTest, vmul_random) {
298 TypeParam a, b;
299 initUniform(a, kRangeMin, kRangeMax);
300 initUniform(b, kRangeMin, kRangeMax);
301 const TypeParam result = veval(
302 [](auto x, auto y) { return x * y; }, a, b);
303 EXPECT_EQ(result, android::audio_utils::intrinsics::vmul(a, b));
304 }
305
TYPED_TEST(IntrisicUtilsTest,vmul_random_scalar)306 TYPED_TEST(IntrisicUtilsTest, vmul_random_scalar) {
307 TypeParam a;
308 initUniform(a, kRangeMin, kRangeMax);
309 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(a));
310 const element_t scalar = 3.f;
311 const TypeParam b(scalar);
312 const TypeParam result = veval(
313 [](auto x, auto y) { return x * y; }, a, b);
314 EXPECT_EQ(result, android::audio_utils::intrinsics::vmul(a, scalar));
315 EXPECT_EQ(result, android::audio_utils::intrinsics::vmul(scalar, a));
316 EXPECT_EQ(result, android::audio_utils::intrinsics::vmul(a, b));
317 }
318
TYPED_TEST(IntrisicUtilsTest,vneg_constant)319 TYPED_TEST(IntrisicUtilsTest, vneg_constant) {
320 const TypeParam value(3.125f);
321 const TypeParam result = veval([](auto v) { return -v; }, value);
322 EXPECT_EQ(result, android::audio_utils::intrinsics::vneg(value));
323 }
324
TYPED_TEST(IntrisicUtilsTest,vneg_random)325 TYPED_TEST(IntrisicUtilsTest, vneg_random) {
326 TypeParam value;
327 initUniform(value, kRangeMin, kRangeMax);
328 const TypeParam result = veval([](auto v) { return -v; }, value);
329 EXPECT_EQ(result, android::audio_utils::intrinsics::vneg(value));
330 }
331
TYPED_TEST(IntrisicUtilsTest,vst1)332 TYPED_TEST(IntrisicUtilsTest, vst1) {
333 constexpr float ref = 2.f;
334 const TypeParam value(ref);
335 TypeParam destination(1.f);
336 using element_t = decltype(android::audio_utils::intrinsics::first_element_of(value));
337 android::audio_utils::intrinsics::vst1(
338 reinterpret_cast<element_t*>(&destination),
339 android::audio_utils::intrinsics::vdupn<TypeParam>(ref));
340 EXPECT_EQ(value, destination);
341 }
342
TYPED_TEST(IntrisicUtilsTest,vsub_constant)343 TYPED_TEST(IntrisicUtilsTest, vsub_constant) {
344 const TypeParam a(1.25f);
345 const TypeParam b(1.5f);
346 const TypeParam result = veval(
347 [](auto x, auto y) { return x - y; }, a, b);
348 EXPECT_EQ(result, android::audio_utils::intrinsics::vsub(a, b));
349 }
350
TYPED_TEST(IntrisicUtilsTest,vsub_random)351 TYPED_TEST(IntrisicUtilsTest, vsub_random) {
352 TypeParam a, b;
353 initUniform(a, kRangeMin, kRangeMax);
354 initUniform(b, kRangeMin, kRangeMax);
355 const TypeParam result = veval(
356 [](auto x, auto y) { return x - y; }, a, b);
357 EXPECT_EQ(result, android::audio_utils::intrinsics::vsub(a, b));
358 }
359
TYPED_TEST(IntrisicUtilsTest,vclamp_constant)360 TYPED_TEST(IntrisicUtilsTest, vclamp_constant) {
361 const TypeParam a(0.25f);
362 const TypeParam b(0.5f);
363 const TypeParam c(1.f);
364 const TypeParam result = veval(
365 [](auto x, auto y, auto z) { if (y > z) {
366 return std::min(std::max(x, y), z); // undefined behavior, make defined.
367 } else {
368 return std::clamp(x, y, z);
369 }
370 }, a, b, c);
371 ASSERT_EQ(result, android::audio_utils::intrinsics::vclamp(a, b, c));
372 }
373
TYPED_TEST(IntrisicUtilsTest,vclamp_random)374 TYPED_TEST(IntrisicUtilsTest, vclamp_random) {
375 TypeParam a, b, c;
376 initUniform(a, kRangeMin, kRangeMax);
377 initUniform(b, kRangeMin, kRangeMax);
378 initUniform(c, kRangeMin, kRangeMax);
379 const TypeParam result = veval(
380 [](auto x, auto y, auto z) { if (y > z) {
381 return std::min(std::max(x, y), z); // undefined behavior, make defined.
382 } else {
383 return std::clamp(x, y, z);
384 }
385 }, a, b, c);
386 ASSERT_EQ(result, android::audio_utils::intrinsics::vclamp(a, b, c));
387 }
388