qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

fpu_helper.c (14605B)


      1 /*
      2  *  TriCore emulation for qemu: fpu helper.
      3  *
      4  *  Copyright (c) 2016 Bastian Koppelmann University of Paderborn
      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/helper-proto.h"
     23 #include "fpu/softfloat.h"
     24 
     25 #define QUIET_NAN 0x7fc00000
     26 #define ADD_NAN   0x7fc00001
     27 #define SQRT_NAN  0x7fc00004
     28 #define DIV_NAN   0x7fc00008
     29 #define MUL_NAN   0x7fc00002
     30 #define FPU_FS PSW_USB_C
     31 #define FPU_FI PSW_USB_V
     32 #define FPU_FV PSW_USB_SV
     33 #define FPU_FZ PSW_USB_AV
     34 #define FPU_FU PSW_USB_SAV
     35 
     36 #define float32_sqrt_nan make_float32(SQRT_NAN)
     37 #define float32_quiet_nan make_float32(QUIET_NAN)
     38 
     39 /* we don't care about input_denormal */
     40 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
     41 {
     42     return get_float_exception_flags(&env->fp_status)
     43            & (float_flag_invalid
     44               | float_flag_overflow
     45               | float_flag_underflow
     46               | float_flag_output_denormal
     47               | float_flag_divbyzero
     48               | float_flag_inexact);
     49 }
     50 
     51 static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
     52                                            float32 arg3, float32 result,
     53                                            uint32_t muladd_negate_c)
     54 {
     55     uint32_t aSign, bSign, cSign;
     56     uint32_t aExp, bExp, cExp;
     57 
     58     if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
     59         float32_is_any_nan(arg3)) {
     60         return QUIET_NAN;
     61     } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
     62         return MUL_NAN;
     63     } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
     64         return MUL_NAN;
     65     } else {
     66         aSign = arg1 >> 31;
     67         bSign = arg2 >> 31;
     68         cSign = arg3 >> 31;
     69 
     70         aExp = (arg1 >> 23) & 0xff;
     71         bExp = (arg2 >> 23) & 0xff;
     72         cExp = (arg3 >> 23) & 0xff;
     73 
     74         if (muladd_negate_c) {
     75             cSign ^= 1;
     76         }
     77         if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
     78             if (aSign ^ bSign ^ cSign) {
     79                 return ADD_NAN;
     80             }
     81         }
     82     }
     83 
     84     return result;
     85 }
     86 
     87 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
     88 {
     89     uint8_t some_excp = 0;
     90     set_float_exception_flags(0, &env->fp_status);
     91 
     92     if (flags & float_flag_invalid) {
     93         env->FPU_FI = 1 << 31;
     94         some_excp = 1;
     95     }
     96 
     97     if (flags & float_flag_overflow) {
     98         env->FPU_FV = 1 << 31;
     99         some_excp = 1;
    100     }
    101 
    102     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
    103         env->FPU_FU = 1 << 31;
    104         some_excp = 1;
    105     }
    106 
    107     if (flags & float_flag_divbyzero) {
    108         env->FPU_FZ = 1 << 31;
    109         some_excp = 1;
    110     }
    111 
    112     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
    113         env->PSW |= 1 << 26;
    114         some_excp = 1;
    115     }
    116 
    117     env->FPU_FS = some_excp;
    118 }
    119 
    120 #define FADD_SUB(op)                                                           \
    121 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
    122 {                                                                              \
    123     float32 arg1 = make_float32(r1);                                           \
    124     float32 arg2 = make_float32(r2);                                           \
    125     uint32_t flags;                                                            \
    126     float32 f_result;                                                          \
    127                                                                                \
    128     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
    129     flags = f_get_excp_flags(env);                                             \
    130     if (flags) {                                                               \
    131         /* If the output is a NaN, but the inputs aren't,                      \
    132            we return a unique value.  */                                       \
    133         if ((flags & float_flag_invalid)                                       \
    134             && !float32_is_any_nan(arg1)                                       \
    135             && !float32_is_any_nan(arg2)) {                                    \
    136             f_result = ADD_NAN;                                                \
    137         }                                                                      \
    138         f_update_psw_flags(env, flags);                                        \
    139     } else {                                                                   \
    140         env->FPU_FS = 0;                                                       \
    141     }                                                                          \
    142     return (uint32_t)f_result;                                                 \
    143 }
    144 FADD_SUB(add)
    145 FADD_SUB(sub)
    146 
    147 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    148 {
    149     uint32_t flags;
    150     float32 arg1 = make_float32(r1);
    151     float32 arg2 = make_float32(r2);
    152     float32 f_result;
    153 
    154     f_result = float32_mul(arg1, arg2, &env->fp_status);
    155 
    156     flags = f_get_excp_flags(env);
    157     if (flags) {
    158         /* If the output is a NaN, but the inputs aren't,
    159            we return a unique value.  */
    160         if ((flags & float_flag_invalid)
    161             && !float32_is_any_nan(arg1)
    162             && !float32_is_any_nan(arg2)) {
    163                 f_result = MUL_NAN;
    164         }
    165         f_update_psw_flags(env, flags);
    166     } else {
    167         env->FPU_FS = 0;
    168     }
    169     return (uint32_t)f_result;
    170 
    171 }
    172 
    173 /*
    174  * Target TriCore QSEED.F significand Lookup Table
    175  *
    176  * The QSEED.F output significand depends on the least-significant
    177  * exponent bit and the 6 most-significant significand bits.
    178  *
    179  * IEEE 754 float datatype
    180  * partitioned into Sign (S), Exponent (E) and Significand (M):
    181  *
    182  * S   E E E E E E E E   M M M M M M ...
    183  *    |             |               |
    184  *    +------+------+-------+-------+
    185  *           |              |
    186  *          for        lookup table
    187  *      calculating     index for
    188  *        output E       output M
    189  *
    190  * This lookup table was extracted by analyzing QSEED output
    191  * from the real hardware
    192  */
    193 static const uint8_t target_qseed_significand_table[128] = {
    194     253, 252, 245, 244, 239, 238, 231, 230, 225, 224, 217, 216,
    195     211, 210, 205, 204, 201, 200, 195, 194, 189, 188, 185, 184,
    196     179, 178, 175, 174, 169, 168, 165, 164, 161, 160, 157, 156,
    197     153, 152, 149, 148, 145, 144, 141, 140, 137, 136, 133, 132,
    198     131, 130, 127, 126, 123, 122, 121, 120, 117, 116, 115, 114,
    199     111, 110, 109, 108, 103, 102, 99, 98, 93, 92, 89, 88, 83,
    200     82, 79, 78, 75, 74, 71, 70, 67, 66, 63, 62, 59, 58, 55,
    201     54, 53, 52, 49, 48, 45, 44, 43, 42, 39, 38, 37, 36, 33,
    202     32, 31, 30, 27, 26, 25, 24, 23, 22, 19, 18, 17, 16, 15,
    203     14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2
    204 };
    205 
    206 uint32_t helper_qseed(CPUTriCoreState *env, uint32_t r1)
    207 {
    208     uint32_t arg1, S, E, M, E_minus_one, m_idx;
    209     uint32_t new_E, new_M, new_S, result;
    210 
    211     arg1 = make_float32(r1);
    212 
    213     /* fetch IEEE-754 fields S, E and the uppermost 6-bit of M */
    214     S = extract32(arg1, 31, 1);
    215     E = extract32(arg1, 23, 8);
    216     M = extract32(arg1, 17, 6);
    217 
    218     if (float32_is_any_nan(arg1)) {
    219         result = float32_quiet_nan;
    220     } else if (float32_is_zero_or_denormal(arg1)) {
    221         if (float32_is_neg(arg1)) {
    222             result = float32_infinity | (1 << 31);
    223         } else {
    224             result = float32_infinity;
    225         }
    226     } else if (float32_is_neg(arg1)) {
    227         result = float32_sqrt_nan;
    228     } else if (float32_is_infinity(arg1)) {
    229         result = float32_zero;
    230     } else {
    231         E_minus_one = E - 1;
    232         m_idx = ((E_minus_one & 1) << 6) | M;
    233         new_S = S;
    234         new_E = 0xBD - E_minus_one / 2;
    235         new_M = target_qseed_significand_table[m_idx];
    236 
    237         result = 0;
    238         result = deposit32(result, 31, 1, new_S);
    239         result = deposit32(result, 23, 8, new_E);
    240         result = deposit32(result, 15, 8, new_M);
    241     }
    242 
    243     if (float32_is_signaling_nan(arg1, &env->fp_status)
    244         || result == float32_sqrt_nan) {
    245         env->FPU_FI = 1 << 31;
    246         env->FPU_FS = 1;
    247     } else {
    248         env->FPU_FS = 0;
    249     }
    250 
    251     return (uint32_t) result;
    252 }
    253 
    254 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    255 {
    256     uint32_t flags;
    257     float32 arg1 = make_float32(r1);
    258     float32 arg2 = make_float32(r2);
    259     float32 f_result;
    260 
    261     f_result = float32_div(arg1, arg2 , &env->fp_status);
    262 
    263     flags = f_get_excp_flags(env);
    264     if (flags) {
    265         /* If the output is a NaN, but the inputs aren't,
    266            we return a unique value.  */
    267         if ((flags & float_flag_invalid)
    268             && !float32_is_any_nan(arg1)
    269             && !float32_is_any_nan(arg2)) {
    270                 f_result = DIV_NAN;
    271         }
    272         f_update_psw_flags(env, flags);
    273     } else {
    274         env->FPU_FS = 0;
    275     }
    276 
    277     return (uint32_t)f_result;
    278 }
    279 
    280 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
    281                       uint32_t r2, uint32_t r3)
    282 {
    283     uint32_t flags;
    284     float32 arg1 = make_float32(r1);
    285     float32 arg2 = make_float32(r2);
    286     float32 arg3 = make_float32(r3);
    287     float32 f_result;
    288 
    289     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
    290 
    291     flags = f_get_excp_flags(env);
    292     if (flags) {
    293         if (flags & float_flag_invalid) {
    294             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
    295             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
    296             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
    297             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
    298         }
    299         f_update_psw_flags(env, flags);
    300     } else {
    301         env->FPU_FS = 0;
    302     }
    303     return (uint32_t)f_result;
    304 }
    305 
    306 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
    307                       uint32_t r2, uint32_t r3)
    308 {
    309     uint32_t flags;
    310     float32 arg1 = make_float32(r1);
    311     float32 arg2 = make_float32(r2);
    312     float32 arg3 = make_float32(r3);
    313     float32 f_result;
    314 
    315     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
    316                               &env->fp_status);
    317 
    318     flags = f_get_excp_flags(env);
    319     if (flags) {
    320         if (flags & float_flag_invalid) {
    321             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
    322             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
    323             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
    324 
    325             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
    326         }
    327         f_update_psw_flags(env, flags);
    328     } else {
    329         env->FPU_FS = 0;
    330     }
    331     return (uint32_t)f_result;
    332 }
    333 
    334 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    335 {
    336     uint32_t result, flags;
    337     float32 arg1 = make_float32(r1);
    338     float32 arg2 = make_float32(r2);
    339 
    340     set_flush_inputs_to_zero(0, &env->fp_status);
    341 
    342     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
    343     result |= float32_is_denormal(arg1) << 4;
    344     result |= float32_is_denormal(arg2) << 5;
    345 
    346     flags = f_get_excp_flags(env);
    347     if (flags) {
    348         f_update_psw_flags(env, flags);
    349     } else {
    350         env->FPU_FS = 0;
    351     }
    352 
    353     set_flush_inputs_to_zero(1, &env->fp_status);
    354     return result;
    355 }
    356 
    357 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
    358 {
    359     float32 f_arg = make_float32(arg);
    360     int32_t result, flags;
    361 
    362     result = float32_to_int32(f_arg, &env->fp_status);
    363 
    364     flags = f_get_excp_flags(env);
    365     if (flags) {
    366         if (float32_is_any_nan(f_arg)) {
    367             result = 0;
    368         }
    369         f_update_psw_flags(env, flags);
    370     } else {
    371         env->FPU_FS = 0;
    372     }
    373     return (uint32_t)result;
    374 }
    375 
    376 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
    377 {
    378     float32 f_result;
    379     uint32_t flags;
    380     f_result = int32_to_float32(arg, &env->fp_status);
    381 
    382     flags = f_get_excp_flags(env);
    383     if (flags) {
    384         f_update_psw_flags(env, flags);
    385     } else {
    386         env->FPU_FS = 0;
    387     }
    388     return (uint32_t)f_result;
    389 }
    390 
    391 uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg)
    392 {
    393     float32 f_result;
    394     uint32_t flags;
    395 
    396     f_result = uint32_to_float32(arg, &env->fp_status);
    397 
    398     flags = f_get_excp_flags(env);
    399     if (flags) {
    400         f_update_psw_flags(env, flags);
    401     } else {
    402         env->FPU_FS = 0;
    403     }
    404     return (uint32_t)f_result;
    405 }
    406 
    407 uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg)
    408 {
    409     float32 f_arg = make_float32(arg);
    410     uint32_t result;
    411     int32_t flags;
    412 
    413     result = float32_to_int32_round_to_zero(f_arg, &env->fp_status);
    414 
    415     flags = f_get_excp_flags(env);
    416     if (flags & float_flag_invalid) {
    417         flags &= ~float_flag_inexact;
    418         if (float32_is_any_nan(f_arg)) {
    419             result = 0;
    420         }
    421     }
    422 
    423     if (flags) {
    424         f_update_psw_flags(env, flags);
    425     } else {
    426         env->FPU_FS = 0;
    427     }
    428 
    429     return result;
    430 }
    431 
    432 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
    433 {
    434     float32 f_arg = make_float32(arg);
    435     uint32_t result;
    436     int32_t flags;
    437 
    438     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
    439 
    440     flags = f_get_excp_flags(env);
    441     if (flags & float_flag_invalid) {
    442         flags &= ~float_flag_inexact;
    443         if (float32_is_any_nan(f_arg)) {
    444             result = 0;
    445         }
    446     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
    447         flags = float_flag_invalid;
    448         result = 0;
    449     }
    450 
    451     if (flags) {
    452         f_update_psw_flags(env, flags);
    453     } else {
    454         env->FPU_FS = 0;
    455     }
    456     return result;
    457 }
    458 
    459 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
    460 {
    461     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
    462     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
    463     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
    464     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
    465     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
    466     /* clear FX and RM */
    467     env->PSW &= ~(extract32(arg, 10, 1) << 26);
    468     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
    469 
    470     fpu_set_state(env);
    471 }