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        return false;                       \
24} while (0)
25
26static bool trans_flw(DisasContext *ctx, arg_flw *a)
27{
28    TCGv_i64 dest;
29    TCGv addr;
30
31    REQUIRE_FPU;
32    REQUIRE_EXT(ctx, RVF);
33
34    addr = get_address(ctx, a->rs1, a->imm);
35    dest = cpu_fpr[a->rd];
36    tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL);
37    gen_nanbox_s(dest, dest);
38
39    mark_fs_dirty(ctx);
40    return true;
41}
42
43static bool trans_fsw(DisasContext *ctx, arg_fsw *a)
44{
45    TCGv addr;
46
47    REQUIRE_FPU;
48    REQUIRE_EXT(ctx, RVF);
49
50    addr = get_address(ctx, a->rs1, a->imm);
51    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL);
52    return true;
53}
54
55static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a)
56{
57    REQUIRE_FPU;
58    REQUIRE_EXT(ctx, RVF);
59    gen_set_rm(ctx, a->rm);
60    gen_helper_fmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
61                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
62    mark_fs_dirty(ctx);
63    return true;
64}
65
66static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a)
67{
68    REQUIRE_FPU;
69    REQUIRE_EXT(ctx, RVF);
70    gen_set_rm(ctx, a->rm);
71    gen_helper_fmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
72                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
73    mark_fs_dirty(ctx);
74    return true;
75}
76
77static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a)
78{
79    REQUIRE_FPU;
80    REQUIRE_EXT(ctx, RVF);
81    gen_set_rm(ctx, a->rm);
82    gen_helper_fnmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
83                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
84    mark_fs_dirty(ctx);
85    return true;
86}
87
88static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a)
89{
90    REQUIRE_FPU;
91    REQUIRE_EXT(ctx, RVF);
92    gen_set_rm(ctx, a->rm);
93    gen_helper_fnmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
94                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
95    mark_fs_dirty(ctx);
96    return true;
97}
98
99static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a)
100{
101    REQUIRE_FPU;
102    REQUIRE_EXT(ctx, RVF);
103
104    gen_set_rm(ctx, a->rm);
105    gen_helper_fadd_s(cpu_fpr[a->rd], cpu_env,
106                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
107    mark_fs_dirty(ctx);
108    return true;
109}
110
111static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a)
112{
113    REQUIRE_FPU;
114    REQUIRE_EXT(ctx, RVF);
115
116    gen_set_rm(ctx, a->rm);
117    gen_helper_fsub_s(cpu_fpr[a->rd], cpu_env,
118                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
119    mark_fs_dirty(ctx);
120    return true;
121}
122
123static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a)
124{
125    REQUIRE_FPU;
126    REQUIRE_EXT(ctx, RVF);
127
128    gen_set_rm(ctx, a->rm);
129    gen_helper_fmul_s(cpu_fpr[a->rd], cpu_env,
130                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
131    mark_fs_dirty(ctx);
132    return true;
133}
134
135static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a)
136{
137    REQUIRE_FPU;
138    REQUIRE_EXT(ctx, RVF);
139
140    gen_set_rm(ctx, a->rm);
141    gen_helper_fdiv_s(cpu_fpr[a->rd], cpu_env,
142                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
143    mark_fs_dirty(ctx);
144    return true;
145}
146
147static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a)
148{
149    REQUIRE_FPU;
150    REQUIRE_EXT(ctx, RVF);
151
152    gen_set_rm(ctx, a->rm);
153    gen_helper_fsqrt_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
154    mark_fs_dirty(ctx);
155    return true;
156}
157
158static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a)
159{
160    REQUIRE_FPU;
161    REQUIRE_EXT(ctx, RVF);
162
163    if (a->rs1 == a->rs2) { /* FMOV */
164        gen_check_nanbox_s(cpu_fpr[a->rd], cpu_fpr[a->rs1]);
165    } else { /* FSGNJ */
166        TCGv_i64 rs1 = tcg_temp_new_i64();
167        TCGv_i64 rs2 = tcg_temp_new_i64();
168
169        gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]);
170        gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]);
171
172        /* This formulation retains the nanboxing of rs2. */
173        tcg_gen_deposit_i64(cpu_fpr[a->rd], rs2, rs1, 0, 31);
174        tcg_temp_free_i64(rs1);
175        tcg_temp_free_i64(rs2);
176    }
177    mark_fs_dirty(ctx);
178    return true;
179}
180
181static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a)
182{
183    TCGv_i64 rs1, rs2, mask;
184
185    REQUIRE_FPU;
186    REQUIRE_EXT(ctx, RVF);
187
188    rs1 = tcg_temp_new_i64();
189    gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]);
190
191    if (a->rs1 == a->rs2) { /* FNEG */
192        tcg_gen_xori_i64(cpu_fpr[a->rd], rs1, MAKE_64BIT_MASK(31, 1));
193    } else {
194        rs2 = tcg_temp_new_i64();
195        gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]);
196
197        /*
198         * Replace bit 31 in rs1 with inverse in rs2.
199         * This formulation retains the nanboxing of rs1.
200         */
201        mask = tcg_constant_i64(~MAKE_64BIT_MASK(31, 1));
202        tcg_gen_nor_i64(rs2, rs2, mask);
203        tcg_gen_and_i64(rs1, mask, rs1);
204        tcg_gen_or_i64(cpu_fpr[a->rd], rs1, rs2);
205
206        tcg_temp_free_i64(rs2);
207    }
208    tcg_temp_free_i64(rs1);
209
210    mark_fs_dirty(ctx);
211    return true;
212}
213
214static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a)
215{
216    TCGv_i64 rs1, rs2;
217
218    REQUIRE_FPU;
219    REQUIRE_EXT(ctx, RVF);
220
221    rs1 = tcg_temp_new_i64();
222    gen_check_nanbox_s(rs1, cpu_fpr[a->rs1]);
223
224    if (a->rs1 == a->rs2) { /* FABS */
225        tcg_gen_andi_i64(cpu_fpr[a->rd], rs1, ~MAKE_64BIT_MASK(31, 1));
226    } else {
227        rs2 = tcg_temp_new_i64();
228        gen_check_nanbox_s(rs2, cpu_fpr[a->rs2]);
229
230        /*
231         * Xor bit 31 in rs1 with that in rs2.
232         * This formulation retains the nanboxing of rs1.
233         */
234        tcg_gen_andi_i64(rs2, rs2, MAKE_64BIT_MASK(31, 1));
235        tcg_gen_xor_i64(cpu_fpr[a->rd], rs1, rs2);
236
237        tcg_temp_free_i64(rs2);
238    }
239    tcg_temp_free_i64(rs1);
240
241    mark_fs_dirty(ctx);
242    return true;
243}
244
245static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a)
246{
247    REQUIRE_FPU;
248    REQUIRE_EXT(ctx, RVF);
249
250    gen_helper_fmin_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
251                      cpu_fpr[a->rs2]);
252    mark_fs_dirty(ctx);
253    return true;
254}
255
256static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a)
257{
258    REQUIRE_FPU;
259    REQUIRE_EXT(ctx, RVF);
260
261    gen_helper_fmax_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
262                      cpu_fpr[a->rs2]);
263    mark_fs_dirty(ctx);
264    return true;
265}
266
267static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a)
268{
269    REQUIRE_FPU;
270    REQUIRE_EXT(ctx, RVF);
271
272    TCGv dest = dest_gpr(ctx, a->rd);
273
274    gen_set_rm(ctx, a->rm);
275    gen_helper_fcvt_w_s(dest, cpu_env, cpu_fpr[a->rs1]);
276    gen_set_gpr(ctx, a->rd, dest);
277    return true;
278}
279
280static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a)
281{
282    REQUIRE_FPU;
283    REQUIRE_EXT(ctx, RVF);
284
285    TCGv dest = dest_gpr(ctx, a->rd);
286
287    gen_set_rm(ctx, a->rm);
288    gen_helper_fcvt_wu_s(dest, cpu_env, cpu_fpr[a->rs1]);
289    gen_set_gpr(ctx, a->rd, dest);
290    return true;
291}
292
293static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a)
294{
295    /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */
296    REQUIRE_FPU;
297    REQUIRE_EXT(ctx, RVF);
298
299    TCGv dest = dest_gpr(ctx, a->rd);
300
301#if defined(TARGET_RISCV64)
302    tcg_gen_ext32s_tl(dest, cpu_fpr[a->rs1]);
303#else
304    tcg_gen_extrl_i64_i32(dest, cpu_fpr[a->rs1]);
305#endif
306
307    gen_set_gpr(ctx, a->rd, dest);
308    return true;
309}
310
311static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a)
312{
313    REQUIRE_FPU;
314    REQUIRE_EXT(ctx, RVF);
315
316    TCGv dest = dest_gpr(ctx, a->rd);
317
318    gen_helper_feq_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
319    gen_set_gpr(ctx, a->rd, dest);
320    return true;
321}
322
323static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a)
324{
325    REQUIRE_FPU;
326    REQUIRE_EXT(ctx, RVF);
327
328    TCGv dest = dest_gpr(ctx, a->rd);
329
330    gen_helper_flt_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
331    gen_set_gpr(ctx, a->rd, dest);
332    return true;
333}
334
335static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a)
336{
337    REQUIRE_FPU;
338    REQUIRE_EXT(ctx, RVF);
339
340    TCGv dest = dest_gpr(ctx, a->rd);
341
342    gen_helper_fle_s(dest, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
343    gen_set_gpr(ctx, a->rd, dest);
344    return true;
345}
346
347static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a)
348{
349    REQUIRE_FPU;
350    REQUIRE_EXT(ctx, RVF);
351
352    TCGv dest = dest_gpr(ctx, a->rd);
353
354    gen_helper_fclass_s(dest, cpu_fpr[a->rs1]);
355    gen_set_gpr(ctx, a->rd, dest);
356    return true;
357}
358
359static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a)
360{
361    REQUIRE_FPU;
362    REQUIRE_EXT(ctx, RVF);
363
364    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
365
366    gen_set_rm(ctx, a->rm);
367    gen_helper_fcvt_s_w(cpu_fpr[a->rd], cpu_env, src);
368
369    mark_fs_dirty(ctx);
370    return true;
371}
372
373static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a)
374{
375    REQUIRE_FPU;
376    REQUIRE_EXT(ctx, RVF);
377
378    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
379
380    gen_set_rm(ctx, a->rm);
381    gen_helper_fcvt_s_wu(cpu_fpr[a->rd], cpu_env, src);
382
383    mark_fs_dirty(ctx);
384    return true;
385}
386
387static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a)
388{
389    /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */
390    REQUIRE_FPU;
391    REQUIRE_EXT(ctx, RVF);
392
393    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
394
395    tcg_gen_extu_tl_i64(cpu_fpr[a->rd], src);
396    gen_nanbox_s(cpu_fpr[a->rd], cpu_fpr[a->rd]);
397
398    mark_fs_dirty(ctx);
399    return true;
400}
401
402static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a)
403{
404    REQUIRE_64BIT(ctx);
405    REQUIRE_FPU;
406    REQUIRE_EXT(ctx, RVF);
407
408    TCGv dest = dest_gpr(ctx, a->rd);
409
410    gen_set_rm(ctx, a->rm);
411    gen_helper_fcvt_l_s(dest, cpu_env, cpu_fpr[a->rs1]);
412    gen_set_gpr(ctx, a->rd, dest);
413    return true;
414}
415
416static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a)
417{
418    REQUIRE_64BIT(ctx);
419    REQUIRE_FPU;
420    REQUIRE_EXT(ctx, RVF);
421
422    TCGv dest = dest_gpr(ctx, a->rd);
423
424    gen_set_rm(ctx, a->rm);
425    gen_helper_fcvt_lu_s(dest, cpu_env, cpu_fpr[a->rs1]);
426    gen_set_gpr(ctx, a->rd, dest);
427    return true;
428}
429
430static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a)
431{
432    REQUIRE_64BIT(ctx);
433    REQUIRE_FPU;
434    REQUIRE_EXT(ctx, RVF);
435
436    TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
437
438    gen_set_rm(ctx, a->rm);
439    gen_helper_fcvt_s_l(cpu_fpr[a->rd], cpu_env, src);
440
441    mark_fs_dirty(ctx);
442    return true;
443}
444
445static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a)
446{
447    REQUIRE_64BIT(ctx);
448    REQUIRE_FPU;
449    REQUIRE_EXT(ctx, RVF);
450
451    TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
452
453    gen_set_rm(ctx, a->rm);
454    gen_helper_fcvt_s_lu(cpu_fpr[a->rd], cpu_env, src);
455
456    mark_fs_dirty(ctx);
457    return true;
458}
459