2 * ARM translation: M-profile MVE instructions
4 * Copyright (c) 2021 Linaro, Ltd.
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.
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.
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/>.
20 #include "qemu/osdep.h"
21 #include "tcg/tcg-op.h"
22 #include "tcg/tcg-op-gvec.h"
23 #include "exec/exec-all.h"
24 #include "exec/gen-icount.h"
25 #include "translate.h"
26 #include "translate-a32.h"
28 /* Include the generated decoder */
29 #include "decode-mve.c.inc"
31 typedef void MVEGenLdStFn(TCGv_ptr
, TCGv_ptr
, TCGv_i32
);
32 typedef void MVEGenOneOpFn(TCGv_ptr
, TCGv_ptr
, TCGv_ptr
);
33 typedef void MVEGenTwoOpFn(TCGv_ptr
, TCGv_ptr
, TCGv_ptr
, TCGv_ptr
);
35 /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */
36 static inline long mve_qreg_offset(unsigned reg
)
38 return offsetof(CPUARMState
, vfp
.zregs
[reg
].d
[0]);
41 static TCGv_ptr
mve_qreg_ptr(unsigned reg
)
43 TCGv_ptr ret
= tcg_temp_new_ptr();
44 tcg_gen_addi_ptr(ret
, cpu_env
, mve_qreg_offset(reg
));
48 static bool mve_check_qreg_bank(DisasContext
*s
, int qmask
)
51 * Check whether Qregs are in range. For v8.1M only Q0..Q7
52 * are supported, see VFPSmallRegisterBank().
57 static bool mve_eci_check(DisasContext
*s
)
60 * This is a beatwise insn: check that ECI is valid (not a
61 * reserved value) and note that we are handling it.
62 * Return true if OK, false if we generated an exception.
64 s
->eci_handled
= true;
73 /* Reserved value: INVSTATE UsageFault */
74 gen_exception_insn(s
, s
->pc_curr
, EXCP_INVSTATE
, syn_uncategorized(),
75 default_exception_el(s
));
80 static void mve_update_eci(DisasContext
*s
)
83 * The helper function will always update the CPUState field,
84 * so we only need to update the DisasContext field.
87 s
->eci
= (s
->eci
== ECI_A0A1A2B0
) ? ECI_A0
: ECI_NONE
;
91 static bool do_ldst(DisasContext
*s
, arg_VLDR_VSTR
*a
, MVEGenLdStFn
*fn
)
97 if (!dc_isar_feature(aa32_mve
, s
) ||
98 !mve_check_qreg_bank(s
, a
->qd
) ||
103 /* CONSTRAINED UNPREDICTABLE: we choose to UNDEF */
104 if (a
->rn
== 15 || (a
->rn
== 13 && a
->w
)) {
108 if (!mve_eci_check(s
) || !vfp_access_check(s
)) {
112 offset
= a
->imm
<< a
->size
;
116 addr
= load_reg(s
, a
->rn
);
118 tcg_gen_addi_i32(addr
, addr
, offset
);
121 qreg
= mve_qreg_ptr(a
->qd
);
122 fn(cpu_env
, qreg
, addr
);
123 tcg_temp_free_ptr(qreg
);
126 * Writeback always happens after the last beat of the insn,
127 * regardless of predication
131 tcg_gen_addi_i32(addr
, addr
, offset
);
133 store_reg(s
, a
->rn
, addr
);
135 tcg_temp_free_i32(addr
);
141 static bool trans_VLDR_VSTR(DisasContext
*s
, arg_VLDR_VSTR
*a
)
143 static MVEGenLdStFn
* const ldstfns
[4][2] = {
144 { gen_helper_mve_vstrb
, gen_helper_mve_vldrb
},
145 { gen_helper_mve_vstrh
, gen_helper_mve_vldrh
},
146 { gen_helper_mve_vstrw
, gen_helper_mve_vldrw
},
149 return do_ldst(s
, a
, ldstfns
[a
->size
][a
->l
]);
152 #define DO_VLDST_WIDE_NARROW(OP, SLD, ULD, ST) \
153 static bool trans_##OP(DisasContext *s, arg_VLDR_VSTR *a) \
155 static MVEGenLdStFn * const ldstfns[2][2] = { \
156 { gen_helper_mve_##ST, gen_helper_mve_##SLD }, \
157 { NULL, gen_helper_mve_##ULD }, \
159 return do_ldst(s, a, ldstfns[a->u][a->l]); \
162 DO_VLDST_WIDE_NARROW(VLDSTB_H
, vldrb_sh
, vldrb_uh
, vstrb_h
)
163 DO_VLDST_WIDE_NARROW(VLDSTB_W
, vldrb_sw
, vldrb_uw
, vstrb_w
)
164 DO_VLDST_WIDE_NARROW(VLDSTH_W
, vldrh_sw
, vldrh_uw
, vstrh_w
)
166 static bool trans_VDUP(DisasContext
*s
, arg_VDUP
*a
)
171 if (!dc_isar_feature(aa32_mve
, s
) ||
172 !mve_check_qreg_bank(s
, a
->qd
)) {
175 if (a
->rt
== 13 || a
->rt
== 15) {
176 /* UNPREDICTABLE; we choose to UNDEF */
179 if (!mve_eci_check(s
) || !vfp_access_check(s
)) {
183 qd
= mve_qreg_ptr(a
->qd
);
184 rt
= load_reg(s
, a
->rt
);
185 tcg_gen_dup_i32(a
->size
, rt
, rt
);
186 gen_helper_mve_vdup(cpu_env
, qd
, rt
);
187 tcg_temp_free_ptr(qd
);
188 tcg_temp_free_i32(rt
);
193 static bool do_1op(DisasContext
*s
, arg_1op
*a
, MVEGenOneOpFn fn
)
197 if (!dc_isar_feature(aa32_mve
, s
) ||
198 !mve_check_qreg_bank(s
, a
->qd
| a
->qm
) ||
203 if (!mve_eci_check(s
) || !vfp_access_check(s
)) {
207 qd
= mve_qreg_ptr(a
->qd
);
208 qm
= mve_qreg_ptr(a
->qm
);
210 tcg_temp_free_ptr(qd
);
211 tcg_temp_free_ptr(qm
);
216 #define DO_1OP(INSN, FN) \
217 static bool trans_##INSN(DisasContext *s, arg_1op *a) \
219 static MVEGenOneOpFn * const fns[] = { \
220 gen_helper_mve_##FN##b, \
221 gen_helper_mve_##FN##h, \
222 gen_helper_mve_##FN##w, \
225 return do_1op(s, a, fns[a->size]); \
233 static bool trans_VREV16(DisasContext
*s
, arg_1op
*a
)
235 static MVEGenOneOpFn
* const fns
[] = {
236 gen_helper_mve_vrev16b
,
241 return do_1op(s
, a
, fns
[a
->size
]);
244 static bool trans_VREV32(DisasContext
*s
, arg_1op
*a
)
246 static MVEGenOneOpFn
* const fns
[] = {
247 gen_helper_mve_vrev32b
,
248 gen_helper_mve_vrev32h
,
252 return do_1op(s
, a
, fns
[a
->size
]);
255 static bool trans_VREV64(DisasContext
*s
, arg_1op
*a
)
257 static MVEGenOneOpFn
* const fns
[] = {
258 gen_helper_mve_vrev64b
,
259 gen_helper_mve_vrev64h
,
260 gen_helper_mve_vrev64w
,
263 return do_1op(s
, a
, fns
[a
->size
]);
266 static bool trans_VMVN(DisasContext
*s
, arg_1op
*a
)
268 return do_1op(s
, a
, gen_helper_mve_vmvn
);
271 static bool trans_VABS_fp(DisasContext
*s
, arg_1op
*a
)
273 static MVEGenOneOpFn
* const fns
[] = {
275 gen_helper_mve_vfabsh
,
276 gen_helper_mve_vfabss
,
279 if (!dc_isar_feature(aa32_mve_fp
, s
)) {
282 return do_1op(s
, a
, fns
[a
->size
]);
285 static bool trans_VNEG_fp(DisasContext
*s
, arg_1op
*a
)
287 static MVEGenOneOpFn
* const fns
[] = {
289 gen_helper_mve_vfnegh
,
290 gen_helper_mve_vfnegs
,
293 if (!dc_isar_feature(aa32_mve_fp
, s
)) {
296 return do_1op(s
, a
, fns
[a
->size
]);
299 static bool do_2op(DisasContext
*s
, arg_2op
*a
, MVEGenTwoOpFn fn
)
303 if (!dc_isar_feature(aa32_mve
, s
) ||
304 !mve_check_qreg_bank(s
, a
->qd
| a
->qn
| a
->qm
) ||
308 if (!mve_eci_check(s
) || !vfp_access_check(s
)) {
312 qd
= mve_qreg_ptr(a
->qd
);
313 qn
= mve_qreg_ptr(a
->qn
);
314 qm
= mve_qreg_ptr(a
->qm
);
315 fn(cpu_env
, qd
, qn
, qm
);
316 tcg_temp_free_ptr(qd
);
317 tcg_temp_free_ptr(qn
);
318 tcg_temp_free_ptr(qm
);
323 #define DO_LOGIC(INSN, HELPER) \
324 static bool trans_##INSN(DisasContext *s, arg_2op *a) \
326 return do_2op(s, a, HELPER); \
329 DO_LOGIC(VAND
, gen_helper_mve_vand
)
330 DO_LOGIC(VBIC
, gen_helper_mve_vbic
)
331 DO_LOGIC(VORR
, gen_helper_mve_vorr
)
332 DO_LOGIC(VORN
, gen_helper_mve_vorn
)
333 DO_LOGIC(VEOR
, gen_helper_mve_veor
)
335 #define DO_2OP(INSN, FN) \
336 static bool trans_##INSN(DisasContext *s, arg_2op *a) \
338 static MVEGenTwoOpFn * const fns[] = { \
339 gen_helper_mve_##FN##b, \
340 gen_helper_mve_##FN##h, \
341 gen_helper_mve_##FN##w, \
344 return do_2op(s, a, fns[a->size]); \
350 DO_2OP(VMULH_S
, vmulhs
)
351 DO_2OP(VMULH_U
, vmulhu
)
352 DO_2OP(VRMULH_S
, vrmulhs
)
353 DO_2OP(VRMULH_U
, vrmulhu
)
354 DO_2OP(VMAX_S
, vmaxs
)
355 DO_2OP(VMAX_U
, vmaxu
)
356 DO_2OP(VMIN_S
, vmins
)
357 DO_2OP(VMIN_U
, vminu
)
358 DO_2OP(VABD_S
, vabds
)
359 DO_2OP(VABD_U
, vabdu
)