4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
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.
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]
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
39 #if defined(__SUNPRO_C)
44 #include "fenv_inlines.h"
45 #include "libm_inlines.h"
49 #define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
51 #define FPREG(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_dregs[(X>>1)| \
56 #include <sys/procfs.h>
58 #define FPxreg(X) &((prxregset_t*)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfr.pr_regs[X]
60 #define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
62 #define FPREG(X) ((X & 1)? FPxreg(X - 1) : FPreg(X))
64 #endif /* __sparcv9 */
66 #include "fex_handler.h"
68 /* avoid dependence on libsunmath */
69 static enum fp_class_type
70 my_fp_classl(long double *a
)
72 int msw
= *(int*)a
& ~0x80000000;
74 if (msw
>= 0x7fff0000) {
75 if (((msw
& 0xffff) | *(1+(int*)a
) | *(2+(int*)a
) | *(3+(int*)a
)) == 0)
77 else if (msw
& 0x8000)
81 } else if (msw
< 0x10000) {
82 if ((msw
| *(1+(int*)a
) | *(2+(int*)a
) | *(3+(int*)a
)) == 0)
91 * Determine which type of invalid operation exception occurred
94 __fex_get_invalid_type(siginfo_t
*sip
, ucontext_t
*uap
)
96 unsigned instr
, opf
, rs1
, rs2
;
97 enum fp_class_type t1
, t2
;
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;
105 /* determine the classes of the operands */
108 t1
= fp_classf(*(float*)FPreg(rs1
));
109 t2
= fp_classf(*(float*)FPreg(rs2
));
113 t1
= fp_class(*(double*)FPREG(rs1
));
114 t2
= fp_class(*(double*)FPREG(rs2
));
118 t1
= my_fp_classl((long double*)FPREG(rs1
));
119 t2
= my_fp_classl((long double*)FPREG(rs2
));
122 default: /* integer operands never cause an invalid operation */
123 return (enum fex_exception
) -1;
126 /* if rs2 is snan, return immediately */
127 if (t2
== fp_signaling
)
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) {
135 case 0x44: /* add or subtract */
136 if (t1
== fp_signaling
)
143 case 0x6c: /* multiply */
144 if (t1
== fp_signaling
)
149 case 0x4c: /* divide */
150 if (t1
== fp_signaling
)
152 else if (t1
== fp_zero
)
157 case 0x28: /* square root */
161 case 0xd0: /* convert to integer */
166 case 0x1035: /* compare */
167 if (t1
== fp_signaling
)
173 return (enum fex_exception
) -1;
177 extern void _Qp_sqrt(long double *, const long double *);
179 extern long double _Q_sqrt(long double);
183 * Get the operands, generate the default untrapped result with
184 * exceptions, and set a code indicating the type of operation
187 __fex_get_op(siginfo_t
*sip
, ucontext_t
*uap
, fex_info_t
*info
)
190 unsigned instr
, opf
, rs1
, rs2
;
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;
199 /* get the operands */
201 case 0: /* integer */
202 info
->op1
.type
= fex_nodata
;
204 info
->op2
.type
= fex_int
;
205 info
->op2
.val
.i
= *(int*)FPreg(rs2
);
208 info
->op2
.type
= fex_llong
;
209 info
->op2
.val
.l
= *(long long*)FPREG(rs2
);
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
);
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
);
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
);
232 /* initialize res to the default untrapped result and ex to the
233 corresponding flags (assume trapping is disabled and flags
235 info
->op
= fex_other
;
236 info
->res
.type
= fex_nodata
;
237 switch ((instr
>> 19) & 0x183f) {
238 case 0x1035: /* compare */
241 case 0x51: /* compare single */
242 c
= (info
->op1
.val
.f
== info
->op2
.val
.f
);
245 case 0x52: /* compare double */
246 c
= (info
->op1
.val
.d
== info
->op2
.val
.d
);
249 case 0x53: /* compare quad */
250 c
= (info
->op1
.val
.q
== info
->op2
.val
.q
);
253 case 0x55: /* compare single with exception */
254 c
= (info
->op1
.val
.f
< info
->op2
.val
.f
);
257 case 0x56: /* compare double with exception */
258 c
= (info
->op1
.val
.d
< info
->op2
.val
.d
);
261 case 0x57: /* compare quad with exception */
262 c
= (info
->op1
.val
.q
< info
->op2
.val
.q
);
267 case 0x1034: /* add, subtract, multiply, divide, square root, convert */
269 case 0x41: /* add single */
271 info
->res
.type
= fex_float
;
272 info
->res
.val
.f
= info
->op1
.val
.f
+ info
->op2
.val
.f
;
275 case 0x42: /* add double */
277 info
->res
.type
= fex_double
;
278 info
->res
.val
.d
= info
->op1
.val
.d
+ info
->op2
.val
.d
;
281 case 0x43: /* add quad */
283 info
->res
.type
= fex_ldouble
;
284 info
->res
.val
.q
= info
->op1
.val
.q
+ info
->op2
.val
.q
;
287 case 0x45: /* subtract single */
289 info
->res
.type
= fex_float
;
290 info
->res
.val
.f
= info
->op1
.val
.f
- info
->op2
.val
.f
;
293 case 0x46: /* subtract double */
295 info
->res
.type
= fex_double
;
296 info
->res
.val
.d
= info
->op1
.val
.d
- info
->op2
.val
.d
;
299 case 0x47: /* subtract quad */
301 info
->res
.type
= fex_ldouble
;
302 info
->res
.val
.q
= info
->op1
.val
.q
- info
->op2
.val
.q
;
305 case 0x49: /* multiply single */
307 info
->res
.type
= fex_float
;
308 info
->res
.val
.f
= info
->op1
.val
.f
* info
->op2
.val
.f
;
311 case 0x4a: /* multiply double */
313 info
->res
.type
= fex_double
;
314 info
->res
.val
.d
= info
->op1
.val
.d
* info
->op2
.val
.d
;
317 case 0x4b: /* multiply quad */
319 info
->res
.type
= fex_ldouble
;
320 info
->res
.val
.q
= info
->op1
.val
.q
* info
->op2
.val
.q
;
323 case 0x69: /* fsmuld */
325 info
->res
.type
= fex_double
;
326 info
->res
.val
.d
= (double)info
->op1
.val
.f
* (double)info
->op2
.val
.f
;
329 case 0x6e: /* fdmulq */
331 info
->res
.type
= fex_ldouble
;
332 info
->res
.val
.q
= (long double)info
->op1
.val
.d
*
333 (long double)info
->op2
.val
.d
;
336 case 0x4d: /* divide single */
338 info
->res
.type
= fex_float
;
339 info
->res
.val
.f
= info
->op1
.val
.f
/ info
->op2
.val
.f
;
342 case 0x4e: /* divide double */
344 info
->res
.type
= fex_double
;
345 info
->res
.val
.d
= info
->op1
.val
.d
/ info
->op2
.val
.d
;
348 case 0x4f: /* divide quad */
350 info
->res
.type
= fex_ldouble
;
351 info
->res
.val
.q
= info
->op1
.val
.q
/ info
->op2
.val
.q
;
354 case 0x29: /* square root single */
356 info
->op1
= info
->op2
;
357 info
->op2
.type
= fex_nodata
;
358 info
->res
.type
= fex_float
;
359 info
->res
.val
.f
= sqrtf(info
->op1
.val
.f
);
362 case 0x2a: /* square root double */
364 info
->op1
= info
->op2
;
365 info
->op2
.type
= fex_nodata
;
366 info
->res
.type
= fex_double
;
367 info
->res
.val
.d
= sqrt(info
->op1
.val
.d
);
370 case 0x2b: /* square root quad */
372 info
->op1
= info
->op2
;
373 info
->op2
.type
= fex_nodata
;
374 info
->res
.type
= fex_ldouble
;
376 _Qp_sqrt(&info
->res
.val
.q
, &info
->op1
.val
.q
);
378 info
->res
.val
.q
= _Q_sqrt(info
->op1
.val
.q
);
382 default: /* conversions */
384 info
->op1
= info
->op2
;
385 info
->op2
.type
= fex_nodata
;
387 case 0xd1: /* convert single to int */
388 info
->res
.type
= fex_int
;
389 info
->res
.val
.i
= (int) info
->op1
.val
.f
;
392 case 0xd2: /* convert double to int */
393 info
->res
.type
= fex_int
;
394 info
->res
.val
.i
= (int) info
->op1
.val
.d
;
397 case 0xd3: /* convert quad to int */
398 info
->res
.type
= fex_int
;
399 info
->res
.val
.i
= (int) info
->op1
.val
.q
;
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
;
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
;
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
;
417 case 0xc4: /* convert int to single */
418 info
->res
.type
= fex_float
;
419 info
->res
.val
.f
= (float) info
->op1
.val
.i
;
422 case 0x84: /* convert long long to single */
423 info
->res
.type
= fex_float
;
424 info
->res
.val
.f
= (float) info
->op1
.val
.l
;
427 case 0x88: /* convert long long to double */
428 info
->res
.type
= fex_double
;
429 info
->res
.val
.d
= (double) info
->op1
.val
.l
;
432 case 0xc6: /* convert double to single */
433 info
->res
.type
= fex_float
;
434 info
->res
.val
.f
= (float) info
->op1
.val
.d
;
437 case 0xc7: /* convert quad to single */
438 info
->res
.type
= fex_float
;
439 info
->res
.val
.f
= (float) info
->op1
.val
.q
;
442 case 0xc9: /* convert single to double */
443 info
->res
.type
= fex_double
;
444 info
->res
.val
.d
= (double) info
->op1
.val
.f
;
447 case 0xcb: /* convert quad to double */
448 info
->res
.type
= fex_double
;
449 info
->res
.val
.d
= (double) info
->op1
.val
.q
;
452 case 0xcd: /* convert single to quad */
453 info
->res
.type
= fex_ldouble
;
454 info
->res
.val
.q
= (long double) info
->op1
.val
.f
;
457 case 0xce: /* convert double to quad */
458 info
->res
.type
= fex_ldouble
;
459 info
->res
.val
.q
= (long double) info
->op1
.val
.d
;
466 info
->flags
= (int)__fenv_get_ex(fsr
);
467 __fenv_set_ex(fsr
, 0);
472 * Store the specified result; if no result is given but the exception
473 * is underflow or overflow, supply the default trapped result
476 __fex_st_result(siginfo_t
*sip
, ucontext_t
*uap
, fex_info_t
*info
)
478 unsigned instr
, opf
, rs1
, rs2
, rd
;
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;
488 rd
= (instr
>> 25) & 0x1f;
490 /* if the instruction is a compare, just set fcc to unordered */
491 if (((instr
>> 19) & 0x183f) == 0x1035) {
493 uap
->uc_mcontext
.fpregs
.fpu_fsr
|= 0xc00;
496 uap
->uc_mcontext
.fpregs
.fpu_fsr
|= (3l << ((rd
<< 1) + 30));
498 ((prxregset_t
*)uap
->uc_mcontext
.xrs
.xrs_ptr
)->pr_un
.pr_v8p
.pr_xfsr
|= (3 << ((rd
- 1) << 1));
504 /* if there is no result available, try to generate the untrapped
506 if (info
->res
.type
== fex_nodata
) {
507 /* set scale factors for exponent wrapping */
508 switch (sip
->si_code
) {
510 fscl
= 1.262177448e-29f
; /* 2^-96 */
511 dscl
= 6.441148769597133308e-232; /* 2^-768 */
512 qscl
= 8.778357852076208839765066529179033145e-3700l;/* 2^-12288 */
516 fscl
= 7.922816251e+28f
; /* 2^96 */
517 dscl
= 1.552518092300708935e+231; /* 2^768 */
518 qscl
= 1.139165225263043370845938579315932009e+3699l;/* 2^12288 */
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
)
531 /* get the operands */
534 info
->op1
.val
.f
= *(float*)FPreg(rs1
);
535 info
->op2
.val
.f
= *(float*)FPreg(rs2
);
539 info
->op1
.val
.d
= *(double*)FPREG(rs1
);
540 info
->op2
.val
.d
= *(double*)FPREG(rs2
);
544 info
->op1
.val
.q
= *(long double*)FPREG(rs1
);
545 info
->op2
.val
.q
= *(long double*)FPREG(rs2
);
549 /* generate the wrapped result */
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
);
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
);
563 case 0x43: /* add quad */
564 info
->res
.type
= fex_ldouble
;
565 info
->res
.val
.q
= qscl
* (qscl
* info
->op1
.val
.q
+
566 qscl
* info
->op2
.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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
));
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
));
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
));
639 if (info
->res
.type
== fex_nodata
)
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
) {
650 info
->res
.val
.i
= (int) info
->res
.val
.l
;
654 info
->res
.val
.i
= (int) info
->res
.val
.f
;
658 info
->res
.val
.i
= (int) info
->res
.val
.d
;
662 info
->res
.val
.i
= (int) info
->res
.val
.q
;
668 *(int*)FPreg(rd
) = info
->res
.val
.i
;
673 case 0: /* result is long long */
674 switch (info
->res
.type
) {
676 info
->res
.val
.l
= (long long) info
->res
.val
.i
;
680 info
->res
.val
.l
= (long long) info
->res
.val
.f
;
684 info
->res
.val
.l
= (long long) info
->res
.val
.d
;
688 info
->res
.val
.l
= (long long) info
->res
.val
.q
;
694 *(long long*)FPREG(rd
) = info
->res
.val
.l
;
697 case 0x4: /* result is float */
698 switch (info
->res
.type
) {
700 info
->res
.val
.f
= (float) info
->res
.val
.i
;
704 info
->res
.val
.f
= (float) info
->res
.val
.l
;
708 info
->res
.val
.f
= (float) info
->res
.val
.d
;
712 info
->res
.val
.f
= (float) info
->res
.val
.q
;
718 *(float*)FPreg(rd
) = info
->res
.val
.f
;
721 case 0x8: /* result is double */
722 switch (info
->res
.type
) {
724 info
->res
.val
.d
= (double) info
->res
.val
.i
;
728 info
->res
.val
.d
= (double) info
->res
.val
.l
;
732 info
->res
.val
.d
= (double) info
->res
.val
.f
;
736 info
->res
.val
.d
= (double) info
->res
.val
.q
;
742 *(double*)FPREG(rd
) = info
->res
.val
.d
;
745 case 0xc: /* result is long double */
746 switch (info
->res
.type
) {
748 info
->res
.val
.q
= (long double) info
->res
.val
.i
;
752 info
->res
.val
.q
= (long double) info
->res
.val
.l
;
756 info
->res
.val
.q
= (long double) info
->res
.val
.f
;
760 info
->res
.val
.q
= (long double) info
->res
.val
.d
;
766 *(long double*)FPREG(rd
) = info
->res
.val
.q
;
772 if ((opf
& 0xf0) == 0x60) { /* fsmuld, fdmulq */
773 switch (opf
& 0xc0) {
774 case 0x8: /* result is double */
775 switch (info
->res
.type
) {
777 info
->res
.val
.d
= (double) info
->res
.val
.i
;
781 info
->res
.val
.d
= (double) info
->res
.val
.l
;
785 info
->res
.val
.d
= (double) info
->res
.val
.f
;
789 info
->res
.val
.d
= (double) info
->res
.val
.q
;
795 *(double*)FPREG(rd
) = info
->res
.val
.d
;
798 case 0xc: /* result is long double */
799 switch (info
->res
.type
) {
801 info
->res
.val
.q
= (long double) info
->res
.val
.i
;
805 info
->res
.val
.q
= (long double) info
->res
.val
.l
;
809 info
->res
.val
.q
= (long double) info
->res
.val
.f
;
813 info
->res
.val
.q
= (long double) info
->res
.val
.d
;
819 *(long double*)FPREG(rd
) = info
->res
.val
.q
;
825 switch (opf
& 3) { /* other arithmetic op */
826 case 1: /* result is float */
827 switch (info
->res
.type
) {
829 info
->res
.val
.f
= (float) info
->res
.val
.i
;
833 info
->res
.val
.f
= (float) info
->res
.val
.l
;
837 info
->res
.val
.f
= (float) info
->res
.val
.d
;
841 info
->res
.val
.f
= (float) info
->res
.val
.q
;
847 *(float*)FPreg(rd
) = info
->res
.val
.f
;
850 case 2: /* result is double */
851 switch (info
->res
.type
) {
853 info
->res
.val
.d
= (double) info
->res
.val
.i
;
857 info
->res
.val
.d
= (double) info
->res
.val
.l
;
861 info
->res
.val
.d
= (double) info
->res
.val
.f
;
865 info
->res
.val
.d
= (double) info
->res
.val
.q
;
871 *(double*)FPREG(rd
) = info
->res
.val
.d
;
874 case 3: /* result is long double */
875 switch (info
->res
.type
) {
877 info
->res
.val
.q
= (long double) info
->res
.val
.i
;
881 info
->res
.val
.q
= (long double) info
->res
.val
.l
;
885 info
->res
.val
.q
= (long double) info
->res
.val
.f
;
889 info
->res
.val
.q
= (long double) info
->res
.val
.d
;
895 *(long double*)FPREG(rd
) = info
->res
.val
.q
;
899 #endif /* defined(__sparc) */