* [PATCH 01/17] tests/tcg/hexagon: fix undefined signed left shift in circ.c
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-08 15:54 ` Sid Manning
2026-09-05 18:09 ` [PATCH 02/17] tests/tcg/hexagon: fix undefined signed left shift in brev.c Brian Cain
` (15 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Cast operands to uint32_t before left-shifting in build_mreg() to
avoid undefined behavior when the shift result overflows int32_t.
Found with UBSan.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/circ.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/tcg/hexagon/circ.c b/tests/tcg/hexagon/circ.c
index ab949ebef1c..2a15c324242 100644
--- a/tests/tcg/hexagon/circ.c
+++ b/tests/tcg/hexagon/circ.c
@@ -97,9 +97,9 @@ INIT(dbuf, NDOBLS)
*/
static int32_t build_mreg(int32_t inc, int32_t K, int32_t len)
{
- return ((inc & 0x780) << 21) |
- ((K & 0xf) << 24) |
- ((inc & 0x7f) << 17) |
+ return ((uint32_t)(inc & 0x780) << 21) |
+ ((uint32_t)(K & 0xf) << 24) |
+ ((uint32_t)(inc & 0x7f) << 17) |
(len & 0x1ffff);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 01/17] tests/tcg/hexagon: fix undefined signed left shift in circ.c
2026-09-05 18:09 ` [PATCH 01/17] tests/tcg/hexagon: fix undefined signed left shift in circ.c Brian Cain
@ 2026-09-08 15:54 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-08 15:54 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 1031 bytes --]
LGTM
On Sat, Sep 5, 2026 at 1:09 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Cast operands to uint32_t before left-shifting in build_mreg() to
> avoid undefined behavior when the shift result overflows int32_t.
> Found with UBSan.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/circ.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/tests/tcg/hexagon/circ.c b/tests/tcg/hexagon/circ.c
> index ab949ebef1c..2a15c324242 100644
> --- a/tests/tcg/hexagon/circ.c
> +++ b/tests/tcg/hexagon/circ.c
> @@ -97,9 +97,9 @@ INIT(dbuf, NDOBLS)
> */
> static int32_t build_mreg(int32_t inc, int32_t K, int32_t len)
> {
> - return ((inc & 0x780) << 21) |
> - ((K & 0xf) << 24) |
> - ((inc & 0x7f) << 17) |
> + return ((uint32_t)(inc & 0x780) << 21) |
> + ((uint32_t)(K & 0xf) << 24) |
> + ((uint32_t)(inc & 0x7f) << 17) |
> (len & 0x1ffff);
> }
>
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 1570 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 02/17] tests/tcg/hexagon: fix undefined signed left shift in brev.c
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
2026-09-05 18:09 ` [PATCH 01/17] tests/tcg/hexagon: fix undefined signed left shift in circ.c Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-09 14:43 ` Sid Manning
2026-09-05 18:09 ` [PATCH 03/17] tests/tcg/hexagon: fix undefined signed integer overflow in hvx_misc Brian Cain
` (14 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Cast to uint32_t before left-shifting in sext8() to avoid undefined
behavior when shifting a negative value. The subsequent arithmetic
right shift on the int32_t cast correctly sign-extends the result.
Found with UBSan.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/brev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/tcg/hexagon/brev.c b/tests/tcg/hexagon/brev.c
index 6c7b1340849..1473e309ee0 100644
--- a/tests/tcg/hexagon/brev.c
+++ b/tests/tcg/hexagon/brev.c
@@ -106,7 +106,7 @@ uint32_t bitreverse(uint32_t x)
int32_t sext8(int32_t x)
{
- return (x << 24) >> 24;
+ return (int32_t)((uint32_t)x << 24) >> 24;
}
#define TEST_BREV_LOAD(SZ, TYPE, BUF, SHIFT, EXP) \
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 02/17] tests/tcg/hexagon: fix undefined signed left shift in brev.c
2026-09-05 18:09 ` [PATCH 02/17] tests/tcg/hexagon: fix undefined signed left shift in brev.c Brian Cain
@ 2026-09-09 14:43 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-09 14:43 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 979 bytes --]
LGTM
Reviewed-by:Sid Manning sid.manning@oss.qualcomm.com
On Sat, Sep 5, 2026 at 1:09 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Cast to uint32_t before left-shifting in sext8() to avoid undefined
> behavior when shifting a negative value. The subsequent arithmetic
> right shift on the int32_t cast correctly sign-extends the result.
> Found with UBSan.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/brev.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tests/tcg/hexagon/brev.c b/tests/tcg/hexagon/brev.c
> index 6c7b1340849..1473e309ee0 100644
> --- a/tests/tcg/hexagon/brev.c
> +++ b/tests/tcg/hexagon/brev.c
> @@ -106,7 +106,7 @@ uint32_t bitreverse(uint32_t x)
>
> int32_t sext8(int32_t x)
> {
> - return (x << 24) >> 24;
> + return (int32_t)((uint32_t)x << 24) >> 24;
> }
>
> #define TEST_BREV_LOAD(SZ, TYPE, BUF, SHIFT, EXP) \
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 1509 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 03/17] tests/tcg/hexagon: fix undefined signed integer overflow in hvx_misc
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
2026-09-05 18:09 ` [PATCH 01/17] tests/tcg/hexagon: fix undefined signed left shift in circ.c Brian Cain
2026-09-05 18:09 ` [PATCH 02/17] tests/tcg/hexagon: fix undefined signed left shift in brev.c Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-09 14:50 ` Sid Manning
2026-09-05 18:09 ` [PATCH 04/17] tests/tcg/hexagon: fix undefined integer overflow in v69_hvx.c Brian Cain
` (13 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Use unsigned (.uw) instead of signed (.w) array members for
vector addition and subtraction reference calculations to avoid
signed integer overflow. Wrapping arithmetic is well-defined for
unsigned types. Add CHECK_OUTPUT_FUNC(uw, 4) to support unsigned
word comparisons in the TEST_VEC_OP2 macro.
Found with UBSan.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/hvx_misc.h | 1 +
tests/tcg/hexagon/hvx_misc.c | 18 +++++++++---------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h
index c21ea975c16..e32715ed11d 100644
--- a/tests/tcg/hexagon/hvx_misc.h
+++ b/tests/tcg/hexagon/hvx_misc.h
@@ -69,6 +69,7 @@ static inline void check_output_##FIELD(int line, size_t num_vectors) \
CHECK_OUTPUT_FUNC(d, 8)
CHECK_OUTPUT_FUNC(w, 4)
CHECK_OUTPUT_FUNC(sf, 4)
+CHECK_OUTPUT_FUNC(uw, 4)
CHECK_OUTPUT_FUNC(h, 2)
CHECK_OUTPUT_FUNC(uh, 2)
CHECK_OUTPUT_FUNC(hf, 2)
diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c
index 32a3661a86f..f20afc8e670 100644
--- a/tests/tcg/hexagon/hvx_misc.c
+++ b/tests/tcg/hexagon/hvx_misc.c
@@ -55,7 +55,7 @@ static void test_load_tmp(void)
pout += sizeof(MMVector);
for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
- expect[i].w[j] = buffer0[i].w[j] + buffer1[i].w[j] + 1;
+ expect[i].uw[j] = buffer0[i].uw[j] + buffer1[i].uw[j] + 1;
}
}
@@ -285,8 +285,8 @@ static void test_max_temps()
/* The first two vectors come from the vadd-pair instruction */
for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
- expect[0].w[i] = buffer0[0].w[i] + buffer0[2].w[i];
- expect[1].w[i] = buffer0[1].w[i] + buffer0[3].w[i];
+ expect[0].uw[i] = buffer0[0].uw[i] + buffer0[2].uw[i];
+ expect[1].uw[i] = buffer0[1].uw[i] + buffer0[3].uw[i];
}
/* The third vector comes from the vshuffe instruction */
for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
@@ -295,7 +295,7 @@ static void test_max_temps()
}
/* The fourth vector comes from the vadd-single instruction */
for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
- expect[3].w[i] = buffer0[1].w[i] + buffer0[5].w[i];
+ expect[3].uw[i] = buffer0[1].uw[i] + buffer0[5].uw[i];
}
/*
* The fifth vector comes from the load to v4
@@ -306,10 +306,10 @@ static void test_max_temps()
check_output_b(__LINE__, 5);
}
-TEST_VEC_OP2(vadd_w, vadd, .w, w, 4, +)
+TEST_VEC_OP2(vadd_w, vadd, .w, uw, 4, +)
TEST_VEC_OP2(vadd_h, vadd, .h, h, 2, +)
TEST_VEC_OP2(vadd_b, vadd, .b, b, 1, +)
-TEST_VEC_OP2(vsub_w, vsub, .w, w, 4, -)
+TEST_VEC_OP2(vsub_w, vsub, .w, uw, 4, -)
TEST_VEC_OP2(vsub_h, vsub, .h, h, 2, -)
TEST_VEC_OP2(vsub_b, vsub, .b, b, 1, -)
TEST_VEC_OP2(vxor, vxor, , d, 8, ^)
@@ -489,9 +489,9 @@ static void test_load_tmp_predicated(void)
pout += sizeof(MMVector);
for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
- expect[i].w[j] =
- pred ? buffer0[i].w[j] + buffer1[i].w[j] + 1
- : buffer0[i].w[j] + 2;
+ expect[i].uw[j] =
+ pred ? buffer0[i].uw[j] + buffer1[i].uw[j] + 1
+ : buffer0[i].uw[j] + 2;
}
pred = !pred;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 03/17] tests/tcg/hexagon: fix undefined signed integer overflow in hvx_misc
2026-09-05 18:09 ` [PATCH 03/17] tests/tcg/hexagon: fix undefined signed integer overflow in hvx_misc Brian Cain
@ 2026-09-09 14:50 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-09 14:50 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 3805 bytes --]
LGTM
Reviewed-by:Sid Manning sid.manning@oss.qualcomm.com
On Sat, Sep 5, 2026 at 1:09 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Use unsigned (.uw) instead of signed (.w) array members for
> vector addition and subtraction reference calculations to avoid
> signed integer overflow. Wrapping arithmetic is well-defined for
> unsigned types. Add CHECK_OUTPUT_FUNC(uw, 4) to support unsigned
> word comparisons in the TEST_VEC_OP2 macro.
> Found with UBSan.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/hvx_misc.h | 1 +
> tests/tcg/hexagon/hvx_misc.c | 18 +++++++++---------
> 2 files changed, 10 insertions(+), 9 deletions(-)
>
> diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h
> index c21ea975c16..e32715ed11d 100644
> --- a/tests/tcg/hexagon/hvx_misc.h
> +++ b/tests/tcg/hexagon/hvx_misc.h
> @@ -69,6 +69,7 @@ static inline void check_output_##FIELD(int line, size_t
> num_vectors) \
> CHECK_OUTPUT_FUNC(d, 8)
> CHECK_OUTPUT_FUNC(w, 4)
> CHECK_OUTPUT_FUNC(sf, 4)
> +CHECK_OUTPUT_FUNC(uw, 4)
> CHECK_OUTPUT_FUNC(h, 2)
> CHECK_OUTPUT_FUNC(uh, 2)
> CHECK_OUTPUT_FUNC(hf, 2)
> diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c
> index 32a3661a86f..f20afc8e670 100644
> --- a/tests/tcg/hexagon/hvx_misc.c
> +++ b/tests/tcg/hexagon/hvx_misc.c
> @@ -55,7 +55,7 @@ static void test_load_tmp(void)
> pout += sizeof(MMVector);
>
> for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
> - expect[i].w[j] = buffer0[i].w[j] + buffer1[i].w[j] + 1;
> + expect[i].uw[j] = buffer0[i].uw[j] + buffer1[i].uw[j] + 1;
> }
> }
>
> @@ -285,8 +285,8 @@ static void test_max_temps()
>
> /* The first two vectors come from the vadd-pair instruction */
> for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
> - expect[0].w[i] = buffer0[0].w[i] + buffer0[2].w[i];
> - expect[1].w[i] = buffer0[1].w[i] + buffer0[3].w[i];
> + expect[0].uw[i] = buffer0[0].uw[i] + buffer0[2].uw[i];
> + expect[1].uw[i] = buffer0[1].uw[i] + buffer0[3].uw[i];
> }
> /* The third vector comes from the vshuffe instruction */
> for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> @@ -295,7 +295,7 @@ static void test_max_temps()
> }
> /* The fourth vector comes from the vadd-single instruction */
> for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
> - expect[3].w[i] = buffer0[1].w[i] + buffer0[5].w[i];
> + expect[3].uw[i] = buffer0[1].uw[i] + buffer0[5].uw[i];
> }
> /*
> * The fifth vector comes from the load to v4
> @@ -306,10 +306,10 @@ static void test_max_temps()
> check_output_b(__LINE__, 5);
> }
>
> -TEST_VEC_OP2(vadd_w, vadd, .w, w, 4, +)
> +TEST_VEC_OP2(vadd_w, vadd, .w, uw, 4, +)
> TEST_VEC_OP2(vadd_h, vadd, .h, h, 2, +)
> TEST_VEC_OP2(vadd_b, vadd, .b, b, 1, +)
> -TEST_VEC_OP2(vsub_w, vsub, .w, w, 4, -)
> +TEST_VEC_OP2(vsub_w, vsub, .w, uw, 4, -)
> TEST_VEC_OP2(vsub_h, vsub, .h, h, 2, -)
> TEST_VEC_OP2(vsub_b, vsub, .b, b, 1, -)
> TEST_VEC_OP2(vxor, vxor, , d, 8, ^)
> @@ -489,9 +489,9 @@ static void test_load_tmp_predicated(void)
> pout += sizeof(MMVector);
>
> for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
> - expect[i].w[j] =
> - pred ? buffer0[i].w[j] + buffer1[i].w[j] + 1
> - : buffer0[i].w[j] + 2;
> + expect[i].uw[j] =
> + pred ? buffer0[i].uw[j] + buffer1[i].uw[j] + 1
> + : buffer0[i].uw[j] + 2;
> }
> pred = !pred;
> }
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 4666 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 04/17] tests/tcg/hexagon: fix undefined integer overflow in v69_hvx.c
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (2 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 03/17] tests/tcg/hexagon: fix undefined signed integer overflow in hvx_misc Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-09 14:51 ` Sid Manning
2026-09-05 18:09 ` [PATCH 05/17] tests/tcg/multiarch: suppress UBSan for float-to-int conversions Brian Cain
` (12 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Cast to uint32_t before multiplying uint16_t values to prevent
signed integer overflow. Without the cast, the uint16_t operands
are promoted to int, and the product can overflow the int range.
Found with UBSan.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/v69_hvx.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/tcg/hexagon/v69_hvx.c b/tests/tcg/hexagon/v69_hvx.c
index a0d567d142f..eff936064a0 100644
--- a/tests/tcg/hexagon/v69_hvx.c
+++ b/tests/tcg/hexagon/v69_hvx.c
@@ -292,7 +292,8 @@ static void test_vmpyuhvs(void)
pout += sizeof(MMVector);
for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) {
- expect[i].uh[j] = (buffer0[i].uh[j] * buffer1[i].uh[j]) >> 16;
+ expect[i].uh[j] =
+ ((uint32_t)buffer0[i].uh[j] * buffer1[i].uh[j]) >> 16;
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 04/17] tests/tcg/hexagon: fix undefined integer overflow in v69_hvx.c
2026-09-05 18:09 ` [PATCH 04/17] tests/tcg/hexagon: fix undefined integer overflow in v69_hvx.c Brian Cain
@ 2026-09-09 14:51 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-09 14:51 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 1118 bytes --]
LGTM
Reviewed-by:Sid Manning sid.manning@oss.qualcomm.com
On Sat, Sep 5, 2026 at 1:10 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Cast to uint32_t before multiplying uint16_t values to prevent
> signed integer overflow. Without the cast, the uint16_t operands
> are promoted to int, and the product can overflow the int range.
> Found with UBSan.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/v69_hvx.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/tests/tcg/hexagon/v69_hvx.c b/tests/tcg/hexagon/v69_hvx.c
> index a0d567d142f..eff936064a0 100644
> --- a/tests/tcg/hexagon/v69_hvx.c
> +++ b/tests/tcg/hexagon/v69_hvx.c
> @@ -292,7 +292,8 @@ static void test_vmpyuhvs(void)
> pout += sizeof(MMVector);
>
> for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) {
> - expect[i].uh[j] = (buffer0[i].uh[j] * buffer1[i].uh[j]) >> 16;
> + expect[i].uh[j] =
> + ((uint32_t)buffer0[i].uh[j] * buffer1[i].uh[j]) >> 16;
> }
> }
>
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 1681 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 05/17] tests/tcg/multiarch: suppress UBSan for float-to-int conversions
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (3 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 04/17] tests/tcg/hexagon: fix undefined integer overflow in v69_hvx.c Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 19:43 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 06/17] tests/tcg/hexagon: add FP classification, conversion, and fixup tests Brian Cain
` (11 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Add __attribute__((no_sanitize("undefined"))) to the float/double
to integer conversion functions.
C17 6.3.1.4p1 only defines float-to-integer conversion for finite
values whose integral part fits in the destination type; it discards
the fractional part and explicitly makes it UB if the integral part
doesn't fit.
These tests intentionally exercise both of those UB cases, so we'll suppress
it to get a clean ubsan run on the TCG test suite.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/multiarch/float_convd.c | 1 +
tests/tcg/multiarch/float_convs.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/tests/tcg/multiarch/float_convd.c b/tests/tcg/multiarch/float_convd.c
index 58d7f8b4c58..cb3884df974 100644
--- a/tests/tcg/multiarch/float_convd.c
+++ b/tests/tcg/multiarch/float_convd.c
@@ -62,6 +62,7 @@ static void convert_double_to_single(double input)
#define str(a) #a
#define CONVERT_DOUBLE_TO_INT(TYPE, FMT) \
+ __attribute__((no_sanitize("undefined"))) \
static void convert_double_to_ ## TYPE(double input) \
{ \
TYPE ## _t output; \
diff --git a/tests/tcg/multiarch/float_convs.c b/tests/tcg/multiarch/float_convs.c
index cb1fdd439e3..584ce89aa17 100644
--- a/tests/tcg/multiarch/float_convs.c
+++ b/tests/tcg/multiarch/float_convs.c
@@ -62,6 +62,7 @@ static void convert_single_to_double(float input)
#define str(a) #a
#define CONVERT_SINGLE_TO_INT(TYPE, FMT) \
+ __attribute__((no_sanitize("undefined"))) \
static void convert_single_to_ ## TYPE(float input) \
{ \
TYPE ## _t output; \
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 05/17] tests/tcg/multiarch: suppress UBSan for float-to-int conversions
2026-09-05 18:09 ` [PATCH 05/17] tests/tcg/multiarch: suppress UBSan for float-to-int conversions Brian Cain
@ 2026-09-14 19:43 ` Pierrick Bouvier
0 siblings, 0 replies; 35+ messages in thread
From: Pierrick Bouvier @ 2026-09-14 19:43 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Alex Bennée, sid.manning
On 9/5/2026 11:09 AM, Brian Cain wrote:
> Add __attribute__((no_sanitize("undefined"))) to the float/double
> to integer conversion functions.
>
> C17 6.3.1.4p1 only defines float-to-integer conversion for finite
> values whose integral part fits in the destination type; it discards
> the fractional part and explicitly makes it UB if the integral part
> doesn't fit.
>
> These tests intentionally exercise both of those UB cases, so we'll suppress
> it to get a clean ubsan run on the TCG test suite.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/multiarch/float_convd.c | 1 +
> tests/tcg/multiarch/float_convs.c | 1 +
> 2 files changed, 2 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 06/17] tests/tcg/hexagon: add FP classification, conversion, and fixup tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (4 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 05/17] tests/tcg/multiarch: suppress UBSan for float-to-int conversions Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 19:43 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 07/17] tests/tcg/hexagon: add bit interleave and convergent rounding tests Brian Cain
` (10 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Add sfclass/dfclass FP classification tests, unsigned-to-double
conversions, and dfmpyfix denormal fixup tests.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/fpstuff.c | 175 ++++++++++++++++++++++++++++++++++++
1 file changed, 175 insertions(+)
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index 6aadaccabdf..7561f31cdc3 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -711,6 +711,176 @@ static void check_dfmpyxx(void)
check64(res64, 0x7fefffffffffffffULL);
}
+/*
+ * sfclass mask bits:
+ * bit 0: positive/negative zero
+ * bit 1: positive/negative normal
+ * bit 2: positive/negative denormal
+ * bit 3: positive/negative infinity
+ * bit 4: positive/negative NaN
+ */
+#define TEST_SFCLASS(VAL, MASK, EXPECT) \
+ do { \
+ uint32_t res; \
+ asm("%[res] = #0\n\t" \
+ "p0 = sfclass(%[val], #" #MASK ")\n\t" \
+ "if (p0) %[res] = #1\n\t" \
+ : [res] "=&r"(res) \
+ : [val] "r"(VAL) \
+ : "p0"); \
+ check32(res, EXPECT); \
+ } while (0)
+
+static void check_sfclass(void)
+{
+ /* Zero: mask bit 0 */
+ TEST_SFCLASS(SF_zero, 0x01, 1);
+ TEST_SFCLASS(SF_zero_neg, 0x01, 1);
+ TEST_SFCLASS(SF_zero, 0x02, 0);
+
+ /* Normal: mask bit 1 */
+ TEST_SFCLASS(SF_one, 0x02, 1);
+ TEST_SFCLASS(SF_one, 0x01, 0);
+
+ /* Denormal: mask bit 2 */
+ TEST_SFCLASS(SF_denorm, 0x04, 1);
+ TEST_SFCLASS(SF_denorm, 0x01, 0);
+
+ /* Infinity: mask bit 3 */
+ TEST_SFCLASS(SF_INF, 0x08, 1);
+ TEST_SFCLASS(SF_INF, 0x01, 0);
+
+ /* NaN: mask bit 4 */
+ TEST_SFCLASS(SF_QNaN, 0x10, 1);
+ TEST_SFCLASS(SF_SNaN, 0x10, 1);
+ TEST_SFCLASS(SF_QNaN, 0x01, 0);
+
+ /* Combined: any class (all bits set) */
+ TEST_SFCLASS(SF_one, 0x1f, 1);
+}
+
+/*
+ * dfclass mask bits are the same as sfclass.
+ */
+static const uint64_t DF_INF = 0x7ff0000000000000ULL;
+static const uint64_t DF_denorm = 0x0000000000000001ULL;
+static const uint64_t DF_neg_one = 0xbff0000000000000ULL;
+
+#define TEST_DFCLASS(VAL, MASK, EXPECT) \
+ do { \
+ uint32_t res; \
+ asm("%[res] = #0\n\t" \
+ "p0 = dfclass(%[val], #" #MASK ")\n\t" \
+ "if (p0) %[res] = #1\n\t" \
+ : [res] "=&r"(res) \
+ : [val] "r"(VAL) \
+ : "p0"); \
+ check32(res, EXPECT); \
+ } while (0)
+
+static void check_dfclass(void)
+{
+ /* Zero: mask bit 0 */
+ TEST_DFCLASS(DF_zero, 0x01, 1);
+ TEST_DFCLASS(DF_zero_neg, 0x01, 1);
+ TEST_DFCLASS(DF_zero, 0x02, 0);
+
+ /* Normal: mask bit 1 */
+ TEST_DFCLASS(DF_one, 0x02, 1);
+ TEST_DFCLASS(DF_neg_one, 0x02, 1);
+ TEST_DFCLASS(DF_one, 0x01, 0);
+
+ /* Denormal: mask bit 2 */
+ TEST_DFCLASS(DF_denorm, 0x04, 1);
+ TEST_DFCLASS(DF_denorm, 0x01, 0);
+
+ /* Infinity: mask bit 3 */
+ TEST_DFCLASS(DF_INF, 0x08, 1);
+ TEST_DFCLASS(DF_INF, 0x01, 0);
+
+ /* NaN: mask bit 4 */
+ TEST_DFCLASS(DF_QNaN, 0x10, 1);
+ TEST_DFCLASS(DF_SNaN, 0x10, 1);
+ TEST_DFCLASS(DF_QNaN, 0x01, 0);
+}
+
+/* Rdd = convert_uw2df(Rs) */
+static uint64_t conv_uw2df(uint32_t val)
+{
+ uint64_t result;
+
+ asm("%[res] = convert_uw2df(%[val])\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(val));
+ return result;
+}
+
+static void check_conv_uw2df(void)
+{
+ check64(conv_uw2df(0), DF_zero);
+ check64(conv_uw2df(1), DF_one);
+ /* 100 -> 0x4059000000000000 */
+ check64(conv_uw2df(100), 0x4059000000000000ULL);
+ /* 0xFFFFFFFF -> 4294967295.0 = 0x41EFFFFFFFE00000 */
+ check64(conv_uw2df(0xFFFFFFFF), 0x41EFFFFFFFE00000ULL);
+}
+
+/* Rdd = convert_ud2df(Rss) */
+static uint64_t conv_ud2df(uint64_t val)
+{
+ uint64_t result;
+
+ asm("%[res] = convert_ud2df(%[val])\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(val));
+ return result;
+}
+
+static void check_conv_ud2df(void)
+{
+ check64(conv_ud2df(0ULL), DF_zero);
+ check64(conv_ud2df(1ULL), DF_one);
+ /* 1000000 -> 0x412E848000000000 */
+ check64(conv_ud2df(1000000ULL), 0x412E848000000000ULL);
+}
+
+/* Rdd = dfmpyfix(Rss,Rtt) -- DF multiply denormal fixup */
+static uint64_t do_dfmpyfix(uint64_t a, uint64_t b)
+{
+ uint64_t result;
+
+ asm("%[res] = dfmpyfix(%[a], %[b])\n\t"
+ : [res] "=r"(result)
+ : [a] "r"(a), [b] "r"(b));
+ return result;
+}
+
+static void check_dfmpyfix(void)
+{
+ /*
+ * With two normal values (neither denormal, exps < 512),
+ * the result should be the first operand unchanged.
+ */
+ check64(do_dfmpyfix(DF_one, DF_one), DF_one);
+
+ /*
+ * Case: b is denormal AND a is normal with exp >= 512.
+ * a gets multiplied by 2^-52 (0x3cb0000000000000).
+ * a = 1.0, result = 1.0 * 2^-52 = 0x3cb0000000000000
+ */
+ check64(do_dfmpyfix(DF_one, DF_denorm), 0x3CB0000000000000ULL);
+
+ /*
+ * Case: a is denormal AND b is normal with exp >= 512.
+ * a gets multiplied by 2^52 (0x4330000000000000).
+ * a = smallest denorm = 2^-1074, b = 2^512 (biased exp 0x5ff).
+ * Result = 2^-1074 * 2^52 = 2^-1022 = smallest normal
+ * = 0x0010000000000000
+ */
+ check64(do_dfmpyfix(DF_denorm, 0x5FF0000000000000ULL),
+ 0x0010000000000000ULL);
+}
+
int main()
{
check_compare_exception();
@@ -725,6 +895,11 @@ int main()
check_float2int_convs();
check_float_consts();
check_dfmpyxx();
+ check_sfclass();
+ check_dfclass();
+ check_conv_uw2df();
+ check_conv_ud2df();
+ check_dfmpyfix();
puts(err ? "FAIL" : "PASS");
return err ? 1 : 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 06/17] tests/tcg/hexagon: add FP classification, conversion, and fixup tests
2026-09-05 18:09 ` [PATCH 06/17] tests/tcg/hexagon: add FP classification, conversion, and fixup tests Brian Cain
@ 2026-09-14 19:43 ` Pierrick Bouvier
0 siblings, 0 replies; 35+ messages in thread
From: Pierrick Bouvier @ 2026-09-14 19:43 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Alex Bennée, sid.manning
On 9/5/2026 11:09 AM, Brian Cain wrote:
> Add sfclass/dfclass FP classification tests, unsigned-to-double
> conversions, and dfmpyfix denormal fixup tests.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/fpstuff.c | 175 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 175 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 07/17] tests/tcg/hexagon: add bit interleave and convergent rounding tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (5 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 06/17] tests/tcg/hexagon: add FP classification, conversion, and fixup tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-10 17:54 ` Sid Manning
2026-09-05 18:09 ` [PATCH 08/17] tests/tcg/hexagon: add cond call, tstbit-jump, and endloop01 tests Brian Cain
` (9 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test the interleave/deinterleave 64-bit bit-manipulation instructions
and the cround instruction.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/test_bitops.c | 98 +++++++++++++++++++++++++++++++++
tests/tcg/hexagon/meson.build | 1 +
2 files changed, 99 insertions(+)
create mode 100644 tests/tcg/hexagon/test_bitops.c
diff --git a/tests/tcg/hexagon/test_bitops.c b/tests/tcg/hexagon/test_bitops.c
new file mode 100644
index 00000000000..3e9274ddddd
--- /dev/null
+++ b/tests/tcg/hexagon/test_bitops.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Test bit interleave/deinterleave and convergent rounding.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <hexagon_protos.h>
+
+int err;
+
+#include "hex_test.h"
+
+static void check_interleave(void)
+{
+ uint64_t input;
+ uint64_t inter;
+ uint64_t deinter;
+
+ /*
+ * interleave takes the 32 even bits and 32 odd bits of Rss and
+ * interleaves them: even[0] goes to bit 0, odd[0] to bit 1,
+ * even[1] to bit 2, odd[1] to bit 3, etc.
+ *
+ * Input: low 32 bits (even half), high 32 bits (odd half)
+ * If low = 0xFFFFFFFF, high = 0x00000000:
+ * result has every even bit set, every odd bit clear
+ * = 0x5555555555555555
+ */
+ input = 0x00000000FFFFFFFFULL;
+ inter = Q6_P_interleave_P(input);
+ check64(inter, 0x5555555555555555ULL);
+
+ /* All ones should stay all ones */
+ input = 0xFFFFFFFFFFFFFFFFULL;
+ inter = Q6_P_interleave_P(input);
+ check64(inter, 0xFFFFFFFFFFFFFFFFULL);
+
+ /* All zeros should stay all zeros */
+ input = 0x0000000000000000ULL;
+ inter = Q6_P_interleave_P(input);
+ check64(inter, 0x0000000000000000ULL);
+
+ /*
+ * deinterleave is the inverse of interleave.
+ * deinterleave(interleave(x)) should equal x.
+ */
+ input = 0xDEADBEEFCAFEBABEULL;
+ inter = Q6_P_interleave_P(input);
+ deinter = Q6_P_deinterleave_P(inter);
+ check64(deinter, input);
+
+ /* deinterleave of 0x5555...5555 -> low half all 1s, high half all 0s */
+ deinter = Q6_P_deinterleave_P(0x5555555555555555ULL);
+ check64(deinter, 0x00000000FFFFFFFFULL);
+}
+
+static void check_cround(void)
+{
+ /*
+ * cround(0x300, #8) -> 3 (exact shift, no rounding needed).
+ * 0x300 >> 8 = 3
+ */
+ check32(Q6_R_cround_RI(0x300, 8), 3);
+
+ /*
+ * cround(0x380, #8) -> 4 (0x380 >> 8 = 3.5, rounds to even = 4)
+ */
+ check32(Q6_R_cround_RI(0x380, 8), 4);
+
+ /*
+ * cround(0x280, #8) -> 2 (0x280 >> 8 = 2.5, rounds to even = 2)
+ */
+ check32(Q6_R_cround_RI(0x280, 8), 2);
+
+ /*
+ * cround(0x3C0, #8) -> 4 (0x3C0 >> 8 = 3.75, rounds up to 4)
+ */
+ check32(Q6_R_cround_RI(0x3C0, 8), 4);
+
+ /*
+ * Shift by 0 -> return the input unchanged.
+ */
+ check32(Q6_R_cround_RI(42, 0), 42);
+}
+
+int main(void)
+{
+ check_interleave();
+ check_cround();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index 4a2826c4276..6c8e26876af 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -69,6 +69,7 @@ tests += {
'test_abs.S': {'cflags': asmflags},
'test_bitcnt.S': {'cflags': asmflags},
'test_bitsplit.S': {'cflags': asmflags},
+ 'test_bitops.c': {'cflags': cflags},
'test_call.S': {'cflags': asmflags},
'test_clobber.S': {'cflags': asmflags},
'test_cmp.S': {'cflags': asmflags},
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 07/17] tests/tcg/hexagon: add bit interleave and convergent rounding tests
2026-09-05 18:09 ` [PATCH 07/17] tests/tcg/hexagon: add bit interleave and convergent rounding tests Brian Cain
@ 2026-09-10 17:54 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-10 17:54 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 4168 bytes --]
LGTM
Reviewed-by:Sid Manning <sid.manning@oss.qualcomm.com>
On Sat, Sep 5, 2026 at 1:10 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Test the interleave/deinterleave 64-bit bit-manipulation instructions
> and the cround instruction.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/test_bitops.c | 98 +++++++++++++++++++++++++++++++++
> tests/tcg/hexagon/meson.build | 1 +
> 2 files changed, 99 insertions(+)
> create mode 100644 tests/tcg/hexagon/test_bitops.c
>
> diff --git a/tests/tcg/hexagon/test_bitops.c
> b/tests/tcg/hexagon/test_bitops.c
> new file mode 100644
> index 00000000000..3e9274ddddd
> --- /dev/null
> +++ b/tests/tcg/hexagon/test_bitops.c
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +/*
> + * Test bit interleave/deinterleave and convergent rounding.
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <hexagon_protos.h>
> +
> +int err;
> +
> +#include "hex_test.h"
> +
> +static void check_interleave(void)
> +{
> + uint64_t input;
> + uint64_t inter;
> + uint64_t deinter;
> +
> + /*
> + * interleave takes the 32 even bits and 32 odd bits of Rss and
> + * interleaves them: even[0] goes to bit 0, odd[0] to bit 1,
> + * even[1] to bit 2, odd[1] to bit 3, etc.
> + *
> + * Input: low 32 bits (even half), high 32 bits (odd half)
> + * If low = 0xFFFFFFFF, high = 0x00000000:
> + * result has every even bit set, every odd bit clear
> + * = 0x5555555555555555
> + */
> + input = 0x00000000FFFFFFFFULL;
> + inter = Q6_P_interleave_P(input);
> + check64(inter, 0x5555555555555555ULL);
> +
> + /* All ones should stay all ones */
> + input = 0xFFFFFFFFFFFFFFFFULL;
> + inter = Q6_P_interleave_P(input);
> + check64(inter, 0xFFFFFFFFFFFFFFFFULL);
> +
> + /* All zeros should stay all zeros */
> + input = 0x0000000000000000ULL;
> + inter = Q6_P_interleave_P(input);
> + check64(inter, 0x0000000000000000ULL);
> +
> + /*
> + * deinterleave is the inverse of interleave.
> + * deinterleave(interleave(x)) should equal x.
> + */
> + input = 0xDEADBEEFCAFEBABEULL;
> + inter = Q6_P_interleave_P(input);
> + deinter = Q6_P_deinterleave_P(inter);
> + check64(deinter, input);
> +
> + /* deinterleave of 0x5555...5555 -> low half all 1s, high half all 0s
> */
> + deinter = Q6_P_deinterleave_P(0x5555555555555555ULL);
> + check64(deinter, 0x00000000FFFFFFFFULL);
> +}
> +
> +static void check_cround(void)
> +{
> + /*
> + * cround(0x300, #8) -> 3 (exact shift, no rounding needed).
> + * 0x300 >> 8 = 3
> + */
> + check32(Q6_R_cround_RI(0x300, 8), 3);
> +
> + /*
> + * cround(0x380, #8) -> 4 (0x380 >> 8 = 3.5, rounds to even = 4)
> + */
> + check32(Q6_R_cround_RI(0x380, 8), 4);
> +
> + /*
> + * cround(0x280, #8) -> 2 (0x280 >> 8 = 2.5, rounds to even = 2)
> + */
> + check32(Q6_R_cround_RI(0x280, 8), 2);
> +
> + /*
> + * cround(0x3C0, #8) -> 4 (0x3C0 >> 8 = 3.75, rounds up to 4)
> + */
> + check32(Q6_R_cround_RI(0x3C0, 8), 4);
> +
> + /*
> + * Shift by 0 -> return the input unchanged.
> + */
> + check32(Q6_R_cround_RI(42, 0), 42);
> +}
> +
> +int main(void)
> +{
> + check_interleave();
> + check_cround();
> +
> + puts(err ? "FAIL" : "PASS");
> + return err ? 1 : 0;
> +}
> diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
> index 4a2826c4276..6c8e26876af 100644
> --- a/tests/tcg/hexagon/meson.build
> +++ b/tests/tcg/hexagon/meson.build
> @@ -69,6 +69,7 @@ tests += {
> 'test_abs.S': {'cflags': asmflags},
> 'test_bitcnt.S': {'cflags': asmflags},
> 'test_bitsplit.S': {'cflags': asmflags},
> + 'test_bitops.c': {'cflags': cflags},
> 'test_call.S': {'cflags': asmflags},
> 'test_clobber.S': {'cflags': asmflags},
> 'test_cmp.S': {'cflags': asmflags},
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 5247 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 08/17] tests/tcg/hexagon: add cond call, tstbit-jump, and endloop01 tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (6 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 07/17] tests/tcg/hexagon: add bit interleave and convergent rounding tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 19:44 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 09/17] tests/tcg/hexagon: add dczeroa cache line zero test Brian Cain
` (8 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test conditional callr, compound tstbit0-and-jump instructions and
endloop01. These exercise previously uncovered branch paths in translate.c.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/test_cond_branch.c | 162 +++++++++++++++++++++++++++
tests/tcg/hexagon/meson.build | 1 +
2 files changed, 163 insertions(+)
create mode 100644 tests/tcg/hexagon/test_cond_branch.c
diff --git a/tests/tcg/hexagon/test_cond_branch.c b/tests/tcg/hexagon/test_cond_branch.c
new file mode 100644
index 00000000000..73be8fbb43c
--- /dev/null
+++ b/tests/tcg/hexagon/test_cond_branch.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Test conditional calls, compound testbit0-and-jump instructions,
+ * and endloop01 (dual hardware loop end).
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+int err;
+
+#include "hex_test.h"
+
+/* Target function for conditional calls */
+static int call_target_val;
+
+__attribute__((noinline))
+static void call_target(void)
+{
+ call_target_val = 42;
+}
+
+/*
+ * Test conditional call: if (Pu) callr Rs / if (!Pu) callr Rs
+ */
+static void check_cond_call(void)
+{
+ void (*fn)(void) = call_target;
+
+ /*
+ * callr clobbers all caller-saved registers.
+ * Hexagon caller-saved: r0-r5, r14-r15, r28, p0-p3.
+ */
+
+ /* if (p0) callr Rs -- predicate true */
+ call_target_val = 0;
+ asm volatile("p0 = cmp.eq(%[one], %[one])\n\t" /* p0 = true */
+ "if (p0) callr %[fn]\n\t"
+ :
+ : [one] "r"(1), [fn] "r"(fn)
+ : "r0", "r1", "r2", "r3", "r4", "r5",
+ "r14", "r15", "r28", "r31",
+ "p0", "p1", "p2", "p3", "memory");
+ check32(call_target_val, 42);
+
+ /* if (!p0) callr Rs -- predicate true, so !p0 is false */
+ call_target_val = 0;
+ asm volatile("p0 = cmp.eq(%[one], %[one])\n\t" /* p0 = true */
+ "if (!p0) callr %[fn]\n\t"
+ :
+ : [one] "r"(1), [fn] "r"(fn)
+ : "r0", "r1", "r2", "r3", "r4", "r5",
+ "r14", "r15", "r28", "r31",
+ "p0", "p1", "p2", "p3", "memory");
+ check32(call_target_val, 0);
+
+ /* if (!p0) callr Rs -- predicate false, so !p0 is true */
+ call_target_val = 0;
+ asm volatile("p0 = cmp.eq(%[a], %[b])\n\t" /* p0 = false (1 != 2) */
+ "if (!p0) callr %[fn]\n\t"
+ :
+ : [a] "r"(1), [b] "r"(2), [fn] "r"(fn)
+ : "r0", "r1", "r2", "r3", "r4", "r5",
+ "r14", "r15", "r28", "r31",
+ "p0", "p1", "p2", "p3", "memory");
+ check32(call_target_val, 42);
+}
+
+/*
+ * Test compound testbit0-and-jump.
+ * p0 = tstbit(Rs, #0); if ([!]p0.new) jump:t/nt target
+ */
+static void check_tstbit0_jump(void)
+{
+ uint32_t result;
+
+ /* Rs has bit0 = 1, test "if (p0.new) jump" -> should jump */
+ result = 0;
+ asm volatile(
+ "{\n\t"
+ " p0 = tstbit(%[val], #0)\n\t"
+ " if (p0.new) jump:t 1f\n\t"
+ "}\n\t"
+ "%[res] = #0\n\t"
+ "jump 2f\n\t"
+ "1:\n\t"
+ "%[res] = #1\n\t"
+ "2:\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(0x5) /* bit 0 is set */
+ : "p0");
+ check32(result, 1);
+
+ /* Rs has bit0 = 0, test "if (p0.new) jump" -> should NOT jump */
+ result = 0;
+ asm volatile(
+ "{\n\t"
+ " p0 = tstbit(%[val], #0)\n\t"
+ " if (p0.new) jump:t 1f\n\t"
+ "}\n\t"
+ "%[res] = #0\n\t"
+ "jump 2f\n\t"
+ "1:\n\t"
+ "%[res] = #1\n\t"
+ "2:\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(0x4) /* bit 0 is clear */
+ : "p0");
+ check32(result, 0);
+
+ /* Rs has bit0 = 0, test "if (!p0.new) jump" -> should jump */
+ result = 0;
+ asm volatile(
+ "{\n\t"
+ " p0 = tstbit(%[val], #0)\n\t"
+ " if (!p0.new) jump:t 1f\n\t"
+ "}\n\t"
+ "%[res] = #0\n\t"
+ "jump 2f\n\t"
+ "1:\n\t"
+ "%[res] = #1\n\t"
+ "2:\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(0x4) /* bit 0 is clear */
+ : "p0");
+ check32(result, 1);
+}
+
+/*
+ * Test endloop01: a packet that terminates both loop0 and loop1.
+ * Outer loop runs 3 iterations, inner loop runs 4.
+ */
+static void check_endloop01(void)
+{
+ uint32_t count;
+
+ asm volatile(
+ "%[cnt] = #0\n\t"
+ "loop1(1f, #3)\n\t" /* outer loop: 3 iterations */
+ "1:\n\t"
+ "loop0(2f, #4)\n\t" /* inner loop: 4 iterations */
+ "2:\n\t"
+ "{ %[cnt] = add(%[cnt], #1) }:endloop01\n\t"
+ : [cnt] "=r"(count));
+
+ /* 3 outer * 4 inner = 12 total iterations */
+ check32(count, 12);
+}
+
+int main(void)
+{
+ check_cond_call();
+ check_tstbit0_jump();
+ check_endloop01();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index 6c8e26876af..aa176e56883 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -73,6 +73,7 @@ tests += {
'test_call.S': {'cflags': asmflags},
'test_clobber.S': {'cflags': asmflags},
'test_cmp.S': {'cflags': asmflags},
+ 'test_cond_branch.c': {'cflags': cflags},
'test_dotnew.S': {'cflags': asmflags},
'test_ext.S': {'cflags': asmflags},
'test_fibonacci.S': {'cflags': asmflags},
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 08/17] tests/tcg/hexagon: add cond call, tstbit-jump, and endloop01 tests
2026-09-05 18:09 ` [PATCH 08/17] tests/tcg/hexagon: add cond call, tstbit-jump, and endloop01 tests Brian Cain
@ 2026-09-14 19:44 ` Pierrick Bouvier
0 siblings, 0 replies; 35+ messages in thread
From: Pierrick Bouvier @ 2026-09-14 19:44 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Alex Bennée, sid.manning
On 9/5/2026 11:09 AM, Brian Cain wrote:
> Test conditional callr, compound tstbit0-and-jump instructions and
> endloop01. These exercise previously uncovered branch paths in translate.c.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/test_cond_branch.c | 162 +++++++++++++++++++++++++++
> tests/tcg/hexagon/meson.build | 1 +
> 2 files changed, 163 insertions(+)
> create mode 100644 tests/tcg/hexagon/test_cond_branch.c
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 09/17] tests/tcg/hexagon: add dczeroa cache line zero test
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (7 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 08/17] tests/tcg/hexagon: add cond call, tstbit-jump, and endloop01 tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-10 18:14 ` Sid Manning
2026-09-05 18:09 ` [PATCH 10/17] tests/tcg/hexagon: add HVX vwhist tests Brian Cain
` (7 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test the dczeroa instruction which zeroes a cache line. Verifies aligned,
unaligned, and buffer-start addresses.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/test_dczeroa.c | 88 ++++++++++++++++++++++++++++++++
tests/tcg/hexagon/meson.build | 1 +
2 files changed, 89 insertions(+)
create mode 100644 tests/tcg/hexagon/test_dczeroa.c
diff --git a/tests/tcg/hexagon/test_dczeroa.c b/tests/tcg/hexagon/test_dczeroa.c
new file mode 100644
index 00000000000..8cdcdd6a3b5
--- /dev/null
+++ b/tests/tcg/hexagon/test_dczeroa.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Test the dczeroa instruction, which zeroes a 32-byte cache line.
+ * The address is rounded down to a 32-byte boundary: (addr & ~0x1f).
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <hexagon_protos.h>
+
+int err;
+
+#include "hex_test.h"
+
+static uint8_t buf[128] __attribute__((aligned(32)));
+
+/* dczeroa with an aligned address */
+static void test_dczeroa_aligned(void)
+{
+ int i;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ Q6_dczeroa_A(&buf[32]);
+
+ for (i = 0; i < 32; i++) {
+ check32(buf[i], 0xff);
+ }
+ for (i = 32; i < 64; i++) {
+ check32(buf[i], 0x00);
+ }
+ for (i = 64; i < 96; i++) {
+ check32(buf[i], 0xff);
+ }
+}
+
+/* dczeroa with an unaligned address (should round down) */
+static void test_dczeroa_unaligned(void)
+{
+ int i;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ /* Address buf+40 rounds down to buf+32 */
+ Q6_dczeroa_A(&buf[40]);
+
+ for (i = 0; i < 32; i++) {
+ check32(buf[i], 0xff);
+ }
+ for (i = 32; i < 64; i++) {
+ check32(buf[i], 0x00);
+ }
+ for (i = 64; i < 96; i++) {
+ check32(buf[i], 0xff);
+ }
+}
+
+/* dczeroa at the start of the buffer */
+static void test_dczeroa_start(void)
+{
+ int i;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ Q6_dczeroa_A(&buf[0]);
+
+ for (i = 0; i < 32; i++) {
+ check32(buf[i], 0x00);
+ }
+ for (i = 32; i < 64; i++) {
+ check32(buf[i], 0xff);
+ }
+}
+
+int main(void)
+{
+ test_dczeroa_aligned();
+ test_dczeroa_unaligned();
+ test_dczeroa_start();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index aa176e56883..fcf0bd38f26 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -74,6 +74,7 @@ tests += {
'test_clobber.S': {'cflags': asmflags},
'test_cmp.S': {'cflags': asmflags},
'test_cond_branch.c': {'cflags': cflags},
+ 'test_dczeroa.c': {'cflags': cflags},
'test_dotnew.S': {'cflags': asmflags},
'test_ext.S': {'cflags': asmflags},
'test_fibonacci.S': {'cflags': asmflags},
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 09/17] tests/tcg/hexagon: add dczeroa cache line zero test
2026-09-05 18:09 ` [PATCH 09/17] tests/tcg/hexagon: add dczeroa cache line zero test Brian Cain
@ 2026-09-10 18:14 ` Sid Manning
0 siblings, 0 replies; 35+ messages in thread
From: Sid Manning @ 2026-09-10 18:14 UTC (permalink / raw)
To: Brian Cain; +Cc: qemu-devel, Pierrick Bouvier, Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 3396 bytes --]
Reviewed-by:Sid Manning <sid.manning@oss.qualcomm.com>
On Sat, Sep 5, 2026 at 1:10 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:
> Test the dczeroa instruction which zeroes a cache line. Verifies aligned,
> unaligned, and buffer-start addresses.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/test_dczeroa.c | 88 ++++++++++++++++++++++++++++++++
> tests/tcg/hexagon/meson.build | 1 +
> 2 files changed, 89 insertions(+)
> create mode 100644 tests/tcg/hexagon/test_dczeroa.c
>
> diff --git a/tests/tcg/hexagon/test_dczeroa.c
> b/tests/tcg/hexagon/test_dczeroa.c
> new file mode 100644
> index 00000000000..8cdcdd6a3b5
> --- /dev/null
> +++ b/tests/tcg/hexagon/test_dczeroa.c
> @@ -0,0 +1,88 @@
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +/*
> + * Test the dczeroa instruction, which zeroes a 32-byte cache line.
> + * The address is rounded down to a 32-byte boundary: (addr & ~0x1f).
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <hexagon_protos.h>
> +
> +int err;
> +
> +#include "hex_test.h"
> +
> +static uint8_t buf[128] __attribute__((aligned(32)));
> +
> +/* dczeroa with an aligned address */
> +static void test_dczeroa_aligned(void)
> +{
> + int i;
> +
> + memset(buf, 0xff, sizeof(buf));
> +
> + Q6_dczeroa_A(&buf[32]);
> +
> + for (i = 0; i < 32; i++) {
> + check32(buf[i], 0xff);
> + }
> + for (i = 32; i < 64; i++) {
> + check32(buf[i], 0x00);
> + }
> + for (i = 64; i < 96; i++) {
> + check32(buf[i], 0xff);
> + }
> +}
> +
> +/* dczeroa with an unaligned address (should round down) */
> +static void test_dczeroa_unaligned(void)
> +{
> + int i;
> +
> + memset(buf, 0xff, sizeof(buf));
> +
> + /* Address buf+40 rounds down to buf+32 */
> + Q6_dczeroa_A(&buf[40]);
> +
> + for (i = 0; i < 32; i++) {
> + check32(buf[i], 0xff);
> + }
> + for (i = 32; i < 64; i++) {
> + check32(buf[i], 0x00);
> + }
> + for (i = 64; i < 96; i++) {
> + check32(buf[i], 0xff);
> + }
> +}
> +
> +/* dczeroa at the start of the buffer */
> +static void test_dczeroa_start(void)
> +{
> + int i;
> +
> + memset(buf, 0xff, sizeof(buf));
> +
> + Q6_dczeroa_A(&buf[0]);
> +
> + for (i = 0; i < 32; i++) {
> + check32(buf[i], 0x00);
> + }
> + for (i = 32; i < 64; i++) {
> + check32(buf[i], 0xff);
> + }
> +}
> +
> +int main(void)
> +{
> + test_dczeroa_aligned();
> + test_dczeroa_unaligned();
> + test_dczeroa_start();
> +
> + puts(err ? "FAIL" : "PASS");
> + return err ? 1 : 0;
> +}
> diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
> index aa176e56883..fcf0bd38f26 100644
> --- a/tests/tcg/hexagon/meson.build
> +++ b/tests/tcg/hexagon/meson.build
> @@ -74,6 +74,7 @@ tests += {
> 'test_clobber.S': {'cflags': asmflags},
> 'test_cmp.S': {'cflags': asmflags},
> 'test_cond_branch.c': {'cflags': cflags},
> + 'test_dczeroa.c': {'cflags': cflags},
> 'test_dotnew.S': {'cflags': asmflags},
> 'test_ext.S': {'cflags': asmflags},
> 'test_fibonacci.S': {'cflags': asmflags},
> --
> 2.34.1
>
>
[-- Attachment #2: Type: text/html, Size: 4400 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 10/17] tests/tcg/hexagon: add HVX vwhist tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (8 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 09/17] tests/tcg/hexagon: add dczeroa cache line zero test Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:21 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 11/17] tests/tcg/hexagon: add sfrecipa edge case tests Brian Cain
` (6 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test all eight vwhist instruction variants and the four Q-masked forms.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/test_vwhist.c | 361 ++++++++++++++++++++++++++++++++
tests/tcg/hexagon/meson.build | 1 +
2 files changed, 362 insertions(+)
create mode 100644 tests/tcg/hexagon/test_vwhist.c
diff --git a/tests/tcg/hexagon/test_vwhist.c b/tests/tcg/hexagon/test_vwhist.c
new file mode 100644
index 00000000000..5e143f79a43
--- /dev/null
+++ b/tests/tcg/hexagon/test_vwhist.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Test HVX weighted histogram instructions (vwhist128/vwhist256 variants).
+ *
+ * Each halfword in the input vector (loaded via Vx.tmp) contains a bucket
+ * index (low byte) and a weight (high byte). The instruction accumulates
+ * the weight into the appropriate element of the destination vector.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+int err;
+
+#define MAX_VEC_SIZE_BYTES 128
+
+typedef union {
+ uint32_t uw[MAX_VEC_SIZE_BYTES / 4];
+ uint16_t uh[MAX_VEC_SIZE_BYTES / 2];
+ uint8_t ub[MAX_VEC_SIZE_BYTES];
+} MMVector;
+
+/*
+ * Build input vector for vwhist256: each halfword is (weight << 8 | bucket).
+ * We put bucket=0, weight=1 in every slot so that v0.uh[0] gets incremented
+ * by 1 for each of the 64 halfwords -> v0.uh[0] should equal 64.
+ */
+static MMVector input __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
+static MMVector zero_vec __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
+static MMVector result __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
+
+static void check_uint32(int line, int idx, uint32_t val, uint32_t expect)
+{
+ if (val != expect) {
+ printf("ERROR at line %d: [%d] 0x%08x != 0x%08x\n",
+ line, idx, val, expect);
+ err++;
+ }
+}
+
+static void check_uint16(int line, int idx, uint16_t val, uint16_t expect)
+{
+ if (val != expect) {
+ printf("ERROR at line %d: [%d] 0x%04x != 0x%04x\n",
+ line, idx, val, expect);
+ err++;
+ }
+}
+
+/*
+ * Test vwhist256: 256-bin histogram with 16-bit accumulation.
+ * Each halfword of the input: low byte = bucket, high byte = weight.
+ * bucket bits [7:3] -> vindex (which vector register), [2:0] + lane -> element.
+ * All entries have bucket=0 weight=1, so v0.uh[0..7] get incremented.
+ */
+static void test_vwhist256(void)
+{
+ int i;
+
+ /* Set all input halfwords to bucket=0, weight=1 */
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ /* Zero out v0 (the target for bucket 0) */
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist256\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result)
+ : "v0", "v12", "memory");
+
+ /*
+ * 64 input halfwords all targeting bucket 0.
+ * elindex = (i & ~7) | (bucket & 7) = i & ~7 (since bucket=0).
+ * So elements 0,8,16,24,32,40,48,56 each get 8 increments.
+ */
+ for (i = 0; i < 64; i++) {
+ uint16_t expected = ((i & 7) == 0) ? 8 : 0;
+ check_uint16(__LINE__, i, result.uh[i], expected);
+ }
+}
+
+/*
+ * Test vwhist256:sat -- saturating variant.
+ * Use weight=0xFF to test that saturation to 0xFFFF works.
+ */
+static void test_vwhist256_sat(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0xFF00; /* weight=0xFF, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist256:sat\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result)
+ : "v0", "v12", "memory");
+
+ /*
+ * Same distribution as vwhist256: elements 0,8,16,...,56.
+ * Each gets 8 * 0xFF = 2040 = 0x7F8 (within uint16 range).
+ */
+ for (i = 0; i < 64; i++) {
+ uint16_t expected = ((i & 7) == 0) ? 8 * 0xFF : 0;
+ check_uint16(__LINE__, i, result.uh[i], expected);
+ }
+}
+
+/*
+ * Test vwhist128: 128-bin histogram with 32-bit accumulation.
+ * bucket bits [7:3] -> vindex, [2:1] + lane -> element (word granularity).
+ */
+static void test_vwhist128(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist128\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result)
+ : "v0", "v12", "memory");
+
+ /*
+ * 128-bin: 64 halfwords all targeting bucket 0.
+ * bucket 0 -> vindex=0, elindex based on (i>>1)&(~3) | (bucket>>1)&3.
+ * With bucket=0, elindex = (i>>1)&(~3).
+ * For i=0,1: elindex=0; i=2,3: elindex=0; ... up to i=6,7: elindex=0
+ * i ranges 0..63. (i>>1) ranges 0..31.
+ * (i>>1)&(~3) = 0,0,0,0,4,4,4,4,8,...
+ * So elements 0,4,8,12,16,20,24,28 each get 8 increments.
+ */
+ for (i = 0; i < 32; i++) {
+ uint32_t expected = ((i & 3) == 0) ? 8 : 0;
+ check_uint32(__LINE__, i, result.uw[i], expected);
+ }
+}
+
+/*
+ * Test vwhist128(#0) -- masked variant.
+ * Only processes elements where (bucket & 1) == mode.
+ */
+static void test_vwhist128m(void)
+{
+ int i;
+
+ /* All bucket=0, weight=1. bucket&1==0, so mode=0 matches all. */
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist128(#0)\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result)
+ : "v0", "v12", "memory");
+
+ /* Same distribution as vwhist128 since all buckets have bit0=0 */
+ for (i = 0; i < 32; i++) {
+ uint32_t expected = ((i & 3) == 0) ? 8 : 0;
+ check_uint32(__LINE__, i, result.uw[i], expected);
+ }
+
+ /* Now test with mode=1: bucket=0 has bit0=0, so nothing should match */
+ memset(&result, 0, sizeof(result));
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist128(#1)\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result)
+ : "v0", "v12", "memory");
+
+ for (i = 0; i < 32; i++) {
+ check_uint32(__LINE__, i, result.uw[i], 0);
+ }
+}
+
+static MMVector ones_vec __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
+
+/*
+ * Test vwhist256(Qv4) -- Q-masked vwhist256.
+ * Set Q0 to all-ones so all elements pass the mask -> same as vwhist256.
+ */
+static void test_vwhist256q(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "v1 = vmem(%[ones] + #0)\n\t"
+ "q0 = vcmp.eq(v1.b, v1.b)\n\t" /* all-ones Q0 */
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist256(q0)\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result), [ones] "r"(&ones_vec)
+ : "v0", "v1", "v12", "q0", "memory");
+
+ for (i = 0; i < 64; i++) {
+ uint16_t expected = ((i & 7) == 0) ? 8 : 0;
+ check_uint16(__LINE__, i, result.uh[i], expected);
+ }
+}
+
+/*
+ * Test vwhist256(Qv4):sat -- Q-masked saturating vwhist256.
+ */
+static void test_vwhist256q_sat(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0xFF00; /* weight=0xFF, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "v1 = vmem(%[ones] + #0)\n\t"
+ "q0 = vcmp.eq(v1.b, v1.b)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist256(q0):sat\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result), [ones] "r"(&ones_vec)
+ : "v0", "v1", "v12", "q0", "memory");
+
+ for (i = 0; i < 64; i++) {
+ uint16_t expected = ((i & 7) == 0) ? 8 * 0xFF : 0;
+ check_uint16(__LINE__, i, result.uh[i], expected);
+ }
+}
+
+/*
+ * Test vwhist128(Qv4) -- Q-masked vwhist128.
+ */
+static void test_vwhist128q(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "v1 = vmem(%[ones] + #0)\n\t"
+ "q0 = vcmp.eq(v1.b, v1.b)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist128(q0)\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result), [ones] "r"(&ones_vec)
+ : "v0", "v1", "v12", "q0", "memory");
+
+ for (i = 0; i < 32; i++) {
+ uint32_t expected = ((i & 3) == 0) ? 8 : 0;
+ check_uint32(__LINE__, i, result.uw[i], expected);
+ }
+}
+
+/*
+ * Test vwhist128(Qv4,#0) -- Q-masked mode vwhist128.
+ */
+static void test_vwhist128qm(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ input.uh[i] = 0x0100; /* weight=1, bucket=0 */
+ }
+
+ asm volatile(
+ "v0 = vmem(%[zero] + #0)\n\t"
+ "v1 = vmem(%[ones] + #0)\n\t"
+ "q0 = vcmp.eq(v1.b, v1.b)\n\t"
+ "{\n\t"
+ " v12.tmp = vmem(%[inp] + #0)\n\t"
+ " vwhist128(q0,#0)\n\t"
+ "}\n\t"
+ "vmem(%[out] + #0) = v0\n\t"
+ :
+ : [inp] "r"(&input), [zero] "r"(&zero_vec),
+ [out] "r"(&result), [ones] "r"(&ones_vec)
+ : "v0", "v1", "v12", "q0", "memory");
+
+ /* bucket=0, bit0=0, mode=0 matches -> same as vwhist128 */
+ for (i = 0; i < 32; i++) {
+ uint32_t expected = ((i & 3) == 0) ? 8 : 0;
+ check_uint32(__LINE__, i, result.uw[i], expected);
+ }
+}
+
+int main(void)
+{
+ memset(&zero_vec, 0, sizeof(zero_vec));
+ memset(&ones_vec, 0xff, sizeof(ones_vec));
+
+ test_vwhist256();
+ test_vwhist256_sat();
+ test_vwhist128();
+ test_vwhist128m();
+ test_vwhist256q();
+ test_vwhist256q_sat();
+ test_vwhist128q();
+ test_vwhist128qm();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index fcf0bd38f26..4c70875034d 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -94,6 +94,7 @@ tests += {
'test_vminh.S': {'cflags': asmflags},
'test_vpmpyh.S': {'cflags': asmflags},
'test_vspliceb.S': {'cflags': asmflags},
+ 'test_vwhist.c': {'cflags': [cflags, '-mhvx']},
'unaligned_pc.c': {'cflags': cflags},
'unaligned_data.c': {'cflags': cflags},
'usr.c': {
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 10/17] tests/tcg/hexagon: add HVX vwhist tests
2026-09-05 18:09 ` [PATCH 10/17] tests/tcg/hexagon: add HVX vwhist tests Brian Cain
@ 2026-09-14 21:21 ` Pierrick Bouvier
0 siblings, 0 replies; 35+ messages in thread
From: Pierrick Bouvier @ 2026-09-14 21:21 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Alex Bennée, sid.manning
On 9/5/2026 11:09 AM, Brian Cain wrote:
> Test all eight vwhist instruction variants and the four Q-masked forms.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/test_vwhist.c | 361 ++++++++++++++++++++++++++++++++
> tests/tcg/hexagon/meson.build | 1 +
> 2 files changed, 362 insertions(+)
> create mode 100644 tests/tcg/hexagon/test_vwhist.c
>
> diff --git a/tests/tcg/hexagon/test_vwhist.c b/tests/tcg/hexagon/test_vwhist.c
> new file mode 100644
> index 00000000000..5e143f79a43
> --- /dev/null
> +++ b/tests/tcg/hexagon/test_vwhist.c
> @@ -0,0 +1,361 @@
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +/*
> + * Test HVX weighted histogram instructions (vwhist128/vwhist256 variants).
> + *
> + * Each halfword in the input vector (loaded via Vx.tmp) contains a bucket
> + * index (low byte) and a weight (high byte). The instruction accumulates
> + * the weight into the appropriate element of the destination vector.
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <string.h>
> +
> +int err;
> +
> +#define MAX_VEC_SIZE_BYTES 128
> +
> +typedef union {
> + uint32_t uw[MAX_VEC_SIZE_BYTES / 4];
> + uint16_t uh[MAX_VEC_SIZE_BYTES / 2];
> + uint8_t ub[MAX_VEC_SIZE_BYTES];
> +} MMVector;
> +
> +/*
> + * Build input vector for vwhist256: each halfword is (weight << 8 | bucket).
> + * We put bucket=0, weight=1 in every slot so that v0.uh[0] gets incremented
> + * by 1 for each of the 64 halfwords -> v0.uh[0] should equal 64.
> + */
> +static MMVector input __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
> +static MMVector zero_vec __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
> +static MMVector result __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
> +
> +static void check_uint32(int line, int idx, uint32_t val, uint32_t expect)
> +{
> + if (val != expect) {
> + printf("ERROR at line %d: [%d] 0x%08x != 0x%08x\n",
> + line, idx, val, expect);
> + err++;
> + }
> +}
> +
> +static void check_uint16(int line, int idx, uint16_t val, uint16_t expect)
> +{
> + if (val != expect) {
> + printf("ERROR at line %d: [%d] 0x%04x != 0x%04x\n",
> + line, idx, val, expect);
> + err++;
> + }
> +}
> +
Seems like duplication of check* in hex_test.h.
check16 needs to be added there.
> +/*
> + * Test vwhist256: 256-bin histogram with 16-bit accumulation.
> + * Each halfword of the input: low byte = bucket, high byte = weight.
> + * bucket bits [7:3] -> vindex (which vector register), [2:0] + lane -> element.
> + * All entries have bucket=0 weight=1, so v0.uh[0..7] get incremented.
> + */
> +static void test_vwhist256(void)
> +{
> + int i;
> +
> + /* Set all input halfwords to bucket=0, weight=1 */
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + /* Zero out v0 (the target for bucket 0) */
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist256\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result)
> + : "v0", "v12", "memory");
> +
> + /*
> + * 64 input halfwords all targeting bucket 0.
> + * elindex = (i & ~7) | (bucket & 7) = i & ~7 (since bucket=0).
> + * So elements 0,8,16,24,32,40,48,56 each get 8 increments.
> + */
> + for (i = 0; i < 64; i++) {
> + uint16_t expected = ((i & 7) == 0) ? 8 : 0;
> + check_uint16(__LINE__, i, result.uh[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist256:sat -- saturating variant.
> + * Use weight=0xFF to test that saturation to 0xFFFF works.
> + */
> +static void test_vwhist256_sat(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0xFF00; /* weight=0xFF, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist256:sat\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result)
> + : "v0", "v12", "memory");
> +
> + /*
> + * Same distribution as vwhist256: elements 0,8,16,...,56.
> + * Each gets 8 * 0xFF = 2040 = 0x7F8 (within uint16 range).
> + */
> + for (i = 0; i < 64; i++) {
> + uint16_t expected = ((i & 7) == 0) ? 8 * 0xFF : 0;
> + check_uint16(__LINE__, i, result.uh[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist128: 128-bin histogram with 32-bit accumulation.
> + * bucket bits [7:3] -> vindex, [2:1] + lane -> element (word granularity).
> + */
> +static void test_vwhist128(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist128\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result)
> + : "v0", "v12", "memory");
> +
> + /*
> + * 128-bin: 64 halfwords all targeting bucket 0.
> + * bucket 0 -> vindex=0, elindex based on (i>>1)&(~3) | (bucket>>1)&3.
> + * With bucket=0, elindex = (i>>1)&(~3).
> + * For i=0,1: elindex=0; i=2,3: elindex=0; ... up to i=6,7: elindex=0
> + * i ranges 0..63. (i>>1) ranges 0..31.
> + * (i>>1)&(~3) = 0,0,0,0,4,4,4,4,8,...
> + * So elements 0,4,8,12,16,20,24,28 each get 8 increments.
> + */
> + for (i = 0; i < 32; i++) {
> + uint32_t expected = ((i & 3) == 0) ? 8 : 0;
> + check_uint32(__LINE__, i, result.uw[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist128(#0) -- masked variant.
> + * Only processes elements where (bucket & 1) == mode.
> + */
> +static void test_vwhist128m(void)
> +{
> + int i;
> +
> + /* All bucket=0, weight=1. bucket&1==0, so mode=0 matches all. */
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist128(#0)\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result)
> + : "v0", "v12", "memory");
> +
> + /* Same distribution as vwhist128 since all buckets have bit0=0 */
> + for (i = 0; i < 32; i++) {
> + uint32_t expected = ((i & 3) == 0) ? 8 : 0;
> + check_uint32(__LINE__, i, result.uw[i], expected);
> + }
> +
> + /* Now test with mode=1: bucket=0 has bit0=0, so nothing should match */
> + memset(&result, 0, sizeof(result));
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist128(#1)\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result)
> + : "v0", "v12", "memory");
> +
> + for (i = 0; i < 32; i++) {
> + check_uint32(__LINE__, i, result.uw[i], 0);
> + }
> +}
> +
> +static MMVector ones_vec __attribute__((aligned(MAX_VEC_SIZE_BYTES)));
> +
> +/*
> + * Test vwhist256(Qv4) -- Q-masked vwhist256.
> + * Set Q0 to all-ones so all elements pass the mask -> same as vwhist256.
> + */
> +static void test_vwhist256q(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "v1 = vmem(%[ones] + #0)\n\t"
> + "q0 = vcmp.eq(v1.b, v1.b)\n\t" /* all-ones Q0 */
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist256(q0)\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result), [ones] "r"(&ones_vec)
> + : "v0", "v1", "v12", "q0", "memory");
> +
> + for (i = 0; i < 64; i++) {
> + uint16_t expected = ((i & 7) == 0) ? 8 : 0;
> + check_uint16(__LINE__, i, result.uh[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist256(Qv4):sat -- Q-masked saturating vwhist256.
> + */
> +static void test_vwhist256q_sat(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0xFF00; /* weight=0xFF, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "v1 = vmem(%[ones] + #0)\n\t"
> + "q0 = vcmp.eq(v1.b, v1.b)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist256(q0):sat\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result), [ones] "r"(&ones_vec)
> + : "v0", "v1", "v12", "q0", "memory");
> +
> + for (i = 0; i < 64; i++) {
> + uint16_t expected = ((i & 7) == 0) ? 8 * 0xFF : 0;
> + check_uint16(__LINE__, i, result.uh[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist128(Qv4) -- Q-masked vwhist128.
> + */
> +static void test_vwhist128q(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "v1 = vmem(%[ones] + #0)\n\t"
> + "q0 = vcmp.eq(v1.b, v1.b)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist128(q0)\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result), [ones] "r"(&ones_vec)
> + : "v0", "v1", "v12", "q0", "memory");
> +
> + for (i = 0; i < 32; i++) {
> + uint32_t expected = ((i & 3) == 0) ? 8 : 0;
> + check_uint32(__LINE__, i, result.uw[i], expected);
> + }
> +}
> +
> +/*
> + * Test vwhist128(Qv4,#0) -- Q-masked mode vwhist128.
> + */
> +static void test_vwhist128qm(void)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
> + input.uh[i] = 0x0100; /* weight=1, bucket=0 */
> + }
> +
> + asm volatile(
> + "v0 = vmem(%[zero] + #0)\n\t"
> + "v1 = vmem(%[ones] + #0)\n\t"
> + "q0 = vcmp.eq(v1.b, v1.b)\n\t"
> + "{\n\t"
> + " v12.tmp = vmem(%[inp] + #0)\n\t"
> + " vwhist128(q0,#0)\n\t"
> + "}\n\t"
> + "vmem(%[out] + #0) = v0\n\t"
> + :
> + : [inp] "r"(&input), [zero] "r"(&zero_vec),
> + [out] "r"(&result), [ones] "r"(&ones_vec)
> + : "v0", "v1", "v12", "q0", "memory");
> +
> + /* bucket=0, bit0=0, mode=0 matches -> same as vwhist128 */
> + for (i = 0; i < 32; i++) {
> + uint32_t expected = ((i & 3) == 0) ? 8 : 0;
> + check_uint32(__LINE__, i, result.uw[i], expected);
> + }
> +}
> +
> +int main(void)
> +{
> + memset(&zero_vec, 0, sizeof(zero_vec));
> + memset(&ones_vec, 0xff, sizeof(ones_vec));
> +
> + test_vwhist256();
> + test_vwhist256_sat();
> + test_vwhist128();
> + test_vwhist128m();
> + test_vwhist256q();
> + test_vwhist256q_sat();
> + test_vwhist128q();
> + test_vwhist128qm();
> +
> + puts(err ? "FAIL" : "PASS");
> + return err ? 1 : 0;
> +}
> diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
> index fcf0bd38f26..4c70875034d 100644
> --- a/tests/tcg/hexagon/meson.build
> +++ b/tests/tcg/hexagon/meson.build
> @@ -94,6 +94,7 @@ tests += {
> 'test_vminh.S': {'cflags': asmflags},
> 'test_vpmpyh.S': {'cflags': asmflags},
> 'test_vspliceb.S': {'cflags': asmflags},
> + 'test_vwhist.c': {'cflags': [cflags, '-mhvx']},
> 'unaligned_pc.c': {'cflags': cflags},
> 'unaligned_data.c': {'cflags': cflags},
> 'usr.c': {
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 11/17] tests/tcg/hexagon: add sfrecipa edge case tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (9 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 10/17] tests/tcg/hexagon: add HVX vwhist tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:22 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 12/17] tests/tcg/hexagon: add dfmpyhh Brian Cain
` (5 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Add edge case tests for the sfrecipa instruction
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/fpstuff.c | 81 +++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index 7561f31cdc3..f82262ab4ac 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -345,6 +345,86 @@ static void check_sfrecipa(void)
check32(pred, 0x80);
}
+/*
+ * Helper to run sfrecipa and capture result + USR.
+ */
+static void do_sfrecipa(uint32_t Rs, uint32_t Rt,
+ uint32_t *Rd, uint32_t *usr_out)
+{
+ uint32_t rd, usr;
+
+ asm volatile(CLEAR_FPSTATUS
+ "%[rd],p0 = sfrecipa(%[Rs], %[Rt])\n\t"
+ "%[usr] = usr\n\t"
+ : [rd] "=r"(rd), [usr] "=r"(usr)
+ : [Rs] "r"(Rs), [Rt] "r"(Rt)
+ : "p0", "r2", "usr");
+ *Rd = rd;
+ *usr_out = usr;
+}
+
+/*
+ * Additional sfrecipa edge cases:
+ * - inf/inf, 0/0 -> NaN + invalid
+ * - normal/inf, 0/normal, inf/normal
+ * - denominator with extreme biased exponents
+ */
+static void check_sfrecipa_edges(void)
+{
+ uint32_t rd, usr;
+
+ /* inf / inf -> invalid */
+ do_sfrecipa(SF_INF, SF_INF, &rd, &usr);
+ check_fpstatus(usr, USR_FPINVF);
+
+ /* -inf / +inf -> invalid */
+ do_sfrecipa(0xff800000, SF_INF, &rd, &usr);
+ check_fpstatus(usr, USR_FPINVF);
+
+ /* 0 / 0 -> invalid */
+ do_sfrecipa(SF_zero, SF_zero, &rd, &usr);
+ check_fpstatus(usr, USR_FPINVF);
+
+ /* -0 / +0 -> invalid */
+ do_sfrecipa(SF_zero_neg, SF_zero, &rd, &usr);
+ check_fpstatus(usr, USR_FPINVF);
+
+ /* normal / inf -> fixup (Rd = 1.0) */
+ do_sfrecipa(SF_one, SF_INF, &rd, &usr);
+ check32(rd, SF_one);
+
+ do_sfrecipa(SF_two, SF_INF, &rd, &usr);
+ check32(rd, SF_one);
+
+ /* 0 / normal -> fixup (Rd = 1.0) */
+ do_sfrecipa(SF_zero, SF_one, &rd, &usr);
+ check32(rd, SF_one);
+
+ do_sfrecipa(SF_zero_neg, SF_two, &rd, &usr);
+ check32(rd, SF_one);
+
+ /* inf / normal -> fixup (Rd = 1.0) */
+ do_sfrecipa(SF_INF, SF_one, &rd, &usr);
+ check32(rd, SF_one);
+
+ do_sfrecipa(0xff800000, SF_two, &rd, &usr);
+ check32(rd, SF_one);
+
+ /*
+ * Denominator with biased exponent <= 1.
+ * Rs: biased exp 26, Rt: biased exp 1.
+ */
+ do_sfrecipa(26U << 23, 1U << 23, &rd, &usr);
+ check32_ne(rd, 0);
+
+ /*
+ * Denominator with biased exponent > 252.
+ * Both: biased exp 253.
+ */
+ do_sfrecipa(253U << 23, 253U << 23, &rd, &usr);
+ check32_ne(rd, 0);
+}
+
static void check_canonical_NaN(void)
{
uint32_t sf_result;
@@ -887,6 +967,7 @@ int main()
check_sfminmax();
check_dfminmax();
check_sfrecipa();
+ check_sfrecipa_edges();
check_canonical_NaN();
check_invsqrta();
check_sffixupn();
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* [PATCH 12/17] tests/tcg/hexagon: add dfmpyhh
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (10 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 11/17] tests/tcg/hexagon: add sfrecipa edge case tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:22 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 13/17] tests/tcg/hexagon: add unconditional store-immediate tests Brian Cain
` (4 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test the dfmpyhh instruction.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/fpstuff.c | 57 +++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index f82262ab4ac..0f28a218d84 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -935,6 +935,16 @@ static uint64_t do_dfmpyfix(uint64_t a, uint64_t b)
return result;
}
+static uint64_t do_dfmpyhh(uint64_t a, uint64_t b, uint64_t acc)
+{
+ uint64_t result = acc;
+
+ asm("%[res] += dfmpyhh(%[a], %[b])\n\t"
+ : [res] "+r"(result)
+ : [a] "r"(a), [b] "r"(b));
+ return result;
+}
+
static void check_dfmpyfix(void)
{
/*
@@ -961,6 +971,52 @@ static void check_dfmpyfix(void)
0x0010000000000000ULL);
}
+/*
+ * Test dfmpyhh (double-precision FP multiply high*high and accumulate):
+ * - normal inputs
+ * - denormal inputs (crushed to inexact zero)
+ * - zero/NaN/infinity inputs
+ * - nonzero accumulator
+ */
+static inline uint64_t df_abs(uint64_t v) { return v & 0x7FFFFFFFFFFFFFFFULL; }
+static inline uint64_t df_exp(uint64_t v) { return v & 0x7FF0000000000000ULL; }
+static inline uint64_t df_mant(uint64_t v) { return v & 0x000FFFFFFFFFFFFFULL; }
+
+static void check_dfmpyhh(void)
+{
+ uint64_t result;
+
+ /* Normal * normal: 1.0 * 1.0 + 0 -> nonzero finite */
+ result = do_dfmpyhh(DF_one, DF_one, 0ULL);
+ check64_ne(df_abs(result), 0ULL);
+
+ /* Denormal input a: crushed to zero */
+ result = do_dfmpyhh(0x0008000000000000ULL, DF_one, 0ULL);
+ check64(df_abs(result), 0ULL);
+
+ /* Denormal input b: crushed to zero */
+ result = do_dfmpyhh(DF_one, 0x0008000000000000ULL, 0ULL);
+ check64(df_abs(result), 0ULL);
+
+ /* Zero input: early exit */
+ result = do_dfmpyhh(DF_zero, DF_one, 0ULL);
+ check64(df_abs(result), 0ULL);
+
+ /* Infinity input: early exit, result contains inf */
+ result = do_dfmpyhh(0x7FF0000000000000ULL, DF_one, 0ULL);
+ check64(df_exp(result), 0x7FF0000000000000ULL);
+
+ /* NaN input: early exit, result is NaN */
+ result = do_dfmpyhh(DF_QNaN, DF_one, 0ULL);
+ check64(df_exp(result), 0x7FF0000000000000ULL);
+ check64_ne(df_mant(result), 0ULL);
+
+ /* Nonzero accumulator: nonzero finite result */
+ result = do_dfmpyhh(DF_one, DF_one, DF_one);
+ check64_ne(df_abs(result), 0ULL);
+ check64_ne(df_exp(result), 0x7FF0000000000000ULL);
+}
+
int main()
{
check_compare_exception();
@@ -981,6 +1037,7 @@ int main()
check_conv_uw2df();
check_conv_ud2df();
check_dfmpyfix();
+ check_dfmpyhh();
puts(err ? "FAIL" : "PASS");
return err ? 1 : 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* [PATCH 13/17] tests/tcg/hexagon: add unconditional store-immediate tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (11 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 12/17] tests/tcg/hexagon: add dfmpyhh Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:23 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 14/17] tests/tcg/hexagon: add predicate register transfer test Brian Cain
` (3 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/misc.c | 44 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/tests/tcg/hexagon/misc.c b/tests/tcg/hexagon/misc.c
index ca22bb79f7b..c07547b6f68 100644
--- a/tests/tcg/hexagon/misc.c
+++ b/tests/tcg/hexagon/misc.c
@@ -404,6 +404,48 @@ void test_dpmpyss_rnd_s0(void)
check32(dpmpyss_rnd_s0(0x7fffffff, 0x7fffffff), 0x3fffffff);
}
+static uint8_t storeimm_byte_array[16];
+static uint16_t storeimm_half_array[16];
+static uint32_t storeimm_word_array[16];
+
+/*
+ * Unconditional store-immediate instructions (S4_storeir*_io).
+ * The predicated forms are tested above; these exercise the base encoding
+ * with positive and negative #S8 immediate values.
+ */
+static void check_store_imm(void)
+{
+ /* memb(Rs+#u6:0) = #S8 */
+ memset(storeimm_byte_array, 0, sizeof(storeimm_byte_array));
+ asm volatile("memb(%[ptr] + #0) = #0x12\n\t"
+ : : [ptr] "r"(storeimm_byte_array) : "memory");
+ check32(storeimm_byte_array[0], 0x12);
+
+ asm volatile("memb(%[ptr] + #3) = #-1\n\t"
+ : : [ptr] "r"(storeimm_byte_array) : "memory");
+ check32(storeimm_byte_array[3], 0xff);
+
+ /* memh(Rs+#u6:1) = #S8 */
+ memset(storeimm_half_array, 0, sizeof(storeimm_half_array));
+ asm volatile("memh(%[ptr] + #0) = #0x34\n\t"
+ : : [ptr] "r"(storeimm_half_array) : "memory");
+ check32(storeimm_half_array[0], 0x34);
+
+ asm volatile("memh(%[ptr] + #4) = #-1\n\t"
+ : : [ptr] "r"(storeimm_half_array) : "memory");
+ check32(storeimm_half_array[2], 0xffff);
+
+ /* memw(Rs+#u6:2) = #S8 */
+ memset(storeimm_word_array, 0, sizeof(storeimm_word_array));
+ asm volatile("memw(%[ptr] + #0) = #0x56\n\t"
+ : : [ptr] "r"(storeimm_word_array) : "memory");
+ check32(storeimm_word_array[0], 0x56);
+
+ asm volatile("memw(%[ptr] + #8) = #-1\n\t"
+ : : [ptr] "r"(storeimm_word_array) : "memory");
+ check32(storeimm_word_array[2], 0xffffffff);
+}
+
int main()
{
int32_t res;
@@ -547,6 +589,8 @@ int main()
test_dpmpyss_rnd_s0();
+ check_store_imm();
+
puts(err ? "FAIL" : "PASS");
return err;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* [PATCH 14/17] tests/tcg/hexagon: add predicate register transfer test
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (12 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 13/17] tests/tcg/hexagon: add unconditional store-immediate tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:23 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 15/17] tests/tcg/hexagon: add rounding conv, sffma:lib, and sfinvsqrta Brian Cain
` (2 subsequent siblings)
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/misc.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tests/tcg/hexagon/misc.c b/tests/tcg/hexagon/misc.c
index c07547b6f68..abb34ab6a65 100644
--- a/tests/tcg/hexagon/misc.c
+++ b/tests/tcg/hexagon/misc.c
@@ -446,6 +446,31 @@ static void check_store_imm(void)
check32(storeimm_word_array[2], 0xffffffff);
}
+/*
+ * Rd = Ps -- transfer predicate register to general register.
+ * The result is 0x00 or 0xff depending on the predicate value.
+ */
+static void check_preg_transfer(void)
+{
+ uint32_t result;
+
+ /* Set p0 = true, then Rd = p0 should give 0xff */
+ asm volatile("p0 = cmp.eq(%[val], %[val])\n\t"
+ "%[res] = p0\n\t"
+ : [res] "=r"(result)
+ : [val] "r"(1)
+ : "p0");
+ check32(result, 0xff);
+
+ /* Set p0 = false, then Rd = p0 should give 0x00 */
+ asm volatile("p0 = cmp.eq(%[a], %[b])\n\t"
+ "%[res] = p0\n\t"
+ : [res] "=r"(result)
+ : [a] "r"(1), [b] "r"(2)
+ : "p0");
+ check32(result, 0x00);
+}
+
int main()
{
int32_t res;
@@ -590,6 +615,7 @@ int main()
test_dpmpyss_rnd_s0();
check_store_imm();
+ check_preg_transfer();
puts(err ? "FAIL" : "PASS");
return err;
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* [PATCH 15/17] tests/tcg/hexagon: add rounding conv, sffma:lib, and sfinvsqrta
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (13 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 14/17] tests/tcg/hexagon: add predicate register transfer test Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:23 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 16/17] tests/tcg/hexagon: add scalar+HVX and masked store tests Brian Cain
2026-09-05 18:09 ` [PATCH 17/17] tests/tcg/hexagon: add decbin MPS test case Brian Cain
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Add round-to-nearest float-to-int conversion tests for the non-:chop variants,
sfmpy:lib with Inf-Inf to verify exception suppression, and sfinvsqrta
with a denormal input.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/fpstuff.c | 99 +++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index 0f28a218d84..9ea79c617d9 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -1017,6 +1017,102 @@ static void check_dfmpyhh(void)
check64_ne(df_exp(result), 0x7FF0000000000000ULL);
}
+/*
+ * Round-to-nearest float-to-int conversions with normal values.
+ * These test the non-:chop variants which use the USR rounding mode.
+ */
+static void check_conv_rnd(void)
+{
+ uint32_t res32;
+ uint64_t res64;
+
+ /* convert_sf2w(1.5) rounds to nearest-even = 2 */
+ asm(CLEAR_FPSTATUS
+ "%[res] = convert_sf2w(%[val])\n\t"
+ : [res] "=r"(res32)
+ : [val] "r"(0x3fc00000) /* 1.5f */
+ : "r2", "usr");
+ check32(res32, 2);
+
+ /* convert_sf2d(1.5) = 2 */
+ asm(CLEAR_FPSTATUS
+ "%[res] = convert_sf2d(%[val])\n\t"
+ : [res] "=r"(res64)
+ : [val] "r"(0x3fc00000) /* 1.5f */
+ : "r2", "usr");
+ check64(res64, 2ULL);
+
+ /* convert_df2w(1.5) = 2 */
+ asm(CLEAR_FPSTATUS
+ "%[res] = convert_df2w(%[val])\n\t"
+ : [res] "=r"(res32)
+ : [val] "r"(0x3FF8000000000000ULL) /* 1.5 */
+ : "r2", "usr");
+ check32(res32, 2);
+
+ /* convert_df2d(1.5) = 2 */
+ asm(CLEAR_FPSTATUS
+ "%[res] = convert_df2d(%[val])\n\t"
+ : [res] "=r"(res64)
+ : [val] "r"(0x3FF8000000000000ULL) /* 1.5 */
+ : "r2", "usr");
+ check64(res64, 2ULL);
+}
+
+/*
+ * Test sfmpy:lib with Inf-Inf.
+ *
+ * Rx += sfmpy(Rs, Rt):lib with Rx = -Inf, Rs*Rt -> +Inf
+ * Inf - Inf is invalid, but the :lib variant suppresses the
+ * exception and returns zero.
+ */
+static void check_sffma_lib_inf(void)
+{
+ uint32_t result;
+
+ /* +Inf += sfmpy(+Inf, 1.0):lib -> Inf (no exception, normal) */
+ result = SF_INF;
+ asm(CLEAR_FPSTATUS
+ "%[res] += sfmpy(%[a], %[b]):lib\n\t"
+ : [res] "+r"(result)
+ : [a] "r"(SF_INF), [b] "r"(SF_one)
+ : "r2", "usr");
+ check32(result, SF_INF);
+
+ /*
+ * -Inf += sfmpy(+Inf, 1.0):lib -> Inf - Inf = invalid
+ * The :lib variant should return zero for Inf - Inf.
+ */
+ result = SF_INF | (1u << 31); /* -Inf */
+ asm(CLEAR_FPSTATUS
+ "%[res] += sfmpy(%[a], %[b]):lib\n\t"
+ : [res] "+r"(result)
+ : [a] "r"(SF_INF), [b] "r"(SF_one)
+ : "r2", "usr");
+ check32(result, 0);
+}
+
+/*
+ * Test sfinvsqrta with a denormal input.
+ */
+static void check_invsqrta_denorm(void)
+{
+ uint32_t result;
+ uint32_t predval;
+ uint32_t exp_bits;
+
+ /* Denormal positive float: 0x00400000 (small denorm) */
+ asm volatile("%[res],p0 = sfinvsqrta(%[val])\n\t"
+ "%[pred] = p0\n\t"
+ : [res] "=r"(result), [pred] "=r"(predval)
+ : [val] "r"(SF_denorm)
+ : "p0");
+ /* Result should be a valid float (not NaN/zero) */
+ check32_ne(result, 0);
+ exp_bits = (result >> 23) & 0xff;
+ check32_ne(exp_bits, 0xff); /* not Inf/NaN */
+}
+
int main()
{
check_compare_exception();
@@ -1038,6 +1134,9 @@ int main()
check_conv_ud2df();
check_dfmpyfix();
check_dfmpyhh();
+ check_conv_rnd();
+ check_sffma_lib_inf();
+ check_invsqrta_denorm();
puts(err ? "FAIL" : "PASS");
return err ? 1 : 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* Re: [PATCH 15/17] tests/tcg/hexagon: add rounding conv, sffma:lib, and sfinvsqrta
2026-09-05 18:09 ` [PATCH 15/17] tests/tcg/hexagon: add rounding conv, sffma:lib, and sfinvsqrta Brian Cain
@ 2026-09-14 21:23 ` Pierrick Bouvier
0 siblings, 0 replies; 35+ messages in thread
From: Pierrick Bouvier @ 2026-09-14 21:23 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Alex Bennée, sid.manning
On 9/5/2026 11:09 AM, Brian Cain wrote:
> Add round-to-nearest float-to-int conversion tests for the non-:chop variants,
> sfmpy:lib with Inf-Inf to verify exception suppression, and sfinvsqrta
> with a denormal input.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> tests/tcg/hexagon/fpstuff.c | 99 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 99 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH 16/17] tests/tcg/hexagon: add scalar+HVX and masked store tests
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (14 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 15/17] tests/tcg/hexagon: add rounding conv, sffma:lib, and sfinvsqrta Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:24 ` Pierrick Bouvier
2026-09-05 18:09 ` [PATCH 17/17] tests/tcg/hexagon: add decbin MPS test case Brian Cain
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Test a packet with both a scalar store and an HVX store, and test
a non-inverted masked store (if (q0) vmem) to verify the invert=false
path in masked store generation.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/hvx_misc.c | 66 ++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c
index f20afc8e670..0285886c2a5 100644
--- a/tests/tcg/hexagon/hvx_misc.c
+++ b/tests/tcg/hexagon/hvx_misc.c
@@ -566,6 +566,69 @@ void test_store_new()
check_output_w(__LINE__, 1);
}
+/*
+ * Test a packet with both a scalar store and an HVX store.
+ */
+static uint32_t scalar_store_dst;
+
+static void test_scalar_hvx_store(void)
+{
+ scalar_store_dst = 0;
+ memset(&expect[0], 0, sizeof(MMVector));
+
+ /* Fill v0 with 0xABCD pattern */
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0xABCDABCD;
+ }
+
+ asm("r0 = #0xABCDABCD\n\t"
+ "v0 = vsplat(r0)\n\t"
+ "r1 = #42\n\t"
+ "{\n\t"
+ " memw(%[scalar]) = r1\n\t"
+ " vmem(%[hvx]) = v0\n\t"
+ "}\n\t"
+ :
+ : [scalar] "r"(&scalar_store_dst),
+ [hvx] "r"(&output[0])
+ : "r0", "r1", "v0", "memory");
+
+ check_output_w(__LINE__, 1);
+ if (scalar_store_dst != 42) {
+ printf("ERROR at line %d: scalar_store_dst = %d, expected 42\n",
+ __LINE__, scalar_store_dst);
+ err++;
+ }
+}
+
+/*
+ * Test non-inverted masked store: if (Qv) vmem(Rt) = Vs.
+ * Existing tests only cover the inverted form (!Qv).
+ * Use an all-true Q predicate to unconditionally store.
+ */
+static void test_masked_store_noninvert(void)
+{
+ void *p0 = buffer0;
+ void *pout = output;
+
+ memset(&output[0], 0xff, sizeof(MMVector));
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = buffer0[0].uw[i];
+ }
+
+ asm("v5 = vmem(%[src] + #0)\n\t"
+ "r0 = #-1\n\t"
+ "v6 = vsplat(r0)\n\t"
+ "q0 = vand(v6, r0)\n\t"
+ "if (q0) vmem(%[dst]) = v5\n\t"
+ :
+ : [src] "r"(p0), [dst] "r"(pout)
+ : "r0", "v5", "v6", "q0", "memory");
+
+ check_output_w(__LINE__, 1);
+}
+
int main()
{
init_buffers();
@@ -615,6 +678,9 @@ int main()
test_store_new();
+ test_scalar_hvx_store();
+ test_masked_store_noninvert();
+
puts(err ? "FAIL" : "PASS");
return err ? 1 : 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread* [PATCH 17/17] tests/tcg/hexagon: add decbin MPS test case
2026-09-05 18:09 [PATCH 00/17] hexagon: TCG test coverage + ubsan fixes Brian Cain
` (15 preceding siblings ...)
2026-09-05 18:09 ` [PATCH 16/17] tests/tcg/hexagon: add scalar+HVX and masked store tests Brian Cain
@ 2026-09-05 18:09 ` Brian Cain
2026-09-14 21:24 ` Pierrick Bouvier
16 siblings, 1 reply; 35+ messages in thread
From: Brian Cain @ 2026-09-05 18:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Alex Bennée, Brian Cain, sid.manning
Add a decbin test vector exercising the MPS case
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
tests/tcg/hexagon/misc.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tests/tcg/hexagon/misc.c b/tests/tcg/hexagon/misc.c
index abb34ab6a65..08845201b59 100644
--- a/tests/tcg/hexagon/misc.c
+++ b/tests/tcg/hexagon/misc.c
@@ -599,6 +599,15 @@ int main()
res64 = decbin(0xfLL, 0x1bLL, &pred);
check64(res64, 0x78000100LL);
check32(pred, true);
+
+ /*
+ * Test the MPS (most-probable symbol) case in decbin.
+ * Rss: word0=range=0xFF800000, word1=offset=0x00000000
+ * Rtt: word0=bitpos=0, word1=state=0|valMPS=1(bit8)
+ */
+ res64 = decbin(0x00000000FF800000LL, 0x0000010000000000LL, &pred);
+ check64(res64, 0x0000000087800101LL);
+ check32(pred, true);
#else
puts("Skipping cabac tests");
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 35+ messages in thread