fpu_helper.c (14669B)
1/* 2 * Helpers for floating point instructions. 3 * 4 * Copyright (c) 2007 Jocelyn Mayer 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/exec-all.h" 23#include "exec/helper-proto.h" 24#include "fpu/softfloat.h" 25 26#define FP_STATUS (env->fp_status) 27 28 29void helper_setroundmode(CPUAlphaState *env, uint32_t val) 30{ 31 set_float_rounding_mode(val, &FP_STATUS); 32} 33 34void helper_setflushzero(CPUAlphaState *env, uint32_t val) 35{ 36 set_flush_to_zero(val, &FP_STATUS); 37} 38 39#define CONVERT_BIT(X, SRC, DST) \ 40 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC)) 41 42static uint32_t soft_to_fpcr_exc(CPUAlphaState *env) 43{ 44 uint8_t exc = get_float_exception_flags(&FP_STATUS); 45 uint32_t ret = 0; 46 47 if (unlikely(exc)) { 48 set_float_exception_flags(0, &FP_STATUS); 49 ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV); 50 ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE); 51 ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF); 52 ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF); 53 ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE); 54 } 55 56 return ret; 57} 58 59static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr, 60 uint32_t exc, uint32_t regno, uint32_t hw_exc) 61{ 62 hw_exc |= CONVERT_BIT(exc, FPCR_INV, EXC_M_INV); 63 hw_exc |= CONVERT_BIT(exc, FPCR_DZE, EXC_M_DZE); 64 hw_exc |= CONVERT_BIT(exc, FPCR_OVF, EXC_M_FOV); 65 hw_exc |= CONVERT_BIT(exc, FPCR_UNF, EXC_M_UNF); 66 hw_exc |= CONVERT_BIT(exc, FPCR_INE, EXC_M_INE); 67 hw_exc |= CONVERT_BIT(exc, FPCR_IOV, EXC_M_IOV); 68 69 arith_excp(env, retaddr, hw_exc, 1ull << regno); 70} 71 72/* Raise exceptions for ieee fp insns without software completion. 73 In that case there are no exceptions that don't trap; the mask 74 doesn't apply. */ 75void helper_fp_exc_raise(CPUAlphaState *env, uint32_t ignore, uint32_t regno) 76{ 77 uint32_t exc = env->error_code; 78 if (exc) { 79 env->fpcr |= exc; 80 exc &= ~ignore; 81 if (exc) { 82 fp_exc_raise1(env, GETPC(), exc, regno, 0); 83 } 84 } 85} 86 87/* Raise exceptions for ieee fp insns with software completion. */ 88void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno) 89{ 90 uint32_t exc = env->error_code & ~ignore; 91 if (exc) { 92 env->fpcr |= exc; 93 exc &= env->fpcr_exc_enable; 94 /* 95 * In system mode, the software handler gets invoked 96 * for any non-ignored exception. 97 * In user mode, the kernel's software handler only 98 * delivers a signal if the exception is enabled. 99 */ 100#ifdef CONFIG_USER_ONLY 101 if (!exc) { 102 return; 103 } 104#endif 105 fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC); 106 } 107} 108 109/* Input handing without software completion. Trap for all 110 non-finite numbers. */ 111void helper_ieee_input(CPUAlphaState *env, uint64_t val) 112{ 113 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; 114 uint64_t frac = val & 0xfffffffffffffull; 115 116 if (exp == 0) { 117 /* Denormals without /S raise an exception. */ 118 if (frac != 0) { 119 arith_excp(env, GETPC(), EXC_M_INV, 0); 120 } 121 } else if (exp == 0x7ff) { 122 /* Infinity or NaN. */ 123 env->fpcr |= FPCR_INV; 124 arith_excp(env, GETPC(), EXC_M_INV, 0); 125 } 126} 127 128/* Similar, but does not trap for infinities. Used for comparisons. */ 129void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val) 130{ 131 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; 132 uint64_t frac = val & 0xfffffffffffffull; 133 134 if (exp == 0) { 135 /* Denormals without /S raise an exception. */ 136 if (frac != 0) { 137 arith_excp(env, GETPC(), EXC_M_INV, 0); 138 } 139 } else if (exp == 0x7ff && frac) { 140 /* NaN. */ 141 env->fpcr |= FPCR_INV; 142 arith_excp(env, GETPC(), EXC_M_INV, 0); 143 } 144} 145 146/* Input handing with software completion. Trap for denorms, unless DNZ 147 is set. If we try to support DNOD (which none of the produced hardware 148 did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set; 149 then the code downstream of that will need to cope with denorms sans 150 flush_input_to_zero. Most of it should work sanely, but there's 151 nothing to compare with. */ 152void helper_ieee_input_s(CPUAlphaState *env, uint64_t val) 153{ 154 if (unlikely(2 * val - 1 < 0x1fffffffffffffull) 155 && !env->fp_status.flush_inputs_to_zero) { 156 arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0); 157 } 158} 159 160/* S floating (single) */ 161 162/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ 163static inline uint64_t float32_to_s_int(uint32_t fi) 164{ 165 uint32_t frac = fi & 0x7fffff; 166 uint32_t sign = fi >> 31; 167 uint32_t exp_msb = (fi >> 30) & 1; 168 uint32_t exp_low = (fi >> 23) & 0x7f; 169 uint32_t exp; 170 171 exp = (exp_msb << 10) | exp_low; 172 if (exp_msb) { 173 if (exp_low == 0x7f) { 174 exp = 0x7ff; 175 } 176 } else { 177 if (exp_low != 0x00) { 178 exp |= 0x380; 179 } 180 } 181 182 return (((uint64_t)sign << 63) 183 | ((uint64_t)exp << 52) 184 | ((uint64_t)frac << 29)); 185} 186 187static inline uint64_t float32_to_s(float32 fa) 188{ 189 CPU_FloatU a; 190 a.f = fa; 191 return float32_to_s_int(a.l); 192} 193 194static inline uint32_t s_to_float32_int(uint64_t a) 195{ 196 return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); 197} 198 199static inline float32 s_to_float32(uint64_t a) 200{ 201 CPU_FloatU r; 202 r.l = s_to_float32_int(a); 203 return r.f; 204} 205 206uint32_t helper_s_to_memory(uint64_t a) 207{ 208 return s_to_float32_int(a); 209} 210 211uint64_t helper_memory_to_s(uint32_t a) 212{ 213 return float32_to_s_int(a); 214} 215 216uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b) 217{ 218 float32 fa, fb, fr; 219 220 fa = s_to_float32(a); 221 fb = s_to_float32(b); 222 fr = float32_add(fa, fb, &FP_STATUS); 223 env->error_code = soft_to_fpcr_exc(env); 224 225 return float32_to_s(fr); 226} 227 228uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b) 229{ 230 float32 fa, fb, fr; 231 232 fa = s_to_float32(a); 233 fb = s_to_float32(b); 234 fr = float32_sub(fa, fb, &FP_STATUS); 235 env->error_code = soft_to_fpcr_exc(env); 236 237 return float32_to_s(fr); 238} 239 240uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b) 241{ 242 float32 fa, fb, fr; 243 244 fa = s_to_float32(a); 245 fb = s_to_float32(b); 246 fr = float32_mul(fa, fb, &FP_STATUS); 247 env->error_code = soft_to_fpcr_exc(env); 248 249 return float32_to_s(fr); 250} 251 252uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b) 253{ 254 float32 fa, fb, fr; 255 256 fa = s_to_float32(a); 257 fb = s_to_float32(b); 258 fr = float32_div(fa, fb, &FP_STATUS); 259 env->error_code = soft_to_fpcr_exc(env); 260 261 return float32_to_s(fr); 262} 263 264uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a) 265{ 266 float32 fa, fr; 267 268 fa = s_to_float32(a); 269 fr = float32_sqrt(fa, &FP_STATUS); 270 env->error_code = soft_to_fpcr_exc(env); 271 272 return float32_to_s(fr); 273} 274 275 276/* T floating (double) */ 277static inline float64 t_to_float64(uint64_t a) 278{ 279 /* Memory format is the same as float64 */ 280 CPU_DoubleU r; 281 r.ll = a; 282 return r.d; 283} 284 285static inline uint64_t float64_to_t(float64 fa) 286{ 287 /* Memory format is the same as float64 */ 288 CPU_DoubleU r; 289 r.d = fa; 290 return r.ll; 291} 292 293uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b) 294{ 295 float64 fa, fb, fr; 296 297 fa = t_to_float64(a); 298 fb = t_to_float64(b); 299 fr = float64_add(fa, fb, &FP_STATUS); 300 env->error_code = soft_to_fpcr_exc(env); 301 302 return float64_to_t(fr); 303} 304 305uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b) 306{ 307 float64 fa, fb, fr; 308 309 fa = t_to_float64(a); 310 fb = t_to_float64(b); 311 fr = float64_sub(fa, fb, &FP_STATUS); 312 env->error_code = soft_to_fpcr_exc(env); 313 314 return float64_to_t(fr); 315} 316 317uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b) 318{ 319 float64 fa, fb, fr; 320 321 fa = t_to_float64(a); 322 fb = t_to_float64(b); 323 fr = float64_mul(fa, fb, &FP_STATUS); 324 env->error_code = soft_to_fpcr_exc(env); 325 326 return float64_to_t(fr); 327} 328 329uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b) 330{ 331 float64 fa, fb, fr; 332 333 fa = t_to_float64(a); 334 fb = t_to_float64(b); 335 fr = float64_div(fa, fb, &FP_STATUS); 336 env->error_code = soft_to_fpcr_exc(env); 337 338 return float64_to_t(fr); 339} 340 341uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a) 342{ 343 float64 fa, fr; 344 345 fa = t_to_float64(a); 346 fr = float64_sqrt(fa, &FP_STATUS); 347 env->error_code = soft_to_fpcr_exc(env); 348 349 return float64_to_t(fr); 350} 351 352/* Comparisons */ 353uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b) 354{ 355 float64 fa, fb; 356 uint64_t ret = 0; 357 358 fa = t_to_float64(a); 359 fb = t_to_float64(b); 360 361 if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { 362 ret = 0x4000000000000000ULL; 363 } 364 env->error_code = soft_to_fpcr_exc(env); 365 366 return ret; 367} 368 369uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b) 370{ 371 float64 fa, fb; 372 uint64_t ret = 0; 373 374 fa = t_to_float64(a); 375 fb = t_to_float64(b); 376 377 if (float64_eq_quiet(fa, fb, &FP_STATUS)) { 378 ret = 0x4000000000000000ULL; 379 } 380 env->error_code = soft_to_fpcr_exc(env); 381 382 return ret; 383} 384 385uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b) 386{ 387 float64 fa, fb; 388 uint64_t ret = 0; 389 390 fa = t_to_float64(a); 391 fb = t_to_float64(b); 392 393 if (float64_le(fa, fb, &FP_STATUS)) { 394 ret = 0x4000000000000000ULL; 395 } 396 env->error_code = soft_to_fpcr_exc(env); 397 398 return ret; 399} 400 401uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b) 402{ 403 float64 fa, fb; 404 uint64_t ret = 0; 405 406 fa = t_to_float64(a); 407 fb = t_to_float64(b); 408 409 if (float64_lt(fa, fb, &FP_STATUS)) { 410 ret = 0x4000000000000000ULL; 411 } 412 env->error_code = soft_to_fpcr_exc(env); 413 414 return ret; 415} 416 417/* Floating point format conversion */ 418uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a) 419{ 420 float64 fa; 421 float32 fr; 422 423 fa = t_to_float64(a); 424 fr = float64_to_float32(fa, &FP_STATUS); 425 env->error_code = soft_to_fpcr_exc(env); 426 427 return float32_to_s(fr); 428} 429 430uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a) 431{ 432 float32 fa; 433 float64 fr; 434 435 fa = s_to_float32(a); 436 fr = float32_to_float64(fa, &FP_STATUS); 437 env->error_code = soft_to_fpcr_exc(env); 438 439 return float64_to_t(fr); 440} 441 442uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) 443{ 444 float32 fr = int64_to_float32(a, &FP_STATUS); 445 env->error_code = soft_to_fpcr_exc(env); 446 447 return float32_to_s(fr); 448} 449 450/* Implement float64 to uint64_t conversion without saturation -- we must 451 supply the truncated result. This behaviour is used by the compiler 452 to get unsigned conversion for free with the same instruction. */ 453 454static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) 455{ 456 uint64_t frac, ret = 0; 457 uint32_t exp, sign, exc = 0; 458 int shift; 459 460 sign = (a >> 63); 461 exp = (uint32_t)(a >> 52) & 0x7ff; 462 frac = a & 0xfffffffffffffull; 463 464 if (exp == 0) { 465 if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) { 466 goto do_underflow; 467 } 468 } else if (exp == 0x7ff) { 469 exc = FPCR_INV; 470 } else { 471 /* Restore implicit bit. */ 472 frac |= 0x10000000000000ull; 473 474 shift = exp - 1023 - 52; 475 if (shift >= 0) { 476 /* In this case the number is so large that we must shift 477 the fraction left. There is no rounding to do. */ 478 if (shift < 64) { 479 ret = frac << shift; 480 } 481 /* Check for overflow. Note the special case of -0x1p63. */ 482 if (shift >= 11 && a != 0xC3E0000000000000ull) { 483 exc = FPCR_IOV | FPCR_INE; 484 } 485 } else { 486 uint64_t round; 487 488 /* In this case the number is smaller than the fraction as 489 represented by the 52 bit number. Here we must think 490 about rounding the result. Handle this by shifting the 491 fractional part of the number into the high bits of ROUND. 492 This will let us efficiently handle round-to-nearest. */ 493 shift = -shift; 494 if (shift < 63) { 495 ret = frac >> shift; 496 round = frac << (64 - shift); 497 } else { 498 /* The exponent is so small we shift out everything. 499 Leave a sticky bit for proper rounding below. */ 500 do_underflow: 501 round = 1; 502 } 503 504 if (round) { 505 exc = FPCR_INE; 506 switch (roundmode) { 507 case float_round_nearest_even: 508 if (round == (1ull << 63)) { 509 /* Fraction is exactly 0.5; round to even. */ 510 ret += (ret & 1); 511 } else if (round > (1ull << 63)) { 512 ret += 1; 513 } 514 break; 515 case float_round_to_zero: 516 break; 517 case float_round_up: 518 ret += 1 - sign; 519 break; 520 case float_round_down: 521 ret += sign; 522 break; 523 } 524 } 525 } 526 if (sign) { 527 ret = -ret; 528 } 529 } 530 env->error_code = exc; 531 532 return ret; 533} 534 535uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a) 536{ 537 return do_cvttq(env, a, FP_STATUS.float_rounding_mode); 538} 539 540uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a) 541{ 542 return do_cvttq(env, a, float_round_to_zero); 543} 544 545uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a) 546{ 547 float64 fr = int64_to_float64(a, &FP_STATUS); 548 env->error_code = soft_to_fpcr_exc(env); 549 return float64_to_t(fr); 550} 551 552uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val) 553{ 554 uint32_t exc = 0; 555 if (val != (int32_t)val) { 556 exc = FPCR_IOV | FPCR_INE; 557 } 558 env->error_code = exc; 559 560 return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29); 561}