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

Reply via email to