5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24 */
25 /*
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29
30 #if defined(__sparc)
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <siginfo.h>
36 #include <thread.h>
37 #include <ucontext.h>
38 #include <math.h>
39 #if defined(__SUNPRO_C)
40 #include <sunmath.h>
41 #endif
42 #include <fenv.h>
43
44 #include "fenv_inlines.h"
45 #include "libm_inlines.h"
46
47 #ifdef __sparcv9
48
49 #define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
50
51 #define FPREG(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_dregs[(X>>1)| \
52 ((X&1)<<4)]
53
54 #else
55
56 #include <sys/procfs.h>
57
58 #define FPxreg(X) &((prxregset_t*)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfr.pr_regs[X]
59
60 #define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
61
62 #define FPREG(X) ((X & 1)? FPxreg(X - 1) : FPreg(X))
63
64 #endif /* __sparcv9 */
65
66 #include "fex_handler.h"
67
68 /* avoid dependence on libsunmath */
69 static enum fp_class_type
70 my_fp_classl(long double *a)
71 {
72 int msw = *(int*)a & ~0x80000000;
73
74 if (msw >= 0x7fff0000) {
75 if (((msw & 0xffff) | *(1+(int*)a) | *(2+(int*)a) | *(3+(int*)a)) == 0)
76 return fp_infinity;
77 else if (msw & 0x8000)
78 return fp_quiet;
79 else
80 return fp_signaling;
81 } else if (msw < 0x10000) {
82 if ((msw | *(1+(int*)a) | *(2+(int*)a) | *(3+(int*)a)) == 0)
83 return fp_zero;
84 else
85 return fp_subnormal;
86 } else
87 return fp_normal;
88 }
89
90 /*
91 * Determine which type of invalid operation exception occurred
92 */
93 enum fex_exception
94 __fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
95 {
96 unsigned instr, opf, rs1, rs2;
97 enum fp_class_type t1, t2;
98
99 /* parse the instruction which caused the exception */
100 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
101 opf = (instr >> 5) & 0x1ff;
102 rs1 = (instr >> 14) & 0x1f;
103 rs2 = instr & 0x1f;
104
105 /* determine the classes of the operands */
106 switch (opf & 3) {
107 case 1: /* single */
108 t1 = fp_classf(*(float*)FPreg(rs1));
109 t2 = fp_classf(*(float*)FPreg(rs2));
110 break;
111
112 case 2: /* double */
113 t1 = fp_class(*(double*)FPREG(rs1));
114 t2 = fp_class(*(double*)FPREG(rs2));
115 break;
116
117 case 3: /* quad */
118 t1 = my_fp_classl((long double*)FPREG(rs1));
119 t2 = my_fp_classl((long double*)FPREG(rs2));
120 break;
121
122 default: /* integer operands never cause an invalid operation */
123 return (enum fex_exception) -1;
124 }
125
126 /* if rs2 is snan, return immediately */
127 if (t2 == fp_signaling)
128 return fex_inv_snan;
129
130 /* determine the type of operation */
131 switch ((instr >> 19) & 0x183f) {
132 case 0x1034: /* add, subtract, multiply, divide, square root, convert */
133 switch (opf & 0x1fc) {
134 case 0x40:
135 case 0x44: /* add or subtract */
136 if (t1 == fp_signaling)
137 return fex_inv_snan;
138 else
139 return fex_inv_isi;
140
141 case 0x48:
142 case 0x68:
143 case 0x6c: /* multiply */
144 if (t1 == fp_signaling)
145 return fex_inv_snan;
146 else
147 return fex_inv_zmi;
148
149 case 0x4c: /* divide */
150 if (t1 == fp_signaling)
151 return fex_inv_snan;
152 else if (t1 == fp_zero)
153 return fex_inv_zdz;
154 else
155 return fex_inv_idi;
156
157 case 0x28: /* square root */
158 return fex_inv_sqrt;
159
160 case 0x80:
161 case 0xd0: /* convert to integer */
162 return fex_inv_int;
163 }
164 break;
165
166 case 0x1035: /* compare */
167 if (t1 == fp_signaling)
168 return fex_inv_snan;
169 else
170 return fex_inv_cmp;
171 }
172
173 return (enum fex_exception) -1;
174 }
175
176 #ifdef __sparcv9
177 extern void _Qp_sqrt(long double *, const long double *);
178 #else
179 extern long double _Q_sqrt(long double);
180 #endif
181
182 /*
183 * Get the operands, generate the default untrapped result with
184 * exceptions, and set a code indicating the type of operation
185 */
186 void
187 __fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
188 {
189 unsigned long fsr;
190 unsigned instr, opf, rs1, rs2;
191 volatile int c;
192
193 /* parse the instruction which caused the exception */
194 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
195 opf = (instr >> 5) & 0x1ff;
196 rs1 = (instr >> 14) & 0x1f;
197 rs2 = instr & 0x1f;
198
199 /* get the operands */
200 switch (opf & 3) {
201 case 0: /* integer */
202 info->op1.type = fex_nodata;
203 if (opf & 0x40) {
204 info->op2.type = fex_int;
205 info->op2.val.i = *(int*)FPreg(rs2);
206 }
207 else {
208 info->op2.type = fex_llong;
209 info->op2.val.l = *(long long*)FPREG(rs2);
210 }
211 break;
212
213 case 1: /* single */
214 info->op1.type = info->op2.type = fex_float;
215 info->op1.val.f = *(float*)FPreg(rs1);
216 info->op2.val.f = *(float*)FPreg(rs2);
217 break;
218
219 case 2: /* double */
220 info->op1.type = info->op2.type = fex_double;
221 info->op1.val.d = *(double*)FPREG(rs1);
222 info->op2.val.d = *(double*)FPREG(rs2);
223 break;
224
225 case 3: /* quad */
226 info->op1.type = info->op2.type = fex_ldouble;
227 info->op1.val.q = *(long double*)FPREG(rs1);
228 info->op2.val.q = *(long double*)FPREG(rs2);
229 break;
230 }
231
232 /* initialize res to the default untrapped result and ex to the
233 corresponding flags (assume trapping is disabled and flags
234 are clear) */
235 info->op = fex_other;
236 info->res.type = fex_nodata;
237 switch ((instr >> 19) & 0x183f) {
238 case 0x1035: /* compare */
239 info->op = fex_cmp;
240 switch (opf) {
241 case 0x51: /* compare single */
242 c = (info->op1.val.f == info->op2.val.f);
243 break;
244
245 case 0x52: /* compare double */
246 c = (info->op1.val.d == info->op2.val.d);
247 break;
248
249 case 0x53: /* compare quad */
250 c = (info->op1.val.q == info->op2.val.q);
251 break;
252
253 case 0x55: /* compare single with exception */
254 c = (info->op1.val.f < info->op2.val.f);
255 break;
256
257 case 0x56: /* compare double with exception */
258 c = (info->op1.val.d < info->op2.val.d);
259 break;
260
261 case 0x57: /* compare quad with exception */
262 c = (info->op1.val.q < info->op2.val.q);
263 break;
264 }
265 break;
266
267 case 0x1034: /* add, subtract, multiply, divide, square root, convert */
268 switch (opf) {
269 case 0x41: /* add single */
270 info->op = fex_add;
271 info->res.type = fex_float;
272 info->res.val.f = info->op1.val.f + info->op2.val.f;
273 break;
274
275 case 0x42: /* add double */
276 info->op = fex_add;
277 info->res.type = fex_double;
278 info->res.val.d = info->op1.val.d + info->op2.val.d;
279 break;
280
281 case 0x43: /* add quad */
282 info->op = fex_add;
283 info->res.type = fex_ldouble;
284 info->res.val.q = info->op1.val.q + info->op2.val.q;
285 break;
286
287 case 0x45: /* subtract single */
306 info->op = fex_mul;
307 info->res.type = fex_float;
308 info->res.val.f = info->op1.val.f * info->op2.val.f;
309 break;
310
311 case 0x4a: /* multiply double */
312 info->op = fex_mul;
313 info->res.type = fex_double;
314 info->res.val.d = info->op1.val.d * info->op2.val.d;
315 break;
316
317 case 0x4b: /* multiply quad */
318 info->op = fex_mul;
319 info->res.type = fex_ldouble;
320 info->res.val.q = info->op1.val.q * info->op2.val.q;
321 break;
322
323 case 0x69: /* fsmuld */
324 info->op = fex_mul;
325 info->res.type = fex_double;
326 info->res.val.d = (double)info->op1.val.f * (double)info->op2.val.f;
327 break;
328
329 case 0x6e: /* fdmulq */
330 info->op = fex_mul;
331 info->res.type = fex_ldouble;
332 info->res.val.q = (long double)info->op1.val.d *
333 (long double)info->op2.val.d;
334 break;
335
336 case 0x4d: /* divide single */
337 info->op = fex_div;
338 info->res.type = fex_float;
339 info->res.val.f = info->op1.val.f / info->op2.val.f;
340 break;
341
342 case 0x4e: /* divide double */
343 info->op = fex_div;
344 info->res.type = fex_double;
345 info->res.val.d = info->op1.val.d / info->op2.val.d;
346 break;
347
348 case 0x4f: /* divide quad */
349 info->op = fex_div;
350 info->res.type = fex_ldouble;
351 info->res.val.q = info->op1.val.q / info->op2.val.q;
352 break;
353
366 info->res.type = fex_double;
367 info->res.val.d = sqrt(info->op1.val.d);
368 break;
369
370 case 0x2b: /* square root quad */
371 info->op = fex_sqrt;
372 info->op1 = info->op2;
373 info->op2.type = fex_nodata;
374 info->res.type = fex_ldouble;
375 #ifdef __sparcv9
376 _Qp_sqrt(&info->res.val.q, &info->op1.val.q);
377 #else
378 info->res.val.q = _Q_sqrt(info->op1.val.q);
379 #endif
380 break;
381
382 default: /* conversions */
383 info->op = fex_cnvt;
384 info->op1 = info->op2;
385 info->op2.type = fex_nodata;
386 switch (opf) {
387 case 0xd1: /* convert single to int */
388 info->res.type = fex_int;
389 info->res.val.i = (int) info->op1.val.f;
390 break;
391
392 case 0xd2: /* convert double to int */
393 info->res.type = fex_int;
394 info->res.val.i = (int) info->op1.val.d;
395 break;
396
397 case 0xd3: /* convert quad to int */
398 info->res.type = fex_int;
399 info->res.val.i = (int) info->op1.val.q;
400 break;
401
402 case 0x81: /* convert single to long long */
403 info->res.type = fex_llong;
404 info->res.val.l = (long long) info->op1.val.f;
405 break;
406
407 case 0x82: /* convert double to long long */
408 info->res.type = fex_llong;
409 info->res.val.l = (long long) info->op1.val.d;
410 break;
411
412 case 0x83: /* convert quad to long long */
413 info->res.type = fex_llong;
414 info->res.val.l = (long long) info->op1.val.q;
415 break;
416
417 case 0xc4: /* convert int to single */
418 info->res.type = fex_float;
419 info->res.val.f = (float) info->op1.val.i;
420 break;
421
422 case 0x84: /* convert long long to single */
423 info->res.type = fex_float;
424 info->res.val.f = (float) info->op1.val.l;
425 break;
426
427 case 0x88: /* convert long long to double */
428 info->res.type = fex_double;
429 info->res.val.d = (double) info->op1.val.l;
430 break;
431
432 case 0xc6: /* convert double to single */
433 info->res.type = fex_float;
434 info->res.val.f = (float) info->op1.val.d;
435 break;
436
437 case 0xc7: /* convert quad to single */
438 info->res.type = fex_float;
439 info->res.val.f = (float) info->op1.val.q;
440 break;
441
442 case 0xc9: /* convert single to double */
443 info->res.type = fex_double;
444 info->res.val.d = (double) info->op1.val.f;
445 break;
446
447 case 0xcb: /* convert quad to double */
448 info->res.type = fex_double;
449 info->res.val.d = (double) info->op1.val.q;
450 break;
451
452 case 0xcd: /* convert single to quad */
453 info->res.type = fex_ldouble;
454 info->res.val.q = (long double) info->op1.val.f;
455 break;
456
457 case 0xce: /* convert double to quad */
458 info->res.type = fex_ldouble;
459 info->res.val.q = (long double) info->op1.val.d;
460 break;
461 }
462 }
463 break;
464 }
465 __fenv_getfsr(&fsr);
466 info->flags = (int)__fenv_get_ex(fsr);
467 __fenv_set_ex(fsr, 0);
468 __fenv_setfsr(&fsr);
469 }
470
471 /*
472 * Store the specified result; if no result is given but the exception
473 * is underflow or overflow, supply the default trapped result
474 */
475 void
476 __fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
477 {
478 unsigned instr, opf, rs1, rs2, rd;
479 long double qscl;
480 double dscl;
481 float fscl;
482
483 /* parse the instruction which caused the exception */
484 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
485 opf = (instr >> 5) & 0x1ff;
486 rs1 = (instr >> 14) & 0x1f;
487 rs2 = instr & 0x1f;
488 rd = (instr >> 25) & 0x1f;
489
490 /* if the instruction is a compare, just set fcc to unordered */
491 if (((instr >> 19) & 0x183f) == 0x1035) {
492 if (rd == 0)
493 uap->uc_mcontext.fpregs.fpu_fsr |= 0xc00;
494 else {
495 #ifdef __sparcv9
496 uap->uc_mcontext.fpregs.fpu_fsr |= (3l << ((rd << 1) + 30));
497 #else
498 ((prxregset_t*)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfsr |= (3 << ((rd - 1) << 1));
499 #endif
500 }
501 return;
502 }
503
504 /* if there is no result available, try to generate the untrapped
505 default */
506 if (info->res.type == fex_nodata) {
507 /* set scale factors for exponent wrapping */
508 switch (sip->si_code) {
509 case FPE_FLTOVF:
510 fscl = 1.262177448e-29f; /* 2^-96 */
511 dscl = 6.441148769597133308e-232; /* 2^-768 */
512 qscl = 8.778357852076208839765066529179033145e-3700l;/* 2^-12288 */
513 break;
514
515 case FPE_FLTUND:
516 fscl = 7.922816251e+28f; /* 2^96 */
517 dscl = 1.552518092300708935e+231; /* 2^768 */
518 qscl = 1.139165225263043370845938579315932009e+3699l;/* 2^12288 */
519 break;
520
521 default:
522 /* user may have blown away the default result by mistake,
523 so try to regenerate it */
524 (void) __fex_get_op(sip, uap, info);
525 if (info->res.type != fex_nodata)
526 goto stuff;
527 /* couldn't do it */
528 return;
529 }
530
531 /* get the operands */
532 switch (opf & 3) {
533 case 1: /* single */
534 info->op1.val.f = *(float*)FPreg(rs1);
535 info->op2.val.f = *(float*)FPreg(rs2);
536 break;
537
538 case 2: /* double */
539 info->op1.val.d = *(double*)FPREG(rs1);
540 info->op2.val.d = *(double*)FPREG(rs2);
541 break;
542
543 case 3: /* quad */
544 info->op1.val.q = *(long double*)FPREG(rs1);
545 info->op2.val.q = *(long double*)FPREG(rs2);
546 break;
547 }
548
549 /* generate the wrapped result */
550 switch (opf) {
551 case 0x41: /* add single */
552 info->res.type = fex_float;
553 info->res.val.f = fscl * (fscl * info->op1.val.f +
554 fscl * info->op2.val.f);
555 break;
556
557 case 0x42: /* add double */
558 info->res.type = fex_double;
559 info->res.val.d = dscl * (dscl * info->op1.val.d +
560 dscl * info->op2.val.d);
561 break;
562
563 case 0x43: /* add quad */
564 info->res.type = fex_ldouble;
565 info->res.val.q = qscl * (qscl * info->op1.val.q +
569 case 0x45: /* subtract single */
570 info->res.type = fex_float;
571 info->res.val.f = fscl * (fscl * info->op1.val.f -
572 fscl * info->op2.val.f);
573 break;
574
575 case 0x46: /* subtract double */
576 info->res.type = fex_double;
577 info->res.val.d = dscl * (dscl * info->op1.val.d -
578 dscl * info->op2.val.d);
579 break;
580
581 case 0x47: /* subtract quad */
582 info->res.type = fex_ldouble;
583 info->res.val.q = qscl * (qscl * info->op1.val.q -
584 qscl * info->op2.val.q);
585 break;
586
587 case 0x49: /* multiply single */
588 info->res.type = fex_float;
589 info->res.val.f = (fscl * info->op1.val.f) *
590 (fscl * info->op2.val.f);
591 break;
592
593 case 0x4a: /* multiply double */
594 info->res.type = fex_double;
595 info->res.val.d = (dscl * info->op1.val.d) *
596 (dscl * info->op2.val.d);
597 break;
598
599 case 0x4b: /* multiply quad */
600 info->res.type = fex_ldouble;
601 info->res.val.q = (qscl * info->op1.val.q) *
602 (qscl * info->op2.val.q);
603 break;
604
605 case 0x4d: /* divide single */
606 info->res.type = fex_float;
607 info->res.val.f = (fscl * info->op1.val.f) /
608 (info->op2.val.f / fscl);
609 break;
610
611 case 0x4e: /* divide double */
612 info->res.type = fex_double;
613 info->res.val.d = (dscl * info->op1.val.d) /
614 (info->op2.val.d / dscl);
615 break;
616
617 case 0x4f: /* divide quad */
618 info->res.type = fex_ldouble;
619 info->res.val.q = (qscl * info->op1.val.q) /
620 (info->op2.val.q / qscl);
621 break;
622
623 case 0xc6: /* convert double to single */
624 info->res.type = fex_float;
625 info->res.val.f = (float) (fscl * (fscl * info->op1.val.d));
626 break;
627
628 case 0xc7: /* convert quad to single */
629 info->res.type = fex_float;
630 info->res.val.f = (float) (fscl * (fscl * info->op1.val.q));
631 break;
632
633 case 0xcb: /* convert quad to double */
634 info->res.type = fex_double;
635 info->res.val.d = (double) (dscl * (dscl * info->op1.val.q));
636 break;
637 }
638
639 if (info->res.type == fex_nodata)
640 /* couldn't do it */
641 return;
642 }
643
644 stuff:
645 /* stick the result in the destination */
646 if (opf & 0x80) { /* conversion */
647 if (opf & 0x10) { /* result is an int */
648 switch (info->res.type) {
649 case fex_llong:
650 info->res.val.i = (int) info->res.val.l;
651 break;
652
653 case fex_float:
654 info->res.val.i = (int) info->res.val.f;
655 break;
656
657 case fex_double:
658 info->res.val.i = (int) info->res.val.d;
659 break;
660
661 case fex_ldouble:
662 info->res.val.i = (int) info->res.val.q;
663 break;
664
665 default:
666 break;
667 }
668 *(int*)FPreg(rd) = info->res.val.i;
669 return;
670 }
671
672 switch (opf & 0xc) {
673 case 0: /* result is long long */
674 switch (info->res.type) {
675 case fex_int:
676 info->res.val.l = (long long) info->res.val.i;
677 break;
678
679 case fex_float:
680 info->res.val.l = (long long) info->res.val.f;
681 break;
682
683 case fex_double:
684 info->res.val.l = (long long) info->res.val.d;
685 break;
686
687 case fex_ldouble:
688 info->res.val.l = (long long) info->res.val.q;
689 break;
690
691 default:
692 break;
693 }
694 *(long long*)FPREG(rd) = info->res.val.l;
695 break;
696
697 case 0x4: /* result is float */
698 switch (info->res.type) {
699 case fex_int:
700 info->res.val.f = (float) info->res.val.i;
701 break;
702
703 case fex_llong:
704 info->res.val.f = (float) info->res.val.l;
705 break;
706
707 case fex_double:
708 info->res.val.f = (float) info->res.val.d;
709 break;
710
711 case fex_ldouble:
712 info->res.val.f = (float) info->res.val.q;
713 break;
714
715 default:
716 break;
717 }
718 *(float*)FPreg(rd) = info->res.val.f;
719 break;
720
721 case 0x8: /* result is double */
722 switch (info->res.type) {
723 case fex_int:
724 info->res.val.d = (double) info->res.val.i;
725 break;
726
727 case fex_llong:
728 info->res.val.d = (double) info->res.val.l;
729 break;
730
731 case fex_float:
732 info->res.val.d = (double) info->res.val.f;
733 break;
734
735 case fex_ldouble:
736 info->res.val.d = (double) info->res.val.q;
737 break;
738
739 default:
740 break;
741 }
742 *(double*)FPREG(rd) = info->res.val.d;
743 break;
744
745 case 0xc: /* result is long double */
746 switch (info->res.type) {
747 case fex_int:
748 info->res.val.q = (long double) info->res.val.i;
749 break;
750
751 case fex_llong:
752 info->res.val.q = (long double) info->res.val.l;
753 break;
754
755 case fex_float:
756 info->res.val.q = (long double) info->res.val.f;
757 break;
758
759 case fex_double:
760 info->res.val.q = (long double) info->res.val.d;
761 break;
762
763 default:
764 break;
765 }
766 *(long double*)FPREG(rd) = info->res.val.q;
767 break;
768 }
769 return;
770 }
771
772 if ((opf & 0xf0) == 0x60) { /* fsmuld, fdmulq */
773 switch (opf & 0xc0) {
774 case 0x8: /* result is double */
775 switch (info->res.type) {
776 case fex_int:
777 info->res.val.d = (double) info->res.val.i;
778 break;
779
780 case fex_llong:
781 info->res.val.d = (double) info->res.val.l;
782 break;
783
784 case fex_float:
785 info->res.val.d = (double) info->res.val.f;
786 break;
787
788 case fex_ldouble:
789 info->res.val.d = (double) info->res.val.q;
790 break;
791
792 default:
793 break;
794 }
795 *(double*)FPREG(rd) = info->res.val.d;
796 break;
797
798 case 0xc: /* result is long double */
799 switch (info->res.type) {
800 case fex_int:
801 info->res.val.q = (long double) info->res.val.i;
802 break;
803
804 case fex_llong:
805 info->res.val.q = (long double) info->res.val.l;
806 break;
807
808 case fex_float:
809 info->res.val.q = (long double) info->res.val.f;
810 break;
811
812 case fex_double:
813 info->res.val.q = (long double) info->res.val.d;
814 break;
815
816 default:
817 break;
818 }
819 *(long double*)FPREG(rd) = info->res.val.q;
820 break;
821 }
822 return;
823 }
824
825 switch (opf & 3) { /* other arithmetic op */
826 case 1: /* result is float */
827 switch (info->res.type) {
828 case fex_int:
829 info->res.val.f = (float) info->res.val.i;
830 break;
831
832 case fex_llong:
833 info->res.val.f = (float) info->res.val.l;
834 break;
835
836 case fex_double:
837 info->res.val.f = (float) info->res.val.d;
838 break;
839
840 case fex_ldouble:
841 info->res.val.f = (float) info->res.val.q;
842 break;
843
844 default:
845 break;
846 }
847 *(float*)FPreg(rd) = info->res.val.f;
848 break;
849
850 case 2: /* result is double */
851 switch (info->res.type) {
852 case fex_int:
853 info->res.val.d = (double) info->res.val.i;
854 break;
855
856 case fex_llong:
857 info->res.val.d = (double) info->res.val.l;
858 break;
859
860 case fex_float:
861 info->res.val.d = (double) info->res.val.f;
862 break;
863
864 case fex_ldouble:
865 info->res.val.d = (double) info->res.val.q;
866 break;
867
868 default:
869 break;
870 }
871 *(double*)FPREG(rd) = info->res.val.d;
872 break;
873
874 case 3: /* result is long double */
875 switch (info->res.type) {
876 case fex_int:
877 info->res.val.q = (long double) info->res.val.i;
878 break;
879
880 case fex_llong:
881 info->res.val.q = (long double) info->res.val.l;
882 break;
883
884 case fex_float:
885 info->res.val.q = (long double) info->res.val.f;
886 break;
887
888 case fex_double:
889 info->res.val.q = (long double) info->res.val.d;
890 break;
891
892 default:
893 break;
894 }
895 *(long double*)FPREG(rd) = info->res.val.q;
896 break;
897 }
898 }
899 #endif /* defined(__sparc) */
|
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 #if defined(__sparc)
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <siginfo.h>
37 #include <thread.h>
38 #include <ucontext.h>
39 #include <math.h>
40 #if defined(__SUNPRO_C)
41 #include <sunmath.h>
42 #endif
43 #include <fenv.h>
44
45 #include "fenv_inlines.h"
46 #include "libm_inlines.h"
47
48 #ifdef __sparcv9
49 #define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
50
51 #define FPREG(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_dregs[(X >> 1) | \
52 ((X & 1) << 4)]
53 #else
54 #include <sys/procfs.h>
55
56 #define FPxreg(X) & \
57 ((prxregset_t *)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfr. \
58 pr_regs[X]
59
60 #define FPreg(X) & uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
61
62 #define FPREG(X) ((X & 1) ? FPxreg(X - 1) : FPreg(X))
63 #endif /* __sparcv9 */
64
65 #include "fex_handler.h"
66
67 /* avoid dependence on libsunmath */
68 static enum fp_class_type
69 my_fp_classl(long double *a)
70 {
71 int msw = *(int *)a & ~0x80000000;
72
73 if (msw >= 0x7fff0000) {
74 if (((msw & 0xffff) | *(1 + (int *)a) | *(2 + (int *)a) | *(3 +
75 (int *)a)) == 0)
76 return (fp_infinity);
77 else if (msw & 0x8000)
78 return (fp_quiet);
79 else
80 return (fp_signaling);
81 } else if (msw < 0x10000) {
82 if ((msw | *(1 + (int *)a) | *(2 + (int *)a) | *(3 +
83 (int *)a)) == 0)
84 return (fp_zero);
85 else
86 return (fp_subnormal);
87 } else {
88 return (fp_normal);
89 }
90 }
91
92 /*
93 * Determine which type of invalid operation exception occurred
94 */
95 enum fex_exception
96 __fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
97 {
98 unsigned instr, opf, rs1, rs2;
99 enum fp_class_type t1, t2;
100
101 /* parse the instruction which caused the exception */
102 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
103 opf = (instr >> 5) & 0x1ff;
104 rs1 = (instr >> 14) & 0x1f;
105 rs2 = instr & 0x1f;
106
107 /* determine the classes of the operands */
108 switch (opf & 3) {
109 case 1: /* single */
110 t1 = fp_classf(*(float *)FPreg(rs1));
111 t2 = fp_classf(*(float *)FPreg(rs2));
112 break;
113
114 case 2: /* double */
115 t1 = fp_class(*(double *)FPREG(rs1));
116 t2 = fp_class(*(double *)FPREG(rs2));
117 break;
118
119 case 3: /* quad */
120 t1 = my_fp_classl((long double *)FPREG(rs1));
121 t2 = my_fp_classl((long double *)FPREG(rs2));
122 break;
123
124 default: /* integer operands never cause an invalid operation */
125 return ((enum fex_exception)-1);
126 }
127
128 /* if rs2 is snan, return immediately */
129 if (t2 == fp_signaling)
130 return (fex_inv_snan);
131
132 /* determine the type of operation */
133 switch ((instr >> 19) & 0x183f) {
134 case 0x1034: /* add, subtract, multiply, divide, square root, convert */
135
136 switch (opf & 0x1fc) {
137 case 0x40:
138 case 0x44: /* add or subtract */
139
140 if (t1 == fp_signaling)
141 return (fex_inv_snan);
142 else
143 return (fex_inv_isi);
144
145 case 0x48:
146 case 0x68:
147 case 0x6c: /* multiply */
148
149 if (t1 == fp_signaling)
150 return (fex_inv_snan);
151 else
152 return (fex_inv_zmi);
153
154 case 0x4c: /* divide */
155
156 if (t1 == fp_signaling)
157 return (fex_inv_snan);
158 else if (t1 == fp_zero)
159 return (fex_inv_zdz);
160 else
161 return (fex_inv_idi);
162
163 case 0x28: /* square root */
164 return (fex_inv_sqrt);
165
166 case 0x80:
167 case 0xd0: /* convert to integer */
168 return (fex_inv_int);
169 }
170
171 break;
172
173 case 0x1035: /* compare */
174
175 if (t1 == fp_signaling)
176 return (fex_inv_snan);
177 else
178 return (fex_inv_cmp);
179 }
180
181 return ((enum fex_exception)-1);
182 }
183
184 #ifdef __sparcv9
185 extern void _Qp_sqrt(long double *, const long double *);
186 #else
187 extern long double _Q_sqrt(long double);
188 #endif
189
190 /*
191 * Get the operands, generate the default untrapped result with
192 * exceptions, and set a code indicating the type of operation
193 */
194 void
195 __fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
196 {
197 unsigned long fsr;
198 unsigned instr, opf, rs1, rs2;
199 volatile int c;
200
201 /* parse the instruction which caused the exception */
202 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
203 opf = (instr >> 5) & 0x1ff;
204 rs1 = (instr >> 14) & 0x1f;
205 rs2 = instr & 0x1f;
206
207 /* get the operands */
208 switch (opf & 3) {
209 case 0: /* integer */
210 info->op1.type = fex_nodata;
211
212 if (opf & 0x40) {
213 info->op2.type = fex_int;
214 info->op2.val.i = *(int *)FPreg(rs2);
215 } else {
216 info->op2.type = fex_llong;
217 info->op2.val.l = *(long long *)FPREG(rs2);
218 }
219
220 break;
221
222 case 1: /* single */
223 info->op1.type = info->op2.type = fex_float;
224 info->op1.val.f = *(float *)FPreg(rs1);
225 info->op2.val.f = *(float *)FPreg(rs2);
226 break;
227
228 case 2: /* double */
229 info->op1.type = info->op2.type = fex_double;
230 info->op1.val.d = *(double *)FPREG(rs1);
231 info->op2.val.d = *(double *)FPREG(rs2);
232 break;
233
234 case 3: /* quad */
235 info->op1.type = info->op2.type = fex_ldouble;
236 info->op1.val.q = *(long double *)FPREG(rs1);
237 info->op2.val.q = *(long double *)FPREG(rs2);
238 break;
239 }
240
241 /*
242 * initialize res to the default untrapped result and ex to the
243 * corresponding flags (assume trapping is disabled and flags are
244 * clear)
245 */
246 info->op = fex_other;
247 info->res.type = fex_nodata;
248
249 switch ((instr >> 19) & 0x183f) {
250 case 0x1035: /* compare */
251 info->op = fex_cmp;
252
253 switch (opf) {
254 case 0x51: /* compare single */
255 c = (info->op1.val.f == info->op2.val.f);
256 break;
257
258 case 0x52: /* compare double */
259 c = (info->op1.val.d == info->op2.val.d);
260 break;
261
262 case 0x53: /* compare quad */
263 c = (info->op1.val.q == info->op2.val.q);
264 break;
265
266 case 0x55: /* compare single with exception */
267 c = (info->op1.val.f < info->op2.val.f);
268 break;
269
270 case 0x56: /* compare double with exception */
271 c = (info->op1.val.d < info->op2.val.d);
272 break;
273
274 case 0x57: /* compare quad with exception */
275 c = (info->op1.val.q < info->op2.val.q);
276 break;
277 }
278
279 break;
280
281 case 0x1034: /* add, subtract, multiply, divide, square root, convert */
282
283 switch (opf) {
284 case 0x41: /* add single */
285 info->op = fex_add;
286 info->res.type = fex_float;
287 info->res.val.f = info->op1.val.f + info->op2.val.f;
288 break;
289
290 case 0x42: /* add double */
291 info->op = fex_add;
292 info->res.type = fex_double;
293 info->res.val.d = info->op1.val.d + info->op2.val.d;
294 break;
295
296 case 0x43: /* add quad */
297 info->op = fex_add;
298 info->res.type = fex_ldouble;
299 info->res.val.q = info->op1.val.q + info->op2.val.q;
300 break;
301
302 case 0x45: /* subtract single */
321 info->op = fex_mul;
322 info->res.type = fex_float;
323 info->res.val.f = info->op1.val.f * info->op2.val.f;
324 break;
325
326 case 0x4a: /* multiply double */
327 info->op = fex_mul;
328 info->res.type = fex_double;
329 info->res.val.d = info->op1.val.d * info->op2.val.d;
330 break;
331
332 case 0x4b: /* multiply quad */
333 info->op = fex_mul;
334 info->res.type = fex_ldouble;
335 info->res.val.q = info->op1.val.q * info->op2.val.q;
336 break;
337
338 case 0x69: /* fsmuld */
339 info->op = fex_mul;
340 info->res.type = fex_double;
341 info->res.val.d = (double)info->op1.val.f *
342 (double)info->op2.val.f;
343 break;
344
345 case 0x6e: /* fdmulq */
346 info->op = fex_mul;
347 info->res.type = fex_ldouble;
348 info->res.val.q = (long double)info->op1.val.d * (long
349 double)info->op2.val.d;
350 break;
351
352 case 0x4d: /* divide single */
353 info->op = fex_div;
354 info->res.type = fex_float;
355 info->res.val.f = info->op1.val.f / info->op2.val.f;
356 break;
357
358 case 0x4e: /* divide double */
359 info->op = fex_div;
360 info->res.type = fex_double;
361 info->res.val.d = info->op1.val.d / info->op2.val.d;
362 break;
363
364 case 0x4f: /* divide quad */
365 info->op = fex_div;
366 info->res.type = fex_ldouble;
367 info->res.val.q = info->op1.val.q / info->op2.val.q;
368 break;
369
382 info->res.type = fex_double;
383 info->res.val.d = sqrt(info->op1.val.d);
384 break;
385
386 case 0x2b: /* square root quad */
387 info->op = fex_sqrt;
388 info->op1 = info->op2;
389 info->op2.type = fex_nodata;
390 info->res.type = fex_ldouble;
391 #ifdef __sparcv9
392 _Qp_sqrt(&info->res.val.q, &info->op1.val.q);
393 #else
394 info->res.val.q = _Q_sqrt(info->op1.val.q);
395 #endif
396 break;
397
398 default: /* conversions */
399 info->op = fex_cnvt;
400 info->op1 = info->op2;
401 info->op2.type = fex_nodata;
402
403 switch (opf) {
404 case 0xd1: /* convert single to int */
405 info->res.type = fex_int;
406 info->res.val.i = (int)info->op1.val.f;
407 break;
408
409 case 0xd2: /* convert double to int */
410 info->res.type = fex_int;
411 info->res.val.i = (int)info->op1.val.d;
412 break;
413
414 case 0xd3: /* convert quad to int */
415 info->res.type = fex_int;
416 info->res.val.i = (int)info->op1.val.q;
417 break;
418
419 case 0x81: /* convert single to long long */
420 info->res.type = fex_llong;
421 info->res.val.l = (long long)info->op1.val.f;
422 break;
423
424 case 0x82: /* convert double to long long */
425 info->res.type = fex_llong;
426 info->res.val.l = (long long)info->op1.val.d;
427 break;
428
429 case 0x83: /* convert quad to long long */
430 info->res.type = fex_llong;
431 info->res.val.l = (long long)info->op1.val.q;
432 break;
433
434 case 0xc4: /* convert int to single */
435 info->res.type = fex_float;
436 info->res.val.f = (float)info->op1.val.i;
437 break;
438
439 case 0x84: /* convert long long to single */
440 info->res.type = fex_float;
441 info->res.val.f = (float)info->op1.val.l;
442 break;
443
444 case 0x88: /* convert long long to double */
445 info->res.type = fex_double;
446 info->res.val.d = (double)info->op1.val.l;
447 break;
448
449 case 0xc6: /* convert double to single */
450 info->res.type = fex_float;
451 info->res.val.f = (float)info->op1.val.d;
452 break;
453
454 case 0xc7: /* convert quad to single */
455 info->res.type = fex_float;
456 info->res.val.f = (float)info->op1.val.q;
457 break;
458
459 case 0xc9: /* convert single to double */
460 info->res.type = fex_double;
461 info->res.val.d = (double)info->op1.val.f;
462 break;
463
464 case 0xcb: /* convert quad to double */
465 info->res.type = fex_double;
466 info->res.val.d = (double)info->op1.val.q;
467 break;
468
469 case 0xcd: /* convert single to quad */
470 info->res.type = fex_ldouble;
471 info->res.val.q = (long double)info->op1.val.f;
472 break;
473
474 case 0xce: /* convert double to quad */
475 info->res.type = fex_ldouble;
476 info->res.val.q = (long double)info->op1.val.d;
477 break;
478 }
479 }
480
481 break;
482 }
483
484 __fenv_getfsr(&fsr);
485 info->flags = (int)__fenv_get_ex(fsr);
486 __fenv_set_ex(fsr, 0);
487 __fenv_setfsr(&fsr);
488 }
489
490 /*
491 * Store the specified result; if no result is given but the exception
492 * is underflow or overflow, supply the default trapped result
493 */
494 void
495 __fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
496 {
497 unsigned instr, opf, rs1, rs2, rd;
498 long double qscl;
499 double dscl;
500 float fscl;
501
502 /* parse the instruction which caused the exception */
503 instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
504 opf = (instr >> 5) & 0x1ff;
505 rs1 = (instr >> 14) & 0x1f;
506 rs2 = instr & 0x1f;
507 rd = (instr >> 25) & 0x1f;
508
509 /* if the instruction is a compare, just set fcc to unordered */
510 if (((instr >> 19) & 0x183f) == 0x1035) {
511 if (rd == 0) {
512 uap->uc_mcontext.fpregs.fpu_fsr |= 0xc00;
513 } else {
514 #ifdef __sparcv9
515 uap->uc_mcontext.fpregs.fpu_fsr |= (3l << ((rd << 1) +
516 30));
517 #else
518 ((prxregset_t *)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.
519 pr_v8p.pr_xfsr |= (3 << ((rd - 1) << 1));
520 #endif
521 }
522
523 return;
524 }
525
526 /*
527 * if there is no result available, try to generate the untrapped
528 * default
529 */
530 if (info->res.type == fex_nodata) {
531 /* set scale factors for exponent wrapping */
532 switch (sip->si_code) {
533 case FPE_FLTOVF:
534 /* 2^-96 */
535 fscl = 1.262177448e-29f;
536 /* 2^-768 */
537 dscl = 6.441148769597133308e-232;
538 /* 2^-12288 */
539 qscl = 8.778357852076208839765066529179033145e-3700l;
540 break;
541
542 case FPE_FLTUND:
543 /* 2^96 */
544 fscl = 7.922816251e+28f;
545 /* 2^768 */
546 dscl = 1.552518092300708935e+231;
547 /* 2^12288 */
548 qscl = 1.139165225263043370845938579315932009e+3699l;
549 break;
550
551 default:
552
553 /*
554 * user may have blown away the default result by
555 * mistake, so try to regenerate it
556 */
557 (void) __fex_get_op(sip, uap, info);
558
559 if (info->res.type != fex_nodata)
560 goto stuff;
561
562 /* couldn't do it */
563 return;
564 }
565
566 /* get the operands */
567 switch (opf & 3) {
568 case 1: /* single */
569 info->op1.val.f = *(float *)FPreg(rs1);
570 info->op2.val.f = *(float *)FPreg(rs2);
571 break;
572
573 case 2: /* double */
574 info->op1.val.d = *(double *)FPREG(rs1);
575 info->op2.val.d = *(double *)FPREG(rs2);
576 break;
577
578 case 3: /* quad */
579 info->op1.val.q = *(long double *)FPREG(rs1);
580 info->op2.val.q = *(long double *)FPREG(rs2);
581 break;
582 }
583
584 /* generate the wrapped result */
585 switch (opf) {
586 case 0x41: /* add single */
587 info->res.type = fex_float;
588 info->res.val.f = fscl * (fscl * info->op1.val.f +
589 fscl * info->op2.val.f);
590 break;
591
592 case 0x42: /* add double */
593 info->res.type = fex_double;
594 info->res.val.d = dscl * (dscl * info->op1.val.d +
595 dscl * info->op2.val.d);
596 break;
597
598 case 0x43: /* add quad */
599 info->res.type = fex_ldouble;
600 info->res.val.q = qscl * (qscl * info->op1.val.q +
604 case 0x45: /* subtract single */
605 info->res.type = fex_float;
606 info->res.val.f = fscl * (fscl * info->op1.val.f -
607 fscl * info->op2.val.f);
608 break;
609
610 case 0x46: /* subtract double */
611 info->res.type = fex_double;
612 info->res.val.d = dscl * (dscl * info->op1.val.d -
613 dscl * info->op2.val.d);
614 break;
615
616 case 0x47: /* subtract quad */
617 info->res.type = fex_ldouble;
618 info->res.val.q = qscl * (qscl * info->op1.val.q -
619 qscl * info->op2.val.q);
620 break;
621
622 case 0x49: /* multiply single */
623 info->res.type = fex_float;
624 info->res.val.f = (fscl * info->op1.val.f) * (fscl *
625 info->op2.val.f);
626 break;
627
628 case 0x4a: /* multiply double */
629 info->res.type = fex_double;
630 info->res.val.d = (dscl * info->op1.val.d) * (dscl *
631 info->op2.val.d);
632 break;
633
634 case 0x4b: /* multiply quad */
635 info->res.type = fex_ldouble;
636 info->res.val.q = (qscl * info->op1.val.q) * (qscl *
637 info->op2.val.q);
638 break;
639
640 case 0x4d: /* divide single */
641 info->res.type = fex_float;
642 info->res.val.f = (fscl * info->op1.val.f) /
643 (info->op2.val.f / fscl);
644 break;
645
646 case 0x4e: /* divide double */
647 info->res.type = fex_double;
648 info->res.val.d = (dscl * info->op1.val.d) /
649 (info->op2.val.d / dscl);
650 break;
651
652 case 0x4f: /* divide quad */
653 info->res.type = fex_ldouble;
654 info->res.val.q = (qscl * info->op1.val.q) /
655 (info->op2.val.q / qscl);
656 break;
657
658 case 0xc6: /* convert double to single */
659 info->res.type = fex_float;
660 info->res.val.f = (float)(fscl * (fscl *
661 info->op1.val.d));
662 break;
663
664 case 0xc7: /* convert quad to single */
665 info->res.type = fex_float;
666 info->res.val.f = (float)(fscl * (fscl *
667 info->op1.val.q));
668 break;
669
670 case 0xcb: /* convert quad to double */
671 info->res.type = fex_double;
672 info->res.val.d = (double)(dscl * (dscl *
673 info->op1.val.q));
674 break;
675 }
676
677 if (info->res.type == fex_nodata)
678 /* couldn't do it */
679 return;
680 }
681
682 stuff:
683 /* stick the result in the destination */
684 if (opf & 0x80) { /* conversion */
685 if (opf & 0x10) { /* result is an int */
686 switch (info->res.type) {
687 case fex_llong:
688 info->res.val.i = (int)info->res.val.l;
689 break;
690
691 case fex_float:
692 info->res.val.i = (int)info->res.val.f;
693 break;
694
695 case fex_double:
696 info->res.val.i = (int)info->res.val.d;
697 break;
698
699 case fex_ldouble:
700 info->res.val.i = (int)info->res.val.q;
701 break;
702
703 default:
704 break;
705 }
706
707 *(int *)FPreg(rd) = info->res.val.i;
708 return;
709 }
710
711 switch (opf & 0xc) {
712 case 0: /* result is long long */
713
714 switch (info->res.type) {
715 case fex_int:
716 info->res.val.l = (long long)info->res.val.i;
717 break;
718
719 case fex_float:
720 info->res.val.l = (long long)info->res.val.f;
721 break;
722
723 case fex_double:
724 info->res.val.l = (long long)info->res.val.d;
725 break;
726
727 case fex_ldouble:
728 info->res.val.l = (long long)info->res.val.q;
729 break;
730
731 default:
732 break;
733 }
734
735 *(long long *)FPREG(rd) = info->res.val.l;
736 break;
737
738 case 0x4: /* result is float */
739
740 switch (info->res.type) {
741 case fex_int:
742 info->res.val.f = (float)info->res.val.i;
743 break;
744
745 case fex_llong:
746 info->res.val.f = (float)info->res.val.l;
747 break;
748
749 case fex_double:
750 info->res.val.f = (float)info->res.val.d;
751 break;
752
753 case fex_ldouble:
754 info->res.val.f = (float)info->res.val.q;
755 break;
756
757 default:
758 break;
759 }
760
761 *(float *)FPreg(rd) = info->res.val.f;
762 break;
763
764 case 0x8: /* result is double */
765
766 switch (info->res.type) {
767 case fex_int:
768 info->res.val.d = (double)info->res.val.i;
769 break;
770
771 case fex_llong:
772 info->res.val.d = (double)info->res.val.l;
773 break;
774
775 case fex_float:
776 info->res.val.d = (double)info->res.val.f;
777 break;
778
779 case fex_ldouble:
780 info->res.val.d = (double)info->res.val.q;
781 break;
782
783 default:
784 break;
785 }
786
787 *(double *)FPREG(rd) = info->res.val.d;
788 break;
789
790 case 0xc: /* result is long double */
791
792 switch (info->res.type) {
793 case fex_int:
794 info->res.val.q = (long double)info->res.val.i;
795 break;
796
797 case fex_llong:
798 info->res.val.q = (long double)info->res.val.l;
799 break;
800
801 case fex_float:
802 info->res.val.q = (long double)info->res.val.f;
803 break;
804
805 case fex_double:
806 info->res.val.q = (long double)info->res.val.d;
807 break;
808
809 default:
810 break;
811 }
812
813 *(long double *)FPREG(rd) = info->res.val.q;
814 break;
815 }
816
817 return;
818 }
819
820 if ((opf & 0xf0) == 0x60) { /* fsmuld, fdmulq */
821 switch (opf & 0xc0) {
822 case 0x8: /* result is double */
823
824 switch (info->res.type) {
825 case fex_int:
826 info->res.val.d = (double)info->res.val.i;
827 break;
828
829 case fex_llong:
830 info->res.val.d = (double)info->res.val.l;
831 break;
832
833 case fex_float:
834 info->res.val.d = (double)info->res.val.f;
835 break;
836
837 case fex_ldouble:
838 info->res.val.d = (double)info->res.val.q;
839 break;
840
841 default:
842 break;
843 }
844
845 *(double *)FPREG(rd) = info->res.val.d;
846 break;
847
848 case 0xc: /* result is long double */
849
850 switch (info->res.type) {
851 case fex_int:
852 info->res.val.q = (long double)info->res.val.i;
853 break;
854
855 case fex_llong:
856 info->res.val.q = (long double)info->res.val.l;
857 break;
858
859 case fex_float:
860 info->res.val.q = (long double)info->res.val.f;
861 break;
862
863 case fex_double:
864 info->res.val.q = (long double)info->res.val.d;
865 break;
866
867 default:
868 break;
869 }
870
871 *(long double *)FPREG(rd) = info->res.val.q;
872 break;
873 }
874
875 return;
876 }
877
878 switch (opf & 3) { /* other arithmetic op */
879 case 1: /* result is float */
880
881 switch (info->res.type) {
882 case fex_int:
883 info->res.val.f = (float)info->res.val.i;
884 break;
885
886 case fex_llong:
887 info->res.val.f = (float)info->res.val.l;
888 break;
889
890 case fex_double:
891 info->res.val.f = (float)info->res.val.d;
892 break;
893
894 case fex_ldouble:
895 info->res.val.f = (float)info->res.val.q;
896 break;
897
898 default:
899 break;
900 }
901
902 *(float *)FPreg(rd) = info->res.val.f;
903 break;
904
905 case 2: /* result is double */
906
907 switch (info->res.type) {
908 case fex_int:
909 info->res.val.d = (double)info->res.val.i;
910 break;
911
912 case fex_llong:
913 info->res.val.d = (double)info->res.val.l;
914 break;
915
916 case fex_float:
917 info->res.val.d = (double)info->res.val.f;
918 break;
919
920 case fex_ldouble:
921 info->res.val.d = (double)info->res.val.q;
922 break;
923
924 default:
925 break;
926 }
927
928 *(double *)FPREG(rd) = info->res.val.d;
929 break;
930
931 case 3: /* result is long double */
932
933 switch (info->res.type) {
934 case fex_int:
935 info->res.val.q = (long double)info->res.val.i;
936 break;
937
938 case fex_llong:
939 info->res.val.q = (long double)info->res.val.l;
940 break;
941
942 case fex_float:
943 info->res.val.q = (long double)info->res.val.f;
944 break;
945
946 case fex_double:
947 info->res.val.q = (long double)info->res.val.d;
948 break;
949
950 default:
951 break;
952 }
953
954 *(long double *)FPREG(rd) = info->res.val.q;
955 break;
956 }
957 }
958 #endif /* defined(__sparc) */
|