fpu_helper.c (14605B)
1/* 2 * TriCore emulation for qemu: fpu helper. 3 * 4 * Copyright (c) 2016 Bastian Koppelmann University of Paderborn 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "qemu/osdep.h" 21#include "cpu.h" 22#include "exec/helper-proto.h" 23#include "fpu/softfloat.h" 24 25#define QUIET_NAN 0x7fc00000 26#define ADD_NAN 0x7fc00001 27#define SQRT_NAN 0x7fc00004 28#define DIV_NAN 0x7fc00008 29#define MUL_NAN 0x7fc00002 30#define FPU_FS PSW_USB_C 31#define FPU_FI PSW_USB_V 32#define FPU_FV PSW_USB_SV 33#define FPU_FZ PSW_USB_AV 34#define FPU_FU PSW_USB_SAV 35 36#define float32_sqrt_nan make_float32(SQRT_NAN) 37#define float32_quiet_nan make_float32(QUIET_NAN) 38 39/* we don't care about input_denormal */ 40static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) 41{ 42 return get_float_exception_flags(&env->fp_status) 43 & (float_flag_invalid 44 | float_flag_overflow 45 | float_flag_underflow 46 | float_flag_output_denormal 47 | float_flag_divbyzero 48 | float_flag_inexact); 49} 50 51static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2, 52 float32 arg3, float32 result, 53 uint32_t muladd_negate_c) 54{ 55 uint32_t aSign, bSign, cSign; 56 uint32_t aExp, bExp, cExp; 57 58 if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) || 59 float32_is_any_nan(arg3)) { 60 return QUIET_NAN; 61 } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) { 62 return MUL_NAN; 63 } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) { 64 return MUL_NAN; 65 } else { 66 aSign = arg1 >> 31; 67 bSign = arg2 >> 31; 68 cSign = arg3 >> 31; 69 70 aExp = (arg1 >> 23) & 0xff; 71 bExp = (arg2 >> 23) & 0xff; 72 cExp = (arg3 >> 23) & 0xff; 73 74 if (muladd_negate_c) { 75 cSign ^= 1; 76 } 77 if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) { 78 if (aSign ^ bSign ^ cSign) { 79 return ADD_NAN; 80 } 81 } 82 } 83 84 return result; 85} 86 87static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) 88{ 89 uint8_t some_excp = 0; 90 set_float_exception_flags(0, &env->fp_status); 91 92 if (flags & float_flag_invalid) { 93 env->FPU_FI = 1 << 31; 94 some_excp = 1; 95 } 96 97 if (flags & float_flag_overflow) { 98 env->FPU_FV = 1 << 31; 99 some_excp = 1; 100 } 101 102 if (flags & float_flag_underflow || flags & float_flag_output_denormal) { 103 env->FPU_FU = 1 << 31; 104 some_excp = 1; 105 } 106 107 if (flags & float_flag_divbyzero) { 108 env->FPU_FZ = 1 << 31; 109 some_excp = 1; 110 } 111 112 if (flags & float_flag_inexact || flags & float_flag_output_denormal) { 113 env->PSW |= 1 << 26; 114 some_excp = 1; 115 } 116 117 env->FPU_FS = some_excp; 118} 119 120#define FADD_SUB(op) \ 121uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ 122{ \ 123 float32 arg1 = make_float32(r1); \ 124 float32 arg2 = make_float32(r2); \ 125 uint32_t flags; \ 126 float32 f_result; \ 127 \ 128 f_result = float32_##op(arg2, arg1, &env->fp_status); \ 129 flags = f_get_excp_flags(env); \ 130 if (flags) { \ 131 /* If the output is a NaN, but the inputs aren't, \ 132 we return a unique value. */ \ 133 if ((flags & float_flag_invalid) \ 134 && !float32_is_any_nan(arg1) \ 135 && !float32_is_any_nan(arg2)) { \ 136 f_result = ADD_NAN; \ 137 } \ 138 f_update_psw_flags(env, flags); \ 139 } else { \ 140 env->FPU_FS = 0; \ 141 } \ 142 return (uint32_t)f_result; \ 143} 144FADD_SUB(add) 145FADD_SUB(sub) 146 147uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 148{ 149 uint32_t flags; 150 float32 arg1 = make_float32(r1); 151 float32 arg2 = make_float32(r2); 152 float32 f_result; 153 154 f_result = float32_mul(arg1, arg2, &env->fp_status); 155 156 flags = f_get_excp_flags(env); 157 if (flags) { 158 /* If the output is a NaN, but the inputs aren't, 159 we return a unique value. */ 160 if ((flags & float_flag_invalid) 161 && !float32_is_any_nan(arg1) 162 && !float32_is_any_nan(arg2)) { 163 f_result = MUL_NAN; 164 } 165 f_update_psw_flags(env, flags); 166 } else { 167 env->FPU_FS = 0; 168 } 169 return (uint32_t)f_result; 170 171} 172 173/* 174 * Target TriCore QSEED.F significand Lookup Table 175 * 176 * The QSEED.F output significand depends on the least-significant 177 * exponent bit and the 6 most-significant significand bits. 178 * 179 * IEEE 754 float datatype 180 * partitioned into Sign (S), Exponent (E) and Significand (M): 181 * 182 * S E E E E E E E E M M M M M M ... 183 * | | | 184 * +------+------+-------+-------+ 185 * | | 186 * for lookup table 187 * calculating index for 188 * output E output M 189 * 190 * This lookup table was extracted by analyzing QSEED output 191 * from the real hardware 192 */ 193static const uint8_t target_qseed_significand_table[128] = { 194 253, 252, 245, 244, 239, 238, 231, 230, 225, 224, 217, 216, 195 211, 210, 205, 204, 201, 200, 195, 194, 189, 188, 185, 184, 196 179, 178, 175, 174, 169, 168, 165, 164, 161, 160, 157, 156, 197 153, 152, 149, 148, 145, 144, 141, 140, 137, 136, 133, 132, 198 131, 130, 127, 126, 123, 122, 121, 120, 117, 116, 115, 114, 199 111, 110, 109, 108, 103, 102, 99, 98, 93, 92, 89, 88, 83, 200 82, 79, 78, 75, 74, 71, 70, 67, 66, 63, 62, 59, 58, 55, 201 54, 53, 52, 49, 48, 45, 44, 43, 42, 39, 38, 37, 36, 33, 202 32, 31, 30, 27, 26, 25, 24, 23, 22, 19, 18, 17, 16, 15, 203 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 204}; 205 206uint32_t helper_qseed(CPUTriCoreState *env, uint32_t r1) 207{ 208 uint32_t arg1, S, E, M, E_minus_one, m_idx; 209 uint32_t new_E, new_M, new_S, result; 210 211 arg1 = make_float32(r1); 212 213 /* fetch IEEE-754 fields S, E and the uppermost 6-bit of M */ 214 S = extract32(arg1, 31, 1); 215 E = extract32(arg1, 23, 8); 216 M = extract32(arg1, 17, 6); 217 218 if (float32_is_any_nan(arg1)) { 219 result = float32_quiet_nan; 220 } else if (float32_is_zero_or_denormal(arg1)) { 221 if (float32_is_neg(arg1)) { 222 result = float32_infinity | (1 << 31); 223 } else { 224 result = float32_infinity; 225 } 226 } else if (float32_is_neg(arg1)) { 227 result = float32_sqrt_nan; 228 } else if (float32_is_infinity(arg1)) { 229 result = float32_zero; 230 } else { 231 E_minus_one = E - 1; 232 m_idx = ((E_minus_one & 1) << 6) | M; 233 new_S = S; 234 new_E = 0xBD - E_minus_one / 2; 235 new_M = target_qseed_significand_table[m_idx]; 236 237 result = 0; 238 result = deposit32(result, 31, 1, new_S); 239 result = deposit32(result, 23, 8, new_E); 240 result = deposit32(result, 15, 8, new_M); 241 } 242 243 if (float32_is_signaling_nan(arg1, &env->fp_status) 244 || result == float32_sqrt_nan) { 245 env->FPU_FI = 1 << 31; 246 env->FPU_FS = 1; 247 } else { 248 env->FPU_FS = 0; 249 } 250 251 return (uint32_t) result; 252} 253 254uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 255{ 256 uint32_t flags; 257 float32 arg1 = make_float32(r1); 258 float32 arg2 = make_float32(r2); 259 float32 f_result; 260 261 f_result = float32_div(arg1, arg2 , &env->fp_status); 262 263 flags = f_get_excp_flags(env); 264 if (flags) { 265 /* If the output is a NaN, but the inputs aren't, 266 we return a unique value. */ 267 if ((flags & float_flag_invalid) 268 && !float32_is_any_nan(arg1) 269 && !float32_is_any_nan(arg2)) { 270 f_result = DIV_NAN; 271 } 272 f_update_psw_flags(env, flags); 273 } else { 274 env->FPU_FS = 0; 275 } 276 277 return (uint32_t)f_result; 278} 279 280uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1, 281 uint32_t r2, uint32_t r3) 282{ 283 uint32_t flags; 284 float32 arg1 = make_float32(r1); 285 float32 arg2 = make_float32(r2); 286 float32 arg3 = make_float32(r3); 287 float32 f_result; 288 289 f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status); 290 291 flags = f_get_excp_flags(env); 292 if (flags) { 293 if (flags & float_flag_invalid) { 294 arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 295 arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 296 arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 297 f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0); 298 } 299 f_update_psw_flags(env, flags); 300 } else { 301 env->FPU_FS = 0; 302 } 303 return (uint32_t)f_result; 304} 305 306uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1, 307 uint32_t r2, uint32_t r3) 308{ 309 uint32_t flags; 310 float32 arg1 = make_float32(r1); 311 float32 arg2 = make_float32(r2); 312 float32 arg3 = make_float32(r3); 313 float32 f_result; 314 315 f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product, 316 &env->fp_status); 317 318 flags = f_get_excp_flags(env); 319 if (flags) { 320 if (flags & float_flag_invalid) { 321 arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 322 arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 323 arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 324 325 f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1); 326 } 327 f_update_psw_flags(env, flags); 328 } else { 329 env->FPU_FS = 0; 330 } 331 return (uint32_t)f_result; 332} 333 334uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 335{ 336 uint32_t result, flags; 337 float32 arg1 = make_float32(r1); 338 float32 arg2 = make_float32(r2); 339 340 set_flush_inputs_to_zero(0, &env->fp_status); 341 342 result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); 343 result |= float32_is_denormal(arg1) << 4; 344 result |= float32_is_denormal(arg2) << 5; 345 346 flags = f_get_excp_flags(env); 347 if (flags) { 348 f_update_psw_flags(env, flags); 349 } else { 350 env->FPU_FS = 0; 351 } 352 353 set_flush_inputs_to_zero(1, &env->fp_status); 354 return result; 355} 356 357uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) 358{ 359 float32 f_arg = make_float32(arg); 360 int32_t result, flags; 361 362 result = float32_to_int32(f_arg, &env->fp_status); 363 364 flags = f_get_excp_flags(env); 365 if (flags) { 366 if (float32_is_any_nan(f_arg)) { 367 result = 0; 368 } 369 f_update_psw_flags(env, flags); 370 } else { 371 env->FPU_FS = 0; 372 } 373 return (uint32_t)result; 374} 375 376uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) 377{ 378 float32 f_result; 379 uint32_t flags; 380 f_result = int32_to_float32(arg, &env->fp_status); 381 382 flags = f_get_excp_flags(env); 383 if (flags) { 384 f_update_psw_flags(env, flags); 385 } else { 386 env->FPU_FS = 0; 387 } 388 return (uint32_t)f_result; 389} 390 391uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg) 392{ 393 float32 f_result; 394 uint32_t flags; 395 396 f_result = uint32_to_float32(arg, &env->fp_status); 397 398 flags = f_get_excp_flags(env); 399 if (flags) { 400 f_update_psw_flags(env, flags); 401 } else { 402 env->FPU_FS = 0; 403 } 404 return (uint32_t)f_result; 405} 406 407uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg) 408{ 409 float32 f_arg = make_float32(arg); 410 uint32_t result; 411 int32_t flags; 412 413 result = float32_to_int32_round_to_zero(f_arg, &env->fp_status); 414 415 flags = f_get_excp_flags(env); 416 if (flags & float_flag_invalid) { 417 flags &= ~float_flag_inexact; 418 if (float32_is_any_nan(f_arg)) { 419 result = 0; 420 } 421 } 422 423 if (flags) { 424 f_update_psw_flags(env, flags); 425 } else { 426 env->FPU_FS = 0; 427 } 428 429 return result; 430} 431 432uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) 433{ 434 float32 f_arg = make_float32(arg); 435 uint32_t result; 436 int32_t flags; 437 438 result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); 439 440 flags = f_get_excp_flags(env); 441 if (flags & float_flag_invalid) { 442 flags &= ~float_flag_inexact; 443 if (float32_is_any_nan(f_arg)) { 444 result = 0; 445 } 446 } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { 447 flags = float_flag_invalid; 448 result = 0; 449 } 450 451 if (flags) { 452 f_update_psw_flags(env, flags); 453 } else { 454 env->FPU_FS = 0; 455 } 456 return result; 457} 458 459void helper_updfl(CPUTriCoreState *env, uint32_t arg) 460{ 461 env->FPU_FS = extract32(arg, 7, 1) & extract32(arg, 15, 1); 462 env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31; 463 env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31; 464 env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31; 465 env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31; 466 /* clear FX and RM */ 467 env->PSW &= ~(extract32(arg, 10, 1) << 26); 468 env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26; 469 470 fpu_set_state(env); 471}