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        tcg_temp_free_i64(t0);
254    }
255    gen_set_fpr_d(ctx, a->rd, dest);
256    mark_fs_dirty(ctx);
257    return true;
258}
259
260static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a)
261{
262    REQUIRE_FPU;
263    REQUIRE_ZDINX_OR_D(ctx);
264    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
265
266    TCGv_i64 dest = dest_fpr(ctx, a->rd);
267    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
268
269    if (a->rs1 == a->rs2) { /* FABS */
270        tcg_gen_andi_i64(dest, src1, ~INT64_MIN);
271    } else {
272        TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
273        TCGv_i64 t0 = tcg_temp_new_i64();
274        tcg_gen_andi_i64(t0, src2, INT64_MIN);
275        tcg_gen_xor_i64(dest, src1, t0);
276        tcg_temp_free_i64(t0);
277    }
278    gen_set_fpr_d(ctx, a->rd, dest);
279    mark_fs_dirty(ctx);
280    return true;
281}
282
283static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a)
284{
285    REQUIRE_FPU;
286    REQUIRE_ZDINX_OR_D(ctx);
287    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
288
289    TCGv_i64 dest = dest_fpr(ctx, a->rd);
290    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
291    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
292
293    gen_helper_fmin_d(dest, cpu_env, src1, src2);
294    gen_set_fpr_d(ctx, a->rd, dest);
295    mark_fs_dirty(ctx);
296    return true;
297}
298
299static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a)
300{
301    REQUIRE_FPU;
302    REQUIRE_ZDINX_OR_D(ctx);
303    REQUIRE_EVEN(ctx, a->rd | a->rs1 | a->rs2);
304
305    TCGv_i64 dest = dest_fpr(ctx, a->rd);
306    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
307    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
308
309    gen_helper_fmax_d(dest, cpu_env, src1, src2);
310    gen_set_fpr_d(ctx, a->rd, dest);
311    mark_fs_dirty(ctx);
312    return true;
313}
314
315static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a)
316{
317    REQUIRE_FPU;
318    REQUIRE_ZDINX_OR_D(ctx);
319    REQUIRE_EVEN(ctx, a->rs1);
320
321    TCGv_i64 dest = dest_fpr(ctx, a->rd);
322    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
323
324    gen_set_rm(ctx, a->rm);
325    gen_helper_fcvt_s_d(dest, cpu_env, src1);
326    gen_set_fpr_hs(ctx, a->rd, dest);
327    mark_fs_dirty(ctx);
328    return true;
329}
330
331static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a)
332{
333    REQUIRE_FPU;
334    REQUIRE_ZDINX_OR_D(ctx);
335    REQUIRE_EVEN(ctx, a->rd);
336
337    TCGv_i64 dest = dest_fpr(ctx, a->rd);
338    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
339
340    gen_set_rm(ctx, a->rm);
341    gen_helper_fcvt_d_s(dest, cpu_env, src1);
342    gen_set_fpr_d(ctx, a->rd, dest);
343    mark_fs_dirty(ctx);
344    return true;
345}
346
347static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a)
348{
349    REQUIRE_FPU;
350    REQUIRE_ZDINX_OR_D(ctx);
351    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
352
353    TCGv dest = dest_gpr(ctx, a->rd);
354    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
355    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
356
357    gen_helper_feq_d(dest, cpu_env, src1, src2);
358    gen_set_gpr(ctx, a->rd, dest);
359    return true;
360}
361
362static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a)
363{
364    REQUIRE_FPU;
365    REQUIRE_ZDINX_OR_D(ctx);
366    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
367
368    TCGv dest = dest_gpr(ctx, a->rd);
369    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
370    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
371
372    gen_helper_flt_d(dest, cpu_env, src1, src2);
373    gen_set_gpr(ctx, a->rd, dest);
374    return true;
375}
376
377static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a)
378{
379    REQUIRE_FPU;
380    REQUIRE_ZDINX_OR_D(ctx);
381    REQUIRE_EVEN(ctx, a->rs1 | a->rs2);
382
383    TCGv dest = dest_gpr(ctx, a->rd);
384    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
385    TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
386
387    gen_helper_fle_d(dest, cpu_env, src1, src2);
388    gen_set_gpr(ctx, a->rd, dest);
389    return true;
390}
391
392static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a)
393{
394    REQUIRE_FPU;
395    REQUIRE_ZDINX_OR_D(ctx);
396    REQUIRE_EVEN(ctx, a->rs1);
397
398    TCGv dest = dest_gpr(ctx, a->rd);
399    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
400
401    gen_helper_fclass_d(dest, src1);
402    gen_set_gpr(ctx, a->rd, dest);
403    return true;
404}
405
406static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a)
407{
408    REQUIRE_FPU;
409    REQUIRE_ZDINX_OR_D(ctx);
410    REQUIRE_EVEN(ctx, a->rs1);
411
412    TCGv dest = dest_gpr(ctx, a->rd);
413    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
414
415    gen_set_rm(ctx, a->rm);
416    gen_helper_fcvt_w_d(dest, cpu_env, src1);
417    gen_set_gpr(ctx, a->rd, dest);
418    return true;
419}
420
421static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a)
422{
423    REQUIRE_FPU;
424    REQUIRE_ZDINX_OR_D(ctx);
425    REQUIRE_EVEN(ctx, a->rs1);
426
427    TCGv dest = dest_gpr(ctx, a->rd);
428    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
429
430    gen_set_rm(ctx, a->rm);
431    gen_helper_fcvt_wu_d(dest, cpu_env, src1);
432    gen_set_gpr(ctx, a->rd, dest);
433    return true;
434}
435
436static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a)
437{
438    REQUIRE_FPU;
439    REQUIRE_ZDINX_OR_D(ctx);
440    REQUIRE_EVEN(ctx, a->rd);
441
442    TCGv_i64 dest = dest_fpr(ctx, a->rd);
443    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
444
445    gen_set_rm(ctx, a->rm);
446    gen_helper_fcvt_d_w(dest, cpu_env, src);
447    gen_set_fpr_d(ctx, a->rd, dest);
448
449    mark_fs_dirty(ctx);
450    return true;
451}
452
453static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a)
454{
455    REQUIRE_FPU;
456    REQUIRE_ZDINX_OR_D(ctx);
457    REQUIRE_EVEN(ctx, a->rd);
458
459    TCGv_i64 dest = dest_fpr(ctx, a->rd);
460    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
461
462    gen_set_rm(ctx, a->rm);
463    gen_helper_fcvt_d_wu(dest, cpu_env, src);
464    gen_set_fpr_d(ctx, a->rd, dest);
465
466    mark_fs_dirty(ctx);
467    return true;
468}
469
470static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a)
471{
472    REQUIRE_64BIT(ctx);
473    REQUIRE_FPU;
474    REQUIRE_ZDINX_OR_D(ctx);
475    REQUIRE_EVEN(ctx, a->rs1);
476
477    TCGv dest = dest_gpr(ctx, a->rd);
478    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
479
480    gen_set_rm(ctx, a->rm);
481    gen_helper_fcvt_l_d(dest, cpu_env, src1);
482    gen_set_gpr(ctx, a->rd, dest);
483    return true;
484}
485
486static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a)
487{
488    REQUIRE_64BIT(ctx);
489    REQUIRE_FPU;
490    REQUIRE_ZDINX_OR_D(ctx);
491    REQUIRE_EVEN(ctx, a->rs1);
492
493    TCGv dest = dest_gpr(ctx, a->rd);
494    TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
495
496    gen_set_rm(ctx, a->rm);
497    gen_helper_fcvt_lu_d(dest, cpu_env, src1);
498    gen_set_gpr(ctx, a->rd, dest);
499    return true;
500}
501
502static bool trans_fmv_x_d(DisasContext *ctx, arg_fmv_x_d *a)
503{
504    REQUIRE_64BIT(ctx);
505    REQUIRE_FPU;
506    REQUIRE_EXT(ctx, RVD);
507
508#ifdef TARGET_RISCV64
509    gen_set_gpr(ctx, a->rd, cpu_fpr[a->rs1]);
510    return true;
511#else
512    qemu_build_not_reached();
513#endif
514}
515
516static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a)
517{
518    REQUIRE_64BIT(ctx);
519    REQUIRE_FPU;
520    REQUIRE_ZDINX_OR_D(ctx);
521    REQUIRE_EVEN(ctx, a->rd);
522
523    TCGv_i64 dest = dest_fpr(ctx, a->rd);
524    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
525
526    gen_set_rm(ctx, a->rm);
527    gen_helper_fcvt_d_l(dest, cpu_env, src);
528    gen_set_fpr_d(ctx, a->rd, dest);
529
530    mark_fs_dirty(ctx);
531    return true;
532}
533
534static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a)
535{
536    REQUIRE_64BIT(ctx);
537    REQUIRE_FPU;
538    REQUIRE_ZDINX_OR_D(ctx);
539    REQUIRE_EVEN(ctx, a->rd);
540
541    TCGv_i64 dest = dest_fpr(ctx, a->rd);
542    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
543
544    gen_set_rm(ctx, a->rm);
545    gen_helper_fcvt_d_lu(dest, cpu_env, src);
546    gen_set_fpr_d(ctx, a->rd, dest);
547
548    mark_fs_dirty(ctx);
549    return true;
550}
551
552static bool trans_fmv_d_x(DisasContext *ctx, arg_fmv_d_x *a)
553{
554    REQUIRE_64BIT(ctx);
555    REQUIRE_FPU;
556    REQUIRE_EXT(ctx, RVD);
557
558#ifdef TARGET_RISCV64
559    tcg_gen_mov_tl(cpu_fpr[a->rd], get_gpr(ctx, a->rs1, EXT_NONE));
560    mark_fs_dirty(ctx);
561    return true;
562#else
563    qemu_build_not_reached();
564#endif
565}
566