master
c 620 lines 17.6 KB
Raw
1 /*
2 * Copyright(c) 2021-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdio.h>
19 #include <stdint.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <limits.h>
23 #include <hexagon_types.h>
24 #include <hvx_hexagon_protos.h>
25
26 int err;
27
28 #include "hvx_misc.h"
29
30 static void test_load_tmp(void)
31 {
32 void *p0 = buffer0;
33 void *p1 = buffer1;
34 void *pout = output;
35
36 for (int i = 0; i < BUFSIZE; i++) {
37 /*
38 * Load into v12 as .tmp, then use it in the next packet
39 * Should get the new value within the same packet and
40 * the old value in the next packet
41 */
42 asm("v3 = vmem(%0 + #0)\n\t"
43 "r1 = #1\n\t"
44 "v12 = vsplat(r1)\n\t"
45 "{\n\t"
46 " v12.tmp = vmem(%1 + #0)\n\t"
47 " v4.w = vadd(v12.w, v3.w)\n\t"
48 "}\n\t"
49 "v4.w = vadd(v4.w, v12.w)\n\t"
50 "vmem(%2 + #0) = v4\n\t"
51 : : "r"(p0), "r"(p1), "r"(pout)
52 : "r1", "v12", "v3", "v4", "v6", "memory");
53 p0 += sizeof(MMVector);
54 p1 += sizeof(MMVector);
55 pout += sizeof(MMVector);
56
57 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
58 expect[i].w[j] = buffer0[i].w[j] + buffer1[i].w[j] + 1;
59 }
60 }
61
62 check_output_w(__LINE__, BUFSIZE);
63 }
64
65 static void test_load_tmp2(void)
66 {
67 void *pout0 = &output[0];
68 void *pout1 = &output[1];
69
70 asm volatile(
71 "r0 = #0x03030303\n\t"
72 "v16 = vsplat(r0)\n\t"
73 "r0 = #0x04040404\n\t"
74 "v18 = vsplat(r0)\n\t"
75 "r0 = #0x05050505\n\t"
76 "v21 = vsplat(r0)\n\t"
77 "{\n\t"
78 " v25:24 += vmpyo(v18.w, v14.h)\n\t"
79 " v15:14.tmp = vcombine(v21, v16)\n\t"
80 "}\n\t"
81 "vmem(%0 + #0) = v24\n\t"
82 "vmem(%1 + #0) = v25\n\t"
83 : : "r"(pout0), "r"(pout1)
84 : "r0", "v16", "v18", "v21", "v24", "v25", "memory"
85 );
86
87 for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; ++i) {
88 expect[0].w[i] = 0x180c0000;
89 expect[1].w[i] = 0x000c1818;
90 }
91
92 check_output_w(__LINE__, 2);
93 }
94
95 static void test_load_cur(void)
96 {
97 void *p0 = buffer0;
98 void *pout = output;
99
100 for (int i = 0; i < BUFSIZE; i++) {
101 asm("{\n\t"
102 " v2.cur = vmem(%0 + #0)\n\t"
103 " vmem(%1 + #0) = v2\n\t"
104 "}\n\t"
105 : : "r"(p0), "r"(pout) : "v2", "memory");
106 p0 += sizeof(MMVector);
107 pout += sizeof(MMVector);
108
109 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
110 expect[i].uw[j] = buffer0[i].uw[j];
111 }
112 }
113
114 check_output_w(__LINE__, BUFSIZE);
115 }
116
117 static void test_load_aligned(void)
118 {
119 /* Aligned loads ignore the low bits of the address */
120 void *p0 = buffer0;
121 void *pout = output;
122 const size_t offset = 13;
123
124 p0 += offset; /* Create an unaligned address */
125 asm("v2 = vmem(%0 + #0)\n\t"
126 "vmem(%1 + #0) = v2\n\t"
127 : : "r"(p0), "r"(pout) : "v2", "memory");
128
129 expect[0] = buffer0[0];
130
131 check_output_w(__LINE__, 1);
132 }
133
134 static void test_load_unaligned(void)
135 {
136 void *p0 = buffer0;
137 void *pout = output;
138 const size_t offset = 12;
139
140 p0 += offset; /* Create an unaligned address */
141 asm("v2 = vmemu(%0 + #0)\n\t"
142 "vmem(%1 + #0) = v2\n\t"
143 : : "r"(p0), "r"(pout) : "v2", "memory");
144
145 memcpy(expect, &buffer0[0].ub[offset], sizeof(MMVector));
146
147 check_output_w(__LINE__, 1);
148 }
149
150 static void test_store_aligned(void)
151 {
152 /* Aligned stores ignore the low bits of the address */
153 void *p0 = buffer0;
154 void *pout = output;
155 const size_t offset = 13;
156
157 pout += offset; /* Create an unaligned address */
158 asm("v2 = vmem(%0 + #0)\n\t"
159 "vmem(%1 + #0) = v2\n\t"
160 : : "r"(p0), "r"(pout) : "v2", "memory");
161
162 expect[0] = buffer0[0];
163
164 check_output_w(__LINE__, 1);
165 }
166
167 static void test_store_unaligned(void)
168 {
169 void *p0 = buffer0;
170 void *pout = output;
171 const size_t offset = 12;
172
173 pout += offset; /* Create an unaligned address */
174 asm("v2 = vmem(%0 + #0)\n\t"
175 "vmemu(%1 + #0) = v2\n\t"
176 : : "r"(p0), "r"(pout) : "v2", "memory");
177
178 memcpy(expect, buffer0, 2 * sizeof(MMVector));
179 memcpy(&expect[0].ub[offset], buffer0, sizeof(MMVector));
180
181 check_output_w(__LINE__, 2);
182 }
183
184 static void test_masked_store(bool invert)
185 {
186 void *p0 = buffer0;
187 void *pmask = mask;
188 void *pout = output;
189
190 memset(expect, 0xff, sizeof(expect));
191 memset(output, 0xff, sizeof(expect));
192
193 for (int i = 0; i < BUFSIZE; i++) {
194 if (invert) {
195 asm("r4 = #0\n\t"
196 "v4 = vsplat(r4)\n\t"
197 "v5 = vmem(%0 + #0)\n\t"
198 "q0 = vcmp.eq(v4.w, v5.w)\n\t"
199 "v5 = vmem(%1)\n\t"
200 "if (!q0) vmem(%2) = v5\n\t" /* Inverted test */
201 : : "r"(pmask), "r"(p0), "r"(pout)
202 : "r4", "v4", "v5", "q0", "memory");
203 } else {
204 asm("r4 = #0\n\t"
205 "v4 = vsplat(r4)\n\t"
206 "v5 = vmem(%0 + #0)\n\t"
207 "q0 = vcmp.eq(v4.w, v5.w)\n\t"
208 "v5 = vmem(%1)\n\t"
209 "if (q0) vmem(%2) = v5\n\t" /* Non-inverted test */
210 : : "r"(pmask), "r"(p0), "r"(pout)
211 : "r4", "v4", "v5", "q0", "memory");
212 }
213 p0 += sizeof(MMVector);
214 pmask += sizeof(MMVector);
215 pout += sizeof(MMVector);
216
217 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
218 if (invert) {
219 if (i + j % MASKMOD != 0) {
220 expect[i].w[j] = buffer0[i].w[j];
221 }
222 } else {
223 if (i + j % MASKMOD == 0) {
224 expect[i].w[j] = buffer0[i].w[j];
225 }
226 }
227 }
228 }
229
230 check_output_w(__LINE__, BUFSIZE);
231 }
232
233 static void test_new_value_store(void)
234 {
235 void *p0 = buffer0;
236 void *p1 = buffer1;
237 void *pout = output;
238
239 asm("{\n\t"
240 " v2 = vmem(%0 + #0)\n\t"
241 " vmem(%1 + #0) = v2.new\n\t"
242 "}\n\t"
243 : : "r"(p0), "r"(pout) : "v2", "memory");
244
245 expect[0] = buffer0[0];
246
247 check_output_w(__LINE__, 1);
248
249 /* Test the .new read from the high half of a pair */
250 asm("v7 = vmem(%0 + #0)\n\t"
251 "v12 = vmem(%1 + #0)\n\t"
252 "{\n\t"
253 " v5:4 = vcombine(v12, v7)\n\t"
254 " vmem(%2 + #0) = v5.new\n\t"
255 "}\n\t"
256 : : "r"(p0), "r"(p1), "r"(pout) : "v4", "v5", "v7", "v12", "memory");
257
258 expect[0] = buffer1[0];
259
260 check_output_w(__LINE__, 1);
261 }
262
263 static void test_max_temps()
264 {
265 void *p0 = buffer0;
266 void *pout = output;
267
268 asm("v0 = vmem(%0 + #0)\n\t"
269 "v1 = vmem(%0 + #1)\n\t"
270 "v2 = vmem(%0 + #2)\n\t"
271 "v3 = vmem(%0 + #3)\n\t"
272 "v4 = vmem(%0 + #4)\n\t"
273 "{\n\t"
274 " v1:0.w = vadd(v3:2.w, v1:0.w)\n\t"
275 " v2.b = vshuffe(v3.b, v2.b)\n\t"
276 " v3.w = vadd(v1.w, v4.w)\n\t"
277 " v4.tmp = vmem(%0 + #5)\n\t"
278 "}\n\t"
279 "vmem(%1 + #0) = v0\n\t"
280 "vmem(%1 + #1) = v1\n\t"
281 "vmem(%1 + #2) = v2\n\t"
282 "vmem(%1 + #3) = v3\n\t"
283 "vmem(%1 + #4) = v4\n\t"
284 : : "r"(p0), "r"(pout) : "memory");
285
286 /* The first two vectors come from the vadd-pair instruction */
287 for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
288 expect[0].w[i] = buffer0[0].w[i] + buffer0[2].w[i];
289 expect[1].w[i] = buffer0[1].w[i] + buffer0[3].w[i];
290 }
291 /* The third vector comes from the vshuffe instruction */
292 for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
293 expect[2].uh[i] = (buffer0[2].uh[i] & 0xff) |
294 (buffer0[3].uh[i] & 0xff) << 8;
295 }
296 /* The fourth vector comes from the vadd-single instruction */
297 for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
298 expect[3].w[i] = buffer0[1].w[i] + buffer0[5].w[i];
299 }
300 /*
301 * The fifth vector comes from the load to v4
302 * make sure the .tmp is dropped
303 */
304 expect[4] = buffer0[4];
305
306 check_output_b(__LINE__, 5);
307 }
308
309 TEST_VEC_OP2(vadd_w, vadd, .w, w, 4, +)
310 TEST_VEC_OP2(vadd_h, vadd, .h, h, 2, +)
311 TEST_VEC_OP2(vadd_b, vadd, .b, b, 1, +)
312 TEST_VEC_OP2(vsub_w, vsub, .w, w, 4, -)
313 TEST_VEC_OP2(vsub_h, vsub, .h, h, 2, -)
314 TEST_VEC_OP2(vsub_b, vsub, .b, b, 1, -)
315 TEST_VEC_OP2(vxor, vxor, , d, 8, ^)
316 TEST_VEC_OP2(vand, vand, , d, 8, &)
317 TEST_VEC_OP2(vor, vor, , d, 8, |)
318 TEST_VEC_OP1(vnot, vnot, , d, 8, ~)
319
320 #define TEST_VEC_ABSDIFF(NAME, INTRINSIC, SRC_FIELD, DST_FIELD, \
321 CHECK_FIELD, FIELDSZ) \
322 static inline void test_##NAME(void) \
323 { \
324 HVX_Vector v0; \
325 HVX_Vector v1; \
326 HVX_Vector vres; \
327 for (int i = 0; i < BUFSIZE; i++) { \
328 memcpy(&v0, &buffer0[i], sizeof(MMVector)); \
329 memcpy(&v1, &buffer1[i], sizeof(MMVector)); \
330 vres = INTRINSIC(v0, v1); \
331 memcpy(&output[i], &vres, sizeof(MMVector)); \
332 } \
333 for (int i = 0; i < BUFSIZE; i++) { \
334 for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \
335 int64_t diff = (int64_t)buffer0[i].SRC_FIELD[j] - \
336 (int64_t)buffer1[i].SRC_FIELD[j]; \
337 expect[i].DST_FIELD[j] = diff < 0 ? -diff : diff; \
338 } \
339 } \
340 check_output_##CHECK_FIELD(__LINE__, BUFSIZE); \
341 }
342
343 TEST_VEC_ABSDIFF(vabsdiffub, Q6_Vub_vabsdiff_VubVub, ub, ub, b, 1)
344 TEST_VEC_ABSDIFF(vabsdiffuh, Q6_Vuh_vabsdiff_VuhVuh, uh, uh, h, 2)
345 TEST_VEC_ABSDIFF(vabsdiffh, Q6_Vuh_vabsdiff_VhVh, h, uh, h, 2)
346 TEST_VEC_ABSDIFF(vabsdiffw, Q6_Vuw_vabsdiff_VwVw, w, uw, w, 4)
347
348 TEST_PRED_OP2(pred_or, or, |, "")
349 TEST_PRED_OP2(pred_or_n, or, |, "!")
350 TEST_PRED_OP2(pred_and, and, &, "")
351 TEST_PRED_OP2(pred_and_n, and, &, "!")
352 TEST_PRED_OP2(pred_xor, xor, ^, "")
353
354 static void test_vadduwsat(void)
355 {
356 /*
357 * Test for saturation by adding two numbers that add to more than UINT_MAX
358 * and make sure the result saturates to UINT_MAX
359 */
360 const uint32_t x = 0xffff0000;
361 const uint32_t y = 0x000fffff;
362
363 memset(expect, 0x12, sizeof(MMVector));
364 memset(output, 0x34, sizeof(MMVector));
365
366 asm volatile ("v10 = vsplat(%0)\n\t"
367 "v11 = vsplat(%1)\n\t"
368 "v21.uw = vadd(v11.uw, v10.uw):sat\n\t"
369 "vmem(%2+#0) = v21\n\t"
370 : /* no outputs */
371 : "r"(x), "r"(y), "r"(output)
372 : "v10", "v11", "v21", "memory");
373
374 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
375 expect[0].uw[j] = UINT_MAX;
376 }
377
378 check_output_w(__LINE__, 1);
379 }
380
381 static void test_vsubuwsat_dv(void)
382 {
383 /*
384 * Test for saturation by subtracting two numbers where the result is
385 * negative and make sure the result saturates to zero
386 *
387 * vsubuwsat_dv operates on an HVX register pair, so we'll have a
388 * pair of subtractions
389 * w - x < 0
390 * y - z < 0
391 */
392 const uint32_t w = 0x000000b7;
393 const uint32_t x = 0xffffff4e;
394 const uint32_t y = 0x31fe88e7;
395 const uint32_t z = 0x7fffff79;
396
397 memset(expect, 0x12, sizeof(MMVector) * 2);
398 memset(output, 0x34, sizeof(MMVector) * 2);
399
400 asm volatile ("v16 = vsplat(%0)\n\t"
401 "v17 = vsplat(%1)\n\t"
402 "v26 = vsplat(%2)\n\t"
403 "v27 = vsplat(%3)\n\t"
404 "v25:24.uw = vsub(v17:16.uw, v27:26.uw):sat\n\t"
405 "vmem(%4+#0) = v24\n\t"
406 "vmem(%4+#1) = v25\n\t"
407 : /* no outputs */
408 : "r"(w), "r"(y), "r"(x), "r"(z), "r"(output)
409 : "v16", "v17", "v24", "v25", "v26", "v27", "memory");
410
411 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
412 expect[0].uw[j] = 0x00000000;
413 expect[1].uw[j] = 0x00000000;
414 }
415
416 check_output_w(__LINE__, 2);
417 }
418
419 static void test_vsubwsat(void)
420 {
421 const int32_t x0 = INT32_MIN;
422 const int32_t y0 = 1;
423 const int32_t x1 = INT32_MAX;
424 const int32_t y1 = -1;
425 HVX_Vector v0;
426 HVX_Vector v1;
427 HVX_Vector vres;
428
429 /* INT32_MIN - 1 underflows and must saturate to INT32_MIN */
430 memset(expect, 0x12, sizeof(MMVector));
431 memset(output, 0x34, sizeof(MMVector));
432
433 v0 = Q6_V_vsplat_R(x0);
434 v1 = Q6_V_vsplat_R(y0);
435 vres = Q6_Vw_vsub_VwVw_sat(v0, v1);
436 memcpy(&output[0], &vres, sizeof(MMVector));
437
438 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
439 expect[0].w[j] = INT32_MIN;
440 }
441
442 check_output_w(__LINE__, 1);
443
444 /* INT32_MAX - (-1) overflows and must saturate to INT32_MAX */
445 memset(expect, 0x12, sizeof(MMVector));
446 memset(output, 0x34, sizeof(MMVector));
447
448 v0 = Q6_V_vsplat_R(x1);
449 v1 = Q6_V_vsplat_R(y1);
450 vres = Q6_Vw_vsub_VwVw_sat(v0, v1);
451 memcpy(&output[0], &vres, sizeof(MMVector));
452
453 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
454 expect[0].w[j] = INT32_MAX;
455 }
456
457 check_output_w(__LINE__, 1);
458 }
459
460 static void test_load_tmp_predicated(void)
461 {
462 void *p0 = buffer0;
463 void *p1 = buffer1;
464 void *pout = output;
465 bool pred = true;
466
467 for (int i = 0; i < BUFSIZE; i++) {
468 /*
469 * Load into v12 as .tmp with a predicate
470 * When the predicate is true, we get the vector from buffer1[i]
471 * When the predicate is false, we get a vector of all 1's
472 * Regardless of the predicate, the next packet should have
473 * a vector of all 1's
474 */
475 asm("v3 = vmem(%0 + #0)\n\t"
476 "r1 = #1\n\t"
477 "v12 = vsplat(r1)\n\t"
478 "p1 = !cmp.eq(%3, #0)\n\t"
479 "{\n\t"
480 " if (p1) v12.tmp = vmem(%1 + #0)\n\t"
481 " v4.w = vadd(v12.w, v3.w)\n\t"
482 "}\n\t"
483 "v4.w = vadd(v4.w, v12.w)\n\t"
484 "vmem(%2 + #0) = v4\n\t"
485 : : "r"(p0), "r"(p1), "r"(pout), "r"(pred)
486 : "r1", "p1", "v12", "v3", "v4", "v6", "memory");
487 p0 += sizeof(MMVector);
488 p1 += sizeof(MMVector);
489 pout += sizeof(MMVector);
490
491 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
492 expect[i].w[j] =
493 pred ? buffer0[i].w[j] + buffer1[i].w[j] + 1
494 : buffer0[i].w[j] + 2;
495 }
496 pred = !pred;
497 }
498
499 check_output_w(__LINE__, BUFSIZE);
500 }
501
502 static void test_load_cur_predicated(void)
503 {
504 bool pred = true;
505 for (int i = 0; i < BUFSIZE; i++) {
506 asm volatile("p0 = !cmp.eq(%3, #0)\n\t"
507 "v3 = vmem(%0+#0)\n\t"
508 /*
509 * Preload v4 to make sure that the assignment from the
510 * packet below is not being ignored when pred is false.
511 */
512 "r0 = #0x01237654\n\t"
513 "v4 = vsplat(r0)\n\t"
514 "{\n\t"
515 " if (p0) v3.cur = vmem(%1+#0)\n\t"
516 " v4 = v3\n\t"
517 "}\n\t"
518 "vmem(%2+#0) = v4\n\t"
519 :
520 : "r"(&buffer0[i]), "r"(&buffer1[i]),
521 "r"(&output[i]), "r"(pred)
522 : "r0", "p0", "v3", "v4", "memory");
523 expect[i] = pred ? buffer1[i] : buffer0[i];
524 pred = !pred;
525 }
526 check_output_w(__LINE__, BUFSIZE);
527 }
528
529 static void test_vcombine(void)
530 {
531 for (int i = 0; i < BUFSIZE / 2; i++) {
532 asm volatile("v2 = vsplat(%0)\n\t"
533 "v3 = vsplat(%1)\n\t"
534 "v3:2 = vcombine(v2, v3)\n\t"
535 "vmem(%2+#0) = v2\n\t"
536 "vmem(%2+#1) = v3\n\t"
537 :
538 : "r"(2 * i), "r"(2 * i + 1), "r"(&output[2 * i])
539 : "v2", "v3", "memory");
540 for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
541 expect[2 * i].w[j] = 2 * i + 1;
542 expect[2 * i + 1].w[j] = 2 * i;
543 }
544 }
545 check_output_w(__LINE__, BUFSIZE);
546 }
547
548 void test_store_new()
549 {
550 asm volatile(
551 "r0 = #0x12345678\n"
552 "v0 = vsplat(r0)\n"
553 "r0 = #0xff00ff00\n"
554 "v1 = vsplat(r0)\n"
555 "{\n"
556 " vdeal(v1,v0,r0)\n"
557 " vmem(%0) = v0.new\n"
558 "}\n"
559 :
560 : "r"(&output[0])
561 : "r0", "v0", "v1", "memory"
562 );
563 for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
564 expect[0].w[i] = 0x12345678;
565 }
566 check_output_w(__LINE__, 1);
567 }
568
569 int main()
570 {
571 init_buffers();
572
573 test_load_tmp();
574 test_load_tmp2();
575 test_load_cur();
576 test_load_aligned();
577 test_load_unaligned();
578 test_store_aligned();
579 test_store_unaligned();
580 test_masked_store(false);
581 test_masked_store(true);
582 test_new_value_store();
583 test_max_temps();
584
585 test_vadd_w();
586 test_vadd_h();
587 test_vadd_b();
588 test_vsub_w();
589 test_vsub_h();
590 test_vsub_b();
591 test_vxor();
592 test_vand();
593 test_vor();
594 test_vnot();
595
596 test_pred_or(false);
597 test_pred_or_n(true);
598 test_pred_and(false);
599 test_pred_and_n(true);
600 test_pred_xor(false);
601
602 test_vadduwsat();
603 test_vsubuwsat_dv();
604 test_vsubwsat();
605
606 test_vabsdiffub();
607 test_vabsdiffuh();
608 test_vabsdiffh();
609 test_vabsdiffw();
610
611 test_load_tmp_predicated();
612 test_load_cur_predicated();
613
614 test_vcombine();
615
616 test_store_new();
617
618 puts(err ? "FAIL" : "PASS");
619 return err ? 1 : 0;
620 }