target/arm: Implement MVE VABD
[qemu.git] / target / arm / translate-mve.c
blob041fd1ef14fb41a689c55f4e9a515c10ba14387a
1 /*
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));
45 return ret;
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().
54 return qmask < 8;
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;
65 switch (s->eci) {
66 case ECI_NONE:
67 case ECI_A0:
68 case ECI_A0A1:
69 case ECI_A0A1A2:
70 case ECI_A0A1A2B0:
71 return true;
72 default:
73 /* Reserved value: INVSTATE UsageFault */
74 gen_exception_insn(s, s->pc_curr, EXCP_INVSTATE, syn_uncategorized(),
75 default_exception_el(s));
76 return false;
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.
86 if (s->eci) {
87 s->eci = (s->eci == ECI_A0A1A2B0) ? ECI_A0 : ECI_NONE;
91 static bool do_ldst(DisasContext *s, arg_VLDR_VSTR *a, MVEGenLdStFn *fn)
93 TCGv_i32 addr;
94 uint32_t offset;
95 TCGv_ptr qreg;
97 if (!dc_isar_feature(aa32_mve, s) ||
98 !mve_check_qreg_bank(s, a->qd) ||
99 !fn) {
100 return false;
103 /* CONSTRAINED UNPREDICTABLE: we choose to UNDEF */
104 if (a->rn == 15 || (a->rn == 13 && a->w)) {
105 return false;
108 if (!mve_eci_check(s) || !vfp_access_check(s)) {
109 return true;
112 offset = a->imm << a->size;
113 if (!a->a) {
114 offset = -offset;
116 addr = load_reg(s, a->rn);
117 if (a->p) {
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
129 if (a->w) {
130 if (!a->p) {
131 tcg_gen_addi_i32(addr, addr, offset);
133 store_reg(s, a->rn, addr);
134 } else {
135 tcg_temp_free_i32(addr);
137 mve_update_eci(s);
138 return true;
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 },
147 { NULL, NULL }
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 }, \
158 }; \
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)
168 TCGv_ptr qd;
169 TCGv_i32 rt;
171 if (!dc_isar_feature(aa32_mve, s) ||
172 !mve_check_qreg_bank(s, a->qd)) {
173 return false;
175 if (a->rt == 13 || a->rt == 15) {
176 /* UNPREDICTABLE; we choose to UNDEF */
177 return false;
179 if (!mve_eci_check(s) || !vfp_access_check(s)) {
180 return true;
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);
189 mve_update_eci(s);
190 return true;
193 static bool do_1op(DisasContext *s, arg_1op *a, MVEGenOneOpFn fn)
195 TCGv_ptr qd, qm;
197 if (!dc_isar_feature(aa32_mve, s) ||
198 !mve_check_qreg_bank(s, a->qd | a->qm) ||
199 !fn) {
200 return false;
203 if (!mve_eci_check(s) || !vfp_access_check(s)) {
204 return true;
207 qd = mve_qreg_ptr(a->qd);
208 qm = mve_qreg_ptr(a->qm);
209 fn(cpu_env, qd, qm);
210 tcg_temp_free_ptr(qd);
211 tcg_temp_free_ptr(qm);
212 mve_update_eci(s);
213 return true;
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, \
223 NULL, \
224 }; \
225 return do_1op(s, a, fns[a->size]); \
228 DO_1OP(VCLZ, vclz)
229 DO_1OP(VCLS, vcls)
230 DO_1OP(VABS, vabs)
231 DO_1OP(VNEG, vneg)
233 static bool trans_VREV16(DisasContext *s, arg_1op *a)
235 static MVEGenOneOpFn * const fns[] = {
236 gen_helper_mve_vrev16b,
237 NULL,
238 NULL,
239 NULL,
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,
249 NULL,
250 NULL,
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,
261 NULL,
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[] = {
274 NULL,
275 gen_helper_mve_vfabsh,
276 gen_helper_mve_vfabss,
277 NULL,
279 if (!dc_isar_feature(aa32_mve_fp, s)) {
280 return false;
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[] = {
288 NULL,
289 gen_helper_mve_vfnegh,
290 gen_helper_mve_vfnegs,
291 NULL,
293 if (!dc_isar_feature(aa32_mve_fp, s)) {
294 return false;
296 return do_1op(s, a, fns[a->size]);
299 static bool do_2op(DisasContext *s, arg_2op *a, MVEGenTwoOpFn fn)
301 TCGv_ptr qd, qn, qm;
303 if (!dc_isar_feature(aa32_mve, s) ||
304 !mve_check_qreg_bank(s, a->qd | a->qn | a->qm) ||
305 !fn) {
306 return false;
308 if (!mve_eci_check(s) || !vfp_access_check(s)) {
309 return true;
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);
319 mve_update_eci(s);
320 return true;
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, \
342 NULL, \
343 }; \
344 return do_2op(s, a, fns[a->size]); \
347 DO_2OP(VADD, vadd)
348 DO_2OP(VSUB, vsub)
349 DO_2OP(VMUL, vmul)
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)