Merge branch 'sched-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6] / arch / x86 / math-emu / reg_compare.c
1 /*---------------------------------------------------------------------------+
2  |  reg_compare.c                                                            |
3  |                                                                           |
4  | Compare two floating point registers                                      |
5  |                                                                           |
6  | Copyright (C) 1992,1993,1994,1997                                         |
7  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8  |                  E-mail   billm@suburbia.net                              |
9  |                                                                           |
10  |                                                                           |
11  +---------------------------------------------------------------------------*/
12
13 /*---------------------------------------------------------------------------+
14  | compare() is the core FPU_REG comparison function                         |
15  +---------------------------------------------------------------------------*/
16
17 #include "fpu_system.h"
18 #include "exception.h"
19 #include "fpu_emu.h"
20 #include "control_w.h"
21 #include "status_w.h"
22
23 static int compare(FPU_REG const *b, int tagb)
24 {
25         int diff, exp0, expb;
26         u_char st0_tag;
27         FPU_REG *st0_ptr;
28         FPU_REG x, y;
29         u_char st0_sign, signb = getsign(b);
30
31         st0_ptr = &st(0);
32         st0_tag = FPU_gettag0();
33         st0_sign = getsign(st0_ptr);
34
35         if (tagb == TAG_Special)
36                 tagb = FPU_Special(b);
37         if (st0_tag == TAG_Special)
38                 st0_tag = FPU_Special(st0_ptr);
39
40         if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
41             || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
42                 if (st0_tag == TAG_Zero) {
43                         if (tagb == TAG_Zero)
44                                 return COMP_A_eq_B;
45                         if (tagb == TAG_Valid)
46                                 return ((signb ==
47                                          SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
48                         if (tagb == TW_Denormal)
49                                 return ((signb ==
50                                          SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
51                                     | COMP_Denormal;
52                 } else if (tagb == TAG_Zero) {
53                         if (st0_tag == TAG_Valid)
54                                 return ((st0_sign ==
55                                          SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
56                         if (st0_tag == TW_Denormal)
57                                 return ((st0_sign ==
58                                          SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
59                                     | COMP_Denormal;
60                 }
61
62                 if (st0_tag == TW_Infinity) {
63                         if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
64                                 return ((st0_sign ==
65                                          SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
66                         else if (tagb == TW_Denormal)
67                                 return ((st0_sign ==
68                                          SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
69                                     | COMP_Denormal;
70                         else if (tagb == TW_Infinity) {
71                                 /* The 80486 book says that infinities can be equal! */
72                                 return (st0_sign == signb) ? COMP_A_eq_B :
73                                     ((st0_sign ==
74                                       SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
75                         }
76                         /* Fall through to the NaN code */
77                 } else if (tagb == TW_Infinity) {
78                         if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
79                                 return ((signb ==
80                                          SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
81                         if (st0_tag == TW_Denormal)
82                                 return ((signb ==
83                                          SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
84                                     | COMP_Denormal;
85                         /* Fall through to the NaN code */
86                 }
87
88                 /* The only possibility now should be that one of the arguments
89                    is a NaN */
90                 if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
91                         int signalling = 0, unsupported = 0;
92                         if (st0_tag == TW_NaN) {
93                                 signalling =
94                                     (st0_ptr->sigh & 0xc0000000) == 0x80000000;
95                                 unsupported = !((exponent(st0_ptr) == EXP_OVER)
96                                                 && (st0_ptr->
97                                                     sigh & 0x80000000));
98                         }
99                         if (tagb == TW_NaN) {
100                                 signalling |=
101                                     (b->sigh & 0xc0000000) == 0x80000000;
102                                 unsupported |= !((exponent(b) == EXP_OVER)
103                                                  && (b->sigh & 0x80000000));
104                         }
105                         if (signalling || unsupported)
106                                 return COMP_No_Comp | COMP_SNaN | COMP_NaN;
107                         else
108                                 /* Neither is a signaling NaN */
109                                 return COMP_No_Comp | COMP_NaN;
110                 }
111
112                 EXCEPTION(EX_Invalid);
113         }
114
115         if (st0_sign != signb) {
116                 return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
117                     | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
118                        COMP_Denormal : 0);
119         }
120
121         if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
122                 FPU_to_exp16(st0_ptr, &x);
123                 FPU_to_exp16(b, &y);
124                 st0_ptr = &x;
125                 b = &y;
126                 exp0 = exponent16(st0_ptr);
127                 expb = exponent16(b);
128         } else {
129                 exp0 = exponent(st0_ptr);
130                 expb = exponent(b);
131         }
132
133 #ifdef PARANOID
134         if (!(st0_ptr->sigh & 0x80000000))
135                 EXCEPTION(EX_Invalid);
136         if (!(b->sigh & 0x80000000))
137                 EXCEPTION(EX_Invalid);
138 #endif /* PARANOID */
139
140         diff = exp0 - expb;
141         if (diff == 0) {
142                 diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
143                                                    identical */
144                 if (diff == 0) {
145                         diff = st0_ptr->sigl > b->sigl;
146                         if (diff == 0)
147                                 diff = -(st0_ptr->sigl < b->sigl);
148                 }
149         }
150
151         if (diff > 0) {
152                 return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
153                     | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
154                        COMP_Denormal : 0);
155         }
156         if (diff < 0) {
157                 return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
158                     | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
159                        COMP_Denormal : 0);
160         }
161
162         return COMP_A_eq_B
163             | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
164                COMP_Denormal : 0);
165
166 }
167
168 /* This function requires that st(0) is not empty */
169 int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
170 {
171         int f = 0, c;
172
173         c = compare(loaded_data, loaded_tag);
174
175         if (c & COMP_NaN) {
176                 EXCEPTION(EX_Invalid);
177                 f = SW_C3 | SW_C2 | SW_C0;
178         } else
179                 switch (c & 7) {
180                 case COMP_A_lt_B:
181                         f = SW_C0;
182                         break;
183                 case COMP_A_eq_B:
184                         f = SW_C3;
185                         break;
186                 case COMP_A_gt_B:
187                         f = 0;
188                         break;
189                 case COMP_No_Comp:
190                         f = SW_C3 | SW_C2 | SW_C0;
191                         break;
192 #ifdef PARANOID
193                 default:
194                         EXCEPTION(EX_INTERNAL | 0x121);
195                         f = SW_C3 | SW_C2 | SW_C0;
196                         break;
197 #endif /* PARANOID */
198                 }
199         setcc(f);
200         if (c & COMP_Denormal) {
201                 return denormal_operand() < 0;
202         }
203         return 0;
204 }
205
206 static int compare_st_st(int nr)
207 {
208         int f = 0, c;
209         FPU_REG *st_ptr;
210
211         if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
212                 setcc(SW_C3 | SW_C2 | SW_C0);
213                 /* Stack fault */
214                 EXCEPTION(EX_StackUnder);
215                 return !(control_word & CW_Invalid);
216         }
217
218         st_ptr = &st(nr);
219         c = compare(st_ptr, FPU_gettagi(nr));
220         if (c & COMP_NaN) {
221                 setcc(SW_C3 | SW_C2 | SW_C0);
222                 EXCEPTION(EX_Invalid);
223                 return !(control_word & CW_Invalid);
224         } else
225                 switch (c & 7) {
226                 case COMP_A_lt_B:
227                         f = SW_C0;
228                         break;
229                 case COMP_A_eq_B:
230                         f = SW_C3;
231                         break;
232                 case COMP_A_gt_B:
233                         f = 0;
234                         break;
235                 case COMP_No_Comp:
236                         f = SW_C3 | SW_C2 | SW_C0;
237                         break;
238 #ifdef PARANOID
239                 default:
240                         EXCEPTION(EX_INTERNAL | 0x122);
241                         f = SW_C3 | SW_C2 | SW_C0;
242                         break;
243 #endif /* PARANOID */
244                 }
245         setcc(f);
246         if (c & COMP_Denormal) {
247                 return denormal_operand() < 0;
248         }
249         return 0;
250 }
251
252 static int compare_u_st_st(int nr)
253 {
254         int f = 0, c;
255         FPU_REG *st_ptr;
256
257         if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
258                 setcc(SW_C3 | SW_C2 | SW_C0);
259                 /* Stack fault */
260                 EXCEPTION(EX_StackUnder);
261                 return !(control_word & CW_Invalid);
262         }
263
264         st_ptr = &st(nr);
265         c = compare(st_ptr, FPU_gettagi(nr));
266         if (c & COMP_NaN) {
267                 setcc(SW_C3 | SW_C2 | SW_C0);
268                 if (c & COMP_SNaN) {    /* This is the only difference between
269                                            un-ordered and ordinary comparisons */
270                         EXCEPTION(EX_Invalid);
271                         return !(control_word & CW_Invalid);
272                 }
273                 return 0;
274         } else
275                 switch (c & 7) {
276                 case COMP_A_lt_B:
277                         f = SW_C0;
278                         break;
279                 case COMP_A_eq_B:
280                         f = SW_C3;
281                         break;
282                 case COMP_A_gt_B:
283                         f = 0;
284                         break;
285                 case COMP_No_Comp:
286                         f = SW_C3 | SW_C2 | SW_C0;
287                         break;
288 #ifdef PARANOID
289                 default:
290                         EXCEPTION(EX_INTERNAL | 0x123);
291                         f = SW_C3 | SW_C2 | SW_C0;
292                         break;
293 #endif /* PARANOID */
294                 }
295         setcc(f);
296         if (c & COMP_Denormal) {
297                 return denormal_operand() < 0;
298         }
299         return 0;
300 }
301
302 /*---------------------------------------------------------------------------*/
303
304 void fcom_st(void)
305 {
306         /* fcom st(i) */
307         compare_st_st(FPU_rm);
308 }
309
310 void fcompst(void)
311 {
312         /* fcomp st(i) */
313         if (!compare_st_st(FPU_rm))
314                 FPU_pop();
315 }
316
317 void fcompp(void)
318 {
319         /* fcompp */
320         if (FPU_rm != 1) {
321                 FPU_illegal();
322                 return;
323         }
324         if (!compare_st_st(1))
325                 poppop();
326 }
327
328 void fucom_(void)
329 {
330         /* fucom st(i) */
331         compare_u_st_st(FPU_rm);
332
333 }
334
335 void fucomp(void)
336 {
337         /* fucomp st(i) */
338         if (!compare_u_st_st(FPU_rm))
339                 FPU_pop();
340 }
341
342 void fucompp(void)
343 {
344         /* fucompp */
345         if (FPU_rm == 1) {
346                 if (!compare_u_st_st(1))
347                         poppop();
348         } else
349                 FPU_illegal();
350 }