1 /* 2 * Helpers for HPPA FPU instructions. 3 * 4 * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> 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 void HELPER(loaded_fr0)(CPUHPPAState *env) 26 { 27 uint32_t shadow = env->fr[0] >> 32; 28 int rm, d; 29 30 env->fr0_shadow = shadow; 31 32 switch (FIELD_EX32(shadow, FPSR, RM)) { 33 default: 34 rm = float_round_nearest_even; 35 break; 36 case 1: 37 rm = float_round_to_zero; 38 break; 39 case 2: 40 rm = float_round_up; 41 break; 42 case 3: 43 rm = float_round_down; 44 break; 45 } 46 set_float_rounding_mode(rm, &env->fp_status); 47 48 d = FIELD_EX32(shadow, FPSR, D); 49 set_flush_to_zero(d, &env->fp_status); 50 set_flush_inputs_to_zero(d, &env->fp_status); 51 52 /* 53 * TODO: we only need to do this at CPU reset, but currently 54 * HPPA does note implement a CPU reset method at all... 55 */ 56 set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); 57 /* 58 * TODO: The HPPA architecture reference only documents its NaN 59 * propagation rule for 2-operand operations. Testing on real hardware 60 * might be necessary to confirm whether this order for muladd is correct. 61 * Not preferring the SNaN is almost certainly incorrect as it diverges 62 * from the documented rules for 2-operand operations. 63 */ 64 set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status); 65 /* For inf * 0 + NaN, return the input NaN */ 66 set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); 67 /* Default NaN: sign bit clear, msb-1 frac bit set */ 68 set_float_default_nan_pattern(0b00100000, &env->fp_status); 69 set_snan_bit_is_one(true, &env->fp_status); 70 /* 71 * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing 72 * enabled by FPSR.D happens before or after rounding. We pick "before" 73 * for consistency with tininess detection. 74 */ 75 set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); 76 /* 77 * TODO: "PA-RISC 2.0 Architecture" chapter 10 says that we should 78 * detect tininess before rounding, but we don't set that here so we 79 * get the default tininess after rounding. 80 */ 81 } 82 83 void cpu_hppa_loaded_fr0(CPUHPPAState *env) 84 { 85 helper_loaded_fr0(env); 86 } 87 88 #define CONVERT_BIT(X, SRC, DST) \ 89 ((unsigned)(SRC) > (unsigned)(DST) \ 90 ? (X) / ((SRC) / (DST)) & (DST) \ 91 : ((X) & (SRC)) * ((DST) / (SRC))) 92 93 static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) 94 { 95 uint32_t soft_exp = get_float_exception_flags(&env->fp_status); 96 uint32_t hard_exp = 0; 97 uint32_t shadow = env->fr0_shadow; 98 uint32_t to_flag = 0; 99 uint32_t fr1 = 0; 100 101 if (likely(soft_exp == 0)) { 102 env->fr[0] = (uint64_t)shadow << 32; 103 return; 104 } 105 set_float_exception_flags(0, &env->fp_status); 106 107 hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK); 108 hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK); 109 hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); 110 hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); 111 hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); 112 if (hard_exp & shadow) { 113 shadow = FIELD_DP32(shadow, FPSR, T, 1); 114 /* fill exception register #1, which is lower 32-bits of fr[0] */ 115 #if !defined(CONFIG_USER_ONLY) 116 if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) { 117 /* over- and underflow both set overflow flag only */ 118 fr1 = FIELD_DP32(fr1, FPSR, C, 1); 119 fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1); 120 } else 121 #endif 122 { 123 fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); 124 } 125 } 126 /* Set the Flag bits for every exception that was not enabled */ 127 to_flag = hard_exp & ~shadow; 128 shadow |= to_flag << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); 129 130 env->fr0_shadow = shadow; 131 env->fr[0] = (uint64_t)shadow << 32 | fr1; 132 133 if (hard_exp & shadow) { 134 hppa_dynamic_excp(env, EXCP_ASSIST, ra); 135 } 136 } 137 138 float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) 139 { 140 float32 ret = float32_sqrt(arg, &env->fp_status); 141 update_fr0_op(env, GETPC()); 142 return ret; 143 } 144 145 float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) 146 { 147 float32 ret = float32_round_to_int(arg, &env->fp_status); 148 update_fr0_op(env, GETPC()); 149 return ret; 150 } 151 152 float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) 153 { 154 float32 ret = float32_add(a, b, &env->fp_status); 155 update_fr0_op(env, GETPC()); 156 return ret; 157 } 158 159 float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) 160 { 161 float32 ret = float32_sub(a, b, &env->fp_status); 162 update_fr0_op(env, GETPC()); 163 return ret; 164 } 165 166 float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) 167 { 168 float32 ret = float32_mul(a, b, &env->fp_status); 169 update_fr0_op(env, GETPC()); 170 return ret; 171 } 172 173 float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) 174 { 175 float32 ret = float32_div(a, b, &env->fp_status); 176 update_fr0_op(env, GETPC()); 177 return ret; 178 } 179 180 float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) 181 { 182 float64 ret = float64_sqrt(arg, &env->fp_status); 183 update_fr0_op(env, GETPC()); 184 return ret; 185 } 186 187 float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) 188 { 189 float64 ret = float64_round_to_int(arg, &env->fp_status); 190 update_fr0_op(env, GETPC()); 191 return ret; 192 } 193 194 float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) 195 { 196 float64 ret = float64_add(a, b, &env->fp_status); 197 update_fr0_op(env, GETPC()); 198 return ret; 199 } 200 201 float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) 202 { 203 float64 ret = float64_sub(a, b, &env->fp_status); 204 update_fr0_op(env, GETPC()); 205 return ret; 206 } 207 208 float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) 209 { 210 float64 ret = float64_mul(a, b, &env->fp_status); 211 update_fr0_op(env, GETPC()); 212 return ret; 213 } 214 215 float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) 216 { 217 float64 ret = float64_div(a, b, &env->fp_status); 218 update_fr0_op(env, GETPC()); 219 return ret; 220 } 221 222 float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) 223 { 224 float64 ret = float32_to_float64(arg, &env->fp_status); 225 update_fr0_op(env, GETPC()); 226 return ret; 227 } 228 229 float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) 230 { 231 float32 ret = float64_to_float32(arg, &env->fp_status); 232 update_fr0_op(env, GETPC()); 233 return ret; 234 } 235 236 float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) 237 { 238 float32 ret = int32_to_float32(arg, &env->fp_status); 239 update_fr0_op(env, GETPC()); 240 return ret; 241 } 242 243 float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) 244 { 245 float32 ret = int64_to_float32(arg, &env->fp_status); 246 update_fr0_op(env, GETPC()); 247 return ret; 248 } 249 250 float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) 251 { 252 float64 ret = int32_to_float64(arg, &env->fp_status); 253 update_fr0_op(env, GETPC()); 254 return ret; 255 } 256 257 float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) 258 { 259 float64 ret = int64_to_float64(arg, &env->fp_status); 260 update_fr0_op(env, GETPC()); 261 return ret; 262 } 263 264 int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) 265 { 266 int32_t ret = float32_to_int32(arg, &env->fp_status); 267 update_fr0_op(env, GETPC()); 268 return ret; 269 } 270 271 int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) 272 { 273 int32_t ret = float64_to_int32(arg, &env->fp_status); 274 update_fr0_op(env, GETPC()); 275 return ret; 276 } 277 278 int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) 279 { 280 int64_t ret = float32_to_int64(arg, &env->fp_status); 281 update_fr0_op(env, GETPC()); 282 return ret; 283 } 284 285 int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) 286 { 287 int64_t ret = float64_to_int64(arg, &env->fp_status); 288 update_fr0_op(env, GETPC()); 289 return ret; 290 } 291 292 int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) 293 { 294 int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); 295 update_fr0_op(env, GETPC()); 296 return ret; 297 } 298 299 int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) 300 { 301 int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); 302 update_fr0_op(env, GETPC()); 303 return ret; 304 } 305 306 int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) 307 { 308 int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); 309 update_fr0_op(env, GETPC()); 310 return ret; 311 } 312 313 int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) 314 { 315 int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); 316 update_fr0_op(env, GETPC()); 317 return ret; 318 } 319 320 float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) 321 { 322 float32 ret = uint32_to_float32(arg, &env->fp_status); 323 update_fr0_op(env, GETPC()); 324 return ret; 325 } 326 327 float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) 328 { 329 float32 ret = uint64_to_float32(arg, &env->fp_status); 330 update_fr0_op(env, GETPC()); 331 return ret; 332 } 333 334 float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) 335 { 336 float64 ret = uint32_to_float64(arg, &env->fp_status); 337 update_fr0_op(env, GETPC()); 338 return ret; 339 } 340 341 float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) 342 { 343 float64 ret = uint64_to_float64(arg, &env->fp_status); 344 update_fr0_op(env, GETPC()); 345 return ret; 346 } 347 348 uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) 349 { 350 uint32_t ret = float32_to_uint32(arg, &env->fp_status); 351 update_fr0_op(env, GETPC()); 352 return ret; 353 } 354 355 uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) 356 { 357 uint32_t ret = float64_to_uint32(arg, &env->fp_status); 358 update_fr0_op(env, GETPC()); 359 return ret; 360 } 361 362 uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) 363 { 364 uint64_t ret = float32_to_uint64(arg, &env->fp_status); 365 update_fr0_op(env, GETPC()); 366 return ret; 367 } 368 369 uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) 370 { 371 uint64_t ret = float64_to_uint64(arg, &env->fp_status); 372 update_fr0_op(env, GETPC()); 373 return ret; 374 } 375 376 uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) 377 { 378 uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); 379 update_fr0_op(env, GETPC()); 380 return ret; 381 } 382 383 uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) 384 { 385 uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); 386 update_fr0_op(env, GETPC()); 387 return ret; 388 } 389 390 uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) 391 { 392 uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); 393 update_fr0_op(env, GETPC()); 394 return ret; 395 } 396 397 uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) 398 { 399 uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); 400 update_fr0_op(env, GETPC()); 401 return ret; 402 } 403 404 static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, 405 uint32_t c, FloatRelation r) 406 { 407 uint32_t shadow = env->fr0_shadow; 408 409 switch (r) { 410 case float_relation_greater: 411 c = extract32(c, 4, 1); 412 break; 413 case float_relation_less: 414 c = extract32(c, 3, 1); 415 break; 416 case float_relation_equal: 417 c = extract32(c, 2, 1); 418 break; 419 case float_relation_unordered: 420 c = extract32(c, 1, 1); 421 break; 422 default: 423 g_assert_not_reached(); 424 } 425 426 if (y) { 427 /* targeted comparison */ 428 /* set fpsr[ca[y - 1]] to current compare */ 429 shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c); 430 } else { 431 /* queued comparison */ 432 /* shift cq right by one place */ 433 shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); 434 /* move fpsr[c] to fpsr[cq[0]] */ 435 shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); 436 /* set fpsr[c] to current compare */ 437 shadow = FIELD_DP32(shadow, FPSR, C, c); 438 } 439 440 env->fr0_shadow = shadow; 441 env->fr[0] = (uint64_t)shadow << 32; 442 } 443 444 void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, 445 uint32_t y, uint32_t c) 446 { 447 FloatRelation r; 448 if (c & 1) { 449 r = float32_compare(a, b, &env->fp_status); 450 } else { 451 r = float32_compare_quiet(a, b, &env->fp_status); 452 } 453 update_fr0_op(env, GETPC()); 454 update_fr0_cmp(env, y, c, r); 455 } 456 457 void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, 458 uint32_t y, uint32_t c) 459 { 460 FloatRelation r; 461 if (c & 1) { 462 r = float64_compare(a, b, &env->fp_status); 463 } else { 464 r = float64_compare_quiet(a, b, &env->fp_status); 465 } 466 update_fr0_op(env, GETPC()); 467 update_fr0_cmp(env, y, c, r); 468 } 469 470 float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 471 { 472 float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); 473 update_fr0_op(env, GETPC()); 474 return ret; 475 } 476 477 float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 478 { 479 float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, 480 &env->fp_status); 481 update_fr0_op(env, GETPC()); 482 return ret; 483 } 484 485 float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 486 { 487 float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); 488 update_fr0_op(env, GETPC()); 489 return ret; 490 } 491 492 float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 493 { 494 float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, 495 &env->fp_status); 496 update_fr0_op(env, GETPC()); 497 return ret; 498 } 499