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