target/arm: Implement BFCVTN for SVE
Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260609192110.752384-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Richard Henderson committed
Jun 9, 2026 at 12:20 UTC
1bfffd17deec118ca5ea21e58819b40197e50226
4 files changed
+99
target/arm/tcg/fp8_helper.c
+92
@@ -72,6 +72,17 @@ static FP8Context fp8_src_start(CPUARMState *env, uint32_t desc, int scale_mask)
72
return fp8_start(env, desc, f8fmt, scale);
73
}
74
75
+static FP8Context fp8_dst_start(CPUARMState *env, uint32_t desc, bool is_f16)
76
+{
77
+ uint64_t fpmr = env->vfp.fpmr;
78
+ FPMRType f8fmt = FIELD_EX64(fpmr, FPMR, F8D);
79
+ int scale = (is_f16
80
+ ? FIELD_SEX64(fpmr, FPMR, NSCALE_F16)
81
+ : FIELD_SEX64(fpmr, FPMR, NSCALE));
82
+
83
+ return fp8_start(env, desc, f8fmt, scale);
84
+}
85
+
86
/*
87
* Invalid input format: we could take one of the usual set of
88
* CONSTRAINED UNPREDICTABLE options for use of a reserved value,
@@ -111,6 +122,64 @@ static float16 fcvt_fp8_to_f16(uint8_t x, fp8_input_fn *f8fmt,
122
return float16_round_pack_canonical(&p, s);
123
}
124
125
+/*
126
+ * Invalid output format: we could take one of the usual set of
127
+ * CONSTRAINED UNPREDICTABLE options for use of a reserved value,
128
+ * but choose to take the additional option provided by the FPMR
129
+ * register specification, of setting the result to 0xff and
130
+ * signaling Invalid Operation.
131
+ */
132
+static uint8_t fcvt_fp8_invalid_output(FloatParts64 *p, int scale,
133
+ bool saturate, float_status *s)
134
+{
135
+ float_raise(float_flag_invalid, s);
136
+ return 0xff;
137
+}
138
+
139
+static uint8_t fcvt_fp8_e4m3_output(FloatParts64 *p, int scale,
140
+ bool saturate, float_status *s)
141
+{
142
+ *p = parts64_scalbn(p, scale, s);
143
+ /*
144
+ * Saturating Inf -> Max handled in uncanon_e4m3_overflow
145
+ * because there is no infinity encoding.
146
+ */
147
+ return float8_e4m3_round_pack_canonical(p, s, saturate);
148
+}
149
+
150
+static uint8_t fcvt_fp8_e5m2_output(FloatParts64 *p, int scale,
151
+ bool saturate, float_status *s)
152
+{
153
+ /*
154
+ * Because e5m2 has an infinity encoding, we need to handle
155
+ * saturation conversion of Inf -> Max manually.
156
+ */
157
+ if (unlikely(p->cls == float_class_inf)) {
158
+ if (saturate) {
159
+ /* maximum or minimum normal value for E5M2 */
160
+ return 0x7b | (p->sign << 7);
161
+ }
162
+ } else {
163
+ *p = parts64_scalbn(p, scale, s);
164
+ }
165
+ return float8_e5m2_round_pack_canonical(p, s, saturate);
166
+}
167
+
168
+typedef uint8_t fcvt_fp8_output_fn(FloatParts64 *, int, bool, float_status *);
169
+
170
+static fcvt_fp8_output_fn * const fcvt_fp8_output_fmt[8] = {
171
+ [0 ... 7] = fcvt_fp8_invalid_output,
172
+ [OFP8_E5M2] = fcvt_fp8_e5m2_output,
173
+ [OFP8_E4M3] = fcvt_fp8_e4m3_output,
174
+};
175
+
176
+static uint8_t fcvt_b16_to_fp8(bfloat16 x, fcvt_fp8_output_fn *f8fmt,
177
+ int scale, bool saturate, float_status *s)
178
+{
179
+ FloatParts64 p = bfloat16_unpack_canonical(x, s);
180
+ return f8fmt(&p, scale, saturate, s);
181
+}
182
+
183
void HELPER(advsimd_bfcvtl)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
184
{
185
FP8Context ctx = fp8_src_start(env, desc, 0x3f);
@@ -277,3 +346,26 @@ void HELPER(sme2_fcvtl_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
346
347
fp8_cvt_finish(env, &ctx);
348
}
349
+
350
+void HELPER(sve2_bfcvtn_bh)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
351
+{
352
+ FP8Context ctx = fp8_dst_start(env, desc, false);
353
+ fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
354
+ uint16_t *n0 = vn;
355
+ uint16_t *n1 = vn + sizeof(ARMVectorReg);
356
+ uint8_t *d = vd;
357
+ size_t oprsz = simd_oprsz(desc);
358
+ size_t nelem = oprsz / 2;
359
+ bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
360
+
361
+ for (size_t i = 0; i < nelem; ++i) {
362
+ bfloat16 e0 = n0[H2(i)];
363
+ bfloat16 e1 = n1[H2(i)];
364
+ d[H1(2 * i + 0)] = fcvt_b16_to_fp8(e0, output_fmt,
365
+ ctx.scale, osc, &ctx.stat);
366
+ d[H1(2 * i + 1)] = fcvt_b16_to_fp8(e1, output_fmt,
367
+ ctx.scale, osc, &ctx.stat);
368
+ }
369
+
370
+ fp8_cvt_finish(env, &ctx);
371
+}
target/arm/tcg/helper-fp8-defs.h
+2
@@ -12,3 +12,5 @@ DEF_HELPER_FLAGS_4(advsimd_fcvtl_hb, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
12
DEF_HELPER_FLAGS_4(sve2_fcvt_hb, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
13
DEF_HELPER_FLAGS_4(sme2_fcvt_hb, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
14
DEF_HELPER_FLAGS_4(sme2_fcvtl_hb, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
15
+
16
+DEF_HELPER_FLAGS_4(sve2_bfcvtn_bh, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32)
target/arm/tcg/sve.decode
+2
@@ -1101,6 +1101,8 @@ BF2CVT 01100101 00 001 000 001111 ..... ..... @rd_rn_e0
1101
BF1CVTLT 01100101 00 001 001 001110 ..... ..... @rd_rn_e0
1102
BF2CVTLT 01100101 00 001 001 001111 ..... ..... @rd_rn_e0
1103
1104
+BFCVTN 01100101 00 001 010 001110 ....0 ..... @rd_rnx2 esz=1
1105
+
1106
### SVE FP Compare with Zero Group
1107
1108
FCMGE_ppz0 01100101 .. 0100 00 001 ... ..... 0 .... @pd_pg_rn
target/arm/tcg/translate-sve.c
+3
@@ -4100,6 +4100,9 @@ TRANS_FEAT_STREAMING_IF(BF1CVTLT, aa64_sme2_or_sve2_f8cvt, aa64_sme2,
4100
TRANS_FEAT_STREAMING_IF(BF2CVTLT, aa64_sme2_or_sve2_f8cvt, aa64_sme2,
4101
do_f8cvt, a, gen_helper_sve2_bfcvt, true, true)
4102
4103
+TRANS_FEAT_STREAMING_IF(BFCVTN, aa64_sme2_or_sve2_f8cvt, aa64_sme2,
4104
+ do_f8cvt, a, gen_helper_sve2_bfcvtn_bh, false, false)
4105
+
4106
/*
4107
*** SVE Floating Point Compare with Zero Group
4108
*/