master
c 2,259 lines 73.2 KB
Raw
1 /*
2 * ARM translation: M-profile MVE instructions
3 *
4 * Copyright (c) 2021 Linaro, Ltd.
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 #include "qemu/osdep.h"
21 #include "helper-mve.h"
22 #include "translate.h"
23 #include "translate-a32.h"
24
25 static inline int vidup_imm(DisasContext *s, int x)
26 {
27 return 1 << x;
28 }
29
30 /* Include the generated decoder */
31 #include "decode-mve.c.inc"
32
33 typedef void MVEGenLdStFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
34 typedef void MVEGenLdStSGFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
35 typedef void MVEGenLdStIlFn(TCGv_ptr, TCGv_i32, TCGv_i32);
36 typedef void MVEGenOneOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
37 typedef void MVEGenTwoOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr);
38 typedef void MVEGenTwoOpScalarFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
39 typedef void MVEGenTwoOpShiftFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
40 typedef void MVEGenLongDualAccOpFn(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64);
41 typedef void MVEGenVADDVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32);
42 typedef void MVEGenOneOpImmFn(TCGv_ptr, TCGv_ptr, TCGv_i64);
43 typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32);
44 typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32);
45 typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
46 typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
47 typedef void MVEGenVABAVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
48 typedef void MVEGenDualAccOpFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
49 typedef void MVEGenVCVTRmodeFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
50
51 /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */
52 static inline long mve_qreg_offset(unsigned reg)
53 {
54 return offsetof(CPUARMState, vfp.zregs[reg].d[0]);
55 }
56
57 static TCGv_ptr mve_qreg_ptr(unsigned reg)
58 {
59 TCGv_ptr ret = tcg_temp_new_ptr();
60 tcg_gen_addi_ptr(ret, tcg_env, mve_qreg_offset(reg));
61 return ret;
62 }
63
64 static bool mve_no_predication(DisasContext *s)
65 {
66 /*
67 * Return true if we are executing the entire MVE instruction
68 * with no predication or partial-execution, and so we can safely
69 * use an inline TCG vector implementation.
70 */
71 return s->eci == 0 && s->mve_no_pred;
72 }
73
74 static bool mve_check_qreg_bank(DisasContext *s, int qmask)
75 {
76 /*
77 * Check whether Qregs are in range. For v8.1M only Q0..Q7
78 * are supported, see VFPSmallRegisterBank().
79 */
80 return qmask < 8;
81 }
82
83 bool mve_eci_check(DisasContext *s)
84 {
85 /*
86 * This is a beatwise insn: check that ECI is valid (not a
87 * reserved value) and note that we are handling it.
88 * Return true if OK, false if we generated an exception.
89 */
90 s->eci_handled = true;
91 switch (s->eci) {
92 case ECI_NONE:
93 case ECI_A0:
94 case ECI_A0A1:
95 case ECI_A0A1A2:
96 case ECI_A0A1A2B0:
97 return true;
98 default:
99 /* Reserved value: INVSTATE UsageFault */
100 gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized());
101 return false;
102 }
103 }
104
105 void mve_update_eci(DisasContext *s)
106 {
107 /*
108 * The helper function will always update the CPUState field,
109 * so we only need to update the DisasContext field.
110 */
111 if (s->eci) {
112 s->eci = (s->eci == ECI_A0A1A2B0) ? ECI_A0 : ECI_NONE;
113 }
114 }
115
116 void mve_update_and_store_eci(DisasContext *s)
117 {
118 /*
119 * For insns which don't call a helper function that will call
120 * mve_advance_vpt(), this version updates s->eci and also stores
121 * it out to the CPUState field.
122 */
123 if (s->eci) {
124 mve_update_eci(s);
125 store_cpu_field(tcg_constant_i32(s->eci << 4), condexec_bits);
126 }
127 }
128
129 static bool mve_skip_first_beat(DisasContext *s)
130 {
131 /* Return true if PSR.ECI says we must skip the first beat of this insn */
132 switch (s->eci) {
133 case ECI_NONE:
134 return false;
135 case ECI_A0:
136 case ECI_A0A1:
137 case ECI_A0A1A2:
138 case ECI_A0A1A2B0:
139 return true;
140 default:
141 g_assert_not_reached();
142 }
143 }
144
145 static bool do_ldst(DisasContext *s, arg_VLDR_VSTR *a, MVEGenLdStFn *fn,
146 unsigned msize)
147 {
148 TCGv_i32 addr;
149 uint32_t offset;
150 TCGv_ptr qreg;
151
152 if (!dc_isar_feature(aa32_mve, s) ||
153 !mve_check_qreg_bank(s, a->qd) ||
154 !fn) {
155 return false;
156 }
157
158 /* CONSTRAINED UNPREDICTABLE: we choose to UNDEF */
159 if (a->rn == 15 || (a->rn == 13 && a->w)) {
160 return false;
161 }
162
163 if (!mve_eci_check(s) || !vfp_access_check(s)) {
164 return true;
165 }
166
167 offset = a->imm << msize;
168 if (!a->a) {
169 offset = -offset;
170 }
171 addr = load_reg(s, a->rn);
172 if (a->p) {
173 tcg_gen_addi_i32(addr, addr, offset);
174 }
175
176 qreg = mve_qreg_ptr(a->qd);
177 fn(tcg_env, qreg, addr);
178
179 /*
180 * Writeback always happens after the last beat of the insn,
181 * regardless of predication
182 */
183 if (a->w) {
184 if (!a->p) {
185 tcg_gen_addi_i32(addr, addr, offset);
186 }
187 store_reg(s, a->rn, addr);
188 }
189 mve_update_eci(s);
190 return true;
191 }
192
193 static bool trans_VLDR_VSTR(DisasContext *s, arg_VLDR_VSTR *a)
194 {
195 static MVEGenLdStFn * const ldstfns[4][2] = {
196 { gen_helper_mve_vstrb, gen_helper_mve_vldrb },
197 { gen_helper_mve_vstrh, gen_helper_mve_vldrh },
198 { gen_helper_mve_vstrw, gen_helper_mve_vldrw },
199 { NULL, NULL }
200 };
201 return do_ldst(s, a, ldstfns[a->size][a->l], a->size);
202 }
203
204 #define DO_VLDST_WIDE_NARROW(OP, SLD, ULD, ST, MSIZE) \
205 static bool trans_##OP(DisasContext *s, arg_VLDR_VSTR *a) \
206 { \
207 static MVEGenLdStFn * const ldstfns[2][2] = { \
208 { gen_helper_mve_##ST, gen_helper_mve_##SLD }, \
209 { NULL, gen_helper_mve_##ULD }, \
210 }; \
211 return do_ldst(s, a, ldstfns[a->u][a->l], MSIZE); \
212 }
213
214 DO_VLDST_WIDE_NARROW(VLDSTB_H, vldrb_sh, vldrb_uh, vstrb_h, MO_8)
215 DO_VLDST_WIDE_NARROW(VLDSTB_W, vldrb_sw, vldrb_uw, vstrb_w, MO_8)
216 DO_VLDST_WIDE_NARROW(VLDSTH_W, vldrh_sw, vldrh_uw, vstrh_w, MO_16)
217
218 static bool do_ldst_sg(DisasContext *s, arg_vldst_sg *a, MVEGenLdStSGFn fn)
219 {
220 TCGv_i32 addr;
221 TCGv_ptr qd, qm;
222
223 if (!dc_isar_feature(aa32_mve, s) ||
224 !mve_check_qreg_bank(s, a->qd | a->qm) ||
225 !fn || a->rn == 15) {
226 /* Rn case is UNPREDICTABLE */
227 return false;
228 }
229
230 if (!mve_eci_check(s) || !vfp_access_check(s)) {
231 return true;
232 }
233
234 addr = load_reg(s, a->rn);
235
236 qd = mve_qreg_ptr(a->qd);
237 qm = mve_qreg_ptr(a->qm);
238 fn(tcg_env, qd, qm, addr);
239 mve_update_eci(s);
240 return true;
241 }
242
243 /*
244 * The naming scheme here is "vldrb_sg_sh == in-memory byte loads
245 * signextended to halfword elements in register". _os_ indicates that
246 * the offsets in Qm should be scaled by the element size.
247 */
248 /* This macro is just to make the arrays more compact in these functions */
249 #define F(N) gen_helper_mve_##N
250
251 /* VLDRB/VSTRB (ie msize 1) with OS=1 is UNPREDICTABLE; we UNDEF */
252 static bool trans_VLDR_S_sg(DisasContext *s, arg_vldst_sg *a)
253 {
254 static MVEGenLdStSGFn * const fns[2][4][4] = { {
255 { NULL, F(vldrb_sg_sh), F(vldrb_sg_sw), NULL },
256 { NULL, NULL, F(vldrh_sg_sw), NULL },
257 { NULL, NULL, NULL, NULL },
258 { NULL, NULL, NULL, NULL }
259 }, {
260 { NULL, NULL, NULL, NULL },
261 { NULL, NULL, F(vldrh_sg_os_sw), NULL },
262 { NULL, NULL, NULL, NULL },
263 { NULL, NULL, NULL, NULL }
264 }
265 };
266 if (a->qd == a->qm) {
267 return false; /* UNPREDICTABLE */
268 }
269 return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]);
270 }
271
272 static bool trans_VLDR_U_sg(DisasContext *s, arg_vldst_sg *a)
273 {
274 static MVEGenLdStSGFn * const fns[2][4][4] = { {
275 { F(vldrb_sg_ub), F(vldrb_sg_uh), F(vldrb_sg_uw), NULL },
276 { NULL, F(vldrh_sg_uh), F(vldrh_sg_uw), NULL },
277 { NULL, NULL, F(vldrw_sg_uw), NULL },
278 { NULL, NULL, NULL, F(vldrd_sg_ud) }
279 }, {
280 { NULL, NULL, NULL, NULL },
281 { NULL, F(vldrh_sg_os_uh), F(vldrh_sg_os_uw), NULL },
282 { NULL, NULL, F(vldrw_sg_os_uw), NULL },
283 { NULL, NULL, NULL, F(vldrd_sg_os_ud) }
284 }
285 };
286 if (a->qd == a->qm) {
287 return false; /* UNPREDICTABLE */
288 }
289 return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]);
290 }
291
292 static bool trans_VSTR_sg(DisasContext *s, arg_vldst_sg *a)
293 {
294 static MVEGenLdStSGFn * const fns[2][4][4] = { {
295 { F(vstrb_sg_ub), F(vstrb_sg_uh), F(vstrb_sg_uw), NULL },
296 { NULL, F(vstrh_sg_uh), F(vstrh_sg_uw), NULL },
297 { NULL, NULL, F(vstrw_sg_uw), NULL },
298 { NULL, NULL, NULL, F(vstrd_sg_ud) }
299 }, {
300 { NULL, NULL, NULL, NULL },
301 { NULL, F(vstrh_sg_os_uh), F(vstrh_sg_os_uw), NULL },
302 { NULL, NULL, F(vstrw_sg_os_uw), NULL },
303 { NULL, NULL, NULL, F(vstrd_sg_os_ud) }
304 }
305 };
306 return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]);
307 }
308
309 #undef F
310
311 static bool do_ldst_sg_imm(DisasContext *s, arg_vldst_sg_imm *a,
312 MVEGenLdStSGFn *fn, unsigned msize)
313 {
314 uint32_t offset;
315 TCGv_ptr qd, qm;
316
317 if (!dc_isar_feature(aa32_mve, s) ||
318 !mve_check_qreg_bank(s, a->qd | a->qm) ||
319 !fn) {
320 return false;
321 }
322
323 if (!mve_eci_check(s) || !vfp_access_check(s)) {
324 return true;
325 }
326
327 offset = a->imm << msize;
328 if (!a->a) {
329 offset = -offset;
330 }
331
332 qd = mve_qreg_ptr(a->qd);
333 qm = mve_qreg_ptr(a->qm);
334 fn(tcg_env, qd, qm, tcg_constant_i32(offset));
335 mve_update_eci(s);
336 return true;
337 }
338
339 static bool trans_VLDRW_sg_imm(DisasContext *s, arg_vldst_sg_imm *a)
340 {
341 static MVEGenLdStSGFn * const fns[] = {
342 gen_helper_mve_vldrw_sg_uw,
343 gen_helper_mve_vldrw_sg_wb_uw,
344 };
345 if (a->qd == a->qm) {
346 return false; /* UNPREDICTABLE */
347 }
348 return do_ldst_sg_imm(s, a, fns[a->w], MO_32);
349 }
350
351 static bool trans_VLDRD_sg_imm(DisasContext *s, arg_vldst_sg_imm *a)
352 {
353 static MVEGenLdStSGFn * const fns[] = {
354 gen_helper_mve_vldrd_sg_ud,
355 gen_helper_mve_vldrd_sg_wb_ud,
356 };
357 if (a->qd == a->qm) {
358 return false; /* UNPREDICTABLE */
359 }
360 return do_ldst_sg_imm(s, a, fns[a->w], MO_64);
361 }
362
363 static bool trans_VSTRW_sg_imm(DisasContext *s, arg_vldst_sg_imm *a)
364 {
365 static MVEGenLdStSGFn * const fns[] = {
366 gen_helper_mve_vstrw_sg_uw,
367 gen_helper_mve_vstrw_sg_wb_uw,
368 };
369 return do_ldst_sg_imm(s, a, fns[a->w], MO_32);
370 }
371
372 static bool trans_VSTRD_sg_imm(DisasContext *s, arg_vldst_sg_imm *a)
373 {
374 static MVEGenLdStSGFn * const fns[] = {
375 gen_helper_mve_vstrd_sg_ud,
376 gen_helper_mve_vstrd_sg_wb_ud,
377 };
378 return do_ldst_sg_imm(s, a, fns[a->w], MO_64);
379 }
380
381 static bool do_vldst_il(DisasContext *s, arg_vldst_il *a, MVEGenLdStIlFn *fn,
382 int addrinc)
383 {
384 TCGv_i32 rn;
385
386 if (!dc_isar_feature(aa32_mve, s) ||
387 !mve_check_qreg_bank(s, a->qd) ||
388 !fn || (a->rn == 13 && a->w) || a->rn == 15) {
389 /* Variously UNPREDICTABLE or UNDEF or related-encoding */
390 return false;
391 }
392 if (!mve_eci_check(s) || !vfp_access_check(s)) {
393 return true;
394 }
395
396 rn = load_reg(s, a->rn);
397 /*
398 * We pass the index of Qd, not a pointer, because the helper must
399 * access multiple Q registers starting at Qd and working up.
400 */
401 fn(tcg_env, tcg_constant_i32(a->qd), rn);
402
403 if (a->w) {
404 tcg_gen_addi_i32(rn, rn, addrinc);
405 store_reg(s, a->rn, rn);
406 }
407 mve_update_and_store_eci(s);
408 return true;
409 }
410
411 /* This macro is just to make the arrays more compact in these functions */
412 #define F(N) gen_helper_mve_##N
413
414 static bool trans_VLD2(DisasContext *s, arg_vldst_il *a)
415 {
416 static MVEGenLdStIlFn * const fns[4][4] = {
417 { F(vld20b), F(vld20h), F(vld20w), NULL, },
418 { F(vld21b), F(vld21h), F(vld21w), NULL, },
419 { NULL, NULL, NULL, NULL },
420 { NULL, NULL, NULL, NULL },
421 };
422 if (a->qd > 6) {
423 return false;
424 }
425 return do_vldst_il(s, a, fns[a->pat][a->size], 32);
426 }
427
428 static bool trans_VLD4(DisasContext *s, arg_vldst_il *a)
429 {
430 static MVEGenLdStIlFn * const fns[4][4] = {
431 { F(vld40b), F(vld40h), F(vld40w), NULL, },
432 { F(vld41b), F(vld41h), F(vld41w), NULL, },
433 { F(vld42b), F(vld42h), F(vld42w), NULL, },
434 { F(vld43b), F(vld43h), F(vld43w), NULL, },
435 };
436 if (a->qd > 4) {
437 return false;
438 }
439 return do_vldst_il(s, a, fns[a->pat][a->size], 64);
440 }
441
442 static bool trans_VST2(DisasContext *s, arg_vldst_il *a)
443 {
444 static MVEGenLdStIlFn * const fns[4][4] = {
445 { F(vst20b), F(vst20h), F(vst20w), NULL, },
446 { F(vst21b), F(vst21h), F(vst21w), NULL, },
447 { NULL, NULL, NULL, NULL },
448 { NULL, NULL, NULL, NULL },
449 };
450 if (a->qd > 6) {
451 return false;
452 }
453 return do_vldst_il(s, a, fns[a->pat][a->size], 32);
454 }
455
456 static bool trans_VST4(DisasContext *s, arg_vldst_il *a)
457 {
458 static MVEGenLdStIlFn * const fns[4][4] = {
459 { F(vst40b), F(vst40h), F(vst40w), NULL, },
460 { F(vst41b), F(vst41h), F(vst41w), NULL, },
461 { F(vst42b), F(vst42h), F(vst42w), NULL, },
462 { F(vst43b), F(vst43h), F(vst43w), NULL, },
463 };
464 if (a->qd > 4) {
465 return false;
466 }
467 return do_vldst_il(s, a, fns[a->pat][a->size], 64);
468 }
469
470 #undef F
471
472 static bool trans_VDUP(DisasContext *s, arg_VDUP *a)
473 {
474 TCGv_ptr qd;
475 TCGv_i32 rt;
476
477 if (!dc_isar_feature(aa32_mve, s) ||
478 !mve_check_qreg_bank(s, a->qd)) {
479 return false;
480 }
481 if (a->rt == 13 || a->rt == 15) {
482 /* UNPREDICTABLE; we choose to UNDEF */
483 return false;
484 }
485 if (!mve_eci_check(s) || !vfp_access_check(s)) {
486 return true;
487 }
488
489 rt = load_reg(s, a->rt);
490 if (mve_no_predication(s)) {
491 tcg_gen_gvec_dup_i32(a->size, mve_qreg_offset(a->qd), 16, 16, rt);
492 } else {
493 qd = mve_qreg_ptr(a->qd);
494 tcg_gen_dup_i32(a->size, rt, rt);
495 gen_helper_mve_vdup(tcg_env, qd, rt);
496 }
497 mve_update_eci(s);
498 return true;
499 }
500
501 static bool do_1op_vec(DisasContext *s, arg_1op *a, MVEGenOneOpFn fn,
502 GVecGen2Fn vecfn)
503 {
504 TCGv_ptr qd, qm;
505
506 if (!dc_isar_feature(aa32_mve, s) ||
507 !mve_check_qreg_bank(s, a->qd | a->qm) ||
508 !fn) {
509 return false;
510 }
511
512 if (!mve_eci_check(s) || !vfp_access_check(s)) {
513 return true;
514 }
515
516 if (vecfn && mve_no_predication(s)) {
517 vecfn(a->size, mve_qreg_offset(a->qd), mve_qreg_offset(a->qm), 16, 16);
518 } else {
519 qd = mve_qreg_ptr(a->qd);
520 qm = mve_qreg_ptr(a->qm);
521 fn(tcg_env, qd, qm);
522 }
523 mve_update_eci(s);
524 return true;
525 }
526
527 static bool do_1op(DisasContext *s, arg_1op *a, MVEGenOneOpFn fn)
528 {
529 return do_1op_vec(s, a, fn, NULL);
530 }
531
532 #define DO_1OP_VEC(INSN, FN, VECFN) \
533 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
534 { \
535 static MVEGenOneOpFn * const fns[] = { \
536 gen_helper_mve_##FN##b, \
537 gen_helper_mve_##FN##h, \
538 gen_helper_mve_##FN##w, \
539 NULL, \
540 }; \
541 return do_1op_vec(s, a, fns[a->size], VECFN); \
542 }
543
544 #define DO_1OP(INSN, FN) DO_1OP_VEC(INSN, FN, NULL)
545
546 DO_1OP(VCLZ, vclz)
547 DO_1OP(VCLS, vcls)
548 DO_1OP_VEC(VABS, vabs, tcg_gen_gvec_abs)
549 DO_1OP_VEC(VNEG, vneg, tcg_gen_gvec_neg)
550 DO_1OP(VQABS, vqabs)
551 DO_1OP(VQNEG, vqneg)
552 DO_1OP(VMAXA, vmaxa)
553 DO_1OP(VMINA, vmina)
554
555 /*
556 * For simple float/int conversions we use the fixed-point
557 * conversion helpers with a zero shift count
558 */
559 #define DO_VCVT(INSN, HFN, SFN) \
560 static void gen_##INSN##h(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
561 { \
562 gen_helper_mve_##HFN(env, qd, qm, tcg_constant_i32(0)); \
563 } \
564 static void gen_##INSN##s(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
565 { \
566 gen_helper_mve_##SFN(env, qd, qm, tcg_constant_i32(0)); \
567 } \
568 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
569 { \
570 static MVEGenOneOpFn * const fns[] = { \
571 NULL, \
572 gen_##INSN##h, \
573 gen_##INSN##s, \
574 NULL, \
575 }; \
576 if (!dc_isar_feature(aa32_mve_fp, s)) { \
577 return false; \
578 } \
579 return do_1op(s, a, fns[a->size]); \
580 }
581
582 DO_VCVT(VCVT_SF, vcvt_sh, vcvt_sf)
583 DO_VCVT(VCVT_UF, vcvt_uh, vcvt_uf)
584 DO_VCVT(VCVT_FS, vcvt_hs, vcvt_fs)
585 DO_VCVT(VCVT_FU, vcvt_hu, vcvt_fu)
586
587 static bool do_vcvt_rmode(DisasContext *s, arg_1op *a,
588 ARMFPRounding rmode, bool u)
589 {
590 /*
591 * Handle VCVT fp to int with specified rounding mode.
592 * This is a 1op fn but we must pass the rounding mode as
593 * an immediate to the helper.
594 */
595 TCGv_ptr qd, qm;
596 static MVEGenVCVTRmodeFn * const fns[4][2] = {
597 { NULL, NULL },
598 { gen_helper_mve_vcvt_rm_sh, gen_helper_mve_vcvt_rm_uh },
599 { gen_helper_mve_vcvt_rm_ss, gen_helper_mve_vcvt_rm_us },
600 { NULL, NULL },
601 };
602 MVEGenVCVTRmodeFn *fn = fns[a->size][u];
603
604 if (!dc_isar_feature(aa32_mve_fp, s) ||
605 !mve_check_qreg_bank(s, a->qd | a->qm) ||
606 !fn) {
607 return false;
608 }
609
610 if (!mve_eci_check(s) || !vfp_access_check(s)) {
611 return true;
612 }
613
614 qd = mve_qreg_ptr(a->qd);
615 qm = mve_qreg_ptr(a->qm);
616 fn(tcg_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode)));
617 mve_update_eci(s);
618 return true;
619 }
620
621 #define DO_VCVT_RMODE(INSN, RMODE, U) \
622 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
623 { \
624 return do_vcvt_rmode(s, a, RMODE, U); \
625 } \
626
627 DO_VCVT_RMODE(VCVTAS, FPROUNDING_TIEAWAY, false)
628 DO_VCVT_RMODE(VCVTAU, FPROUNDING_TIEAWAY, true)
629 DO_VCVT_RMODE(VCVTNS, FPROUNDING_TIEEVEN, false)
630 DO_VCVT_RMODE(VCVTNU, FPROUNDING_TIEEVEN, true)
631 DO_VCVT_RMODE(VCVTPS, FPROUNDING_POSINF, false)
632 DO_VCVT_RMODE(VCVTPU, FPROUNDING_POSINF, true)
633 DO_VCVT_RMODE(VCVTMS, FPROUNDING_NEGINF, false)
634 DO_VCVT_RMODE(VCVTMU, FPROUNDING_NEGINF, true)
635
636 #define DO_VCVT_SH(INSN, FN) \
637 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
638 { \
639 if (!dc_isar_feature(aa32_mve_fp, s)) { \
640 return false; \
641 } \
642 return do_1op(s, a, gen_helper_mve_##FN); \
643 } \
644
645 DO_VCVT_SH(VCVTB_SH, vcvtb_sh)
646 DO_VCVT_SH(VCVTT_SH, vcvtt_sh)
647 DO_VCVT_SH(VCVTB_HS, vcvtb_hs)
648 DO_VCVT_SH(VCVTT_HS, vcvtt_hs)
649
650 #define DO_VRINT(INSN, RMODE) \
651 static void gen_##INSN##h(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
652 { \
653 gen_helper_mve_vrint_rm_h(env, qd, qm, \
654 tcg_constant_i32(arm_rmode_to_sf(RMODE))); \
655 } \
656 static void gen_##INSN##s(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
657 { \
658 gen_helper_mve_vrint_rm_s(env, qd, qm, \
659 tcg_constant_i32(arm_rmode_to_sf(RMODE))); \
660 } \
661 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
662 { \
663 static MVEGenOneOpFn * const fns[] = { \
664 NULL, \
665 gen_##INSN##h, \
666 gen_##INSN##s, \
667 NULL, \
668 }; \
669 if (!dc_isar_feature(aa32_mve_fp, s)) { \
670 return false; \
671 } \
672 return do_1op(s, a, fns[a->size]); \
673 }
674
675 DO_VRINT(VRINTN, FPROUNDING_TIEEVEN)
676 DO_VRINT(VRINTA, FPROUNDING_TIEAWAY)
677 DO_VRINT(VRINTZ, FPROUNDING_ZERO)
678 DO_VRINT(VRINTM, FPROUNDING_NEGINF)
679 DO_VRINT(VRINTP, FPROUNDING_POSINF)
680
681 static bool trans_VRINTX(DisasContext *s, arg_1op *a)
682 {
683 static MVEGenOneOpFn * const fns[] = {
684 NULL,
685 gen_helper_mve_vrintx_h,
686 gen_helper_mve_vrintx_s,
687 NULL,
688 };
689 if (!dc_isar_feature(aa32_mve_fp, s)) {
690 return false;
691 }
692 return do_1op(s, a, fns[a->size]);
693 }
694
695 /* Narrowing moves: only size 0 and 1 are valid */
696 #define DO_VMOVN(INSN, FN) \
697 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
698 { \
699 static MVEGenOneOpFn * const fns[] = { \
700 gen_helper_mve_##FN##b, \
701 gen_helper_mve_##FN##h, \
702 NULL, \
703 NULL, \
704 }; \
705 return do_1op(s, a, fns[a->size]); \
706 }
707
708 DO_VMOVN(VMOVNB, vmovnb)
709 DO_VMOVN(VMOVNT, vmovnt)
710 DO_VMOVN(VQMOVUNB, vqmovunb)
711 DO_VMOVN(VQMOVUNT, vqmovunt)
712 DO_VMOVN(VQMOVN_BS, vqmovnbs)
713 DO_VMOVN(VQMOVN_TS, vqmovnts)
714 DO_VMOVN(VQMOVN_BU, vqmovnbu)
715 DO_VMOVN(VQMOVN_TU, vqmovntu)
716
717 static bool trans_VREV16(DisasContext *s, arg_1op *a)
718 {
719 static MVEGenOneOpFn * const fns[] = {
720 gen_helper_mve_vrev16b,
721 NULL,
722 NULL,
723 NULL,
724 };
725 return do_1op(s, a, fns[a->size]);
726 }
727
728 static bool trans_VREV32(DisasContext *s, arg_1op *a)
729 {
730 static MVEGenOneOpFn * const fns[] = {
731 gen_helper_mve_vrev32b,
732 gen_helper_mve_vrev32h,
733 NULL,
734 NULL,
735 };
736 return do_1op(s, a, fns[a->size]);
737 }
738
739 static bool trans_VREV64(DisasContext *s, arg_1op *a)
740 {
741 static MVEGenOneOpFn * const fns[] = {
742 gen_helper_mve_vrev64b,
743 gen_helper_mve_vrev64h,
744 gen_helper_mve_vrev64w,
745 NULL,
746 };
747 return do_1op(s, a, fns[a->size]);
748 }
749
750 static bool trans_VMVN(DisasContext *s, arg_1op *a)
751 {
752 return do_1op_vec(s, a, gen_helper_mve_vmvn, tcg_gen_gvec_not);
753 }
754
755 static bool trans_VABS_fp(DisasContext *s, arg_1op *a)
756 {
757 static MVEGenOneOpFn * const fns[] = {
758 NULL,
759 gen_helper_mve_vfabsh,
760 gen_helper_mve_vfabss,
761 NULL,
762 };
763 if (!dc_isar_feature(aa32_mve_fp, s)) {
764 return false;
765 }
766 return do_1op(s, a, fns[a->size]);
767 }
768
769 static bool trans_VNEG_fp(DisasContext *s, arg_1op *a)
770 {
771 static MVEGenOneOpFn * const fns[] = {
772 NULL,
773 gen_helper_mve_vfnegh,
774 gen_helper_mve_vfnegs,
775 NULL,
776 };
777 if (!dc_isar_feature(aa32_mve_fp, s)) {
778 return false;
779 }
780 return do_1op(s, a, fns[a->size]);
781 }
782
783 static bool do_2op_vec(DisasContext *s, arg_2op *a, MVEGenTwoOpFn fn,
784 GVecGen3Fn *vecfn)
785 {
786 TCGv_ptr qd, qn, qm;
787
788 if (!dc_isar_feature(aa32_mve, s) ||
789 !mve_check_qreg_bank(s, a->qd | a->qn | a->qm) ||
790 !fn) {
791 return false;
792 }
793 if (!mve_eci_check(s) || !vfp_access_check(s)) {
794 return true;
795 }
796
797 if (vecfn && mve_no_predication(s)) {
798 vecfn(a->size, mve_qreg_offset(a->qd), mve_qreg_offset(a->qn),
799 mve_qreg_offset(a->qm), 16, 16);
800 } else {
801 qd = mve_qreg_ptr(a->qd);
802 qn = mve_qreg_ptr(a->qn);
803 qm = mve_qreg_ptr(a->qm);
804 fn(tcg_env, qd, qn, qm);
805 }
806 mve_update_eci(s);
807 return true;
808 }
809
810 static bool do_2op(DisasContext *s, arg_2op *a, MVEGenTwoOpFn *fn)
811 {
812 return do_2op_vec(s, a, fn, NULL);
813 }
814
815 #define DO_LOGIC(INSN, HELPER, VECFN) \
816 static bool trans_##INSN(DisasContext *s, arg_2op *a) \
817 { \
818 return do_2op_vec(s, a, HELPER, VECFN); \
819 }
820
821 DO_LOGIC(VAND, gen_helper_mve_vand, tcg_gen_gvec_and)
822 DO_LOGIC(VBIC, gen_helper_mve_vbic, tcg_gen_gvec_andc)
823 DO_LOGIC(VORR, gen_helper_mve_vorr, tcg_gen_gvec_or)
824 DO_LOGIC(VORN, gen_helper_mve_vorn, tcg_gen_gvec_orc)
825 DO_LOGIC(VEOR, gen_helper_mve_veor, tcg_gen_gvec_xor)
826
827 static bool trans_VPSEL(DisasContext *s, arg_2op *a)
828 {
829 /* This insn updates predication bits */
830 s->base.is_jmp = DISAS_UPDATE_NOCHAIN;
831 return do_2op(s, a, gen_helper_mve_vpsel);
832 }
833
834 #define DO_2OP_VEC(INSN, FN, VECFN) \
835 static bool trans_##INSN(DisasContext *s, arg_2op *a) \
836 { \
837 static MVEGenTwoOpFn * const fns[] = { \
838 gen_helper_mve_##FN##b, \
839 gen_helper_mve_##FN##h, \
840 gen_helper_mve_##FN##w, \
841 NULL, \
842 }; \
843 return do_2op_vec(s, a, fns[a->size], VECFN); \
844 }
845
846 #define DO_2OP(INSN, FN) DO_2OP_VEC(INSN, FN, NULL)
847
848 DO_2OP_VEC(VADD, vadd, tcg_gen_gvec_add)
849 DO_2OP_VEC(VSUB, vsub, tcg_gen_gvec_sub)
850 DO_2OP_VEC(VMUL, vmul, tcg_gen_gvec_mul)
851 DO_2OP(VMULH_S, vmulhs)
852 DO_2OP(VMULH_U, vmulhu)
853 DO_2OP(VRMULH_S, vrmulhs)
854 DO_2OP(VRMULH_U, vrmulhu)
855 DO_2OP_VEC(VMAX_S, vmaxs, tcg_gen_gvec_smax)
856 DO_2OP_VEC(VMAX_U, vmaxu, tcg_gen_gvec_umax)
857 DO_2OP_VEC(VMIN_S, vmins, tcg_gen_gvec_smin)
858 DO_2OP_VEC(VMIN_U, vminu, tcg_gen_gvec_umin)
859 DO_2OP(VABD_S, vabds)
860 DO_2OP(VABD_U, vabdu)
861 DO_2OP(VHADD_S, vhadds)
862 DO_2OP(VHADD_U, vhaddu)
863 DO_2OP(VHSUB_S, vhsubs)
864 DO_2OP(VHSUB_U, vhsubu)
865 DO_2OP(VMULL_BS, vmullbs)
866 DO_2OP(VMULL_BU, vmullbu)
867 DO_2OP(VMULL_TS, vmullts)
868 DO_2OP(VMULL_TU, vmulltu)
869 DO_2OP(VQDMULH, vqdmulh)
870 DO_2OP(VQRDMULH, vqrdmulh)
871 DO_2OP(VQADD_S, vqadds)
872 DO_2OP(VQADD_U, vqaddu)
873 DO_2OP(VQSUB_S, vqsubs)
874 DO_2OP(VQSUB_U, vqsubu)
875 DO_2OP(VSHL_S, vshls)
876 DO_2OP(VSHL_U, vshlu)
877 DO_2OP(VRSHL_S, vrshls)
878 DO_2OP(VRSHL_U, vrshlu)
879 DO_2OP(VQSHL_S, vqshls)
880 DO_2OP(VQSHL_U, vqshlu)
881 DO_2OP(VQRSHL_S, vqrshls)
882 DO_2OP(VQRSHL_U, vqrshlu)
883 DO_2OP(VQDMLADH, vqdmladh)
884 DO_2OP(VQDMLADHX, vqdmladhx)
885 DO_2OP(VQRDMLADH, vqrdmladh)
886 DO_2OP(VQRDMLADHX, vqrdmladhx)
887 DO_2OP(VQDMLSDH, vqdmlsdh)
888 DO_2OP(VQDMLSDHX, vqdmlsdhx)
889 DO_2OP(VQRDMLSDH, vqrdmlsdh)
890 DO_2OP(VQRDMLSDHX, vqrdmlsdhx)
891 DO_2OP(VRHADD_S, vrhadds)
892 DO_2OP(VRHADD_U, vrhaddu)
893 /*
894 * VCADD Qd == Qm at size MO_32 is UNPREDICTABLE; we choose not to diagnose
895 * so we can reuse the DO_2OP macro. (Our implementation calculates the
896 * "expected" results in this case.) Similarly for VHCADD.
897 */
898 DO_2OP(VCADD90, vcadd90)
899 DO_2OP(VCADD270, vcadd270)
900 DO_2OP(VHCADD90, vhcadd90)
901 DO_2OP(VHCADD270, vhcadd270)
902
903 static bool trans_VQDMULLB(DisasContext *s, arg_2op *a)
904 {
905 static MVEGenTwoOpFn * const fns[] = {
906 NULL,
907 gen_helper_mve_vqdmullbh,
908 gen_helper_mve_vqdmullbw,
909 NULL,
910 };
911 if (a->size == MO_32 && (a->qd == a->qm || a->qd == a->qn)) {
912 /* UNPREDICTABLE; we choose to undef */
913 return false;
914 }
915 return do_2op(s, a, fns[a->size]);
916 }
917
918 static bool trans_VQDMULLT(DisasContext *s, arg_2op *a)
919 {
920 static MVEGenTwoOpFn * const fns[] = {
921 NULL,
922 gen_helper_mve_vqdmullth,
923 gen_helper_mve_vqdmulltw,
924 NULL,
925 };
926 if (a->size == MO_32 && (a->qd == a->qm || a->qd == a->qn)) {
927 /* UNPREDICTABLE; we choose to undef */
928 return false;
929 }
930 return do_2op(s, a, fns[a->size]);
931 }
932
933 static bool trans_VMULLP_B(DisasContext *s, arg_2op *a)
934 {
935 /*
936 * Note that a->size indicates the output size, ie VMULL.P8
937 * is the 8x8->16 operation and a->size is MO_16; VMULL.P16
938 * is the 16x16->32 operation and a->size is MO_32.
939 */
940 static MVEGenTwoOpFn * const fns[] = {
941 NULL,
942 gen_helper_mve_vmullpbh,
943 gen_helper_mve_vmullpbw,
944 NULL,
945 };
946 return do_2op(s, a, fns[a->size]);
947 }
948
949 static bool trans_VMULLP_T(DisasContext *s, arg_2op *a)
950 {
951 /* a->size is as for trans_VMULLP_B */
952 static MVEGenTwoOpFn * const fns[] = {
953 NULL,
954 gen_helper_mve_vmullpth,
955 gen_helper_mve_vmullptw,
956 NULL,
957 };
958 return do_2op(s, a, fns[a->size]);
959 }
960
961 /*
962 * VADC and VSBC: these perform an add-with-carry or subtract-with-carry
963 * of the 32-bit elements in each lane of the input vectors, where the
964 * carry-out of each add is the carry-in of the next. The initial carry
965 * input is either fixed (0 for VADCI, 1 for VSBCI) or is from FPSCR.C
966 * (for VADC and VSBC); the carry out at the end is written back to FPSCR.C.
967 * These insns are subject to beat-wise execution. Partial execution
968 * of an I=1 (initial carry input fixed) insn which does not
969 * execute the first beat must start with the current FPSCR.NZCV
970 * value, not the fixed constant input.
971 */
972 static bool trans_VADC(DisasContext *s, arg_2op *a)
973 {
974 return do_2op(s, a, gen_helper_mve_vadc);
975 }
976
977 static bool trans_VADCI(DisasContext *s, arg_2op *a)
978 {
979 if (mve_skip_first_beat(s)) {
980 return trans_VADC(s, a);
981 }
982 return do_2op(s, a, gen_helper_mve_vadci);
983 }
984
985 static bool trans_VSBC(DisasContext *s, arg_2op *a)
986 {
987 return do_2op(s, a, gen_helper_mve_vsbc);
988 }
989
990 static bool trans_VSBCI(DisasContext *s, arg_2op *a)
991 {
992 if (mve_skip_first_beat(s)) {
993 return trans_VSBC(s, a);
994 }
995 return do_2op(s, a, gen_helper_mve_vsbci);
996 }
997
998 #define DO_2OP_FP(INSN, FN) \
999 static bool trans_##INSN(DisasContext *s, arg_2op *a) \
1000 { \
1001 static MVEGenTwoOpFn * const fns[] = { \
1002 NULL, \
1003 gen_helper_mve_##FN##h, \
1004 gen_helper_mve_##FN##s, \
1005 NULL, \
1006 }; \
1007 if (!dc_isar_feature(aa32_mve_fp, s)) { \
1008 return false; \
1009 } \
1010 return do_2op(s, a, fns[a->size]); \
1011 }
1012
1013 DO_2OP_FP(VADD_fp, vfadd)
1014 DO_2OP_FP(VSUB_fp, vfsub)
1015 DO_2OP_FP(VMUL_fp, vfmul)
1016 DO_2OP_FP(VABD_fp, vfabd)
1017 DO_2OP_FP(VMAXNM, vmaxnm)
1018 DO_2OP_FP(VMINNM, vminnm)
1019 DO_2OP_FP(VCADD90_fp, vfcadd90)
1020 DO_2OP_FP(VCADD270_fp, vfcadd270)
1021 DO_2OP_FP(VFMA, vfma)
1022 DO_2OP_FP(VFMS, vfms)
1023 DO_2OP_FP(VCMUL0, vcmul0)
1024 DO_2OP_FP(VCMUL90, vcmul90)
1025 DO_2OP_FP(VCMUL180, vcmul180)
1026 DO_2OP_FP(VCMUL270, vcmul270)
1027 DO_2OP_FP(VCMLA0, vcmla0)
1028 DO_2OP_FP(VCMLA90, vcmla90)
1029 DO_2OP_FP(VCMLA180, vcmla180)
1030 DO_2OP_FP(VCMLA270, vcmla270)
1031 DO_2OP_FP(VMAXNMA, vmaxnma)
1032 DO_2OP_FP(VMINNMA, vminnma)
1033
1034 static bool do_2op_scalar(DisasContext *s, arg_2scalar *a,
1035 MVEGenTwoOpScalarFn fn)
1036 {
1037 TCGv_ptr qd, qn;
1038 TCGv_i32 rm;
1039
1040 if (!dc_isar_feature(aa32_mve, s) ||
1041 !mve_check_qreg_bank(s, a->qd | a->qn) ||
1042 !fn) {
1043 return false;
1044 }
1045 if (a->rm == 13 || a->rm == 15) {
1046 /* UNPREDICTABLE */
1047 return false;
1048 }
1049 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1050 return true;
1051 }
1052
1053 qd = mve_qreg_ptr(a->qd);
1054 qn = mve_qreg_ptr(a->qn);
1055 rm = load_reg(s, a->rm);
1056 fn(tcg_env, qd, qn, rm);
1057 mve_update_eci(s);
1058 return true;
1059 }
1060
1061 #define DO_2OP_SCALAR(INSN, FN) \
1062 static bool trans_##INSN(DisasContext *s, arg_2scalar *a) \
1063 { \
1064 static MVEGenTwoOpScalarFn * const fns[] = { \
1065 gen_helper_mve_##FN##b, \
1066 gen_helper_mve_##FN##h, \
1067 gen_helper_mve_##FN##w, \
1068 NULL, \
1069 }; \
1070 return do_2op_scalar(s, a, fns[a->size]); \
1071 }
1072
1073 DO_2OP_SCALAR(VADD_scalar, vadd_scalar)
1074 DO_2OP_SCALAR(VSUB_scalar, vsub_scalar)
1075 DO_2OP_SCALAR(VMUL_scalar, vmul_scalar)
1076 DO_2OP_SCALAR(VHADD_S_scalar, vhadds_scalar)
1077 DO_2OP_SCALAR(VHADD_U_scalar, vhaddu_scalar)
1078 DO_2OP_SCALAR(VHSUB_S_scalar, vhsubs_scalar)
1079 DO_2OP_SCALAR(VHSUB_U_scalar, vhsubu_scalar)
1080 DO_2OP_SCALAR(VQADD_S_scalar, vqadds_scalar)
1081 DO_2OP_SCALAR(VQADD_U_scalar, vqaddu_scalar)
1082 DO_2OP_SCALAR(VQSUB_S_scalar, vqsubs_scalar)
1083 DO_2OP_SCALAR(VQSUB_U_scalar, vqsubu_scalar)
1084 DO_2OP_SCALAR(VQDMULH_scalar, vqdmulh_scalar)
1085 DO_2OP_SCALAR(VQRDMULH_scalar, vqrdmulh_scalar)
1086 DO_2OP_SCALAR(VBRSR, vbrsr)
1087 DO_2OP_SCALAR(VMLA, vmla)
1088 DO_2OP_SCALAR(VMLAS, vmlas)
1089 DO_2OP_SCALAR(VQDMLAH, vqdmlah)
1090 DO_2OP_SCALAR(VQRDMLAH, vqrdmlah)
1091 DO_2OP_SCALAR(VQDMLASH, vqdmlash)
1092 DO_2OP_SCALAR(VQRDMLASH, vqrdmlash)
1093
1094 static bool trans_VQDMULLB_scalar(DisasContext *s, arg_2scalar *a)
1095 {
1096 static MVEGenTwoOpScalarFn * const fns[] = {
1097 NULL,
1098 gen_helper_mve_vqdmullb_scalarh,
1099 gen_helper_mve_vqdmullb_scalarw,
1100 NULL,
1101 };
1102 if (a->qd == a->qn && a->size == MO_32) {
1103 /* UNPREDICTABLE; we choose to undef */
1104 return false;
1105 }
1106 return do_2op_scalar(s, a, fns[a->size]);
1107 }
1108
1109 static bool trans_VQDMULLT_scalar(DisasContext *s, arg_2scalar *a)
1110 {
1111 static MVEGenTwoOpScalarFn * const fns[] = {
1112 NULL,
1113 gen_helper_mve_vqdmullt_scalarh,
1114 gen_helper_mve_vqdmullt_scalarw,
1115 NULL,
1116 };
1117 if (a->qd == a->qn && a->size == MO_32) {
1118 /* UNPREDICTABLE; we choose to undef */
1119 return false;
1120 }
1121 return do_2op_scalar(s, a, fns[a->size]);
1122 }
1123
1124
1125 #define DO_2OP_FP_SCALAR(INSN, FN) \
1126 static bool trans_##INSN(DisasContext *s, arg_2scalar *a) \
1127 { \
1128 static MVEGenTwoOpScalarFn * const fns[] = { \
1129 NULL, \
1130 gen_helper_mve_##FN##h, \
1131 gen_helper_mve_##FN##s, \
1132 NULL, \
1133 }; \
1134 if (!dc_isar_feature(aa32_mve_fp, s)) { \
1135 return false; \
1136 } \
1137 return do_2op_scalar(s, a, fns[a->size]); \
1138 }
1139
1140 DO_2OP_FP_SCALAR(VADD_fp_scalar, vfadd_scalar)
1141 DO_2OP_FP_SCALAR(VSUB_fp_scalar, vfsub_scalar)
1142 DO_2OP_FP_SCALAR(VMUL_fp_scalar, vfmul_scalar)
1143 DO_2OP_FP_SCALAR(VFMA_scalar, vfma_scalar)
1144 DO_2OP_FP_SCALAR(VFMAS_scalar, vfmas_scalar)
1145
1146 static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a,
1147 MVEGenLongDualAccOpFn *fn)
1148 {
1149 TCGv_ptr qn, qm;
1150 TCGv_i64 rda_i, rda_o;
1151 TCGv_i32 rdalo, rdahi;
1152
1153 if (!dc_isar_feature(aa32_mve, s) ||
1154 !mve_check_qreg_bank(s, a->qn | a->qm) ||
1155 !fn) {
1156 return false;
1157 }
1158 /*
1159 * rdahi == 13 is UNPREDICTABLE; rdahi == 15 is a related
1160 * encoding; rdalo always has bit 0 clear so cannot be 13 or 15.
1161 */
1162 if (a->rdahi == 13 || a->rdahi == 15) {
1163 return false;
1164 }
1165 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1166 return true;
1167 }
1168
1169 qn = mve_qreg_ptr(a->qn);
1170 qm = mve_qreg_ptr(a->qm);
1171
1172 /*
1173 * This insn is subject to beat-wise execution. Partial execution
1174 * of an A=0 (no-accumulate) insn which does not execute the first
1175 * beat must start with the current rda value, not 0.
1176 */
1177 rda_o = tcg_temp_new_i64();
1178 if (a->a || mve_skip_first_beat(s)) {
1179 rda_i = rda_o;
1180 rdalo = load_reg(s, a->rdalo);
1181 rdahi = load_reg(s, a->rdahi);
1182 tcg_gen_concat_i32_i64(rda_i, rdalo, rdahi);
1183 } else {
1184 rda_i = tcg_constant_i64(0);
1185 }
1186
1187 fn(rda_o, tcg_env, qn, qm, rda_i);
1188
1189 rdalo = tcg_temp_new_i32();
1190 rdahi = tcg_temp_new_i32();
1191 tcg_gen_extrl_i64_i32(rdalo, rda_o);
1192 tcg_gen_extrh_i64_i32(rdahi, rda_o);
1193 store_reg(s, a->rdalo, rdalo);
1194 store_reg(s, a->rdahi, rdahi);
1195 mve_update_eci(s);
1196 return true;
1197 }
1198
1199 static bool trans_VMLALDAV_S(DisasContext *s, arg_vmlaldav *a)
1200 {
1201 static MVEGenLongDualAccOpFn * const fns[4][2] = {
1202 { NULL, NULL },
1203 { gen_helper_mve_vmlaldavsh, gen_helper_mve_vmlaldavxsh },
1204 { gen_helper_mve_vmlaldavsw, gen_helper_mve_vmlaldavxsw },
1205 { NULL, NULL },
1206 };
1207 return do_long_dual_acc(s, a, fns[a->size][a->x]);
1208 }
1209
1210 static bool trans_VMLALDAV_U(DisasContext *s, arg_vmlaldav *a)
1211 {
1212 static MVEGenLongDualAccOpFn * const fns[4][2] = {
1213 { NULL, NULL },
1214 { gen_helper_mve_vmlaldavuh, NULL },
1215 { gen_helper_mve_vmlaldavuw, NULL },
1216 { NULL, NULL },
1217 };
1218 return do_long_dual_acc(s, a, fns[a->size][a->x]);
1219 }
1220
1221 static bool trans_VMLSLDAV(DisasContext *s, arg_vmlaldav *a)
1222 {
1223 static MVEGenLongDualAccOpFn * const fns[4][2] = {
1224 { NULL, NULL },
1225 { gen_helper_mve_vmlsldavsh, gen_helper_mve_vmlsldavxsh },
1226 { gen_helper_mve_vmlsldavsw, gen_helper_mve_vmlsldavxsw },
1227 { NULL, NULL },
1228 };
1229 return do_long_dual_acc(s, a, fns[a->size][a->x]);
1230 }
1231
1232 static bool trans_VRMLALDAVH_S(DisasContext *s, arg_vmlaldav *a)
1233 {
1234 static MVEGenLongDualAccOpFn * const fns[] = {
1235 gen_helper_mve_vrmlaldavhsw, gen_helper_mve_vrmlaldavhxsw,
1236 };
1237 return do_long_dual_acc(s, a, fns[a->x]);
1238 }
1239
1240 static bool trans_VRMLALDAVH_U(DisasContext *s, arg_vmlaldav *a)
1241 {
1242 static MVEGenLongDualAccOpFn * const fns[] = {
1243 gen_helper_mve_vrmlaldavhuw, NULL,
1244 };
1245 return do_long_dual_acc(s, a, fns[a->x]);
1246 }
1247
1248 static bool trans_VRMLSLDAVH(DisasContext *s, arg_vmlaldav *a)
1249 {
1250 static MVEGenLongDualAccOpFn * const fns[] = {
1251 gen_helper_mve_vrmlsldavhsw, gen_helper_mve_vrmlsldavhxsw,
1252 };
1253 return do_long_dual_acc(s, a, fns[a->x]);
1254 }
1255
1256 static bool do_dual_acc(DisasContext *s, arg_vmladav *a, MVEGenDualAccOpFn *fn)
1257 {
1258 TCGv_ptr qn, qm;
1259 TCGv_i32 rda_i, rda_o;
1260
1261 if (!dc_isar_feature(aa32_mve, s) ||
1262 !mve_check_qreg_bank(s, a->qn) ||
1263 !fn) {
1264 return false;
1265 }
1266 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1267 return true;
1268 }
1269
1270 qn = mve_qreg_ptr(a->qn);
1271 qm = mve_qreg_ptr(a->qm);
1272
1273 /*
1274 * This insn is subject to beat-wise execution. Partial execution
1275 * of an A=0 (no-accumulate) insn which does not execute the first
1276 * beat must start with the current rda value, not 0.
1277 */
1278 if (a->a || mve_skip_first_beat(s)) {
1279 rda_o = rda_i = load_reg(s, a->rda);
1280 } else {
1281 rda_i = tcg_constant_i32(0);
1282 rda_o = tcg_temp_new_i32();
1283 }
1284
1285 fn(rda_o, tcg_env, qn, qm, rda_i);
1286 store_reg(s, a->rda, rda_o);
1287
1288 mve_update_eci(s);
1289 return true;
1290 }
1291
1292 #define DO_DUAL_ACC(INSN, FN) \
1293 static bool trans_##INSN(DisasContext *s, arg_vmladav *a) \
1294 { \
1295 static MVEGenDualAccOpFn * const fns[4][2] = { \
1296 { gen_helper_mve_##FN##b, gen_helper_mve_##FN##xb }, \
1297 { gen_helper_mve_##FN##h, gen_helper_mve_##FN##xh }, \
1298 { gen_helper_mve_##FN##w, gen_helper_mve_##FN##xw }, \
1299 { NULL, NULL }, \
1300 }; \
1301 return do_dual_acc(s, a, fns[a->size][a->x]); \
1302 }
1303
1304 DO_DUAL_ACC(VMLADAV_S, vmladavs)
1305 DO_DUAL_ACC(VMLSDAV, vmlsdav)
1306
1307 static bool trans_VMLADAV_U(DisasContext *s, arg_vmladav *a)
1308 {
1309 static MVEGenDualAccOpFn * const fns[4][2] = {
1310 { gen_helper_mve_vmladavub, NULL },
1311 { gen_helper_mve_vmladavuh, NULL },
1312 { gen_helper_mve_vmladavuw, NULL },
1313 { NULL, NULL },
1314 };
1315 return do_dual_acc(s, a, fns[a->size][a->x]);
1316 }
1317
1318 static void gen_vpst(DisasContext *s, uint32_t mask)
1319 {
1320 /*
1321 * Set the VPR mask fields. We take advantage of MASK01 and MASK23
1322 * being adjacent fields in the register.
1323 *
1324 * Updating the masks is not predicated, but it is subject to beat-wise
1325 * execution, and the mask is updated on the odd-numbered beats.
1326 * So if PSR.ECI says we should skip beat 1, we mustn't update the
1327 * 01 mask field.
1328 */
1329 TCGv_i32 vpr = load_cpu_field(v7m.vpr);
1330 switch (s->eci) {
1331 case ECI_NONE:
1332 case ECI_A0:
1333 /* Update both 01 and 23 fields */
1334 tcg_gen_deposit_i32(vpr, vpr,
1335 tcg_constant_i32(mask | (mask << 4)),
1336 R_V7M_VPR_MASK01_SHIFT,
1337 R_V7M_VPR_MASK01_LENGTH + R_V7M_VPR_MASK23_LENGTH);
1338 break;
1339 case ECI_A0A1:
1340 case ECI_A0A1A2:
1341 case ECI_A0A1A2B0:
1342 /* Update only the 23 mask field */
1343 tcg_gen_deposit_i32(vpr, vpr,
1344 tcg_constant_i32(mask),
1345 R_V7M_VPR_MASK23_SHIFT, R_V7M_VPR_MASK23_LENGTH);
1346 break;
1347 default:
1348 g_assert_not_reached();
1349 }
1350 store_cpu_field(vpr, v7m.vpr);
1351 }
1352
1353 static bool trans_VPST(DisasContext *s, arg_VPST *a)
1354 {
1355 /* mask == 0 is a "related encoding" */
1356 if (!dc_isar_feature(aa32_mve, s) || !a->mask) {
1357 return false;
1358 }
1359 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1360 return true;
1361 }
1362 gen_vpst(s, a->mask);
1363 mve_update_and_store_eci(s);
1364 return true;
1365 }
1366
1367 static bool trans_VPNOT(DisasContext *s, arg_VPNOT *a)
1368 {
1369 /*
1370 * Invert the predicate in VPR.P0. We have call out to
1371 * a helper because this insn itself is beatwise and can
1372 * be predicated.
1373 */
1374 if (!dc_isar_feature(aa32_mve, s)) {
1375 return false;
1376 }
1377 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1378 return true;
1379 }
1380
1381 gen_helper_mve_vpnot(tcg_env);
1382 /* This insn updates predication bits */
1383 s->base.is_jmp = DISAS_UPDATE_NOCHAIN;
1384 mve_update_eci(s);
1385 return true;
1386 }
1387
1388 static bool trans_VADDV(DisasContext *s, arg_VADDV *a)
1389 {
1390 /* VADDV: vector add across vector */
1391 static MVEGenVADDVFn * const fns[4][2] = {
1392 { gen_helper_mve_vaddvsb, gen_helper_mve_vaddvub },
1393 { gen_helper_mve_vaddvsh, gen_helper_mve_vaddvuh },
1394 { gen_helper_mve_vaddvsw, gen_helper_mve_vaddvuw },
1395 { NULL, NULL }
1396 };
1397 TCGv_ptr qm;
1398 TCGv_i32 rda_i, rda_o;
1399
1400 if (!dc_isar_feature(aa32_mve, s) ||
1401 a->size == 3) {
1402 return false;
1403 }
1404 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1405 return true;
1406 }
1407
1408 /*
1409 * This insn is subject to beat-wise execution. Partial execution
1410 * of an A=0 (no-accumulate) insn which does not execute the first
1411 * beat must start with the current value of Rda, not zero.
1412 */
1413 if (a->a || mve_skip_first_beat(s)) {
1414 /* Accumulate input from Rda */
1415 rda_o = rda_i = load_reg(s, a->rda);
1416 } else {
1417 /* Accumulate starting at zero */
1418 rda_i = tcg_constant_i32(0);
1419 rda_o = tcg_temp_new_i32();
1420 }
1421
1422 qm = mve_qreg_ptr(a->qm);
1423 fns[a->size][a->u](rda_o, tcg_env, qm, rda_i);
1424 store_reg(s, a->rda, rda_o);
1425
1426 mve_update_eci(s);
1427 return true;
1428 }
1429
1430 static bool trans_VADDLV(DisasContext *s, arg_VADDLV *a)
1431 {
1432 /*
1433 * Vector Add Long Across Vector: accumulate the 32-bit
1434 * elements of the vector into a 64-bit result stored in
1435 * a pair of general-purpose registers.
1436 * No need to check Qm's bank: it is only 3 bits in decode.
1437 */
1438 TCGv_ptr qm;
1439 TCGv_i64 rda_i, rda_o;
1440 TCGv_i32 rdalo, rdahi;
1441
1442 if (!dc_isar_feature(aa32_mve, s)) {
1443 return false;
1444 }
1445 /*
1446 * rdahi == 13 is UNPREDICTABLE; rdahi == 15 is a related
1447 * encoding; rdalo always has bit 0 clear so cannot be 13 or 15.
1448 */
1449 if (a->rdahi == 13 || a->rdahi == 15) {
1450 return false;
1451 }
1452 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1453 return true;
1454 }
1455
1456 /*
1457 * This insn is subject to beat-wise execution. Partial execution
1458 * of an A=0 (no-accumulate) insn which does not execute the first
1459 * beat must start with the current value of RdaHi:RdaLo, not zero.
1460 */
1461 rda_o = tcg_temp_new_i64();
1462 if (a->a || mve_skip_first_beat(s)) {
1463 /* Accumulate input from RdaHi:RdaLo */
1464 rda_i = rda_o;
1465 rdalo = load_reg(s, a->rdalo);
1466 rdahi = load_reg(s, a->rdahi);
1467 tcg_gen_concat_i32_i64(rda_i, rdalo, rdahi);
1468 } else {
1469 /* Accumulate starting at zero */
1470 rda_i = tcg_constant_i64(0);
1471 }
1472
1473 qm = mve_qreg_ptr(a->qm);
1474 if (a->u) {
1475 gen_helper_mve_vaddlv_u(rda_o, tcg_env, qm, rda_i);
1476 } else {
1477 gen_helper_mve_vaddlv_s(rda_o, tcg_env, qm, rda_i);
1478 }
1479
1480 rdalo = tcg_temp_new_i32();
1481 rdahi = tcg_temp_new_i32();
1482 tcg_gen_extrl_i64_i32(rdalo, rda_o);
1483 tcg_gen_extrh_i64_i32(rdahi, rda_o);
1484 store_reg(s, a->rdalo, rdalo);
1485 store_reg(s, a->rdahi, rdahi);
1486 mve_update_eci(s);
1487 return true;
1488 }
1489
1490 static bool do_1imm(DisasContext *s, arg_1imm *a, MVEGenOneOpImmFn *fn,
1491 GVecGen2iFn *vecfn)
1492 {
1493 TCGv_ptr qd;
1494 uint64_t imm;
1495
1496 if (!dc_isar_feature(aa32_mve, s) ||
1497 !mve_check_qreg_bank(s, a->qd) ||
1498 !fn) {
1499 return false;
1500 }
1501 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1502 return true;
1503 }
1504
1505 imm = asimd_imm_const(a->imm, a->cmode, a->op);
1506
1507 if (vecfn && mve_no_predication(s)) {
1508 vecfn(MO_64, mve_qreg_offset(a->qd), mve_qreg_offset(a->qd),
1509 imm, 16, 16);
1510 } else {
1511 qd = mve_qreg_ptr(a->qd);
1512 fn(tcg_env, qd, tcg_constant_i64(imm));
1513 }
1514 mve_update_eci(s);
1515 return true;
1516 }
1517
1518 static void gen_gvec_vmovi(unsigned vece, uint32_t dofs, uint32_t aofs,
1519 int64_t c, uint32_t oprsz, uint32_t maxsz)
1520 {
1521 tcg_gen_gvec_dup_imm(vece, dofs, oprsz, maxsz, c);
1522 }
1523
1524 static bool trans_Vimm_1r(DisasContext *s, arg_1imm *a)
1525 {
1526 /* Handle decode of cmode/op here between VORR/VBIC/VMOV */
1527 MVEGenOneOpImmFn *fn;
1528 GVecGen2iFn *vecfn;
1529
1530 if ((a->cmode & 1) && a->cmode < 12) {
1531 if (a->op) {
1532 /*
1533 * For op=1, the immediate will be inverted by asimd_imm_const(),
1534 * so the VBIC becomes a logical AND operation.
1535 */
1536 fn = gen_helper_mve_vandi;
1537 vecfn = tcg_gen_gvec_andi;
1538 } else {
1539 fn = gen_helper_mve_vorri;
1540 vecfn = tcg_gen_gvec_ori;
1541 }
1542 } else {
1543 /* There is one unallocated cmode/op combination in this space */
1544 if (a->cmode == 15 && a->op == 1) {
1545 return false;
1546 }
1547 /* asimd_imm_const() sorts out VMVNI vs VMOVI for us */
1548 fn = gen_helper_mve_vmovi;
1549 vecfn = gen_gvec_vmovi;
1550 }
1551 return do_1imm(s, a, fn, vecfn);
1552 }
1553
1554 static bool do_2shift_vec(DisasContext *s, arg_2shift *a, MVEGenTwoOpShiftFn fn,
1555 bool negateshift, GVecGen2iFn vecfn)
1556 {
1557 TCGv_ptr qd, qm;
1558 int shift = a->shift;
1559
1560 if (!dc_isar_feature(aa32_mve, s) ||
1561 !mve_check_qreg_bank(s, a->qd | a->qm) ||
1562 !fn) {
1563 return false;
1564 }
1565 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1566 return true;
1567 }
1568
1569 /*
1570 * When we handle a right shift insn using a left-shift helper
1571 * which permits a negative shift count to indicate a right-shift,
1572 * we must negate the shift count.
1573 */
1574 if (negateshift) {
1575 shift = -shift;
1576 }
1577
1578 if (vecfn && mve_no_predication(s)) {
1579 vecfn(a->size, mve_qreg_offset(a->qd), mve_qreg_offset(a->qm),
1580 shift, 16, 16);
1581 } else {
1582 qd = mve_qreg_ptr(a->qd);
1583 qm = mve_qreg_ptr(a->qm);
1584 fn(tcg_env, qd, qm, tcg_constant_i32(shift));
1585 }
1586 mve_update_eci(s);
1587 return true;
1588 }
1589
1590 static bool do_2shift(DisasContext *s, arg_2shift *a, MVEGenTwoOpShiftFn fn,
1591 bool negateshift)
1592 {
1593 return do_2shift_vec(s, a, fn, negateshift, NULL);
1594 }
1595
1596 #define DO_2SHIFT_VEC(INSN, FN, NEGATESHIFT, VECFN) \
1597 static bool trans_##INSN(DisasContext *s, arg_2shift *a) \
1598 { \
1599 static MVEGenTwoOpShiftFn * const fns[] = { \
1600 gen_helper_mve_##FN##b, \
1601 gen_helper_mve_##FN##h, \
1602 gen_helper_mve_##FN##w, \
1603 NULL, \
1604 }; \
1605 return do_2shift_vec(s, a, fns[a->size], NEGATESHIFT, VECFN); \
1606 }
1607
1608 #define DO_2SHIFT(INSN, FN, NEGATESHIFT) \
1609 DO_2SHIFT_VEC(INSN, FN, NEGATESHIFT, NULL)
1610
1611 static void do_gvec_shri_s(unsigned vece, uint32_t dofs, uint32_t aofs,
1612 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1613 {
1614 /*
1615 * We get here with a negated shift count, and we must handle
1616 * shifts by the element size, which tcg_gen_gvec_sari() does not do.
1617 */
1618 shift = -shift;
1619 if (shift == (8 << vece)) {
1620 shift--;
1621 }
1622 tcg_gen_gvec_sari(vece, dofs, aofs, shift, oprsz, maxsz);
1623 }
1624
1625 static void do_gvec_shri_u(unsigned vece, uint32_t dofs, uint32_t aofs,
1626 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1627 {
1628 /*
1629 * We get here with a negated shift count, and we must handle
1630 * shifts by the element size, which tcg_gen_gvec_shri() does not do.
1631 */
1632 shift = -shift;
1633 if (shift == (8 << vece)) {
1634 tcg_gen_gvec_dup_imm(vece, dofs, oprsz, maxsz, 0);
1635 } else {
1636 tcg_gen_gvec_shri(vece, dofs, aofs, shift, oprsz, maxsz);
1637 }
1638 }
1639
1640 DO_2SHIFT_VEC(VSHLI, vshli_u, false, tcg_gen_gvec_shli)
1641 DO_2SHIFT(VQSHLI_S, vqshli_s, false)
1642 DO_2SHIFT(VQSHLI_U, vqshli_u, false)
1643 DO_2SHIFT(VQSHLUI, vqshlui_s, false)
1644 /* These right shifts use a left-shift helper with negated shift count */
1645 DO_2SHIFT_VEC(VSHRI_S, vshli_s, true, do_gvec_shri_s)
1646 DO_2SHIFT_VEC(VSHRI_U, vshli_u, true, do_gvec_shri_u)
1647 DO_2SHIFT(VRSHRI_S, vrshli_s, true)
1648 DO_2SHIFT(VRSHRI_U, vrshli_u, true)
1649
1650 DO_2SHIFT_VEC(VSRI, vsri, false, gen_gvec_sri)
1651 DO_2SHIFT_VEC(VSLI, vsli, false, gen_gvec_sli)
1652
1653 #define DO_2SHIFT_FP(INSN, FN) \
1654 static bool trans_##INSN(DisasContext *s, arg_2shift *a) \
1655 { \
1656 if (!dc_isar_feature(aa32_mve_fp, s)) { \
1657 return false; \
1658 } \
1659 return do_2shift(s, a, gen_helper_mve_##FN, false); \
1660 }
1661
1662 DO_2SHIFT_FP(VCVT_SH_fixed, vcvt_sh)
1663 DO_2SHIFT_FP(VCVT_UH_fixed, vcvt_uh)
1664 DO_2SHIFT_FP(VCVT_HS_fixed, vcvt_hs)
1665 DO_2SHIFT_FP(VCVT_HU_fixed, vcvt_hu)
1666 DO_2SHIFT_FP(VCVT_SF_fixed, vcvt_sf)
1667 DO_2SHIFT_FP(VCVT_UF_fixed, vcvt_uf)
1668 DO_2SHIFT_FP(VCVT_FS_fixed, vcvt_fs)
1669 DO_2SHIFT_FP(VCVT_FU_fixed, vcvt_fu)
1670
1671 static bool do_2shift_scalar(DisasContext *s, arg_shl_scalar *a,
1672 MVEGenTwoOpShiftFn *fn)
1673 {
1674 TCGv_ptr qda;
1675 TCGv_i32 rm;
1676
1677 if (!dc_isar_feature(aa32_mve, s) ||
1678 !mve_check_qreg_bank(s, a->qda) ||
1679 a->rm == 13 || a->rm == 15 || !fn) {
1680 /* Rm cases are UNPREDICTABLE */
1681 return false;
1682 }
1683 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1684 return true;
1685 }
1686
1687 qda = mve_qreg_ptr(a->qda);
1688 rm = load_reg(s, a->rm);
1689 fn(tcg_env, qda, qda, rm);
1690 mve_update_eci(s);
1691 return true;
1692 }
1693
1694 #define DO_2SHIFT_SCALAR(INSN, FN) \
1695 static bool trans_##INSN(DisasContext *s, arg_shl_scalar *a) \
1696 { \
1697 static MVEGenTwoOpShiftFn * const fns[] = { \
1698 gen_helper_mve_##FN##b, \
1699 gen_helper_mve_##FN##h, \
1700 gen_helper_mve_##FN##w, \
1701 NULL, \
1702 }; \
1703 return do_2shift_scalar(s, a, fns[a->size]); \
1704 }
1705
1706 DO_2SHIFT_SCALAR(VSHL_S_scalar, vshli_s)
1707 DO_2SHIFT_SCALAR(VSHL_U_scalar, vshli_u)
1708 DO_2SHIFT_SCALAR(VRSHL_S_scalar, vrshli_s)
1709 DO_2SHIFT_SCALAR(VRSHL_U_scalar, vrshli_u)
1710 DO_2SHIFT_SCALAR(VQSHL_S_scalar, vqshli_s)
1711 DO_2SHIFT_SCALAR(VQSHL_U_scalar, vqshli_u)
1712 DO_2SHIFT_SCALAR(VQRSHL_S_scalar, vqrshli_s)
1713 DO_2SHIFT_SCALAR(VQRSHL_U_scalar, vqrshli_u)
1714
1715 #define DO_VSHLL(INSN, FN) \
1716 static bool trans_##INSN(DisasContext *s, arg_2shift *a) \
1717 { \
1718 static MVEGenTwoOpShiftFn * const fns[] = { \
1719 gen_helper_mve_##FN##b, \
1720 gen_helper_mve_##FN##h, \
1721 }; \
1722 return do_2shift_vec(s, a, fns[a->size], false, do_gvec_##FN); \
1723 }
1724
1725 /*
1726 * For the VSHLL vector helpers, the vece is the size of the input
1727 * (ie MO_8 or MO_16); the helpers want to work in the output size.
1728 * The shift count can be 0..<input size>, inclusive. (0 is VMOVL.)
1729 */
1730 static void do_gvec_vshllbs(unsigned vece, uint32_t dofs, uint32_t aofs,
1731 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1732 {
1733 unsigned ovece = vece + 1;
1734 unsigned ibits = vece == MO_8 ? 8 : 16;
1735 tcg_gen_gvec_shli(ovece, dofs, aofs, ibits, oprsz, maxsz);
1736 tcg_gen_gvec_sari(ovece, dofs, dofs, ibits - shift, oprsz, maxsz);
1737 }
1738
1739 static void do_gvec_vshllbu(unsigned vece, uint32_t dofs, uint32_t aofs,
1740 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1741 {
1742 unsigned ovece = vece + 1;
1743 tcg_gen_gvec_andi(ovece, dofs, aofs,
1744 ovece == MO_16 ? 0xff : 0xffff, oprsz, maxsz);
1745 tcg_gen_gvec_shli(ovece, dofs, dofs, shift, oprsz, maxsz);
1746 }
1747
1748 static void do_gvec_vshllts(unsigned vece, uint32_t dofs, uint32_t aofs,
1749 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1750 {
1751 unsigned ovece = vece + 1;
1752 unsigned ibits = vece == MO_8 ? 8 : 16;
1753 if (shift == 0) {
1754 tcg_gen_gvec_sari(ovece, dofs, aofs, ibits, oprsz, maxsz);
1755 } else {
1756 tcg_gen_gvec_andi(ovece, dofs, aofs,
1757 ovece == MO_16 ? 0xff00 : 0xffff0000, oprsz, maxsz);
1758 tcg_gen_gvec_sari(ovece, dofs, dofs, ibits - shift, oprsz, maxsz);
1759 }
1760 }
1761
1762 static void do_gvec_vshlltu(unsigned vece, uint32_t dofs, uint32_t aofs,
1763 int64_t shift, uint32_t oprsz, uint32_t maxsz)
1764 {
1765 unsigned ovece = vece + 1;
1766 unsigned ibits = vece == MO_8 ? 8 : 16;
1767 if (shift == 0) {
1768 tcg_gen_gvec_shri(ovece, dofs, aofs, ibits, oprsz, maxsz);
1769 } else {
1770 tcg_gen_gvec_andi(ovece, dofs, aofs,
1771 ovece == MO_16 ? 0xff00 : 0xffff0000, oprsz, maxsz);
1772 tcg_gen_gvec_shri(ovece, dofs, dofs, ibits - shift, oprsz, maxsz);
1773 }
1774 }
1775
1776 DO_VSHLL(VSHLL_BS, vshllbs)
1777 DO_VSHLL(VSHLL_BU, vshllbu)
1778 DO_VSHLL(VSHLL_TS, vshllts)
1779 DO_VSHLL(VSHLL_TU, vshlltu)
1780
1781 #define DO_2SHIFT_N(INSN, FN) \
1782 static bool trans_##INSN(DisasContext *s, arg_2shift *a) \
1783 { \
1784 static MVEGenTwoOpShiftFn * const fns[] = { \
1785 gen_helper_mve_##FN##b, \
1786 gen_helper_mve_##FN##h, \
1787 }; \
1788 return do_2shift(s, a, fns[a->size], false); \
1789 }
1790
1791 DO_2SHIFT_N(VSHRNB, vshrnb)
1792 DO_2SHIFT_N(VSHRNT, vshrnt)
1793 DO_2SHIFT_N(VRSHRNB, vrshrnb)
1794 DO_2SHIFT_N(VRSHRNT, vrshrnt)
1795 DO_2SHIFT_N(VQSHRNB_S, vqshrnb_s)
1796 DO_2SHIFT_N(VQSHRNT_S, vqshrnt_s)
1797 DO_2SHIFT_N(VQSHRNB_U, vqshrnb_u)
1798 DO_2SHIFT_N(VQSHRNT_U, vqshrnt_u)
1799 DO_2SHIFT_N(VQSHRUNB, vqshrunb)
1800 DO_2SHIFT_N(VQSHRUNT, vqshrunt)
1801 DO_2SHIFT_N(VQRSHRNB_S, vqrshrnb_s)
1802 DO_2SHIFT_N(VQRSHRNT_S, vqrshrnt_s)
1803 DO_2SHIFT_N(VQRSHRNB_U, vqrshrnb_u)
1804 DO_2SHIFT_N(VQRSHRNT_U, vqrshrnt_u)
1805 DO_2SHIFT_N(VQRSHRUNB, vqrshrunb)
1806 DO_2SHIFT_N(VQRSHRUNT, vqrshrunt)
1807
1808 static bool trans_VSHLC(DisasContext *s, arg_VSHLC *a)
1809 {
1810 /*
1811 * Whole Vector Left Shift with Carry. The carry is taken
1812 * from a general purpose register and written back there.
1813 * An imm of 0 means "shift by 32".
1814 */
1815 TCGv_ptr qd;
1816 TCGv_i32 rdm;
1817
1818 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd)) {
1819 return false;
1820 }
1821 if (a->rdm == 13 || a->rdm == 15) {
1822 /* CONSTRAINED UNPREDICTABLE: we UNDEF */
1823 return false;
1824 }
1825 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1826 return true;
1827 }
1828
1829 qd = mve_qreg_ptr(a->qd);
1830 rdm = load_reg(s, a->rdm);
1831 gen_helper_mve_vshlc(rdm, tcg_env, qd, rdm, tcg_constant_i32(a->imm));
1832 store_reg(s, a->rdm, rdm);
1833 mve_update_eci(s);
1834 return true;
1835 }
1836
1837 static bool do_vidup(DisasContext *s, arg_vidup *a, MVEGenVIDUPFn *fn)
1838 {
1839 TCGv_ptr qd;
1840 TCGv_i32 rn;
1841
1842 /*
1843 * Vector increment/decrement with wrap and duplicate (VIDUP, VDDUP).
1844 * This fills the vector with elements of successively increasing
1845 * or decreasing values, starting from Rn.
1846 */
1847 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd)) {
1848 return false;
1849 }
1850 if (a->size == MO_64) {
1851 /* size 0b11 is another encoding */
1852 return false;
1853 }
1854 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1855 return true;
1856 }
1857
1858 qd = mve_qreg_ptr(a->qd);
1859 rn = load_reg(s, a->rn);
1860 fn(rn, tcg_env, qd, rn, tcg_constant_i32(a->imm));
1861 store_reg(s, a->rn, rn);
1862 mve_update_eci(s);
1863 return true;
1864 }
1865
1866 static bool do_viwdup(DisasContext *s, arg_viwdup *a, MVEGenVIWDUPFn *fn)
1867 {
1868 TCGv_ptr qd;
1869 TCGv_i32 rn, rm;
1870
1871 /*
1872 * Vector increment/decrement with wrap and duplicate (VIWDUp, VDWDUP)
1873 * This fills the vector with elements of successively increasing
1874 * or decreasing values, starting from Rn. Rm specifies a point where
1875 * the count wraps back around to 0. The updated offset is written back
1876 * to Rn.
1877 */
1878 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd)) {
1879 return false;
1880 }
1881 if (!fn || a->rm == 13 || a->rm == 15) {
1882 /*
1883 * size 0b11 is another encoding; Rm == 13 is UNPREDICTABLE;
1884 * Rm == 13 is VIWDUP, VDWDUP.
1885 */
1886 return false;
1887 }
1888 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1889 return true;
1890 }
1891
1892 qd = mve_qreg_ptr(a->qd);
1893 rn = load_reg(s, a->rn);
1894 rm = load_reg(s, a->rm);
1895 fn(rn, tcg_env, qd, rn, rm, tcg_constant_i32(a->imm));
1896 store_reg(s, a->rn, rn);
1897 mve_update_eci(s);
1898 return true;
1899 }
1900
1901 static bool trans_VIDUP(DisasContext *s, arg_vidup *a)
1902 {
1903 static MVEGenVIDUPFn * const fns[] = {
1904 gen_helper_mve_vidupb,
1905 gen_helper_mve_viduph,
1906 gen_helper_mve_vidupw,
1907 NULL,
1908 };
1909 return do_vidup(s, a, fns[a->size]);
1910 }
1911
1912 static bool trans_VDDUP(DisasContext *s, arg_vidup *a)
1913 {
1914 static MVEGenVIDUPFn * const fns[] = {
1915 gen_helper_mve_vidupb,
1916 gen_helper_mve_viduph,
1917 gen_helper_mve_vidupw,
1918 NULL,
1919 };
1920 /* VDDUP is just like VIDUP but with a negative immediate */
1921 a->imm = -a->imm;
1922 return do_vidup(s, a, fns[a->size]);
1923 }
1924
1925 static bool trans_VIWDUP(DisasContext *s, arg_viwdup *a)
1926 {
1927 static MVEGenVIWDUPFn * const fns[] = {
1928 gen_helper_mve_viwdupb,
1929 gen_helper_mve_viwduph,
1930 gen_helper_mve_viwdupw,
1931 NULL,
1932 };
1933 return do_viwdup(s, a, fns[a->size]);
1934 }
1935
1936 static bool trans_VDWDUP(DisasContext *s, arg_viwdup *a)
1937 {
1938 static MVEGenVIWDUPFn * const fns[] = {
1939 gen_helper_mve_vdwdupb,
1940 gen_helper_mve_vdwduph,
1941 gen_helper_mve_vdwdupw,
1942 NULL,
1943 };
1944 return do_viwdup(s, a, fns[a->size]);
1945 }
1946
1947 static bool do_vcmp(DisasContext *s, arg_vcmp *a, MVEGenCmpFn *fn)
1948 {
1949 TCGv_ptr qn, qm;
1950
1951 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qm) ||
1952 !fn) {
1953 return false;
1954 }
1955 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1956 return true;
1957 }
1958
1959 qn = mve_qreg_ptr(a->qn);
1960 qm = mve_qreg_ptr(a->qm);
1961 fn(tcg_env, qn, qm);
1962 if (a->mask) {
1963 /* VPT */
1964 gen_vpst(s, a->mask);
1965 }
1966 /* This insn updates predication bits */
1967 s->base.is_jmp = DISAS_UPDATE_NOCHAIN;
1968 mve_update_eci(s);
1969 return true;
1970 }
1971
1972 static bool do_vcmp_scalar(DisasContext *s, arg_vcmp_scalar *a,
1973 MVEGenScalarCmpFn *fn)
1974 {
1975 TCGv_ptr qn;
1976 TCGv_i32 rm;
1977
1978 if (!dc_isar_feature(aa32_mve, s) || !fn || a->rm == 13) {
1979 return false;
1980 }
1981 if (!mve_eci_check(s) || !vfp_access_check(s)) {
1982 return true;
1983 }
1984
1985 qn = mve_qreg_ptr(a->qn);
1986 if (a->rm == 15) {
1987 /* Encoding Rm=0b1111 means "constant zero" */
1988 rm = tcg_constant_i32(0);
1989 } else {
1990 rm = load_reg(s, a->rm);
1991 }
1992 fn(tcg_env, qn, rm);
1993 if (a->mask) {
1994 /* VPT */
1995 gen_vpst(s, a->mask);
1996 }
1997 /* This insn updates predication bits */
1998 s->base.is_jmp = DISAS_UPDATE_NOCHAIN;
1999 mve_update_eci(s);
2000 return true;
2001 }
2002
2003 #define DO_VCMP(INSN, FN) \
2004 static bool trans_##INSN(DisasContext *s, arg_vcmp *a) \
2005 { \
2006 static MVEGenCmpFn * const fns[] = { \
2007 gen_helper_mve_##FN##b, \
2008 gen_helper_mve_##FN##h, \
2009 gen_helper_mve_##FN##w, \
2010 NULL, \
2011 }; \
2012 return do_vcmp(s, a, fns[a->size]); \
2013 } \
2014 static bool trans_##INSN##_scalar(DisasContext *s, \
2015 arg_vcmp_scalar *a) \
2016 { \
2017 static MVEGenScalarCmpFn * const fns[] = { \
2018 gen_helper_mve_##FN##_scalarb, \
2019 gen_helper_mve_##FN##_scalarh, \
2020 gen_helper_mve_##FN##_scalarw, \
2021 NULL, \
2022 }; \
2023 return do_vcmp_scalar(s, a, fns[a->size]); \
2024 }
2025
2026 DO_VCMP(VCMPEQ, vcmpeq)
2027 DO_VCMP(VCMPNE, vcmpne)
2028 DO_VCMP(VCMPCS, vcmpcs)
2029 DO_VCMP(VCMPHI, vcmphi)
2030 DO_VCMP(VCMPGE, vcmpge)
2031 DO_VCMP(VCMPLT, vcmplt)
2032 DO_VCMP(VCMPGT, vcmpgt)
2033 DO_VCMP(VCMPLE, vcmple)
2034
2035 #define DO_VCMP_FP(INSN, FN) \
2036 static bool trans_##INSN(DisasContext *s, arg_vcmp *a) \
2037 { \
2038 static MVEGenCmpFn * const fns[] = { \
2039 NULL, \
2040 gen_helper_mve_##FN##h, \
2041 gen_helper_mve_##FN##s, \
2042 NULL, \
2043 }; \
2044 if (!dc_isar_feature(aa32_mve_fp, s)) { \
2045 return false; \
2046 } \
2047 return do_vcmp(s, a, fns[a->size]); \
2048 } \
2049 static bool trans_##INSN##_scalar(DisasContext *s, \
2050 arg_vcmp_scalar *a) \
2051 { \
2052 static MVEGenScalarCmpFn * const fns[] = { \
2053 NULL, \
2054 gen_helper_mve_##FN##_scalarh, \
2055 gen_helper_mve_##FN##_scalars, \
2056 NULL, \
2057 }; \
2058 if (!dc_isar_feature(aa32_mve_fp, s)) { \
2059 return false; \
2060 } \
2061 return do_vcmp_scalar(s, a, fns[a->size]); \
2062 }
2063
2064 DO_VCMP_FP(VCMPEQ_fp, vfcmpeq)
2065 DO_VCMP_FP(VCMPNE_fp, vfcmpne)
2066 DO_VCMP_FP(VCMPGE_fp, vfcmpge)
2067 DO_VCMP_FP(VCMPLT_fp, vfcmplt)
2068 DO_VCMP_FP(VCMPGT_fp, vfcmpgt)
2069 DO_VCMP_FP(VCMPLE_fp, vfcmple)
2070
2071 static bool do_vmaxv(DisasContext *s, arg_vmaxv *a, MVEGenVADDVFn fn)
2072 {
2073 /*
2074 * MIN/MAX operations across a vector: compute the min or
2075 * max of the initial value in a general purpose register
2076 * and all the elements in the vector, and store it back
2077 * into the general purpose register.
2078 */
2079 TCGv_ptr qm;
2080 TCGv_i32 rda;
2081
2082 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qm) ||
2083 !fn || a->rda == 13 || a->rda == 15) {
2084 /* Rda cases are UNPREDICTABLE */
2085 return false;
2086 }
2087 if (!mve_eci_check(s) || !vfp_access_check(s)) {
2088 return true;
2089 }
2090
2091 qm = mve_qreg_ptr(a->qm);
2092 rda = load_reg(s, a->rda);
2093 fn(rda, tcg_env, qm, rda);
2094 store_reg(s, a->rda, rda);
2095 mve_update_eci(s);
2096 return true;
2097 }
2098
2099 #define DO_VMAXV(INSN, FN) \
2100 static bool trans_##INSN(DisasContext *s, arg_vmaxv *a) \
2101 { \
2102 static MVEGenVADDVFn * const fns[] = { \
2103 gen_helper_mve_##FN##b, \
2104 gen_helper_mve_##FN##h, \
2105 gen_helper_mve_##FN##w, \
2106 NULL, \
2107 }; \
2108 return do_vmaxv(s, a, fns[a->size]); \
2109 }
2110
2111 DO_VMAXV(VMAXV_S, vmaxvs)
2112 DO_VMAXV(VMAXV_U, vmaxvu)
2113 DO_VMAXV(VMAXAV, vmaxav)
2114 DO_VMAXV(VMINV_S, vminvs)
2115 DO_VMAXV(VMINV_U, vminvu)
2116 DO_VMAXV(VMINAV, vminav)
2117
2118 #define DO_VMAXV_FP(INSN, FN) \
2119 static bool trans_##INSN(DisasContext *s, arg_vmaxv *a) \
2120 { \
2121 static MVEGenVADDVFn * const fns[] = { \
2122 NULL, \
2123 gen_helper_mve_##FN##h, \
2124 gen_helper_mve_##FN##s, \
2125 NULL, \
2126 }; \
2127 if (!dc_isar_feature(aa32_mve_fp, s)) { \
2128 return false; \
2129 } \
2130 return do_vmaxv(s, a, fns[a->size]); \
2131 }
2132
2133 DO_VMAXV_FP(VMAXNMV, vmaxnmv)
2134 DO_VMAXV_FP(VMINNMV, vminnmv)
2135 DO_VMAXV_FP(VMAXNMAV, vmaxnmav)
2136 DO_VMAXV_FP(VMINNMAV, vminnmav)
2137
2138 static bool do_vabav(DisasContext *s, arg_vabav *a, MVEGenVABAVFn *fn)
2139 {
2140 /* Absolute difference accumulated across vector */
2141 TCGv_ptr qn, qm;
2142 TCGv_i32 rda;
2143
2144 if (!dc_isar_feature(aa32_mve, s) ||
2145 !mve_check_qreg_bank(s, a->qm | a->qn) ||
2146 !fn || a->rda == 13 || a->rda == 15) {
2147 /* Rda cases are UNPREDICTABLE */
2148 return false;
2149 }
2150 if (!mve_eci_check(s) || !vfp_access_check(s)) {
2151 return true;
2152 }
2153
2154 qm = mve_qreg_ptr(a->qm);
2155 qn = mve_qreg_ptr(a->qn);
2156 rda = load_reg(s, a->rda);
2157 fn(rda, tcg_env, qn, qm, rda);
2158 store_reg(s, a->rda, rda);
2159 mve_update_eci(s);
2160 return true;
2161 }
2162
2163 #define DO_VABAV(INSN, FN) \
2164 static bool trans_##INSN(DisasContext *s, arg_vabav *a) \
2165 { \
2166 static MVEGenVABAVFn * const fns[] = { \
2167 gen_helper_mve_##FN##b, \
2168 gen_helper_mve_##FN##h, \
2169 gen_helper_mve_##FN##w, \
2170 NULL, \
2171 }; \
2172 return do_vabav(s, a, fns[a->size]); \
2173 }
2174
2175 DO_VABAV(VABAV_S, vabavs)
2176 DO_VABAV(VABAV_U, vabavu)
2177
2178 static bool trans_VMOV_to_2gp(DisasContext *s, arg_VMOV_to_2gp *a)
2179 {
2180 /*
2181 * VMOV two 32-bit vector lanes to two general-purpose registers.
2182 * This insn is not predicated but it is subject to beat-wise
2183 * execution if it is not in an IT block. For us this means
2184 * only that if PSR.ECI says we should not be executing the beat
2185 * corresponding to the lane of the vector register being accessed
2186 * then we should skip performing the move, and that we need to do
2187 * the usual check for bad ECI state and advance of ECI state.
2188 * (If PSR.ECI is non-zero then we cannot be in an IT block.)
2189 */
2190 TCGv_i32 tmp;
2191 int vd;
2192
2193 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd) ||
2194 a->rt == 13 || a->rt == 15 || a->rt2 == 13 || a->rt2 == 15 ||
2195 a->rt == a->rt2) {
2196 /* Rt/Rt2 cases are UNPREDICTABLE */
2197 return false;
2198 }
2199 if (!mve_eci_check(s) || !vfp_access_check(s)) {
2200 return true;
2201 }
2202
2203 /* Convert Qreg index to Dreg for read_neon_element32() etc */
2204 vd = a->qd * 2;
2205
2206 if (!mve_skip_vmov(s, vd, a->idx, MO_32)) {
2207 tmp = tcg_temp_new_i32();
2208 read_neon_element32(tmp, vd, a->idx, MO_32);
2209 store_reg(s, a->rt, tmp);
2210 }
2211 if (!mve_skip_vmov(s, vd + 1, a->idx, MO_32)) {
2212 tmp = tcg_temp_new_i32();
2213 read_neon_element32(tmp, vd + 1, a->idx, MO_32);
2214 store_reg(s, a->rt2, tmp);
2215 }
2216
2217 mve_update_and_store_eci(s);
2218 return true;
2219 }
2220
2221 static bool trans_VMOV_from_2gp(DisasContext *s, arg_VMOV_to_2gp *a)
2222 {
2223 /*
2224 * VMOV two general-purpose registers to two 32-bit vector lanes.
2225 * This insn is not predicated but it is subject to beat-wise
2226 * execution if it is not in an IT block. For us this means
2227 * only that if PSR.ECI says we should not be executing the beat
2228 * corresponding to the lane of the vector register being accessed
2229 * then we should skip performing the move, and that we need to do
2230 * the usual check for bad ECI state and advance of ECI state.
2231 * (If PSR.ECI is non-zero then we cannot be in an IT block.)
2232 */
2233 TCGv_i32 tmp;
2234 int vd;
2235
2236 if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd) ||
2237 a->rt == 13 || a->rt == 15 || a->rt2 == 13 || a->rt2 == 15) {
2238 /* Rt/Rt2 cases are UNPREDICTABLE */
2239 return false;
2240 }
2241 if (!mve_eci_check(s) || !vfp_access_check(s)) {
2242 return true;
2243 }
2244
2245 /* Convert Qreg idx to Dreg for read_neon_element32() etc */
2246 vd = a->qd * 2;
2247
2248 if (!mve_skip_vmov(s, vd, a->idx, MO_32)) {
2249 tmp = load_reg(s, a->rt);
2250 write_neon_element32(tmp, vd, a->idx, MO_32);
2251 }
2252 if (!mve_skip_vmov(s, vd + 1, a->idx, MO_32)) {
2253 tmp = load_reg(s, a->rt2);
2254 write_neon_element32(tmp, vd + 1, a->idx, MO_32);
2255 }
2256
2257 mve_update_and_store_eci(s);
2258 return true;
2259 }