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