master
h 565 lines 18.8 KB
Raw
1 /*
2 * ARM AdvSIMD / SVE Vector Helpers
3 *
4 * Copyright (c) 2020 Linaro
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef TARGET_ARM_VEC_INTERNAL_H
21 #define TARGET_ARM_VEC_INTERNAL_H
22
23 #include "fpu/softfloat.h"
24 #include "vector-type.h"
25
26 typedef struct CPUArchState CPUARMState;
27
28 /*
29 * Note that vector data is stored in host-endian 64-bit chunks,
30 * so addressing units smaller than that needs a host-endian fixup.
31 *
32 * The H<N> macros are used when indexing an array of elements of size N.
33 *
34 * The H1_<N> macros are used when performing byte arithmetic and then
35 * casting the final pointer to a type of size N.
36 */
37 #if HOST_BIG_ENDIAN
38 #define H1(x) ((x) ^ 7)
39 #define H1_2(x) ((x) ^ 6)
40 #define H1_4(x) ((x) ^ 4)
41 #define H2(x) ((x) ^ 3)
42 #define H4(x) ((x) ^ 1)
43 #else
44 #define H1(x) (x)
45 #define H1_2(x) (x)
46 #define H1_4(x) (x)
47 #define H2(x) (x)
48 #define H4(x) (x)
49 #endif
50 /*
51 * Access to 64-bit elements isn't host-endian dependent; we provide H8
52 * and H1_8 so that when a function is being generated from a macro we
53 * can pass these rather than an empty macro argument, for clarity.
54 */
55 #define H8(x) (x)
56 #define H1_8(x) (x)
57
58 /*
59 * When considering the ZA storage as an array of elements of
60 * type T, the index within that array of the Nth element of
61 * a vertical slice of a tile can be calculated like this,
62 * regardless of the size of type T. This is because the tiles
63 * are interleaved, so if type T is size N bytes then row 1 of
64 * the tile is N rows away from row 0. The division by N to
65 * convert a byte offset into an array index and the multiplication
66 * by N to convert from vslice-index-within-the-tile to
67 * the index within the ZA storage cancel out.
68 */
69 #define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg))
70
71 /*
72 * When doing byte arithmetic on the ZA storage, the element
73 * byteoff bytes away in a tile vertical slice is always this
74 * many bytes away in the ZA storage, regardless of the
75 * size of the tile element, assuming that byteoff is a multiple
76 * of the element size. Again this is because of the interleaving
77 * of the tiles. For instance if we have 1 byte per element then
78 * each row of the ZA storage has one byte of the vslice data,
79 * and (counting from 0) byte 8 goes in row 8 of the storage
80 * at offset (8 * row-size-in-bytes).
81 * If we have 8 bytes per element then each row of the ZA storage
82 * has 8 bytes of the data, but there are 8 interleaved tiles and
83 * so byte 8 of the data goes into row 1 of the tile,
84 * which is again row 8 of the storage, so the offset is still
85 * (8 * row-size-in-bytes). Similarly for other element sizes.
86 */
87 #define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg))
88
89 /*
90 * Expand active predicate bits to bytes, for byte elements.
91 */
92 extern const uint64_t expand_pred_b_data[256];
93 static inline uint64_t expand_pred_b(uint8_t byte)
94 {
95 return expand_pred_b_data[byte];
96 }
97
98 /* Similarly for half-word elements. */
99 extern const uint64_t expand_pred_h_data[0x55 + 1];
100 static inline uint64_t expand_pred_h(uint8_t byte)
101 {
102 return expand_pred_h_data[byte & 0x55];
103 }
104
105 static inline void clear_tail(void *vd, uintptr_t opr_sz, uintptr_t max_sz)
106 {
107 uint64_t *d = vd + opr_sz;
108 uintptr_t i;
109
110 for (i = opr_sz; i < max_sz; i += 8) {
111 *d++ = 0;
112 }
113 }
114
115 static inline int32_t do_sqrshl_bhs(int32_t src, int32_t shift, int bits,
116 bool round, uint32_t *sat)
117 {
118 if (shift <= -bits) {
119 /* Rounding the sign bit always produces 0. */
120 if (round) {
121 return 0;
122 }
123 return src >> 31;
124 } else if (shift < 0) {
125 if (round) {
126 src >>= -shift - 1;
127 return (src >> 1) + (src & 1);
128 }
129 return src >> -shift;
130 } else if (shift < bits) {
131 int32_t val = src << shift;
132 if (bits == 32) {
133 if (!sat || val >> shift == src) {
134 return val;
135 }
136 } else {
137 int32_t extval = sextract32(val, 0, bits);
138 if (!sat || val == extval) {
139 return extval;
140 }
141 }
142 } else if (!sat || src == 0) {
143 return 0;
144 }
145
146 *sat = 1;
147 return (1u << (bits - 1)) - (src >= 0);
148 }
149
150 static inline uint32_t do_uqrshl_bhs(uint32_t src, int32_t shift, int bits,
151 bool round, uint32_t *sat)
152 {
153 if (shift <= -(bits + round)) {
154 return 0;
155 } else if (shift < 0) {
156 if (round) {
157 src >>= -shift - 1;
158 return (src >> 1) + (src & 1);
159 }
160 return src >> -shift;
161 } else if (shift < bits) {
162 uint32_t val = src << shift;
163 if (bits == 32) {
164 if (!sat || val >> shift == src) {
165 return val;
166 }
167 } else {
168 uint32_t extval = extract32(val, 0, bits);
169 if (!sat || val == extval) {
170 return extval;
171 }
172 }
173 } else if (!sat || src == 0) {
174 return 0;
175 }
176
177 *sat = 1;
178 return MAKE_64BIT_MASK(0, bits);
179 }
180
181 static inline int32_t do_suqrshl_bhs(int32_t src, int32_t shift, int bits,
182 bool round, uint32_t *sat)
183 {
184 if (sat && src < 0) {
185 *sat = 1;
186 return 0;
187 }
188 return do_uqrshl_bhs(src, shift, bits, round, sat);
189 }
190
191 static inline int64_t do_sqrshl_d(int64_t src, int64_t shift,
192 bool round, uint32_t *sat)
193 {
194 if (shift <= -64) {
195 /* Rounding the sign bit always produces 0. */
196 if (round) {
197 return 0;
198 }
199 return src >> 63;
200 } else if (shift < 0) {
201 if (round) {
202 src >>= -shift - 1;
203 return (src >> 1) + (src & 1);
204 }
205 return src >> -shift;
206 } else if (shift < 64) {
207 int64_t val = src << shift;
208 if (!sat || val >> shift == src) {
209 return val;
210 }
211 } else if (!sat || src == 0) {
212 return 0;
213 }
214
215 *sat = 1;
216 return src < 0 ? INT64_MIN : INT64_MAX;
217 }
218
219 static inline uint64_t do_uqrshl_d(uint64_t src, int64_t shift,
220 bool round, uint32_t *sat)
221 {
222 if (shift <= -(64 + round)) {
223 return 0;
224 } else if (shift < 0) {
225 if (round) {
226 src >>= -shift - 1;
227 return (src >> 1) + (src & 1);
228 }
229 return src >> -shift;
230 } else if (shift < 64) {
231 uint64_t val = src << shift;
232 if (!sat || val >> shift == src) {
233 return val;
234 }
235 } else if (!sat || src == 0) {
236 return 0;
237 }
238
239 *sat = 1;
240 return UINT64_MAX;
241 }
242
243 static inline int64_t do_suqrshl_d(int64_t src, int64_t shift,
244 bool round, uint32_t *sat)
245 {
246 if (sat && src < 0) {
247 *sat = 1;
248 return 0;
249 }
250 return do_uqrshl_d(src, shift, round, sat);
251 }
252
253 int8_t do_sqrdmlah_b(int8_t, int8_t, int8_t, bool, bool);
254 int16_t do_sqrdmlah_h(int16_t, int16_t, int16_t, bool, bool, uint32_t *);
255 int32_t do_sqrdmlah_s(int32_t, int32_t, int32_t, bool, bool, uint32_t *);
256 int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool);
257
258 #define do_ssat_b(val) MIN(MAX(val, INT8_MIN), INT8_MAX)
259 #define do_ssat_h(val) MIN(MAX(val, INT16_MIN), INT16_MAX)
260 #define do_ssat_s(val) MIN(MAX(val, INT32_MIN), INT32_MAX)
261 #define do_usat_b(val) MIN(MAX(val, 0), UINT8_MAX)
262 #define do_usat_h(val) MIN(MAX(val, 0), UINT16_MAX)
263 #define do_usat_s(val) MIN(MAX(val, 0), UINT32_MAX)
264
265 static inline uint64_t do_urshr(uint64_t x, unsigned sh)
266 {
267 if (likely(sh < 64)) {
268 return (x >> sh) + ((x >> (sh - 1)) & 1);
269 } else if (sh == 64) {
270 return x >> 63;
271 } else {
272 return 0;
273 }
274 }
275
276 static inline int64_t do_srshr(int64_t x, unsigned sh)
277 {
278 if (likely(sh < 64)) {
279 return (x >> sh) + ((x >> (sh - 1)) & 1);
280 } else {
281 /* Rounding the sign bit always produces 0. */
282 return 0;
283 }
284 }
285
286 /**
287 * bfdotadd:
288 * @sum: addend
289 * @e1, @e2: multiplicand vectors
290 * @fpst: floating-point status to use
291 *
292 * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum.
293 * The @e1 and @e2 operands correspond to the 32-bit source vector
294 * slots and contain two Bfloat16 values each.
295 *
296 * Corresponds to the ARM pseudocode function BFDotAdd, specialized
297 * for the FPCR.EBF == 0 case.
298 */
299 float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst);
300 /**
301 * bfdotadd_ebf:
302 * @sum: addend
303 * @e1, @e2: multiplicand vectors
304 * @fpst: floating-point status to use
305 *
306 * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum.
307 * The @e1 and @e2 operands correspond to the 32-bit source vector
308 * slots and contain two Bfloat16 values each.
309 *
310 * Corresponds to the ARM pseudocode function BFDotAdd, specialized
311 * for the FPCR.EBF == 1 case.
312 */
313 float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst);
314
315 /**
316 * is_ebf:
317 * @env: CPU state
318 * @statusp: pointer to floating point status to fill in
319 *
320 * Determine whether a BFDotAdd operation should use FPCR.EBF = 0
321 * or FPCR.EBF = 1 semantics. On return, has initialized *statusp as suitable
322 * for float_status arguments to either bfdotadd() or bfdotadd_ebf().
323 * Returns true for EBF = 1, false for EBF = 0. (The caller should use this
324 * to decide whether to call bfdotadd() or bfdotadd_ebf().)
325 */
326 bool is_ebf(CPUARMState *env, float_status *statusp);
327
328 /*
329 * Negate as for FPCR.AH=1 -- do not negate NaNs.
330 */
331 static inline float16 bfloat16_ah_chs(float16 a)
332 {
333 return bfloat16_is_any_nan(a) ? a : bfloat16_chs(a);
334 }
335
336 static inline float16 float16_ah_chs(float16 a)
337 {
338 return float16_is_any_nan(a) ? a : float16_chs(a);
339 }
340
341 static inline float32 float32_ah_chs(float32 a)
342 {
343 return float32_is_any_nan(a) ? a : float32_chs(a);
344 }
345
346 static inline float64 float64_ah_chs(float64 a)
347 {
348 return float64_is_any_nan(a) ? a : float64_chs(a);
349 }
350
351 static inline float16 float16_maybe_ah_chs(float16 a, bool fpcr_ah)
352 {
353 return fpcr_ah && float16_is_any_nan(a) ? a : float16_chs(a);
354 }
355
356 static inline float32 float32_maybe_ah_chs(float32 a, bool fpcr_ah)
357 {
358 return fpcr_ah && float32_is_any_nan(a) ? a : float32_chs(a);
359 }
360
361 static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah)
362 {
363 return fpcr_ah && float64_is_any_nan(a) ? a : float64_chs(a);
364 }
365
366 /* Not actually called directly as a helper, but uses similar machinery. */
367 bfloat16 helper_sme2_ah_fmax_b16(bfloat16 a, bfloat16 b, float_status *fpst);
368 bfloat16 helper_sme2_ah_fmin_b16(bfloat16 a, bfloat16 b, float_status *fpst);
369
370 float32 sve_f16_to_f32(float16 f, float_status *fpst);
371 float16 sve_f32_to_f16(float32 f, float_status *fpst);
372
373 float16 float16_famax(float16, float16, float_status *);
374 float16 float16_famin(float16, float16, float_status *);
375 float32 float32_famax(float32, float32, float_status *);
376 float32 float32_famin(float32, float32, float_status *);
377 float64 float64_famax(float64, float64, float_status *);
378 float64 float64_famin(float64, float64, float_status *);
379
380 static inline float64 scalbn_d(float64 a, int64_t b, float_status *s)
381 {
382 int b_int = MIN(MAX(b, INT_MIN), INT_MAX);
383 return float64_scalbn(a, b_int, s);
384 }
385
386 /*
387 * Decode helper functions for predicate as counter.
388 */
389
390 typedef struct {
391 unsigned count;
392 unsigned lg2_stride;
393 bool invert;
394 } DecodeCounter;
395
396 static inline DecodeCounter
397 decode_counter(unsigned png, unsigned vl, unsigned v_esz)
398 {
399 DecodeCounter ret = { };
400
401 /* C.f. Arm pseudocode CounterToPredicate. */
402 if (likely(png & 0xf)) {
403 unsigned p_esz = ctz32(png);
404
405 /*
406 * maxbit = log2(pl(bits) * 4)
407 * = log2(vl(bytes) * 4)
408 * = log2(vl) + 2
409 * maxbit_mask = ones<maxbit:0>
410 * = (1 << (maxbit + 1)) - 1
411 * = (1 << (log2(vl) + 2 + 1)) - 1
412 * = (1 << (log2(vl) + 3)) - 1
413 * = (pow2ceil(vl) << 3) - 1
414 */
415 ret.count = png & (((unsigned)pow2ceil(vl) << 3) - 1);
416 ret.count >>= p_esz + 1;
417
418 ret.invert = (png >> 15) & 1;
419
420 /*
421 * The Arm pseudocode for CounterToPredicate expands the count to
422 * a set of bits, and then the operation proceeds as for the original
423 * interpretation of predicates as a set of bits.
424 *
425 * We can avoid the expansion by adjusting the count and supplying
426 * an element stride.
427 */
428 if (unlikely(p_esz != v_esz)) {
429 if (p_esz < v_esz) {
430 /*
431 * For predicate esz < vector esz, the expanded predicate
432 * will have more bits set than will be consumed.
433 * Adjust the count down, rounding up.
434 * Consider p_esz = MO_8, v_esz = MO_64, count 14:
435 * The expanded predicate would be
436 * 0011 1111 1111 1111
437 * The significant bits are
438 * ...1 ...1 ...1 ...1
439 */
440 unsigned shift = v_esz - p_esz;
441 unsigned trunc = ret.count >> shift;
442 ret.count = trunc + (ret.count != (trunc << shift));
443 } else {
444 /*
445 * For predicate esz > vector esz, the expanded predicate
446 * will have bits set only at power-of-two multiples of
447 * the vector esz. Bits at other multiples will all be
448 * false. Adjust the count up, and supply the caller
449 * with a stride of elements to skip.
450 */
451 unsigned shift = p_esz - v_esz;
452 ret.count <<= shift;
453 ret.lg2_stride = shift;
454 }
455 }
456 }
457 return ret;
458 }
459
460 /* Extract @len bits from an array of uint64_t at offset @pos bits. */
461 static inline uint64_t extractn(uint64_t *p, unsigned pos, unsigned len)
462 {
463 uint64_t x;
464
465 p += pos / 64;
466 pos = pos % 64;
467
468 x = p[0];
469 if (pos + len > 64) {
470 x = (x >> pos) | (p[1] << (-pos & 63));
471 pos = 0;
472 }
473 return extract64(x, pos, len);
474 }
475
476 /* Deposit @len bits into an array of uint64_t at offset @pos bits. */
477 static inline void depositn(uint64_t *p, unsigned pos,
478 unsigned len, uint64_t val)
479 {
480 p += pos / 64;
481 pos = pos % 64;
482
483 if (pos + len <= 64) {
484 p[0] = deposit64(p[0], pos, len, val);
485 } else {
486 unsigned len0 = 64 - pos;
487 unsigned len1 = len - len0;
488
489 p[0] = deposit64(p[0], pos, len0, val);
490 p[1] = deposit64(p[1], 0, len1, val >> len0);
491 }
492 }
493
494 /* Determine if [x, x+nx) overlaps [y, y+ny). */
495 static inline bool vectors_overlap(ARMVectorReg *x, unsigned nx,
496 ARMVectorReg *y, unsigned ny)
497 {
498 return !(x + nx <= y || y + ny <= x);
499 }
500
501 #define DO_3OP(NAME, FUNC, TYPE) \
502 void HELPER(NAME)(void *vd, void *vn, void *vm, \
503 float_status * stat, uint32_t desc) \
504 { \
505 intptr_t i, oprsz = simd_oprsz(desc); \
506 TYPE *d = vd, *n = vn, *m = vm; \
507 for (i = 0; i < oprsz / sizeof(TYPE); i++) { \
508 d[i] = FUNC(n[i], m[i], stat); \
509 } \
510 clear_tail(d, oprsz, simd_maxsz(desc)); \
511 }
512
513 #define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \
514 void HELPER(NAME)(void *vd, void *vn, void *vm, \
515 float_status * stat, uint32_t desc) \
516 { \
517 ARMVectorReg scratch; \
518 intptr_t oprsz = simd_oprsz(desc); \
519 intptr_t half = oprsz / sizeof(TYPE) / 2; \
520 TYPE *d = vd, *n = vn, *m = vm; \
521 if (unlikely(d == m)) { \
522 m = memcpy(&scratch, m, oprsz); \
523 } \
524 for (intptr_t i = 0; i < half; ++i) { \
525 d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)], stat); \
526 } \
527 for (intptr_t i = 0; i < half; ++i) { \
528 d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)], stat); \
529 } \
530 clear_tail(d, oprsz, simd_maxsz(desc)); \
531 }
532
533 #define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \
534 void HELPER(NAME)(void *vd, void *vn, void *vm, \
535 float_status * stat, uint32_t desc) \
536 { \
537 intptr_t i, j, oprsz = simd_oprsz(desc); \
538 intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \
539 intptr_t idx = simd_data(desc); \
540 TYPE *d = vd, *n = vn, *m = vm; \
541 for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \
542 TYPE mm = m[H(i + idx)]; \
543 for (j = 0; j < segment; j++) { \
544 d[i + j] = ADD(d[i + j], MUL(n[i + j], mm, stat), stat); \
545 } \
546 } \
547 clear_tail(d, oprsz, simd_maxsz(desc)); \
548 }
549
550 /*
551 * Perform SME quarter-tile outer product.
552 * Iterate over ZAtile[] for esize, calling fn for each element.
553 */
554 void sme_mop4(void *vza, void *vzn, void *vzm, void *fn_opaque,
555 uint32_t desc, size_t esize,
556 void (*fn)(void *, void *, void *, void *));
557
558 /*
559 * Perform SME sparse outer product, 4-way, 8 to 32-bit.
560 */
561 void sme_tmop_4way_sb(uint32_t *za, uint8_t *zn0, uint32_t *zm,
562 uint64_t *zk, void *fn_opaque, uint32_t desc,
563 void (*fn)(void *, void *, void *, void *));
564
565 #endif /* TARGET_ARM_VEC_INTERNAL_H */