xref: /openbmc/linux/arch/x86/math-emu/reg_compare.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1  // SPDX-License-Identifier: GPL-2.0
2  /*---------------------------------------------------------------------------+
3   |  reg_compare.c                                                            |
4   |                                                                           |
5   | Compare two floating point registers                                      |
6   |                                                                           |
7   | Copyright (C) 1992,1993,1994,1997                                         |
8   |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9   |                  E-mail   billm@suburbia.net                              |
10   |                                                                           |
11   |                                                                           |
12   +---------------------------------------------------------------------------*/
13  
14  /*---------------------------------------------------------------------------+
15   | compare() is the core FPU_REG comparison function                         |
16   +---------------------------------------------------------------------------*/
17  
18  #include "fpu_system.h"
19  #include "exception.h"
20  #include "fpu_emu.h"
21  #include "control_w.h"
22  #include "status_w.h"
23  
compare(FPU_REG const * b,int tagb)24  static int compare(FPU_REG const *b, int tagb)
25  {
26  	int diff, exp0, expb;
27  	u_char st0_tag;
28  	FPU_REG *st0_ptr;
29  	FPU_REG x, y;
30  	u_char st0_sign, signb = getsign(b);
31  
32  	st0_ptr = &st(0);
33  	st0_tag = FPU_gettag0();
34  	st0_sign = getsign(st0_ptr);
35  
36  	if (tagb == TAG_Special)
37  		tagb = FPU_Special(b);
38  	if (st0_tag == TAG_Special)
39  		st0_tag = FPU_Special(st0_ptr);
40  
41  	if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
42  	    || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
43  		if (st0_tag == TAG_Zero) {
44  			if (tagb == TAG_Zero)
45  				return COMP_A_eq_B;
46  			if (tagb == TAG_Valid)
47  				return ((signb ==
48  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
49  			if (tagb == TW_Denormal)
50  				return ((signb ==
51  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
52  				    | COMP_Denormal;
53  		} else if (tagb == TAG_Zero) {
54  			if (st0_tag == TAG_Valid)
55  				return ((st0_sign ==
56  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
57  			if (st0_tag == TW_Denormal)
58  				return ((st0_sign ==
59  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
60  				    | COMP_Denormal;
61  		}
62  
63  		if (st0_tag == TW_Infinity) {
64  			if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
65  				return ((st0_sign ==
66  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
67  			else if (tagb == TW_Denormal)
68  				return ((st0_sign ==
69  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
70  				    | COMP_Denormal;
71  			else if (tagb == TW_Infinity) {
72  				/* The 80486 book says that infinities can be equal! */
73  				return (st0_sign == signb) ? COMP_A_eq_B :
74  				    ((st0_sign ==
75  				      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
76  			}
77  			/* Fall through to the NaN code */
78  		} else if (tagb == TW_Infinity) {
79  			if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
80  				return ((signb ==
81  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
82  			if (st0_tag == TW_Denormal)
83  				return ((signb ==
84  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
85  				    | COMP_Denormal;
86  			/* Fall through to the NaN code */
87  		}
88  
89  		/* The only possibility now should be that one of the arguments
90  		   is a NaN */
91  		if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
92  			int signalling = 0, unsupported = 0;
93  			if (st0_tag == TW_NaN) {
94  				signalling =
95  				    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
96  				unsupported = !((exponent(st0_ptr) == EXP_OVER)
97  						&& (st0_ptr->
98  						    sigh & 0x80000000));
99  			}
100  			if (tagb == TW_NaN) {
101  				signalling |=
102  				    (b->sigh & 0xc0000000) == 0x80000000;
103  				unsupported |= !((exponent(b) == EXP_OVER)
104  						 && (b->sigh & 0x80000000));
105  			}
106  			if (signalling || unsupported)
107  				return COMP_No_Comp | COMP_SNaN | COMP_NaN;
108  			else
109  				/* Neither is a signaling NaN */
110  				return COMP_No_Comp | COMP_NaN;
111  		}
112  
113  		EXCEPTION(EX_Invalid);
114  	}
115  
116  	if (st0_sign != signb) {
117  		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
118  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
119  		       COMP_Denormal : 0);
120  	}
121  
122  	if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
123  		FPU_to_exp16(st0_ptr, &x);
124  		FPU_to_exp16(b, &y);
125  		st0_ptr = &x;
126  		b = &y;
127  		exp0 = exponent16(st0_ptr);
128  		expb = exponent16(b);
129  	} else {
130  		exp0 = exponent(st0_ptr);
131  		expb = exponent(b);
132  	}
133  
134  #ifdef PARANOID
135  	if (!(st0_ptr->sigh & 0x80000000))
136  		EXCEPTION(EX_Invalid);
137  	if (!(b->sigh & 0x80000000))
138  		EXCEPTION(EX_Invalid);
139  #endif /* PARANOID */
140  
141  	diff = exp0 - expb;
142  	if (diff == 0) {
143  		diff = st0_ptr->sigh - b->sigh;	/* Works only if ms bits are
144  						   identical */
145  		if (diff == 0) {
146  			diff = st0_ptr->sigl > b->sigl;
147  			if (diff == 0)
148  				diff = -(st0_ptr->sigl < b->sigl);
149  		}
150  	}
151  
152  	if (diff > 0) {
153  		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
154  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
155  		       COMP_Denormal : 0);
156  	}
157  	if (diff < 0) {
158  		return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
159  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
160  		       COMP_Denormal : 0);
161  	}
162  
163  	return COMP_A_eq_B
164  	    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
165  	       COMP_Denormal : 0);
166  
167  }
168  
169  /* This function requires that st(0) is not empty */
FPU_compare_st_data(FPU_REG const * loaded_data,u_char loaded_tag)170  int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
171  {
172  	int f, c;
173  
174  	c = compare(loaded_data, loaded_tag);
175  
176  	if (c & COMP_NaN) {
177  		EXCEPTION(EX_Invalid);
178  		f = SW_C3 | SW_C2 | SW_C0;
179  	} else
180  		switch (c & 7) {
181  		case COMP_A_lt_B:
182  			f = SW_C0;
183  			break;
184  		case COMP_A_eq_B:
185  			f = SW_C3;
186  			break;
187  		case COMP_A_gt_B:
188  			f = 0;
189  			break;
190  		case COMP_No_Comp:
191  			f = SW_C3 | SW_C2 | SW_C0;
192  			break;
193  		default:
194  #ifdef PARANOID
195  			EXCEPTION(EX_INTERNAL | 0x121);
196  #endif /* PARANOID */
197  			f = SW_C3 | SW_C2 | SW_C0;
198  			break;
199  		}
200  	setcc(f);
201  	if (c & COMP_Denormal) {
202  		return denormal_operand() < 0;
203  	}
204  	return 0;
205  }
206  
compare_st_st(int nr)207  static int compare_st_st(int nr)
208  {
209  	int f, c;
210  	FPU_REG *st_ptr;
211  
212  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
213  		setcc(SW_C3 | SW_C2 | SW_C0);
214  		/* Stack fault */
215  		EXCEPTION(EX_StackUnder);
216  		return !(control_word & CW_Invalid);
217  	}
218  
219  	st_ptr = &st(nr);
220  	c = compare(st_ptr, FPU_gettagi(nr));
221  	if (c & COMP_NaN) {
222  		setcc(SW_C3 | SW_C2 | SW_C0);
223  		EXCEPTION(EX_Invalid);
224  		return !(control_word & CW_Invalid);
225  	} else
226  		switch (c & 7) {
227  		case COMP_A_lt_B:
228  			f = SW_C0;
229  			break;
230  		case COMP_A_eq_B:
231  			f = SW_C3;
232  			break;
233  		case COMP_A_gt_B:
234  			f = 0;
235  			break;
236  		case COMP_No_Comp:
237  			f = SW_C3 | SW_C2 | SW_C0;
238  			break;
239  		default:
240  #ifdef PARANOID
241  			EXCEPTION(EX_INTERNAL | 0x122);
242  #endif /* PARANOID */
243  			f = SW_C3 | SW_C2 | SW_C0;
244  			break;
245  		}
246  	setcc(f);
247  	if (c & COMP_Denormal) {
248  		return denormal_operand() < 0;
249  	}
250  	return 0;
251  }
252  
compare_i_st_st(int nr)253  static int compare_i_st_st(int nr)
254  {
255  	int f, c;
256  	FPU_REG *st_ptr;
257  
258  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
259  		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
260  		/* Stack fault */
261  		EXCEPTION(EX_StackUnder);
262  		return !(control_word & CW_Invalid);
263  	}
264  
265  	partial_status &= ~SW_C0;
266  	st_ptr = &st(nr);
267  	c = compare(st_ptr, FPU_gettagi(nr));
268  	if (c & COMP_NaN) {
269  		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
270  		EXCEPTION(EX_Invalid);
271  		return !(control_word & CW_Invalid);
272  	}
273  
274  	switch (c & 7) {
275  	case COMP_A_lt_B:
276  		f = X86_EFLAGS_CF;
277  		break;
278  	case COMP_A_eq_B:
279  		f = X86_EFLAGS_ZF;
280  		break;
281  	case COMP_A_gt_B:
282  		f = 0;
283  		break;
284  	case COMP_No_Comp:
285  		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
286  		break;
287  	default:
288  #ifdef PARANOID
289  		EXCEPTION(EX_INTERNAL | 0x122);
290  #endif /* PARANOID */
291  		f = 0;
292  		break;
293  	}
294  	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
295  	if (c & COMP_Denormal) {
296  		return denormal_operand() < 0;
297  	}
298  	return 0;
299  }
300  
compare_u_st_st(int nr)301  static int compare_u_st_st(int nr)
302  {
303  	int f = 0, c;
304  	FPU_REG *st_ptr;
305  
306  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
307  		setcc(SW_C3 | SW_C2 | SW_C0);
308  		/* Stack fault */
309  		EXCEPTION(EX_StackUnder);
310  		return !(control_word & CW_Invalid);
311  	}
312  
313  	st_ptr = &st(nr);
314  	c = compare(st_ptr, FPU_gettagi(nr));
315  	if (c & COMP_NaN) {
316  		setcc(SW_C3 | SW_C2 | SW_C0);
317  		if (c & COMP_SNaN) {	/* This is the only difference between
318  					   un-ordered and ordinary comparisons */
319  			EXCEPTION(EX_Invalid);
320  			return !(control_word & CW_Invalid);
321  		}
322  		return 0;
323  	} else
324  		switch (c & 7) {
325  		case COMP_A_lt_B:
326  			f = SW_C0;
327  			break;
328  		case COMP_A_eq_B:
329  			f = SW_C3;
330  			break;
331  		case COMP_A_gt_B:
332  			f = 0;
333  			break;
334  		case COMP_No_Comp:
335  			f = SW_C3 | SW_C2 | SW_C0;
336  			break;
337  #ifdef PARANOID
338  		default:
339  			EXCEPTION(EX_INTERNAL | 0x123);
340  			f = SW_C3 | SW_C2 | SW_C0;
341  			break;
342  #endif /* PARANOID */
343  		}
344  	setcc(f);
345  	if (c & COMP_Denormal) {
346  		return denormal_operand() < 0;
347  	}
348  	return 0;
349  }
350  
compare_ui_st_st(int nr)351  static int compare_ui_st_st(int nr)
352  {
353  	int f = 0, c;
354  	FPU_REG *st_ptr;
355  
356  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
357  		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
358  		/* Stack fault */
359  		EXCEPTION(EX_StackUnder);
360  		return !(control_word & CW_Invalid);
361  	}
362  
363  	partial_status &= ~SW_C0;
364  	st_ptr = &st(nr);
365  	c = compare(st_ptr, FPU_gettagi(nr));
366  	if (c & COMP_NaN) {
367  		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
368  		if (c & COMP_SNaN) {	/* This is the only difference between
369  					   un-ordered and ordinary comparisons */
370  			EXCEPTION(EX_Invalid);
371  			return !(control_word & CW_Invalid);
372  		}
373  		return 0;
374  	}
375  
376  	switch (c & 7) {
377  	case COMP_A_lt_B:
378  		f = X86_EFLAGS_CF;
379  		break;
380  	case COMP_A_eq_B:
381  		f = X86_EFLAGS_ZF;
382  		break;
383  	case COMP_A_gt_B:
384  		f = 0;
385  		break;
386  	case COMP_No_Comp:
387  		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
388  		break;
389  #ifdef PARANOID
390  	default:
391  		EXCEPTION(EX_INTERNAL | 0x123);
392  		f = 0;
393  		break;
394  #endif /* PARANOID */
395  	}
396  	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
397  	if (c & COMP_Denormal) {
398  		return denormal_operand() < 0;
399  	}
400  	return 0;
401  }
402  
403  /*---------------------------------------------------------------------------*/
404  
fcom_st(void)405  void fcom_st(void)
406  {
407  	/* fcom st(i) */
408  	compare_st_st(FPU_rm);
409  }
410  
fcompst(void)411  void fcompst(void)
412  {
413  	/* fcomp st(i) */
414  	if (!compare_st_st(FPU_rm))
415  		FPU_pop();
416  }
417  
fcompp(void)418  void fcompp(void)
419  {
420  	/* fcompp */
421  	if (FPU_rm != 1) {
422  		FPU_illegal();
423  		return;
424  	}
425  	if (!compare_st_st(1))
426  		poppop();
427  }
428  
fucom_(void)429  void fucom_(void)
430  {
431  	/* fucom st(i) */
432  	compare_u_st_st(FPU_rm);
433  
434  }
435  
fucomp(void)436  void fucomp(void)
437  {
438  	/* fucomp st(i) */
439  	if (!compare_u_st_st(FPU_rm))
440  		FPU_pop();
441  }
442  
fucompp(void)443  void fucompp(void)
444  {
445  	/* fucompp */
446  	if (FPU_rm == 1) {
447  		if (!compare_u_st_st(1))
448  			poppop();
449  	} else
450  		FPU_illegal();
451  }
452  
453  /* P6+ compare-to-EFLAGS ops */
454  
fcomi_(void)455  void fcomi_(void)
456  {
457  	/* fcomi st(i) */
458  	compare_i_st_st(FPU_rm);
459  }
460  
fcomip(void)461  void fcomip(void)
462  {
463  	/* fcomip st(i) */
464  	if (!compare_i_st_st(FPU_rm))
465  		FPU_pop();
466  }
467  
fucomi_(void)468  void fucomi_(void)
469  {
470  	/* fucomi st(i) */
471  	compare_ui_st_st(FPU_rm);
472  }
473  
fucomip(void)474  void fucomip(void)
475  {
476  	/* fucomip st(i) */
477  	if (!compare_ui_st_st(FPU_rm))
478  		FPU_pop();
479  }
480