Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/math-emu/reg_compare.c
26424 views
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 [email protected] |
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
481