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