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