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