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