1 /* 2 * Helpers for HPPA 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 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 "exec/cpu_ldst.h" 25 26 void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) 27 { 28 HPPACPU *cpu = hppa_env_get_cpu(env); 29 CPUState *cs = CPU(cpu); 30 31 cs->exception_index = excp; 32 cpu_loop_exit(cs); 33 } 34 35 static void QEMU_NORETURN dynexcp(CPUHPPAState *env, int excp, uintptr_t ra) 36 { 37 HPPACPU *cpu = hppa_env_get_cpu(env); 38 CPUState *cs = CPU(cpu); 39 40 cs->exception_index = excp; 41 cpu_loop_exit_restore(cs, ra); 42 } 43 44 void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) 45 { 46 if (unlikely((target_long)cond < 0)) { 47 dynexcp(env, EXCP_SIGFPE, GETPC()); 48 } 49 } 50 51 void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) 52 { 53 if (unlikely(cond)) { 54 dynexcp(env, EXCP_SIGFPE, GETPC()); 55 } 56 } 57 58 static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val, 59 uint32_t mask, uintptr_t ra) 60 { 61 uint32_t old, new, cmp; 62 63 #ifdef CONFIG_USER_ONLY 64 uint32_t *haddr = g2h(addr - 1); 65 old = *haddr; 66 while (1) { 67 new = (old & ~mask) | (val & mask); 68 cmp = atomic_cmpxchg(haddr, old, new); 69 if (cmp == old) { 70 return; 71 } 72 old = cmp; 73 } 74 #else 75 #error "Not implemented." 76 #endif 77 } 78 79 static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val, 80 bool parallel) 81 { 82 uintptr_t ra = GETPC(); 83 84 switch (addr & 3) { 85 case 3: 86 cpu_stb_data_ra(env, addr, val, ra); 87 break; 88 case 2: 89 cpu_stw_data_ra(env, addr, val, ra); 90 break; 91 case 1: 92 /* The 3 byte store must appear atomic. */ 93 if (parallel) { 94 atomic_store_3(env, addr, val, 0x00ffffffu, ra); 95 } else { 96 cpu_stb_data_ra(env, addr, val >> 16, ra); 97 cpu_stw_data_ra(env, addr + 1, val, ra); 98 } 99 break; 100 default: 101 cpu_stl_data_ra(env, addr, val, ra); 102 break; 103 } 104 } 105 106 void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) 107 { 108 do_stby_b(env, addr, val, false); 109 } 110 111 void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, 112 target_ulong val) 113 { 114 do_stby_b(env, addr, val, true); 115 } 116 117 static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ulong val, 118 bool parallel) 119 { 120 uintptr_t ra = GETPC(); 121 122 switch (addr & 3) { 123 case 3: 124 /* The 3 byte store must appear atomic. */ 125 if (parallel) { 126 atomic_store_3(env, addr - 3, val, 0xffffff00u, ra); 127 } else { 128 cpu_stw_data_ra(env, addr - 3, val >> 16, ra); 129 cpu_stb_data_ra(env, addr - 1, val >> 8, ra); 130 } 131 break; 132 case 2: 133 cpu_stw_data_ra(env, addr - 2, val >> 16, ra); 134 break; 135 case 1: 136 cpu_stb_data_ra(env, addr - 1, val >> 24, ra); 137 break; 138 default: 139 /* Nothing is stored, but protection is checked and the 140 cacheline is marked dirty. */ 141 #ifndef CONFIG_USER_ONLY 142 probe_write(env, addr, cpu_mmu_index(env, 0), ra); 143 #endif 144 break; 145 } 146 } 147 148 void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) 149 { 150 do_stby_e(env, addr, val, false); 151 } 152 153 void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, 154 target_ulong val) 155 { 156 do_stby_e(env, addr, val, true); 157 } 158 159 target_ulong HELPER(probe_r)(target_ulong addr) 160 { 161 return page_check_range(addr, 1, PAGE_READ); 162 } 163 164 target_ulong HELPER(probe_w)(target_ulong addr) 165 { 166 return page_check_range(addr, 1, PAGE_WRITE); 167 } 168 169 void HELPER(loaded_fr0)(CPUHPPAState *env) 170 { 171 uint32_t shadow = env->fr[0] >> 32; 172 int rm, d; 173 174 env->fr0_shadow = shadow; 175 176 switch (extract32(shadow, 9, 2)) { 177 default: 178 rm = float_round_nearest_even; 179 break; 180 case 1: 181 rm = float_round_to_zero; 182 break; 183 case 2: 184 rm = float_round_up; 185 break; 186 case 3: 187 rm = float_round_down; 188 break; 189 } 190 set_float_rounding_mode(rm, &env->fp_status); 191 192 d = extract32(shadow, 5, 1); 193 set_flush_to_zero(d, &env->fp_status); 194 set_flush_inputs_to_zero(d, &env->fp_status); 195 } 196 197 void cpu_hppa_loaded_fr0(CPUHPPAState *env) 198 { 199 helper_loaded_fr0(env); 200 } 201 202 #define CONVERT_BIT(X, SRC, DST) \ 203 ((SRC) > (DST) \ 204 ? (X) / ((SRC) / (DST)) & (DST) \ 205 : ((X) & (SRC)) * ((DST) / (SRC))) 206 207 static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) 208 { 209 uint32_t soft_exp = get_float_exception_flags(&env->fp_status); 210 uint32_t hard_exp = 0; 211 uint32_t shadow = env->fr0_shadow; 212 213 if (likely(soft_exp == 0)) { 214 env->fr[0] = (uint64_t)shadow << 32; 215 return; 216 } 217 set_float_exception_flags(0, &env->fp_status); 218 219 hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); 220 hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); 221 hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); 222 hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); 223 hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); 224 shadow |= hard_exp << (32 - 5); 225 env->fr0_shadow = shadow; 226 env->fr[0] = (uint64_t)shadow << 32; 227 228 if (hard_exp & shadow) { 229 dynexcp(env, EXCP_SIGFPE, ra); 230 } 231 } 232 233 float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) 234 { 235 float32 ret = float32_sqrt(arg, &env->fp_status); 236 update_fr0_op(env, GETPC()); 237 return ret; 238 } 239 240 float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) 241 { 242 float32 ret = float32_round_to_int(arg, &env->fp_status); 243 update_fr0_op(env, GETPC()); 244 return ret; 245 } 246 247 float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) 248 { 249 float32 ret = float32_add(a, b, &env->fp_status); 250 update_fr0_op(env, GETPC()); 251 return ret; 252 } 253 254 float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) 255 { 256 float32 ret = float32_sub(a, b, &env->fp_status); 257 update_fr0_op(env, GETPC()); 258 return ret; 259 } 260 261 float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) 262 { 263 float32 ret = float32_mul(a, b, &env->fp_status); 264 update_fr0_op(env, GETPC()); 265 return ret; 266 } 267 268 float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) 269 { 270 float32 ret = float32_div(a, b, &env->fp_status); 271 update_fr0_op(env, GETPC()); 272 return ret; 273 } 274 275 float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) 276 { 277 float64 ret = float64_sqrt(arg, &env->fp_status); 278 update_fr0_op(env, GETPC()); 279 return ret; 280 } 281 282 float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) 283 { 284 float64 ret = float64_round_to_int(arg, &env->fp_status); 285 update_fr0_op(env, GETPC()); 286 return ret; 287 } 288 289 float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) 290 { 291 float64 ret = float64_add(a, b, &env->fp_status); 292 update_fr0_op(env, GETPC()); 293 return ret; 294 } 295 296 float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) 297 { 298 float64 ret = float64_sub(a, b, &env->fp_status); 299 update_fr0_op(env, GETPC()); 300 return ret; 301 } 302 303 float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) 304 { 305 float64 ret = float64_mul(a, b, &env->fp_status); 306 update_fr0_op(env, GETPC()); 307 return ret; 308 } 309 310 float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) 311 { 312 float64 ret = float64_div(a, b, &env->fp_status); 313 update_fr0_op(env, GETPC()); 314 return ret; 315 } 316 317 float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) 318 { 319 float64 ret = float32_to_float64(arg, &env->fp_status); 320 ret = float64_maybe_silence_nan(ret, &env->fp_status); 321 update_fr0_op(env, GETPC()); 322 return ret; 323 } 324 325 float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) 326 { 327 float32 ret = float64_to_float32(arg, &env->fp_status); 328 ret = float32_maybe_silence_nan(ret, &env->fp_status); 329 update_fr0_op(env, GETPC()); 330 return ret; 331 } 332 333 float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) 334 { 335 float32 ret = int32_to_float32(arg, &env->fp_status); 336 update_fr0_op(env, GETPC()); 337 return ret; 338 } 339 340 float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) 341 { 342 float32 ret = int64_to_float32(arg, &env->fp_status); 343 update_fr0_op(env, GETPC()); 344 return ret; 345 } 346 347 float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) 348 { 349 float64 ret = int32_to_float64(arg, &env->fp_status); 350 update_fr0_op(env, GETPC()); 351 return ret; 352 } 353 354 float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) 355 { 356 float64 ret = int64_to_float64(arg, &env->fp_status); 357 update_fr0_op(env, GETPC()); 358 return ret; 359 } 360 361 int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) 362 { 363 int32_t ret = float32_to_int32(arg, &env->fp_status); 364 update_fr0_op(env, GETPC()); 365 return ret; 366 } 367 368 int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) 369 { 370 int32_t ret = float64_to_int32(arg, &env->fp_status); 371 update_fr0_op(env, GETPC()); 372 return ret; 373 } 374 375 int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) 376 { 377 int64_t ret = float32_to_int64(arg, &env->fp_status); 378 update_fr0_op(env, GETPC()); 379 return ret; 380 } 381 382 int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) 383 { 384 int64_t ret = float64_to_int64(arg, &env->fp_status); 385 update_fr0_op(env, GETPC()); 386 return ret; 387 } 388 389 int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) 390 { 391 int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); 392 update_fr0_op(env, GETPC()); 393 return ret; 394 } 395 396 int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) 397 { 398 int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); 399 update_fr0_op(env, GETPC()); 400 return ret; 401 } 402 403 int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) 404 { 405 int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); 406 update_fr0_op(env, GETPC()); 407 return ret; 408 } 409 410 int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) 411 { 412 int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); 413 update_fr0_op(env, GETPC()); 414 return ret; 415 } 416 417 float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) 418 { 419 float32 ret = uint32_to_float32(arg, &env->fp_status); 420 update_fr0_op(env, GETPC()); 421 return ret; 422 } 423 424 float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) 425 { 426 float32 ret = uint64_to_float32(arg, &env->fp_status); 427 update_fr0_op(env, GETPC()); 428 return ret; 429 } 430 431 float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) 432 { 433 float64 ret = uint32_to_float64(arg, &env->fp_status); 434 update_fr0_op(env, GETPC()); 435 return ret; 436 } 437 438 float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) 439 { 440 float64 ret = uint64_to_float64(arg, &env->fp_status); 441 update_fr0_op(env, GETPC()); 442 return ret; 443 } 444 445 uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) 446 { 447 uint32_t ret = float32_to_uint32(arg, &env->fp_status); 448 update_fr0_op(env, GETPC()); 449 return ret; 450 } 451 452 uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) 453 { 454 uint32_t ret = float64_to_uint32(arg, &env->fp_status); 455 update_fr0_op(env, GETPC()); 456 return ret; 457 } 458 459 uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) 460 { 461 uint64_t ret = float32_to_uint64(arg, &env->fp_status); 462 update_fr0_op(env, GETPC()); 463 return ret; 464 } 465 466 uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) 467 { 468 uint64_t ret = float64_to_uint64(arg, &env->fp_status); 469 update_fr0_op(env, GETPC()); 470 return ret; 471 } 472 473 uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) 474 { 475 uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); 476 update_fr0_op(env, GETPC()); 477 return ret; 478 } 479 480 uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) 481 { 482 uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); 483 update_fr0_op(env, GETPC()); 484 return ret; 485 } 486 487 uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) 488 { 489 uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); 490 update_fr0_op(env, GETPC()); 491 return ret; 492 } 493 494 uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) 495 { 496 uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); 497 update_fr0_op(env, GETPC()); 498 return ret; 499 } 500 501 static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, uint32_t c, int r) 502 { 503 uint32_t shadow = env->fr0_shadow; 504 505 switch (r) { 506 case float_relation_greater: 507 c = extract32(c, 4, 1); 508 break; 509 case float_relation_less: 510 c = extract32(c, 3, 1); 511 break; 512 case float_relation_equal: 513 c = extract32(c, 2, 1); 514 break; 515 case float_relation_unordered: 516 c = extract32(c, 1, 1); 517 break; 518 default: 519 g_assert_not_reached(); 520 } 521 522 if (y) { 523 /* targeted comparison */ 524 /* set fpsr[ca[y - 1]] to current compare */ 525 shadow = deposit32(shadow, 21 - (y - 1), 1, c); 526 } else { 527 /* queued comparison */ 528 /* shift cq right by one place */ 529 shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); 530 /* move fpsr[c] to fpsr[cq[0]] */ 531 shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); 532 /* set fpsr[c] to current compare */ 533 shadow = deposit32(shadow, 26, 1, c); 534 } 535 536 env->fr0_shadow = shadow; 537 env->fr[0] = (uint64_t)shadow << 32; 538 } 539 540 void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, 541 uint32_t y, uint32_t c) 542 { 543 int r; 544 if (c & 1) { 545 r = float32_compare(a, b, &env->fp_status); 546 } else { 547 r = float32_compare_quiet(a, b, &env->fp_status); 548 } 549 update_fr0_op(env, GETPC()); 550 update_fr0_cmp(env, y, c, r); 551 } 552 553 void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, 554 uint32_t y, uint32_t c) 555 { 556 int r; 557 if (c & 1) { 558 r = float64_compare(a, b, &env->fp_status); 559 } else { 560 r = float64_compare_quiet(a, b, &env->fp_status); 561 } 562 update_fr0_op(env, GETPC()); 563 update_fr0_cmp(env, y, c, r); 564 } 565 566 float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 567 { 568 float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); 569 update_fr0_op(env, GETPC()); 570 return ret; 571 } 572 573 float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) 574 { 575 float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, 576 &env->fp_status); 577 update_fr0_op(env, GETPC()); 578 return ret; 579 } 580 581 float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 582 { 583 float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); 584 update_fr0_op(env, GETPC()); 585 return ret; 586 } 587 588 float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) 589 { 590 float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, 591 &env->fp_status); 592 update_fr0_op(env, GETPC()); 593 return ret; 594 } 595