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 /* 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 99 if (likely(soft_exp == 0)) { 100 env->fr[0] = (uint64_t)shadow << 32; 101 return; 102 } 103 set_float_exception_flags(0, &env->fp_status); 104 105 hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK); 106 hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK); 107 hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); 108 hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); 109 hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); 110 shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); 111 env->fr0_shadow = shadow; 112 env->fr[0] = (uint64_t)shadow << 32; 113 114 if (hard_exp & shadow) { 115 hppa_dynamic_excp(env, EXCP_ASSIST, ra); 116 } 117 } 118 119 float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) 120 { 121 float32 ret = float32_sqrt(arg, &env->fp_status); 122 update_fr0_op(env, GETPC()); 123 return ret; 124 } 125 126 float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) 127 { 128 float32 ret = float32_round_to_int(arg, &env->fp_status); 129 update_fr0_op(env, GETPC()); 130 return ret; 131 } 132 133 float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) 134 { 135 float32 ret = float32_add(a, b, &env->fp_status); 136 update_fr0_op(env, GETPC()); 137 return ret; 138 } 139 140 float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) 141 { 142 float32 ret = float32_sub(a, b, &env->fp_status); 143 update_fr0_op(env, GETPC()); 144 return ret; 145 } 146 147 float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) 148 { 149 float32 ret = float32_mul(a, b, &env->fp_status); 150 update_fr0_op(env, GETPC()); 151 return ret; 152 } 153 154 float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) 155 { 156 float32 ret = float32_div(a, b, &env->fp_status); 157 update_fr0_op(env, GETPC()); 158 return ret; 159 } 160 161 float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) 162 { 163 float64 ret = float64_sqrt(arg, &env->fp_status); 164 update_fr0_op(env, GETPC()); 165 return ret; 166 } 167 168 float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) 169 { 170 float64 ret = float64_round_to_int(arg, &env->fp_status); 171 update_fr0_op(env, GETPC()); 172 return ret; 173 } 174 175 float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) 176 { 177 float64 ret = float64_add(a, b, &env->fp_status); 178 update_fr0_op(env, GETPC()); 179 return ret; 180 } 181 182 float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) 183 { 184 float64 ret = float64_sub(a, b, &env->fp_status); 185 update_fr0_op(env, GETPC()); 186 return ret; 187 } 188 189 float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) 190 { 191 float64 ret = float64_mul(a, b, &env->fp_status); 192 update_fr0_op(env, GETPC()); 193 return ret; 194 } 195 196 float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) 197 { 198 float64 ret = float64_div(a, b, &env->fp_status); 199 update_fr0_op(env, GETPC()); 200 return ret; 201 } 202 203 float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) 204 { 205 float64 ret = float32_to_float64(arg, &env->fp_status); 206 update_fr0_op(env, GETPC()); 207 return ret; 208 } 209 210 float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) 211 { 212 float32 ret = float64_to_float32(arg, &env->fp_status); 213 update_fr0_op(env, GETPC()); 214 return ret; 215 } 216 217 float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) 218 { 219 float32 ret = int32_to_float32(arg, &env->fp_status); 220 update_fr0_op(env, GETPC()); 221 return ret; 222 } 223 224 float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) 225 { 226 float32 ret = int64_to_float32(arg, &env->fp_status); 227 update_fr0_op(env, GETPC()); 228 return ret; 229 } 230 231 float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) 232 { 233 float64 ret = int32_to_float64(arg, &env->fp_status); 234 update_fr0_op(env, GETPC()); 235 return ret; 236 } 237 238 float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) 239 { 240 float64 ret = int64_to_float64(arg, &env->fp_status); 241 update_fr0_op(env, GETPC()); 242 return ret; 243 } 244 245 int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) 246 { 247 int32_t ret = float32_to_int32(arg, &env->fp_status); 248 update_fr0_op(env, GETPC()); 249 return ret; 250 } 251 252 int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) 253 { 254 int32_t ret = float64_to_int32(arg, &env->fp_status); 255 update_fr0_op(env, GETPC()); 256 return ret; 257 } 258 259 int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) 260 { 261 int64_t ret = float32_to_int64(arg, &env->fp_status); 262 update_fr0_op(env, GETPC()); 263 return ret; 264 } 265 266 int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) 267 { 268 int64_t ret = float64_to_int64(arg, &env->fp_status); 269 update_fr0_op(env, GETPC()); 270 return ret; 271 } 272 273 int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) 274 { 275 int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); 276 update_fr0_op(env, GETPC()); 277 return ret; 278 } 279 280 int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) 281 { 282 int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); 283 update_fr0_op(env, GETPC()); 284 return ret; 285 } 286 287 int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) 288 { 289 int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); 290 update_fr0_op(env, GETPC()); 291 return ret; 292 } 293 294 int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) 295 { 296 int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); 297 update_fr0_op(env, GETPC()); 298 return ret; 299 } 300 301 float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) 302 { 303 float32 ret = uint32_to_float32(arg, &env->fp_status); 304 update_fr0_op(env, GETPC()); 305 return ret; 306 } 307 308 float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) 309 { 310 float32 ret = uint64_to_float32(arg, &env->fp_status); 311 update_fr0_op(env, GETPC()); 312 return ret; 313 } 314 315 float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) 316 { 317 float64 ret = uint32_to_float64(arg, &env->fp_status); 318 update_fr0_op(env, GETPC()); 319 return ret; 320 } 321 322 float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) 323 { 324 float64 ret = uint64_to_float64(arg, &env->fp_status); 325 update_fr0_op(env, GETPC()); 326 return ret; 327 } 328 329 uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) 330 { 331 uint32_t ret = float32_to_uint32(arg, &env->fp_status); 332 update_fr0_op(env, GETPC()); 333 return ret; 334 } 335 336 uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) 337 { 338 uint32_t ret = float64_to_uint32(arg, &env->fp_status); 339 update_fr0_op(env, GETPC()); 340 return ret; 341 } 342 343 uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) 344 { 345 uint64_t ret = float32_to_uint64(arg, &env->fp_status); 346 update_fr0_op(env, GETPC()); 347 return ret; 348 } 349 350 uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) 351 { 352 uint64_t ret = float64_to_uint64(arg, &env->fp_status); 353 update_fr0_op(env, GETPC()); 354 return ret; 355 } 356 357 uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) 358 { 359 uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); 360 update_fr0_op(env, GETPC()); 361 return ret; 362 } 363 364 uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) 365 { 366 uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); 367 update_fr0_op(env, GETPC()); 368 return ret; 369 } 370 371 uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) 372 { 373 uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); 374 update_fr0_op(env, GETPC()); 375 return ret; 376 } 377 378 uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) 379 { 380 uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); 381 update_fr0_op(env, GETPC()); 382 return ret; 383 } 384 385 static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, 386 uint32_t c, FloatRelation r) 387 { 388 uint32_t shadow = env->fr0_shadow; 389 390 switch (r) { 391 case float_relation_greater: 392 c = extract32(c, 4, 1); 393 break; 394 case float_relation_less: 395 c = extract32(c, 3, 1); 396 break; 397 case float_relation_equal: 398 c = extract32(c, 2, 1); 399 break; 400 case float_relation_unordered: 401 c = extract32(c, 1, 1); 402 break; 403 default: 404 g_assert_not_reached(); 405 } 406 407 if (y) { 408 /* targeted comparison */ 409 /* set fpsr[ca[y - 1]] to current compare */ 410 shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c); 411 } else { 412 /* queued comparison */ 413 /* shift cq right by one place */ 414 shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); 415 /* move fpsr[c] to fpsr[cq[0]] */ 416 shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); 417 /* set fpsr[c] to current compare */ 418 shadow = FIELD_DP32(shadow, FPSR, C, c); 419 } 420 421 env->fr0_shadow = shadow; 422 env->fr[0] = (uint64_t)shadow << 32; 423 } 424 425 void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, 426 uint32_t y, uint32_t c) 427 { 428 FloatRelation r; 429 if (c & 1) { 430 r = float32_compare(a, b, &env->fp_status); 431 } else { 432 r = float32_compare_quiet(a, b, &env->fp_status); 433 } 434 update_fr0_op(env, GETPC()); 435 update_fr0_cmp(env, y, c, r); 436 } 437 438 void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, 439 uint32_t y, uint32_t c) 440 { 441 FloatRelation r; 442 if (c & 1) { 443 r = float64_compare(a, b, &env->fp_status); 444 } else { 445 r = float64_compare_quiet(a, b, &env->fp_status); 446 } 447 update_fr0_op(env, GETPC()); 448 update_fr0_cmp(env, y, c, r); 449 } 450 451 float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 452 { 453 float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); 454 update_fr0_op(env, GETPC()); 455 return ret; 456 } 457 458 float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 459 { 460 float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, 461 &env->fp_status); 462 update_fr0_op(env, GETPC()); 463 return ret; 464 } 465 466 float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 467 { 468 float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); 469 update_fr0_op(env, GETPC()); 470 return ret; 471 } 472 473 float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 474 { 475 float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, 476 &env->fp_status); 477 update_fr0_op(env, GETPC()); 478 return ret; 479 } 480