1/*
2 * RISC-V translation routines for the RV64F 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_FPU do {\
22    if (ctx->mstatus_fs == 0) \
23        if (!ctx->cfg_ptr->ext_zfinx) \
24            return false; \
25} while (0)
26
27#define REQUIRE_ZFINX_OR_F(ctx) do {\
28    if (!ctx->cfg_ptr->ext_zfinx) { \
29        REQUIRE_EXT(ctx, RVF); \
30    } \
31} while (0)
32
33#define REQUIRE_ZCF(ctx) do {                  \
34    if (!ctx->cfg_ptr->ext_zcf) {              \
35        return false;                          \
36    }                                          \
37} while (0)
38
39static bool trans_flw(DisasContext *ctx, arg_flw *a)
40{
41    TCGv_i64 dest;
42    TCGv addr;
43
44    REQUIRE_FPU;
45    REQUIRE_EXT(ctx, RVF);
46
47    decode_save_opc(ctx);
48    addr = get_address(ctx, a->rs1, a->imm);
49    dest = cpu_fpr[a->rd];
50    tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL);
51    gen_nanbox_s(dest, dest);
52
53    mark_fs_dirty(ctx);
54    return true;
55}
56
57static bool trans_fsw(DisasContext *ctx, arg_fsw *a)
58{
59    TCGv addr;
60
61    REQUIRE_FPU;
62    REQUIRE_EXT(ctx, RVF);
63
64    decode_save_opc(ctx);
65    addr = get_address(ctx, a->rs1, a->imm);
66    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL);
67    return true;
68}
69
70static bool trans_c_flw(DisasContext *ctx, arg_flw *a)
71{
72    REQUIRE_ZCF(ctx);
73    return trans_flw(ctx, a);
74}
75
76static bool trans_c_fsw(DisasContext *ctx, arg_fsw *a)
77{
78    REQUIRE_ZCF(ctx);
79    return trans_fsw(ctx, a);
80}
81
82static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a)
83{
84    REQUIRE_FPU;
85    REQUIRE_ZFINX_OR_F(ctx);
86
87    TCGv_i64 dest = dest_fpr(ctx, a->rd);
88    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
89    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
90    TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
91
92    gen_set_rm(ctx, a->rm);
93    gen_helper_fmadd_s(dest, cpu_env, src1, src2, src3);
94    gen_set_fpr_hs(ctx, a->rd, dest);
95    mark_fs_dirty(ctx);
96    return true;
97}
98
99static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a)
100{
101    REQUIRE_FPU;
102    REQUIRE_ZFINX_OR_F(ctx);
103
104    TCGv_i64 dest = dest_fpr(ctx, a->rd);
105    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
106    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
107    TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
108
109    gen_set_rm(ctx, a->rm);
110    gen_helper_fmsub_s(dest, cpu_env, src1, src2, src3);
111    gen_set_fpr_hs(ctx, a->rd, dest);
112    mark_fs_dirty(ctx);
113    return true;
114}
115
116static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a)
117{
118    REQUIRE_FPU;
119    REQUIRE_ZFINX_OR_F(ctx);
120
121    TCGv_i64 dest = dest_fpr(ctx, a->rd);
122    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
123    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
124    TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
125
126    gen_set_rm(ctx, a->rm);
127    gen_helper_fnmsub_s(dest, cpu_env, src1, src2, src3);
128    gen_set_fpr_hs(ctx, a->rd, dest);
129    mark_fs_dirty(ctx);
130    return true;
131}
132
133static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a)
134{
135    REQUIRE_FPU;
136    REQUIRE_ZFINX_OR_F(ctx);
137
138    TCGv_i64 dest = dest_fpr(ctx, a->rd);
139    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
140    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
141    TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
142
143    gen_set_rm(ctx, a->rm);
144    gen_helper_fnmadd_s(dest, cpu_env, src1, src2, src3);
145    gen_set_fpr_hs(ctx, a->rd, dest);
146    mark_fs_dirty(ctx);
147    return true;
148}
149
150static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a)
151{
152    REQUIRE_FPU;
153    REQUIRE_ZFINX_OR_F(ctx);
154
155    TCGv_i64 dest = dest_fpr(ctx, a->rd);
156    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
157    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
158
159    gen_set_rm(ctx, a->rm);
160    gen_helper_fadd_s(dest, cpu_env, src1, src2);
161    gen_set_fpr_hs(ctx, a->rd, dest);
162    mark_fs_dirty(ctx);
163    return true;
164}
165
166static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a)
167{
168    REQUIRE_FPU;
169    REQUIRE_ZFINX_OR_F(ctx);
170
171    TCGv_i64 dest = dest_fpr(ctx, a->rd);
172    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
173    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
174
175    gen_set_rm(ctx, a->rm);
176    gen_helper_fsub_s(dest, cpu_env, src1, src2);
177    gen_set_fpr_hs(ctx, a->rd, dest);
178    mark_fs_dirty(ctx);
179    return true;
180}
181
182static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a)
183{
184    REQUIRE_FPU;
185    REQUIRE_ZFINX_OR_F(ctx);
186
187    TCGv_i64 dest = dest_fpr(ctx, a->rd);
188    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
189    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
190
191    gen_set_rm(ctx, a->rm);
192    gen_helper_fmul_s(dest, cpu_env, src1, src2);
193    gen_set_fpr_hs(ctx, a->rd, dest);
194    mark_fs_dirty(ctx);
195    return true;
196}
197
198static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a)
199{
200    REQUIRE_FPU;
201    REQUIRE_ZFINX_OR_F(ctx);
202
203    TCGv_i64 dest = dest_fpr(ctx, a->rd);
204    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
205    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
206
207    gen_set_rm(ctx, a->rm);
208    gen_helper_fdiv_s(dest, cpu_env, src1, src2);
209    gen_set_fpr_hs(ctx, a->rd, dest);
210    mark_fs_dirty(ctx);
211    return true;
212}
213
214static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a)
215{
216    REQUIRE_FPU;
217    REQUIRE_ZFINX_OR_F(ctx);
218
219    TCGv_i64 dest = dest_fpr(ctx, a->rd);
220    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
221
222    gen_set_rm(ctx, a->rm);
223    gen_helper_fsqrt_s(dest, cpu_env, src1);
224    gen_set_fpr_hs(ctx, a->rd, dest);
225    mark_fs_dirty(ctx);
226    return true;
227}
228
229static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a)
230{
231    REQUIRE_FPU;
232    REQUIRE_ZFINX_OR_F(ctx);
233
234    TCGv_i64 dest = dest_fpr(ctx, a->rd);
235    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
236
237    if (a->rs1 == a->rs2) { /* FMOV */
238        if (!ctx->cfg_ptr->ext_zfinx) {
239            gen_check_nanbox_s(dest, src1);
240        } else {
241            tcg_gen_ext32s_i64(dest, src1);
242        }
243    } else { /* FSGNJ */
244        TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
245
246        if (!ctx->cfg_ptr->ext_zfinx) {
247            TCGv_i64 rs1 = tcg_temp_new_i64();
248            TCGv_i64 rs2 = tcg_temp_new_i64();
249            gen_check_nanbox_s(rs1, src1);
250            gen_check_nanbox_s(rs2, src2);
251
252            /* This formulation retains the nanboxing of rs2 in normal 'F'. */
253            tcg_gen_deposit_i64(dest, rs2, rs1, 0, 31);
254        } else {
255            tcg_gen_deposit_i64(dest, src2, src1, 0, 31);
256            tcg_gen_ext32s_i64(dest, dest);
257        }
258    }
259    gen_set_fpr_hs(ctx, a->rd, dest);
260    mark_fs_dirty(ctx);
261    return true;
262}
263
264static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a)
265{
266    TCGv_i64 rs1, rs2, mask;
267
268    REQUIRE_FPU;
269    REQUIRE_ZFINX_OR_F(ctx);
270
271    TCGv_i64 dest = dest_fpr(ctx, a->rd);
272    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
273
274    rs1 = tcg_temp_new_i64();
275    if (!ctx->cfg_ptr->ext_zfinx) {
276        gen_check_nanbox_s(rs1, src1);
277    } else {
278        tcg_gen_mov_i64(rs1, src1);
279    }
280    if (a->rs1 == a->rs2) { /* FNEG */
281        tcg_gen_xori_i64(dest, rs1, MAKE_64BIT_MASK(31, 1));
282    } else {
283        TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
284        rs2 = tcg_temp_new_i64();
285        if (!ctx->cfg_ptr->ext_zfinx) {
286            gen_check_nanbox_s(rs2, src2);
287        } else {
288            tcg_gen_mov_i64(rs2, src2);
289        }
290
291        /*
292         * Replace bit 31 in rs1 with inverse in rs2.
293         * This formulation retains the nanboxing of rs1.
294         */
295        mask = tcg_constant_i64(~MAKE_64BIT_MASK(31, 1));
296        tcg_gen_nor_i64(rs2, rs2, mask);
297        tcg_gen_and_i64(dest, mask, rs1);
298        tcg_gen_or_i64(dest, dest, rs2);
299    }
300    /* signed-extended intead of nanboxing for result if enable zfinx */
301    if (ctx->cfg_ptr->ext_zfinx) {
302        tcg_gen_ext32s_i64(dest, dest);
303    }
304    gen_set_fpr_hs(ctx, a->rd, dest);
305    mark_fs_dirty(ctx);
306    return true;
307}
308
309static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a)
310{
311    TCGv_i64 rs1, rs2;
312
313    REQUIRE_FPU;
314    REQUIRE_ZFINX_OR_F(ctx);
315
316    TCGv_i64 dest = dest_fpr(ctx, a->rd);
317    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
318    rs1 = tcg_temp_new_i64();
319
320    if (!ctx->cfg_ptr->ext_zfinx) {
321        gen_check_nanbox_s(rs1, src1);
322    } else {
323        tcg_gen_mov_i64(rs1, src1);
324    }
325
326    if (a->rs1 == a->rs2) { /* FABS */
327        tcg_gen_andi_i64(dest, rs1, ~MAKE_64BIT_MASK(31, 1));
328    } else {
329        TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
330        rs2 = tcg_temp_new_i64();
331
332        if (!ctx->cfg_ptr->ext_zfinx) {
333            gen_check_nanbox_s(rs2, src2);
334        } else {
335            tcg_gen_mov_i64(rs2, src2);
336        }
337
338        /*
339         * Xor bit 31 in rs1 with that in rs2.
340         * This formulation retains the nanboxing of rs1.
341         */
342        tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(31, 1));
343        tcg_gen_xor_i64(dest, rs1, dest);
344    }
345    /* signed-extended intead of nanboxing for result if enable zfinx */
346    if (ctx->cfg_ptr->ext_zfinx) {
347        tcg_gen_ext32s_i64(dest, dest);
348    }
349    gen_set_fpr_hs(ctx, a->rd, dest);
350    mark_fs_dirty(ctx);
351    return true;
352}
353
354static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a)
355{
356    REQUIRE_FPU;
357    REQUIRE_ZFINX_OR_F(ctx);
358
359    TCGv_i64 dest = dest_fpr(ctx, a->rd);
360    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
361    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
362
363    gen_helper_fmin_s(dest, cpu_env, src1, src2);
364    gen_set_fpr_hs(ctx, a->rd, dest);
365    mark_fs_dirty(ctx);
366    return true;
367}
368
369static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a)
370{
371    REQUIRE_FPU;
372    REQUIRE_ZFINX_OR_F(ctx);
373
374    TCGv_i64 dest = dest_fpr(ctx, a->rd);
375    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
376    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
377
378    gen_helper_fmax_s(dest, cpu_env, src1, src2);
379    gen_set_fpr_hs(ctx, a->rd, dest);
380    mark_fs_dirty(ctx);
381    return true;
382}
383
384static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a)
385{
386    REQUIRE_FPU;
387    REQUIRE_ZFINX_OR_F(ctx);
388
389    TCGv dest = dest_gpr(ctx, a->rd);
390    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
391
392    gen_set_rm(ctx, a->rm);
393    gen_helper_fcvt_w_s(dest, cpu_env, src1);
394    gen_set_gpr(ctx, a->rd, dest);
395    return true;
396}
397
398static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a)
399{
400    REQUIRE_FPU;
401    REQUIRE_ZFINX_OR_F(ctx);
402
403    TCGv dest = dest_gpr(ctx, a->rd);
404    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
405
406    gen_set_rm(ctx, a->rm);
407    gen_helper_fcvt_wu_s(dest, cpu_env, src1);
408    gen_set_gpr(ctx, a->rd, dest);
409    return true;
410}
411
412static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a)
413{
414    /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */
415    REQUIRE_FPU;
416    REQUIRE_ZFINX_OR_F(ctx);
417
418    TCGv dest = dest_gpr(ctx, a->rd);
419    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
420#if defined(TARGET_RISCV64)
421    tcg_gen_ext32s_tl(dest, src1);
422#else
423    tcg_gen_extrl_i64_i32(dest, src1);
424#endif
425
426    gen_set_gpr(ctx, a->rd, dest);
427    return true;
428}
429
430static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a)
431{
432    REQUIRE_FPU;
433    REQUIRE_ZFINX_OR_F(ctx);
434
435    TCGv dest = dest_gpr(ctx, a->rd);
436    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
437    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
438
439    gen_helper_feq_s(dest, cpu_env, src1, src2);
440    gen_set_gpr(ctx, a->rd, dest);
441    return true;
442}
443
444static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a)
445{
446    REQUIRE_FPU;
447    REQUIRE_ZFINX_OR_F(ctx);
448
449    TCGv dest = dest_gpr(ctx, a->rd);
450    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
451    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
452
453    gen_helper_flt_s(dest, cpu_env, src1, src2);
454    gen_set_gpr(ctx, a->rd, dest);
455    return true;
456}
457
458static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a)
459{
460    REQUIRE_FPU;
461    REQUIRE_ZFINX_OR_F(ctx);
462
463    TCGv dest = dest_gpr(ctx, a->rd);
464    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
465    TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
466
467    gen_helper_fle_s(dest, cpu_env, src1, src2);
468    gen_set_gpr(ctx, a->rd, dest);
469    return true;
470}
471
472static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a)
473{
474    REQUIRE_FPU;
475    REQUIRE_ZFINX_OR_F(ctx);
476
477    TCGv dest = dest_gpr(ctx, a->rd);
478    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
479
480    gen_helper_fclass_s(dest, cpu_env, src1);
481    gen_set_gpr(ctx, a->rd, dest);
482    return true;
483}
484
485static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a)
486{
487    REQUIRE_FPU;
488    REQUIRE_ZFINX_OR_F(ctx);
489
490    TCGv_i64 dest = dest_fpr(ctx, a->rd);
491    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
492
493    gen_set_rm(ctx, a->rm);
494    gen_helper_fcvt_s_w(dest, cpu_env, src);
495    gen_set_fpr_hs(ctx, a->rd, dest);
496    mark_fs_dirty(ctx);
497    return true;
498}
499
500static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a)
501{
502    REQUIRE_FPU;
503    REQUIRE_ZFINX_OR_F(ctx);
504
505    TCGv_i64 dest = dest_fpr(ctx, a->rd);
506    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
507
508    gen_set_rm(ctx, a->rm);
509    gen_helper_fcvt_s_wu(dest, cpu_env, src);
510    gen_set_fpr_hs(ctx, a->rd, dest);
511    mark_fs_dirty(ctx);
512    return true;
513}
514
515static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a)
516{
517    /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */
518    REQUIRE_FPU;
519    REQUIRE_ZFINX_OR_F(ctx);
520
521    TCGv_i64 dest = dest_fpr(ctx, a->rd);
522    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
523
524    tcg_gen_extu_tl_i64(dest, src);
525    gen_nanbox_s(dest, dest);
526    gen_set_fpr_hs(ctx, a->rd, dest);
527    mark_fs_dirty(ctx);
528    return true;
529}
530
531static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a)
532{
533    REQUIRE_64BIT(ctx);
534    REQUIRE_FPU;
535    REQUIRE_ZFINX_OR_F(ctx);
536
537    TCGv dest = dest_gpr(ctx, a->rd);
538    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
539
540    gen_set_rm(ctx, a->rm);
541    gen_helper_fcvt_l_s(dest, cpu_env, src1);
542    gen_set_gpr(ctx, a->rd, dest);
543    return true;
544}
545
546static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a)
547{
548    REQUIRE_64BIT(ctx);
549    REQUIRE_FPU;
550    REQUIRE_ZFINX_OR_F(ctx);
551
552    TCGv dest = dest_gpr(ctx, a->rd);
553    TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
554
555    gen_set_rm(ctx, a->rm);
556    gen_helper_fcvt_lu_s(dest, cpu_env, src1);
557    gen_set_gpr(ctx, a->rd, dest);
558    return true;
559}
560
561static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a)
562{
563    REQUIRE_64BIT(ctx);
564    REQUIRE_FPU;
565    REQUIRE_ZFINX_OR_F(ctx);
566
567    TCGv_i64 dest = dest_fpr(ctx, a->rd);
568    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
569
570    gen_set_rm(ctx, a->rm);
571    gen_helper_fcvt_s_l(dest, cpu_env, src);
572    gen_set_fpr_hs(ctx, a->rd, dest);
573    mark_fs_dirty(ctx);
574    return true;
575}
576
577static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a)
578{
579    REQUIRE_64BIT(ctx);
580    REQUIRE_FPU;
581    REQUIRE_ZFINX_OR_F(ctx);
582
583    TCGv_i64 dest = dest_fpr(ctx, a->rd);
584    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
585
586    gen_set_rm(ctx, a->rm);
587    gen_helper_fcvt_s_lu(dest, cpu_env, src);
588    gen_set_fpr_hs(ctx, a->rd, dest);
589    mark_fs_dirty(ctx);
590    return true;
591}
592