mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-10-28 20:34:43 +08:00
target/hexagon: add the QFloat logic
Add routines to handle QFloat/HVX floating point values. Also: add documentation to describe the feature. Reviewed-by: Marco Liebel <marco.liebel@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
This commit is contained in:
1 parent
ea6d8b429d
commit
2633da1e12
11 files changed
+1049
-24
No files matched your search
@@ -5,11 +5,13 @@
|
||||
Hexagon CPU architecture support
|
||||
================================
|
||||
|
||||
QEMU's TCG emulation includes support for v65, v66, v67, v68, v69, v71, v73.
|
||||
QEMU's TCG emulation includes support for v65, v66, v67, v68, v69, v71, v73,
|
||||
v75, v77, v79, v81.
|
||||
It also has support for the following architecture extensions:
|
||||
|
||||
- HVX (Hexagon Vector eXtensions)
|
||||
- `HVX floating point <https://docs.qualcomm.com/doc/80-N2040-61/topic/hvx-floating-point.html>`_.
|
||||
|
||||
For information on the specifics of the HVX extension, please refer
|
||||
to the `Qualcomm Hexagon V73 HVX Programmer's Reference Manual
|
||||
<https://docs.qualcomm.com/bundle/publicresource/80-N2040-53.pdf>`_.
|
||||
to the `Qualcomm Hexagon V79 HVX Programmer's Reference Manual
|
||||
<https://docs.qualcomm.com/doc/80-N2040-61/>`_.
|
||||
@@ -30,7 +30,7 @@ static const VMStateDescription vmstate_mmvector = {
|
||||
.minimum_version_id = 1,
|
||||
.fields = (const VMStateField[]){
|
||||
VMSTATE_UINT64_ARRAY(ud, MMVector, MAX_VEC_SIZE_BYTES / 8),
|
||||
VMSTATE_UINT8_ARRAY(ext, MMVector, MAX_VEC_SIZE_BYTES / 4),
|
||||
VMSTATE_UINT8_ARRAY_V(ext, MMVector, MAX_VEC_SIZE_BYTES / 4, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ def gen_disabled_ieee_insn(f, tag, regs):
|
||||
for regtype, regid in regs:
|
||||
reg = hex_common.get_register(tag, regtype, regid)
|
||||
if reg.is_hvx_reg() and reg.is_written():
|
||||
reg.gen_zero(f)
|
||||
reg.gen_zero(f, tag)
|
||||
f.write(" return;\n")
|
||||
f.write(" }\n")
|
||||
|
||||
|
||||
@@ -448,16 +448,61 @@ class Hvx:
|
||||
return "void *"
|
||||
def helper_arg_name(self):
|
||||
return f"{self.reg_tcg()}_void"
|
||||
def gen_clear_ext(self, f):
|
||||
def gen_clear_ext(self, f, tag):
|
||||
predicate = None
|
||||
guard = ""
|
||||
end_guard = ""
|
||||
if is_predicated(tag):
|
||||
for regtype, regid in compute_tag_regs(tag, False):
|
||||
if regtype == "P":
|
||||
predicate = get_register(tag, regtype, regid).reg_tcg()
|
||||
break
|
||||
assert predicate is not None
|
||||
cond = ("TCG_COND_NE" if "fLSBOLDNOT" in semdict[tag]
|
||||
else "TCG_COND_EQ")
|
||||
guard = f"""
|
||||
TCGv {self.reg_tcg()}_ext_pred = tcg_temp_new();
|
||||
TCGLabel *{self.reg_tcg()}_ext_skip = gen_new_label();
|
||||
tcg_gen_andi_tl({self.reg_tcg()}_ext_pred, {predicate}, 1);
|
||||
tcg_gen_brcondi_tl({cond}, {self.reg_tcg()}_ext_pred, 0,
|
||||
{self.reg_tcg()}_ext_skip);
|
||||
"""
|
||||
end_guard = f"""
|
||||
gen_set_label({self.reg_tcg()}_ext_skip);
|
||||
"""
|
||||
f.write(code_fmt(f"""\
|
||||
{guard}
|
||||
tcg_gen_gvec_dup_imm_var(MO_8, {self.hvx_base()},
|
||||
{self.hvx_off()} + offsetof(MMVector, ext),
|
||||
sizeof_field(MMVector, ext),
|
||||
sizeof_field(MMVector, ext),
|
||||
V_EXTENDED_BYTEVAL);
|
||||
{end_guard}
|
||||
"""))
|
||||
def gen_clear_ext_pair(self, f):
|
||||
def gen_clear_ext_pair(self, f, tag):
|
||||
predicate = None
|
||||
guard = ""
|
||||
end_guard = ""
|
||||
if is_predicated(tag):
|
||||
for regtype, regid in compute_tag_regs(tag, False):
|
||||
if regtype == "P":
|
||||
predicate = get_register(tag, regtype, regid).reg_tcg()
|
||||
break
|
||||
assert predicate is not None
|
||||
cond = ("TCG_COND_NE" if "fLSBOLDNOT" in semdict[tag]
|
||||
else "TCG_COND_EQ")
|
||||
guard = f"""
|
||||
TCGv {self.reg_tcg()}_ext_pred = tcg_temp_new();
|
||||
TCGLabel *{self.reg_tcg()}_ext_skip = gen_new_label();
|
||||
tcg_gen_andi_tl({self.reg_tcg()}_ext_pred, {predicate}, 1);
|
||||
tcg_gen_brcondi_tl({cond}, {self.reg_tcg()}_ext_pred, 0,
|
||||
{self.reg_tcg()}_ext_skip);
|
||||
"""
|
||||
end_guard = f"""
|
||||
gen_set_label({self.reg_tcg()}_ext_skip);
|
||||
"""
|
||||
f.write(code_fmt(f"""\
|
||||
{guard}
|
||||
tcg_gen_gvec_dup_imm_var(MO_8, {self.hvx_base()},
|
||||
{self.hvx_off()} + offsetof(MMVector, ext),
|
||||
sizeof_field(MMVector, ext),
|
||||
@@ -469,6 +514,7 @@ class Hvx:
|
||||
sizeof_field(MMVector, ext),
|
||||
sizeof_field(MMVector, ext),
|
||||
V_EXTENDED_BYTEVAL);
|
||||
{end_guard}
|
||||
"""))
|
||||
|
||||
#
|
||||
@@ -815,13 +861,13 @@ class VRegDest(Register, Hvx, Dest):
|
||||
"""))
|
||||
if not skip_qemu_helper(tag):
|
||||
self.decl_tcg_ptr(f)
|
||||
self.gen_clear_ext(f)
|
||||
def gen_zero(self, f):
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_zero(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
tcg_gen_gvec_dup_imm_var(MO_64, {self.hvx_base()},
|
||||
{self.hvx_off()}, sizeof(MMVector), sizeof(MMVector), 0);
|
||||
"""))
|
||||
self.gen_clear_ext(f)
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_write(self, f, tag):
|
||||
pass
|
||||
def helper_hvx_desc(self, f):
|
||||
@@ -892,13 +938,13 @@ class VRegReadWrite(Register, Hvx, ReadWrite):
|
||||
"""))
|
||||
if not skip_qemu_helper(tag):
|
||||
self.decl_tcg_ptr(f)
|
||||
self.gen_clear_ext(f)
|
||||
def gen_zero(self, f):
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_zero(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
tcg_gen_gvec_dup_imm_var(MO_64, {self.hvx_base()},
|
||||
{self.hvx_off()}, sizeof(MMVector), sizeof(MMVector), 0);
|
||||
"""))
|
||||
self.gen_clear_ext(f)
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_write(self, f, tag):
|
||||
pass
|
||||
def helper_hvx_desc(self, f):
|
||||
@@ -937,13 +983,13 @@ class VRegTmp(Register, Hvx, ReadWrite):
|
||||
{self.reg_tcg()}_srcoff,
|
||||
sizeof(MMVector), sizeof(MMVector));
|
||||
"""))
|
||||
self.gen_clear_ext(f)
|
||||
def gen_zero(self, f):
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_zero(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()},
|
||||
sizeof(MMVector), sizeof(MMVector), 0);
|
||||
"""))
|
||||
self.gen_clear_ext(f)
|
||||
self.gen_clear_ext(f, tag)
|
||||
def gen_write(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
gen_vreg_write(ctx, {self.hvx_base()}, {self.hvx_off()},
|
||||
@@ -976,13 +1022,13 @@ class VRegPairDest(Register, Hvx, Dest):
|
||||
"""))
|
||||
if not skip_qemu_helper(tag):
|
||||
self.decl_tcg_ptr(f)
|
||||
self.gen_clear_ext_pair(f)
|
||||
def gen_zero(self, f):
|
||||
self.gen_clear_ext_pair(f, tag)
|
||||
def gen_zero(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
tcg_gen_gvec_dup_imm_var(MO_64, {self.hvx_base()}, {self.hvx_off()},
|
||||
sizeof(MMVectorPair), sizeof(MMVectorPair), 0);
|
||||
"""))
|
||||
self.gen_clear_ext_pair(f)
|
||||
self.gen_clear_ext_pair(f, tag)
|
||||
def gen_write(self, f, tag):
|
||||
pass
|
||||
def helper_hvx_desc(self, f):
|
||||
@@ -1056,13 +1102,13 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite):
|
||||
"""))
|
||||
if not skip_qemu_helper(tag):
|
||||
self.decl_tcg_ptr(f)
|
||||
self.gen_clear_ext_pair(f)
|
||||
def gen_zero(self, f):
|
||||
self.gen_clear_ext_pair(f, tag)
|
||||
def gen_zero(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
tcg_gen_gvec_dup_imm_var(MO_64, {self.hvx_base()}, {self.hvx_off()},
|
||||
sizeof(MMVectorPair), sizeof(MMVectorPair), 0);
|
||||
"""))
|
||||
self.gen_clear_ext_pair(f)
|
||||
self.gen_clear_ext_pair(f, tag)
|
||||
def gen_write(self, f, tag):
|
||||
f.write(code_fmt(f"""\
|
||||
gen_vreg_write_pair(ctx, {self.hvx_base()}, {self.hvx_off()},
|
||||
|
||||
@@ -3149,8 +3149,7 @@ ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_h_hf,"Vd32.h=Vu32.hf",
|
||||
|
||||
ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_h_hf_rnd,"Vd32.h=Vu32.hf:rnd",
|
||||
"Vector conversion of hf16 format to int hw, round to nearest even",
|
||||
VdV.h[i] = float16_to_int16_scalbn(VuV.hf[i], float_round_nearest_even, 0,
|
||||
&env->hvx_fp_status))
|
||||
VdV.h[i] = conv_h_hf_rnd(VuV.hf[i], &env->hvx_fp_status))
|
||||
|
||||
ITERATOR_INSN_SHIFT_SLOT_FLT(32, vconv_sf_w,"Vd32.sf=Vu32.w",
|
||||
"Vector conversion of int w format to sf32",
|
||||
|
||||
@@ -253,6 +253,7 @@ hexagon_ss.add(files(
|
||||
'mmvec/decode_ext_mmvec.c',
|
||||
'mmvec/system_ext_mmvec.c',
|
||||
'mmvec/hvx_ieee_fp.c',
|
||||
'mmvec/hvx_qfloat.c',
|
||||
))
|
||||
|
||||
hexagon_softmmu_ss.add(files(
|
||||
|
||||
@@ -88,6 +88,15 @@ int16_t conv_h_hf(float16 a, float_status *fp_status)
|
||||
return float16_to_int16_round_to_zero(a, fp_status);
|
||||
}
|
||||
|
||||
int16_t conv_h_hf_rnd(float16 a, float_status *fp_status)
|
||||
{
|
||||
/* float16_to_int16_scalbn converts any NaN to MAX. */
|
||||
if (float16_is_any_nan(a)) {
|
||||
return float16_is_neg(a) ? INT16_MIN : INT16_MAX;
|
||||
}
|
||||
return float16_to_int16_scalbn(a, float_round_nearest_even, 0, fp_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if f1 > f2, where at least one of the elements is guaranteed
|
||||
* to be NaN.
|
||||
|
||||
@@ -27,6 +27,7 @@ float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status);
|
||||
|
||||
int32_t conv_w_sf(float32 a, float_status *fp_status);
|
||||
int16_t conv_h_hf(float16 a, float_status *fp_status);
|
||||
int16_t conv_h_hf_rnd(float16 a, float_status *fp_status);
|
||||
|
||||
/* IEEE - FP compare instructions */
|
||||
uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status);
|
||||
|
||||
@@ -0,0 +1,832 @@
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "reg_fields.h"
|
||||
#include "mmvec_qfloat.h"
|
||||
|
||||
#define BIAS_QF32 127
|
||||
#define BIAS_EXTQF32 383
|
||||
#define BIAS_QF16 15
|
||||
#define BIAS_SF 127
|
||||
#define BIAS_HF 15
|
||||
|
||||
#define E_MAX_QF16 16
|
||||
#define E_MIN_QF16 -15
|
||||
#define E_MAX_EXTQF16 E_MAX_QF16
|
||||
#define E_MAX_EXTQF32 256
|
||||
#define E_MAX_SF 128
|
||||
#define E_MIN_SF -126
|
||||
#define E_MAX_HF 16
|
||||
#define E_MIN_HF -14
|
||||
|
||||
#define sf_MANTBITS 23
|
||||
#define hf_MANTBITS 10
|
||||
#define bf_MANTBITS 7
|
||||
|
||||
#define EXTQF32_BITMASK 0xFFFFFFFFFULL
|
||||
#define EXTQF16_BITMASK 0x3FFFFULL
|
||||
|
||||
#define MAX_SIG_QF16 0x3ff
|
||||
#define MAX_SIG_QF32 0x7fffff
|
||||
|
||||
#define ieee_pos_NaN_32 0x7FFFFFFF
|
||||
#define ieee_pos_NaN_16 0x7FFF
|
||||
#define ieee_pos_inf_32 0x7f800000
|
||||
#define ieee_pos_inf_16 0x7C00
|
||||
#define BF_POS_NAN 0x7FFF
|
||||
#define BF_POS_INF 0x7f80
|
||||
|
||||
#define QF32_ILOG2_ZERO_EXP 0x80000000
|
||||
#define QF32_ILOG2_NAN_EXP 0x7FFFFFFF
|
||||
#define QF32_ILOG2_INF_EXP 0x7FFFFFFE
|
||||
#define QF16_ILOG2_ZERO_EXP 0x8000
|
||||
#define QF16_ILOG2_NAN_EXP 0x7FFF
|
||||
#define QF16_ILOG2_INF_EXP 0x7FFE
|
||||
|
||||
typedef uint8_t LREQ_t;
|
||||
|
||||
enum {
|
||||
LREQ_Q = 1 << 0,
|
||||
LREQ_E = 1 << 1,
|
||||
LREQ_R = 1 << 2,
|
||||
LREQ_L = 1 << 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Common indicators
|
||||
*/
|
||||
|
||||
bool is_unfloat_neg(unfloat u)
|
||||
{
|
||||
return u.parts.sign ^ u.sign;
|
||||
}
|
||||
|
||||
/* One's complement the LRQ bits, but leave E (exponent extension) alone */
|
||||
static LREQ_t negate_lreq(LREQ_t lreq)
|
||||
{
|
||||
LREQ_t neg_lreq = ~lreq & EXT32_BITMASK;
|
||||
|
||||
return (neg_lreq & ~LREQ_E) | (lreq & LREQ_E);
|
||||
}
|
||||
|
||||
static bool is_extqf32_nan(uint64_t in)
|
||||
{
|
||||
return in == extqf32_pos_nan || in == extqf32_neg_nan ||
|
||||
in == extqf32_pos_nan_inexact || in == extqf32_neg_nan_inexact;
|
||||
}
|
||||
|
||||
static bool is_extqf32_inf(uint64_t in)
|
||||
{
|
||||
return in == extqf32_pos_inf_exact || in == extqf32_pos_inf_inexact ||
|
||||
in == extqf32_neg_inf_exact || in == extqf32_neg_inf_inexact;
|
||||
}
|
||||
|
||||
static bool is_extqf16_nan(uint32_t in)
|
||||
{
|
||||
return in == extqf16_pos_nan || in == extqf16_neg_nan;
|
||||
}
|
||||
|
||||
static bool is_extqf16_inf(uint32_t in)
|
||||
{
|
||||
return in == extqf16_pos_inf || in == extqf16_neg_inf;
|
||||
}
|
||||
|
||||
static FloatParts64 qfloat_unpack_ext_parts(unsigned size, uint32_t value,
|
||||
uint8_t ext)
|
||||
{
|
||||
int32_t sig;
|
||||
int exp;
|
||||
unsigned precision;
|
||||
uint64_t frac;
|
||||
bool sign;
|
||||
|
||||
if (size == 32) {
|
||||
LREQ_t lreq = ext & EXT32_BITMASK;
|
||||
|
||||
/* L is the low bit of the signed 24-bit significand. */
|
||||
sig = (sextract32(value, 7, 25) & ~1) | !!(lreq & LREQ_L);
|
||||
/* The R bit is encoded as a deferred negation. */
|
||||
sig += !!(lreq & LREQ_R);
|
||||
sign = sig < 0;
|
||||
exp = ((((value & 0xff) | (!!(lreq & LREQ_E) << 8)) - 128) &
|
||||
0x1ff) - 255;
|
||||
precision = 23;
|
||||
} else {
|
||||
LREQ_t lreq = (ext & EXT16_BITMASK) << 2;
|
||||
|
||||
sig = (sextract32(value, 4, 12) & ~1) | !!(lreq & LREQ_L);
|
||||
sig += !!(lreq & LREQ_R);
|
||||
sign = sig < 0;
|
||||
exp = (value & 0x1f) - BIAS_QF16;
|
||||
precision = 10;
|
||||
}
|
||||
|
||||
if (sig == 0) {
|
||||
return (FloatParts64) { .cls = float_class_zero };
|
||||
}
|
||||
|
||||
frac = sig < 0 ? -(int64_t)sig : sig;
|
||||
return (FloatParts64) {
|
||||
.cls = float_class_normal,
|
||||
.sign = sign,
|
||||
.exp = exp + 63 - clz64(frac) - precision,
|
||||
.frac = frac << clz64(frac),
|
||||
};
|
||||
}
|
||||
|
||||
FloatParts64 qfloat_unfloat_parts(unfloat u)
|
||||
{
|
||||
FloatParts64 parts = u.parts;
|
||||
|
||||
parts.sign ^= u.sign;
|
||||
return parts;
|
||||
}
|
||||
|
||||
static void qfloat_apply_inexact(FloatParts64 *parts, uint8_t ext)
|
||||
{
|
||||
LREQ_t lreq = ext & EXT32_BITMASK;
|
||||
|
||||
if (parts->cls != float_class_normal ||
|
||||
!!(lreq & LREQ_R) == !!(lreq & LREQ_Q)) {
|
||||
return;
|
||||
}
|
||||
if (!parts->sign) {
|
||||
parts->frac |= 1;
|
||||
} else if (--parts->frac < (UINT64_C(1) << 63)) {
|
||||
parts->frac = parts->frac << 1 | 1;
|
||||
parts->exp--;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t qfloat_pack_ext(unsigned size, int64_t sig, int biased_exp,
|
||||
LREQ_t lreq)
|
||||
{
|
||||
lreq = (lreq & ~LREQ_E) | (((biased_exp >> 8) & 1) ? LREQ_E : 0);
|
||||
if (size == 32) {
|
||||
uint64_t value = ((uint64_t)sig << 8) | (biased_exp & 0xff);
|
||||
|
||||
return ((value & QF32_BITMASK) << 4) |
|
||||
(lreq & EXT32_BITMASK);
|
||||
}
|
||||
return (((uint64_t)sig << 5 | (biased_exp & 0x1f)) & QF16_BITMASK) << 2 |
|
||||
((lreq >> 2) & EXT16_BITMASK);
|
||||
}
|
||||
|
||||
static uint64_t qfloat_ext_overflow(unsigned size, bool sign,
|
||||
qfrnd_mode_enum_t mode)
|
||||
{
|
||||
uint32_t sig = size == 32 ? MAX_SIG_QF32 : MAX_SIG_QF16;
|
||||
int exp = size == 32 ? E_MAX_EXTQF32 : E_MAX_EXTQF16;
|
||||
int bias = size == 32 ? BIAS_EXTQF32 : BIAS_QF16;
|
||||
LREQ_t lreq = 0;
|
||||
|
||||
if (mode == RND_TO_ZERO ||
|
||||
(mode == RND_TOWARDS_NEG_INF && !sign) ||
|
||||
(mode == RND_TOWARDS_POS_INF && sign)) {
|
||||
exp--;
|
||||
}
|
||||
lreq |= LREQ_L | LREQ_Q;
|
||||
if (sign) {
|
||||
sig = ~sig;
|
||||
lreq = negate_lreq(lreq);
|
||||
}
|
||||
return qfloat_pack_ext(size, sig, exp + bias, lreq);
|
||||
}
|
||||
|
||||
static uint64_t qfloat_ext_underflow(unsigned size, bool sign,
|
||||
qfrnd_mode_enum_t mode)
|
||||
{
|
||||
int exp = size == 32 ? E_MIN_EXTQF32 : E_MIN_EXTQF16;
|
||||
int bias = size == 32 ? BIAS_EXTQF32 : BIAS_QF16;
|
||||
LREQ_t lreq = 0;
|
||||
uint32_t sig = 0;
|
||||
|
||||
if (mode == RND_TOWARDS_POS_INF) {
|
||||
lreq |= LREQ_R;
|
||||
} else if (mode == RND_TOWARDS_NEG_INF) {
|
||||
lreq |= LREQ_Q;
|
||||
} else {
|
||||
lreq |= LREQ_Q;
|
||||
}
|
||||
if (sign) {
|
||||
sig = ~sig;
|
||||
lreq = negate_lreq(lreq);
|
||||
}
|
||||
return qfloat_pack_ext(size, sig, exp + bias, lreq);
|
||||
}
|
||||
|
||||
uint64_t qfloat_round_ext_parts(unsigned size, FloatParts64 parts, int exp,
|
||||
bool deferred_sign,
|
||||
qfrnd_mode_enum_t mode)
|
||||
{
|
||||
unsigned precision = size == 32 ? 23 : 10;
|
||||
int emax = size == 32 ? E_MAX_EXTQF32 : E_MAX_EXTQF16;
|
||||
int emin = size == 32 ? E_MIN_EXTQF32 : E_MIN_EXTQF16;
|
||||
int bias = size == 32 ? BIAS_EXTQF32 : BIAS_QF16;
|
||||
int shift;
|
||||
uint64_t integer, discarded, mask;
|
||||
bool half, sticky, increment;
|
||||
uint64_t quarters, rq1, even;
|
||||
int64_t sig;
|
||||
LREQ_t lreq = 0;
|
||||
|
||||
if (parts.cls == float_class_zero) {
|
||||
exp = emin + bias;
|
||||
if (size == 32) {
|
||||
return (uint64_t)(exp & 0xff) << 4 |
|
||||
(((exp >> 8) & 1) << 1) | 7;
|
||||
}
|
||||
return (uint64_t)(exp & 0x1f) << 2 | 3;
|
||||
}
|
||||
if (parts.exp - exp >= 1) {
|
||||
exp++;
|
||||
}
|
||||
if (exp > emax ||
|
||||
(exp == emax && parts.frac > (UINT64_C(1) << 63))) {
|
||||
return qfloat_ext_overflow(size, parts.sign, mode);
|
||||
}
|
||||
if (exp < emin) {
|
||||
return qfloat_ext_underflow(size, parts.sign, mode);
|
||||
}
|
||||
|
||||
shift = 63 - precision - (parts.exp - exp);
|
||||
if (shift > 64) {
|
||||
integer = 0;
|
||||
half = false;
|
||||
sticky = true;
|
||||
} else if (shift == 64) {
|
||||
integer = 0;
|
||||
half = parts.frac >> 63;
|
||||
sticky = parts.frac << 1;
|
||||
} else if (shift > 0) {
|
||||
integer = parts.frac >> shift;
|
||||
mask = (UINT64_C(1) << shift) - 1;
|
||||
discarded = parts.frac & mask;
|
||||
half = discarded & (UINT64_C(1) << (shift - 1));
|
||||
sticky = discarded & ((UINT64_C(1) << (shift - 1)) - 1);
|
||||
} else {
|
||||
integer = parts.frac << -shift;
|
||||
half = false;
|
||||
sticky = false;
|
||||
}
|
||||
quarters = integer * 4 + (half ? 2 : 0) + (sticky ? 1 : 0);
|
||||
|
||||
switch (mode) {
|
||||
case RND_TO_NEAREST_EVEN:
|
||||
increment = (quarters & 3) >= ((quarters >> 2 & 1) ? 2 : 3);
|
||||
break;
|
||||
case RND_TO_ZERO:
|
||||
increment = false;
|
||||
break;
|
||||
case RND_TOWARDS_NEG_INF:
|
||||
increment = parts.sign && (quarters & 3);
|
||||
break;
|
||||
case RND_TOWARDS_POS_INF:
|
||||
increment = !parts.sign && (quarters & 3);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
rq1 = (quarters >> 2) * 8 + increment * 4 +
|
||||
(increment ^ ((quarters & 3) != 0)) * 2 + 1;
|
||||
even = (rq1 >> 3) & ~UINT64_C(1);
|
||||
rq1 -= even * 8 + 1;
|
||||
sig = even >> 1;
|
||||
lreq |= (rq1 >> 3) ? LREQ_L : 0;
|
||||
lreq |= ((rq1 >> 2) & 1) ? LREQ_R : 0;
|
||||
lreq |= ((rq1 >> 1) & 1) ? LREQ_Q : 0;
|
||||
|
||||
if (parts.sign) {
|
||||
sig = ~sig;
|
||||
lreq = negate_lreq(lreq);
|
||||
}
|
||||
if (deferred_sign) {
|
||||
lreq = negate_lreq(lreq);
|
||||
}
|
||||
|
||||
return qfloat_pack_ext(size, sig, exp + bias, lreq);
|
||||
}
|
||||
|
||||
/*
|
||||
* NaN/Inf input behavior
|
||||
*/
|
||||
|
||||
uint64_t handle_infinity_nan_add(unfloat u, unfloat v, uint64_t pos_nan_val,
|
||||
uint64_t neg_nan_val, uint64_t pos_inf_val,
|
||||
uint64_t neg_inf_val)
|
||||
{
|
||||
bool u_neg = is_unfloat_neg(u);
|
||||
bool v_neg = is_unfloat_neg(v);
|
||||
|
||||
if (u.nan || v.nan) {
|
||||
/* +NaN has the highest priority */
|
||||
if ((!u_neg && u.nan) || (!v_neg && v.nan)) {
|
||||
return pos_nan_val;
|
||||
}
|
||||
return neg_nan_val;
|
||||
}
|
||||
|
||||
/* Infinities of opposing sign added together produce a NaN */
|
||||
if (u.inf && v.inf && (u_neg ^ v_neg)) {
|
||||
return pos_nan_val;
|
||||
}
|
||||
if ((u_neg && u.inf) || (v_neg && v.inf)) {
|
||||
return neg_inf_val;
|
||||
}
|
||||
return pos_inf_val;
|
||||
}
|
||||
|
||||
uint64_t handle_infinity_nan_mpy(unfloat u, unfloat v, uint64_t pos_nan_val,
|
||||
uint64_t neg_nan_val, uint64_t pos_inf_val,
|
||||
uint64_t neg_inf_val)
|
||||
{
|
||||
bool u_neg = is_unfloat_neg(u);
|
||||
bool v_neg = is_unfloat_neg(v);
|
||||
|
||||
if (u.nan || v.nan) {
|
||||
return (u_neg ^ v_neg) ? neg_nan_val : pos_nan_val;
|
||||
}
|
||||
/* Implicitly, one of the operands is infinite: 0 * inf is a NaN */
|
||||
if (u.zero || v.zero) {
|
||||
return (u_neg ^ v_neg) ? neg_nan_val : pos_nan_val;
|
||||
}
|
||||
return (u_neg ^ v_neg) ? neg_inf_val : pos_inf_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse SF/HF/QF16/QF32 into an unfloat
|
||||
*/
|
||||
|
||||
unfloat parse_hf(int16_t in)
|
||||
{
|
||||
INIT_UNFLOAT(out)
|
||||
float_status status = { 0 };
|
||||
uint16_t raw = in;
|
||||
|
||||
out.exp = (raw >> hf_MANTBITS) & 0x1f;
|
||||
out.exp -= BIAS_HF;
|
||||
if (out.exp < E_MIN_HF) {
|
||||
out.exp = E_MIN_HF;
|
||||
}
|
||||
|
||||
out.sign = raw >> 15;
|
||||
out.parts = float16_unpack_canonical(make_float16(in), &status);
|
||||
out.inf = out.parts.cls == float_class_inf;
|
||||
out.nan = out.parts.cls == float_class_qnan ||
|
||||
out.parts.cls == float_class_snan;
|
||||
out.zero = out.parts.cls == float_class_zero;
|
||||
out.parts.sign = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
static unfloat parse_sf(int32_t in)
|
||||
{
|
||||
INIT_UNFLOAT(out)
|
||||
float_status status = { 0 };
|
||||
uint32_t raw = in;
|
||||
|
||||
out.exp = (raw >> sf_MANTBITS) & 0xff;
|
||||
out.exp -= BIAS_SF;
|
||||
if (out.exp < E_MIN_SF) {
|
||||
out.exp = E_MIN_SF;
|
||||
}
|
||||
|
||||
out.sign = raw >> 31;
|
||||
out.parts = float32_unpack_canonical(make_float32(in), &status);
|
||||
out.inf = out.parts.cls == float_class_inf;
|
||||
out.nan = out.parts.cls == float_class_qnan ||
|
||||
out.parts.cls == float_class_snan;
|
||||
out.zero = out.parts.cls == float_class_zero;
|
||||
out.parts.sign = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
unfloat parse_sf_daz(uint32_t in, bool daz_mode)
|
||||
{
|
||||
unfloat a = parse_sf(in);
|
||||
|
||||
if (daz_mode && (in & 0x7f800000) == 0 && (in & 0x007fffff)) {
|
||||
a.zero = true;
|
||||
a.parts = (FloatParts64) { .cls = float_class_zero };
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
unfloat parse_extqf16(uint16_t in, uint8_t in_ext)
|
||||
{
|
||||
INIT_UNFLOAT(out)
|
||||
LREQ_t lreq = (in_ext & EXT16_BITMASK) << 2;
|
||||
uint32_t raw = ((uint32_t)in << 4) | (in_ext & EXT16_BITMASK);
|
||||
|
||||
out.exp = (in & 0x1f) - BIAS_QF16;
|
||||
out.sign = !!(lreq & LREQ_R);
|
||||
out.inf = is_extqf16_inf(raw);
|
||||
out.nan = is_extqf16_nan(raw);
|
||||
out.parts = qfloat_unpack_ext_parts(16, in, in_ext);
|
||||
out.parts.sign ^= out.sign;
|
||||
out.zero = out.parts.cls == float_class_zero;
|
||||
return out;
|
||||
}
|
||||
|
||||
unfloat parse_extqf32(uint32_t in, uint8_t in_ext)
|
||||
{
|
||||
INIT_UNFLOAT(out)
|
||||
LREQ_t lreq = in_ext & EXT32_BITMASK;
|
||||
uint64_t raw = ((uint64_t)in << 4) | (in_ext & EXT32_BITMASK);
|
||||
int exponent = (in & 0xff) | (!!(lreq & LREQ_E) << 8);
|
||||
|
||||
/* First step, sub 128 to get to IEEE-like; then sub 255 for qf32 bias */
|
||||
out.exp = ((exponent - 128) & 0x1ff) - 255;
|
||||
out.sign = !!(lreq & LREQ_R);
|
||||
out.inf = is_extqf32_inf(raw);
|
||||
out.nan = is_extqf32_nan(raw);
|
||||
out.parts = qfloat_unpack_ext_parts(32, in, in_ext);
|
||||
out.parts.sign ^= out.sign;
|
||||
out.zero = out.parts.cls == float_class_zero;
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert QF16/QF32 to HF/SF/BF
|
||||
*/
|
||||
|
||||
static FloatRoundMode qfrnd_to_float_round(qfrnd_mode_enum_t qfrnd)
|
||||
{
|
||||
switch (qfrnd) {
|
||||
case RND_TO_NEAREST_EVEN:
|
||||
return float_round_nearest_even;
|
||||
case RND_TO_ZERO:
|
||||
return float_round_to_zero;
|
||||
case RND_TOWARDS_NEG_INF:
|
||||
return float_round_down;
|
||||
case RND_TOWARDS_POS_INF:
|
||||
return float_round_up;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t conv_sf_extqf32(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd)
|
||||
{
|
||||
uint64_t raw = ((uint64_t)a << 4) | (a_ext & EXT32_BITMASK);
|
||||
FloatParts64 parts;
|
||||
float_status status = { 0 };
|
||||
|
||||
if (is_extqf32_nan(raw)) {
|
||||
return raw == extqf32_neg_nan || raw == extqf32_neg_nan_inexact ?
|
||||
ieee_pos_NaN_32 | 0x80000000 : ieee_pos_NaN_32;
|
||||
}
|
||||
if (is_extqf32_inf(raw)) {
|
||||
return raw == extqf32_neg_inf_exact ||
|
||||
raw == extqf32_neg_inf_inexact ?
|
||||
ieee_pos_inf_32 | 0x80000000 : ieee_pos_inf_32;
|
||||
}
|
||||
set_float_rounding_mode(qfrnd_to_float_round(qfrnd), &status);
|
||||
parts = qfloat_unpack_ext_parts(32, a, a_ext);
|
||||
qfloat_apply_inexact(&parts, a_ext);
|
||||
return float32_val(float32_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
|
||||
int16_t conv_hf_extqf32(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd)
|
||||
{
|
||||
uint64_t raw = ((uint64_t)a << 4) | (a_ext & EXT32_BITMASK);
|
||||
FloatParts64 parts;
|
||||
float_status status = { 0 };
|
||||
|
||||
if (is_extqf32_nan(raw)) {
|
||||
return (int16_t)(raw == extqf32_neg_nan ||
|
||||
raw == extqf32_neg_nan_inexact ?
|
||||
ieee_pos_NaN_16 | 0x8000 : ieee_pos_NaN_16);
|
||||
}
|
||||
if (is_extqf32_inf(raw)) {
|
||||
return (int16_t)(raw == extqf32_neg_inf_exact ||
|
||||
raw == extqf32_neg_inf_inexact ?
|
||||
ieee_pos_inf_16 | 0x8000 : ieee_pos_inf_16);
|
||||
}
|
||||
set_float_rounding_mode(qfrnd_to_float_round(qfrnd), &status);
|
||||
parts = qfloat_unpack_ext_parts(32, a, a_ext);
|
||||
qfloat_apply_inexact(&parts, a_ext);
|
||||
return float16_val(float16_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
|
||||
int16_t conv_hf_extqf16(uint16_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd)
|
||||
{
|
||||
uint32_t raw = ((uint32_t)a << 4) | (a_ext & EXT16_BITMASK);
|
||||
FloatParts64 parts;
|
||||
float_status status = { 0 };
|
||||
|
||||
if (is_extqf16_nan(raw)) {
|
||||
return (int16_t)(raw == extqf16_neg_nan ?
|
||||
ieee_pos_NaN_16 | 0x8000 : ieee_pos_NaN_16);
|
||||
}
|
||||
if (is_extqf16_inf(raw)) {
|
||||
return (int16_t)(raw == extqf16_neg_inf ?
|
||||
ieee_pos_inf_16 | 0x8000 : ieee_pos_inf_16);
|
||||
}
|
||||
set_float_rounding_mode(qfrnd_to_float_round(qfrnd), &status);
|
||||
parts = qfloat_unpack_ext_parts(16, a, a_ext);
|
||||
return float16_val(float16_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
|
||||
uint16_t conv_qf32_to_bf(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd)
|
||||
{
|
||||
uint64_t raw = ((uint64_t)a << 4) | (a_ext & EXT32_BITMASK);
|
||||
FloatParts64 parts;
|
||||
float_status status = { 0 };
|
||||
|
||||
if (is_extqf32_nan(raw)) {
|
||||
return raw == extqf32_neg_nan || raw == extqf32_neg_nan_inexact ?
|
||||
BF_POS_NAN | 0x8000 : BF_POS_NAN;
|
||||
}
|
||||
if (is_extqf32_inf(raw)) {
|
||||
return raw == extqf32_neg_inf_exact ||
|
||||
raw == extqf32_neg_inf_inexact ? BF_POS_INF | 0x8000 :
|
||||
BF_POS_INF;
|
||||
}
|
||||
set_float_rounding_mode(qfrnd_to_float_round(qfrnd), &status);
|
||||
parts = qfloat_unpack_ext_parts(32, a, a_ext);
|
||||
qfloat_apply_inexact(&parts, a_ext);
|
||||
return bfloat16_round_pack_canonical(&parts, &status);
|
||||
}
|
||||
|
||||
int qf_vilog2(f_type type, uint32_t a, uint8_t a_ext)
|
||||
{
|
||||
unfloat u = { 0 };
|
||||
|
||||
if (type == EXTQF32) {
|
||||
uint64_t raw = ((uint64_t)a << 4) | (a_ext & EXT32_BITMASK);
|
||||
FloatParts64 p;
|
||||
|
||||
if (is_extqf32_nan(raw)) {
|
||||
return QF32_ILOG2_NAN_EXP;
|
||||
}
|
||||
if (is_extqf32_inf(raw)) {
|
||||
return QF32_ILOG2_INF_EXP;
|
||||
}
|
||||
p = qfloat_unpack_ext_parts(32, a, a_ext);
|
||||
return p.cls == float_class_zero ? QF32_ILOG2_ZERO_EXP : p.exp;
|
||||
}
|
||||
if (type == EXTQF16) {
|
||||
uint32_t raw = (a << 4) | (a_ext & EXT16_BITMASK);
|
||||
FloatParts64 p;
|
||||
|
||||
if (is_extqf16_nan(raw)) {
|
||||
return QF16_ILOG2_NAN_EXP;
|
||||
}
|
||||
if (is_extqf16_inf(raw)) {
|
||||
return QF16_ILOG2_INF_EXP;
|
||||
}
|
||||
p = qfloat_unpack_ext_parts(16, a, a_ext);
|
||||
return p.cls == float_class_zero ? QF16_ILOG2_ZERO_EXP : p.exp;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SF:
|
||||
u = parse_sf(a);
|
||||
break;
|
||||
case HF:
|
||||
u = parse_hf(a);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (type == SF) {
|
||||
if (u.zero) {
|
||||
return QF32_ILOG2_ZERO_EXP;
|
||||
} else if (u.nan) {
|
||||
return QF32_ILOG2_NAN_EXP;
|
||||
} else if (u.inf) {
|
||||
return QF32_ILOG2_INF_EXP;
|
||||
}
|
||||
} else {
|
||||
if (u.zero) {
|
||||
return QF16_ILOG2_ZERO_EXP;
|
||||
} else if (u.nan) {
|
||||
return QF16_ILOG2_NAN_EXP;
|
||||
} else if (u.inf) {
|
||||
return QF16_ILOG2_INF_EXP;
|
||||
}
|
||||
}
|
||||
return u.parts.exp;
|
||||
}
|
||||
|
||||
/*
|
||||
* USR field accessors
|
||||
*/
|
||||
|
||||
static uint32_t get_usr_field(const CPUHexagonState *env, int field)
|
||||
{
|
||||
return extract32(env->gpr[HEX_REG_USR], reg_field_info[field].offset,
|
||||
reg_field_info[field].width);
|
||||
}
|
||||
|
||||
bool is_daz_mode(const CPUHexagonState *env)
|
||||
{
|
||||
const HexagonCPU *cpu = env_archcpu(env);
|
||||
|
||||
return cpu->cfg.hex_def->hex_version >= HEX_VER_V81 &&
|
||||
get_usr_field(env, USR_FPDAZ);
|
||||
}
|
||||
|
||||
bool qfloat_is_extended(const CPUHexagonState *env)
|
||||
{
|
||||
const HexagonCPU *cpu = env_archcpu(env);
|
||||
|
||||
return cpu->cfg.hex_def->hex_version >= HEX_VER_V79;
|
||||
}
|
||||
|
||||
unfloat legacy_parse_qf32(int32_t in)
|
||||
{
|
||||
unfloat out = { 0 };
|
||||
int32_t signif;
|
||||
uint32_t magnitude;
|
||||
|
||||
out.sign = (uint32_t)in >> 31;
|
||||
out.exp = (in & 0xff) - BIAS_QF32;
|
||||
signif = sextract32(in, 7, 25) | 1;
|
||||
magnitude = signif < 0 ? -(uint32_t)signif : signif;
|
||||
out.parts = (FloatParts64) {
|
||||
.cls = float_class_normal,
|
||||
.exp = out.exp + 31 - clz32(magnitude) - sf_MANTBITS,
|
||||
.frac = (uint64_t)magnitude << (32 + clz32(magnitude)),
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
unfloat legacy_parse_qf16(int16_t in)
|
||||
{
|
||||
unfloat out = { 0 };
|
||||
int16_t signif;
|
||||
uint16_t magnitude;
|
||||
|
||||
out.sign = (uint16_t)in >> 15;
|
||||
out.exp = (in & 0x1f) - BIAS_QF16;
|
||||
signif = sextract32((uint16_t)in, 4, 12) | 1;
|
||||
magnitude = signif < 0 ? -(uint16_t)signif : signif;
|
||||
out.parts = (FloatParts64) {
|
||||
.cls = float_class_normal,
|
||||
.exp = out.exp + 31 - clz32(magnitude) - hf_MANTBITS,
|
||||
.frac = (uint64_t)magnitude << (32 + clz32(magnitude)),
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
static int legacy_add_exp(unfloat a, unfloat b)
|
||||
{
|
||||
if (a.exp > b.exp) {
|
||||
return MAX(a.parts.exp, b.exp);
|
||||
}
|
||||
return MAX(b.parts.exp, a.exp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the legacy rounded significand numerator. The result is N where
|
||||
* the rounded value is normally N * 2^-(frac + 1). `half` selects the
|
||||
* legacy arm which uses N * 2^-(frac + 2) and increments the exponent.
|
||||
*/
|
||||
static int64_t legacy_round_n(FloatParts64 parts, int exp, int frac, int adj,
|
||||
bool midpoint, bool *half)
|
||||
{
|
||||
int shift = 65 + exp - parts.exp - frac + adj;
|
||||
uint64_t mask, low, threshold;
|
||||
int64_t base;
|
||||
|
||||
g_assert(parts.cls == float_class_normal);
|
||||
if (shift >= 64) {
|
||||
return parts.sign ? -1 : 1;
|
||||
}
|
||||
if (shift <= 0) {
|
||||
uint64_t magnitude = parts.frac << -shift;
|
||||
int64_t value = parts.sign ? -(int64_t)magnitude : magnitude;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
mask = (UINT64_C(1) << shift) - 1;
|
||||
if (parts.sign) {
|
||||
uint64_t twos = -parts.frac;
|
||||
|
||||
low = twos & mask;
|
||||
base = -(int64_t)((parts.frac + mask) >> shift) * 4;
|
||||
} else {
|
||||
low = parts.frac & mask;
|
||||
base = (parts.frac >> shift) * 4;
|
||||
}
|
||||
|
||||
if (midpoint) {
|
||||
threshold = UINT64_C(3) << (shift - 3);
|
||||
if (low <= threshold) {
|
||||
return base + 1;
|
||||
}
|
||||
threshold = UINT64_C(5) << (shift - 3);
|
||||
if (low <= threshold) {
|
||||
*half = true;
|
||||
return base + 2;
|
||||
}
|
||||
} else {
|
||||
threshold = UINT64_C(1) << (shift - 1);
|
||||
if (low <= threshold) {
|
||||
return base + 1;
|
||||
}
|
||||
}
|
||||
return base + 3;
|
||||
}
|
||||
|
||||
static uint32_t legacy_rnd_sat_qf(int size, int exp, FloatParts64 parts)
|
||||
{
|
||||
int frac = size == 32 ? sf_MANTBITS : hf_MANTBITS;
|
||||
int emax = size == 32 ? 128 : 16;
|
||||
int emin = size == 32 ? -127 : -15;
|
||||
int bias = size == 32 ? BIAS_QF32 : BIAS_QF16;
|
||||
uint32_t mask = size == 32 ? 0x7fffff : 0x3ff;
|
||||
bool sign = parts.sign;
|
||||
bool exact_neg_two = sign && parts.exp == exp + 1 &&
|
||||
parts.frac == UINT64_C(1) << 63;
|
||||
bool prod_ovf = parts.cls != float_class_zero &&
|
||||
parts.exp >= exp + 1 && !exact_neg_two;
|
||||
bool half = false;
|
||||
int adj = 0;
|
||||
int64_t n;
|
||||
uint32_t sig_out;
|
||||
|
||||
if (parts.cls == float_class_zero) {
|
||||
return 0;
|
||||
}
|
||||
if (exp >= emax + 1 || (prod_ovf && exp == emax)) {
|
||||
sig_out = (sign - 1) & mask;
|
||||
exp = emax + bias;
|
||||
goto pack;
|
||||
}
|
||||
if (exp <= emin - 2) {
|
||||
sig_out = -sign & mask;
|
||||
exp = emin + bias;
|
||||
goto pack;
|
||||
}
|
||||
adj = exp == emin - 1 || (prod_ovf && exp < emax);
|
||||
n = legacy_round_n(parts, exp, frac, adj,
|
||||
!adj && exp >= emin && exp < emax, &half);
|
||||
sig_out = ((n < 0 ? -(uint64_t)n : (uint64_t)n) >>
|
||||
(half ? 2 : 1)) & mask;
|
||||
if (sign) {
|
||||
sig_out = ~sig_out;
|
||||
}
|
||||
exp += bias + adj + half;
|
||||
|
||||
pack:
|
||||
return (((sign << frac) | (sig_out & mask)) << (size == 32 ? 8 : 5)) |
|
||||
(exp & (size == 32 ? 0xff : 0x1f));
|
||||
}
|
||||
|
||||
uint32_t legacy_qf_add(int size, unfloat a, unfloat b)
|
||||
{
|
||||
float_status status = { 0 };
|
||||
FloatParts64 pa = qfloat_unfloat_parts(a);
|
||||
FloatParts64 pb = qfloat_unfloat_parts(b);
|
||||
FloatParts64 result = parts64_addsub(&pa, &pb, &status, false);
|
||||
|
||||
return legacy_rnd_sat_qf(size, legacy_add_exp(a, b), result);
|
||||
}
|
||||
|
||||
uint32_t legacy_qf_mpy(int size, unfloat a, unfloat b)
|
||||
{
|
||||
float_status status = { 0 };
|
||||
FloatParts64 pa = qfloat_unfloat_parts(a);
|
||||
FloatParts64 pb = qfloat_unfloat_parts(b);
|
||||
FloatParts64 result = parts64_mul(&pa, &pb, &status);
|
||||
|
||||
return legacy_rnd_sat_qf(size, a.exp + b.exp, result);
|
||||
}
|
||||
|
||||
int32_t legacy_conv_sf_qf32(uint32_t a)
|
||||
{
|
||||
unfloat value = legacy_parse_qf32(a);
|
||||
FloatParts64 parts = qfloat_unfloat_parts(value);
|
||||
float_status status = { 0 };
|
||||
|
||||
return float32_val(float32_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
|
||||
int16_t legacy_conv_hf_qf32(uint32_t a)
|
||||
{
|
||||
unfloat value = legacy_parse_qf32(a);
|
||||
FloatParts64 parts = qfloat_unfloat_parts(value);
|
||||
float_status status = { 0 };
|
||||
|
||||
return float16_val(float16_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
|
||||
int16_t legacy_conv_hf_qf16(uint16_t a)
|
||||
{
|
||||
unfloat value = legacy_parse_qf16(a);
|
||||
FloatParts64 parts = qfloat_unfloat_parts(value);
|
||||
float_status status = { 0 };
|
||||
|
||||
return float16_val(float16_round_pack_canonical(&parts, &status));
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/*
|
||||
* HVX qfloat extended-precision arithmetic.
|
||||
*
|
||||
* qfloat is an intermediate format used to chain consecutive HVX
|
||||
* floating-point operations without rounding at each step:
|
||||
* a qf16/qf32 result carries a few extra bits of precision beyond its
|
||||
* visible 16/32-bit mantissa/exponent, stashed in the vector register's
|
||||
* extended bits.
|
||||
*
|
||||
* Qfloat arithmetic uses FloatParts64, including qfloat-specific adapters for
|
||||
* the non-IEEE signed significand and LREQ extension bits.
|
||||
*/
|
||||
|
||||
#ifndef MMVEC_QFLOAT_H
|
||||
#define MMVEC_QFLOAT_H
|
||||
|
||||
#include "mmvec.h"
|
||||
#include "fpu/softfloat.h"
|
||||
#include "fpu/softfloat-parts.h"
|
||||
|
||||
/* Forward declaration to avoid a circular include with cpu.h */
|
||||
typedef struct CPUArchState CPUHexagonState;
|
||||
|
||||
typedef enum float_type {
|
||||
SF,
|
||||
HF,
|
||||
EXTQF32,
|
||||
EXTQF16,
|
||||
} f_type;
|
||||
|
||||
typedef enum {
|
||||
RND_TO_NEAREST_EVEN,
|
||||
RND_TO_ZERO,
|
||||
RND_TOWARDS_NEG_INF,
|
||||
RND_TOWARDS_POS_INF,
|
||||
MAX_RND_MODES
|
||||
} qfrnd_mode_enum_t;
|
||||
|
||||
/* Only RND_TO_NEAREST_EVEN is ever selected today; see hvx_qfloat.c */
|
||||
#define CVI_QFRND_MODE RND_TO_NEAREST_EVEN
|
||||
|
||||
#define QF32_BITMASK 0xFFFFFFFFULL
|
||||
#define QF16_BITMASK 0xFFFFULL
|
||||
#define EXT32_BITMASK 0x0F
|
||||
#define EXT16_BITMASK 0x03
|
||||
|
||||
#define E_MIN_EXTQF16 -15
|
||||
#define E_MIN_EXTQF32 -255
|
||||
|
||||
/* extqf32 NaN/Inf sentinels: 32-bit qf32 value + 4-bit LREQ, packed */
|
||||
#define extqf32_pos_nan 0X7FFFFF7FDULL
|
||||
#define extqf32_neg_nan 0X8000007F0ULL
|
||||
#define extqf32_pos_nan_inexact 0X7FFFFF7FCULL
|
||||
#define extqf32_neg_nan_inexact 0X8000007F1ULL
|
||||
#define extqf32_pos_inf 0x7FFFFF7F8ULL
|
||||
#define extqf32_pos_inf_exact 0x7FFFFF7F8ULL
|
||||
#define extqf32_pos_inf_inexact 0x7FFFFF7F9ULL
|
||||
#define extqf32_neg_inf 0x8000007F5ULL
|
||||
#define extqf32_neg_inf_exact 0x8000007F5ULL
|
||||
#define extqf32_neg_inf_inexact 0x8000007F4ULL
|
||||
|
||||
/* extqf16 NaN/Inf sentinels: 16-bit qf16 value + 2-bit LR, packed */
|
||||
#define extqf16_pos_nan 0x7FFF3
|
||||
#define extqf16_neg_nan 0x801F0
|
||||
#define extqf16_pos_inf 0x7FFF2
|
||||
#define extqf16_neg_inf 0x801F1
|
||||
|
||||
#define INIT_UNFLOAT(U) \
|
||||
unfloat U = { .sign = 0, .exp = 0, .inf = false, .nan = false, \
|
||||
.zero = false, \
|
||||
.parts = { .cls = float_class_zero } };
|
||||
|
||||
/*
|
||||
* "Un-normalized float": the common intermediate representation used while
|
||||
* parsing/adding/multiplying/rounding qfloat and IEEE values. `exp` retains
|
||||
* the source format's qfloat scale while `parts` holds its numeric value.
|
||||
* sign: literal sign bit for IEEE-parsed values; for qfloat-parsed
|
||||
* values this is instead the "deferred negation" flag left by
|
||||
* the extended-bits' R bit (see is_unfloat_neg()) -- not a
|
||||
* literal sign, except after is_unfloat_neg() resolves it.
|
||||
*/
|
||||
typedef struct {
|
||||
int exp;
|
||||
bool inf;
|
||||
bool nan;
|
||||
int sign;
|
||||
bool zero;
|
||||
FloatParts64 parts;
|
||||
} unfloat;
|
||||
|
||||
unfloat parse_hf(int16_t in);
|
||||
unfloat parse_sf_daz(uint32_t in, bool daz_mode);
|
||||
unfloat parse_extqf16(uint16_t in, uint8_t in_ext);
|
||||
unfloat parse_extqf32(uint32_t in, uint8_t in_ext);
|
||||
|
||||
int32_t conv_sf_extqf32(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd);
|
||||
int16_t conv_hf_extqf32(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd);
|
||||
int16_t conv_hf_extqf16(uint16_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd);
|
||||
uint16_t conv_qf32_to_bf(uint32_t a, uint8_t a_ext, qfrnd_mode_enum_t qfrnd);
|
||||
|
||||
int qf_vilog2(f_type type, uint32_t a, uint8_t a_ext);
|
||||
|
||||
uint64_t qfloat_round_ext_parts(unsigned size, FloatParts64 parts, int exp,
|
||||
bool deferred_sign,
|
||||
qfrnd_mode_enum_t qfrnd_mode);
|
||||
FloatParts64 qfloat_unfloat_parts(unfloat u);
|
||||
|
||||
uint64_t handle_infinity_nan_add(unfloat u, unfloat v, uint64_t pos_nan_val,
|
||||
uint64_t neg_nan_val, uint64_t pos_inf_val,
|
||||
uint64_t neg_inf_val);
|
||||
uint64_t handle_infinity_nan_mpy(unfloat u, unfloat v, uint64_t pos_nan_val,
|
||||
uint64_t neg_nan_val, uint64_t pos_inf_val,
|
||||
uint64_t neg_inf_val);
|
||||
|
||||
bool is_unfloat_neg(unfloat u);
|
||||
bool is_daz_mode(const CPUHexagonState *env);
|
||||
|
||||
unfloat legacy_parse_qf32(int32_t in);
|
||||
unfloat legacy_parse_qf16(int16_t in);
|
||||
uint32_t legacy_qf_add(int size, unfloat a, unfloat b);
|
||||
uint32_t legacy_qf_mpy(int size, unfloat a, unfloat b);
|
||||
int32_t legacy_conv_sf_qf32(uint32_t a);
|
||||
int16_t legacy_conv_hf_qf32(uint32_t a);
|
||||
int16_t legacy_conv_hf_qf16(uint16_t a);
|
||||
bool qfloat_is_extended(const CPUHexagonState *env);
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,8 @@ DEF_REG_FIELD(USR_FPINPF, 5, 1)
|
||||
|
||||
DEF_REG_FIELD(USR_LPCFG, 8, 2)
|
||||
|
||||
DEF_REG_FIELD(USR_FPDAZ, 17, 1)
|
||||
|
||||
DEF_REG_FIELD(USR_FPRND, 22, 2)
|
||||
|
||||
DEF_REG_FIELD(USR_FPINVE, 25, 1)
|
||||
|
||||
Reference in new issue
Block a user