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