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 | } | ... | ... |