master
c 1,057 lines 32.9 KB
Raw
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 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,
89 * but choose to take the additional option provided by the FPMR
90 * register specification, of treating the input as if it were an SNaN.
91 *
92 * One of the uses of the input will convert to default nan (because
93 * all fp8 operations use default_nan_mode) and raise invalid (which
94 * the operation might suppress by not updating IOC).
95 */
96 static FloatParts64 fp8_invalid_input(uint8_t x, float_status *s)
97 {
98 return (FloatParts64){ .cls = float_class_snan };
99 }
100
101 typedef FloatParts64 fp8_input_fn(uint8_t x, float_status *s);
102
103 static fp8_input_fn * const fp8_input_fmt[8] = {
104 [0 ... 7] = fp8_invalid_input,
105 [OFP8_E5M2] = float8_e5m2_unpack_canonical,
106 [OFP8_E4M3] = float8_e4m3_unpack_canonical,
107 };
108
109 static bfloat16 fcvt_fp8_to_b16(uint8_t x, fp8_input_fn *f8fmt,
110 int scale, float_status *s)
111 {
112 FloatParts64 p = f8fmt(x, s);
113 p = parts64_scalbn(&p, scale, s);
114 return bfloat16_round_pack_canonical(&p, s);
115 }
116
117 static float16 fcvt_fp8_to_f16(uint8_t x, fp8_input_fn *f8fmt,
118 int scale, float_status *s)
119 {
120 FloatParts64 p = f8fmt(x, s);
121 p = parts64_scalbn(&p, scale, s);
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 static uint8_t fcvt_f16_to_fp8(float16 x, fcvt_fp8_output_fn *f8fmt,
184 int scale, bool saturate, float_status *s)
185 {
186 FloatParts64 p = float16_unpack_canonical(x, s);
187 return f8fmt(&p, scale, saturate, s);
188 }
189
190 static uint8_t fcvt_f32_to_fp8(float32 x, fcvt_fp8_output_fn *f8fmt,
191 int scale, bool saturate, float_status *s)
192 {
193 FloatParts64 p = float32_unpack_canonical(x, s);
194 return f8fmt(&p, scale, saturate, s);
195 }
196
197 void HELPER(advsimd_bfcvtl)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
198 {
199 FP8Context ctx = fp8_src_start(env, desc, 0x3f);
200 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
201 uint8_t *n = vn, scratch[16];
202 bfloat16 *d = vd;
203
204 if (vd == vn) {
205 n = memcpy(scratch, vn, 16);
206 }
207 n += ctx.high * 8;
208
209 for (size_t i = 0; i < 8; ++i) {
210 d[H2(i)] = fcvt_fp8_to_b16(n[H1(i)], input_fmt, ctx.scale, &ctx.stat);
211 }
212
213 fp8_cvt_finish(env, &ctx);
214 clear_tail(vd, 16, simd_maxsz(desc));
215 }
216
217 void HELPER(advsimd_fcvtl_hb)(void *vd, void *vn,
218 CPUARMState *env, uint32_t desc)
219 {
220 FP8Context ctx = fp8_src_start(env, desc, 0xf);
221 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
222 uint8_t *n = vn, scratch[16];
223 float16 *d = vd;
224
225 if (vd == vn) {
226 n = memcpy(scratch, vn, 16);
227 }
228 n += ctx.high * 8;
229
230 for (size_t i = 0; i < 8; ++i) {
231 d[H2(i)] = fcvt_fp8_to_f16(n[H1(i)], input_fmt, ctx.scale, &ctx.stat);
232 }
233
234 fp8_cvt_finish(env, &ctx);
235 clear_tail(vd, 16, simd_maxsz(desc));
236 }
237
238 void HELPER(sve2_bfcvt)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
239 {
240 FP8Context ctx = fp8_src_start(env, desc, 0x3f);
241 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
242 uint8_t *n = vn;
243 uint16_t *d = vd;
244 size_t nelem = simd_oprsz(desc) / 2;
245
246 for (size_t i = 0; i < nelem; ++i) {
247 d[H2(i)] = fcvt_fp8_to_b16(n[H1(2 * i + ctx.high)],
248 input_fmt, ctx.scale, &ctx.stat);
249 }
250
251 fp8_cvt_finish(env, &ctx);
252 }
253
254 void HELPER(sve2_fcvt_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
255 {
256 FP8Context ctx = fp8_src_start(env, desc, 0xf);
257 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
258 uint8_t *n = vn;
259 uint16_t *d = vd;
260 size_t nelem = simd_oprsz(desc) / 2;
261
262 for (size_t i = 0; i < nelem; ++i) {
263 d[H2(i)] = fcvt_fp8_to_f16(n[H1(2 * i + ctx.high)],
264 input_fmt, ctx.scale, &ctx.stat);
265 }
266
267 fp8_cvt_finish(env, &ctx);
268 }
269
270 void HELPER(sme2_bfcvt_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
271 {
272 FP8Context ctx = fp8_src_start(env, desc, 0x3f);
273 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
274 uint8_t *n = vn;
275 uint16_t *d0 = vd;
276 uint16_t *d1 = vd + sizeof(ARMVectorReg);
277 size_t oprsz = simd_oprsz(desc);
278 size_t nelem = oprsz / 2;
279 ARMVectorReg scratch;
280
281 if (vectors_overlap(vd, 2, vn, 1)) {
282 n = memcpy(&scratch, vn, oprsz);
283 }
284
285 for (size_t i = 0; i < nelem; ++i) {
286 d0[H2(i)] = fcvt_fp8_to_b16(n[H1(i)], input_fmt,
287 ctx.scale, &ctx.stat);
288 }
289 for (size_t i = 0; i < nelem; ++i) {
290 d1[H2(i)] = fcvt_fp8_to_b16(n[H1(i + nelem)], input_fmt,
291 ctx.scale, &ctx.stat);
292 }
293
294 fp8_cvt_finish(env, &ctx);
295 }
296
297 void HELPER(sme2_fcvt_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
298 {
299 FP8Context ctx = fp8_src_start(env, desc, 0xf);
300 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
301 uint8_t *n = vn;
302 uint16_t *d0 = vd;
303 uint16_t *d1 = vd + sizeof(ARMVectorReg);
304 size_t oprsz = simd_oprsz(desc);
305 size_t nelem = oprsz / 2;
306 ARMVectorReg scratch;
307
308 if (vectors_overlap(vd, 2, vn, 1)) {
309 n = memcpy(&scratch, vn, oprsz);
310 }
311
312 for (size_t i = 0; i < nelem; ++i) {
313 d0[H2(i)] = fcvt_fp8_to_f16(n[H1(i)], input_fmt,
314 ctx.scale, &ctx.stat);
315 }
316 for (size_t i = 0; i < nelem; ++i) {
317 d1[H2(i)] = fcvt_fp8_to_f16(n[H1(i + nelem)], input_fmt,
318 ctx.scale, &ctx.stat);
319 }
320
321 fp8_cvt_finish(env, &ctx);
322 }
323
324 void HELPER(sme2_bfcvtl_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
325 {
326 FP8Context ctx = fp8_src_start(env, desc, 0x3f);
327 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
328 uint8_t *n = vn;
329 uint16_t *d0 = vd;
330 uint16_t *d1 = vd + sizeof(ARMVectorReg);
331 size_t oprsz = simd_oprsz(desc);
332 size_t nelem = oprsz / 2;
333
334 for (size_t i = 0; i < nelem; ++i) {
335 uint8_t e0 = n[H1(2 * i + 0)];
336 uint8_t e1 = n[H1(2 * i + 1)];
337 d0[H2(i)] = fcvt_fp8_to_b16(e0, input_fmt, ctx.scale, &ctx.stat);
338 d1[H2(i)] = fcvt_fp8_to_b16(e1, input_fmt, ctx.scale, &ctx.stat);
339 }
340
341 fp8_cvt_finish(env, &ctx);
342 }
343
344 void HELPER(sme2_fcvtl_hb)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
345 {
346 FP8Context ctx = fp8_src_start(env, desc, 0xf);
347 fp8_input_fn *input_fmt = fp8_input_fmt[ctx.f8fmt];
348 uint8_t *n = vn;
349 uint16_t *d0 = vd;
350 uint16_t *d1 = vd + sizeof(ARMVectorReg);
351 size_t oprsz = simd_oprsz(desc);
352 size_t nelem = oprsz / 2;
353
354 for (size_t i = 0; i < nelem; ++i) {
355 uint8_t e0 = n[H1(2 * i + 0)];
356 uint8_t e1 = n[H1(2 * i + 1)];
357 d0[H2(i)] = fcvt_fp8_to_f16(e0, input_fmt, ctx.scale, &ctx.stat);
358 d1[H2(i)] = fcvt_fp8_to_f16(e1, input_fmt, ctx.scale, &ctx.stat);
359 }
360
361 fp8_cvt_finish(env, &ctx);
362 }
363
364 void HELPER(sve2_bfcvtn_bh)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
365 {
366 FP8Context ctx = fp8_dst_start(env, desc, false);
367 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
368 uint16_t *n0 = vn;
369 uint16_t *n1 = vn + sizeof(ARMVectorReg);
370 uint8_t *d = vd;
371 size_t oprsz = simd_oprsz(desc);
372 size_t nelem = oprsz / 2;
373 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
374
375 for (size_t i = 0; i < nelem; ++i) {
376 bfloat16 e0 = n0[H2(i)];
377 bfloat16 e1 = n1[H2(i)];
378 d[H1(2 * i + 0)] = fcvt_b16_to_fp8(e0, output_fmt,
379 ctx.scale, osc, &ctx.stat);
380 d[H1(2 * i + 1)] = fcvt_b16_to_fp8(e1, output_fmt,
381 ctx.scale, osc, &ctx.stat);
382 }
383
384 fp8_cvt_finish(env, &ctx);
385 }
386
387 void HELPER(gvec_fcvt_bh)(void *vd, void *vn, void *vm,
388 CPUARMState *env, uint32_t desc)
389 {
390 FP8Context ctx = fp8_dst_start(env, desc, true);
391 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
392 uint16_t *n = vn;
393 uint16_t *m = vm;
394 uint8_t *d = vd;
395 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
396 size_t oprsz = simd_oprsz(desc);
397 size_t nelem = oprsz / 2;
398 ARMVectorReg scratch;
399
400 if (vd == vm) {
401 m = memcpy(&scratch, vm, oprsz);
402 }
403
404 for (size_t i = 0; i < nelem; ++i) {
405 d[H1(i)] = fcvt_f16_to_fp8(n[H2(i)], output_fmt,
406 ctx.scale, osc, &ctx.stat);
407 }
408 for (size_t i = 0; i < nelem; ++i) {
409 d[H1(i) + nelem] = fcvt_f16_to_fp8(m[H2(i)], output_fmt,
410 ctx.scale, osc, &ctx.stat);
411 }
412
413 fp8_cvt_finish(env, &ctx);
414 clear_tail(vd, oprsz, simd_maxsz(desc));
415 }
416
417 void HELPER(sve2_fcvtn_bh)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
418 {
419 FP8Context ctx = fp8_dst_start(env, desc, true);
420 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
421 uint16_t *n0 = vn;
422 uint16_t *n1 = vn + sizeof(ARMVectorReg);
423 uint8_t *d = vd;
424 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
425 size_t oprsz = simd_oprsz(desc);
426 size_t nelem = oprsz / 2;
427
428 for (size_t i = 0; i < nelem; ++i) {
429 float16 e0 = n0[H2(i)];
430 float16 e1 = n1[H2(i)];
431 d[H1(2 * i + 0)] = fcvt_f16_to_fp8(e0, output_fmt,
432 ctx.scale, osc, &ctx.stat);
433 d[H1(2 * i + 1)] = fcvt_f16_to_fp8(e1, output_fmt,
434 ctx.scale, osc, &ctx.stat);
435 }
436
437 fp8_cvt_finish(env, &ctx);
438 }
439
440 void HELPER(advsimd_fcvt_bs)(void *vd, void *vn, void *vm,
441 CPUARMState *env, uint32_t desc)
442 {
443 FP8Context ctx = fp8_dst_start(env, desc, false);
444 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
445 uint32_t *n = vn, *m = vm, scratch[4];
446 uint8_t *d = vd + 8 * ctx.high;
447 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
448
449 if (vd == vm) {
450 m = memcpy(scratch, vm, 16);
451 }
452
453 for (size_t i = 0; i < 4; ++i) {
454 d[H1(i + 0)] = fcvt_f32_to_fp8(n[H4(i)], output_fmt,
455 ctx.scale, osc, &ctx.stat);
456 }
457 for (size_t i = 0; i < 4; ++i) {
458 d[H1(i + 4)] = fcvt_f32_to_fp8(m[H4(i)], output_fmt,
459 ctx.scale, osc, &ctx.stat);
460 }
461
462 fp8_cvt_finish(env, &ctx);
463 clear_tail(vd, ctx.high ? 16 : 8, simd_maxsz(desc));
464 }
465
466 void HELPER(sve2_fcvtnb_bs)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
467 {
468 FP8Context ctx = fp8_dst_start(env, desc, false);
469 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
470 uint32_t *n0 = vn;
471 uint32_t *n1 = vn + sizeof(ARMVectorReg);
472 uint16_t *d = vd;
473 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
474 size_t oprsz = simd_oprsz(desc);
475 size_t nelem = oprsz / 4;
476
477 for (size_t i = 0; i < nelem; ++i) {
478 float32 e0 = n0[H4(i)];
479 float32 e1 = n1[H4(i)];
480 /* Zero-extend uint8_t to clear the odd lanes. */
481 d[H2(2 * i + 0)] = fcvt_f32_to_fp8(e0, output_fmt,
482 ctx.scale, osc, &ctx.stat);
483 d[H2(2 * i + 1)] = fcvt_f32_to_fp8(e1, output_fmt,
484 ctx.scale, osc, &ctx.stat);
485 }
486
487 fp8_cvt_finish(env, &ctx);
488 }
489
490 void HELPER(sve2_fcvtnt_bs)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
491 {
492 FP8Context ctx = fp8_dst_start(env, desc, false);
493 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
494 uint32_t *n0 = vn;
495 uint32_t *n1 = vn + sizeof(ARMVectorReg);
496 uint8_t *d = vd;
497 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
498 size_t oprsz = simd_oprsz(desc);
499 size_t nelem = oprsz / 4;
500
501 for (size_t i = 0; i < nelem; ++i) {
502 float32 e0 = n0[H4(i)];
503 float32 e1 = n1[H4(i)];
504 d[H1(4 * i + 1)] = fcvt_f32_to_fp8(e0, output_fmt,
505 ctx.scale, osc, &ctx.stat);
506 d[H1(4 * i + 3)] = fcvt_f32_to_fp8(e1, output_fmt,
507 ctx.scale, osc, &ctx.stat);
508 }
509
510 fp8_cvt_finish(env, &ctx);
511 }
512
513 void HELPER(sme2_fcvt_bs)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
514 {
515 ARMVectorReg scratch[4];
516 FP8Context ctx = fp8_dst_start(env, desc, false);
517 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
518 uint32_t *n = vn;
519 uint8_t *d = vd;
520 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
521 size_t oprsz = simd_oprsz(desc);
522 size_t nelem = oprsz / 4;
523 size_t stride = sizeof(ARMVectorReg) / 4;
524
525 if (vectors_overlap(vd, 1, vn, 4)) {
526 n = memcpy(scratch, vn, sizeof(scratch));
527 }
528
529 for (size_t i = 0; i < nelem; i++) {
530 for (size_t j = 0; j < 4; j++) {
531 d[H1(i + nelem * j)] = fcvt_f32_to_fp8(n[H4(i) + stride * j],
532 output_fmt, ctx.scale,
533 osc, &ctx.stat);
534 }
535 }
536
537 fp8_cvt_finish(env, &ctx);
538 }
539
540 void HELPER(sme2_fcvtn_bs)(void *vd, void *vn, CPUARMState *env, uint32_t desc)
541 {
542 FP8Context ctx = fp8_dst_start(env, desc, false);
543 fcvt_fp8_output_fn *output_fmt = fcvt_fp8_output_fmt[ctx.f8fmt];
544 uint32_t *n0 = vn;
545 uint32_t *n1 = vn + sizeof(ARMVectorReg);
546 uint32_t *n2 = vn + sizeof(ARMVectorReg) * 2;
547 uint32_t *n3 = vn + sizeof(ARMVectorReg) * 3;
548 uint8_t *d = vd;
549 bool osc = FIELD_EX64(env->vfp.fpmr, FPMR, OSC);
550 size_t oprsz = simd_oprsz(desc);
551 size_t nelem = oprsz / 4;
552
553 for (size_t i = 0; i < nelem; ++i) {
554 float32 e0 = n0[H4(i)];
555 float32 e1 = n1[H4(i)];
556 float32 e2 = n2[H4(i)];
557 float32 e3 = n3[H4(i)];
558
559 d[H1(4 * i + 0)] = fcvt_f32_to_fp8(e0, output_fmt,
560 ctx.scale, osc, &ctx.stat);
561 d[H1(4 * i + 1)] = fcvt_f32_to_fp8(e1, output_fmt,
562 ctx.scale, osc, &ctx.stat);
563 d[H1(4 * i + 2)] = fcvt_f32_to_fp8(e2, output_fmt,
564 ctx.scale, osc, &ctx.stat);
565 d[H1(4 * i + 3)] = fcvt_f32_to_fp8(e3, output_fmt,
566 ctx.scale, osc, &ctx.stat);
567 }
568
569 fp8_cvt_finish(env, &ctx);
570 }
571
572 typedef struct FP8MulContext {
573 float_status stat;
574 fp8_input_fn *fmt1;
575 fp8_input_fn *fmt2;
576 int scale;
577 } FP8MulContext;
578
579 static FP8MulContext fp8_mul_start(CPUARMState *env, int scale_mask)
580 {
581 uint64_t fpmr = env->vfp.fpmr;
582
583 FP8MulContext ret = {
584 .stat = env->vfp.fp_status[FPST_A64],
585 .fmt1 = fp8_input_fmt[FIELD_EX64(fpmr, FPMR, F8S1)],
586 .fmt2 = fp8_input_fmt[FIELD_EX64(fpmr, FPMR, F8S2)],
587 .scale = -(FIELD_EX64(fpmr, FPMR, LSCALE) & scale_mask),
588 };
589
590 set_flush_to_zero(0, &ret.stat);
591 set_flush_inputs_to_zero(0, &ret.stat);
592 set_default_nan_mode(true, &ret.stat);
593 set_float_rounding_mode(FIELD_EX64(fpmr, FPMR, OSM)
594 ? float_round_nearest_even_max
595 : float_round_nearest_even, &ret.stat);
596
597 /*
598 * FP8 multiplies don't update any of the FPSR exception flags,
599 * so we do not need an fp8_mul_finish() to propagate status
600 * changes back from ret.stat into env->vfp.fp_status[].
601 */
602 return ret;
603 }
604
605 static FloatParts64 f8dot(uint64_t a, uint64_t b, int n, FP8MulContext *ctx)
606 {
607 /*
608 * Because of default_nan_mode, NaNs need no special handling.
609 * We'll simply get the default NaN out at the end of the sequence.
610 */
611 FloatParts64 p0 = ctx->fmt1(a & 0xff, &ctx->stat);
612 FloatParts64 p1 = ctx->fmt2(b & 0xff, &ctx->stat);
613 FloatParts64 pr = parts64_mul(&p0, &p1, &ctx->stat);
614
615 for (int i = 1; i < n; ++i) {
616 p0 = ctx->fmt1(extract64(a, i * 8, 8), &ctx->stat);
617 p1 = ctx->fmt2(extract64(b, i * 8, 8), &ctx->stat);
618 pr = parts64_muladd(&p0, &p1, &pr, 0, &ctx->stat);
619 }
620 return parts64_scalbn(&pr, ctx->scale, &ctx->stat);
621 }
622
623 static float16 f8dotadd_h(uint64_t a, uint64_t b, int n, float16 c,
624 FP8MulContext *ctx)
625 {
626 FloatParts64 p0 = f8dot(a, b, n, ctx);
627 FloatParts64 p1 = float16_unpack_canonical(c, &ctx->stat);
628
629 p0 = parts64_addsub(&p0, &p1, &ctx->stat, false);
630 return float16_round_pack_canonical(&p0, &ctx->stat);
631 }
632
633 static float32 f8dotadd_s(uint64_t a, uint64_t b, int n, float32 c,
634 FP8MulContext *ctx)
635 {
636 FloatParts64 p0 = f8dot(a, b, n, ctx);
637 FloatParts64 p1 = float32_unpack_canonical(c, &ctx->stat);
638
639 p0 = parts64_addsub(&p0, &p1, &ctx->stat, false);
640 return float32_round_pack_canonical(&p0, &ctx->stat);
641 }
642
643 void HELPER(gvec_fmla_hb)(void *vd, void *vn, void *vm,
644 CPUARMState *env, uint32_t desc)
645 {
646 FP8MulContext ctx = fp8_mul_start(env, 0xf);
647 bool high = extract32(desc, SIMD_DATA_SHIFT, 1);
648 size_t oprsz = simd_oprsz(desc);
649 size_t nelem = oprsz / 2;
650 uint8_t *n = vn;
651 uint8_t *m = vm;
652 float16 *d = vd;
653
654 for (size_t i = 0; i < nelem; i++) {
655 uint8_t e0 = n[H1(2 * i + high)];
656 uint8_t e1 = m[H1(2 * i + high)];
657
658 d[H2(i)] = f8dotadd_h(e0, e1, 1, d[H2(i)], &ctx);
659 }
660
661 clear_tail(vd, oprsz, simd_maxsz(desc));
662 }
663
664 void HELPER(gvec_fmla_idx_hb)(void *vd, void *vn, void *vm,
665 CPUARMState *env, uint32_t desc)
666 {
667 FP8MulContext ctx = fp8_mul_start(env, 0xf);
668 bool idx_n = extract32(desc, SIMD_DATA_SHIFT, 1);
669 size_t idx_m = extract32(desc, SIMD_DATA_SHIFT + 2, 4);
670 size_t oprsz = simd_oprsz(desc);
671 size_t nelem = oprsz / 2;
672 uint8_t *n = vn;
673 uint8_t *m = vm;
674 float16 *d = vd;
675 size_t i = 0;
676
677 do {
678 uint8_t e1 = m[2 * i + H1(idx_m)];
679 do {
680 uint8_t e0 = n[H1(2 * i + idx_n)];
681 d[H2(i)] = f8dotadd_h(e0, e1, 1, d[H2(i)], &ctx);
682 } while (++i % 8 != 0);
683 } while (i < nelem);
684
685 clear_tail(vd, oprsz, simd_maxsz(desc));
686 }
687
688 void HELPER(gvec_fmla_sb)(void *vd, void *vn, void *vm,
689 CPUARMState *env, uint32_t desc)
690 {
691 FP8MulContext ctx = fp8_mul_start(env, -1);
692 size_t idx = extract32(desc, SIMD_DATA_SHIFT, 2);
693 size_t oprsz = simd_oprsz(desc);
694 size_t nelem = oprsz / 4;
695 uint8_t *n = vn;
696 uint8_t *m = vm;
697 float32 *d = vd;
698
699 for (size_t i = 0; i < nelem; i++) {
700 uint8_t e0 = n[H1(4 * i + idx)];
701 uint8_t e1 = m[H1(4 * i + idx)];
702
703 d[H4(i)] = f8dotadd_s(e0, e1, 1, d[H4(i)], &ctx);
704 }
705
706 clear_tail(vd, oprsz, simd_maxsz(desc));
707 }
708
709 void HELPER(gvec_fmla_idx_sb)(void *vd, void *vn, void *vm,
710 CPUARMState *env, uint32_t desc)
711 {
712 FP8MulContext ctx = fp8_mul_start(env, -1);
713 size_t idx_n = extract32(desc, SIMD_DATA_SHIFT, 2);
714 size_t idx_m = extract32(desc, SIMD_DATA_SHIFT + 2, 4);
715 size_t oprsz = simd_oprsz(desc);
716 size_t nelem = oprsz / 4;
717 uint8_t *n = vn;
718 uint8_t *m = vm;
719 float32 *d = vd;
720 size_t i = 0;
721
722 do {
723 uint8_t e1 = m[4 * i + H1(idx_m)];
724 do {
725 uint8_t e0 = n[H1(4 * i + idx_n)];
726 d[H4(i)] = f8dotadd_s(e0, e1, 1, d[H4(i)], &ctx);
727 } while (++i % 4 != 0);
728 } while (i < nelem);
729
730 clear_tail(vd, oprsz, simd_maxsz(desc));
731 }
732
733 void HELPER(gvec_fdot_sb)(void *vd, void *vn, void *vm,
734 CPUARMState *env, uint32_t desc)
735 {
736 FP8MulContext ctx = fp8_mul_start(env, -1);
737 size_t oprsz = simd_oprsz(desc);
738 size_t nelem = oprsz / 4;
739 uint32_t *n = vn;
740 uint32_t *m = vm;
741 float32 *d = vd;
742
743 for (size_t i = 0; i < nelem; i++) {
744 d[i] = f8dotadd_s(n[i], m[i], 4, d[i], &ctx);
745 }
746
747 clear_tail(vd, oprsz, simd_maxsz(desc));
748 }
749
750 void HELPER(gvec_fdot_idx_sb)(void *vd, void *vn, void *vm,
751 CPUARMState *env, uint32_t desc)
752 {
753 FP8MulContext ctx = fp8_mul_start(env, -1);
754 size_t idx = simd_data(desc);
755 size_t oprsz = simd_oprsz(desc);
756 size_t nelem = oprsz / 4;
757 uint32_t *n = vn;
758 uint32_t *m = vm;
759 float32 *d = vd;
760 size_t i = 0;
761
762 do {
763 uint32_t e1 = m[i + H4(idx)];
764 do {
765 d[i] = f8dotadd_s(n[i], e1, 4, d[i], &ctx);
766 } while (++i % 4 != 0);
767 } while (i < nelem);
768
769 clear_tail(vd, oprsz, simd_maxsz(desc));
770 }
771
772 void HELPER(gvec_fdot_hb)(void *vd, void *vn, void *vm,
773 CPUARMState *env, uint32_t desc)
774 {
775 FP8MulContext ctx = fp8_mul_start(env, 0xf);
776 size_t oprsz = simd_oprsz(desc);
777 size_t nelem = oprsz / 2;
778 uint16_t *n = vn;
779 uint16_t *m = vm;
780 float16 *d = vd;
781
782 for (size_t i = 0; i < nelem; i++) {
783 d[i] = f8dotadd_h(n[i], m[i], 2, d[i], &ctx);
784 }
785
786 clear_tail(vd, oprsz, simd_maxsz(desc));
787 }
788
789 void HELPER(gvec_fdot_idx_hb)(void *vd, void *vn, void *vm,
790 CPUARMState *env, uint32_t desc)
791 {
792 FP8MulContext ctx = fp8_mul_start(env, 0xf);
793 size_t idx = simd_data(desc);
794 size_t oprsz = simd_oprsz(desc);
795 size_t nelem = oprsz / 2;
796 uint16_t *n = vn;
797 uint16_t *m = vm;
798 float16 *d = vd;
799 size_t i = 0;
800
801 do {
802 uint16_t e1 = m[i + H2(idx)];
803 do {
804 d[i] = f8dotadd_h(n[i], e1, 2, d[i], &ctx);
805 } while (++i % 8 != 0);
806 } while (i < nelem);
807
808 clear_tail(vd, oprsz, simd_maxsz(desc));
809 }
810
811 void HELPER(gvec_fmmla_sb)(void *vd, void *vn, void *vm,
812 CPUARMState *env, uint32_t desc)
813 {
814 FP8MulContext ctx = fp8_mul_start(env, -1);
815 size_t oprsz = simd_oprsz(desc);
816 size_t nseg = oprsz / 16;
817 uint64_t *n = vn;
818 uint64_t *m = vm;
819 float32 *d = vd;
820
821 for (size_t seg = 0; seg < nseg; seg++, d += 4, n += 2, m += 2) {
822 float32 d0 = f8dotadd_s(n[0], m[0], 8, d[H4(0)], &ctx);
823 float32 d1 = f8dotadd_s(n[0], m[1], 8, d[H4(1)], &ctx);
824 float32 d2 = f8dotadd_s(n[1], m[0], 8, d[H4(2)], &ctx);
825 float32 d3 = f8dotadd_s(n[1], m[1], 8, d[H4(3)], &ctx);
826
827 d[H4(0)] = d0;
828 d[H4(1)] = d1;
829 d[H4(2)] = d2;
830 d[H4(3)] = d3;
831 }
832
833 clear_tail(vd, oprsz, simd_maxsz(desc));
834 }
835
836 void HELPER(gvec_fmmla_hb)(void *vd, void *vn, void *vm,
837 CPUARMState *env, uint32_t desc)
838 {
839 FP8MulContext ctx = fp8_mul_start(env, 0xf);
840 size_t oprsz = simd_oprsz(desc);
841 size_t nseg = oprsz / 8;
842 uint32_t *n = vn;
843 uint32_t *m = vm;
844 float16 *d = vd;
845
846 for (size_t seg = 0; seg < nseg; seg++, d += 4, n += 2, m += 2) {
847 float16 d0 = f8dotadd_h(n[H4(0)], m[H4(0)], 4, d[H2(0)], &ctx);
848 float16 d1 = f8dotadd_h(n[H4(0)], m[H4(1)], 4, d[H2(1)], &ctx);
849 float16 d2 = f8dotadd_h(n[H4(1)], m[H4(0)], 4, d[H2(2)], &ctx);
850 float16 d3 = f8dotadd_h(n[H4(1)], m[H4(1)], 4, d[H2(3)], &ctx);
851
852 d[H2(0)] = d0;
853 d[H2(1)] = d1;
854 d[H2(2)] = d2;
855 d[H2(3)] = d3;
856 }
857
858 clear_tail(vd, oprsz, simd_maxsz(desc));
859 }
860
861 void HELPER(sme_fmopa_sb)(void *vza, void *vzn, void *vzm, void *vpn,
862 void *vpm, CPUARMState *env, uint32_t desc)
863 {
864 FP8MulContext ctx = fp8_mul_start(env, -1);
865 intptr_t oprsz = simd_maxsz(desc);
866 uint16_t *pn = vpn, *pm = vpm;
867
868 for (intptr_t row = 0; row < oprsz; ) {
869 uint16_t prow = pn[H2(row >> 4)];
870 do {
871 void *vza_row = vza + tile_vslice_offset(row);
872 uint32_t n = *(uint32_t *)(vzn + H1_4(row));
873
874 n &= expand_pred_b(prow & 0xf);
875
876 for (intptr_t col = 0; col < oprsz; ) {
877 uint16_t pcol = pm[H2(col >> 4)];
878 do {
879 if (prow & pcol & 0xf) {
880 uint32_t *a = vza_row + H1_4(col);
881 uint32_t m = *(uint32_t *)(vzm + H1_4(col));
882
883 m &= expand_pred_b(pcol & 0xf);
884 *a = f8dotadd_s(n, m, 4, *a, &ctx);
885 }
886 col += 4;
887 pcol >>= 4;
888 } while (col & 15);
889 }
890 row += 4;
891 prow >>= 4;
892 } while (row & 15);
893 }
894 }
895
896 void HELPER(sme_fmopa_hb)(void *vza, void *vzn, void *vzm, void *vpn,
897 void *vpm, CPUARMState *env, uint32_t desc)
898 {
899 FP8MulContext ctx = fp8_mul_start(env, 0xf);
900 intptr_t oprsz = simd_maxsz(desc);
901 uint16_t *pn = vpn, *pm = vpm;
902
903 for (intptr_t row = 0; row < oprsz; ) {
904 uint16_t prow = pn[H2(row >> 4)];
905 do {
906 void *vza_row = vza + tile_vslice_offset(row);
907 uint16_t n = *(uint16_t *)(vzn + H1_2(row));
908
909 n &= expand_pred_b(prow & 3);
910
911 for (intptr_t col = 0; col < oprsz; ) {
912 uint16_t pcol = pm[H2(col >> 4)];
913 do {
914 if (prow & pcol & 0x3) {
915 uint16_t *a = vza_row + H1_2(col);
916 uint16_t m = *(uint16_t *)(vzm + H1_2(col));
917
918 m &= expand_pred_b(pcol & 3);
919 *a = f8dotadd_h(n, m, 2, *a, &ctx);
920 }
921 col += 2;
922 pcol >>= 2;
923 } while (col & 15);
924 }
925 row += 2;
926 prow >>= 2;
927 } while (row & 15);
928 }
929 }
930
931 void HELPER(sme_fvdot_idx_sb)(void *vd, void *vn, void *vm,
932 CPUARMState *env, uint32_t desc)
933 {
934 FP8MulContext ctx = fp8_mul_start(env, -1);
935 intptr_t oprsz = simd_maxsz(desc);
936 intptr_t elements = oprsz / sizeof(float32);
937 int idx_n = extract32(desc, SIMD_DATA_SHIFT, 2);
938 int idx_m = extract32(desc, SIMD_DATA_SHIFT + 2, 3);
939 float32 *d = vd;
940 uint8_t *n0 = vn;
941 uint8_t *n1 = vn + sizeof(ARMVectorReg);
942 uint16_t *m = vm;
943 intptr_t i = 0;
944
945 do {
946 uint16_t mm = m[H2(2 * i + idx_m)];
947 do {
948 uint16_t nn = n0[H1(4 * i + idx_n)] | (n1[H1(4 * i + idx_n)] << 8);
949 d[H4(i)] = f8dotadd_s(nn, mm, 2, d[H4(i)], &ctx);
950 } while (++i & 3);
951 } while (i < elements);
952 }
953
954 void HELPER(sme_fvdot_idx_hb)(void *vd, void *vn, void *vm,
955 CPUARMState *env, uint32_t desc)
956 {
957 FP8MulContext ctx = fp8_mul_start(env, 0xf);
958 intptr_t oprsz = simd_maxsz(desc);
959 intptr_t elements = oprsz / sizeof(float16);
960 int idx_n = extract32(desc, SIMD_DATA_SHIFT, 1);
961 int idx_m = extract32(desc, SIMD_DATA_SHIFT + 1, 3);
962 float16 *d = vd;
963 uint8_t *n0 = vn;
964 uint8_t *n1 = vn + sizeof(ARMVectorReg);
965 uint16_t *m = vm;
966 intptr_t i = 0;
967
968 do {
969 uint16_t mm = m[H2(2 * i + idx_m)];
970 do {
971 uint16_t nn = n0[H1(4 * i + idx_n)] | (n1[H1(4 * i + idx_n)] << 8);
972 d[H2(i)] = f8dotadd_h(nn, mm, 2, d[H2(i)], &ctx);
973 } while (++i & 7);
974 } while (i < elements);
975 }
976
977 static void inner_fmop4a_sb(void *vd, void *vn, void *vm, void *vinfo)
978 {
979 float32 *d = vd;
980 uint32_t *n = vn, *m = vm;
981 FP8MulContext *ctx = vinfo;
982
983 *d = f8dotadd_s(*n, *m, 4, *d, ctx);
984 }
985
986 void HELPER(sme_fmop4a_sb)(void *vza, void *vzn, void *vzm,
987 CPUArchState *env, uint32_t desc)
988 {
989 FP8MulContext ctx = fp8_mul_start(env, -1);
990 sme_mop4(vza, vzn, vzm, &ctx, desc, sizeof(float32), inner_fmop4a_sb);
991 }
992
993 static void inner_fmop4a_hb(void *vd, void *vn, void *vm, void *vinfo)
994 {
995 float16 *d = vd;
996 uint16_t *n = vn, *m = vm;
997 FP8MulContext *ctx = vinfo;
998
999 *d = f8dotadd_h(*n, *m, 2, *d, ctx);
1000 }
1001
1002 void HELPER(sme_fmop4a_hb)(void *vza, void *vzn, void *vzm,
1003 CPUArchState *env, uint32_t desc)
1004 {
1005 FP8MulContext ctx = fp8_mul_start(env, 0xf);
1006 sme_mop4(vza, vzn, vzm, &ctx, desc, sizeof(float16), inner_fmop4a_hb);
1007 }
1008
1009 void HELPER(sme_ftmopa_hb)(void *vza, void *vzn, void *vzm, void *vzk,
1010 CPUArchState *env, uint32_t desc)
1011 {
1012 FP8MulContext ctx = fp8_mul_start(env, 0xf);
1013 intptr_t oprsz = simd_maxsz(desc);
1014 intptr_t dim = oprsz >> MO_16;
1015 intptr_t index = simd_data(desc);
1016 intptr_t ctrl_base = (index * oprsz) >> 1;
1017 uint8_t *zn0 = vzn, *zn1 = vzn + sizeof(ARMVectorReg);
1018 uint16_t *za = vza, *zm = vzm;
1019 uint64_t *zk = vzk;
1020
1021 for (intptr_t row = 0; row < dim; row++) {
1022 uint16_t *za_row = za + tile_vslice_offset(row);
1023
1024 for (intptr_t col = 0; col < dim; col++) {
1025 uint16_t e2 = zm[H2(col)];
1026 uint16_t *e3 = za_row + H2(col);
1027 uint16_t e1 = 0;
1028
1029 /*
1030 * Four control bits select two elements. The two elements
1031 * may be non-contiguous, so assemble them locally into e1.
1032 * Pseudo-code has a double loop running forward, with a
1033 * test for (i < 2) to limit construction to 2 elements.
1034 * Easier to run a single loop backward, shifting extra
1035 * elements off the top of our uint16_t.
1036 */
1037 uint64_t this_ctrl = extractn(zk, ctrl_base + col * 4, 4);
1038 for (int i = 3; i >= 0; i--) {
1039 if (this_ctrl & (1 << i)) {
1040 bool e = i & 1;
1041 bool r = i & 2;
1042 uint8_t *p = (r ? zn1 : zn0) + H1(2 * row + e);
1043 e1 = (e1 << 8) | *p;
1044 }
1045 }
1046
1047 *e3 = f8dotadd_h(e1, e2, 2, *e3, &ctx);
1048 }
1049 }
1050 }
1051
1052 void HELPER(sme_ftmopa_sb)(void *vza, void *vzn, void *vzm, void *vzk,
1053 CPUArchState *env, uint32_t desc)
1054 {
1055 FP8MulContext ctx = fp8_mul_start(env, 0xf);
1056 sme_tmop_4way_sb(vza, vzn, vzm, vzk, &ctx, desc, inner_fmop4a_sb);
1057 }