Commit 8422b1133739343c1b35d0bba30c3209649e43e1
1 parent
b7a100da
NaN support in FPU comparisons
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1341 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
141 additions
and
67 deletions
target-i386/exec.h
... | ... | @@ -325,6 +325,8 @@ static inline void stfl(target_ulong ptr, float v) |
325 | 325 | #define floatx_abs floatx80_abs |
326 | 326 | #define floatx_chs floatx80_chs |
327 | 327 | #define floatx_round_to_int floatx80_round_to_int |
328 | +#define floatx_compare floatx80_compare | |
329 | +#define floatx_compare_quiet floatx80_compare_quiet | |
328 | 330 | #define sin sinl |
329 | 331 | #define cos cosl |
330 | 332 | #define sqrt sqrtl |
... | ... | @@ -340,6 +342,8 @@ static inline void stfl(target_ulong ptr, float v) |
340 | 342 | #define floatx_abs float64_abs |
341 | 343 | #define floatx_chs float64_chs |
342 | 344 | #define floatx_round_to_int float64_round_to_int |
345 | +#define floatx_compare float64_compare | |
346 | +#define floatx_compare_quiet float64_compare_quiet | |
343 | 347 | #endif |
344 | 348 | |
345 | 349 | extern CPU86_LDouble sin(CPU86_LDouble x); |
... | ... | @@ -547,8 +551,6 @@ void restore_native_fp_state(CPUState *env); |
547 | 551 | void save_native_fp_state(CPUState *env); |
548 | 552 | float approx_rsqrt(float a); |
549 | 553 | float approx_rcp(float a); |
550 | -double helper_sqrt(double a); | |
551 | -int fpu_isnan(double a); | |
552 | 554 | void update_fp_status(void); |
553 | 555 | |
554 | 556 | extern const uint8_t parity_table[256]; | ... | ... |
target-i386/helper.c
target-i386/op.c
... | ... | @@ -1952,52 +1952,94 @@ void OPPROTO op_fxchg_ST0_STN(void) |
1952 | 1952 | |
1953 | 1953 | /* FPU operations */ |
1954 | 1954 | |
1955 | -/* XXX: handle nans */ | |
1956 | 1955 | void OPPROTO op_fcom_ST0_FT0(void) |
1957 | 1956 | { |
1958 | - env->fpus &= (~0x4500); /* (C3,C2,C0) <-- 000 */ | |
1959 | - if (ST0 < FT0) | |
1960 | - env->fpus |= 0x100; /* (C3,C2,C0) <-- 001 */ | |
1961 | - else if (ST0 == FT0) | |
1962 | - env->fpus |= 0x4000; /* (C3,C2,C0) <-- 100 */ | |
1957 | + int cc; | |
1958 | + switch(floatx_compare(ST0, FT0, &env->fp_status)) { | |
1959 | + case -1: | |
1960 | + cc = 0x0100; | |
1961 | + break; | |
1962 | + case 0: | |
1963 | + cc = 0x4000; | |
1964 | + break; | |
1965 | + case 1: | |
1966 | + cc = 0x0000; | |
1967 | + break; | |
1968 | + case 2: | |
1969 | + default: | |
1970 | + cc = 0x4500; | |
1971 | + break; | |
1972 | + } | |
1973 | + env->fpus = (env->fpus & ~0x4500) | cc; | |
1963 | 1974 | FORCE_RET(); |
1964 | 1975 | } |
1965 | 1976 | |
1966 | -/* XXX: handle nans */ | |
1967 | 1977 | void OPPROTO op_fucom_ST0_FT0(void) |
1968 | 1978 | { |
1969 | - env->fpus &= (~0x4500); /* (C3,C2,C0) <-- 000 */ | |
1970 | - if (ST0 < FT0) | |
1971 | - env->fpus |= 0x100; /* (C3,C2,C0) <-- 001 */ | |
1972 | - else if (ST0 == FT0) | |
1973 | - env->fpus |= 0x4000; /* (C3,C2,C0) <-- 100 */ | |
1979 | + int cc; | |
1980 | + switch(floatx_compare_quiet(ST0, FT0, &env->fp_status)) { | |
1981 | + case -1: | |
1982 | + cc = 0x0100; | |
1983 | + break; | |
1984 | + case 0: | |
1985 | + cc = 0x4000; | |
1986 | + break; | |
1987 | + case 1: | |
1988 | + cc = 0x0000; | |
1989 | + break; | |
1990 | + case 2: | |
1991 | + default: | |
1992 | + cc = 0x4500; | |
1993 | + break; | |
1994 | + } | |
1995 | + env->fpus = (env->fpus & ~0x4500) | cc; | |
1974 | 1996 | FORCE_RET(); |
1975 | 1997 | } |
1976 | 1998 | |
1977 | -/* XXX: handle nans */ | |
1978 | 1999 | void OPPROTO op_fcomi_ST0_FT0(void) |
1979 | 2000 | { |
1980 | - int eflags; | |
2001 | + int eflags, cc; | |
2002 | + switch(floatx_compare(ST0, FT0, &env->fp_status)) { | |
2003 | + case -1: | |
2004 | + cc = CC_C; | |
2005 | + break; | |
2006 | + case 0: | |
2007 | + cc = CC_Z; | |
2008 | + break; | |
2009 | + case 1: | |
2010 | + cc = 0; | |
2011 | + break; | |
2012 | + case 2: | |
2013 | + default: | |
2014 | + cc = CC_Z | CC_P | CC_C; | |
2015 | + break; | |
2016 | + } | |
1981 | 2017 | eflags = cc_table[CC_OP].compute_all(); |
1982 | - eflags &= ~(CC_Z | CC_P | CC_C); | |
1983 | - if (ST0 < FT0) | |
1984 | - eflags |= CC_C; | |
1985 | - else if (ST0 == FT0) | |
1986 | - eflags |= CC_Z; | |
2018 | + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | cc; | |
1987 | 2019 | CC_SRC = eflags; |
1988 | 2020 | FORCE_RET(); |
1989 | 2021 | } |
1990 | 2022 | |
1991 | -/* XXX: handle nans */ | |
1992 | 2023 | void OPPROTO op_fucomi_ST0_FT0(void) |
1993 | 2024 | { |
1994 | - int eflags; | |
2025 | + int eflags, cc; | |
2026 | + switch(floatx_compare_quiet(ST0, FT0, &env->fp_status)) { | |
2027 | + case -1: | |
2028 | + cc = CC_C; | |
2029 | + break; | |
2030 | + case 0: | |
2031 | + cc = CC_Z; | |
2032 | + break; | |
2033 | + case 1: | |
2034 | + cc = 0; | |
2035 | + break; | |
2036 | + case 2: | |
2037 | + default: | |
2038 | + cc = CC_Z | CC_P | CC_C; | |
2039 | + break; | |
2040 | + } | |
1995 | 2041 | eflags = cc_table[CC_OP].compute_all(); |
1996 | - eflags &= ~(CC_Z | CC_P | CC_C); | |
1997 | - if (ST0 < FT0) | |
1998 | - eflags |= CC_C; | |
1999 | - else if (ST0 == FT0) | |
2000 | - eflags |= CC_Z; | |
2042 | + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | cc; | |
2001 | 2043 | CC_SRC = eflags; |
2002 | 2044 | FORCE_RET(); |
2003 | 2045 | } | ... | ... |
target-i386/ops_sse.h
... | ... | @@ -704,7 +704,7 @@ SSE_OP_S(sqrt, FPU_SQRT) |
704 | 704 | /* float to float conversions */ |
705 | 705 | void OPPROTO op_cvtps2pd(void) |
706 | 706 | { |
707 | - float s0, s1; | |
707 | + float32 s0, s1; | |
708 | 708 | Reg *d, *s; |
709 | 709 | d = (Reg *)((char *)env + PARAM1); |
710 | 710 | s = (Reg *)((char *)env + PARAM2); |
... | ... | @@ -1031,10 +1031,10 @@ void OPPROTO op_ ## name ## ps (void)\ |
1031 | 1031 | Reg *d, *s;\ |
1032 | 1032 | d = (Reg *)((char *)env + PARAM1);\ |
1033 | 1033 | s = (Reg *)((char *)env + PARAM2);\ |
1034 | - d->XMM_L(0) = F(d->XMM_S(0), s->XMM_S(0));\ | |
1035 | - d->XMM_L(1) = F(d->XMM_S(1), s->XMM_S(1));\ | |
1036 | - d->XMM_L(2) = F(d->XMM_S(2), s->XMM_S(2));\ | |
1037 | - d->XMM_L(3) = F(d->XMM_S(3), s->XMM_S(3));\ | |
1034 | + d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ | |
1035 | + d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1));\ | |
1036 | + d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2));\ | |
1037 | + d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3));\ | |
1038 | 1038 | }\ |
1039 | 1039 | \ |
1040 | 1040 | void OPPROTO op_ ## name ## ss (void)\ |
... | ... | @@ -1042,15 +1042,15 @@ void OPPROTO op_ ## name ## ss (void)\ |
1042 | 1042 | Reg *d, *s;\ |
1043 | 1043 | d = (Reg *)((char *)env + PARAM1);\ |
1044 | 1044 | s = (Reg *)((char *)env + PARAM2);\ |
1045 | - d->XMM_L(0) = F(d->XMM_S(0), s->XMM_S(0));\ | |
1045 | + d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ | |
1046 | 1046 | }\ |
1047 | 1047 | void OPPROTO op_ ## name ## pd (void)\ |
1048 | 1048 | {\ |
1049 | 1049 | Reg *d, *s;\ |
1050 | 1050 | d = (Reg *)((char *)env + PARAM1);\ |
1051 | 1051 | s = (Reg *)((char *)env + PARAM2);\ |
1052 | - d->XMM_Q(0) = F(d->XMM_D(0), s->XMM_D(0));\ | |
1053 | - d->XMM_Q(1) = F(d->XMM_D(1), s->XMM_D(1));\ | |
1052 | + d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ | |
1053 | + d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1));\ | |
1054 | 1054 | }\ |
1055 | 1055 | \ |
1056 | 1056 | void OPPROTO op_ ## name ## sd (void)\ |
... | ... | @@ -1058,17 +1058,17 @@ void OPPROTO op_ ## name ## sd (void)\ |
1058 | 1058 | Reg *d, *s;\ |
1059 | 1059 | d = (Reg *)((char *)env + PARAM1);\ |
1060 | 1060 | s = (Reg *)((char *)env + PARAM2);\ |
1061 | - d->XMM_Q(0) = F(d->XMM_D(0), s->XMM_D(0));\ | |
1061 | + d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ | |
1062 | 1062 | } |
1063 | 1063 | |
1064 | -#define FPU_CMPEQ(a, b) (a) == (b) ? -1 : 0 | |
1065 | -#define FPU_CMPLT(a, b) (a) < (b) ? -1 : 0 | |
1066 | -#define FPU_CMPLE(a, b) (a) <= (b) ? -1 : 0 | |
1067 | -#define FPU_CMPUNORD(a, b) (fpu_isnan(a) || fpu_isnan(b)) ? - 1 : 0 | |
1068 | -#define FPU_CMPNEQ(a, b) (a) == (b) ? 0 : -1 | |
1069 | -#define FPU_CMPNLT(a, b) (a) < (b) ? 0 : -1 | |
1070 | -#define FPU_CMPNLE(a, b) (a) <= (b) ? 0 : -1 | |
1071 | -#define FPU_CMPORD(a, b) (!fpu_isnan(a) && !fpu_isnan(b)) ? - 1 : 0 | |
1064 | +#define FPU_CMPEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? -1 : 0 | |
1065 | +#define FPU_CMPLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0 | |
1066 | +#define FPU_CMPLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? -1 : 0 | |
1067 | +#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? - 1 : 0 | |
1068 | +#define FPU_CMPNEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? 0 : -1 | |
1069 | +#define FPU_CMPNLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1 | |
1070 | +#define FPU_CMPNLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? 0 : -1 | |
1071 | +#define FPU_CMPORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? 0 : -1 | |
1072 | 1072 | |
1073 | 1073 | SSE_OP_CMP(cmpeq, FPU_CMPEQ) |
1074 | 1074 | SSE_OP_CMP(cmplt, FPU_CMPLT) |
... | ... | @@ -1082,19 +1082,28 @@ SSE_OP_CMP(cmpord, FPU_CMPORD) |
1082 | 1082 | void OPPROTO op_ucomiss(void) |
1083 | 1083 | { |
1084 | 1084 | int eflags; |
1085 | - float s0, s1; | |
1085 | + float32 s0, s1; | |
1086 | 1086 | Reg *d, *s; |
1087 | 1087 | d = (Reg *)((char *)env + PARAM1); |
1088 | 1088 | s = (Reg *)((char *)env + PARAM2); |
1089 | 1089 | |
1090 | 1090 | s0 = d->XMM_S(0); |
1091 | 1091 | s1 = s->XMM_S(0); |
1092 | - if (s0 < s1) | |
1092 | + switch(float32_compare_quiet(s0, s1, &env->sse_status)) { | |
1093 | + case -1: | |
1093 | 1094 | eflags = CC_C; |
1094 | - else if (s0 == s1) | |
1095 | + break; | |
1096 | + case 0: | |
1095 | 1097 | eflags = CC_Z; |
1096 | - else | |
1098 | + break; | |
1099 | + case 1: | |
1097 | 1100 | eflags = 0; |
1101 | + break; | |
1102 | + case 2: | |
1103 | + default: | |
1104 | + eflags = CC_Z | CC_P | CC_C; | |
1105 | + break; | |
1106 | + } | |
1098 | 1107 | CC_SRC = eflags; |
1099 | 1108 | FORCE_RET(); |
1100 | 1109 | } |
... | ... | @@ -1102,19 +1111,28 @@ void OPPROTO op_ucomiss(void) |
1102 | 1111 | void OPPROTO op_comiss(void) |
1103 | 1112 | { |
1104 | 1113 | int eflags; |
1105 | - float s0, s1; | |
1114 | + float32 s0, s1; | |
1106 | 1115 | Reg *d, *s; |
1107 | 1116 | d = (Reg *)((char *)env + PARAM1); |
1108 | 1117 | s = (Reg *)((char *)env + PARAM2); |
1109 | 1118 | |
1110 | 1119 | s0 = d->XMM_S(0); |
1111 | 1120 | s1 = s->XMM_S(0); |
1112 | - if (s0 < s1) | |
1121 | + switch(float32_compare(s0, s1, &env->sse_status)) { | |
1122 | + case -1: | |
1113 | 1123 | eflags = CC_C; |
1114 | - else if (s0 == s1) | |
1124 | + break; | |
1125 | + case 0: | |
1115 | 1126 | eflags = CC_Z; |
1116 | - else | |
1127 | + break; | |
1128 | + case 1: | |
1117 | 1129 | eflags = 0; |
1130 | + break; | |
1131 | + case 2: | |
1132 | + default: | |
1133 | + eflags = CC_Z | CC_P | CC_C; | |
1134 | + break; | |
1135 | + } | |
1118 | 1136 | CC_SRC = eflags; |
1119 | 1137 | FORCE_RET(); |
1120 | 1138 | } |
... | ... | @@ -1122,19 +1140,28 @@ void OPPROTO op_comiss(void) |
1122 | 1140 | void OPPROTO op_ucomisd(void) |
1123 | 1141 | { |
1124 | 1142 | int eflags; |
1125 | - double d0, d1; | |
1143 | + float64 d0, d1; | |
1126 | 1144 | Reg *d, *s; |
1127 | 1145 | d = (Reg *)((char *)env + PARAM1); |
1128 | 1146 | s = (Reg *)((char *)env + PARAM2); |
1129 | 1147 | |
1130 | 1148 | d0 = d->XMM_D(0); |
1131 | 1149 | d1 = s->XMM_D(0); |
1132 | - if (d0 < d1) | |
1150 | + switch(float64_compare_quiet(d0, d1, &env->sse_status)) { | |
1151 | + case -1: | |
1133 | 1152 | eflags = CC_C; |
1134 | - else if (d0 == d1) | |
1153 | + break; | |
1154 | + case 0: | |
1135 | 1155 | eflags = CC_Z; |
1136 | - else | |
1156 | + break; | |
1157 | + case 1: | |
1137 | 1158 | eflags = 0; |
1159 | + break; | |
1160 | + case 2: | |
1161 | + default: | |
1162 | + eflags = CC_Z | CC_P | CC_C; | |
1163 | + break; | |
1164 | + } | |
1138 | 1165 | CC_SRC = eflags; |
1139 | 1166 | FORCE_RET(); |
1140 | 1167 | } |
... | ... | @@ -1142,19 +1169,28 @@ void OPPROTO op_ucomisd(void) |
1142 | 1169 | void OPPROTO op_comisd(void) |
1143 | 1170 | { |
1144 | 1171 | int eflags; |
1145 | - double d0, d1; | |
1172 | + float64 d0, d1; | |
1146 | 1173 | Reg *d, *s; |
1147 | 1174 | d = (Reg *)((char *)env + PARAM1); |
1148 | 1175 | s = (Reg *)((char *)env + PARAM2); |
1149 | 1176 | |
1150 | 1177 | d0 = d->XMM_D(0); |
1151 | 1178 | d1 = s->XMM_D(0); |
1152 | - if (d0 < d1) | |
1179 | + switch(float64_compare(d0, d1, &env->sse_status)) { | |
1180 | + case -1: | |
1153 | 1181 | eflags = CC_C; |
1154 | - else if (d0 == d1) | |
1182 | + break; | |
1183 | + case 0: | |
1155 | 1184 | eflags = CC_Z; |
1156 | - else | |
1185 | + break; | |
1186 | + case 1: | |
1157 | 1187 | eflags = 0; |
1188 | + break; | |
1189 | + case 2: | |
1190 | + default: | |
1191 | + eflags = CC_Z | CC_P | CC_C; | |
1192 | + break; | |
1193 | + } | |
1158 | 1194 | CC_SRC = eflags; |
1159 | 1195 | FORCE_RET(); |
1160 | 1196 | } | ... | ... |