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
21static bool trans_fld(DisasContext *ctx, arg_fld *a)
22{
23    REQUIRE_FPU;
24    REQUIRE_EXT(ctx, RVD);
25    TCGv t0 = tcg_temp_new();
26    gen_get_gpr(t0, a->rs1);
27    tcg_gen_addi_tl(t0, t0, a->imm);
28
29    tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEQ);
30
31    mark_fs_dirty(ctx);
32    tcg_temp_free(t0);
33    return true;
34}
35
36static bool trans_fsd(DisasContext *ctx, arg_fsd *a)
37{
38    REQUIRE_FPU;
39    REQUIRE_EXT(ctx, RVD);
40    TCGv t0 = tcg_temp_new();
41    gen_get_gpr(t0, a->rs1);
42    tcg_gen_addi_tl(t0, t0, a->imm);
43
44    tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEQ);
45
46    tcg_temp_free(t0);
47    return true;
48}
49
50static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a)
51{
52    REQUIRE_FPU;
53    REQUIRE_EXT(ctx, RVD);
54    gen_set_rm(ctx, a->rm);
55    gen_helper_fmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
56                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
57    mark_fs_dirty(ctx);
58    return true;
59}
60
61static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a)
62{
63    REQUIRE_FPU;
64    REQUIRE_EXT(ctx, RVD);
65    gen_set_rm(ctx, a->rm);
66    gen_helper_fmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
67                       cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
68    mark_fs_dirty(ctx);
69    return true;
70}
71
72static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a)
73{
74    REQUIRE_FPU;
75    REQUIRE_EXT(ctx, RVD);
76    gen_set_rm(ctx, a->rm);
77    gen_helper_fnmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
78                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
79    mark_fs_dirty(ctx);
80    return true;
81}
82
83static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a)
84{
85    REQUIRE_FPU;
86    REQUIRE_EXT(ctx, RVD);
87    gen_set_rm(ctx, a->rm);
88    gen_helper_fnmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1],
89                        cpu_fpr[a->rs2], cpu_fpr[a->rs3]);
90    mark_fs_dirty(ctx);
91    return true;
92}
93
94static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a)
95{
96    REQUIRE_FPU;
97    REQUIRE_EXT(ctx, RVD);
98
99    gen_set_rm(ctx, a->rm);
100    gen_helper_fadd_d(cpu_fpr[a->rd], cpu_env,
101                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
102
103    mark_fs_dirty(ctx);
104    return true;
105}
106
107static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a)
108{
109    REQUIRE_FPU;
110    REQUIRE_EXT(ctx, RVD);
111
112    gen_set_rm(ctx, a->rm);
113    gen_helper_fsub_d(cpu_fpr[a->rd], cpu_env,
114                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
115
116    mark_fs_dirty(ctx);
117    return true;
118}
119
120static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a)
121{
122    REQUIRE_FPU;
123    REQUIRE_EXT(ctx, RVD);
124
125    gen_set_rm(ctx, a->rm);
126    gen_helper_fmul_d(cpu_fpr[a->rd], cpu_env,
127                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
128
129    mark_fs_dirty(ctx);
130    return true;
131}
132
133static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a)
134{
135    REQUIRE_FPU;
136    REQUIRE_EXT(ctx, RVD);
137
138    gen_set_rm(ctx, a->rm);
139    gen_helper_fdiv_d(cpu_fpr[a->rd], cpu_env,
140                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
141
142    mark_fs_dirty(ctx);
143    return true;
144}
145
146static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a)
147{
148    REQUIRE_FPU;
149    REQUIRE_EXT(ctx, RVD);
150
151    gen_set_rm(ctx, a->rm);
152    gen_helper_fsqrt_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
153
154    mark_fs_dirty(ctx);
155    return true;
156}
157
158static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a)
159{
160    if (a->rs1 == a->rs2) { /* FMOV */
161        tcg_gen_mov_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1]);
162    } else {
163        tcg_gen_deposit_i64(cpu_fpr[a->rd], cpu_fpr[a->rs2],
164                            cpu_fpr[a->rs1], 0, 63);
165    }
166    mark_fs_dirty(ctx);
167    return true;
168}
169
170static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a)
171{
172    REQUIRE_FPU;
173    REQUIRE_EXT(ctx, RVD);
174    if (a->rs1 == a->rs2) { /* FNEG */
175        tcg_gen_xori_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], INT64_MIN);
176    } else {
177        TCGv_i64 t0 = tcg_temp_new_i64();
178        tcg_gen_not_i64(t0, cpu_fpr[a->rs2]);
179        tcg_gen_deposit_i64(cpu_fpr[a->rd], t0, cpu_fpr[a->rs1], 0, 63);
180        tcg_temp_free_i64(t0);
181    }
182    mark_fs_dirty(ctx);
183    return true;
184}
185
186static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a)
187{
188    REQUIRE_FPU;
189    REQUIRE_EXT(ctx, RVD);
190    if (a->rs1 == a->rs2) { /* FABS */
191        tcg_gen_andi_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], ~INT64_MIN);
192    } else {
193        TCGv_i64 t0 = tcg_temp_new_i64();
194        tcg_gen_andi_i64(t0, cpu_fpr[a->rs2], INT64_MIN);
195        tcg_gen_xor_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], t0);
196        tcg_temp_free_i64(t0);
197    }
198    mark_fs_dirty(ctx);
199    return true;
200}
201
202static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a)
203{
204    REQUIRE_FPU;
205    REQUIRE_EXT(ctx, RVD);
206
207    gen_helper_fmin_d(cpu_fpr[a->rd], cpu_env,
208                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
209
210    mark_fs_dirty(ctx);
211    return true;
212}
213
214static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a)
215{
216    REQUIRE_FPU;
217    REQUIRE_EXT(ctx, RVD);
218
219    gen_helper_fmax_d(cpu_fpr[a->rd], cpu_env,
220                      cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
221
222    mark_fs_dirty(ctx);
223    return true;
224}
225
226static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a)
227{
228    REQUIRE_FPU;
229    REQUIRE_EXT(ctx, RVD);
230
231    gen_set_rm(ctx, a->rm);
232    gen_helper_fcvt_s_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
233
234    mark_fs_dirty(ctx);
235    return true;
236}
237
238static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a)
239{
240    REQUIRE_FPU;
241    REQUIRE_EXT(ctx, RVD);
242
243    gen_set_rm(ctx, a->rm);
244    gen_helper_fcvt_d_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]);
245
246    mark_fs_dirty(ctx);
247    return true;
248}
249
250static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a)
251{
252    REQUIRE_FPU;
253    REQUIRE_EXT(ctx, RVD);
254
255    TCGv t0 = tcg_temp_new();
256    gen_helper_feq_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
257    gen_set_gpr(a->rd, t0);
258    tcg_temp_free(t0);
259
260    return true;
261}
262
263static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a)
264{
265    REQUIRE_FPU;
266    REQUIRE_EXT(ctx, RVD);
267
268    TCGv t0 = tcg_temp_new();
269    gen_helper_flt_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
270    gen_set_gpr(a->rd, t0);
271    tcg_temp_free(t0);
272
273    return true;
274}
275
276static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a)
277{
278    REQUIRE_FPU;
279    REQUIRE_EXT(ctx, RVD);
280
281    TCGv t0 = tcg_temp_new();
282    gen_helper_fle_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]);
283    gen_set_gpr(a->rd, t0);
284    tcg_temp_free(t0);
285
286    return true;
287}
288
289static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a)
290{
291    REQUIRE_FPU;
292    REQUIRE_EXT(ctx, RVD);
293
294    TCGv t0 = tcg_temp_new();
295    gen_helper_fclass_d(t0, cpu_fpr[a->rs1]);
296    gen_set_gpr(a->rd, t0);
297    tcg_temp_free(t0);
298    return true;
299}
300
301static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a)
302{
303    REQUIRE_FPU;
304    REQUIRE_EXT(ctx, RVD);
305
306    TCGv t0 = tcg_temp_new();
307    gen_set_rm(ctx, a->rm);
308    gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[a->rs1]);
309    gen_set_gpr(a->rd, t0);
310    tcg_temp_free(t0);
311
312    return true;
313}
314
315static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a)
316{
317    REQUIRE_FPU;
318    REQUIRE_EXT(ctx, RVD);
319
320    TCGv t0 = tcg_temp_new();
321    gen_set_rm(ctx, a->rm);
322    gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[a->rs1]);
323    gen_set_gpr(a->rd, t0);
324    tcg_temp_free(t0);
325
326    return true;
327}
328
329static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a)
330{
331    REQUIRE_FPU;
332    REQUIRE_EXT(ctx, RVD);
333
334    TCGv t0 = tcg_temp_new();
335    gen_get_gpr(t0, a->rs1);
336
337    gen_set_rm(ctx, a->rm);
338    gen_helper_fcvt_d_w(cpu_fpr[a->rd], cpu_env, t0);
339    tcg_temp_free(t0);
340
341    mark_fs_dirty(ctx);
342    return true;
343}
344
345static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a)
346{
347    REQUIRE_FPU;
348    REQUIRE_EXT(ctx, RVD);
349
350    TCGv t0 = tcg_temp_new();
351    gen_get_gpr(t0, a->rs1);
352
353    gen_set_rm(ctx, a->rm);
354    gen_helper_fcvt_d_wu(cpu_fpr[a->rd], cpu_env, t0);
355    tcg_temp_free(t0);
356
357    mark_fs_dirty(ctx);
358    return true;
359}
360
361static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a)
362{
363    REQUIRE_64BIT(ctx);
364    REQUIRE_FPU;
365    REQUIRE_EXT(ctx, RVD);
366
367    TCGv t0 = tcg_temp_new();
368    gen_set_rm(ctx, a->rm);
369    gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[a->rs1]);
370    gen_set_gpr(a->rd, t0);
371    tcg_temp_free(t0);
372    return true;
373}
374
375static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a)
376{
377    REQUIRE_64BIT(ctx);
378    REQUIRE_FPU;
379    REQUIRE_EXT(ctx, RVD);
380
381    TCGv t0 = tcg_temp_new();
382    gen_set_rm(ctx, a->rm);
383    gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[a->rs1]);
384    gen_set_gpr(a->rd, t0);
385    tcg_temp_free(t0);
386    return true;
387}
388
389static bool trans_fmv_x_d(DisasContext *ctx, arg_fmv_x_d *a)
390{
391    REQUIRE_64BIT(ctx);
392    REQUIRE_FPU;
393    REQUIRE_EXT(ctx, RVD);
394
395#ifdef TARGET_RISCV64
396    gen_set_gpr(a->rd, cpu_fpr[a->rs1]);
397    return true;
398#else
399    qemu_build_not_reached();
400#endif
401}
402
403static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a)
404{
405    REQUIRE_64BIT(ctx);
406    REQUIRE_FPU;
407    REQUIRE_EXT(ctx, RVD);
408
409    TCGv t0 = tcg_temp_new();
410    gen_get_gpr(t0, a->rs1);
411
412    gen_set_rm(ctx, a->rm);
413    gen_helper_fcvt_d_l(cpu_fpr[a->rd], cpu_env, t0);
414    tcg_temp_free(t0);
415    mark_fs_dirty(ctx);
416    return true;
417}
418
419static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a)
420{
421    REQUIRE_64BIT(ctx);
422    REQUIRE_FPU;
423    REQUIRE_EXT(ctx, RVD);
424
425    TCGv t0 = tcg_temp_new();
426    gen_get_gpr(t0, a->rs1);
427
428    gen_set_rm(ctx, a->rm);
429    gen_helper_fcvt_d_lu(cpu_fpr[a->rd], cpu_env, t0);
430    tcg_temp_free(t0);
431    mark_fs_dirty(ctx);
432    return true;
433}
434
435static bool trans_fmv_d_x(DisasContext *ctx, arg_fmv_d_x *a)
436{
437    REQUIRE_64BIT(ctx);
438    REQUIRE_FPU;
439    REQUIRE_EXT(ctx, RVD);
440
441#ifdef TARGET_RISCV64
442    TCGv t0 = tcg_temp_new();
443    gen_get_gpr(t0, a->rs1);
444
445    tcg_gen_mov_tl(cpu_fpr[a->rd], t0);
446    tcg_temp_free(t0);
447    mark_fs_dirty(ctx);
448    return true;
449#else
450    qemu_build_not_reached();
451#endif
452}
453