@samitouri / QOSamiQemu / commits / 449e485814

tests/hexagon: add tests for v68 HVX IEEE float arithmetics

Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com> Reviewed-by: Brian Cain <brian.cain@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/791f122bfd744a77177608b524d852fe826cd595.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Matheus Tavares Bernardino committed Apr 16, 2026 at 04:39 UTC 449e485814ddadb1ae6f50ca9c9e01a248985eae
5 files changed +265
tests/tcg/hexagon/Makefile.target
+8
@@ -50,6 +50,8 @@ HEX_TESTS += vector_add_int
50 HEX_TESTS += scatter_gather
51 HEX_TESTS += hvx_misc
52 HEX_TESTS += hvx_histogram
53 +HEX_TESTS += fp_hvx
54 +HEX_TESTS += fp_hvx_disabled
55 HEX_TESTS += invalid-slots
56 HEX_TESTS += valid-slots
57 HEX_TESTS += invalid-encoding
@@ -136,6 +138,12 @@ v68_hvx: CFLAGS += -mhvx -Wno-unused-function
138 v69_hvx: v69_hvx.c hvx_misc.h
139 v69_hvx: CFLAGS += -mhvx -Wno-unused-function
140 v73_scalar: CFLAGS += -Wno-unused-function
141 +fp_hvx: fp_hvx.c hvx_misc.h hex_test.h
142 +fp_hvx: CFLAGS += -mhvx -mhvx-ieee-fp
143 +fp_hvx_disabled: fp_hvx_disabled.c hvx_misc.h hex_test.h
144 +fp_hvx_disabled: CFLAGS += -mhvx -mhvx-ieee-fp
145 +
146 +run-fp_hvx_disabled: QEMU_OPTS += -cpu v73,ieee-fp=false
147
148 hvx_histogram: hvx_histogram.c hvx_histogram_row.S
149 $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS)
tests/tcg/hexagon/fp_hvx.c new
+155
@@ -0,0 +1,155 @@
1 +/*
2 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3 + *
4 + * SPDX-License-Identifier: GPL-2.0-or-later
5 + */
6 +
7 +#include <stdio.h>
8 +#include <stdint.h>
9 +#include <stdbool.h>
10 +#include <string.h>
11 +#include <hexagon_types.h>
12 +#include <hvx_hexagon_protos.h>
13 +
14 +int err;
15 +#include "hvx_misc.h"
16 +
17 +#if __HEXAGON_ARCH__ > 75
18 +#error "After v75, compiler will replace some FP HVX instructions."
19 +#endif
20 +
21 +/******************************************************************************
22 + * NAN handling
23 + *****************************************************************************/
24 +
25 +#define isnan(X) \
26 + (sizeof(X) == bytes_hf ? ((raw_hf(X) & ~0x8000) > 0x7c00) : \
27 + ((raw_sf(X) & ~(1 << 31)) > 0x7f800000UL))
28 +
29 +#define CHECK_NAN(A, DEF_NAN) (isnan(A) ? DEF_NAN : (A))
30 +#define NAN_SF float_sf(0x7FFFFFFF)
31 +#define NAN_HF float_hf(0x7FFF)
32 +
33 +/******************************************************************************
34 + * Binary operations
35 + *****************************************************************************/
36 +
37 +#define DEF_TEST_OP_2(vop, op, type_res, type_arg) \
38 + static void test_##vop##_##type_res##_##type_arg(void) \
39 + { \
40 + memset(expect, 0xff, sizeof(expect)); \
41 + memset(output, 0xff, sizeof(output)); \
42 + for (int i = 0; i < BUFSIZE; i++) { \
43 + HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; \
44 + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \
45 + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \
46 + *hvx_output = \
47 + Q6_V##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \
48 + hvx_buffer1); \
49 + for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \
50 + expect[i].type_res[j] = \
51 + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[j]), \
52 + float_##type_arg(buffer1[i].type_arg[j]))); \
53 + } \
54 + } \
55 + check_output_##type_res(__LINE__, BUFSIZE); \
56 + }
57 +
58 +#define SUM(X, Y, DEF_NAN) CHECK_NAN((X) + (Y), DEF_NAN)
59 +#define SUB(X, Y, DEF_NAN) CHECK_NAN((X) - (Y), DEF_NAN)
60 +#define MULT(X, Y, DEF_NAN) CHECK_NAN((X) * (Y), DEF_NAN)
61 +
62 +#define SUM_SF(X, Y) SUM(X, Y, NAN_SF)
63 +#define SUM_HF(X, Y) SUM(X, Y, NAN_HF)
64 +#define SUB_SF(X, Y) SUB(X, Y, NAN_SF)
65 +#define SUB_HF(X, Y) SUB(X, Y, NAN_HF)
66 +#define MULT_SF(X, Y) MULT(X, Y, NAN_SF)
67 +#define MULT_HF(X, Y) MULT(X, Y, NAN_HF)
68 +
69 +DEF_TEST_OP_2(vadd, SUM_SF, sf, sf);
70 +DEF_TEST_OP_2(vadd, SUM_HF, hf, hf);
71 +DEF_TEST_OP_2(vsub, SUB_SF, sf, sf);
72 +DEF_TEST_OP_2(vsub, SUB_HF, hf, hf);
73 +DEF_TEST_OP_2(vmpy, MULT_SF, sf, sf);
74 +DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf);
75 +
76 +/******************************************************************************
77 + * Other tests
78 + *****************************************************************************/
79 +
80 +static void test_vdmpy_sf_hf(bool acc)
81 +{
82 + memset(expect, 0xff, sizeof(expect));
83 +
84 + for (int i = 0; i < BUFSIZE; i++) {
85 + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i];
86 + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i];
87 + HVX_Vector *hvx_output = (HVX_Vector *)&output[i];
88 +
89 + uint32_t PREFIL_VAL = 0x111222;
90 + *hvx_output = Q6_V_vsplat_R(PREFIL_VAL);
91 +
92 + if (!acc) {
93 + *hvx_output = Q6_Vsf_vdmpy_VhfVhf(hvx_buffer0, hvx_buffer1);
94 + } else {
95 + *hvx_output = Q6_Vsf_vdmpyacc_VsfVhfVhf(*hvx_output, hvx_buffer0,
96 + hvx_buffer1);
97 + }
98 +
99 + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
100 + float a1 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j + 1]));
101 + float a2 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j]));
102 + float a3 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j + 1]));
103 + float a4 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j]));
104 + /*
105 + * Note, IEEE FP specifies +0.0 + -0.0 == +0.0. So we use -0.0 in
106 + * the default case to preserve the zero sign.
107 + */
108 + float prev = acc ? float_sf(PREFIL_VAL) : -0.0;
109 + expect[i].sf[j] = raw_sf(CHECK_NAN((a1 * a3) + (a2 * a4) + prev, NAN_SF));
110 + }
111 + }
112 + check_output_sf(__LINE__, BUFSIZE);
113 +}
114 +
115 +static void test_new(void)
116 +{
117 + asm volatile("r0 = #%2\n"
118 + "v0 = vsplat(r0)\n"
119 + "vmem(%1 + #0) = v0\n"
120 + "r1 = #%3\n"
121 + "v1 = vsplat(r1)\n"
122 + "v2 = vsplat(r1)\n"
123 + "{\n"
124 + " v0.sf = vadd(v1.sf, v2.sf)\n"
125 + " vmem(%0 + #0) = v0.new\n"
126 + "}\n"
127 + :
128 + : "r"(output), "r"(expect), "i"(SF_two), "i"(SF_one)
129 + : "r0", "r1", "v0", "v1", "v2", "memory");
130 + check_output_w(__LINE__, 1);
131 +}
132 +
133 +int main(void)
134 +{
135 + init_buffers_fp();
136 +
137 + /* add/sub */
138 + test_vadd_sf_sf();
139 + test_vadd_hf_hf();
140 + test_vsub_sf_sf();
141 + test_vsub_hf_hf();
142 +
143 + /* multiply */
144 + test_vmpy_sf_sf();
145 + test_vmpy_hf_hf();
146 +
147 + /* dot product */
148 + test_vdmpy_sf_hf(false);
149 + test_vdmpy_sf_hf(true);
150 +
151 + test_new();
152 +
153 + puts(err ? "FAIL" : "PASS");
154 + return err ? 1 : 0;
155 +}
tests/tcg/hexagon/fp_hvx_disabled.c new
+57
@@ -0,0 +1,57 @@
1 +/*
2 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3 + *
4 + * SPDX-License-Identifier: GPL-2.0-or-later
5 + */
6 +
7 +#include <stdio.h>
8 +#include <string.h>
9 +#include <hexagon_types.h>
10 +#include <hvx_hexagon_protos.h>
11 +
12 +int err;
13 +#include "hvx_misc.h"
14 +
15 +static void test_disabled(void)
16 +{
17 + memset(output, 0xAA, sizeof(output));
18 + memset(expect, 0, sizeof(expect));
19 + asm volatile("r0 = #0xff\n"
20 + "v0 = vsplat(r0)\n"
21 + "r1 = #0x1\n"
22 + "v1 = vsplat(r1)\n"
23 + "v2 = vsplat(r1)\n"
24 + "v0.sf = vadd(v1.sf, v2.sf)\n"
25 + "vmem(%0 + #0) = v0\n"
26 + :
27 + : "r"(output)
28 + : "r0", "r1", "v0", "v1", "v2", "memory");
29 + check_output_w(__LINE__, 1);
30 +}
31 +
32 +static void test_disabled_with_new(void)
33 +{
34 + memset(output, 0xAA, sizeof(output));
35 + memset(expect, 0, sizeof(expect));
36 + asm volatile("r0 = #0xff\n"
37 + "v0 = vsplat(r0)\n"
38 + "r1 = #0x1\n"
39 + "v1 = vsplat(r1)\n"
40 + "v2 = vsplat(r1)\n"
41 + "{\n"
42 + " v0.sf = vadd(v1.sf, v2.sf)\n"
43 + " vmem(%0 + #0) = v0.new\n"
44 + "}\n"
45 + :
46 + : "r"(output)
47 + : "r0", "r1", "v0", "v1", "v2", "memory");
48 + check_output_w(__LINE__, 1);
49 +}
50 +
51 +int main(void)
52 +{
53 + test_disabled();
54 + test_disabled_with_new();
55 + puts(err ? "FAIL" : "PASS");
56 + return err ? 1 : 0;
57 +}
tests/tcg/hexagon/hex_test.h
+4
@@ -19,6 +19,8 @@
19 #ifndef HEX_TEST_H
20 #define HEX_TEST_H
21
22 +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
23 +
24 static inline void __check32(int line, uint32_t val, uint32_t expect)
25 {
26 if (val != expect) {
@@ -110,6 +112,7 @@ static inline void __check64_ne(int line, uint64_t val, uint64_t expect)
112
113 /* Some useful floating point values */
114 const uint32_t SF_INF = 0x7f800000;
115 +const uint32_t SF_INF_neg = 0xff800000;
116 const uint32_t SF_QNaN = 0x7fc00000;
117 const uint32_t SF_QNaN_special = 0x7f800001;
118 const uint32_t SF_SNaN = 0x7fb00000;
@@ -128,6 +131,7 @@ const uint32_t SF_large_pos = 0x5afa572e;
131 const uint32_t SF_any = 0x3f800000;
132 const uint32_t SF_denorm = 0x00000001;
133 const uint32_t SF_random = 0x346001d6;
134 +const uint32_t SF_neg_two = 0xc0000000;
135
136 const uint64_t DF_QNaN = 0x7ff8000000000000ULL;
137 const uint64_t DF_SNaN = 0x7ff7000000000000ULL;
tests/tcg/hexagon/hvx_misc.h
+41
@@ -18,6 +18,8 @@
18 #ifndef HVX_MISC_H
19 #define HVX_MISC_H
20
21 +#include "hex_test.h"
22 +
23 static inline void check(int line, int i, int j,
24 uint64_t result, uint64_t expect)
25 {
@@ -34,8 +36,10 @@ typedef union {
36 uint64_t ud[MAX_VEC_SIZE_BYTES / 8];
37 int64_t d[MAX_VEC_SIZE_BYTES / 8];
38 uint32_t uw[MAX_VEC_SIZE_BYTES / 4];
39 + uint32_t sf[MAX_VEC_SIZE_BYTES / 4]; /* convenience alias */
40 int32_t w[MAX_VEC_SIZE_BYTES / 4];
41 uint16_t uh[MAX_VEC_SIZE_BYTES / 2];
42 + uint16_t hf[MAX_VEC_SIZE_BYTES / 2]; /* convenience alias */
43 int16_t h[MAX_VEC_SIZE_BYTES / 2];
44 uint8_t ub[MAX_VEC_SIZE_BYTES / 1];
45 int8_t b[MAX_VEC_SIZE_BYTES / 1];
@@ -63,7 +67,9 @@ static inline void check_output_##FIELD(int line, size_t num_vectors) \
67
68 CHECK_OUTPUT_FUNC(d, 8)
69 CHECK_OUTPUT_FUNC(w, 4)
70 +CHECK_OUTPUT_FUNC(sf, 4)
71 CHECK_OUTPUT_FUNC(h, 2)
72 +CHECK_OUTPUT_FUNC(hf, 2)
73 CHECK_OUTPUT_FUNC(b, 1)
74
75 static inline void init_buffers(void)
@@ -81,6 +87,33 @@ static inline void init_buffers(void)
87 }
88 }
89
90 +static const uint32_t FP_VALUES[] = {
91 + SF_INF, SF_INF_neg, SF_QNaN, SF_QNaN_special, SF_SNaN, SF_QNaN_neg,
92 + SF_SNaN_neg, SF_HEX_NaN, SF_zero, SF_zero_neg, SF_one, SF_one_recip,
93 + SF_one_invsqrta, SF_two, SF_four, SF_small_neg, SF_large_pos, SF_any,
94 + SF_denorm, SF_random, SF_neg_two,
95 +};
96 +#define FP_VALUES_MAX ARRAY_SIZE(FP_VALUES)
97 +
98 +static inline void init_buffers_fp(void)
99 +{
100 + _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 4) >
101 + FP_VALUES_MAX * FP_VALUES_MAX,
102 + "test arrays can't fit all FP_VALUES combinations");
103 + int counter1 = 0, counter2 = 0;
104 + for (int i = 0; i < BUFSIZE; i++) {
105 + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
106 + buffer0[i].sf[j] = FP_VALUES[counter1];
107 + buffer1[i].sf[j] = FP_VALUES[counter2];
108 + counter2++;
109 + if (counter2 == FP_VALUES_MAX) {
110 + counter2 = 0;
111 + counter1 = (counter1 + 1) % FP_VALUES_MAX;
112 + }
113 + }
114 + }
115 +}
116 +
117 #define VEC_OP1(ASM, EL, IN, OUT) \
118 asm("v2 = vmem(%0 + #0)\n\t" \
119 "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \
@@ -175,4 +208,12 @@ static inline void test_##NAME(bool invert) \
208 check_output_b(__LINE__, BUFSIZE); \
209 }
210
211 +#define float_sf(x) ({ typeof(x) _x = (x); *((float *)&(_x)); })
212 +#define float_hf(x) ({ typeof(x) _x = (x); *((_Float16 *) &(_x)); })
213 +#define raw_sf(x) ({ typeof(x) _x = (x); *((uint32_t *)&(_x)); })
214 +#define raw_hf(x) ({ typeof(x) _x = (x); *((uint16_t *)&(_x)); })
215 +#define float_hf_to_sf(x) ((float)x)
216 +#define bytes_hf 2
217 +#define bytes_sf 4
218 +
219 #endif