Add V6_vconv_qf16_f8 and V6_vconv_f8_qf16, which use FP8 with exponent
bias 7.

Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/mmvec/hvx_ieee_fp.h           |   8 ++
 target/hexagon/mmvec/macros.h                |  20 ++++
 target/hexagon/mmvec/hvx_ieee_fp.c           |  21 ++++
 tests/tcg/hexagon/check_rev_gating.c         |   6 +
 tests/tcg/hexagon/qfloat_v81.c               | 111 +++++++++++++++++++
 target/hexagon/tag_rev_info.c.inc            |   2 +
 target/hexagon/imported/mmvec/encode_ext.def |   2 +
 target/hexagon/imported/mmvec/ext.idef       |   6 +
 8 files changed, 176 insertions(+)

diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h 
b/target/hexagon/mmvec/hvx_ieee_fp.h
index 229ee0ee516..996feae67b3 100644
--- a/target/hexagon/mmvec/hvx_ieee_fp.h
+++ b/target/hexagon/mmvec/hvx_ieee_fp.h
@@ -52,6 +52,14 @@ float16 f8_to_f16(uint8_t a, float_status *fp_status);
  */
 uint8_t f16_to_f8(float16 a, bool sat, float_status *fp_status);
 
+/*
+ * The v81 conversions between FP8 and qf16 read the same encoding with
+ * exponent bias 7 instead of 8, so every value is twice as large.
+ * NaN is still 0x80 and narrowing never saturates.
+ */
+float16 f8_bias7_to_f16(uint8_t a, float_status *fp_status);
+uint8_t f16_to_f8_bias7(float16 a, float_status *fp_status);
+
 float16 fp_add_hf_f8(uint8_t a1, uint8_t a2, float_status *fp_status);
 float16 fp_sub_hf_f8(uint8_t a1, uint8_t a2, float_status *fp_status);
 float16 fp_mult_hf_f8(uint8_t a1, uint8_t a2, float_status *fp_status);
diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h
index 0e643b29109..8ac685bdc57 100644
--- a/target/hexagon/mmvec/macros.h
+++ b/target/hexagon/mmvec/macros.h
@@ -417,6 +417,26 @@
 
 #define fQF_VILOG2(TYPE, A, AEXT) qf_vilog2(TYPE, A, AEXT)
 
+/*
+ * Convert an FP8 element to the qf16 element (V).qf16[i].  FP8 is exactly
+ * representable in hf, so go through that.  The one FP8 NaN becomes the
+ * negative qf16 NaN.
+ */
+#define fCONVERT_F8_TO_QF16(V, A) \
+    do { \
+        if ((A) == F8_NAN) { \
+            (V).qf16[i] = extqf16_neg_nan >> 4; \
+            set_extended_bits(&(V), i, 16, \
+                              extqf16_neg_nan & EXT16_BITMASK); \
+        } else { \
+            unfloat u = fPARSEHF(f8_bias7_to_f16(A, &env->hvx_fp_status)); \
+            INIT_UNFLOAT(v) \
+            v.exp = E_MIN_EXTQF16; \
+            v.sign = 1; \
+            fQFADD(16, V, u, v); \
+        } \
+    } while (0)
+
 #define fCONVERT_QF32_TO_SF(A, AEXT) \
     (qfloat_is_extended(env) ? \
      conv_sf_extqf32(A, AEXT, CVI_QFRND_MODE) : legacy_conv_sf_qf32(A))
diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c 
b/target/hexagon/mmvec/hvx_ieee_fp.c
index 6394c175a13..be8bc9b9485 100644
--- a/target/hexagon/mmvec/hvx_ieee_fp.c
+++ b/target/hexagon/mmvec/hvx_ieee_fp.c
@@ -215,6 +215,27 @@ uint8_t f16_to_f8(float16 a, bool sat, float_status 
*fp_status)
     return code ? sign | code : 0;
 }
 
