]> Git Repo - qemu.git/blobdiff - target-ppc/dfp_helper.c
target-ppc: kvm: fix floating point registers sync on little-endian hosts
[qemu.git] / target-ppc / dfp_helper.c
index 548949fcf03a622f6b4abf97e60b45e1042ab5e0..db0ede698bcedc5b999a44435438a23e89cd34bf 100644 (file)
@@ -17,6 +17,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "qemu/osdep.h"
 #include "cpu.h"
 #include "exec/helper-proto.h"
 
@@ -170,27 +171,6 @@ static void dfp_prepare_decimal128(struct PPC_DFP *dfp, uint64_t *a,
     }
 }
 
-#define FP_FX       (1ull << FPSCR_FX)
-#define FP_FEX      (1ull << FPSCR_FEX)
-#define FP_OX       (1ull << FPSCR_OX)
-#define FP_OE       (1ull << FPSCR_OE)
-#define FP_UX       (1ull << FPSCR_UX)
-#define FP_UE       (1ull << FPSCR_UE)
-#define FP_XX       (1ull << FPSCR_XX)
-#define FP_XE       (1ull << FPSCR_XE)
-#define FP_ZX       (1ull << FPSCR_ZX)
-#define FP_ZE       (1ull << FPSCR_ZE)
-#define FP_VX       (1ull << FPSCR_VX)
-#define FP_VXSNAN   (1ull << FPSCR_VXSNAN)
-#define FP_VXISI    (1ull << FPSCR_VXISI)
-#define FP_VXIMZ    (1ull << FPSCR_VXIMZ)
-#define FP_VXZDZ    (1ull << FPSCR_VXZDZ)
-#define FP_VXIDI    (1ull << FPSCR_VXIDI)
-#define FP_VXVC     (1ull << FPSCR_VXVC)
-#define FP_VXCVI    (1ull << FPSCR_VXCVI)
-#define FP_VE       (1ull << FPSCR_VE)
-#define FP_FI       (1ull << FPSCR_FI)
-
 static void dfp_set_FPSCR_flag(struct PPC_DFP *dfp, uint64_t flag,
                 uint64_t enabled)
 {
@@ -411,9 +391,8 @@ static inline int dfp_get_digit(decNumber *dn, int n)
         return (dn->lsu[unit] / 10) % 10;
     case 2:
         return dn->lsu[unit] / 100;
-    default:
-        assert(0);
     }
+    g_assert_not_reached();
 }
 
 #define DFP_HELPER_TAB(op, dnop, postprocs, size)                              \
@@ -921,3 +900,397 @@ void helper_drdpq(CPUPPCState *env, uint64_t *t, uint64_t *b)
     t[0] = dfp.t64[0];
     t[1] = 0;
 }
