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