xref: /linux/arch/x86/math-emu/reg_compare.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
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 
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 */
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 
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 
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 
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 
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 
405 void fcom_st(void)
406 {
407 	/* fcom st(i) */
408 	compare_st_st(FPU_rm);
409 }
410 
411 void fcompst(void)
412 {
413 	/* fcomp st(i) */
414 	if (!compare_st_st(FPU_rm))
415 		FPU_pop();
416 }
417 
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 
429 void fucom_(void)
430 {
431 	/* fucom st(i) */
432 	compare_u_st_st(FPU_rm);
433 
434 }
435 
436 void fucomp(void)
437 {
438 	/* fucomp st(i) */
439 	if (!compare_u_st_st(FPU_rm))
440 		FPU_pop();
441 }
442 
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 
455 void fcomi_(void)
456 {
457 	/* fcomi st(i) */
458 	compare_i_st_st(FPU_rm);
459 }
460 
461 void fcomip(void)
462 {
463 	/* fcomip st(i) */
464 	if (!compare_i_st_st(FPU_rm))
465 		FPU_pop();
466 }
467 
468 void fucomi_(void)
469 {
470 	/* fucomi st(i) */
471 	compare_ui_st_st(FPU_rm);
472 }
473 
474 void fucomip(void)
475 {
476 	/* fucomip st(i) */
477 	if (!compare_ui_st_st(FPU_rm))
478 		FPU_pop();
479 }
480