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