qemu/target/alpha/fpu_helper.c
<<
>>
Prefs
   1/*
   2 *  Helpers for floating point instructions.
   3 *
   4 *  Copyright (c) 2007 Jocelyn Mayer
   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 "fpu/softfloat.h"
  25
  26#define FP_STATUS (env->fp_status)
  27
  28
  29void helper_setroundmode(CPUAlphaState *env, uint32_t val)
  30{
  31    set_float_rounding_mode(val, &FP_STATUS);
  32}
  33
  34void helper_setflushzero(CPUAlphaState *env, uint32_t val)
  35{
  36    set_flush_to_zero(val, &FP_STATUS);
  37}
  38
  39#define CONVERT_BIT(X, SRC, DST) \
  40    (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
  41
  42static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
  43{
  44    uint8_t exc = get_float_exception_flags(&FP_STATUS);
  45    uint32_t ret = 0;
  46
  47    if (unlikely(exc)) {
  48        set_float_exception_flags(0, &FP_STATUS);
  49        ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV);
  50        ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE);
  51        ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF);
  52        ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF);
  53        ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE);
  54    }
  55
  56    return ret;
  57}
  58
  59static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr,
  60                          uint32_t exc, uint32_t regno, uint32_t hw_exc)
  61{
  62    hw_exc |= CONVERT_BIT(exc, FPCR_INV, EXC_M_INV);
  63    hw_exc |= CONVERT_BIT(exc, FPCR_DZE, EXC_M_DZE);
  64    hw_exc |= CONVERT_BIT(exc, FPCR_OVF, EXC_M_FOV);
  65    hw_exc |= CONVERT_BIT(exc, FPCR_UNF, EXC_M_UNF);
  66    hw_exc |= CONVERT_BIT(exc, FPCR_INE, EXC_M_INE);
  67    hw_exc |= CONVERT_BIT(exc, FPCR_IOV, EXC_M_IOV);
  68
  69    arith_excp(env, retaddr, hw_exc, 1ull << regno);
  70}
  71
  72/* Raise exceptions for ieee fp insns without software completion.
  73   In that case there are no exceptions that don't trap; the mask
  74   doesn't apply.  */
  75void helper_fp_exc_raise(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
  76{
  77    uint32_t exc = env->error_code;
  78    if (exc) {
  79        env->fpcr |= exc;
  80        exc &= ~ignore;
  81        if (exc) {
  82            fp_exc_raise1(env, GETPC(), exc, regno, 0);
  83        }
  84    }
  85}
  86
  87/* Raise exceptions for ieee fp insns with software completion.  */
  88void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
  89{
  90    uint32_t exc = env->error_code & ~ignore;
  91    if (exc) {
  92        env->fpcr |= exc;
  93        exc &= ~ignore;
  94#ifdef CONFIG_USER_ONLY
  95        /*
  96         * In user mode, the kernel's software handler only
  97         * delivers a signal if the exception is enabled.
  98         */
  99        if (!(exc & env->fpcr_exc_enable)) {
 100            return;
 101        }
 102#else
 103        /*
 104         * In system mode, the software handler gets invoked
 105         * for any non-ignored exception.
 106         */
 107        if (!exc) {
 108            return;
 109        }
 110#endif
 111        exc &= env->fpcr_exc_enable;
 112        fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC);
 113    }
 114}
 115
 116/* Input handing without software completion.  Trap for all
 117   non-finite numbers.  */
 118void helper_ieee_input(CPUAlphaState *env, uint64_t val)
 119{
 120    uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
 121    uint64_t frac = val & 0xfffffffffffffull;
 122
 123    if (exp == 0) {
 124        /* Denormals without /S raise an exception.  */
 125        if (frac != 0) {
 126            arith_excp(env, GETPC(), EXC_M_INV, 0);
 127        }
 128    } else if (exp == 0x7ff) {
 129        /* Infinity or NaN.  */
 130        env->fpcr |= FPCR_INV;
 131        arith_excp(env, GETPC(), EXC_M_INV, 0);
 132    }
 133}
 134
 135/* Similar, but does not trap for infinities.  Used for comparisons.  */
 136void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val)
 137{
 138    uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
 139    uint64_t frac = val & 0xfffffffffffffull;
 140
 141    if (exp == 0) {
 142        /* Denormals without /S raise an exception.  */
 143        if (frac != 0) {
 144            arith_excp(env, GETPC(), EXC_M_INV, 0);
 145        }
 146    } else if (exp == 0x7ff && frac) {
 147        /* NaN.  */
 148        env->fpcr |= FPCR_INV;
 149        arith_excp(env, GETPC(), EXC_M_INV, 0);
 150    }
 151}
 152
 153/* Input handing with software completion.  Trap for denorms, unless DNZ
 154   is set.  If we try to support DNOD (which none of the produced hardware
 155   did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set;
 156   then the code downstream of that will need to cope with denorms sans
 157   flush_input_to_zero.  Most of it should work sanely, but there's
 158   nothing to compare with.  */
 159void helper_ieee_input_s(CPUAlphaState *env, uint64_t val)
 160{
 161    if (unlikely(2 * val - 1 < 0x1fffffffffffffull)
 162        && !env->fp_status.flush_inputs_to_zero) {
 163        arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0);
 164    }
 165}
 166
 167/* S floating (single) */
 168
 169/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg.  */
 170static inline uint64_t float32_to_s_int(uint32_t fi)
 171{
 172    uint32_t frac = fi & 0x7fffff;
 173    uint32_t sign = fi >> 31;
 174    uint32_t exp_msb = (fi >> 30) & 1;
 175    uint32_t exp_low = (fi >> 23) & 0x7f;
 176    uint32_t exp;
 177
 178    exp = (exp_msb << 10) | exp_low;
 179    if (exp_msb) {
 180        if (exp_low == 0x7f) {
 181            exp = 0x7ff;
 182        }
 183    } else {
 184        if (exp_low != 0x00) {
 185            exp |= 0x380;
 186        }
 187    }
 188
 189    return (((uint64_t)sign << 63)
 190            | ((uint64_t)exp << 52)
 191            | ((uint64_t)frac << 29));
 192}
 193
 194static inline uint64_t float32_to_s(float32 fa)
 195{
 196    CPU_FloatU a;
 197    a.f = fa;
 198    return float32_to_s_int(a.l);
 199}
 200
 201static inline uint32_t s_to_float32_int(uint64_t a)
 202{
 203    return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
 204}
 205
 206static inline float32 s_to_float32(uint64_t a)
 207{
 208    CPU_FloatU r;
 209    r.l = s_to_float32_int(a);
 210    return r.f;
 211}
 212
 213uint32_t helper_s_to_memory(uint64_t a)
 214{
 215    return s_to_float32_int(a);
 216}
 217
 218uint64_t helper_memory_to_s(uint32_t a)
 219{
 220    return float32_to_s_int(a);
 221}
 222
 223uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b)
 224{
 225    float32 fa, fb, fr;
 226
 227    fa = s_to_float32(a);
 228    fb = s_to_float32(b);
 229    fr = float32_add(fa, fb, &FP_STATUS);
 230    env->error_code = soft_to_fpcr_exc(env);
 231
 232    return float32_to_s(fr);
 233}
 234
 235uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b)
 236{
 237    float32 fa, fb, fr;
 238
 239    fa = s_to_float32(a);
 240    fb = s_to_float32(b);
 241    fr = float32_sub(fa, fb, &FP_STATUS);
 242    env->error_code = soft_to_fpcr_exc(env);
 243
 244    return float32_to_s(fr);
 245}
 246
 247uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b)
 248{
 249    float32 fa, fb, fr;
 250
 251    fa = s_to_float32(a);
 252    fb = s_to_float32(b);
 253    fr = float32_mul(fa, fb, &FP_STATUS);
 254    env->error_code = soft_to_fpcr_exc(env);
 255
 256    return float32_to_s(fr);
 257}
 258
 259uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b)
 260{
 261    float32 fa, fb, fr;
 262
 263    fa = s_to_float32(a);
 264    fb = s_to_float32(b);
 265    fr = float32_div(fa, fb, &FP_STATUS);
 266    env->error_code = soft_to_fpcr_exc(env);
 267
 268    return float32_to_s(fr);
 269}
 270
 271uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a)
 272{
 273    float32 fa, fr;
 274
 275    fa = s_to_float32(a);
 276    fr = float32_sqrt(fa, &FP_STATUS);
 277    env->error_code = soft_to_fpcr_exc(env);
 278
 279    return float32_to_s(fr);
 280}
 281
 282
 283/* T floating (double) */
 284static inline float64 t_to_float64(uint64_t a)
 285{
 286    /* Memory format is the same as float64 */
 287    CPU_DoubleU r;
 288    r.ll = a;
 289    return r.d;
 290}
 291
 292static inline uint64_t float64_to_t(float64 fa)
 293{
 294    /* Memory format is the same as float64 */
 295    CPU_DoubleU r;
 296    r.d = fa;
 297    return r.ll;
 298}
 299
 300uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b)
 301{
 302    float64 fa, fb, fr;
 303
 304    fa = t_to_float64(a);
 305    fb = t_to_float64(b);
 306    fr = float64_add(fa, fb, &FP_STATUS);
 307    env->error_code = soft_to_fpcr_exc(env);
 308
 309    return float64_to_t(fr);
 310}
 311
 312uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b)
 313{
 314    float64 fa, fb, fr;
 315
 316    fa = t_to_float64(a);
 317    fb = t_to_float64(b);
 318    fr = float64_sub(fa, fb, &FP_STATUS);
 319    env->error_code = soft_to_fpcr_exc(env);
 320
 321    return float64_to_t(fr);
 322}
 323
 324uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b)
 325{
 326    float64 fa, fb, fr;
 327
 328    fa = t_to_float64(a);
 329    fb = t_to_float64(b);
 330    fr = float64_mul(fa, fb, &FP_STATUS);
 331    env->error_code = soft_to_fpcr_exc(env);
 332
 333    return float64_to_t(fr);
 334}
 335
 336uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b)
 337{
 338    float64 fa, fb, fr;
 339
 340    fa = t_to_float64(a);
 341    fb = t_to_float64(b);
 342    fr = float64_div(fa, fb, &FP_STATUS);
 343    env->error_code = soft_to_fpcr_exc(env);
 344
 345    return float64_to_t(fr);
 346}
 347
 348uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a)
 349{
 350    float64 fa, fr;
 351
 352    fa = t_to_float64(a);
 353    fr = float64_sqrt(fa, &FP_STATUS);
 354    env->error_code = soft_to_fpcr_exc(env);
 355
 356    return float64_to_t(fr);
 357}
 358
 359/* Comparisons */
 360uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b)
 361{
 362    float64 fa, fb;
 363    uint64_t ret = 0;
 364
 365    fa = t_to_float64(a);
 366    fb = t_to_float64(b);
 367
 368    if (float64_unordered_quiet(fa, fb, &FP_STATUS)) {
 369        ret = 0x4000000000000000ULL;
 370    }
 371    env->error_code = soft_to_fpcr_exc(env);
 372
 373    return ret;
 374}
 375
 376uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b)
 377{
 378    float64 fa, fb;
 379    uint64_t ret = 0;
 380
 381    fa = t_to_float64(a);
 382    fb = t_to_float64(b);
 383
 384    if (float64_eq_quiet(fa, fb, &FP_STATUS)) {
 385        ret = 0x4000000000000000ULL;
 386    }
 387    env->error_code = soft_to_fpcr_exc(env);
 388
 389    return ret;
 390}
 391
 392uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b)
 393{
 394    float64 fa, fb;
 395    uint64_t ret = 0;
 396
 397    fa = t_to_float64(a);
 398    fb = t_to_float64(b);
 399
 400    if (float64_le(fa, fb, &FP_STATUS)) {
 401        ret = 0x4000000000000000ULL;
 402    }
 403    env->error_code = soft_to_fpcr_exc(env);
 404
 405    return ret;
 406}
 407
 408uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b)
 409{
 410    float64 fa, fb;
 411    uint64_t ret = 0;
 412
 413    fa = t_to_float64(a);
 414    fb = t_to_float64(b);
 415
 416    if (float64_lt(fa, fb, &FP_STATUS)) {
 417        ret = 0x4000000000000000ULL;
 418    }
 419    env->error_code = soft_to_fpcr_exc(env);
 420
 421    return ret;
 422}
 423
 424/* Floating point format conversion */
 425uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a)
 426{
 427    float64 fa;
 428    float32 fr;
 429
 430    fa = t_to_float64(a);
 431    fr = float64_to_float32(fa, &FP_STATUS);
 432    env->error_code = soft_to_fpcr_exc(env);
 433
 434    return float32_to_s(fr);
 435}
 436
 437uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
 438{
 439    float32 fa;
 440    float64 fr;
 441
 442    fa = s_to_float32(a);
 443    fr = float32_to_float64(fa, &FP_STATUS);
 444    env->error_code = soft_to_fpcr_exc(env);
 445
 446    return float64_to_t(fr);
 447}
 448
 449uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a)
 450{
 451    float32 fr = int64_to_float32(a, &FP_STATUS);
 452    env->error_code = soft_to_fpcr_exc(env);
 453
 454    return float32_to_s(fr);
 455}
 456
 457/* Implement float64 to uint64_t conversion without saturation -- we must
 458   supply the truncated result.  This behaviour is used by the compiler
 459   to get unsigned conversion for free with the same instruction.  */
 460
 461static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode)
 462{
 463    uint64_t frac, ret = 0;
 464    uint32_t exp, sign, exc = 0;
 465    int shift;
 466
 467    sign = (a >> 63);
 468    exp = (uint32_t)(a >> 52) & 0x7ff;
 469    frac = a & 0xfffffffffffffull;
 470
 471    if (exp == 0) {
 472        if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) {
 473            goto do_underflow;
 474        }
 475    } else if (exp == 0x7ff) {
 476        exc = FPCR_INV;
 477    } else {
 478        /* Restore implicit bit.  */
 479        frac |= 0x10000000000000ull;
 480
 481        shift = exp - 1023 - 52;
 482        if (shift >= 0) {
 483            /* In this case the number is so large that we must shift
 484               the fraction left.  There is no rounding to do.  */
 485            if (shift < 64) {
 486                ret = frac << shift;
 487            }
 488            /* Check for overflow.  Note the special case of -0x1p63.  */
 489            if (shift >= 11 && a != 0xC3E0000000000000ull) {
 490                exc = FPCR_IOV | FPCR_INE;
 491            }
 492        } else {
 493            uint64_t round;
 494
 495            /* In this case the number is smaller than the fraction as
 496               represented by the 52 bit number.  Here we must think
 497               about rounding the result.  Handle this by shifting the
 498               fractional part of the number into the high bits of ROUND.
 499               This will let us efficiently handle round-to-nearest.  */
 500            shift = -shift;
 501            if (shift < 63) {
 502                ret = frac >> shift;
 503                round = frac << (64 - shift);
 504            } else {
 505                /* The exponent is so small we shift out everything.
 506                   Leave a sticky bit for proper rounding below.  */
 507            do_underflow:
 508                round = 1;
 509            }
 510
 511            if (round) {
 512                exc = FPCR_INE;
 513                switch (roundmode) {
 514                case float_round_nearest_even:
 515                    if (round == (1ull << 63)) {
 516                        /* Fraction is exactly 0.5; round to even.  */
 517                        ret += (ret & 1);
 518                    } else if (round > (1ull << 63)) {
 519                        ret += 1;
 520                    }
 521                    break;
 522                case float_round_to_zero:
 523                    break;
 524                case float_round_up:
 525                    ret += 1 - sign;
 526                    break;
 527                case float_round_down:
 528                    ret += sign;
 529                    break;
 530                }
 531            }
 532        }
 533        if (sign) {
 534            ret = -ret;
 535        }
 536    }
 537    env->error_code = exc;
 538
 539    return ret;
 540}
 541
 542uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
 543{
 544    return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
 545}
 546
 547uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
 548{
 549    return do_cvttq(env, a, float_round_to_zero);
 550}
 551
 552uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
 553{
 554    float64 fr = int64_to_float64(a, &FP_STATUS);
 555    env->error_code = soft_to_fpcr_exc(env);
 556    return float64_to_t(fr);
 557}
 558
 559uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val)
 560{
 561    uint32_t exc = 0;
 562    if (val != (int32_t)val) {
 563        exc = FPCR_IOV | FPCR_INE;
 564    }
 565    env->error_code = exc;
 566
 567    return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29);
 568}
 569