+
+#define DFP_HELPER_CFFIX(op, size)                                             \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b)                   \
+{                                                                              \
+    struct PPC_DFP dfp;                                                        \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                                \
+    decNumberFromInt64(&dfp.t, (int64_t)(*b));                                 \
+    decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, &dfp.context); \
+    CFFIX_PPs(&dfp);                                                           \
+                                                                               \
+    if (size == 64) {                                                          \
+        t[0] = dfp.t64[0];                                                     \
+    } else if (size == 128) {                                                  \
+        t[0] = dfp.t64[HI_IDX];                                                \
+        t[1] = dfp.t64[LO_IDX];                                                \
+    }                                                                          \
+}
+
+static void CFFIX_PPs(struct PPC_DFP *dfp)
+{
+    dfp_set_FPRF_from_FRT(dfp);
+    dfp_check_for_XX(dfp);
+}
+
+DFP_HELPER_CFFIX(dcffix, 64)
+DFP_HELPER_CFFIX(dcffixq, 128)
+
+#define DFP_HELPER_CTFIX(op, size)                                            \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b)                  \
+{                                                                             \
+    struct PPC_DFP dfp;                                                       \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                               \
+                                                                              \
+    if (unlikely(decNumberIsSpecial(&dfp.b))) {                               \
+        uint64_t invalid_flags = FP_VX | FP_VXCVI;                            \
+        if (decNumberIsInfinite(&dfp.b)) {                                    \
+            dfp.t64[0] = decNumberIsNegative(&dfp.b) ? INT64_MIN : INT64_MAX; \
+        } else { /* NaN */                                                    \
+            dfp.t64[0] = INT64_MIN;                                           \
+            if (decNumberIsSNaN(&dfp.b)) {                                    \
+                invalid_flags |= FP_VXSNAN;                                   \
+            }                                                                 \
+        }                                                                     \
+        dfp_set_FPSCR_flag(&dfp, invalid_flags, FP_VE);                       \
+    } else if (unlikely(decNumberIsZero(&dfp.b))) {                           \
+        dfp.t64[0] = 0;                                                       \
+    } else {                                                                  \
+        decNumberToIntegralExact(&dfp.b, &dfp.b, &dfp.context);               \
+        dfp.t64[0] = decNumberIntegralToInt64(&dfp.b, &dfp.context);          \
+        if (decContextTestStatus(&dfp.context, DEC_Invalid_operation)) {      \
+            dfp.t64[0] = decNumberIsNegative(&dfp.b) ? INT64_MIN : INT64_MAX; \
+            dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FP_VE);                \
+        } else {                                                              \
+            dfp_check_for_XX(&dfp);                                           \
+        }                                                                     \
+    }                                                                         \
+                                                                              \
+    *t = dfp.t64[0];                                                          \
+}
+
+DFP_HELPER_CTFIX(dctfix, 64)
+DFP_HELPER_CTFIX(dctfixq, 128)
+
+static inline void dfp_set_bcd_digit_64(uint64_t *t, uint8_t digit,
+                                            unsigned n)
+{
+    *t |= ((uint64_t)(digit & 0xF) << (n << 2));
+}
+
+static inline void dfp_set_bcd_digit_128(uint64_t *t, uint8_t digit,
+                                             unsigned n)
+{
+    t[(n & 0x10) ? HI_IDX : LO_IDX] |=
+        ((uint64_t)(digit & 0xF) << ((n & 15) << 2));
+}
+
+static inline void dfp_set_sign_64(uint64_t *t, uint8_t sgn)
+{
+    *t <<= 4;
+    *t |= (sgn & 0xF);
+}
+
+static inline void dfp_set_sign_128(uint64_t *t, uint8_t sgn)
+{
+    t[HI_IDX] <<= 4;
+    t[HI_IDX] |= (t[LO_IDX] >> 60);
+    t[LO_IDX] <<= 4;
+    t[LO_IDX] |= (sgn & 0xF);
+}
+
+#define DFP_HELPER_DEDPD(op, size)                                        \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, uint32_t sp) \
+{                                                                         \
+    struct PPC_DFP dfp;                                                   \
+    uint8_t digits[34];                                                   \
+    int i, N;                                                             \
+                                                                          \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                           \
+                                                                          \
+    decNumberGetBCD(&dfp.b, digits);                                      \
+    dfp.t64[0] = dfp.t64[1] = 0;                                          \
+    N = dfp.b.digits;                                                     \
+                                                                          \
+    for (i = 0; (i < N) && (i < (size)/4); i++) {                         \
+        dfp_set_bcd_digit_##size(dfp.t64, digits[N-i-1], i);              \
+    }                                                                     \
+                                                                          \
+    if (sp & 2) {                                                         \
+        uint8_t sgn;                                                      \
+                                                                          \
+        if (decNumberIsNegative(&dfp.b)) {                                \
+            sgn = 0xD;                                                    \
+        } else {                                                          \
+            sgn = ((sp & 1) ? 0xF : 0xC);                                 \
+        }                                                                 \
+        dfp_set_sign_##size(dfp.t64, sgn);                                \
+    }                                                                     \
+                                                                          \
+    if (size == 64) {                                                     \
+        t[0] = dfp.t64[0];                                                \
+    } else if (size == 128) {                                             \
+        t[0] = dfp.t64[HI_IDX];                                           \
+        t[1] = dfp.t64[LO_IDX];                                           \
+    }                                                                     \
+}
+
+DFP_HELPER_DEDPD(ddedpd, 64)
+DFP_HELPER_DEDPD(ddedpdq, 128)
+
+static inline uint8_t dfp_get_bcd_digit_64(uint64_t *t, unsigned n)
+{
+    return *t >> ((n << 2) & 63) & 15;
+}
+
+static inline uint8_t dfp_get_bcd_digit_128(uint64_t *t, unsigned n)
+{
+    return t[(n & 0x10) ? HI_IDX : LO_IDX] >> ((n << 2) & 63) & 15;
+}
+
+#define DFP_HELPER_ENBCD(op, size)                                           \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, uint32_t s)     \
+{                                                                            \
+    struct PPC_DFP dfp;                                                      \
+    uint8_t digits[32];                                                      \
+    int n = 0, offset = 0, sgn = 0, nonzero = 0;                             \
+                                                                             \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                              \
+                                                                             \
+    decNumberZero(&dfp.t);                                                   \
+                                                                             \
+    if (s) {                                                                 \
+        uint8_t sgnNibble = dfp_get_bcd_digit_##size(dfp.b64, offset++);     \
+        switch (sgnNibble) {                                                 \
+        case 0xD:                                                            \
+        case 0xB:                                                            \
+            sgn = 1;                                                         \
+            break;                                                           \
+        case 0xC:                                                            \
+        case 0xF:                                                            \
+        case 0xA:                                                            \
+        case 0xE:                                                            \
+            sgn = 0;                                                         \
+            break;                                                           \
+        default:                                                             \
+            dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE);            \
+            return;                                                          \
+        }                                                                    \
+        }                                                                    \
+                                                                             \
+    while (offset < (size)/4) {                                              \
+        n++;                                                                 \
+        digits[(size)/4-n] = dfp_get_bcd_digit_##size(dfp.b64, offset++);    \
+        if (digits[(size)/4-n] > 10) {                                       \
+            dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE);            \
+            return;                                                          \
+        } else {                                                             \
+            nonzero |= (digits[(size)/4-n] > 0);                             \
+        }                                                                    \
+    }                                                                        \
+                                                                             \
+    if (nonzero) {                                                           \
+        decNumberSetBCD(&dfp.t, digits+((size)/4)-n, n);                     \
+    }                                                                        \
+                                                                             \
+    if (s && sgn)  {                                                         \
+        dfp.t.bits |= DECNEG;                                                \
+    }                                                                        \
+    decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t,              \
+                              &dfp.context);                                 \
+    dfp_set_FPRF_from_FRT(&dfp);                                             \
+    if ((size) == 64) {                                                      \
+        t[0] = dfp.t64[0];                                                   \
+    } else if ((size) == 128) {                                              \
+        t[0] = dfp.t64[HI_IDX];                                              \
+        t[1] = dfp.t64[LO_IDX];                                              \
+    }                                                                        \
+}
+
+DFP_HELPER_ENBCD(denbcd, 64)
+DFP_HELPER_ENBCD(denbcdq, 128)
+
+#define DFP_HELPER_XEX(op, size)                               \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b)   \
+{                                                              \
+    struct PPC_DFP dfp;                                        \
+                                                               \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                \
+                                                               \
+    if (unlikely(decNumberIsSpecial(&dfp.b))) {                \
+        if (decNumberIsInfinite(&dfp.b)) {                     \
+            *t = -1;                                           \
+        } else if (decNumberIsSNaN(&dfp.b)) {                  \
+            *t = -3;                                           \
+        } else if (decNumberIsQNaN(&dfp.b)) {                  \
+            *t = -2;                                           \
+        } else {                                               \
+            assert(0);                                         \
+        }                                                      \
+    } else {                                                   \
+        if ((size) == 64) {                                    \
+            *t = dfp.b.exponent + 398;                         \
+        } else if ((size) == 128) {                            \
+            *t = dfp.b.exponent + 6176;                        \
+        } else {                                               \
+            assert(0);                                         \
+        }                                                      \
+    }                                                          \
+}
+
+DFP_HELPER_XEX(dxex, 64)
+DFP_HELPER_XEX(dxexq, 128)
+
+static void dfp_set_raw_exp_64(uint64_t *t, uint64_t raw)
+{
+    *t &= 0x8003ffffffffffffULL;
+    *t |= (raw << (63-13));
+}
+
+static void dfp_set_raw_exp_128(uint64_t *t, uint64_t raw)
+{
+    t[HI_IDX] &= 0x80003fffffffffffULL;
+    t[HI_IDX] |= (raw << (63-17));
+}
+
+#define DFP_HELPER_IEX(op, size)                                          \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, uint64_t *b) \
+{                                                                         \
+    struct PPC_DFP dfp;                                                   \
+    uint64_t raw_qnan, raw_snan, raw_inf, max_exp;                        \
+    int bias;                                                             \
+    int64_t exp = *((int64_t *)a);                                        \
+                                                                          \
+    dfp_prepare_decimal##size(&dfp, 0, b, env);                           \
+                                                                          \
+    if ((size) == 64) {                                                   \
+        max_exp = 767;                                                    \
+        raw_qnan = 0x1F00;                                                \
+        raw_snan = 0x1F80;                                                \
+        raw_inf = 0x1E00;                                                 \
+        bias = 398;                                                       \
+    } else if ((size) == 128) {                                           \
+        max_exp = 12287;                                                  \
+        raw_qnan = 0x1f000;                                               \
+        raw_snan = 0x1f800;                                               \
+        raw_inf = 0x1e000;                                                \
+        bias = 6176;                                                      \
+    } else {                                                              \
+        assert(0);                                                        \
+    }                                                                     \
+                                                                          \
+    if (unlikely((exp < 0) || (exp > max_exp))) {                         \
+        dfp.t64[0] = dfp.b64[0];                                          \
+        dfp.t64[1] = dfp.b64[1];                                          \
+        if (exp == -1) {                                                  \
+            dfp_set_raw_exp_##size(dfp.t64, raw_inf);                     \
+        } else if (exp == -3) {                                           \
+            dfp_set_raw_exp_##size(dfp.t64, raw_snan);                    \
+        } else {                                                          \
+            dfp_set_raw_exp_##size(dfp.t64, raw_qnan);                    \
+        }                                                                 \
+    } else {                                                              \
+        dfp.t = dfp.b;                                                    \
+        if (unlikely(decNumberIsSpecial(&dfp.t))) {                       \
+            dfp.t.bits &= ~DECSPECIAL;                                    \
+        }                                                                 \
+        dfp.t.exponent = exp - bias;                                      \
+        decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t,       \
+                                  &dfp.context);                          \
+    }                                                                     \
+    if (size == 64) {                                                     \
+        t[0] = dfp.t64[0];                                                \
+    } else if (size == 128) {                                             \
+        t[0] = dfp.t64[HI_IDX];                                           \
+        t[1] = dfp.t64[LO_IDX];                                           \
+    }                                                                     \
+}
+
+DFP_HELPER_IEX(diex, 64)
+DFP_HELPER_IEX(diexq, 128)
+
+static void dfp_clear_lmd_from_g5msb(uint64_t *t)
+{
+
+    /* The most significant 5 bits of the PowerPC DFP format combine bits  */
+    /* from the left-most decimal digit (LMD) and the biased exponent.     */
+    /* This  routine clears the LMD bits while preserving the exponent     */
+    /*  bits.  See "Figure 80: Encoding of bits 0:4 of the G field for     */
+    /*  Finite Numbers" in the Power ISA for additional details.           */
+
+    uint64_t g5msb = (*t >> 58) & 0x1F;
+
+    if ((g5msb >> 3) < 3) { /* LMD in [0-7] ? */
+       *t &= ~(7ULL << 58);
+    } else {
+       switch (g5msb & 7) {
+       case 0:
+       case 1:
+           g5msb = 0;
+           break;
+       case 2:
+       case 3:
+           g5msb = 0x8;
+           break;
+       case 4:
+       case 5:
+           g5msb = 0x10;
+           break;
+       case 6:
+           g5msb = 0x1E;
+           break;
+       case 7:
+           g5msb = 0x1F;
+           break;
+       }
+
+        *t &= ~(0x1fULL << 58);
+        *t |= (g5msb << 58);
+    }
+}
+
+#define DFP_HELPER_SHIFT(op, size, shift_left)                      \
+void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a,        \
+                 uint32_t sh)                                       \
+{                                                                   \
+    struct PPC_DFP dfp;                                             \
+    unsigned max_digits = ((size) == 64) ? 16 : 34;                 \
+                                                                    \
+    dfp_prepare_decimal##size(&dfp, a, 0, env);                     \
+                                                                    \
+    if (sh <= max_digits) {                                         \
+                                                                    \
+        decNumber shd;                                              \
+        unsigned special = dfp.a.bits & DECSPECIAL;                 \
+                                                                    \
+        if (shift_left) {                                           \
+            decNumberFromUInt32(&shd, sh);                          \
+        } else {                                                    \
+            decNumberFromInt32(&shd, -((int32_t)sh));               \
+        }                                                           \
+                                                                    \
+        dfp.a.bits &= ~DECSPECIAL;                                  \
+        decNumberShift(&dfp.t, &dfp.a, &shd, &dfp.context);         \
+                                                                    \
+        dfp.t.bits |= special;                                      \
+        if (special && (dfp.t.digits >= max_digits)) {              \
+            dfp.t.digits = max_digits - 1;                          \
+        }                                                           \
+                                                                    \
+        decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \
+                                  &dfp.context);                    \
+    } else {                                                        \
+        if ((size) == 64) {                                         \
+            dfp.t64[0] = dfp.a64[0] & 0xFFFC000000000000ULL;        \
+            dfp_clear_lmd_from_g5msb(dfp.t64);                      \
+        } else {                                                    \
+            dfp.t64[HI_IDX] = dfp.a64[HI_IDX] &                     \
+                              0xFFFFC00000000000ULL;                \
+            dfp_clear_lmd_from_g5msb(dfp.t64 + HI_IDX);             \
+            dfp.t64[LO_IDX] = 0;                                    \
+        }                                                           \
+    }                                                               \
+                                                                    \
+    if ((size) == 64) {                                             \
+        t[0] = dfp.t64[0];                                          \
+    } else {                                                        \
+        t[0] = dfp.t64[HI_IDX];                                     \
+        t[1] = dfp.t64[LO_IDX];                                     \
+    }                                                               \
+}
+
+DFP_HELPER_SHIFT(dscli, 64, 1)
+DFP_HELPER_SHIFT(dscliq, 128, 1)
+DFP_HELPER_SHIFT(dscri, 64, 0)
+DFP_HELPER_SHIFT(dscriq, 128, 0)
This page took 0.034124 seconds and 4 git commands to generate.