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    addr = get_address(ctx, a->rs1, a->imm);
42    tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ);
43
44    mark_fs_dirty(ctx);
45    return true;
46}
47
48static bool trans_fsd(DisasContext *ctx, arg_fsd *a)
49{
50    TCGv addr;
51
52    REQUIRE_FPU;
53    REQUIRE_EXT(ctx, RVD);
54
55    addr = get_address(ctx, a->rs1, a->imm);
56    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ);
57    return true;
58}
59
60static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a)
61{
62    REQUIRE_FPU;
63    REQUIRE_ZDINX_OR_D(ctx);
64    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
65
66    TCGv_i64 dest = dest_fpr(ctx, a->rd);
67    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
68    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
69    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
70
71    gen_set_rm(ctx, a->rm);
72    gen_helper_fmadd_d(dest, cpu_env, src1, src2, src3);
73    gen_set_fpr_d(ctx, a->rd, dest);
74    mark_fs_dirty(ctx);
75    return true;
76}
77
78static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a)
79{
80    REQUIRE_FPU;
81    REQUIRE_ZDINX_OR_D(ctx);
82    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
83
84    TCGv_i64 dest = dest_fpr(ctx, a->rd);
85    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
86    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
87    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
88
89    gen_set_rm(ctx, a->rm);
90    gen_helper_fmsub_d(dest, cpu_env, src1, src2, src3);
91    gen_set_fpr_d(ctx, a->rd, dest);
92    mark_fs_dirty(ctx);
93    return true;
94}
95
96static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a)
97{
98    REQUIRE_FPU;
99    REQUIRE_ZDINX_OR_D(ctx);
100    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
101
102    TCGv_i64 dest = dest_fpr(ctx, a->rd);
103    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
104    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
105    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
106
107    gen_set_rm(ctx, a->rm);
108    gen_helper_fnmsub_d(dest, cpu_env, src1, src2, src3);
109    gen_set_fpr_d(ctx, a->rd, dest);
110    mark_fs_dirty(ctx);
111    return true;
112}
113
114static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a)
115{
116    REQUIRE_FPU;
117    REQUIRE_ZDINX_OR_D(ctx);
118    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2 | a->rs3);
119
120    TCGv_i64 dest = dest_fpr(ctx, a->rd);
121    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
122    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
123    TCGv_i64 src3 = get_fpr_d(ctx, a->rs3);
124
125    gen_set_rm(ctx, a->rm);
126    gen_helper_fnmadd_d(dest, cpu_env, src1, src2, src3);
127    gen_set_fpr_d(ctx, a->rd, dest);
128    mark_fs_dirty(ctx);
129    return true;
130}
131
132static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a)
133{
134    REQUIRE_FPU;
135    REQUIRE_ZDINX_OR_D(ctx);
136    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
137
138    TCGv_i64 dest = dest_fpr(ctx, a->rd);
139    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
140    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
141
142    gen_set_rm(ctx, a->rm);
143    gen_helper_fadd_d(dest, cpu_env, src1, src2);
144    gen_set_fpr_d(ctx, a->rd, dest);
145    mark_fs_dirty(ctx);
146    return true;
147}
148
149static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a)
150{
151    REQUIRE_FPU;
152    REQUIRE_ZDINX_OR_D(ctx);
153    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
154
155    TCGv_i64 dest = dest_fpr(ctx, a->rd);
156    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
157    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
158
159    gen_set_rm(ctx, a->rm);
160    gen_helper_fsub_d(dest, cpu_env, src1, src2);
161    gen_set_fpr_d(ctx, a->rd, dest);
162    mark_fs_dirty(ctx);
163    return true;
164}
165
166static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a)
167{
168    REQUIRE_FPU;
169    REQUIRE_ZDINX_OR_D(ctx);
170    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
171
172    TCGv_i64 dest = dest_fpr(ctx, a->rd);
173    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
174    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
175
176    gen_set_rm(ctx, a->rm);
177    gen_helper_fmul_d(dest, cpu_env, src1, src2);
178    gen_set_fpr_d(ctx, a->rd, dest);
179    mark_fs_dirty(ctx);
180    return true;
181}
182
183static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a)
184{
185    REQUIRE_FPU;
186    REQUIRE_ZDINX_OR_D(ctx);
187    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
188
189    TCGv_i64 dest = dest_fpr(ctx, a->rd);
190    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
191    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
192
193    gen_set_rm(ctx, a->rm);
194    gen_helper_fdiv_d(dest, cpu_env, src1, src2);
195    gen_set_fpr_d(ctx, a->rd, dest);
196    mark_fs_dirty(ctx);
197    return true;
198}
199
200static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a)
201{
202    REQUIRE_FPU;
203    REQUIRE_ZDINX_OR_D(ctx);
204    REQUIRE_EVEN(ctx, a->rd | a->rs1);
205
206    TCGv_i64 dest = dest_fpr(ctx, a->rd);
207    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
208
209    gen_set_rm(ctx, a->rm);
210    gen_helper_fsqrt_d(dest, cpu_env, src1);
211    gen_set_fpr_d(ctx, a->rd, dest);
212    mark_fs_dirty(ctx);
213    return true;
214}
215
216static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a)
217{
218    REQUIRE_FPU;
219    REQUIRE_ZDINX_OR_D(ctx);
220    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
221
222    TCGv_i64 dest = dest_fpr(ctx, a->rd);
223    if (a->rs1 == a->rs2) { /* FMOV */
224        dest = get_fpr_d(ctx, a->rs1);
225    } else {
226        TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
227        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
228        tcg_gen_deposit_i64(dest, src2, src1, 0, 63);
229    }
230    gen_set_fpr_d(ctx, a->rd, dest);
231    mark_fs_dirty(ctx);
232    return true;
233}
234
235static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a)
236{
237    REQUIRE_FPU;
238    REQUIRE_ZDINX_OR_D(ctx);
239    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
240
241    TCGv_i64 dest = dest_fpr(ctx, a->rd);
242    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
243
244    if (a->rs1 == a->rs2) { /* FNEG */
245        tcg_gen_xori_i64(dest, src1, INT64_MIN);
246    } else {
247        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
248        TCGv_i64 t0 = tcg_temp_new_i64();
249        tcg_gen_not_i64(t0, src2);
250        tcg_gen_deposit_i64(dest, t0, src1, 0, 63);
251        tcg_temp_free_i64(t0);
252    }
253    gen_set_fpr_d(ctx, a->rd, dest);
254    mark_fs_dirty(ctx);
255    return true;
256}
257
258static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a)
259{
260    REQUIRE_FPU;
261    REQUIRE_ZDINX_OR_D(ctx);
262    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
263
264    TCGv_i64 dest = dest_fpr(ctx, a->rd);
265    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
266
267    if (a->rs1 == a->rs2) { /* FABS */
268        tcg_gen_andi_i64(dest, src1, ~INT64_MIN);
269    } else {
270        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
271        TCGv_i64 t0 = tcg_temp_new_i64();
272        tcg_gen_andi_i64(t0, src2, INT64_MIN);
273        tcg_gen_xor_i64(dest, src1, t0);
274        tcg_temp_free_i64(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