1/*
2 * RISC-V translation routines for the RV64Zfh Standard Extension.
3 *
4 * Copyright (c) 2020 Chih-Min Chao, chihmin.chao@sifive.com
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2 or later, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#define REQUIRE_ZFH(ctx) do { \
20    if (!ctx->cfg_ptr->ext_zfh) {      \
21        return false;         \
22    }                         \
23} while (0)
24
25#define REQUIRE_ZFH_OR_ZFHMIN(ctx) do {       \
26    if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin)) { \
27        return false;                         \
28    }                                         \
29} while (0)
30
31static bool trans_flh(DisasContext *ctx, arg_flh *a)
32{
33    TCGv_i64 dest;
34    TCGv t0;
35
36    REQUIRE_FPU;
37    REQUIRE_ZFH_OR_ZFHMIN(ctx);
38
39    t0 = get_gpr(ctx, a->rs1, EXT_NONE);
40    if (a->imm) {
41        TCGv temp = temp_new(ctx);
42        tcg_gen_addi_tl(temp, t0, a->imm);
43        t0 = temp;
44    }
45
46    dest = cpu_fpr[a->rd];
47    tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, MO_TEUW);
48    gen_nanbox_h(dest, dest);
49
50    mark_fs_dirty(ctx);
51    return true;
52}
53
54static bool trans_fsh(DisasContext *ctx, arg_fsh *a)
55{
56    TCGv t0;
57
58    REQUIRE_FPU;
59    REQUIRE_ZFH_OR_ZFHMIN(ctx);
60
61    t0 = get_gpr(ctx, a->rs1, EXT_NONE);
62    if (a->imm) {
63        TCGv temp = tcg_temp_new();
64        tcg_gen_addi_tl(temp, t0, a->imm);
65        t0 = temp;
66    }
67
68    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEUW);
69
70    return true;
71}
72
73static bool trans_fmadd_h(DisasContext *ctx, arg_fmadd_h *a)
74{
75    REQUIRE_FPU;
76    REQUIRE_ZFH(ctx);
77
78    gen_set_rm(ctx, a->rm);
79    gen_helper_fmadd_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
80                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
81    mark_fs_dirty(ctx);
82    return true;
83}
84
85static bool trans_fmsub_h(DisasContext *ctx, arg_fmsub_h *a)
86{
87    REQUIRE_FPU;
88    REQUIRE_ZFH(ctx);
89
90    gen_set_rm(ctx, a->rm);
91    gen_helper_fmsub_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
92                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
93    mark_fs_dirty(ctx);
94    return true;
95}
96
97static bool trans_fnmsub_h(DisasContext *ctx, arg_fnmsub_h *a)
98{
99    REQUIRE_FPU;
100    REQUIRE_ZFH(ctx);
101
102    gen_set_rm(ctx, a->rm);
103    gen_helper_fnmsub_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
104                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
105    mark_fs_dirty(ctx);
106    return true;
107}
108
109static bool trans_fnmadd_h(DisasContext *ctx, arg_fnmadd_h *a)
110{
111    REQUIRE_FPU;
112    REQUIRE_ZFH(ctx);
113
114    gen_set_rm(ctx, a->rm);
115    gen_helper_fnmadd_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
116                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
117    mark_fs_dirty(ctx);
118    return true;
119}
120
121static bool trans_fadd_h(DisasContext *ctx, arg_fadd_h *a)
122{
123    REQUIRE_FPU;
124    REQUIRE_ZFH(ctx);
125
126    gen_set_rm(ctx, a->rm);
127    gen_helper_fadd_h(cpu_fpr[a->rd], cpu_env,
128                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
129    mark_fs_dirty(ctx);
130    return true;
131}
132
133static bool trans_fsub_h(DisasContext *ctx, arg_fsub_h *a)
134{
135    REQUIRE_FPU;
136    REQUIRE_ZFH(ctx);
137
138    gen_set_rm(ctx, a->rm);
139    gen_helper_fsub_h(cpu_fpr[a->rd], cpu_env,
140                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
141    mark_fs_dirty(ctx);
142    return true;
143}
144
145static bool trans_fmul_h(DisasContext *ctx, arg_fmul_h *a)
146{
147    REQUIRE_FPU;
148    REQUIRE_ZFH(ctx);
149
150    gen_set_rm(ctx, a->rm);
151    gen_helper_fmul_h(cpu_fpr[a->rd], cpu_env,
152                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
153    mark_fs_dirty(ctx);
154    return true;
155}
156
157static bool trans_fdiv_h(DisasContext *ctx, arg_fdiv_h *a)
158{
159    REQUIRE_FPU;
160    REQUIRE_ZFH(ctx);
161
162    gen_set_rm(ctx, a->rm);
163    gen_helper_fdiv_h(cpu_fpr[a->rd], cpu_env,
164                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
165    mark_fs_dirty(ctx);
166    return true;
167}
168
169static bool trans_fsqrt_h(DisasContext *ctx, arg_fsqrt_h *a)
170{
171    REQUIRE_FPU;
172    REQUIRE_ZFH(ctx);
173
174    gen_set_rm(ctx, a->rm);
175    gen_helper_fsqrt_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
176    mark_fs_dirty(ctx);
177    return true;
178}
179
180static bool trans_fsgnj_h(DisasContext *ctx, arg_fsgnj_h *a)
181{
182    REQUIRE_FPU;
183    REQUIRE_ZFH(ctx);
184
185    if (a->rs1 == a->rs2) { /* FMOV */
186        gen_check_nanbox_h(cpu_fpr[a->rd], cpu_fpr[a->rs1]);
187    } else {
188        TCGv_i64 rs1 = tcg_temp_new_i64();
189        TCGv_i64 rs2 = tcg_temp_new_i64();
190
191        gen_check_nanbox_h(rs1, cpu_fpr[a->rs1]);
192        gen_check_nanbox_h(rs2, cpu_fpr[a->rs2]);
193
194        /* This formulation retains the nanboxing of rs2. */
195        tcg_gen_deposit_i64(cpu_fpr[a->rd], rs2, rs1, 0, 15);
196        tcg_temp_free_i64(rs1);
197        tcg_temp_free_i64(rs2);
198    }
199
200    mark_fs_dirty(ctx);
201    return true;
202}
203
204static bool trans_fsgnjn_h(DisasContext *ctx, arg_fsgnjn_h *a)
205{
206    TCGv_i64 rs1, rs2, mask;
207
208    REQUIRE_FPU;
209    REQUIRE_ZFH(ctx);
210
211    rs1 = tcg_temp_new_i64();
212    gen_check_nanbox_h(rs1, cpu_fpr[a->rs1]);
213
214    if (a->rs1 == a->rs2) { /* FNEG */
215        tcg_gen_xori_i64(cpu_fpr[a->rd], rs1, MAKE_64BIT_MASK(15, 1));
216    } else {
217        rs2 = tcg_temp_new_i64();
218        gen_check_nanbox_h(rs2, cpu_fpr[a->rs2]);
219
220        /*
221         * Replace bit 15 in rs1 with inverse in rs2.
222         * This formulation retains the nanboxing of rs1.
223         */
224        mask = tcg_const_i64(~MAKE_64BIT_MASK(15, 1));
225        tcg_gen_not_i64(rs2, rs2);
226        tcg_gen_andc_i64(rs2, rs2, mask);
227        tcg_gen_and_i64(rs1, mask, rs1);
228        tcg_gen_or_i64(cpu_fpr[a->rd], rs1, rs2);
229
230        tcg_temp_free_i64(mask);
231        tcg_temp_free_i64(rs2);
232    }
233    mark_fs_dirty(ctx);
234    return true;
235}
236
237static bool trans_fsgnjx_h(DisasContext *ctx, arg_fsgnjx_h *a)
238{
239    TCGv_i64 rs1, rs2;
240
241    REQUIRE_FPU;
242    REQUIRE_ZFH(ctx);
243
244    rs1 = tcg_temp_new_i64();
245    gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]);
246
247    if (a->rs1 == a->rs2) { /* FABS */
248        tcg_gen_andi_i64(cpu_fpr[a->rd], rs1, ~MAKE_64BIT_MASK(15, 1));
249    } else {
250        rs2 = tcg_temp_new_i64();
251        gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]);
252
253        /*
254         * Xor bit 15 in rs1 with that in rs2.
255         * This formulation retains the nanboxing of rs1.
256         */
257        tcg_gen_andi_i64(rs2, rs2, MAKE_64BIT_MASK(15, 1));
258        tcg_gen_xor_i64(cpu_fpr[a->rd], rs1, rs2);
259
260        tcg_temp_free_i64(rs2);
261    }
262
263    mark_fs_dirty(ctx);
264    return true;
265}
266
267static bool trans_fmin_h(DisasContext *ctx, arg_fmin_h *a)
268{
269    REQUIRE_FPU;
270    REQUIRE_ZFH(ctx);
271
272    gen_helper_fmin_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
273                      cpu_fpr[a->rs2]);
274    mark_fs_dirty(ctx);
275    return true;
276}
277
278static bool trans_fmax_h(DisasContext *ctx, arg_fmax_h *a)
279{
280    REQUIRE_FPU;
281    REQUIRE_ZFH(ctx);
282
283    gen_helper_fmax_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
284                      cpu_fpr[a->rs2]);
285    mark_fs_dirty(ctx);
286    return true;
287}
288
289static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a)
290{
291    REQUIRE_FPU;
292    REQUIRE_ZFH_OR_ZFHMIN(ctx);
293
294    gen_set_rm(ctx, a->rm);
295    gen_helper_fcvt_s_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
296
297    mark_fs_dirty(ctx);
298
299    return true;
300}
301
302static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a)
303{
304    REQUIRE_FPU;
305    REQUIRE_ZFH_OR_ZFHMIN(ctx);
306    REQUIRE_EXT(ctx, RVD);
307
308    gen_set_rm(ctx, a->rm);
309    gen_helper_fcvt_d_h(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
310
311    mark_fs_dirty(ctx);
312
313
314    return true;
315}
316
317static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a)
318{
319    REQUIRE_FPU;
320    REQUIRE_ZFH_OR_ZFHMIN(ctx);
321
322    gen_set_rm(ctx, a->rm);
323    gen_helper_fcvt_h_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
324
325    mark_fs_dirty(ctx);
326
327    return true;
328}
329
330static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a)
331{
332    REQUIRE_FPU;
333    REQUIRE_ZFH_OR_ZFHMIN(ctx);
334    REQUIRE_EXT(ctx, RVD);
335
336    gen_set_rm(ctx, a->rm);
337    gen_helper_fcvt_h_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
338
339    mark_fs_dirty(ctx);
340
341    return true;
342}
343
344static bool trans_feq_h(DisasContext *ctx, arg_feq_h *a)
345{
346    REQUIRE_FPU;
347    REQUIRE_ZFH(ctx);
348
349    TCGv dest = dest_gpr(ctx, a->rd);
350
351    gen_helper_feq_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
352    gen_set_gpr(ctx, a->rd, dest);
353    return true;
354}
355
356static bool trans_flt_h(DisasContext *ctx, arg_flt_h *a)
357{
358    REQUIRE_FPU;
359    REQUIRE_ZFH(ctx);
360
361    TCGv dest = dest_gpr(ctx, a->rd);
362
363    gen_helper_flt_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
364    gen_set_gpr(ctx, a->rd, dest);
365
366    return true;
367}
368
369static bool trans_fle_h(DisasContext *ctx, arg_fle_h *a)
370{
371    REQUIRE_FPU;
372    REQUIRE_ZFH(ctx);
373
374    TCGv dest = dest_gpr(ctx, a->rd);
375
376    gen_helper_fle_h(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
377    gen_set_gpr(ctx, a->rd, dest);
378    return true;
379}
380
381static bool trans_fclass_h(DisasContext *ctx, arg_fclass_h *a)
382{
383    REQUIRE_FPU;
384    REQUIRE_ZFH(ctx);
385
386    TCGv dest = dest_gpr(ctx, a->rd);
387
388    gen_helper_fclass_h(dest, cpu_fpr[a->rs1]);
389    gen_set_gpr(ctx, a->rd, dest);
390    return true;
391}
392
393static bool trans_fcvt_w_h(DisasContext *ctx, arg_fcvt_w_h *a)
394{
395    REQUIRE_FPU;
396    REQUIRE_ZFH(ctx);
397
398    TCGv dest = dest_gpr(ctx, a->rd);
399
400    gen_set_rm(ctx, a->rm);
401    gen_helper_fcvt_w_h(dest, cpu_env, cpu_fpr[a->rs1]);
402    gen_set_gpr(ctx, a->rd, dest);
403    return true;
404}
405
406static bool trans_fcvt_wu_h(DisasContext *ctx, arg_fcvt_wu_h *a)
407{
408    REQUIRE_FPU;
409    REQUIRE_ZFH(ctx);
410
411    TCGv dest = dest_gpr(ctx, a->rd);
412
413    gen_set_rm(ctx, a->rm);
414    gen_helper_fcvt_wu_h(dest, cpu_env, cpu_fpr[a->rs1]);
415    gen_set_gpr(ctx, a->rd, dest);
416    return true;
417}
418
419static bool trans_fcvt_h_w(DisasContext *ctx, arg_fcvt_h_w *a)
420{
421    REQUIRE_FPU;
422    REQUIRE_ZFH(ctx);
423
424    TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN);
425
426    gen_set_rm(ctx, a->rm);
427    gen_helper_fcvt_h_w(cpu_fpr[a->rd], cpu_env, t0);
428
429    mark_fs_dirty(ctx);
430    return true;
431}
432
433static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a)
434{
435    REQUIRE_FPU;
436    REQUIRE_ZFH(ctx);
437
438    TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN);
439
440    gen_set_rm(ctx, a->rm);
441    gen_helper_fcvt_h_wu(cpu_fpr[a->rd], cpu_env, t0);
442
443    mark_fs_dirty(ctx);
444    return true;
445}
446
447static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a)
448{
449    REQUIRE_FPU;
450    REQUIRE_ZFH_OR_ZFHMIN(ctx);
451
452    TCGv dest = dest_gpr(ctx, a->rd);
453
454#if defined(TARGET_RISCV64)
455    /* 16 bits -> 64 bits */
456    tcg_gen_ext16s_tl(dest, cpu_fpr[a->rs1]);
457#else
458    /* 16 bits -> 32 bits */
459    tcg_gen_extrl_i64_i32(dest, cpu_fpr[a->rs1]);
460    tcg_gen_ext16s_tl(dest, dest);
461#endif
462
463    gen_set_gpr(ctx, a->rd, dest);
464    return true;
465}
466
467static bool trans_fmv_h_x(DisasContext *ctx, arg_fmv_h_x *a)
468{
469    REQUIRE_FPU;
470    REQUIRE_ZFH_OR_ZFHMIN(ctx);
471
472    TCGv t0 = get_gpr(ctx, a->rs1, EXT_ZERO);
473
474    tcg_gen_extu_tl_i64(cpu_fpr[a->rd], t0);
475    gen_nanbox_h(cpu_fpr[a->rd], cpu_fpr[a->rd]);
476
477    mark_fs_dirty(ctx);
478    return true;
479}
480
481static bool trans_fcvt_l_h(DisasContext *ctx, arg_fcvt_l_h *a)
482{
483    REQUIRE_64BIT(ctx);
484    REQUIRE_FPU;
485    REQUIRE_ZFH(ctx);
486
487    TCGv dest = dest_gpr(ctx, a->rd);
488
489    gen_set_rm(ctx, a->rm);
490    gen_helper_fcvt_l_h(dest, cpu_env, cpu_fpr[a->rs1]);
491    gen_set_gpr(ctx, a->rd, dest);
492    return true;
493}
494
495static bool trans_fcvt_lu_h(DisasContext *ctx, arg_fcvt_lu_h *a)
496{
497    REQUIRE_64BIT(ctx);
498    REQUIRE_FPU;
499    REQUIRE_ZFH(ctx);
500
501    TCGv dest = dest_gpr(ctx, a->rd);
502
503    gen_set_rm(ctx, a->rm);
504    gen_helper_fcvt_lu_h(dest, cpu_env, cpu_fpr[a->rs1]);
505    gen_set_gpr(ctx, a->rd, dest);
506    return true;
507}
508
509static bool trans_fcvt_h_l(DisasContext *ctx, arg_fcvt_h_l *a)
510{
511    REQUIRE_64BIT(ctx);
512    REQUIRE_FPU;
513    REQUIRE_ZFH(ctx);
514
515    TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN);
516
517    gen_set_rm(ctx, a->rm);
518    gen_helper_fcvt_h_l(cpu_fpr[a->rd], cpu_env, t0);
519
520    mark_fs_dirty(ctx);
521    return true;
522}
523
524static bool trans_fcvt_h_lu(DisasContext *ctx, arg_fcvt_h_lu *a)
525{
526    REQUIRE_64BIT(ctx);
527    REQUIRE_FPU;
528    REQUIRE_ZFH(ctx);
529
530    TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN);
531
532    gen_set_rm(ctx, a->rm);
533    gen_helper_fcvt_h_lu(cpu_fpr[a->rd], cpu_env, t0);
534
535    mark_fs_dirty(ctx);
536    return true;
537}
538