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 */
69static enum fp_class_type
70my_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*/
93enum 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
177extern void _Qp_sqrt(long double *, const long double *);
178#else
179extern 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*/
186void
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*/
475void
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
644stuff:
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