+float16 f8_bias7_to_f16(uint8_t a, float_status *fp_status)
+{
+    float_status exact = { 0 };
+
+    /* Preserve NaN's default value from fp_status. */
+    return a == F8_NAN ? f8_to_f16(a, fp_status) :
+           float16_mul(f8_to_f16(a, fp_status), make_float16(0x4000), &exact);
+}
+
+uint8_t f16_to_f8_bias7(float16 a, float_status *fp_status)
+{
+    float_status halve = { 0 };
+
+    if (float16_is_any_nan(a)) {
+        return F8_NAN;
+    }
+    /* Halving only loses insignificant bits before FP8 rounding. */
+    return f16_to_f8(float16_mul(a, make_float16(0x3800), &halve), false,
+                     fp_status);
+}
+
 float16 fp_add_hf_f8(uint8_t a1, uint8_t a2, float_status *fp_status)
 {
     return float16_add(f8_to_f16(a1, fp_status), f8_to_f16(a2, fp_status),
diff --git a/tests/tcg/hexagon/check_rev_gating.c 
b/tests/tcg/hexagon/check_rev_gating.c
index df6cb426c62..015ce607ec7 100644
--- a/tests/tcg/hexagon/check_rev_gating.c
+++ b/tests/tcg/hexagon/check_rev_gating.c
@@ -150,6 +150,10 @@ TRY_FUNC(v79hvx_vfmin_f8,
 
 TRY_FUNC(v81hvx_veqhf,
          ".word 0x1f82c11c    /* q0 = vcmp.eq(v1.hf, v2.hf) */\n")
+TRY_FUNC(v81hvx_vconv_qf16_f8,
+         ".word 0x1e0ce2a0    /* v1:0.qf16 = v2.f8 */\n")
+TRY_FUNC(v81hvx_vconv_f8_qf16,
+         ".word 0x1e0ce4e3    /* v3.f8 = v4.qf16 */\n")
 
 int main(void)
 {
@@ -203,6 +207,8 @@ int main(void)
     assert(try_v79hvx_vfmin_f8() == SIGILL);
 
     assert(try_v81hvx_veqhf() == SIGILL);
+    assert(try_v81hvx_vconv_qf16_f8() == SIGILL);
+    assert(try_v81hvx_vconv_f8_qf16() == SIGILL);
 
     assert(signals_handled == expected_signals);
 
diff --git a/tests/tcg/hexagon/qfloat_v81.c b/tests/tcg/hexagon/qfloat_v81.c
index 4fdd4809071..d7db6ccefe9 100644
--- a/tests/tcg/hexagon/qfloat_v81.c
+++ b/tests/tcg/hexagon/qfloat_v81.c
@@ -108,11 +108,122 @@ static void test_qf32_bf_conversion(void)
     check_output_h(__LINE__, 1);
 }
 
+/* v81 FP8/qf16 conversions use exponent bias 7, unlike v79 vcvt. */
+#define F8_NAN 0x80
+#define QF16_NAN 0x801f
+
+static void f8_to_qf16_parts(uint8_t f8, int *sig11, int *exp)
+{
+    int e = (f8 >> 3) & 0xf;
+    int sig = e ? 8 | (f8 & 7) : f8 & 7;
+    int p = 31 - __builtin_clz(sig);
+
+    *exp = p + (e ? e : 1) - 10;
+    *sig11 = sig << (9 - p);
+}
+
+static uint16_t ref_f8_to_qf16(uint8_t f8)
+{
+    int sig11, exp;
+
+    if (f8 == 0) {
+        return 0;
+    }
+    if (f8 == F8_NAN) {
+        return QF16_NAN;
+    }
+    f8_to_qf16_parts(f8, &sig11, &exp);
+    /* qf16 negative significands are complemented. */
+    if (f8 & 0x80) {
+        sig11 = ~sig11 & 0x7ff;
+    }
+    return sig11 << 5 | (exp + 15);
+}
+
+#define WIDEN_VEC(I) ((I) % 2)
+#define WIDEN_IDX(I) (2 * ((I) / 4) + (((I) % 4) / 2))
+
+static void test_vconv_qf16_f8(void)
+{
+    for (int blk = 0; blk < 2; blk++) {
+        MMVector input;
+
+        for (int i = 0; i < MAX_VEC_SIZE_BYTES; i++) {
+            input.ub[i] = blk * MAX_VEC_SIZE_BYTES + i;
+            expect[WIDEN_VEC(i)].uh[WIDEN_IDX(i)] = 
ref_f8_to_qf16(input.ub[i]);
+        }
+        asm volatile(
+            "v2 = vmemu(%[in])\n"
+            "v1:0.qf16 = v2.f8\n"
+            "vmemu(%[out0]) = v0\n"
+            "vmemu(%[out1]) = v1\n"
+            :
+            : [in] "r"(&input), [out0] "r"(&output[0]),
+              [out1] "r"(&output[1])
+            : "v0", "v1", "v2", "memory");
+        check_output_uh(__LINE__, 2);
+    }
+}
+
+/* Round half precision to FP8, nearest-even without saturation. */
+static uint8_t ref_hf_to_f8(uint16_t hf)
+{
+    _Float16 h;
+    double x, ax;
+    int sign = hf >> 15;
+    int code;
+
+    memcpy(&h, &hf, sizeof(h));
+    x = h;
+    if (x != x || __builtin_isinf(x)) {
+        return F8_NAN;
+    }
+    ax = __builtin_fabs(x);
+    if (ax < 1.0 / 64) {
+        /* Subnormal units are 2^-9. */
+        code = __builtin_rint(ax * 512);
+    } else {
+        int e = __builtin_ilogb(ax);
+        int m = __builtin_rint(__builtin_scalbn(ax, 3 - e));
+
+        code = (e + 7) * 8 + m - 8;
+    }
+    if (code > 0x7f) {
+        return F8_NAN;
+    }
+    return code ? sign << 7 | code : 0;
+}
+
+/* Form qf16 values from each half-precision input. */
+static void test_vconv_f8_qf16(void)
+{
+    for (int base = 0; base < 0x10000; base += MAX_VEC_SIZE_BYTES / 2) {
+        MMVector input;
+
+        for (int i = 0; i < MAX_VEC_SIZE_BYTES / 2; i++) {
+            input.uh[i] = base + i;
+            expect[0].uh[i] = ref_hf_to_f8(base + i);
+        }
+        asm volatile(
+            "v2 = vmemu(%[in])\n"
+            "v3 = vxor(v3, v3)\n"
+            "v0.qf16 = vadd(v2.hf, v3.hf)\n"
+            "v1.f8 = v0.qf16\n"
+            "vmemu(%[out]) = v1\n"
+            :
+            : [in] "r"(&input), [out] "r"(&output[0])
+            : "v0", "v1", "v2", "v3", "memory");
+        check_output_uh(__LINE__, 1);
+    }
+}
+
 int main()
 {
     test_qf16();
     test_qf32();
     test_qf32_bf_conversion();
+    test_vconv_qf16_f8();
+    test_vconv_f8_qf16();
 
     puts(err ? "FAIL" : "PASS");
     return err ? 1 : 0;
diff --git a/target/hexagon/tag_rev_info.c.inc 
b/target/hexagon/tag_rev_info.c.inc
index 92559052516..61ada1f7bf9 100644
--- a/target/hexagon/tag_rev_info.c.inc
+++ b/target/hexagon/tag_rev_info.c.inc
@@ -713,6 +713,8 @@ static const struct tag_rev_info 
tag_rev_info[XX_LAST_OPCODE] = {
     [V6_vconv_h_hf_rnd] = {
         .introduced = HEX_VER_V81, .removed = HEX_VER_NONE
     },
+    [V6_vconv_f8_qf16] = { .introduced = HEX_VER_V81, .removed = HEX_VER_NONE 
},
+    [V6_vconv_qf16_f8] = { .introduced = HEX_VER_V81, .removed = HEX_VER_NONE 
},
     [V6_vconv_bf_qf32] = { .introduced = HEX_VER_V81, .removed = HEX_VER_NONE 
},
 
     [V2_vallocframe_imm] = { .introduced = HEX_VER_V83,
diff --git a/target/hexagon/imported/mmvec/encode_ext.def 
b/target/hexagon/imported/mmvec/encode_ext.def
index 5941e09df93..7139be12e59 100644
--- a/target/hexagon/imported/mmvec/encode_ext.def
+++ b/target/hexagon/imported/mmvec/encode_ext.def
@@ -928,7 +928,9 @@ DEF_ENC(V6_vconv_bf_qf32,"00011110--0-0110PP1uuuuu111ddddd")
 DEF_ENC(V6_vconv_hf_qf16,"00011110--0-0100PP1uuuuu011ddddd")
 DEF_ENC(V6_vconv_hf_qf32,"00011110--0-0100PP1uuuuu110ddddd")
 DEF_ENC(V6_vconv_qf16_hf,"00011110--0-1100PP1uuuuu100ddddd")
+DEF_ENC(V6_vconv_qf16_f8,"00011110--0-1100PP1uuuuu101ddddd")
 DEF_ENC(V6_vconv_qf16_qf16,"00011110--0-1100PP1uuuuu110ddddd")
+DEF_ENC(V6_vconv_f8_qf16,"00011110--0-1100PP1uuuuu111ddddd")
 DEF_ENC(V6_vconv_qf32_qf32,"00011110--0-1101PP1uuuuu111ddddd")
 DEF_ENC(V6_vconv_qf32_sf,"00011110--0-1101PP1uuuuu110ddddd")
 DEF_ENC(V6_vconv_sf_qf32,"00011110--0-0100PP1uuuuu000ddddd")
diff --git a/target/hexagon/imported/mmvec/ext.idef 
b/target/hexagon/imported/mmvec/ext.idef
index 1b8afee8b6f..aa74f4f423a 100644
--- a/target/hexagon/imported/mmvec/ext.idef
+++ b/target/hexagon/imported/mmvec/ext.idef
@@ -3519,6 +3519,12 @@ 
EXTINSN(V6_vconv_hf_qf32,"Vd32.hf=Vuu32.qf32",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS
 
EXTINSN(V6_vconv_qf16_hf,"Vd32.qf16=Vu32.hf",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_HVX_FLT),"Vector
 conversion of qf16 format to ieee HF",
 { fHIDE(int i;) fVFOREACH(16, i) { fHIDE(unfloat )u = fPARSEHF(VuV.hf[i]); 
INIT_UNFLOAT(v) v.exp = E_MIN_EXTQF16; v.sign = 1; fQFADD(16,VdV,u,v)  ; } })
 
+EXTINSN(V6_vconv_qf16_f8,"Vdd32.qf16=Vu32.f8",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VP_VS,A_HVX_FLT),"Vector
 convert to qf16: f8 widen to qf16",
+{ fHIDE(int i;) fVFOREACH(16, i) { fCONVERT_F8_TO_QF16(VddV.v[0], 
VuV.f8[2*i]); fCONVERT_F8_TO_QF16(VddV.v[1], VuV.f8[2*i+1]); } })
+
+EXTINSN(V6_vconv_f8_qf16,"Vd32.f8=Vu32.qf16",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_HVX_FLT),"Vector
 convert qf16 to f8",
+{ fHIDE(int i;) fVFOREACH(16, i) { VdV.uh[i] = 
f16_to_f8_bias7(fCONVERT_QF16_TO_HF(VuV.qf16[i], GET_VEXT(VuV, i, 16)), 
&env->hvx_fp_status); } })
+
 
EXTINSN(V6_vconv_qf16_qf16,"Vd32.qf16=Vu32.qf16",ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_HVX_FLT),"Vector
 conversion of qf16 to qf16",
 { fHIDE(int i;) fVFOREACH(16, i) { fHIDE(unfloat )u = fPARSEQF_EXT(16,VuV,i); 
INIT_UNFLOAT(v) v.exp = E_MIN_EXTQF16; v.sign = 1; fQFADD(16,VdV,u,v)  ; } })
 
-- 
2.34.1

Reply via email to