1 | /* |
2 | * RISC-V FPU Emulation Helpers for QEMU. |
3 | * |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu |
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 | #include "qemu/osdep.h" |
20 | #include "cpu.h" |
21 | #include "qemu/host-utils.h" |
22 | #include "exec/exec-all.h" |
23 | #include "exec/helper-proto.h" |
24 | #include "fpu/softfloat.h" |
25 | |
26 | target_ulong riscv_cpu_get_fflags(CPURISCVState *env) |
27 | { |
28 | int soft = get_float_exception_flags(&env->fp_status); |
29 | target_ulong hard = 0; |
30 | |
31 | hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0; |
32 | hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0; |
33 | hard |= (soft & float_flag_overflow) ? FPEXC_OF : 0; |
34 | hard |= (soft & float_flag_divbyzero) ? FPEXC_DZ : 0; |
35 | hard |= (soft & float_flag_invalid) ? FPEXC_NV : 0; |
36 | |
37 | return hard; |
38 | } |
39 | |
40 | void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) |
41 | { |
42 | int soft = 0; |
43 | |
44 | soft |= (hard & FPEXC_NX) ? float_flag_inexact : 0; |
45 | soft |= (hard & FPEXC_UF) ? float_flag_underflow : 0; |
46 | soft |= (hard & FPEXC_OF) ? float_flag_overflow : 0; |
47 | soft |= (hard & FPEXC_DZ) ? float_flag_divbyzero : 0; |
48 | soft |= (hard & FPEXC_NV) ? float_flag_invalid : 0; |
49 | |
50 | set_float_exception_flags(soft, &env->fp_status); |
51 | } |
52 | |
53 | void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) |
54 | { |
55 | int softrm; |
56 | |
57 | if (rm == 7) { |
58 | rm = env->frm; |
59 | } |
60 | switch (rm) { |
61 | case 0: |
62 | softrm = float_round_nearest_even; |
63 | break; |
64 | case 1: |
65 | softrm = float_round_to_zero; |
66 | break; |
67 | case 2: |
68 | softrm = float_round_down; |
69 | break; |
70 | case 3: |
71 | softrm = float_round_up; |
72 | break; |
73 | case 4: |
74 | softrm = float_round_ties_away; |
75 | break; |
76 | default: |
77 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
78 | } |
79 | |
80 | set_float_rounding_mode(softrm, &env->fp_status); |
81 | } |
82 | |
83 | uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
84 | uint64_t frs3) |
85 | { |
86 | return float32_muladd(frs1, frs2, frs3, 0, &env->fp_status); |
87 | } |
88 | |
89 | uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
90 | uint64_t frs3) |
91 | { |
92 | return float64_muladd(frs1, frs2, frs3, 0, &env->fp_status); |
93 | } |
94 | |
95 | uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
96 | uint64_t frs3) |
97 | { |
98 | return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c, |
99 | &env->fp_status); |
100 | } |
101 | |
102 | uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
103 | uint64_t frs3) |
104 | { |
105 | return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c, |
106 | &env->fp_status); |
107 | } |
108 | |
109 | uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
110 | uint64_t frs3) |
111 | { |
112 | return float32_muladd(frs1, frs2, frs3, float_muladd_negate_product, |
113 | &env->fp_status); |
114 | } |
115 | |
116 | uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
117 | uint64_t frs3) |
118 | { |
119 | return float64_muladd(frs1, frs2, frs3, float_muladd_negate_product, |
120 | &env->fp_status); |
121 | } |
122 | |
123 | uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
124 | uint64_t frs3) |
125 | { |
126 | return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c | |
127 | float_muladd_negate_product, &env->fp_status); |
128 | } |
129 | |
130 | uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, |
131 | uint64_t frs3) |
132 | { |
133 | return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c | |
134 | float_muladd_negate_product, &env->fp_status); |
135 | } |
136 | |
137 | uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
138 | { |
139 | return float32_add(frs1, frs2, &env->fp_status); |
140 | } |
141 | |
142 | uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
143 | { |
144 | return float32_sub(frs1, frs2, &env->fp_status); |
145 | } |
146 | |
147 | uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
148 | { |
149 | return float32_mul(frs1, frs2, &env->fp_status); |
150 | } |
151 | |
152 | uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
153 | { |
154 | return float32_div(frs1, frs2, &env->fp_status); |
155 | } |
156 | |
157 | uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
158 | { |
159 | return float32_minnum(frs1, frs2, &env->fp_status); |
160 | } |
161 | |
162 | uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
163 | { |
164 | return float32_maxnum(frs1, frs2, &env->fp_status); |
165 | } |
166 | |
167 | uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1) |
168 | { |
169 | return float32_sqrt(frs1, &env->fp_status); |
170 | } |
171 | |
172 | target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
173 | { |
174 | return float32_le(frs1, frs2, &env->fp_status); |
175 | } |
176 | |
177 | target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
178 | { |
179 | return float32_lt(frs1, frs2, &env->fp_status); |
180 | } |
181 | |
182 | target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
183 | { |
184 | return float32_eq_quiet(frs1, frs2, &env->fp_status); |
185 | } |
186 | |
187 | target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1) |
188 | { |
189 | return float32_to_int32(frs1, &env->fp_status); |
190 | } |
191 | |
192 | target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1) |
193 | { |
194 | return (int32_t)float32_to_uint32(frs1, &env->fp_status); |
195 | } |
196 | |
197 | #if defined(TARGET_RISCV64) |
198 | uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1) |
199 | { |
200 | return float32_to_int64(frs1, &env->fp_status); |
201 | } |
202 | |
203 | uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1) |
204 | { |
205 | return float32_to_uint64(frs1, &env->fp_status); |
206 | } |
207 | #endif |
208 | |
209 | uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1) |
210 | { |
211 | return int32_to_float32((int32_t)rs1, &env->fp_status); |
212 | } |
213 | |
214 | uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1) |
215 | { |
216 | return uint32_to_float32((uint32_t)rs1, &env->fp_status); |
217 | } |
218 | |
219 | #if defined(TARGET_RISCV64) |
220 | uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1) |
221 | { |
222 | return int64_to_float32(rs1, &env->fp_status); |
223 | } |
224 | |
225 | uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1) |
226 | { |
227 | return uint64_to_float32(rs1, &env->fp_status); |
228 | } |
229 | #endif |
230 | |
231 | target_ulong helper_fclass_s(uint64_t frs1) |
232 | { |
233 | float32 f = frs1; |
234 | bool sign = float32_is_neg(f); |
235 | |
236 | if (float32_is_infinity(f)) { |
237 | return sign ? 1 << 0 : 1 << 7; |
238 | } else if (float32_is_zero(f)) { |
239 | return sign ? 1 << 3 : 1 << 4; |
240 | } else if (float32_is_zero_or_denormal(f)) { |
241 | return sign ? 1 << 2 : 1 << 5; |
242 | } else if (float32_is_any_nan(f)) { |
243 | float_status s = { }; /* for snan_bit_is_one */ |
244 | return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; |
245 | } else { |
246 | return sign ? 1 << 1 : 1 << 6; |
247 | } |
248 | } |
249 | |
250 | uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
251 | { |
252 | return float64_add(frs1, frs2, &env->fp_status); |
253 | } |
254 | |
255 | uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
256 | { |
257 | return float64_sub(frs1, frs2, &env->fp_status); |
258 | } |
259 | |
260 | uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
261 | { |
262 | return float64_mul(frs1, frs2, &env->fp_status); |
263 | } |
264 | |
265 | uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
266 | { |
267 | return float64_div(frs1, frs2, &env->fp_status); |
268 | } |
269 | |
270 | uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
271 | { |
272 | return float64_minnum(frs1, frs2, &env->fp_status); |
273 | } |
274 | |
275 | uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
276 | { |
277 | return float64_maxnum(frs1, frs2, &env->fp_status); |
278 | } |
279 | |
280 | uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) |
281 | { |
282 | return float64_to_float32(rs1, &env->fp_status); |
283 | } |
284 | |
285 | uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1) |
286 | { |
287 | return float32_to_float64(rs1, &env->fp_status); |
288 | } |
289 | |
290 | uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1) |
291 | { |
292 | return float64_sqrt(frs1, &env->fp_status); |
293 | } |
294 | |
295 | target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
296 | { |
297 | return float64_le(frs1, frs2, &env->fp_status); |
298 | } |
299 | |
300 | target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
301 | { |
302 | return float64_lt(frs1, frs2, &env->fp_status); |
303 | } |
304 | |
305 | target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) |
306 | { |
307 | return float64_eq_quiet(frs1, frs2, &env->fp_status); |
308 | } |
309 | |
310 | target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1) |
311 | { |
312 | return float64_to_int32(frs1, &env->fp_status); |
313 | } |
314 | |
315 | target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1) |
316 | { |
317 | return (int32_t)float64_to_uint32(frs1, &env->fp_status); |
318 | } |
319 | |
320 | #if defined(TARGET_RISCV64) |
321 | uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1) |
322 | { |
323 | return float64_to_int64(frs1, &env->fp_status); |
324 | } |
325 | |
326 | uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1) |
327 | { |
328 | return float64_to_uint64(frs1, &env->fp_status); |
329 | } |
330 | #endif |
331 | |
332 | uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1) |
333 | { |
334 | return int32_to_float64((int32_t)rs1, &env->fp_status); |
335 | } |
336 | |
337 | uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1) |
338 | { |
339 | return uint32_to_float64((uint32_t)rs1, &env->fp_status); |
340 | } |
341 | |
342 | #if defined(TARGET_RISCV64) |
343 | uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1) |
344 | { |
345 | return int64_to_float64(rs1, &env->fp_status); |
346 | } |
347 | |
348 | uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1) |
349 | { |
350 | return uint64_to_float64(rs1, &env->fp_status); |
351 | } |
352 | #endif |
353 | |
354 | target_ulong helper_fclass_d(uint64_t frs1) |
355 | { |
356 | float64 f = frs1; |
357 | bool sign = float64_is_neg(f); |
358 | |
359 | if (float64_is_infinity(f)) { |
360 | return sign ? 1 << 0 : 1 << 7; |
361 | } else if (float64_is_zero(f)) { |
362 | return sign ? 1 << 3 : 1 << 4; |
363 | } else if (float64_is_zero_or_denormal(f)) { |
364 | return sign ? 1 << 2 : 1 << 5; |
365 | } else if (float64_is_any_nan(f)) { |
366 | float_status s = { }; /* for snan_bit_is_one */ |
367 | return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; |
368 | } else { |
369 | return sign ? 1 << 1 : 1 << 6; |
370 | } |
371 | } |
372 | |