Blame view

arch/x86/math-emu/reg_compare.c 8.09 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*---------------------------------------------------------------------------+
   |  reg_compare.c                                                            |
   |                                                                           |
   | Compare two floating point registers                                      |
   |                                                                           |
   | Copyright (C) 1992,1993,1994,1997                                         |
   |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   |                  E-mail   billm@suburbia.net                              |
   |                                                                           |
   |                                                                           |
   +---------------------------------------------------------------------------*/
  
  /*---------------------------------------------------------------------------+
   | compare() is the core FPU_REG comparison function                         |
   +---------------------------------------------------------------------------*/
  
  #include "fpu_system.h"
  #include "exception.h"
  #include "fpu_emu.h"
  #include "control_w.h"
  #include "status_w.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  static int compare(FPU_REG const *b, int tagb)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  	int diff, exp0, expb;
  	u_char st0_tag;
  	FPU_REG *st0_ptr;
  	FPU_REG x, y;
  	u_char st0_sign, signb = getsign(b);
  
  	st0_ptr = &st(0);
  	st0_tag = FPU_gettag0();
  	st0_sign = getsign(st0_ptr);
  
  	if (tagb == TAG_Special)
  		tagb = FPU_Special(b);
  	if (st0_tag == TAG_Special)
  		st0_tag = FPU_Special(st0_ptr);
  
  	if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
  	    || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
  		if (st0_tag == TAG_Zero) {
  			if (tagb == TAG_Zero)
  				return COMP_A_eq_B;
  			if (tagb == TAG_Valid)
  				return ((signb ==
  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  			if (tagb == TW_Denormal)
  				return ((signb ==
  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  				    | COMP_Denormal;
  		} else if (tagb == TAG_Zero) {
  			if (st0_tag == TAG_Valid)
  				return ((st0_sign ==
  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  			if (st0_tag == TW_Denormal)
  				return ((st0_sign ==
  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  				    | COMP_Denormal;
  		}
  
  		if (st0_tag == TW_Infinity) {
  			if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
  				return ((st0_sign ==
  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  			else if (tagb == TW_Denormal)
  				return ((st0_sign ==
  					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  				    | COMP_Denormal;
  			else if (tagb == TW_Infinity) {
  				/* The 80486 book says that infinities can be equal! */
  				return (st0_sign == signb) ? COMP_A_eq_B :
  				    ((st0_sign ==
  				      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  			}
  			/* Fall through to the NaN code */
  		} else if (tagb == TW_Infinity) {
  			if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
  				return ((signb ==
  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  			if (st0_tag == TW_Denormal)
  				return ((signb ==
  					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  				    | COMP_Denormal;
  			/* Fall through to the NaN code */
  		}
  
  		/* The only possibility now should be that one of the arguments
  		   is a NaN */
  		if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
  			int signalling = 0, unsupported = 0;
  			if (st0_tag == TW_NaN) {
  				signalling =
  				    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
  				unsupported = !((exponent(st0_ptr) == EXP_OVER)
  						&& (st0_ptr->
  						    sigh & 0x80000000));
  			}
  			if (tagb == TW_NaN) {
  				signalling |=
  				    (b->sigh & 0xc0000000) == 0x80000000;
  				unsupported |= !((exponent(b) == EXP_OVER)
  						 && (b->sigh & 0x80000000));
  			}
  			if (signalling || unsupported)
  				return COMP_No_Comp | COMP_SNaN | COMP_NaN;
  			else
  				/* Neither is a signaling NaN */
  				return COMP_No_Comp | COMP_NaN;
  		}
  
  		EXCEPTION(EX_Invalid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  	}
3d0d14f98   Ingo Molnar   x86: lindent arch...
113
114
115
116
  	if (st0_sign != signb) {
  		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
  		       COMP_Denormal : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
  	}
3d0d14f98   Ingo Molnar   x86: lindent arch...
118
119
120
121
122
123
124
125
126
127
  	if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
  		FPU_to_exp16(st0_ptr, &x);
  		FPU_to_exp16(b, &y);
  		st0_ptr = &x;
  		b = &y;
  		exp0 = exponent16(st0_ptr);
  		expb = exponent16(b);
  	} else {
  		exp0 = exponent(st0_ptr);
  		expb = exponent(b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
  
  #ifdef PARANOID
3d0d14f98   Ingo Molnar   x86: lindent arch...
131
132
133
134
  	if (!(st0_ptr->sigh & 0x80000000))
  		EXCEPTION(EX_Invalid);
  	if (!(b->sigh & 0x80000000))
  		EXCEPTION(EX_Invalid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  #endif /* PARANOID */
3d0d14f98   Ingo Molnar   x86: lindent arch...
136
137
138
139
140
141
142
143
144
  	diff = exp0 - expb;
  	if (diff == 0) {
  		diff = st0_ptr->sigh - b->sigh;	/* Works only if ms bits are
  						   identical */
  		if (diff == 0) {
  			diff = st0_ptr->sigl > b->sigl;
  			if (diff == 0)
  				diff = -(st0_ptr->sigl < b->sigl);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146

3d0d14f98   Ingo Molnar   x86: lindent arch...
147
148
149
150
151
152
153
154
155
156
  	if (diff > 0) {
  		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
  		       COMP_Denormal : 0);
  	}
  	if (diff < 0) {
  		return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
  		       COMP_Denormal : 0);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157

3d0d14f98   Ingo Molnar   x86: lindent arch...
158
159
160
161
162
  	return COMP_A_eq_B
  	    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
  	       COMP_Denormal : 0);
  
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
  
  /* This function requires that st(0) is not empty */
  int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  	int f = 0, c;
  
  	c = compare(loaded_data, loaded_tag);
  
  	if (c & COMP_NaN) {
  		EXCEPTION(EX_Invalid);
  		f = SW_C3 | SW_C2 | SW_C0;
  	} else
  		switch (c & 7) {
  		case COMP_A_lt_B:
  			f = SW_C0;
  			break;
  		case COMP_A_eq_B:
  			f = SW_C3;
  			break;
  		case COMP_A_gt_B:
  			f = 0;
  			break;
  		case COMP_No_Comp:
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  #ifdef PARANOID
3d0d14f98   Ingo Molnar   x86: lindent arch...
189
190
191
192
  		default:
  			EXCEPTION(EX_INTERNAL | 0x121);
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  #endif /* PARANOID */
3d0d14f98   Ingo Molnar   x86: lindent arch...
194
195
196
197
198
199
  		}
  	setcc(f);
  	if (c & COMP_Denormal) {
  		return denormal_operand() < 0;
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  static int compare_st_st(int nr)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  	int f = 0, c;
  	FPU_REG *st_ptr;
  
  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
  		setcc(SW_C3 | SW_C2 | SW_C0);
  		/* Stack fault */
  		EXCEPTION(EX_StackUnder);
  		return !(control_word & CW_Invalid);
  	}
  
  	st_ptr = &st(nr);
  	c = compare(st_ptr, FPU_gettagi(nr));
  	if (c & COMP_NaN) {
  		setcc(SW_C3 | SW_C2 | SW_C0);
  		EXCEPTION(EX_Invalid);
  		return !(control_word & CW_Invalid);
  	} else
  		switch (c & 7) {
  		case COMP_A_lt_B:
  			f = SW_C0;
  			break;
  		case COMP_A_eq_B:
  			f = SW_C3;
  			break;
  		case COMP_A_gt_B:
  			f = 0;
  			break;
  		case COMP_No_Comp:
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  #ifdef PARANOID
3d0d14f98   Ingo Molnar   x86: lindent arch...
234
235
236
237
  		default:
  			EXCEPTION(EX_INTERNAL | 0x122);
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  #endif /* PARANOID */
3d0d14f98   Ingo Molnar   x86: lindent arch...
239
240
241
242
243
244
  		}
  	setcc(f);
  	if (c & COMP_Denormal) {
  		return denormal_operand() < 0;
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
  static int compare_u_st_st(int nr)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
248
249
250
251
252
253
254
255
  	int f = 0, c;
  	FPU_REG *st_ptr;
  
  	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
  		setcc(SW_C3 | SW_C2 | SW_C0);
  		/* Stack fault */
  		EXCEPTION(EX_StackUnder);
  		return !(control_word & CW_Invalid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	}
3d0d14f98   Ingo Molnar   x86: lindent arch...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  
  	st_ptr = &st(nr);
  	c = compare(st_ptr, FPU_gettagi(nr));
  	if (c & COMP_NaN) {
  		setcc(SW_C3 | SW_C2 | SW_C0);
  		if (c & COMP_SNaN) {	/* This is the only difference between
  					   un-ordered and ordinary comparisons */
  			EXCEPTION(EX_Invalid);
  			return !(control_word & CW_Invalid);
  		}
  		return 0;
  	} else
  		switch (c & 7) {
  		case COMP_A_lt_B:
  			f = SW_C0;
  			break;
  		case COMP_A_eq_B:
  			f = SW_C3;
  			break;
  		case COMP_A_gt_B:
  			f = 0;
  			break;
  		case COMP_No_Comp:
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  #ifdef PARANOID
3d0d14f98   Ingo Molnar   x86: lindent arch...
283
284
285
286
287
288
289
290
291
292
293
  		default:
  			EXCEPTION(EX_INTERNAL | 0x123);
  			f = SW_C3 | SW_C2 | SW_C0;
  			break;
  #endif /* PARANOID */
  		}
  	setcc(f);
  	if (c & COMP_Denormal) {
  		return denormal_operand() < 0;
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
  }
  
  /*---------------------------------------------------------------------------*/
  
  void fcom_st(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
300
301
  	/* fcom st(i) */
  	compare_st_st(FPU_rm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
  void fcompst(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
305
306
307
  	/* fcomp st(i) */
  	if (!compare_st_st(FPU_rm))
  		FPU_pop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
  void fcompp(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
311
312
313
314
315
316
317
  	/* fcompp */
  	if (FPU_rm != 1) {
  		FPU_illegal();
  		return;
  	}
  	if (!compare_st_st(1))
  		poppop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
  void fucom_(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
321
322
  	/* fucom st(i) */
  	compare_u_st_st(FPU_rm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
  void fucomp(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
327
328
329
  	/* fucomp st(i) */
  	if (!compare_u_st_st(FPU_rm))
  		FPU_pop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  void fucompp(void)
  {
3d0d14f98   Ingo Molnar   x86: lindent arch...
333
334
335
336
337
338
  	/* fucompp */
  	if (FPU_rm == 1) {
  		if (!compare_u_st_st(1))
  			poppop();
  	} else
  		FPU_illegal();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  }