Reviewed-by: Marco Liebel <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
tests/tcg/hexagon/check_rev_gating.c | 12 +-
tests/tcg/hexagon/hvx_ext.c | 248 +++++++++++++++++++++++++
tests/tcg/hexagon/qfloat_v81.c | 119 ++++++++++++
tests/tcg/hexagon/v81_hvx.c | 259 +++++++++++++++++++++++++++
tests/tcg/hexagon/meson.build | 25 +++
5 files changed, 662 insertions(+), 1 deletion(-)
create mode 100644 tests/tcg/hexagon/hvx_ext.c
create mode 100644 tests/tcg/hexagon/qfloat_v81.c
create mode 100644 tests/tcg/hexagon/v81_hvx.c
diff --git a/tests/tcg/hexagon/check_rev_gating.c
b/tests/tcg/hexagon/check_rev_gating.c
index 3f48ab9cd35..ad22a93bdaf 100644
--- a/tests/tcg/hexagon/check_rev_gating.c
+++ b/tests/tcg/hexagon/check_rev_gating.c
@@ -3,7 +3,7 @@
* are rejected with SIGILL.
*
* Compiled with -mv66 so that e_flags selects CPU v66. The test embeds
- * instructions from v68 up through v79 via .word encoding: the
+ * instructions from v68 up through v81 via .word encoding: the
* assembler enforces the selected CPU's own minimum version, so none
* of these -- including ones it otherwise knows how to assemble at
* their own target, such as callrh or unpause -- can be written as
@@ -122,6 +122,12 @@ TRY_FUNC(v79_dczeroa_nt,
TRY_FUNC(v79_dcfetchbo_nt,
".word 0x9402e000 /* dcfetch(r2+#0):nt */\n")
+TRY_FUNC(v79hvx_vmerge_qf,
+ ".word 0x1f01e0e2 /* v2 = vmerge(v0.x, v1.w) */\n")
+
+TRY_FUNC(v81hvx_veqhf,
+ ".word 0x1f82c11c /* q0 = vcmp.eq(v1.hf, v2.hf) */\n")
+
int main(void)
{
struct sigaction act;
@@ -160,6 +166,10 @@ int main(void)
assert(try_v79_dczeroa_nt() == SIGILL);
assert(try_v79_dcfetchbo_nt() == SIGILL);
+ assert(try_v79hvx_vmerge_qf() == SIGILL);
+
+ assert(try_v81hvx_veqhf() == SIGILL);
+
assert(signals_handled == expected_signals);
puts("PASS");
diff --git a/tests/tcg/hexagon/hvx_ext.c b/tests/tcg/hexagon/hvx_ext.c
new file mode 100644
index 00000000000..e3747d87b76
--- /dev/null
+++ b/tests/tcg/hexagon/hvx_ext.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Tests for HVX qfloat extended-precision bits: vsetqfext/
+ * vgetqfext, vmerge's ext-bit passthrough, and the "any non-qfloat write
+ * resets a vector's ext bits to the V_EXTENDED_BYTEVAL sentinel" rule --
+ * including through .cur/.tmp loads and predicated/packet-internal
+ * forwarding, where it's easy to get the reset timing wrong.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/auxv.h>
+#include <elf.h>
+#include <hexagon_types.h>
+#include <hvx_hexagon_protos.h>
+
+static int err;
+static bool extended_qfloat;
+
+#include "hvx_misc.h"
+
+static unsigned long hwcap(void)
+{
+ return getauxval(AT_HWCAP);
+}
+
+static void test_qf32_mpy(void)
+{
+ HVX_Vector input = Q6_V_vsplat_R(-1);
+ HVX_Vector result = Q6_Vqf32_vmpy_Vqf32Vqf32(input, input);
+
+ memcpy(output, &result, sizeof(result));
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = extended_qfloat ? 0x0000007f : 0x7fffffff;
+ }
+
+ check_output_w(__LINE__, 1);
+}
+
+static void test_ext_bits_reset_on_copy(void)
+{
+ /* Fixed register identities are part of what this test exercises. */
+ asm volatile(
+ "r0 = #0x11111111\n"
+ "v10 = vsplat(r0)\n"
+ "v0.x = vsetqfext(v10, r0)\n"
+ "v1.x = vsetqfext(v10, r0)\n"
+ "v2 = vgetqfext(v0.x, r0)\n"
+ "v3 = vgetqfext(v1.x, r0)\n"
+ "v1 = v0\n"
+ "v4 = vgetqfext(v0.x, r0)\n"
+ "v5 = vgetqfext(v1.x, r0)\n"
+ "vmemu(%[out0]) = v2\n"
+ "vmemu(%[out1]) = v3\n"
+ "vmemu(%[out2]) = v4\n"
+ "vmemu(%[out3]) = v5\n"
+ :
+ : [out0] "r"(&output[0]), [out1] "r"(&output[1]),
+ [out2] "r"(&output[2]), [out3] "r"(&output[3])
+ : "r0", "v0", "v1", "v2", "v3", "v4", "v5", "v10", "memory");
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = expect[1].uw[i] = expect[2].uw[i] = 0x11111111;
+ expect[3].uw[i] = 0x11001100;
+ }
+
+ check_output_w(__LINE__, 4);
+}
+
+static void test_ext_bits_reset_multiple_insns(void)
+{
+ asm volatile(
+ "r1 = #0xe60e31d4\n"
+ "r2 = #0xbaf1fa15\n"
+ "r3 = #0x4a777c7b\n"
+ "v1 = vsplat(r1)\n"
+ "v2 = vsplat(r2)\n"
+ "v3 = vsplat(r3)\n"
+
+ "{\n"
+ " v2 = v1\n"
+ " v4.sf = vmax(v3.sf,v2.sf)\n"
+ "}\n"
+ "v5.hf = v4.qf16\n"
+ "vmemu(%[out]) = v5\n"
+ :
+ : [out] "r"(output)
+ : "r1", "r2", "r3", "v1", "v2", "v3", "v4", "v5", "memory");
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0x5ca76fc7; /* reference from the sim */
+ }
+
+ check_output_w(__LINE__, 1);
+}
+
+static void test_ext_bits_reset_interleaved_qf(void)
+{
+ HVX_Vector v1 = Q6_V_vsplat_R(0x00000057);
+ HVX_Vector v2 = v1;
+ HVX_Vector v3 = Q6_V_vsplat_R(0x2a8b9bf4);
+ HVX_Vector v4 = v3;
+ HVX_Vector v5 = Q6_Vqf32_vadd_VsfVsf(v2, v4);
+ HVX_Vector v6;
+
+ v5 = Q6_Vhf_vmin_VhfVhf(v1, v3);
+ v6 = Q6_Vqf16_vmpy_Vqf16Vhf(v5, v1);
+ memcpy(output, &v6, sizeof(v6));
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0x0000f766; /* reference from the sim */
+ }
+
+ check_output_w(__LINE__, 1);
+}
+
+static void test_ext_bits_preserved_on_false_predicate(void)
+{
+ asm volatile(
+ "r0 = #0x11111111\n"
+ "v3 = vsplat(r0)\n"
+ "v0.x = vsetqfext(v3, r0)\n"
+ "r2 = #1\n"
+ "v1 = vsplat(r2)\n"
+ "r1 = #0\n"
+ "p0 = cmp.eq(r1, r2)\n"
+ "{\n"
+ " if (p0) v0 = v1\n"
+ "}\n"
+ "v2 = vgetqfext(v0.x, r0)\n"
+ "vmemu(%[out]) = v2\n"
+ :
+ : [out] "r"(output)
+ : "r0", "r1", "r2", "v0", "v1", "v2", "v3", "p0", "memory");
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0x11111111;
+ }
+ check_output_w(__LINE__, 1);
+}
+
+static void test_qfloat_semantics(void)
+{
+ HVX_Vector a = Q6_V_vsplat_R(0xfde1d91d);
+ HVX_Vector b = Q6_V_vsplat_R(0xffffff80);
+ HVX_Vector result = Q6_Vqf32_vsub_Vqf32Vqf32(a, b);
+
+ memcpy(output, &result, sizeof(result));
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = extended_qfloat ? 0x7fffff68 : 0x3fffff69;
+ }
+
+ check_output_w(__LINE__, 1);
+}
+
+static void test_qfloat_with_cur(void)
+{
+ memset(buffer0, 0xff, sizeof(buffer0));
+ asm volatile(
+ "r0 = #0x11111111\n"
+ "v10 = vsplat(r0)\n"
+
+ "r1 = #0xffffff80\n"
+ "v1 = vsplat(r1)\n"
+
+ /* tweak ext bits */
+ "v0.x = vsetqfext(v10, r0)\n"
+ "v2 = vmerge(v0.x, v1.w)\n"
+
+ "{\n"
+ " v2.cur = vmem(%[buf]++#0)\n"
+ " v3.qf32=vsub(v2.qf32, v1.qf32)\n"
+ "}\n"
+ "vmemu(%[out]) = v3\n"
+ :
+ : [buf] "r"(buffer0), [out] "r"(output)
+ : "r0", "r1", "v0", "v1", "v2", "v3", "v10", "memory");
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0x800000e7; /* reference from the sim */
+ }
+
+ check_output_w(__LINE__, 1);
+}
+
+static void test_qfloat_with_tmp(void)
+{
+ memset(buffer0, 0xff, sizeof(buffer0));
+ asm volatile(
+ "r0 = #0x11111111\n"
+ "v10 = vsplat(r0)\n"
+
+ "r1 = #0xffffff80\n"
+ "v1 = vsplat(r1)\n"
+
+ /* tweak ext bits */
+ "v0.x = vsetqfext(v10, r0)\n"
+ "v2 = vmerge(v0.x, v1.w)\n"
+
+ "{\n"
+ " v3.tmp = vmem(%[buf]++#0)\n"
+ " v3.qf32=vsub(v2.qf32, v1.qf32)\n"
+ " v4 = v3\n"
+ "}\n"
+ "vmemu(%[out0]) = v3\n"
+
+ "v10 = vgetqfext(v3.x, r0)\n"
+ "vmemu(%[out1]) = v10\n"
+ :
+ : [buf] "r"(buffer0), [out0] "r"(&output[0]), [out1] "r"(&output[1])
+ : "r0", "r1", "v0", "v1", "v2", "v3", "v4", "v10", "memory");
+
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[0].uw[i] = 0x00000080; /* reference from the sim */
+ expect[1].uw[i] = 0x00111111; /* tmp should not reset v3's ext bits */
+ }
+
+ check_output_w(__LINE__, 2);
+}
+
+int main()
+{
+ extended_qfloat = HWCAP_HEXAGON_HAS_ISA(hwcap(),
+ HWCAP_HEXAGON_ISA_V79);
+
+ test_qf32_mpy();
+ if (extended_qfloat) {
+ test_ext_bits_reset_on_copy();
+ test_ext_bits_preserved_on_false_predicate();
+ test_ext_bits_reset_interleaved_qf();
+ test_qfloat_with_cur();
+ test_qfloat_with_tmp();
+ }
+ test_ext_bits_reset_multiple_insns();
+ test_qfloat_semantics();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/qfloat_v81.c b/tests/tcg/hexagon/qfloat_v81.c
new file mode 100644
index 00000000000..4fdd4809071
--- /dev/null
+++ b/tests/tcg/hexagon/qfloat_v81.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <hexagon_types.h>
+#include <hvx_hexagon_protos.h>
+
+static int err;
+
+#include "hvx_misc.h"
+
+static void expect_h(int output_index, uint16_t value)
+{
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ expect[output_index].uh[i] = value;
+ }
+}
+
+static void expect_w(int output_index, uint32_t value)
+{
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) {
+ expect[output_index].uw[i] = value;
+ }
+}
+
+static void test_qf16(void)
+{
+ asm volatile(
+ "r0 = #0x3c003c00\n"
+ "v1 = vsplat(r0)\n"
+ "r0 = #0x40004000\n"
+ "v2 = vsplat(r0)\n"
+ "v0.qf16 = vadd(v1.hf, v2.hf)\n"
+ "v3.hf = v0.qf16\n"
+ "r0 = #0x10001000\n"
+ "v2 = vsplat(r0)\n"
+ "v0.qf16 = vadd(v1.hf, v2.hf)\n"
+ "v5.hf = v0.qf16\n"
+ "r0 = #0xc000c000\n"
+ "v1 = vsplat(r0)\n"
+ "v0.qf16 = vabs(v1.hf)\n"
+ "v6.hf = v0.qf16\n"
+ "r0 = #0x40004000\n"
+ "v1 = vsplat(r0)\n"
+ "v0.qf16 = vneg(v1.hf)\n"
+ "v7.hf = v0.qf16\n"
+ "vmemu(%[out0]) = v3\n"
+ "vmemu(%[out1]) = v5\n"
+ "vmemu(%[out2]) = v6\n"
+ "vmemu(%[out3]) = v7\n"
+ :
+ : [out0] "r"(&output[0]), [out1] "r"(&output[1]),
+ [out2] "r"(&output[2]), [out3] "r"(&output[3])
+ : "r0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+ "memory");
+
+ expect_h(0, 0x4200);
+ expect_h(1, 0x3c00);
+ expect_h(2, 0x4000);
+ expect_h(3, 0xc002);
+ check_output_h(__LINE__, 4);
+}
+
+static void test_qf32(void)
+{
+ asm volatile(
+ "r0 = #0x3f800000\n"
+ "v1 = vsplat(r0)\n"
+ "r0 = #0x40000000\n"
+ "v2 = vsplat(r0)\n"
+ "v0.qf32 = vadd(v1.sf, v2.sf)\n"
+ "v3.sf = v0.qf32\n"
+ "v4.w = vilog2(v0.qf32)\n"
+ "r0 = #0x7f800000\n"
+ "v1 = vsplat(r0)\n"
+ "r0 = #0xff800000\n"
+ "v2 = vsplat(r0)\n"
+ "v0.qf32 = vadd(v1.sf, v2.sf)\n"
+ "v5.sf = v0.qf32\n"
+ "vmemu(%[out0]) = v3\n"
+ "vmemu(%[out1]) = v4\n"
+ "vmemu(%[out2]) = v5\n"
+ :
+ : [out0] "r"(&output[0]), [out1] "r"(&output[1]),
+ [out2] "r"(&output[2])
+ : "r0", "v0", "v1", "v2", "v3", "v4", "v5", "memory");
+
+ expect_w(0, 0x40400000);
+ expect_w(1, 1);
+ expect_w(2, 0x7fffffff);
+ check_output_w(__LINE__, 3);
+}
+
+static void test_qf32_bf_conversion(void)
+{
+ HVX_Vector one = Q6_V_vsplat_R(0x3c003c00);
+ HVX_VectorPair qf = Q6_Wqf32_vmpy_VhfVhf(one, one);
+ HVX_Vector result = Q6_Vbf_equals_Wqf32(qf);
+
+ memcpy(output, &result, sizeof(result));
+ for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+ expect[0].uh[i] = 0x3f80;
+ }
+ check_output_h(__LINE__, 1);
+}
+
+int main()
+{
+ test_qf16();
+ test_qf32();
+ test_qf32_bf_conversion();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/v81_hvx.c b/tests/tcg/hexagon/v81_hvx.c
new file mode 100644
index 00000000000..226dc931d02
--- /dev/null
+++ b/tests/tcg/hexagon/v81_hvx.c
@@ -0,0 +1,259 @@
+/*
+ * Test the HVX instructions that are new in v81:
+ * - V6_veqhf/V6_veqsf (and the _and/_or/_xor predicate-accumulate forms)
+ * - V6_valign4
+ * - V6_vconv_h_hf_rnd
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <hexagon_types.h>
+#include <hvx_hexagon_protos.h>
+
+int err;
+#include "hvx_misc.h"
+#include "hex_test.h"
+
+#define MAX_TESTS_hf (MAX_VEC_SIZE_BYTES / 2)
+#define MAX_TESTS_sf (MAX_VEC_SIZE_BYTES / 4)
+
+#define TRUE_MASK_sf 0xffffffff
+#define TRUE_MASK_hf 0xffff
+
+static const char *comparisons[MAX_TESTS_sf][2];
+static HVX_Vector *hvx_output = (HVX_Vector *)&output[0];
+static HVX_Vector buffers[2], true_vec, false_vec;
+static int exp_index;
+
+#define ADD_TEST_CMP(TYPE, VAL1, VAL2, EXP) do { \
+ assert(exp_index < MAX_TESTS_##TYPE); \
+ ((MMVector *)&buffers[0])->TYPE[exp_index] = VAL1; \
+ ((MMVector *)&buffers[1])->TYPE[exp_index] = VAL2; \
+ expect[0].TYPE[exp_index] = EXP ? TRUE_MASK_##TYPE : 0; \
+ comparisons[exp_index][0] = #VAL1; \
+ comparisons[exp_index][1] = #VAL2; \
+ exp_index++; \
+} while (0)
+
+#define TEST_CMP_EQ(TYPE, VAL1, VAL2, EXP) do { \
+ ADD_TEST_CMP(TYPE, VAL1, VAL2, EXP); \
+ ADD_TEST_CMP(TYPE, VAL2, VAL1, EXP); \
+} while (0)
+
+#define PREP_TEST() do { \
+ memset(&buffers, 0, sizeof(buffers)); \
+ memset(expect, 0, sizeof(expect)); \
+ exp_index = 0; \
+} while (0)
+
+#define CHECK(TYPE, TYPESZ) do { \
+ HVX_VectorPred pred = \
+ Q6_Q_vcmp_eq_V##TYPE##V##TYPE(buffers[0], buffers[1]); \
+ *hvx_output = Q6_V_vmux_QVV(pred, true_vec, false_vec); \
+ for (int j = 0; j < exp_index; j++) { \
+ if (output[0].TYPE[j] != expect[0].TYPE[j]) { \
+ printf("ERROR: expected %s %s %s\n", comparisons[j][0], \
+ (expect[0].TYPE[j] != 0 ? "==" : "!="), comparisons[j][1]);
\
+ err++; \
+ } \
+ } \
+} while (0)
+
+static void test_cmp_eq_sf(void)
+{
+ PREP_TEST();
+ TEST_CMP_EQ(sf, raw_sf(2.2), raw_sf(2.2), true);
+ TEST_CMP_EQ(sf, raw_sf(2.2), raw_sf(2.1), false);
+ TEST_CMP_EQ(sf, SF_zero, SF_zero_neg, true);
+ CHECK(sf, 4);
+
+ /* NaNs never compare equal, even to themselves */
+ PREP_TEST();
+ TEST_CMP_EQ(sf, SF_QNaN, SF_QNaN, false);
+ TEST_CMP_EQ(sf, SF_SNaN, SF_SNaN, false);
+ TEST_CMP_EQ(sf, SF_QNaN, SF_SNaN, false);
+ TEST_CMP_EQ(sf, SF_QNaN, SF_one, false);
+ TEST_CMP_EQ(sf, SF_INF, SF_QNaN, false);
+ CHECK(sf, 4);
+
+ /* Infinities of like sign compare equal */
+ PREP_TEST();
+ TEST_CMP_EQ(sf, SF_INF, SF_INF, true);
+ TEST_CMP_EQ(sf, SF_INF_neg, SF_INF_neg, true);
+ TEST_CMP_EQ(sf, SF_INF, SF_INF_neg, false);
+ CHECK(sf, 4);
+}
+
+static void test_cmp_eq_hf(void)
+{
+ PREP_TEST();
+ TEST_CMP_EQ(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.2), true);
+ TEST_CMP_EQ(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.1), false);
+ TEST_CMP_EQ(hf, (uint16_t)0, (uint16_t)0x8000, true);
+ CHECK(hf, 2);
+
+ /* NaNs never compare equal, even to themselves */
+ PREP_TEST();
+ TEST_CMP_EQ(hf, HF_QNaN, HF_QNaN, false);
+ TEST_CMP_EQ(hf, HF_SNaN, HF_SNaN, false);
+ TEST_CMP_EQ(hf, HF_QNaN, HF_SNaN, false);
+ TEST_CMP_EQ(hf, HF_QNaN, HF_one, false);
+ TEST_CMP_EQ(hf, HF_INF, HF_QNaN, false);
+ CHECK(hf, 2);
+
+ /* Infinities of like sign compare equal */
+ PREP_TEST();
+ TEST_CMP_EQ(hf, HF_INF, HF_INF, true);
+ TEST_CMP_EQ(hf, HF_INF_neg, HF_INF_neg, true);
+ TEST_CMP_EQ(hf, HF_INF, HF_INF_neg, false);
+ CHECK(hf, 2);
+}
+
+static void check_byte_pred(HVX_VectorPred pred, int byte_idx, uint8_t
exp_mask,
+ int line)
+{
+ /*
+ * Note: ((uint8_t *)&pred)[N] returns the expanded value of bit N:
+ * 0xFF if bit is set, 0x00 if clear.
+ */
+ for (int i = 0; i < 8; i++) {
+ int idx = byte_idx * 8 + i;
+ int val = ((uint8_t *)&pred)[idx];
+ int exp = (exp_mask >> i) & 1 ? 0xff : 0x00;
+ if (exp != val) {
+ printf("ERROR line %d: pred bit %d is 0x%x, should be 0x%x\n",
+ line, idx, val, exp);
+ err++;
+ }
+ }
+}
+
+#define CHECK_BYTE_PRED(PRED, BYTE, EXP) \
+ check_byte_pred(PRED, BYTE, EXP, __LINE__)
+
+static void test_cmp_eq_variants(void)
+{
+ HVX_VectorPred pred;
+
+ /*
+ * Setup: comparison result will have bits 4-7 set (0xF0 in pred byte 0)
+ * - sf[0]: SF_zero == SF_one = false -> bits 0-3 = 0
+ * - sf[1]: SF_one == SF_one = true -> bits 4-7 = 1
+ */
+ PREP_TEST();
+ ADD_TEST_CMP(sf, SF_zero, SF_one, false);
+ ADD_TEST_CMP(sf, SF_one, SF_one, true);
+
+ /* equal and: 0xF0 & 0xF0 = 0xF0 */
+ memset(&pred, 0xF0, sizeof(pred));
+ pred = Q6_Q_vcmp_eqand_QVsfVsf(pred, buffers[0], buffers[1]);
+ CHECK_BYTE_PRED(pred, 0, 0xF0);
+
+ /* equal or: 0x0F | 0xF0 = 0xFF */
+ memset(&pred, 0x0F, sizeof(pred));
+ pred = Q6_Q_vcmp_eqor_QVsfVsf(pred, buffers[0], buffers[1]);
+ CHECK_BYTE_PRED(pred, 0, 0xFF);
+
+ /* equal xor: 0xFF ^ 0xF0 = 0x0F */
+ memset(&pred, 0xFF, sizeof(pred));
+ pred = Q6_Q_vcmp_eqxacc_QVsfVsf(pred, buffers[0], buffers[1]);
+ CHECK_BYTE_PRED(pred, 0, 0x0F);
+}
+
+static void test_valign4(void)
+{
+ HVX_Vector vu = Q6_V_vsplat_R(0x11111111);
+ HVX_Vector vv = Q6_V_vsplat_R(0x22222222);
+
+ static const uint32_t expected[4] = {
+ 0x11111111, /* Rt & 0x3 == 0: no shift */
+ 0x11111122, /* Rt & 0x3 == 1: shift by 8 */
+ 0x11112222, /* Rt & 0x3 == 2: shift by 16 */
+ 0x11222222, /* Rt & 0x3 == 3: shift by 24 */
+ };
+
+ for (int rt = 0; rt < 4; rt++) {
+ HVX_Vector result = Q6_V_valign4_VVR(vu, vv, rt);
+ MMVector *r = (MMVector *)&result;
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, rt, j, r->uw[j], expected[rt]);
+ }
+ }
+}
+
+static void test_vconv_h_hf_rnd(void)
+{
+ /*
+ * V6_vconv_h_hf_rnd always rounds to nearest-even, regardless of
+ * USR.FPRND. Verified against hexagon-sim: all four FPRND settings
+ * (nearest-even, toward-zero, toward -inf, toward +inf) produce
+ * identical results for this instruction.
+ */
+ uint32_t usr;
+ HVX_Vector input = Q6_V_vsplat_R(
+ ((uint32_t)raw_hf((_Float16)1.5) << 16) | raw_hf((_Float16)(-1.5)));
+ HVX_Vector result = Q6_Vh_equals_Vhf_rnd(input);
+ MMVector *r = (MMVector *)&result;
+
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, 0, 2 * j, r->h[2 * j], -2);
+ check(__LINE__, 0, 2 * j + 1, r->h[2 * j + 1], 2);
+ }
+
+ asm("%0 = usr" : "=r"(usr));
+ usr = (usr & ~(3u << 22)) | (1u << 22); /* round toward zero */
+ asm("usr = %0" : : "r"(usr) : "usr");
+ result = Q6_Vh_equals_Vhf_rnd(input);
+ r = (MMVector *)&result;
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, 0, 2 * j, r->h[2 * j], -2);
+ check(__LINE__, 0, 2 * j + 1, r->h[2 * j + 1], 2);
+ }
+
+ usr = (usr & ~(3u << 22)) | (2u << 22); /* round toward -infinity */
+ asm("usr = %0" : : "r"(usr) : "usr");
+ result = Q6_Vh_equals_Vhf_rnd(input);
+ r = (MMVector *)&result;
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, 0, 2 * j, r->h[2 * j], -2);
+ check(__LINE__, 0, 2 * j + 1, r->h[2 * j + 1], 2);
+ }
+
+ usr = (usr & ~(3u << 22)) | (3u << 22); /* round toward +infinity */
+ asm("usr = %0" : : "r"(usr) : "usr");
+ result = Q6_Vh_equals_Vhf_rnd(input);
+ r = (MMVector *)&result;
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, 0, 2 * j, r->h[2 * j], -2);
+ check(__LINE__, 0, 2 * j + 1, r->h[2 * j + 1], 2);
+ }
+
+ input = Q6_V_vsplat_R(((uint32_t)0x7e00 << 16) | 0xfe00);
+ result = Q6_Vh_equals_Vhf_rnd(input);
+ r = (MMVector *)&result;
+ for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
+ check(__LINE__, 0, 2 * j, r->h[2 * j], INT16_MIN);
+ check(__LINE__, 0, 2 * j + 1, r->h[2 * j + 1], INT16_MAX);
+ }
+}
+
+int main(void)
+{
+ memset(&true_vec, 0xff, sizeof(true_vec));
+ memset(&false_vec, 0, sizeof(false_vec));
+
+ test_cmp_eq_sf();
+ test_cmp_eq_hf();
+ test_cmp_eq_variants();
+ test_valign4();
+ test_vconv_h_hf_rnd();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index c46d90e3954..a6ace4c77c2 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -42,6 +42,16 @@ tests += {
'qemu_args': ['-cpu', 'v73,ieee-fp=false']},
'fp_hvx_cvt.c': {'cflags': [cflags, '-mv73', '-mhvx', '-mhvx-ieee-fp']},
'fp_hvx_cmp.c': {'cflags': [cflags, '-mv73', '-mhvx', '-mhvx-ieee-fp']},
+ 'v81_hvx.c': {'cflags': [cflags, '-mv81', '-mhvx', '-mhvx-ieee-fp'],
+ 'qemu_args': ['-cpu', 'v81']},
+ 'qfloat_v81.c': {'cflags': [cflags, '-mv81', '-mhvx', '-mhvx-ieee-fp'],
+ 'qemu_args': ['-cpu', 'v81']},
+ # Run the QFloat test below and above the v79 extended-QFloat boundary.
+ 'hvx_ext.c': {
+ 'cflags': [cflags, '-mv79', '-mhvx', '-mhvx-ieee-fp'],
+ 'qemu_args': ['-cpu', 'v68'],
+ 'test_name': 'hvx_ext-v68',
+ },
'hvx_misc.c': {'cflags': [cflags, '-mhvx', '-mv69']},
'hvx_histogram.c': {
'cflags': [cflags, '-mhvx', '-Wno-gnu-folding-constant',
@@ -111,6 +121,21 @@ tests += {
'vector_add_int.c': {'cflags': [cflags, '-mhvx', '-fvectorize']},
}
+tests += [
+ {
+ 'hvx_ext.c': {
+ 'qemu_args': ['-cpu', 'v79'],
+ 'test_name': 'hvx_ext-v79',
+ },
+ },
+ {
+ 'hvx_ext.c': {
+ 'qemu_args': ['-cpu', 'v81'],
+ 'test_name': 'hvx_ext-v81',
+ },
+ },
+]
+
if 'qemu-hexagon' in emulators
tcg_tests += {
'hexagon-linux-user': {
--
2.34.1