Print this page
11210 libm should be cstyle(1ONBLD) clean


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


 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 


 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
 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 +


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


   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 /*
  27  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  28  * Use is subject to license terms.
  29  */
  30 
  31 #if defined(__sparc)
  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 #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 #else

  54 #include <sys/procfs.h>
  55 
  56 #define FPxreg(X)               & \
  57         ((prxregset_t *)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfr. \
  58             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 #endif /* __sparcv9 */
  64 
  65 #include "fex_handler.h"
  66 
  67 /* avoid dependence on libsunmath */
  68 static enum fp_class_type
  69 my_fp_classl(long double *a)
  70 {
  71         int msw = *(int *)a & ~0x80000000;
  72 
  73         if (msw >= 0x7fff0000) {
  74                 if (((msw & 0xffff) | *(1 + (int *)a) | *(2 + (int *)a) | *(3 +
  75                     (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 +
  83                     (int *)a)) == 0)
  84                         return (fp_zero);
  85                 else
  86                         return (fp_subnormal);
  87         } else {
  88                 return (fp_normal);
  89         }
  90 }
  91 
  92 /*
  93  *  Determine which type of invalid operation exception occurred
  94  */
  95 enum fex_exception
  96 __fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
  97 {
  98         unsigned instr, opf, rs1, rs2;
  99         enum fp_class_type t1, t2;
 100 
 101         /* parse the instruction which caused the exception */
 102         instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
 103         opf = (instr >> 5) & 0x1ff;
 104         rs1 = (instr >> 14) & 0x1f;
 105         rs2 = instr & 0x1f;
 106 
 107         /* determine the classes of the operands */
 108         switch (opf & 3) {
 109         case 1:                         /* single */
 110                 t1 = fp_classf(*(float *)FPreg(rs1));
 111                 t2 = fp_classf(*(float *)FPreg(rs2));
 112                 break;
 113 
 114         case 2:                         /* double */
 115                 t1 = fp_class(*(double *)FPREG(rs1));
 116                 t2 = fp_class(*(double *)FPREG(rs2));
 117                 break;
 118 
 119         case 3:                         /* quad */
 120                 t1 = my_fp_classl((long double *)FPREG(rs1));
 121                 t2 = my_fp_classl((long double *)FPREG(rs2));
 122                 break;
 123 
 124         default:        /* integer operands never cause an invalid operation */
 125                 return ((enum fex_exception)-1);
 126         }
 127 
 128         /* if rs2 is snan, return immediately */
 129         if (t2 == fp_signaling)
 130                 return (fex_inv_snan);
 131 
 132         /* determine the type of operation */
 133         switch ((instr >> 19) & 0x183f) {
 134         case 0x1034: /* add, subtract, multiply, divide, square root, convert */
 135 
 136                 switch (opf & 0x1fc) {
 137                 case 0x40:
 138                 case 0x44:              /* add or subtract */
 139 
 140                         if (t1 == fp_signaling)
 141                                 return (fex_inv_snan);
 142                         else
 143                                 return (fex_inv_isi);
 144 
 145                 case 0x48:
 146                 case 0x68:
 147                 case 0x6c:              /* multiply */
 148 
 149                         if (t1 == fp_signaling)
 150                                 return (fex_inv_snan);
 151                         else
 152                                 return (fex_inv_zmi);
 153 
 154                 case 0x4c:              /* divide */
 155 
 156                         if (t1 == fp_signaling)
 157                                 return (fex_inv_snan);
 158                         else if (t1 == fp_zero)
 159                                 return (fex_inv_zdz);
 160                         else
 161                                 return (fex_inv_idi);
 162 
 163                 case 0x28:              /* square root */
 164                         return (fex_inv_sqrt);
 165 
 166                 case 0x80:
 167                 case 0xd0:              /* convert to integer */
 168                         return (fex_inv_int);
 169                 }
 170 
 171                 break;
 172 
 173         case 0x1035:                    /* compare */
 174 
 175                 if (t1 == fp_signaling)
 176                         return (fex_inv_snan);
 177                 else
 178                         return (fex_inv_cmp);
 179         }
 180 
 181         return ((enum fex_exception)-1);
 182 }
 183 
 184 #ifdef __sparcv9
 185 extern void _Qp_sqrt(long double *, const long double *);
 186 #else
 187 extern long double _Q_sqrt(long double);
 188 #endif
 189 
 190 /*
 191  *  Get the operands, generate the default untrapped result with
 192  *  exceptions, and set a code indicating the type of operation
 193  */
 194 void
 195 __fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
 196 {
 197         unsigned long fsr;
 198         unsigned instr, opf, rs1, rs2;
 199         volatile int c;
 200 
 201         /* parse the instruction which caused the exception */
 202         instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
 203         opf = (instr >> 5) & 0x1ff;
 204         rs1 = (instr >> 14) & 0x1f;
 205         rs2 = instr & 0x1f;
 206 
 207         /* get the operands */
 208         switch (opf & 3) {
 209         case 0:                         /* integer */
 210                 info->op1.type = fex_nodata;
 211 
 212                 if (opf & 0x40) {
 213                         info->op2.type = fex_int;
 214                         info->op2.val.i = *(int *)FPreg(rs2);
 215                 } else {

 216                         info->op2.type = fex_llong;
 217                         info->op2.val.l = *(long long *)FPREG(rs2);
 218                 }
 219 
 220                 break;
 221 
 222         case 1:                         /* single */
 223                 info->op1.type = info->op2.type = fex_float;
 224                 info->op1.val.f = *(float *)FPreg(rs1);
 225                 info->op2.val.f = *(float *)FPreg(rs2);
 226                 break;
 227 
 228         case 2:                         /* double */
 229                 info->op1.type = info->op2.type = fex_double;
 230                 info->op1.val.d = *(double *)FPREG(rs1);
 231                 info->op2.val.d = *(double *)FPREG(rs2);
 232                 break;
 233 
 234         case 3:                         /* quad */
 235                 info->op1.type = info->op2.type = fex_ldouble;
 236                 info->op1.val.q = *(long double *)FPREG(rs1);
 237                 info->op2.val.q = *(long double *)FPREG(rs2);
 238                 break;
 239         }
 240 
 241         /*
 242          * initialize res to the default untrapped result and ex to the
 243          * corresponding flags (assume trapping is disabled and flags are
 244          * clear)
 245          */
 246         info->op = fex_other;
 247         info->res.type = fex_nodata;
 248 
 249         switch ((instr >> 19) & 0x183f) {
 250         case 0x1035:                    /* compare */
 251                 info->op = fex_cmp;
 252 
 253                 switch (opf) {
 254                 case 0x51:              /* compare single */
 255                         c = (info->op1.val.f == info->op2.val.f);
 256                         break;
 257 
 258                 case 0x52:              /* compare double */
 259                         c = (info->op1.val.d == info->op2.val.d);
 260                         break;
 261 
 262                 case 0x53:              /* compare quad */
 263                         c = (info->op1.val.q == info->op2.val.q);
 264                         break;
 265 
 266                 case 0x55:              /* compare single with exception */
 267                         c = (info->op1.val.f < info->op2.val.f);
 268                         break;
 269 
 270                 case 0x56:              /* compare double with exception */
 271                         c = (info->op1.val.d < info->op2.val.d);
 272                         break;
 273 
 274                 case 0x57:              /* compare quad with exception */
 275                         c = (info->op1.val.q < info->op2.val.q);
 276                         break;
 277                 }
 278 
 279                 break;
 280 
 281         case 0x1034: /* add, subtract, multiply, divide, square root, convert */
 282 
 283                 switch (opf) {
 284                 case 0x41:              /* add single */
 285                         info->op = fex_add;
 286                         info->res.type = fex_float;
 287                         info->res.val.f = info->op1.val.f + info->op2.val.f;
 288                         break;
 289 
 290                 case 0x42:              /* add double */
 291                         info->op = fex_add;
 292                         info->res.type = fex_double;
 293                         info->res.val.d = info->op1.val.d + info->op2.val.d;
 294                         break;
 295 
 296                 case 0x43:              /* add quad */
 297                         info->op = fex_add;
 298                         info->res.type = fex_ldouble;
 299                         info->res.val.q = info->op1.val.q + info->op2.val.q;
 300                         break;
 301 
 302                 case 0x45:              /* subtract single */


 321                         info->op = fex_mul;
 322                         info->res.type = fex_float;
 323                         info->res.val.f = info->op1.val.f * info->op2.val.f;
 324                         break;
 325 
 326                 case 0x4a:              /* multiply double */
 327                         info->op = fex_mul;
 328                         info->res.type = fex_double;
 329                         info->res.val.d = info->op1.val.d * info->op2.val.d;
 330                         break;
 331 
 332                 case 0x4b:              /* multiply quad */
 333                         info->op = fex_mul;
 334                         info->res.type = fex_ldouble;
 335                         info->res.val.q = info->op1.val.q * info->op2.val.q;
 336                         break;
 337 
 338                 case 0x69:              /* fsmuld */
 339                         info->op = fex_mul;
 340                         info->res.type = fex_double;
 341                         info->res.val.d = (double)info->op1.val.f *
 342                             (double)info->op2.val.f;
 343                         break;
 344 
 345                 case 0x6e:              /* fdmulq */
 346                         info->op = fex_mul;
 347                         info->res.type = fex_ldouble;
 348                         info->res.val.q = (long double)info->op1.val.d * (long
 349                             double)info->op2.val.d;
 350                         break;
 351 
 352                 case 0x4d:              /* divide single */
 353                         info->op = fex_div;
 354                         info->res.type = fex_float;
 355                         info->res.val.f = info->op1.val.f / info->op2.val.f;
 356                         break;
 357 
 358                 case 0x4e:              /* divide double */
 359                         info->op = fex_div;
 360                         info->res.type = fex_double;
 361                         info->res.val.d = info->op1.val.d / info->op2.val.d;
 362                         break;
 363 
 364                 case 0x4f:              /* divide quad */
 365                         info->op = fex_div;
 366                         info->res.type = fex_ldouble;
 367                         info->res.val.q = info->op1.val.q / info->op2.val.q;
 368                         break;
 369 


 382                         info->res.type = fex_double;
 383                         info->res.val.d = sqrt(info->op1.val.d);
 384                         break;
 385 
 386                 case 0x2b:              /* square root quad */
 387                         info->op = fex_sqrt;
 388                         info->op1 = info->op2;
 389                         info->op2.type = fex_nodata;
 390                         info->res.type = fex_ldouble;
 391 #ifdef __sparcv9
 392                         _Qp_sqrt(&info->res.val.q, &info->op1.val.q);
 393 #else
 394                         info->res.val.q = _Q_sqrt(info->op1.val.q);
 395 #endif
 396                         break;
 397 
 398                 default:                /* conversions */
 399                         info->op = fex_cnvt;
 400                         info->op1 = info->op2;
 401                         info->op2.type = fex_nodata;
 402 
 403                         switch (opf) {
 404                         case 0xd1:      /* convert single to int */
 405                                 info->res.type = fex_int;
 406                                 info->res.val.i = (int)info->op1.val.f;
 407                                 break;
 408 
 409                         case 0xd2:      /* convert double to int */
 410                                 info->res.type = fex_int;
 411                                 info->res.val.i = (int)info->op1.val.d;
 412                                 break;
 413 
 414                         case 0xd3:      /* convert quad to int */
 415                                 info->res.type = fex_int;
 416                                 info->res.val.i = (int)info->op1.val.q;
 417                                 break;
 418 
 419                         case 0x81:      /* convert single to long long */
 420                                 info->res.type = fex_llong;
 421                                 info->res.val.l = (long long)info->op1.val.f;
 422                                 break;
 423 
 424                         case 0x82:      /* convert double to long long */
 425                                 info->res.type = fex_llong;
 426                                 info->res.val.l = (long long)info->op1.val.d;
 427                                 break;
 428 
 429                         case 0x83:      /* convert quad to long long */
 430                                 info->res.type = fex_llong;
 431                                 info->res.val.l = (long long)info->op1.val.q;
 432                                 break;
 433 
 434                         case 0xc4:      /* convert int to single */
 435                                 info->res.type = fex_float;
 436                                 info->res.val.f = (float)info->op1.val.i;
 437                                 break;
 438 
 439                         case 0x84:      /* convert long long to single */
 440                                 info->res.type = fex_float;
 441                                 info->res.val.f = (float)info->op1.val.l;
 442                                 break;
 443 
 444                         case 0x88:      /* convert long long to double */
 445                                 info->res.type = fex_double;
 446                                 info->res.val.d = (double)info->op1.val.l;
 447                                 break;
 448 
 449                         case 0xc6:      /* convert double to single */
 450                                 info->res.type = fex_float;
 451                                 info->res.val.f = (float)info->op1.val.d;
 452                                 break;
 453 
 454                         case 0xc7:      /* convert quad to single */
 455                                 info->res.type = fex_float;
 456                                 info->res.val.f = (float)info->op1.val.q;
 457                                 break;
 458 
 459                         case 0xc9:      /* convert single to double */
 460                                 info->res.type = fex_double;
 461                                 info->res.val.d = (double)info->op1.val.f;
 462                                 break;
 463 
 464                         case 0xcb:      /* convert quad to double */
 465                                 info->res.type = fex_double;
 466                                 info->res.val.d = (double)info->op1.val.q;
 467                                 break;
 468 
 469                         case 0xcd:      /* convert single to quad */
 470                                 info->res.type = fex_ldouble;
 471                                 info->res.val.q = (long double)info->op1.val.f;
 472                                 break;
 473 
 474                         case 0xce:      /* convert double to quad */
 475                                 info->res.type = fex_ldouble;
 476                                 info->res.val.q = (long double)info->op1.val.d;
 477                                 break;
 478                         }
 479                 }
 480 
 481                 break;
 482         }
 483 
 484         __fenv_getfsr(&fsr);
 485         info->flags = (int)__fenv_get_ex(fsr);
 486         __fenv_set_ex(fsr, 0);
 487         __fenv_setfsr(&fsr);
 488 }
 489 
 490 /*
 491  *  Store the specified result; if no result is given but the exception
 492  *  is underflow or overflow, supply the default trapped result
 493  */
 494 void
 495 __fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
 496 {
 497         unsigned instr, opf, rs1, rs2, rd;
 498         long double qscl;
 499         double dscl;
 500         float fscl;
 501 
 502         /* parse the instruction which caused the exception */
 503         instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
 504         opf = (instr >> 5) & 0x1ff;
 505         rs1 = (instr >> 14) & 0x1f;
 506         rs2 = instr & 0x1f;
 507         rd = (instr >> 25) & 0x1f;
 508 
 509         /* if the instruction is a compare, just set fcc to unordered */
 510         if (((instr >> 19) & 0x183f) == 0x1035) {
 511                 if (rd == 0) {
 512                         uap->uc_mcontext.fpregs.fpu_fsr |= 0xc00;
 513                 } else {
 514 #ifdef __sparcv9
 515                         uap->uc_mcontext.fpregs.fpu_fsr |= (3l << ((rd << 1) +
 516                             30));
 517 #else
 518                         ((prxregset_t *)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.
 519                             pr_v8p.pr_xfsr |= (3 << ((rd - 1) << 1));
 520 #endif
 521                 }
 522 
 523                 return;
 524         }
 525 
 526         /*
 527          * if there is no result available, try to generate the untrapped
 528          * default
 529          */
 530         if (info->res.type == fex_nodata) {
 531                 /* set scale factors for exponent wrapping */
 532                 switch (sip->si_code) {
 533                 case FPE_FLTOVF:
 534                         /* 2^-96 */
 535                         fscl = 1.262177448e-29f;
 536                         /* 2^-768 */
 537                         dscl = 6.441148769597133308e-232;
 538                         /* 2^-12288 */
 539                         qscl = 8.778357852076208839765066529179033145e-3700l;
 540                         break;
 541 
 542                 case FPE_FLTUND:
 543                         /* 2^96 */
 544                         fscl = 7.922816251e+28f;
 545                         /* 2^768 */
 546                         dscl = 1.552518092300708935e+231;
 547                         /* 2^12288 */
 548                         qscl = 1.139165225263043370845938579315932009e+3699l;
 549                         break;
 550 
 551                 default:
 552 
 553                         /*
 554                          * user may have blown away the default result by
 555                          * mistake, so try to regenerate it
 556                          */
 557                         (void) __fex_get_op(sip, uap, info);
 558 
 559                         if (info->res.type != fex_nodata)
 560                                 goto stuff;
 561 
 562                         /* couldn't do it */
 563                         return;
 564                 }
 565 
 566                 /* get the operands */
 567                 switch (opf & 3) {
 568                 case 1:                 /* single */
 569                         info->op1.val.f = *(float *)FPreg(rs1);
 570                         info->op2.val.f = *(float *)FPreg(rs2);
 571                         break;
 572 
 573                 case 2:                 /* double */
 574                         info->op1.val.d = *(double *)FPREG(rs1);
 575                         info->op2.val.d = *(double *)FPREG(rs2);
 576                         break;
 577 
 578                 case 3:                 /* quad */
 579                         info->op1.val.q = *(long double *)FPREG(rs1);
 580                         info->op2.val.q = *(long double *)FPREG(rs2);
 581                         break;
 582                 }
 583 
 584                 /* generate the wrapped result */
 585                 switch (opf) {
 586                 case 0x41:              /* add single */
 587                         info->res.type = fex_float;
 588                         info->res.val.f = fscl * (fscl * info->op1.val.f +
 589                             fscl * info->op2.val.f);
 590                         break;
 591 
 592                 case 0x42:              /* add double */
 593                         info->res.type = fex_double;
 594                         info->res.val.d = dscl * (dscl * info->op1.val.d +
 595                             dscl * info->op2.val.d);
 596                         break;
 597 
 598                 case 0x43:              /* add quad */
 599                         info->res.type = fex_ldouble;
 600                         info->res.val.q = qscl * (qscl * info->op1.val.q +


 604                 case 0x45:              /* subtract single */
 605                         info->res.type = fex_float;
 606                         info->res.val.f = fscl * (fscl * info->op1.val.f -
 607                             fscl * info->op2.val.f);
 608                         break;
 609 
 610                 case 0x46:              /* subtract double */
 611                         info->res.type = fex_double;
 612                         info->res.val.d = dscl * (dscl * info->op1.val.d -
 613                             dscl * info->op2.val.d);
 614                         break;
 615 
 616                 case 0x47:              /* subtract quad */
 617                         info->res.type = fex_ldouble;
 618                         info->res.val.q = qscl * (qscl * info->op1.val.q -
 619                             qscl * info->op2.val.q);
 620                         break;
 621 
 622                 case 0x49:              /* multiply single */
 623                         info->res.type = fex_float;
 624                         info->res.val.f = (fscl * info->op1.val.f) * (fscl *
 625                             info->op2.val.f);
 626                         break;
 627 
 628                 case 0x4a:              /* multiply double */
 629                         info->res.type = fex_double;
 630                         info->res.val.d = (dscl * info->op1.val.d) * (dscl *
 631                             info->op2.val.d);
 632                         break;
 633 
 634                 case 0x4b:              /* multiply quad */
 635                         info->res.type = fex_ldouble;
 636                         info->res.val.q = (qscl * info->op1.val.q) * (qscl *
 637                             info->op2.val.q);
 638                         break;
 639 
 640                 case 0x4d:              /* divide single */
 641                         info->res.type = fex_float;
 642                         info->res.val.f = (fscl * info->op1.val.f) /
 643                             (info->op2.val.f / fscl);
 644                         break;
 645 
 646                 case 0x4e:              /* divide double */
 647                         info->res.type = fex_double;
 648                         info->res.val.d = (dscl * info->op1.val.d) /
 649                             (info->op2.val.d / dscl);
 650                         break;
 651 
 652                 case 0x4f:              /* divide quad */
 653                         info->res.type = fex_ldouble;
 654                         info->res.val.q = (qscl * info->op1.val.q) /
 655                             (info->op2.val.q / qscl);
 656                         break;
 657 
 658                 case 0xc6:              /* convert double to single */
 659                         info->res.type = fex_float;
 660                         info->res.val.f = (float)(fscl * (fscl *
 661                             info->op1.val.d));
 662                         break;
 663 
 664                 case 0xc7:              /* convert quad to single */
 665                         info->res.type = fex_float;
 666                         info->res.val.f = (float)(fscl * (fscl *
 667                             info->op1.val.q));
 668                         break;
 669 
 670                 case 0xcb:              /* convert quad to double */
 671                         info->res.type = fex_double;
 672                         info->res.val.d = (double)(dscl * (dscl *
 673                             info->op1.val.q));
 674                         break;
 675                 }
 676 
 677                 if (info->res.type == fex_nodata)
 678                         /* couldn't do it */
 679                         return;
 680         }
 681 
 682 stuff:
 683         /* stick the result in the destination */
 684         if (opf & 0x80) {           /* conversion */
 685                 if (opf & 0x10) {   /* result is an int */
 686                         switch (info->res.type) {
 687                         case fex_llong:
 688                                 info->res.val.i = (int)info->res.val.l;
 689                                 break;
 690 
 691                         case fex_float:
 692                                 info->res.val.i = (int)info->res.val.f;
 693                                 break;
 694 
 695                         case fex_double:
 696                                 info->res.val.i = (int)info->res.val.d;
 697                                 break;
 698 
 699                         case fex_ldouble:
 700                                 info->res.val.i = (int)info->res.val.q;
 701                                 break;
 702 
 703                         default:
 704                                 break;
 705                         }
 706 
 707                         *(int *)FPreg(rd) = info->res.val.i;
 708                         return;
 709                 }
 710 
 711                 switch (opf & 0xc) {
 712                 case 0:                 /* result is long long */
 713 
 714                         switch (info->res.type) {
 715                         case fex_int:
 716                                 info->res.val.l = (long long)info->res.val.i;
 717                                 break;
 718 
 719                         case fex_float:
 720                                 info->res.val.l = (long long)info->res.val.f;
 721                                 break;
 722 
 723                         case fex_double:
 724                                 info->res.val.l = (long long)info->res.val.d;
 725                                 break;
 726 
 727                         case fex_ldouble:
 728                                 info->res.val.l = (long long)info->res.val.q;
 729                                 break;
 730 
 731                         default:
 732                                 break;
 733                         }
 734 
 735                         *(long long *)FPREG(rd) = info->res.val.l;
 736                         break;
 737 
 738                 case 0x4:               /* result is float */
 739 
 740                         switch (info->res.type) {
 741                         case fex_int:
 742                                 info->res.val.f = (float)info->res.val.i;
 743                                 break;
 744 
 745                         case fex_llong:
 746                                 info->res.val.f = (float)info->res.val.l;
 747                                 break;
 748 
 749                         case fex_double:
 750                                 info->res.val.f = (float)info->res.val.d;
 751                                 break;
 752 
 753                         case fex_ldouble:
 754                                 info->res.val.f = (float)info->res.val.q;
 755                                 break;
 756 
 757                         default:
 758                                 break;
 759                         }
 760 
 761                         *(float *)FPreg(rd) = info->res.val.f;
 762                         break;
 763 
 764                 case 0x8:               /* result is double */
 765 
 766                         switch (info->res.type) {
 767                         case fex_int:
 768                                 info->res.val.d = (double)info->res.val.i;
 769                                 break;
 770 
 771                         case fex_llong:
 772                                 info->res.val.d = (double)info->res.val.l;
 773                                 break;
 774 
 775                         case fex_float:
 776                                 info->res.val.d = (double)info->res.val.f;
 777                                 break;
 778 
 779                         case fex_ldouble:
 780                                 info->res.val.d = (double)info->res.val.q;
 781                                 break;
 782 
 783                         default:
 784                                 break;
 785                         }
 786 
 787                         *(double *)FPREG(rd) = info->res.val.d;
 788                         break;
 789 
 790                 case 0xc:               /* result is long double */
 791 
 792                         switch (info->res.type) {
 793                         case fex_int:
 794                                 info->res.val.q = (long double)info->res.val.i;
 795                                 break;
 796 
 797                         case fex_llong:
 798                                 info->res.val.q = (long double)info->res.val.l;
 799                                 break;
 800 
 801                         case fex_float:
 802                                 info->res.val.q = (long double)info->res.val.f;
 803                                 break;
 804 
 805                         case fex_double:
 806                                 info->res.val.q = (long double)info->res.val.d;
 807                                 break;
 808 
 809                         default:
 810                                 break;
 811                         }
 812 
 813                         *(long double *)FPREG(rd) = info->res.val.q;
 814                         break;
 815                 }
 816 
 817                 return;
 818         }
 819 
 820         if ((opf & 0xf0) == 0x60) { /* fsmuld, fdmulq */
 821                 switch (opf & 0xc0) {
 822                 case 0x8:               /* result is double */
 823 
 824                         switch (info->res.type) {
 825                         case fex_int:
 826                                 info->res.val.d = (double)info->res.val.i;
 827                                 break;
 828 
 829                         case fex_llong:
 830                                 info->res.val.d = (double)info->res.val.l;
 831                                 break;
 832 
 833                         case fex_float:
 834                                 info->res.val.d = (double)info->res.val.f;
 835                                 break;
 836 
 837                         case fex_ldouble:
 838                                 info->res.val.d = (double)info->res.val.q;
 839                                 break;
 840 
 841                         default:
 842                                 break;
 843                         }
 844 
 845                         *(double *)FPREG(rd) = info->res.val.d;
 846                         break;
 847 
 848                 case 0xc:               /* result is long double */
 849 
 850                         switch (info->res.type) {
 851                         case fex_int:
 852                                 info->res.val.q = (long double)info->res.val.i;
 853                                 break;
 854 
 855                         case fex_llong:
 856                                 info->res.val.q = (long double)info->res.val.l;
 857                                 break;
 858 
 859                         case fex_float:
 860                                 info->res.val.q = (long double)info->res.val.f;
 861                                 break;
 862 
 863                         case fex_double:
 864                                 info->res.val.q = (long double)info->res.val.d;
 865                                 break;
 866 
 867                         default:
 868                                 break;
 869                         }
 870 
 871                         *(long double *)FPREG(rd) = info->res.val.q;
 872                         break;
 873                 }
 874 
 875                 return;
 876         }
 877 
 878         switch (opf & 3) {          /* other arithmetic op */
 879         case 1:                         /* result is float */
 880 
 881                 switch (info->res.type) {
 882                 case fex_int:
 883                         info->res.val.f = (float)info->res.val.i;
 884                         break;
 885 
 886                 case fex_llong:
 887                         info->res.val.f = (float)info->res.val.l;
 888                         break;
 889 
 890                 case fex_double:
 891                         info->res.val.f = (float)info->res.val.d;
 892                         break;
 893 
 894                 case fex_ldouble:
 895                         info->res.val.f = (float)info->res.val.q;
 896                         break;
 897 
 898                 default:
 899                         break;
 900                 }
 901 
 902                 *(float *)FPreg(rd) = info->res.val.f;
 903                 break;
 904 
 905         case 2:                         /* result is double */
 906 
 907                 switch (info->res.type) {
 908                 case fex_int:
 909                         info->res.val.d = (double)info->res.val.i;
 910                         break;
 911 
 912                 case fex_llong:
 913                         info->res.val.d = (double)info->res.val.l;
 914                         break;
 915 
 916                 case fex_float:
 917                         info->res.val.d = (double)info->res.val.f;
 918                         break;
 919 
 920                 case fex_ldouble:
 921                         info->res.val.d = (double)info->res.val.q;
 922                         break;
 923 
 924                 default:
 925                         break;
 926                 }
 927 
 928                 *(double *)FPREG(rd) = info->res.val.d;
 929                 break;
 930 
 931         case 3:                         /* result is long double */
 932 
 933                 switch (info->res.type) {
 934                 case fex_int:
 935                         info->res.val.q = (long double)info->res.val.i;
 936                         break;
 937 
 938                 case fex_llong:
 939                         info->res.val.q = (long double)info->res.val.l;
 940                         break;
 941 
 942                 case fex_float:
 943                         info->res.val.q = (long double)info->res.val.f;
 944                         break;
 945 
 946                 case fex_double:
 947                         info->res.val.q = (long double)info->res.val.d;
 948                         break;
 949 
 950                 default:
 951                         break;
 952                 }
 953 
 954                 *(long double *)FPREG(rd) = info->res.val.q;
 955                 break;
 956         }
 957 }
 958 #endif /* defined(__sparc) */