target/arm: Implement BF1CVTL, BF1CVTL2, BF2CVTL, BF2CVTL2 for AdvSIMD
Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260609192110.752384-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Richard Henderson committed
Jun 9, 2026 at 12:20 UTC
dbaa1996b6b4f149dbf1f214c7dc69f912a25b49
7 files changed
+183
target/arm/helper-fp8.h
new
+14
@@ -0,0 +1,14 @@
1
+/* SPDX-License-Identifier: GPL-2.0-or-later */
2
+
3
+#ifndef HELPER_FP8_H
4
+#define HELPER_FP8_H
5
+
6
+#include "exec/helper-proto-common.h"
7
+#include "exec/helper-gen-common.h"
8
+
9
+#define HELPER_H "tcg/helper-fp8-defs.h"
10
+#include "exec/helper-proto.h.inc"
11
+#include "exec/helper-gen.h.inc"
12
+#undef HELPER_H
13
+
14
+#endif /* HELPER_FP8_H */
target/arm/tcg/a64.decode
+3
@@ -1921,6 +1921,9 @@ URSQRTE_v 0.10 1110 101 00001 11001 0 ..... ..... @qrr_s
1921
1922
FCVTL_v 0.00 1110 0.1 00001 01111 0 ..... ..... @qrr_sd
1923
1924
+BF1CVTL 0.10 1110 101 00001 01111 0 ..... ..... @qrr_h
1925
+BF2CVTL 0.10 1110 111 00001 01111 0 ..... ..... @qrr_h
1926
+
1927
&fcvt_q rd rn esz q shift
1928
@fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \
1929
&fcvt_q esz=1 shift=%fcvt_f_sh_h
target/arm/tcg/fp8_helper.c
new
+124
@@ -0,0 +1,124 @@
1
+/*
2
+ * AArch64 FP8 Operations
3
+ * SPDX-License-Identifier: GPL-2.0-or-later
4
+ */
5
+
6
+#include "qemu/osdep.h"
7
+#include "cpu.h"
8
+#include "internals.h"
9
+#include "tcg/tcg-gvec-desc.h"
10
+#include "fpu/softfloat.h"
11
+#include "fpu/softfloat-parts.h"
12
+#include "helper-fp8.h"
13
+#include "vec_internal.h"
14
+
15
+#define HELPER_H "tcg/helper-fp8-defs.h"
16
+#include "exec/helper-info.c.inc"
17
+
18
+typedef enum FPMRType {
19
+ OFP8_E5M2 = 0,
20
+ OFP8_E4M3 = 1,
21
+} FPMRType;
22
+
23
+typedef struct FP8Context {
24
+ float_status stat;
25
+ ARMFPStatusFlavour fpst;
26
+ FPMRType f8fmt;
27
+ int scale;
28
+ bool high;
29
+} FP8Context;
30
+
31
+static FP8Context fp8_start(CPUARMState *env, uint32_t desc,
32
+ FPMRType f8fmt, int scale)
33
+{
34
+ ARMFPStatusFlavour fpst = extract32(desc, SIMD_DATA_SHIFT + 2, 4);
35
+
36
+ FP8Context ret = {
37
+ .stat = env->vfp.fp_status[fpst],
38
+ .fpst = fpst,
39
+ .f8fmt = f8fmt,
40
+ .scale = scale,
41
+ .high = extract32(desc, SIMD_DATA_SHIFT + 1, 1),
42
+ };
43
+
44
+ set_flush_to_zero(0, &ret.stat);
45
+ set_flush_inputs_to_zero(0, &ret.stat);
46
+ set_default_nan_mode(true, &ret.stat);
47
+ set_float_rounding_mode(float_round_nearest_even, &ret.stat);
48
+
49
+ return ret;
50
+}
51
+
52
+static void fp8_cvt_finish(CPUARMState *env, FP8Context *c)
53
+{
54
+ /* FP8 convert insns don't update FPSR.IDC */
55
+ int e = get_float_exception_flags(&c->stat);
56
+ float_raise(e & ~float_flag_input_denormal_used,
57
+ &env->vfp.fp_status[c->fpst]);
58
+}
59
+
60
+static FP8Context fp8_src_start(CPUARMState *env, uint32_t desc, int scale_mask)
61
+{
62
+ bool issrc2 = extract32(desc, SIMD_DATA_SHIFT, 1);
63
+ uint64_t fpmr = env->vfp.fpmr;
64
+ FPMRType f8fmt = (issrc2
65
+ ? FIELD_EX64(fpmr, FPMR, F8S2)
66
+ : FIELD_EX64(fpmr, FPMR, F8S1));
67
+ int scale;
68
+
69
+ scale = fpmr >> (issrc2 ? R_FPMR_LSCALE2_SHIFT : R_FPMR_LSCALE_SHIFT);
70
+ scale = -(scale & scale_mask);
71
+
72
+ return fp8_start(env, desc, f8fmt, scale);
73
+}
74
+
75
+/*
76
+ * Invalid input format: we could take one of the usual set of
77
+ * CONSTRAINED UNPREDICTABLE options for use of a reserved value,
78
+ * but choose to take the additional option provided by the FPMR
79
+ * register specification, of treating the input as if it were an SNaN.
80
+ *
81
+ * One of the uses of the input will convert to default nan (because
82
+ * all fp8 operations use default_nan_mode) and raise invalid (which
83
+ * the operation might suppress by not updating IOC).
84
+ */
85
+static FloatParts64 fp8_invalid_input(uint8_t x, float_status *s)
86
+{
87
+ return (FloatParts64){ .cls = float_class_snan };
88
+}
89
+
90
+typedef FloatParts64 fp8_input_fn(uint8_t x, float_status *s);
91
+
92
+static fp8_input_fn * const fp8_input_fmt[8] = {
93
+ [0 ... 7] = fp8_invalid_input,
94
+ [OFP8_E5M2] = float8_e5m2_unpack_canonical,
95
+ [OFP8_E4M3] = float8_e4m3_unpack_canonical,
96
+};
97
+
98
+static bfloat16 fcvt_fp8_to_b16(uint8_t x, fp8_input_fn *f8fmt,
99
+ int scale, float_status *s)
100
+{
101
+ FloatParts64 p = f8fmt(x, s);
102
+ p = parts64_scalbn(&p, scale, s);
103
+ return bfloat16_round_pack_canonical(&p, s);
104
+}
105
+
106
+void HELPER(advsimd_bfcvtl)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
107
+{
108
+ FP8Context ctx = fp8_src_start(env, desc, 0x3f);
109
+ fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
110
+ uint8_t *n = vn, scratch[16];
111
+ bfloat16 *d = vd;
112
+
113
+ if (vd == vn) {
114
+ n = memcpy(scratch, vn, 16);
115
+ }
116
+ n += ctx.high * 8;
117
+
118
+ for (size_t i = 0; i < 8; ++i) {
119
+ d[H2(i)] = fcvt_fp8_to_b16(n[H1(i)], input_fmt, ctx.scale, &ctx.stat);
120
+ }
121
+
122
+ fp8_cvt_finish(env, &ctx);
123
+ clear_tail(vd, 16, simd_maxsz(desc));
124
+}
target/arm/tcg/helper-fp8-defs.h
new
+6
@@ -0,0 +1,6 @@
1
+/*
2
+ * AArch64 FP8 helper definitions
3
+ * SPDX-License-Identifier: GPL-2.0-or-later
4
+ */
5
+
6
+DEF_HELPER_FLAGS_4(advsimd_bfcvtl, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
target/arm/tcg/meson.build
+1
@@ -41,6 +41,7 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
41
'sme_helper.c',
42
'sve_helper.c',
43
'vec_helper64.c',
44
+ 'fp8_helper.c',
45
))
46
47
arm_common_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c'))
target/arm/tcg/translate-a64.c
+34
@@ -22,6 +22,7 @@
22
#include "helper-a64.h"
23
#include "helper-sme.h"
24
#include "helper-sve.h"
25
+#include "helper-fp8.h"
26
#include "translate.h"
27
#include "translate-a64.h"
28
#include "tcg/tcg-op.h"
@@ -1461,6 +1462,24 @@ static bool fp_access_check(DisasContext *s)
1462
return fp_access_check_only(s) && nonstreaming_check(s);
1463
}
1464
1465
+/*
1466
+ * Check that FPMR access is enabled, for an indirect reference by a
1467
+ * vector instruction. See CheckFPMREnabled().
1468
+ */
1469
+bool fpmr_access_check(DisasContext *s)
1470
+{
1471
+ if (s->fpmr_el) {
1472
+ /*
1473
+ * While denied direct access to the FPMR raises SystemRegisterTrap
1474
+ * and targets a specific EL, denied indirect access to the FPMR
1475
+ * results in a simple UNDEFINED to the default exception level.
1476
+ */
1477
+ unallocated_encoding(s);
1478
+ return false;
1479
+ }
1480
+ return true;
1481
+}
1482
+
1483
/*
1484
* Return <0 for non-supported element sizes, with MO_16 controlled by
1485
* FEAT_FP16; return 0 for fp disabled; otherwise return >0 for success.
@@ -10709,6 +10728,21 @@ static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a)
10728
return true;
10729
}
10730
10731
+static bool do_f8cvt(DisasContext *s, arg_qrr_e *a,
10732
+ gen_helper_gvec_2_ptr *fn, bool issrc2)
10733
+{
10734
+ if (fpmr_access_check(s) && fp_access_check(s)) {
10735
+ tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->rd),
10736
+ vec_full_reg_offset(s, a->rn),
10737
+ tcg_env, 16, vec_full_reg_size(s),
10738
+ issrc2 | (a->q << 1) | (FPST_A64 << 2), fn);
10739
+ }
10740
+ return true;
10741
+}
10742
+
10743
+TRANS_FEAT(BF1CVTL, aa64_f8cvt, do_f8cvt, a, gen_helper_advsimd_bfcvtl, false)
10744
+TRANS_FEAT(BF2CVTL, aa64_f8cvt, do_f8cvt, a, gen_helper_advsimd_bfcvtl, true)
10745
+
10746
static bool trans_OK(DisasContext *s, arg_OK *a)
10747
{
10748
return true;
target/arm/tcg/translate-a64.h
+1
@@ -25,6 +25,7 @@ TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf);
25
void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v);
26
bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn,
27
unsigned int imms, unsigned int immr);
28
+bool fpmr_access_check(DisasContext *s);
29
bool sve_access_check(DisasContext *s);
30
bool sme_enabled_check(DisasContext *s);
31
bool sme_enabled_check_with_svcr(DisasContext *s, unsigned);