1/*
2 * RISC-V translation routines for the RV64D Standard Extension.
3 *
4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
5 * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de
6 *                    Bastian Koppelmann, kbastian@mail.uni-paderborn.de
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2 or later, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#define REQUIRE_ZDINX_OR_D(ctx) do { \
22    if (!ctx->cfg_ptr->ext_zdinx) { \
23        REQUIRE_EXT(ctx, RVD); \
24    } \
25} while (0)
26
27#define REQUIRE_EVEN(ctx, reg) do { \
28    if (ctx->cfg_ptr->ext_zdinx && (get_xl(ctx) == MXL_RV32) && \
29        ((reg) & 0x1)) { \
30        return false; \
31    } \
32} while (0)
33
34static bool trans_fld(DisasContext *ctx, arg_fld *a)
35{
36    TCGv addr;
37
38    REQUIRE_FPU;
39    REQUIRE_EXT(ctx, RVD);
40
41    decode_save_opc(ctx);
42    addr = get_address(ctx, a->rs1, a->imm);
43    tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ);
44
45    mark_fs_dirty(ctx);
46    return true;
47}
48
49static bool trans_fsd(DisasContext *ctx, arg_fsd *a)
50{
51    TCGv addr;
52
53    REQUIRE_FPU;
54    REQUIRE_EXT(ctx, RVD);
55
56    decode_save_opc(ctx);
57    addr = get_address(ctx, a->rs1, a->imm);
58    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ);
59    return true;
60}
61
62static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a)
63{
64    REQUIRE_FPU;
65    REQUIRE_ZDINX_OR_D(ctx);
66    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
67
68    TCGv_i64 dest = dest_fpr(ctx, a->rd);
69    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
70    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
71    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
72
73    gen_set_rm(ctx, a->rm);
74    gen_helper_fmadd_d(dest, cpu_env, src1, src2, src3);
75    gen_set_fpr_d(ctx, a->rd, dest);
76    mark_fs_dirty(ctx);
77    return true;
78}
79
80static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a)
81{
82    REQUIRE_FPU;
83    REQUIRE_ZDINX_OR_D(ctx);
84    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
85
86    TCGv_i64 dest = dest_fpr(ctx, a->rd);
87    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
88    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
89    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
90
91    gen_set_rm(ctx, a->rm);
92    gen_helper_fmsub_d(dest, cpu_env, src1, src2, src3);
93    gen_set_fpr_d(ctx, a->rd, dest);
94    mark_fs_dirty(ctx);
95    return true;
96}
97
98static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a)
99{
100    REQUIRE_FPU;
101    REQUIRE_ZDINX_OR_D(ctx);
102    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
103
104    TCGv_i64 dest = dest_fpr(ctx, a->rd);
105    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
106    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
107    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
108
109    gen_set_rm(ctx, a->rm);
110    gen_helper_fnmsub_d(dest, cpu_env, src1, src2, src3);
111    gen_set_fpr_d(ctx, a->rd, dest);
112    mark_fs_dirty(ctx);
113    return true;
114}
115
116static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a)
117{
118    REQUIRE_FPU;
119    REQUIRE_ZDINX_OR_D(ctx);
120    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
121
122    TCGv_i64 dest = dest_fpr(ctx, a->rd);
123    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
124    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
125    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
126
127    gen_set_rm(ctx, a->rm);
128    gen_helper_fnmadd_d(dest, cpu_env, src1, src2, src3);
129    gen_set_fpr_d(ctx, a->rd, dest);
130    mark_fs_dirty(ctx);
131    return true;
132}
133
134static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a)
135{
136    REQUIRE_FPU;
137    REQUIRE_ZDINX_OR_D(ctx);
138    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
139
140    TCGv_i64 dest = dest_fpr(ctx, a->rd);
141    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
142    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
143
144    gen_set_rm(ctx, a->rm);
145    gen_helper_fadd_d(dest, cpu_env, src1, src2);
146    gen_set_fpr_d(ctx, a->rd, dest);
147    mark_fs_dirty(ctx);
148    return true;
149}
150
151static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a)
152{
153    REQUIRE_FPU;
154    REQUIRE_ZDINX_OR_D(ctx);
155    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
156
157    TCGv_i64 dest = dest_fpr(ctx, a->rd);
158    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
159    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
160
161    gen_set_rm(ctx, a->rm);
162    gen_helper_fsub_d(dest, cpu_env, src1, src2);
163    gen_set_fpr_d(ctx, a->rd, dest);
164    mark_fs_dirty(ctx);
165    return true;
166}
167
168static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a)
169{
170    REQUIRE_FPU;
171    REQUIRE_ZDINX_OR_D(ctx);
172    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
173
174    TCGv_i64 dest = dest_fpr(ctx, a->rd);
175    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
176    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
177
178    gen_set_rm(ctx, a->rm);
179    gen_helper_fmul_d(dest, cpu_env, src1, src2);
180    gen_set_fpr_d(ctx, a->rd, dest);
181    mark_fs_dirty(ctx);
182    return true;
183}
184
185static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a)
186{
187    REQUIRE_FPU;
188    REQUIRE_ZDINX_OR_D(ctx);
189    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
190
191    TCGv_i64 dest = dest_fpr(ctx, a->rd);
192    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
193    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
194
195    gen_set_rm(ctx, a->rm);
196    gen_helper_fdiv_d(dest, cpu_env, src1, src2);
197    gen_set_fpr_d(ctx, a->rd, dest);
198    mark_fs_dirty(ctx);
199    return true;
200}
201
202static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a)
203{
204    REQUIRE_FPU;
205    REQUIRE_ZDINX_OR_D(ctx);
206    REQUIRE_EVEN(ctx, a->rd | a->rs1);
207
208    TCGv_i64 dest = dest_fpr(ctx, a->rd);
209    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
210
211    gen_set_rm(ctx, a->rm);
212    gen_helper_fsqrt_d(dest, cpu_env, src1);
213    gen_set_fpr_d(ctx, a->rd, dest);
214    mark_fs_dirty(ctx);
215    return true;
216}
217
218static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a)
219{
220    REQUIRE_FPU;
221    REQUIRE_ZDINX_OR_D(ctx);
222    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
223
224    TCGv_i64 dest = dest_fpr(ctx, a->rd);
225    if (a->rs1 == a->rs2) { /* FMOV */
226        dest = get_fpr_d(ctx, a->rs1);
227    } else {
228        TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
229        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
230        tcg_gen_deposit_i64(dest, src2, src1, 0, 63);
231    }
232    gen_set_fpr_d(ctx, a->rd, dest);
233    mark_fs_dirty(ctx);
234    return true;
235}
236
237static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a)
238{
239    REQUIRE_FPU;
240    REQUIRE_ZDINX_OR_D(ctx);
241    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
242
243    TCGv_i64 dest = dest_fpr(ctx, a->rd);
244    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
245
246    if (a->rs1 == a->rs2) { /* FNEG */
247        tcg_gen_xori_i64(dest, src1, INT64_MIN);
248    } else {
249        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
250        TCGv_i64 t0 = tcg_temp_new_i64();
251        tcg_gen_not_i64(t0, src2);
252        tcg_gen_deposit_i64(dest, t0, src1, 0, 63);
253    }
254    gen_set_fpr_d(ctx, a->rd, dest);
255    mark_fs_dirty(ctx);
256    return true;
257}
258
259static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a)
260{
261    REQUIRE_FPU;
262    REQUIRE_ZDINX_OR_D(ctx);
263    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
264
265    TCGv_i64 dest = dest_fpr(ctx, a->rd);
266    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
267
268    if (a->rs1 == a->rs2) { /* FABS */
269        tcg_gen_andi_i64(dest, src1, ~INT64_MIN);
270    } else {
271        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
272        TCGv_i64 t0 = tcg_temp_new_i64();
273        tcg_gen_andi_i64(t0, src2, INT64_MIN);
274        tcg_gen_xor_i64(dest, src1, t0);
275    }
276    gen_set_fpr_d(ctx, a->rd, dest);
277    mark_fs_dirty(ctx);
278    return true;
279}
280
281static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a)
282{
283    REQUIRE_FPU;
284    REQUIRE_ZDINX_OR_D(ctx);
285    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
286
287    TCGv_i64 dest = dest_fpr(ctx, a->rd);
288    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
289    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
290
291    gen_helper_fmin_d(dest, cpu_env, src1, src2);
292    gen_set_fpr_d(ctx, a->rd, dest);
293    mark_fs_dirty(ctx);
294    return true;
295}
296
297static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a)
298{
299    REQUIRE_FPU;
300    REQUIRE_ZDINX_OR_D(ctx);
301    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
302
303    TCGv_i64 dest = dest_fpr(ctx, a->rd);
304    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
305    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
306
307    gen_helper_fmax_d(dest, cpu_env, src1, src2);
308    gen_set_fpr_d(ctx, a->rd, dest);
309    mark_fs_dirty(ctx);
310    return true;
311}
312
313static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a)
314{
315    REQUIRE_FPU;
316    REQUIRE_ZDINX_OR_D(ctx);
317    REQUIRE_EVEN(ctx, a->rs1);
318
319    TCGv_i64 dest = dest_fpr(ctx, a->rd);
320    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
321
322    gen_set_rm(ctx, a->rm);
323    gen_helper_fcvt_s_d(dest, cpu_env, src1);
324    gen_set_fpr_hs(ctx, a->rd, dest);
325    mark_fs_dirty(ctx);
326    return true;
327}
328
329static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a)
330{
331    REQUIRE_FPU;
332    REQUIRE_ZDINX_OR_D(ctx);
333    REQUIRE_EVEN(ctx, a->rd);
334
335    TCGv_i64 dest = dest_fpr(ctx, a->rd);
336    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
337
338    gen_set_rm(ctx, a->rm);
339    gen_helper_fcvt_d_s(dest, cpu_env, src1);
340    gen_set_fpr_d(ctx, a->rd, dest);
341    mark_fs_dirty(ctx);
342    return true;
343}
344
345static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a)
346{
347    REQUIRE_FPU;
348    REQUIRE_ZDINX_OR_D(ctx);
349    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
350
351    TCGv dest = dest_gpr(ctx, a->rd);
352    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
353    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
354
355    gen_helper_feq_d(dest, cpu_env, src1, src2);
356    gen_set_gpr(ctx, a->rd, dest);
357    return true;
358}
359
360static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a)
361{
362    REQUIRE_FPU;
363    REQUIRE_ZDINX_OR_D(ctx);
364    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
365
366    TCGv dest = dest_gpr(ctx, a->rd);
367    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
368    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
369
370    gen_helper_flt_d(dest, cpu_env, src1, src2);
371    gen_set_gpr(ctx, a->rd, dest);
372    return true;
373}
374
375static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a)
376{
377    REQUIRE_FPU;
378    REQUIRE_ZDINX_OR_D(ctx);
379    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
380
381    TCGv dest = dest_gpr(ctx, a->rd);
382    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
383    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
384
385    gen_helper_fle_d(dest, cpu_env, src1, src2);
386    gen_set_gpr(ctx, a->rd, dest);
387    return true;
388}
389
390static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a)
391{
392    REQUIRE_FPU;
393    REQUIRE_ZDINX_OR_D(ctx);
394    REQUIRE_EVEN(ctx, a->rs1);
395
396    TCGv dest = dest_gpr(ctx, a->rd);
397    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
398
399    gen_helper_fclass_d(dest, src1);
400    gen_set_gpr(ctx, a->rd, dest);
401    return true;
402}
403
404static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a)
405{
406    REQUIRE_FPU;
407    REQUIRE_ZDINX_OR_D(ctx);
408    REQUIRE_EVEN(ctx, a->rs1);
409
410    TCGv dest = dest_gpr(ctx, a->rd);
411    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
412
413    gen_set_rm(ctx, a->rm);
414    gen_helper_fcvt_w_d(dest, cpu_env, src1);
415    gen_set_gpr(ctx, a->rd, dest);
416    return true;
417}
418
419static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a)
420{
421    REQUIRE_FPU;
422    REQUIRE_ZDINX_OR_D(ctx);
423    REQUIRE_EVEN(ctx, a->rs1);
424
425    TCGv dest = dest_gpr(ctx, a->rd);
426    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
427
428    gen_set_rm(ctx, a->rm);
429    gen_helper_fcvt_wu_d(dest, cpu_env, src1);
430    gen_set_gpr(ctx, a->rd, dest);
431    return true;
432}
433
434static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a)
435{
436    REQUIRE_FPU;
437    REQUIRE_ZDINX_OR_D(ctx);
438    REQUIRE_EVEN(ctx, a->rd);
439
440    TCGv_i64 dest = dest_fpr(ctx, a->rd);
441    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
442
443    gen_set_rm(ctx, a->rm);
444    gen_helper_fcvt_d_w(dest, cpu_env, src);
445    gen_set_fpr_d(ctx, a->rd, dest);
446
447    mark_fs_dirty(ctx);
448    return true;
449}
450
451static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a)
452{
453    REQUIRE_FPU;
454    REQUIRE_ZDINX_OR_D(ctx);
455    REQUIRE_EVEN(ctx, a->rd);
456
457    TCGv_i64 dest = dest_fpr(ctx, a->rd);
458    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
459
460    gen_set_rm(ctx, a->rm);
461    gen_helper_fcvt_d_wu(dest, cpu_env, src);
462    gen_set_fpr_d(ctx, a->rd, dest);
463
464    mark_fs_dirty(ctx);
465    return true;
466}
467
468static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a)
469{
470    REQUIRE_64BIT(ctx);
471    REQUIRE_FPU;
472    REQUIRE_ZDINX_OR_D(ctx);
473    REQUIRE_EVEN(ctx, a->rs1);
474
475    TCGv dest = dest_gpr(ctx, a->rd);
476    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
477
478    gen_set_rm(ctx, a->rm);
479    gen_helper_fcvt_l_d(dest, cpu_env, src1);
480    gen_set_gpr(ctx, a->rd, dest);
481    return true;
482}
483
484static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a)
485{
486    REQUIRE_64BIT(ctx);
487    REQUIRE_FPU;
488    REQUIRE_ZDINX_OR_D(ctx);
489    REQUIRE_EVEN(ctx, a->rs1);
490
491    TCGv dest = dest_gpr(ctx, a->rd);
492    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
493
494    gen_set_rm(ctx, a->rm);
495    gen_helper_fcvt_lu_d(dest, cpu_env, src1);
496    gen_set_gpr(ctx, a->rd, dest);
497    return true;
498}
499
500static bool trans_fmv_x_d(DisasContext *ctx, arg_fmv_x_d *a)
501{
502    REQUIRE_64BIT(ctx);
503    REQUIRE_FPU;
504    REQUIRE_EXT(ctx, RVD);
505
506#ifdef TARGET_RISCV64
507    gen_set_gpr(ctx, a->rd, cpu_fpr[a->rs1]);
508    return true;
509#else
510    qemu_build_not_reached();
511#endif
512}
513
514static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a)
515{
516    REQUIRE_64BIT(ctx);
517    REQUIRE_FPU;
518    REQUIRE_ZDINX_OR_D(ctx);
519    REQUIRE_EVEN(ctx, a->rd);
520
521    TCGv_i64 dest = dest_fpr(ctx, a->rd);
522    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
523
524    gen_set_rm(ctx, a->rm);
525    gen_helper_fcvt_d_l(dest, cpu_env, src);
526    gen_set_fpr_d(ctx, a->rd, dest);
527
528    mark_fs_dirty(ctx);
529    return true;
530}
531
532static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a)
533{
534    REQUIRE_64BIT(ctx);
535    REQUIRE_FPU;
536    REQUIRE_ZDINX_OR_D(ctx);
537    REQUIRE_EVEN(ctx, a->rd);
538
539    TCGv_i64 dest = dest_fpr(ctx, a->rd);
540    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
541
542    gen_set_rm(ctx, a->rm);
543    gen_helper_fcvt_d_lu(dest, cpu_env, src);
544    gen_set_fpr_d(ctx, a->rd, dest);
545
546    mark_fs_dirty(ctx);
547    return true;
548}
549
550static bool trans_fmv_d_x(DisasContext *ctx, arg_fmv_d_x *a)
551{
552    REQUIRE_64BIT(ctx);
553    REQUIRE_FPU;
554    REQUIRE_EXT(ctx, RVD);
555
556#ifdef TARGET_RISCV64
557    tcg_gen_mov_tl(cpu_fpr[a->rd], get_gpr(ctx, a->rs1, EXT_NONE));
558    mark_fs_dirty(ctx);
559    return true;
560#else
561    qemu_build_not_reached();
562#endif
563}
564