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 <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
my_fp_classl(long double * a)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
__fex_get_invalid_type(siginfo_t * sip,ucontext_t * uap)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
__fex_get_op(siginfo_t * sip,ucontext_t * uap,fex_info_t * info)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 */
288 			info->op = fex_sub;
289 			info->res.type = fex_float;
290 			info->res.val.f = info->op1.val.f - info->op2.val.f;
291 			break;
292 
293 		case 0x46: /* subtract double */
294 			info->op = fex_sub;
295 			info->res.type = fex_double;
296 			info->res.val.d = info->op1.val.d - info->op2.val.d;
297 			break;
298 
299 		case 0x47: /* subtract quad */
300 			info->op = fex_sub;
301 			info->res.type = fex_ldouble;
302 			info->res.val.q = info->op1.val.q - info->op2.val.q;
303 			break;
304 
305 		case 0x49: /* multiply 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 
354 		case 0x29: /* square root single */
355 			info->op = fex_sqrt;
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);
360 			break;
361 
362 		case 0x2a: /* square root double */
363 			info->op = fex_sqrt;
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);
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
__fex_st_result(siginfo_t * sip,ucontext_t * uap,fex_info_t * info)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 +
566 				qscl * info->op2.val.q);
567 			break;
568 
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) */
900