aboutsummaryrefslogtreecommitdiff
path: root/target/i386/fpu_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/i386/fpu_helper.c')
-rw-r--r--target/i386/fpu_helper.c258
1 files changed, 229 insertions, 29 deletions
diff --git a/target/i386/fpu_helper.c b/target/i386/fpu_helper.c
index b34fa784eb..8ef5b463ea 100644
--- a/target/i386/fpu_helper.c
+++ b/target/i386/fpu_helper.c
@@ -59,8 +59,13 @@
#define FPUC_EM 0x3f
#define floatx80_lg2 make_floatx80(0x3ffd, 0x9a209a84fbcff799LL)
+#define floatx80_lg2_d make_floatx80(0x3ffd, 0x9a209a84fbcff798LL)
#define floatx80_l2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcLL)
+#define floatx80_l2e_d make_floatx80(0x3fff, 0xb8aa3b295c17f0bbLL)
#define floatx80_l2t make_floatx80(0x4000, 0xd49a784bcd1b8afeLL)
+#define floatx80_l2t_u make_floatx80(0x4000, 0xd49a784bcd1b8affLL)
+#define floatx80_ln2_d make_floatx80(0x3ffe, 0xb17217f7d1cf79abLL)
+#define floatx80_pi_d make_floatx80(0x4000, 0xc90fdaa22168c234LL)
#if !defined(CONFIG_USER_ONLY)
static qemu_irq ferr_irq;
@@ -156,12 +161,32 @@ static void fpu_set_exception(CPUX86State *env, int mask)
}
}
+static inline uint8_t save_exception_flags(CPUX86State *env)
+{
+ uint8_t old_flags = get_float_exception_flags(&env->fp_status);
+ set_float_exception_flags(0, &env->fp_status);
+ return old_flags;
+}
+
+static void merge_exception_flags(CPUX86State *env, uint8_t old_flags)
+{
+ uint8_t new_flags = get_float_exception_flags(&env->fp_status);
+ float_raise(old_flags, &env->fp_status);
+ fpu_set_exception(env,
+ ((new_flags & float_flag_invalid ? FPUS_IE : 0) |
+ (new_flags & float_flag_divbyzero ? FPUS_ZE : 0) |
+ (new_flags & float_flag_overflow ? FPUS_OE : 0) |
+ (new_flags & float_flag_underflow ? FPUS_UE : 0) |
+ (new_flags & float_flag_inexact ? FPUS_PE : 0) |
+ (new_flags & float_flag_input_denormal ? FPUS_DE : 0)));
+}
+
static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b)
{
- if (floatx80_is_zero(b)) {
- fpu_set_exception(env, FPUS_ZE);
- }
- return floatx80_div(a, b, &env->fp_status);
+ uint8_t old_flags = save_exception_flags(env);
+ floatx80 ret = floatx80_div(a, b, &env->fp_status);
+ merge_exception_flags(env, old_flags);
+ return ret;
}
static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr)
@@ -178,6 +203,7 @@ static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr)
void helper_flds_FT0(CPUX86State *env, uint32_t val)
{
+ uint8_t old_flags = save_exception_flags(env);
union {
float32 f;
uint32_t i;
@@ -185,10 +211,12 @@ void helper_flds_FT0(CPUX86State *env, uint32_t val)
u.i = val;
FT0 = float32_to_floatx80(u.f, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fldl_FT0(CPUX86State *env, uint64_t val)
{
+ uint8_t old_flags = save_exception_flags(env);
union {
float64 f;
uint64_t i;
@@ -196,6 +224,7 @@ void helper_fldl_FT0(CPUX86State *env, uint64_t val)
u.i = val;
FT0 = float64_to_floatx80(u.f, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fildl_FT0(CPUX86State *env, int32_t val)
@@ -205,6 +234,7 @@ void helper_fildl_FT0(CPUX86State *env, int32_t val)
void helper_flds_ST0(CPUX86State *env, uint32_t val)
{
+ uint8_t old_flags = save_exception_flags(env);
int new_fpstt;
union {
float32 f;
@@ -216,10 +246,12 @@ void helper_flds_ST0(CPUX86State *env, uint32_t val)
env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
+ merge_exception_flags(env, old_flags);
}
void helper_fldl_ST0(CPUX86State *env, uint64_t val)
{
+ uint8_t old_flags = save_exception_flags(env);
int new_fpstt;
union {
float64 f;
@@ -231,6 +263,7 @@ void helper_fldl_ST0(CPUX86State *env, uint64_t val)
env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
+ merge_exception_flags(env, old_flags);
}
void helper_fildl_ST0(CPUX86State *env, int32_t val)
@@ -255,90 +288,108 @@ void helper_fildll_ST0(CPUX86State *env, int64_t val)
uint32_t helper_fsts_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
union {
float32 f;
uint32_t i;
} u;
u.f = floatx80_to_float32(ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
return u.i;
}
uint64_t helper_fstl_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
union {
float64 f;
uint64_t i;
} u;
u.f = floatx80_to_float64(ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
return u.i;
}
int32_t helper_fist_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int32_t val;
val = floatx80_to_int32(ST0, &env->fp_status);
if (val != (int16_t)val) {
+ set_float_exception_flags(float_flag_invalid, &env->fp_status);
val = -32768;
}
+ merge_exception_flags(env, old_flags);
return val;
}
int32_t helper_fistl_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int32_t val;
- signed char old_exp_flags;
-
- old_exp_flags = get_float_exception_flags(&env->fp_status);
- set_float_exception_flags(0, &env->fp_status);
val = floatx80_to_int32(ST0, &env->fp_status);
if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) {
val = 0x80000000;
}
- set_float_exception_flags(get_float_exception_flags(&env->fp_status)
- | old_exp_flags, &env->fp_status);
+ merge_exception_flags(env, old_flags);
return val;
}
int64_t helper_fistll_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int64_t val;
- signed char old_exp_flags;
-
- old_exp_flags = get_float_exception_flags(&env->fp_status);
- set_float_exception_flags(0, &env->fp_status);
val = floatx80_to_int64(ST0, &env->fp_status);
if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) {
val = 0x8000000000000000ULL;
}
- set_float_exception_flags(get_float_exception_flags(&env->fp_status)
- | old_exp_flags, &env->fp_status);
+ merge_exception_flags(env, old_flags);
return val;
}
int32_t helper_fistt_ST0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int32_t val;
val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
if (val != (int16_t)val) {
+ set_float_exception_flags(float_flag_invalid, &env->fp_status);
val = -32768;
}
+ merge_exception_flags(env, old_flags);
return val;
}
int32_t helper_fisttl_ST0(CPUX86State *env)
{
- return floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
+ uint8_t old_flags = save_exception_flags(env);
+ int32_t val;
+
+ val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
+ if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) {
+ val = 0x80000000;
+ }
+ merge_exception_flags(env, old_flags);
+ return val;
}
int64_t helper_fisttll_ST0(CPUX86State *env)
{
- return floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
+ uint8_t old_flags = save_exception_flags(env);
+ int64_t val;
+
+ val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
+ if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) {
+ val = 0x8000000000000000ULL;
+ }
+ merge_exception_flags(env, old_flags);
+ return val;
}
void helper_fldt_ST0(CPUX86State *env, target_ulong ptr)
@@ -420,24 +471,29 @@ static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
void helper_fcom_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
FloatRelation ret;
ret = floatx80_compare(ST0, FT0, &env->fp_status);
env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
+ merge_exception_flags(env, old_flags);
}
void helper_fucom_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
FloatRelation ret;
ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
+ merge_exception_flags(env, old_flags);
}
static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
void helper_fcomi_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int eflags;
FloatRelation ret;
@@ -445,10 +501,12 @@ void helper_fcomi_ST0_FT0(CPUX86State *env)
eflags = cpu_cc_compute_all(env, CC_OP);
eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
CC_SRC = eflags;
+ merge_exception_flags(env, old_flags);
}
void helper_fucomi_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
int eflags;
FloatRelation ret;
@@ -456,26 +514,35 @@ void helper_fucomi_ST0_FT0(CPUX86State *env)
eflags = cpu_cc_compute_all(env, CC_OP);
eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
CC_SRC = eflags;
+ merge_exception_flags(env, old_flags);
}
void helper_fadd_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
ST0 = floatx80_add(ST0, FT0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fmul_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fsub_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fsubr_ST0_FT0(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fdiv_ST0_FT0(CPUX86State *env)
@@ -492,22 +559,30 @@ void helper_fdivr_ST0_FT0(CPUX86State *env)
void helper_fadd_STN_ST0(CPUX86State *env, int st_index)
{
+ uint8_t old_flags = save_exception_flags(env);
ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fmul_STN_ST0(CPUX86State *env, int st_index)
{
+ uint8_t old_flags = save_exception_flags(env);
ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fsub_STN_ST0(CPUX86State *env, int st_index)
{
+ uint8_t old_flags = save_exception_flags(env);
ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fsubr_STN_ST0(CPUX86State *env, int st_index)
{
+ uint8_t old_flags = save_exception_flags(env);
ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fdiv_STN_ST0(CPUX86State *env, int st_index)
@@ -544,27 +619,66 @@ void helper_fld1_ST0(CPUX86State *env)
void helper_fldl2t_ST0(CPUX86State *env)
{
- ST0 = floatx80_l2t;
+ switch (env->fpuc & FPU_RC_MASK) {
+ case FPU_RC_UP:
+ ST0 = floatx80_l2t_u;
+ break;
+ default:
+ ST0 = floatx80_l2t;
+ break;
+ }
}
void helper_fldl2e_ST0(CPUX86State *env)
{
- ST0 = floatx80_l2e;
+ switch (env->fpuc & FPU_RC_MASK) {
+ case FPU_RC_DOWN:
+ case FPU_RC_CHOP:
+ ST0 = floatx80_l2e_d;
+ break;
+ default:
+ ST0 = floatx80_l2e;
+ break;
+ }
}
void helper_fldpi_ST0(CPUX86State *env)
{
- ST0 = floatx80_pi;
+ switch (env->fpuc & FPU_RC_MASK) {
+ case FPU_RC_DOWN:
+ case FPU_RC_CHOP:
+ ST0 = floatx80_pi_d;
+ break;
+ default:
+ ST0 = floatx80_pi;
+ break;
+ }
}
void helper_fldlg2_ST0(CPUX86State *env)
{
- ST0 = floatx80_lg2;
+ switch (env->fpuc & FPU_RC_MASK) {
+ case FPU_RC_DOWN:
+ case FPU_RC_CHOP:
+ ST0 = floatx80_lg2_d;
+ break;
+ default:
+ ST0 = floatx80_lg2;
+ break;
+ }
}
void helper_fldln2_ST0(CPUX86State *env)
{
- ST0 = floatx80_ln2;
+ switch (env->fpuc & FPU_RC_MASK) {
+ case FPU_RC_DOWN:
+ case FPU_RC_CHOP:
+ ST0 = floatx80_ln2_d;
+ break;
+ default:
+ ST0 = floatx80_ln2;
+ break;
+ }
}
void helper_fldz_ST0(CPUX86State *env)
@@ -679,14 +793,29 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr)
void helper_fbst_ST0(CPUX86State *env, target_ulong ptr)
{
+ uint8_t old_flags = save_exception_flags(env);
int v;
target_ulong mem_ref, mem_end;
int64_t val;
+ CPU_LDoubleU temp;
+
+ temp.d = ST0;
val = floatx80_to_int64(ST0, &env->fp_status);
mem_ref = ptr;
+ if (val >= 1000000000000000000LL || val <= -1000000000000000000LL) {
+ set_float_exception_flags(float_flag_invalid, &env->fp_status);
+ while (mem_ref < ptr + 7) {
+ cpu_stb_data_ra(env, mem_ref++, 0, GETPC());
+ }
+ cpu_stb_data_ra(env, mem_ref++, 0xc0, GETPC());
+ cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC());
+ cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC());
+ merge_exception_flags(env, old_flags);
+ return;
+ }
mem_end = mem_ref + 9;
- if (val < 0) {
+ if (SIGND(temp)) {
cpu_stb_data_ra(env, mem_end, 0x80, GETPC());
val = -val;
} else {
@@ -704,6 +833,7 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr)
while (mem_ref < mem_end) {
cpu_stb_data_ra(env, mem_ref++, 0, GETPC());
}
+ merge_exception_flags(env, old_flags);
}
void helper_f2xm1(CPUX86State *env)
@@ -757,6 +887,7 @@ void helper_fpatan(CPUX86State *env)
void helper_fxtract(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
CPU_LDoubleU temp;
temp.d = ST0;
@@ -767,16 +898,40 @@ void helper_fxtract(CPUX86State *env)
&env->fp_status);
fpush(env);
ST0 = temp.d;
+ } else if (floatx80_invalid_encoding(ST0)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_default_nan(&env->fp_status);
+ fpush(env);
+ ST0 = ST1;
+ } else if (floatx80_is_any_nan(ST0)) {
+ if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_silence_nan(ST0, &env->fp_status);
+ }
+ fpush(env);
+ ST0 = ST1;
+ } else if (floatx80_is_infinity(ST0)) {
+ fpush(env);
+ ST0 = ST1;
+ ST1 = floatx80_infinity;
} else {
int expdif;
- expdif = EXPD(temp) - EXPBIAS;
+ if (EXPD(temp) == 0) {
+ int shift = clz64(temp.l.lower);
+ temp.l.lower <<= shift;
+ expdif = 1 - EXPBIAS - shift;
+ float_raise(float_flag_input_denormal, &env->fp_status);
+ } else {
+ expdif = EXPD(temp) - EXPBIAS;
+ }
/* DP exponent bias */
ST0 = int32_to_floatx80(expdif, &env->fp_status);
fpush(env);
BIASEXPONENT(temp);
ST0 = temp.d;
}
+ merge_exception_flags(env, old_flags);
}
void helper_fprem1(CPUX86State *env)
@@ -916,11 +1071,13 @@ void helper_fyl2xp1(CPUX86State *env)
void helper_fsqrt(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
if (floatx80_is_neg(ST0)) {
env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
env->fpus |= 0x400;
}
ST0 = floatx80_sqrt(ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fsincos(CPUX86State *env)
@@ -940,17 +1097,60 @@ void helper_fsincos(CPUX86State *env)
void helper_frndint(CPUX86State *env)
{
+ uint8_t old_flags = save_exception_flags(env);
ST0 = floatx80_round_to_int(ST0, &env->fp_status);
+ merge_exception_flags(env, old_flags);
}
void helper_fscale(CPUX86State *env)
{
- if (floatx80_is_any_nan(ST1)) {
+ uint8_t old_flags = save_exception_flags(env);
+ if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_default_nan(&env->fp_status);
+ } else if (floatx80_is_any_nan(ST1)) {
+ if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ }
ST0 = ST1;
+ if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_silence_nan(ST0, &env->fp_status);
+ }
+ } else if (floatx80_is_infinity(ST1) &&
+ !floatx80_invalid_encoding(ST0) &&
+ !floatx80_is_any_nan(ST0)) {
+ if (floatx80_is_neg(ST1)) {
+ if (floatx80_is_infinity(ST0)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_default_nan(&env->fp_status);
+ } else {
+ ST0 = (floatx80_is_neg(ST0) ?
+ floatx80_chs(floatx80_zero) :
+ floatx80_zero);
+ }
+ } else {
+ if (floatx80_is_zero(ST0)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ ST0 = floatx80_default_nan(&env->fp_status);
+ } else {
+ ST0 = (floatx80_is_neg(ST0) ?
+ floatx80_chs(floatx80_infinity) :
+ floatx80_infinity);
+ }
+ }
} else {
- int n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status);
+ int n;
+ signed char save = env->fp_status.floatx80_rounding_precision;
+ uint8_t save_flags = get_float_exception_flags(&env->fp_status);
+ set_float_exception_flags(0, &env->fp_status);
+ n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status);
+ set_float_exception_flags(save_flags, &env->fp_status);
+ env->fp_status.floatx80_rounding_precision = 80;
ST0 = floatx80_scalbn(ST0, n, &env->fp_status);
+ env->fp_status.floatx80_rounding_precision = save;
}
+ merge_exception_flags(env, old_flags);
}
void helper_fsin(CPUX86State *env)
@@ -1000,7 +1200,7 @@ void helper_fxam_ST0(CPUX86State *env)
if (expdif == MAXEXPD) {
if (MANTD(temp) == 0x8000000000000000ULL) {
env->fpus |= 0x500; /* Infinity */
- } else {
+ } else if (MANTD(temp) & 0x8000000000000000ULL) {
env->fpus |= 0x100; /* NaN */
}
} else if (expdif == 0) {
@@ -1009,7 +1209,7 @@ void helper_fxam_ST0(CPUX86State *env)
} else {
env->fpus |= 0x4400; /* Denormal */
}
- } else {
+ } else if (MANTD(temp) & 0x8000000000000000ULL) {
env->fpus |= 0x400;
}
}