@samitouri / QOSamiQemu / commits / 1bfffd17de

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 */