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) */