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