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 #include <stdio.h>
  31 #include <unistd.h>
  32 #include <stdlib.h>
  33 #include <string.h>
  34 #include <signal.h>
  35 #include <siginfo.h>
  36 #include <ucontext.h>
  37 #include <thread.h>
  38 #include <math.h>
  39 #if defined(__SUNPRO_C)
  40 #include <sunmath.h>
  41 #endif
  42 #include <fenv.h>
  43 #include "fex_handler.h"
  44 #include "fenv_inlines.h"
  45 
  46 #if defined(__amd64)
  47 #define test_sse_hw     1
  48 #else
  49 /*
  50  * The following variable lives in libc on Solaris 10, where it
  51  * gets set to a nonzero value at startup time on systems with SSE.
  52  */
  53 extern int _sse_hw;
  54 #define test_sse_hw     _sse_hw
  55 #endif
  56 
  57 static int accrued = 0;
  58 static thread_key_t accrued_key;
  59 static mutex_t accrued_key_lock = DEFAULTMUTEX;
  60 
  61 int *
  62 __fex_accrued()
  63 {
  64         int             *p;
  65 
  66         if (thr_main())
  67                 return &accrued;
  68         else {
  69                 p = NULL;
  70                 mutex_lock(&accrued_key_lock);
  71                 if (thr_getspecific(accrued_key, (void **)&p) != 0 &&
  72                         thr_keycreate(&accrued_key, free) != 0) {
  73                         mutex_unlock(&accrued_key_lock);
  74                         return NULL;
  75                 }
  76                 mutex_unlock(&accrued_key_lock);
  77                 if (!p) {
  78                         if ((p = (int*) malloc(sizeof(int))) == NULL)
  79                                 return NULL;
  80                         if (thr_setspecific(accrued_key, (void *)p) != 0) {
  81                                 (void)free(p);
  82                                 return NULL;
  83                         }
  84                         *p = 0;
  85                 }
  86                 return p;
  87         }
  88 }
  89 
  90 void
  91 __fenv_getfsr(unsigned long *fsr)
  92 {
  93         unsigned int    cwsw, mxcsr;
  94 
  95         __fenv_getcwsw(&cwsw);
  96         /* clear reserved bits for no particularly good reason */
  97         cwsw &= ~0xe0c00000u;
  98         if (test_sse_hw) {
  99                 /* pick up exception flags (excluding denormal operand
 100                    flag) from mxcsr */
 101                 __fenv_getmxcsr(&mxcsr);
 102                 cwsw |= (mxcsr & 0x3d);
 103         }
 104         cwsw |= *__fex_accrued();
 105         *fsr = cwsw ^ 0x003f0000u;
 106 }
 107 
 108 void
 109 __fenv_setfsr(const unsigned long *fsr)
 110 {
 111         unsigned int    cwsw, mxcsr;
 112         int                             te;
 113 
 114         /* save accrued exception flags corresponding to enabled exceptions */
 115         cwsw = (unsigned int)*fsr;
 116         te = __fenv_get_te(cwsw);
 117         *__fex_accrued() = cwsw & te;
 118         cwsw = (cwsw & ~te) ^ 0x003f0000;
 119         if (test_sse_hw) {
 120                 /* propagate rounding direction, masks, and exception flags
 121                    (excluding denormal operand mask and flag) to mxcsr */
 122                 __fenv_getmxcsr(&mxcsr);
 123                 mxcsr = (mxcsr & ~0x7ebd) | ((cwsw >> 13) & 0x6000) |
 124                         ((cwsw >> 9) & 0x1e80) | (cwsw & 0x3d);
 125                 __fenv_setmxcsr(&mxcsr);
 126         }
 127         __fenv_setcwsw(&cwsw);
 128 }
 129 
 130 /* Offsets into the fp environment save area (assumes 32-bit protected mode) */
 131 #define CW      0       /* control word */
 132 #define SW      1       /* status word */
 133 #define TW      2       /* tag word */
 134 #define IP      3       /* instruction pointer */
 135 #define OP      4       /* opcode */
 136 #define EA      5       /* operand address */
 137 
 138 /* macro for accessing fp registers in the save area */
 139 #if defined(__amd64)
 140 #define fpreg(u,x)      *(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.st)
 141 #else
 142 #define fpreg(u,x)      *(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[7])
 143 #endif
 144 
 145 /*
 146 *  Fix sip->si_code; the Solaris x86 kernel can get it wrong
 147 */
 148 void
 149 __fex_get_x86_exc(siginfo_t *sip, ucontext_t *uap)
 150 {
 151         unsigned        sw, cw;
 152 
 153         sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
 154 #if defined(__amd64)
 155         cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw;
 156 #else
 157         cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[CW];
 158 #endif
 159         if ((sw & FE_INVALID) && !(cw & (1 << fp_trap_invalid)))
 160                 /* store 0 for stack fault, FPE_FLTINV for IEEE invalid op */
 161                 sip->si_code = ((sw & 0x40)? 0 : FPE_FLTINV);
 162         else if ((sw & FE_DIVBYZERO) && !(cw & (1 << fp_trap_division)))
 163                 sip->si_code = FPE_FLTDIV;
 164         else if ((sw & FE_OVERFLOW) && !(cw & (1 << fp_trap_overflow)))
 165                 sip->si_code = FPE_FLTOVF;
 166         else if ((sw & FE_UNDERFLOW) && !(cw & (1 << fp_trap_underflow)))
 167                 sip->si_code = FPE_FLTUND;
 168         else if ((sw & FE_INEXACT) && !(cw & (1 << fp_trap_inexact)))
 169                 sip->si_code = FPE_FLTRES;
 170         else
 171                 sip->si_code = 0;
 172 }
 173 
 174 static enum fp_class_type
 175 my_fp_classf(float *x)
 176 {
 177         int             i = *(int*)x & ~0x80000000;
 178 
 179         if (i < 0x7f800000) {
 180                 if (i < 0x00800000)
 181                         return ((i == 0)? fp_zero : fp_subnormal);
 182                 return fp_normal;
 183         }
 184         else if (i == 0x7f800000)
 185                 return fp_infinity;
 186         else if (i & 0x400000)
 187                 return fp_quiet;
 188         else
 189                 return fp_signaling;
 190 }
 191 
 192 static enum fp_class_type
 193 my_fp_class(double *x)
 194 {
 195         int             i = *(1+(int*)x) & ~0x80000000;
 196 
 197         if (i < 0x7ff00000) {
 198                 if (i < 0x00100000)
 199                         return (((i | *(int*)x) == 0)? fp_zero : fp_subnormal);
 200                 return fp_normal;
 201         }
 202         else if (i == 0x7ff00000 && *(int*)x == 0)
 203                 return fp_infinity;
 204         else if (i & 0x80000)
 205                 return fp_quiet;
 206         else
 207                 return fp_signaling;
 208 }
 209 
 210 static enum fp_class_type
 211 my_fp_classl(long double *x)
 212 {
 213         int             i = *(2+(int*)x) & 0x7fff;
 214 
 215         if (i < 0x7fff) {
 216                 if (i < 1) {
 217                         if (*(1+(int*)x) < 0) return fp_normal; /* pseudo-denormal */
 218                         return (((*(1+(int*)x) | *(int*)x) == 0)?
 219                                 fp_zero : fp_subnormal);
 220                 }
 221                 return ((*(1+(int*)x) < 0)? fp_normal :
 222                         (enum fp_class_type) -1); /* unsupported format */
 223         }
 224         else if (*(1+(int*)x) == 0x80000000 && *(int*)x == 0)
 225                 return fp_infinity;
 226         else if (*(1+(unsigned*)x) >= 0xc0000000)
 227                 return fp_quiet;
 228         else if (*(1+(int*)x) < 0)
 229                 return fp_signaling;
 230         else
 231                 return (enum fp_class_type) -1; /* unsupported format */
 232 }
 233 
 234 /*
 235 *  Determine which type of invalid operation exception occurred
 236 */
 237 enum fex_exception
 238 __fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
 239 {
 240         unsigned                        op;
 241         unsigned long                   ea;
 242         enum fp_class_type      t1, t2;
 243 
 244         /* get the opcode and data address */
 245 #if defined(__amd64)
 246         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
 247         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
 248 #else
 249         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
 250         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
 251 #endif
 252 
 253         /* if the instruction is fld, the source must be snan (it can't be
 254            an unsupported format, since fldt doesn't raise any exceptions) */
 255         switch (op & 0x7f8) {
 256         case 0x100:
 257         case 0x140:
 258         case 0x180:
 259         case 0x500:
 260         case 0x540:
 261         case 0x580:
 262                 return fex_inv_snan;
 263         }
 264 
 265         /* otherwise st is one of the operands; see if it's snan */
 266         t1 = my_fp_classl(&fpreg(uap, 0));
 267         if (t1 == fp_signaling)
 268                 return fex_inv_snan;
 269         else if (t1 == (enum fp_class_type) -1)
 270                 return (enum fex_exception) -1;
 271 
 272         /* determine the class of the second operand if there is one */
 273         t2 = fp_normal;
 274         switch (op & 0x7e0) {
 275         case 0x600:
 276         case 0x620:
 277         case 0x640:
 278         case 0x660:
 279         case 0x680:
 280         case 0x6a0:
 281                 /* short memory operand */
 282                 if (!ea)
 283                         return (enum fex_exception) -1;
 284                 if (*(short *)ea == 0)
 285                         t2 = fp_zero;
 286                 break;
 287 
 288         case 0x200:
 289         case 0x220:
 290         case 0x240:
 291         case 0x260:
 292         case 0x280:
 293         case 0x2a0:
 294                 /* int memory operand */
 295                 if (!ea)
 296                         return (enum fex_exception) -1;
 297                 if (*(int *)ea == 0)
 298                         t2 = fp_zero;
 299                 break;
 300 
 301         case 0x000:
 302         case 0x020:
 303         case 0x040:
 304         case 0x060:
 305         case 0x080:
 306         case 0x0a0:
 307                 /* single precision memory operand */
 308                 if (!ea)
 309                         return (enum fex_exception) -1;
 310                 t2 = my_fp_classf((float *)ea);
 311                 break;
 312 
 313         case 0x400:
 314         case 0x420:
 315         case 0x440:
 316         case 0x460:
 317         case 0x480:
 318         case 0x4a0:
 319                 /* double precision memory operand */
 320                 if (!ea)
 321                         return (enum fex_exception) -1;
 322                 t2 = my_fp_class((double *)ea);
 323                 break;
 324 
 325         case 0x0c0:
 326         case 0x0e0:
 327         case 0x3e0:
 328         case 0x4c0:
 329         case 0x4e0:
 330         case 0x5e0:
 331         case 0x6c0:
 332         case 0x6e0:
 333         case 0x7e0:
 334                 /* register operand determined by opcode */
 335                 switch (op & 0x7f8) {
 336                 case 0x3e0:
 337                 case 0x3f8:
 338                 case 0x5f0:
 339                 case 0x5f8:
 340                 case 0x7e0:
 341                 case 0x7f8:
 342                         /* weed out nonexistent opcodes */
 343                         break;
 344 
 345                 default:
 346                         t2 = my_fp_classl(&fpreg(uap, op & 7));
 347                 }
 348                 break;
 349 
 350         case 0x1e0:
 351         case 0x2e0:
 352                 /* special forms */
 353                 switch (op) {
 354                 case 0x1f1: /* fyl2x */
 355                 case 0x1f3: /* fpatan */
 356                 case 0x1f5: /* fprem1 */
 357                 case 0x1f8: /* fprem */
 358                 case 0x1f9: /* fyl2xp1 */
 359                 case 0x1fd: /* fscale */
 360                 case 0x2e9: /* fucompp */
 361                         t2 = my_fp_classl(&fpreg(uap, 1));
 362                         break;
 363                 }
 364                 break;
 365         }
 366 
 367         /* see if the second op is snan */
 368         if (t2 == fp_signaling)
 369                 return fex_inv_snan;
 370         else if (t2 == (enum fp_class_type) -1)
 371                 return (enum fex_exception) -1;
 372 
 373         /* determine the type of operation */
 374         switch (op & 0x7f8) {
 375         case 0x000:
 376         case 0x020:
 377         case 0x028:
 378         case 0x040:
 379         case 0x060:
 380         case 0x068:
 381         case 0x080:
 382         case 0x0a0:
 383         case 0x0a8:
 384         case 0x0c0:
 385         case 0x0e0:
 386         case 0x0e8:
 387         case 0x400:
 388         case 0x420:
 389         case 0x428:
 390         case 0x440:
 391         case 0x460:
 392         case 0x468:
 393         case 0x480:
 394         case 0x4a0:
 395         case 0x4a8:
 396         case 0x4c0:
 397         case 0x4e0:
 398         case 0x4e8:
 399         case 0x6c0:
 400         case 0x6e0:
 401         case 0x6e8:
 402                 /* fadd, fsub, fsubr */
 403                 if (t1 == fp_infinity && t2 == fp_infinity)
 404                         return fex_inv_isi;
 405                 break;
 406 
 407         case 0x008:
 408         case 0x048:
 409         case 0x088:
 410         case 0x0c8:
 411         case 0x208:
 412         case 0x248:
 413         case 0x288:
 414         case 0x408:
 415         case 0x448:
 416         case 0x488:
 417         case 0x4c8:
 418         case 0x608:
 419         case 0x648:
 420         case 0x688:
 421         case 0x6c8:
 422                 /* fmul */
 423                 if ((t1 == fp_zero && t2 == fp_infinity) || (t2 == fp_zero &&
 424                   t1 == fp_infinity))
 425                         return fex_inv_zmi;
 426                 break;
 427 
 428         case 0x030:
 429         case 0x038:
 430         case 0x070:
 431         case 0x078:
 432         case 0x0b0:
 433         case 0x0b8:
 434         case 0x0f0:
 435         case 0x0f8:
 436         case 0x230:
 437         case 0x238:
 438         case 0x270:
 439         case 0x278:
 440         case 0x2b0:
 441         case 0x2b8:
 442         case 0x430:
 443         case 0x438:
 444         case 0x470:
 445         case 0x478:
 446         case 0x4b0:
 447         case 0x4b8:
 448         case 0x4f0:
 449         case 0x4f8:
 450         case 0x630:
 451         case 0x638:
 452         case 0x670:
 453         case 0x678:
 454         case 0x6b0:
 455         case 0x6b8:
 456         case 0x6f0:
 457         case 0x6f8:
 458                 /* fdiv */
 459                 if (t1 == fp_zero && t2 == fp_zero)
 460                         return fex_inv_zdz;
 461                 else if (t1 == fp_infinity && t2 == fp_infinity)
 462                         return fex_inv_idi;
 463                 break;
 464 
 465         case 0x1f0:
 466         case 0x1f8:
 467                 /* fsqrt, other special ops */
 468                 return fex_inv_sqrt;
 469 
 470         case 0x010:
 471         case 0x018:
 472         case 0x050:
 473         case 0x058:
 474         case 0x090:
 475         case 0x098:
 476         case 0x0d0:
 477         case 0x0d8:
 478         case 0x210:
 479         case 0x218:
 480         case 0x250:
 481         case 0x258:
 482         case 0x290:
 483         case 0x298:
 484         case 0x2e8:
 485         case 0x3f0:
 486         case 0x410:
 487         case 0x418:
 488         case 0x450:
 489         case 0x458:
 490         case 0x490:
 491         case 0x498:
 492         case 0x4d0:
 493         case 0x4d8:
 494         case 0x5e0:
 495         case 0x5e8:
 496         case 0x610:
 497         case 0x618:
 498         case 0x650:
 499         case 0x658:
 500         case 0x690:
 501         case 0x698:
 502         case 0x6d0:
 503         case 0x6d8:
 504         case 0x7f0:
 505                 /* fcom */
 506                 if (t1 == fp_quiet || t2 == fp_quiet)
 507                         return fex_inv_cmp;
 508                 break;
 509 
 510         case 0x1e0:
 511                 /* ftst */
 512                 if (op == 0x1e4 && t1 == fp_quiet)
 513                         return fex_inv_cmp;
 514                 break;
 515 
 516         case 0x310:
 517         case 0x318:
 518         case 0x350:
 519         case 0x358:
 520         case 0x390:
 521         case 0x398:
 522         case 0x710:
 523         case 0x718:
 524         case 0x730:
 525         case 0x738:
 526         case 0x750:
 527         case 0x758:
 528         case 0x770:
 529         case 0x778:
 530         case 0x790:
 531         case 0x798:
 532         case 0x7b0:
 533         case 0x7b8:
 534                 /* fist, fbst */
 535                 return fex_inv_int;
 536         }
 537 
 538         return (enum fex_exception) -1;
 539 }
 540 
 541 /* scale factors for exponent unwrapping */
 542 static const long double
 543         two12288 = 1.139165225263043370845938579315932009e+3699l,       /* 2^12288 */
 544         twom12288 = 8.778357852076208839765066529179033145e-3700l,      /* 2^-12288 */
 545         twom12288mulp = 8.778357852076208839289190796475222545e-3700l;
 546                 /* (")*(1-2^-64) */
 547 
 548 /* inline templates */
 549 extern long double f2xm1(long double);
 550 extern long double fyl2x(long double, long double);
 551 extern long double fptan(long double);
 552 extern long double fpatan(long double, long double);
 553 extern long double fxtract(long double);
 554 extern long double fprem1(long double, long double);
 555 extern long double fprem(long double, long double);
 556 extern long double fyl2xp1(long double, long double);
 557 extern long double fsqrt(long double);
 558 extern long double fsincos(long double);
 559 extern long double frndint(long double);
 560 extern long double fscale(long double, long double);
 561 extern long double fsin(long double);
 562 extern long double fcos(long double);
 563 
 564 /*
 565 *  Get the operands, generate the default untrapped result with
 566 *  exceptions, and set a code indicating the type of operation
 567 */
 568 void
 569 __fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
 570 {
 571         fex_numeric_t                   t;
 572         long double                     op2v, x;
 573         unsigned int                    cwsw, ex, sw, op;
 574         unsigned long                   ea;
 575         volatile int                    c;
 576 
 577         /* get the exception type, status word, opcode, and data address */
 578         ex = sip->si_code;
 579         sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
 580 #if defined(__amd64)
 581         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
 582         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
 583 #else
 584         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
 585         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
 586 #endif
 587 
 588         /* initialize res to the default untrapped result and ex to the
 589            corresponding flags (assume trapping is disabled and flags
 590            are clear) */
 591 
 592         /* single operand instructions */
 593         info->op = fex_cnvt;
 594         info->op2.type = fex_nodata;
 595         switch (op & 0x7f8) {
 596         /* load instructions */
 597         case 0x100:
 598         case 0x140:
 599         case 0x180:
 600                 if (!ea) {
 601                         info->op = fex_other;
 602                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
 603                         info->flags = 0;
 604                         return;
 605                 }
 606                 info->op1.type = fex_float;
 607                 info->op1.val.f = *(float *)ea;
 608                 info->res.type = fex_ldouble;
 609                 info->res.val.q = (long double) info->op1.val.f;
 610                 goto done;
 611 
 612         case 0x500:
 613         case 0x540:
 614         case 0x580:
 615                 if (!ea) {
 616                         info->op = fex_other;
 617                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
 618                         info->flags = 0;
 619                         return;
 620                 }
 621                 info->op1.type = fex_double;
 622                 info->op1.val.d = *(double *)ea;
 623                 info->res.type = fex_ldouble;
 624                 info->res.val.q = (long double) info->op1.val.d;
 625                 goto done;
 626 
 627         /* store instructions */
 628         case 0x110:
 629         case 0x118:
 630         case 0x150:
 631         case 0x158:
 632         case 0x190:
 633         case 0x198:
 634                 info->res.type = fex_float;
 635                 if (ex == FPE_FLTRES && (op & 8) != 0) {
 636                         /* inexact, stack popped */
 637                         if (!ea) {
 638                                 info->op = fex_other;
 639                                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
 640                                 info->flags = 0;
 641                                 return;
 642                         }
 643                         info->op1.type = fex_nodata;
 644                         info->res.val.f = *(float *)ea;
 645                         info->flags = FE_INEXACT;
 646                         return;
 647                 }
 648                 info->op1.type = fex_ldouble;
 649                 info->op1.val.q = fpreg(uap, 0);
 650                 info->res.val.f = (float) info->op1.val.q;
 651                 goto done;
 652 
 653         case 0x310:
 654         case 0x318:
 655         case 0x350:
 656         case 0x358:
 657         case 0x390:
 658         case 0x398:
 659                 info->res.type = fex_int;
 660                 if (ex == FPE_FLTRES && (op & 8) != 0) {
 661                         /* inexact, stack popped */
 662                         if (!ea) {
 663                                 info->op = fex_other;
 664                                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
 665                                 info->flags = 0;
 666                                 return;
 667                         }
 668                         info->op1.type = fex_nodata;
 669                         info->res.val.i = *(int *)ea;
 670                         info->flags = FE_INEXACT;
 671                         return;
 672                 }
 673                 info->op1.type = fex_ldouble;
 674                 info->op1.val.q = fpreg(uap, 0);
 675                 info->res.val.i = (int) info->op1.val.q;
 676                 goto done;
 677 
 678         case 0x510:
 679         case 0x518:
 680         case 0x550:
 681         case 0x558:
 682         case 0x590:
 683         case 0x598:
 684                 info->res.type = fex_double;
 685                 if (ex == FPE_FLTRES && (op & 8) != 0) {
 686                         /* inexact, stack popped */
 687                         if (!ea) {
 688                                 info->op = fex_other;
 689                                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
 690                                 info->flags = 0;
 691                                 return;
 692                         }
 693                         info->op1.type = fex_nodata;
 694                         info->res.val.d = *(double *)ea;
 695                         info->flags = FE_INEXACT;
 696                         return;
 697                 }
 698                 info->op1.type = fex_ldouble;
 699                 info->op1.val.q = fpreg(uap, 0);
 700                 info->res.val.d = (double) info->op1.val.q;
 701                 goto done;
 702 
 703         case 0x710:
 704         case 0x718:
 705         case 0x750:
 706         case 0x758:
 707         case 0x790:
 708         case 0x798:
 709                 info->res.type = fex_int;
 710                 if (ex == FPE_FLTRES && (op & 8) != 0) {
 711                         /* inexact, stack popped */
 712                         if (!ea) {
 713                                 info->op = fex_other;
 714                                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
 715                                 info->flags = 0;
 716                                 return;
 717                         }
 718                         info->op1.type = fex_nodata;
 719                         info->res.val.i = *(short *)ea;
 720                         info->flags = FE_INEXACT;
 721                         return;
 722                 }
 723                 info->op1.type = fex_ldouble;
 724                 info->op1.val.q = fpreg(uap, 0);
 725                 info->res.val.i = (short) info->op1.val.q;
 726                 goto done;
 727 
 728         case 0x730:
 729         case 0x770:
 730         case 0x7b0:
 731                 /* fbstp; don't bother */
 732                 info->op = fex_other;
 733                 info->op1.type = info->res.type = fex_nodata;
 734                 info->flags = 0;
 735                 return;
 736 
 737         case 0x738:
 738         case 0x778:
 739         case 0x7b8:
 740                 info->res.type = fex_llong;
 741                 if (ex == FPE_FLTRES) {
 742                         /* inexact, stack popped */
 743                         if (!ea) {
 744                                 info->op = fex_other;
 745                                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
 746                                 info->flags = 0;
 747                                 return;
 748                         }
 749                         info->op1.type = fex_nodata;
 750                         info->res.val.l = *(long long *)ea;
 751                         info->flags = FE_INEXACT;
 752                         return;
 753                 }
 754                 info->op1.type = fex_ldouble;
 755                 info->op1.val.q = fpreg(uap, 0);
 756                 info->res.val.l = (long long) info->op1.val.q;
 757                 goto done;
 758         }
 759 
 760         /* all other ops (except compares) have destinations on the stack
 761            so overflow, underflow, and inexact will stomp their operands */
 762         if (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES) {
 763                 /* find the trapped result */
 764                 info->op1.type = info->op2.type = fex_nodata;
 765                 info->res.type = fex_ldouble;
 766                 switch (op & 0x7f8) {
 767                 case 0x1f0:
 768                         /* fptan pushes 1.0 afterward, so result is in st(1) */
 769                         info->res.val.q = ((op == 0x1f2)? fpreg(uap, 1) :
 770                                 fpreg(uap, 0));
 771                         break;
 772 
 773                 case 0x4c0:
 774                 case 0x4c8:
 775                 case 0x4e0:
 776                 case 0x4e8:
 777                 case 0x4f0:
 778                 case 0x4f8:
 779                         info->res.val.q = fpreg(uap, op & 7);
 780                         break;
 781 
 782                 case 0x6c0:
 783                 case 0x6c8:
 784                 case 0x6e0:
 785                 case 0x6e8:
 786                 case 0x6f0:
 787                 case 0x6f8:
 788                         /* stack was popped afterward */
 789                         info->res.val.q = fpreg(uap, (op - 1) & 7);
 790                         break;
 791 
 792                 default:
 793                         info->res.val.q = fpreg(uap, 0);
 794                 }
 795 
 796                 /* reconstruct default untrapped result */
 797                 if (ex == FPE_FLTOVF) {
 798                         /* generate an overflow with the sign of the result */
 799                         x = two12288;
 800                         *(4+(short*)&x) |= (*(4+(short*)&info->res.val.q) & 0x8000);
 801                         info->res.val.q = x * two12288;
 802                         info->flags = FE_OVERFLOW | FE_INEXACT;
 803                         __fenv_getcwsw(&cwsw);
 804                         cwsw &= ~FE_ALL_EXCEPT;
 805                         __fenv_setcwsw(&cwsw);
 806                 }
 807                 else if (ex == FPE_FLTUND) {
 808                         /* undo the scaling; we can't distinguish a chopped result
 809                            from an exact one without futzing around to trap all in-
 810                            exact exceptions so as to keep the flag clear, so we just
 811                            punt */
 812                         if (sw & 0x200) /* result was rounded up */
 813                                 info->res.val.q = (info->res.val.q * twom12288) * twom12288mulp;
 814                         else
 815                                 info->res.val.q = (info->res.val.q * twom12288) * twom12288;
 816                         __fenv_getcwsw(&cwsw);
 817                         info->flags = (cwsw & FE_INEXACT) | FE_UNDERFLOW;
 818                         cwsw &= ~FE_ALL_EXCEPT;
 819                         __fenv_setcwsw(&cwsw);
 820                 }
 821                 else
 822                         info->flags = FE_INEXACT;
 823 
 824                 /* determine the operation code */
 825                 switch (op) {
 826                 case 0x1f0: /* f2xm1 */
 827                 case 0x1f1: /* fyl2x */
 828                 case 0x1f2: /* fptan */
 829                 case 0x1f3: /* fpatan */
 830                 case 0x1f5: /* fprem1 */
 831                 case 0x1f8: /* fprem */
 832                 case 0x1f9: /* fyl2xp1 */
 833                 case 0x1fb: /* fsincos */
 834                 case 0x1fc: /* frndint */
 835                 case 0x1fd: /* fscale */
 836                 case 0x1fe: /* fsin */
 837                 case 0x1ff: /* fcos */
 838                         info->op = fex_other;
 839                         return;
 840 
 841                 case 0x1fa: /* fsqrt */
 842                         info->op = fex_sqrt;
 843                         return;
 844                 }
 845 
 846                 info->op = fex_other;
 847                 switch (op & 0x7c0) {
 848                 case 0x000:
 849                 case 0x040:
 850                 case 0x080:
 851                 case 0x0c0:
 852                 case 0x200:
 853                 case 0x240:
 854                 case 0x280:
 855                 case 0x400:
 856                 case 0x440:
 857                 case 0x480:
 858                 case 0x4c0:
 859                 case 0x600:
 860                 case 0x640:
 861                 case 0x680:
 862                 case 0x6c0:
 863                         switch (op & 0x38) {
 864                         case 0x00:
 865                                 info->op = fex_add;
 866                                 break;
 867 
 868                         case 0x08:
 869                                 info->op = fex_mul;
 870                                 break;
 871 
 872                         case 0x20:
 873                         case 0x28:
 874                                 info->op = fex_sub;
 875                                 break;
 876 
 877                         case 0x30:
 878                         case 0x38:
 879                                 info->op = fex_div;
 880                                 break;
 881                         }
 882                 }
 883                 return;
 884         }
 885 
 886         /* for other exceptions, the operands are preserved, so we can
 887            just emulate the operation with traps disabled */
 888 
 889         /* one operand is always in st */
 890         info->op1.type = fex_ldouble;
 891         info->op1.val.q = fpreg(uap, 0);
 892 
 893         /* oddball instructions */
 894         info->op = fex_other;
 895         switch (op) {
 896         case 0x1e4: /* ftst */
 897                 info->op = fex_cmp;
 898                 info->op2.type = fex_ldouble;
 899                 info->op2.val.q = 0.0l;
 900                 info->res.type = fex_nodata;
 901                 c = (info->op1.val.q < info->op2.val.q);
 902                 goto done;
 903 
 904         case 0x1f0: /* f2xm1 */
 905                 info->res.type = fex_ldouble;
 906                 info->res.val.q = f2xm1(info->op1.val.q);
 907                 goto done;
 908 
 909         case 0x1f1: /* fyl2x */
 910                 info->op2.type = fex_ldouble;
 911                 info->op2.val.q = fpreg(uap, 1);
 912                 info->res.type = fex_ldouble;
 913                 info->res.val.q = fyl2x(info->op1.val.q, info->op2.val.q);
 914                 goto done;
 915 
 916         case 0x1f2: /* fptan */
 917                 info->res.type = fex_ldouble;
 918                 info->res.val.q = fptan(info->op1.val.q);
 919                 goto done;
 920 
 921         case 0x1f3: /* fpatan */
 922                 info->op2.type = fex_ldouble;
 923                 info->op2.val.q = fpreg(uap, 1);
 924                 info->res.type = fex_ldouble;
 925                 info->res.val.q = fpatan(info->op1.val.q, info->op2.val.q);
 926                 goto done;
 927 
 928         case 0x1f4: /* fxtract */
 929                 info->res.type = fex_ldouble;
 930                 info->res.val.q = fxtract(info->op1.val.q);
 931                 goto done;
 932 
 933         case 0x1f5: /* fprem1 */
 934                 info->op2.type = fex_ldouble;
 935                 info->op2.val.q = fpreg(uap, 1);
 936                 info->res.type = fex_ldouble;
 937                 info->res.val.q = fprem1(info->op1.val.q, info->op2.val.q);
 938                 goto done;
 939 
 940         case 0x1f8: /* fprem */
 941                 info->op2.type = fex_ldouble;
 942                 info->op2.val.q = fpreg(uap, 1);
 943                 info->res.type = fex_ldouble;
 944                 info->res.val.q = fprem(info->op1.val.q, info->op2.val.q);
 945                 goto done;
 946 
 947         case 0x1f9: /* fyl2xp1 */
 948                 info->op2.type = fex_ldouble;
 949                 info->op2.val.q = fpreg(uap, 1);
 950                 info->res.type = fex_ldouble;
 951                 info->res.val.q = fyl2xp1(info->op1.val.q, info->op2.val.q);
 952                 goto done;
 953 
 954         case 0x1fa: /* fsqrt */
 955                 info->op = fex_sqrt;
 956                 info->res.type = fex_ldouble;
 957                 info->res.val.q = fsqrt(info->op1.val.q);
 958                 goto done;
 959 
 960         case 0x1fb: /* fsincos */
 961                 info->res.type = fex_ldouble;
 962                 info->res.val.q = fsincos(info->op1.val.q);
 963                 goto done;
 964 
 965         case 0x1fc: /* frndint */
 966                 info->res.type = fex_ldouble;
 967                 info->res.val.q = frndint(info->op1.val.q);
 968                 goto done;
 969 
 970         case 0x1fd: /* fscale */
 971                 info->op2.type = fex_ldouble;
 972                 info->op2.val.q = fpreg(uap, 1);
 973                 info->res.type = fex_ldouble;
 974                 info->res.val.q = fscale(info->op1.val.q, info->op2.val.q);
 975                 goto done;
 976 
 977         case 0x1fe: /* fsin */
 978                 info->res.type = fex_ldouble;
 979                 info->res.val.q = fsin(info->op1.val.q);
 980                 goto done;
 981 
 982         case 0x1ff: /* fcos */
 983                 info->res.type = fex_ldouble;
 984                 info->res.val.q = fcos(info->op1.val.q);
 985                 goto done;
 986 
 987         case 0x2e9: /* fucompp */
 988                 info->op = fex_cmp;
 989                 info->op2.type = fex_ldouble;
 990                 info->op2.val.q = fpreg(uap, 1);
 991                 info->res.type = fex_nodata;
 992                 c = (info->op1.val.q == info->op2.val.q);
 993                 goto done;
 994         }
 995 
 996         /* fucom[p], fcomi[p], fucomi[p] */
 997         switch (op & 0x7f8) {
 998         case 0x3e8:
 999         case 0x5e0:
1000         case 0x5e8:
1001         case 0x7e8: /* unordered compares */
1002                 info->op = fex_cmp;
1003                 info->op2.type = fex_ldouble;
1004                 info->op2.val.q = fpreg(uap, op & 7);
1005                 info->res.type = fex_nodata;
1006                 c = (info->op1.val.q == info->op2.val.q);
1007                 goto done;
1008 
1009         case 0x3f0:
1010         case 0x7f0: /* ordered compares */
1011                 info->op = fex_cmp;
1012                 info->op2.type = fex_ldouble;
1013                 info->op2.val.q = fpreg(uap, op & 7);
1014                 info->res.type = fex_nodata;
1015                 c = (info->op1.val.q < info->op2.val.q);
1016                 goto done;
1017         }
1018 
1019         /* all other instructions come in groups of the form
1020            fadd, fmul, fcom, fcomp, fsub, fsubr, fdiv, fdivr */
1021 
1022         /* get the second operand */
1023         switch (op & 0x7c0) {
1024         case 0x000:
1025         case 0x040:
1026         case 0x080:
1027                 if (!ea) {
1028                         info->op = fex_other;
1029                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
1030                         info->flags = 0;
1031                         return;
1032                 }
1033                 info->op2.type = fex_float;
1034                 info->op2.val.f = *(float *)ea;
1035                 op2v = (long double) info->op2.val.f;
1036                 break;
1037 
1038         case 0x0c0:
1039                 info->op2.type = fex_ldouble;
1040                 op2v = info->op2.val.q = fpreg(uap, op & 7);
1041                 break;
1042 
1043         case 0x200:
1044         case 0x240:
1045         case 0x280:
1046                 if (!ea) {
1047                         info->op = fex_other;
1048                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
1049                         info->flags = 0;
1050                         return;
1051                 }
1052                 info->op2.type = fex_int;
1053                 info->op2.val.i = *(int *)ea;
1054                 op2v = (long double) info->op2.val.i;
1055                 break;
1056 
1057         case 0x400:
1058         case 0x440:
1059         case 0x480:
1060                 if (!ea) {
1061                         info->op = fex_other;
1062                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
1063                         info->flags = 0;
1064                         return;
1065                 }
1066                 info->op2.type = fex_double;
1067                 info->op2.val.d = *(double *)ea;
1068                 op2v = (long double) info->op2.val.d;
1069                 break;
1070 
1071         case 0x4c0:
1072         case 0x6c0:
1073                 info->op2.type = fex_ldouble;
1074                 info->op2.val.q = fpreg(uap, op & 7);
1075                 t = info->op1;
1076                 info->op1 = info->op2;
1077                 info->op2 = t;
1078                 op2v = info->op2.val.q;
1079                 break;
1080 
1081         case 0x600:
1082         case 0x640:
1083         case 0x680:
1084                 if (!ea) {
1085                         info->op = fex_other;
1086                         info->op1.type = info->op2.type = info->res.type = fex_nodata;
1087                         info->flags = 0;
1088                         return;
1089                 }
1090                 info->op2.type = fex_int;
1091                 info->op2.val.i = *(short *)ea;
1092                 op2v = (long double) info->op2.val.i;
1093                 break;
1094 
1095         default:
1096                 info->op = fex_other;
1097                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
1098                 info->flags = 0;
1099                 return;
1100         }
1101 
1102         /* distinguish different operations in the group */
1103         info->res.type = fex_ldouble;
1104         switch (op & 0x38) {
1105         case 0x00:
1106                 info->op = fex_add;
1107                 info->res.val.q = info->op1.val.q + op2v;
1108                 break;
1109 
1110         case 0x08:
1111                 info->op = fex_mul;
1112                 info->res.val.q = info->op1.val.q * op2v;
1113                 break;
1114 
1115         case 0x10:
1116         case 0x18:
1117                 info->op = fex_cmp;
1118                 info->res.type = fex_nodata;
1119                 c = (info->op1.val.q < op2v);
1120                 break;
1121 
1122         case 0x20:
1123                 info->op = fex_sub;
1124                 info->res.val.q = info->op1.val.q - op2v;
1125                 break;
1126 
1127         case 0x28:
1128                 info->op = fex_sub;
1129                 info->res.val.q = op2v - info->op1.val.q;
1130                 t = info->op1;
1131                 info->op1 = info->op2;
1132                 info->op2 = t;
1133                 break;
1134 
1135         case 0x30:
1136                 info->op = fex_div;
1137                 info->res.val.q = info->op1.val.q / op2v;
1138                 break;
1139 
1140         case 0x38:
1141                 info->op = fex_div;
1142                 info->res.val.q = op2v / info->op1.val.q;
1143                 t = info->op1;
1144                 info->op1 = info->op2;
1145                 info->op2 = t;
1146                 break;
1147 
1148         default:
1149                 info->op = fex_other;
1150                 info->op1.type = info->op2.type = info->res.type = fex_nodata;
1151                 info->flags = 0;
1152                 return;
1153         }
1154 
1155 done:
1156         __fenv_getcwsw(&cwsw);
1157         info->flags = cwsw & FE_ALL_EXCEPT;
1158         cwsw &= ~FE_ALL_EXCEPT;
1159         __fenv_setcwsw(&cwsw);
1160 }
1161 
1162 /* pop the saved stack */
1163 static void pop(ucontext_t *uap)
1164 {
1165         unsigned top;
1166 
1167         fpreg(uap, 0) = fpreg(uap, 1);
1168         fpreg(uap, 1) = fpreg(uap, 2);
1169         fpreg(uap, 2) = fpreg(uap, 3);
1170         fpreg(uap, 3) = fpreg(uap, 4);
1171         fpreg(uap, 4) = fpreg(uap, 5);
1172         fpreg(uap, 5) = fpreg(uap, 6);
1173         fpreg(uap, 6) = fpreg(uap, 7);
1174 #if defined(__amd64)
1175         top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
1176                 & 0xe;
1177         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw |= (3 << top);
1178         top = (top + 2) & 0xe;
1179         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
1180                 (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
1181                 | (top << 10);
1182 #else
1183         top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
1184                 & 0xe;
1185         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] |= (3 << top);
1186         top = (top + 2) & 0xe;
1187         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
1188                 (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
1189                 | (top << 10);
1190 #endif
1191 }
1192 
1193 /* push x onto the saved stack */
1194 static void push(long double x, ucontext_t *uap)
1195 {
1196         unsigned top;
1197 
1198         fpreg(uap, 7) = fpreg(uap, 6);
1199         fpreg(uap, 6) = fpreg(uap, 5);
1200         fpreg(uap, 5) = fpreg(uap, 4);
1201         fpreg(uap, 4) = fpreg(uap, 3);
1202         fpreg(uap, 3) = fpreg(uap, 2);
1203         fpreg(uap, 2) = fpreg(uap, 1);
1204         fpreg(uap, 1) = fpreg(uap, 0);
1205         fpreg(uap, 0) = x;
1206 #if defined(__amd64)
1207         top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
1208                 & 0xe;
1209         top = (top - 2) & 0xe;
1210         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw &= ~(3 << top);
1211         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
1212                 (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
1213                 | (top << 10);
1214 #else
1215         top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
1216                 & 0xe;
1217         top = (top - 2) & 0xe;
1218         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] &= ~(3 << top);
1219         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
1220                 (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
1221                 | (top << 10);
1222 #endif
1223 }
1224 
1225 /* scale factors for exponent wrapping */
1226 static const float
1227         fun = 7.922816251e+28f, /* 2^96 */
1228         fov = 1.262177448e-29f; /* 2^-96 */
1229 static const double
1230         dun = 1.552518092300708935e+231,        /* 2^768 */
1231         dov = 6.441148769597133308e-232;        /* 2^-768 */
1232 
1233 /*
1234 *  Store the specified result; if no result is given but the exception
1235 *  is underflow or overflow, use the default trapped result
1236 */
1237 void
1238 __fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
1239 {
1240         fex_numeric_t   r;
1241         unsigned long           ex, op, ea, stack;
1242 
1243         /* get the exception type, opcode, and data address */
1244         ex = sip->si_code;
1245 #if defined(__amd64)
1246         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
1247         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp; /*???*/
1248 #else
1249         op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
1250         ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
1251 #endif
1252 
1253         /* if the instruction is a compare, set the condition codes
1254            to unordered and update the stack */
1255         switch (op & 0x7f8) {
1256         case 0x010:
1257         case 0x050:
1258         case 0x090:
1259         case 0x0d0:
1260         case 0x210:
1261         case 0x250:
1262         case 0x290:
1263         case 0x410:
1264         case 0x450:
1265         case 0x490:
1266         case 0x4d0:
1267         case 0x5e0:
1268         case 0x610:
1269         case 0x650:
1270         case 0x690:
1271                 /* f[u]com */
1272 #if defined(__amd64)
1273                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1274 #else
1275                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1276 #endif
1277                 return;
1278 
1279         case 0x018:
1280         case 0x058:
1281         case 0x098:
1282         case 0x0d8:
1283         case 0x218:
1284         case 0x258:
1285         case 0x298:
1286         case 0x418:
1287         case 0x458:
1288         case 0x498:
1289         case 0x4d8:
1290         case 0x5e8:
1291         case 0x618:
1292         case 0x658:
1293         case 0x698:
1294         case 0x6d0:
1295                 /* f[u]comp */
1296 #if defined(__amd64)
1297                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1298 #else
1299                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1300 #endif
1301                 pop(uap);
1302                 return;
1303 
1304         case 0x2e8:
1305         case 0x6d8:
1306                 /* f[u]compp */
1307 #if defined(__amd64)
1308                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1309 #else
1310                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1311 #endif
1312                 pop(uap);
1313                 pop(uap);
1314                 return;
1315 
1316         case 0x1e0:
1317                 if (op == 0x1e4) { /* ftst */
1318 #if defined(__amd64)
1319                         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1320 #else
1321                         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1322 #endif
1323                         return;
1324                 }
1325                 break;
1326 
1327         case 0x3e8:
1328         case 0x3f0:
1329                 /* f[u]comi */
1330 #if defined(__amd64)
1331                 uap->uc_mcontext.gregs[REG_PS] |= 0x45;
1332 #else
1333                 uap->uc_mcontext.gregs[EFL] |= 0x45;
1334 #endif
1335                 return;
1336 
1337         case 0x7e8:
1338         case 0x7f0:
1339                 /* f[u]comip */
1340 #if defined(__amd64)
1341                 uap->uc_mcontext.gregs[REG_PS] |= 0x45;
1342 #else
1343                 uap->uc_mcontext.gregs[EFL] |= 0x45;
1344 #endif
1345                 pop(uap);
1346                 return;
1347         }
1348 
1349         /* if there is no result available and the exception is overflow
1350            or underflow, use the wrapped result */
1351         r = info->res;
1352         if (r.type == fex_nodata) {
1353                 if (ex == FPE_FLTOVF || ex == FPE_FLTUND) {
1354                         /* for store instructions, do the scaling and store */
1355                         switch (op & 0x7f8) {
1356                         case 0x110:
1357                         case 0x118:
1358                         case 0x150:
1359                         case 0x158:
1360                         case 0x190:
1361                         case 0x198:
1362                                 if (!ea)
1363                                         return;
1364                                 if (ex == FPE_FLTOVF)
1365                                         *(float *)ea = (fpreg(uap, 0) * fov) * fov;
1366                                 else
1367                                         *(float *)ea = (fpreg(uap, 0) * fun) * fun;
1368                                 if ((op & 8) != 0)
1369                                         pop(uap);
1370                                 break;
1371 
1372                         case 0x510:
1373                         case 0x518:
1374                         case 0x550:
1375                         case 0x558:
1376                         case 0x590:
1377                         case 0x598:
1378                                 if (!ea)
1379                                         return;
1380                                 if (ex == FPE_FLTOVF)
1381                                         *(double *)ea = (fpreg(uap, 0) * dov) * dov;
1382                                 else
1383                                         *(double *)ea = (fpreg(uap, 0) * dun) * dun;
1384                                 if ((op & 8) != 0)
1385                                         pop(uap);
1386                                 break;
1387                         }
1388                 }
1389 #ifdef DEBUG
1390                 else if (ex != FPE_FLTRES)
1391                         printf("No result supplied, stack may be hosed\n");
1392 #endif
1393                 return;
1394         }
1395 
1396         /* otherwise convert the supplied result to the correct type,
1397            put it in the destination, and update the stack as need be */
1398 
1399         /* store instructions */
1400         switch (op & 0x7f8) {
1401         case 0x110:
1402         case 0x118:
1403         case 0x150:
1404         case 0x158:
1405         case 0x190:
1406         case 0x198:
1407                 if (!ea)
1408                         return;
1409                 switch (r.type) {
1410                 case fex_int:
1411                         *(float *)ea = (float) r.val.i;
1412                         break;
1413 
1414                 case fex_llong:
1415                         *(float *)ea = (float) r.val.l;
1416                         break;
1417 
1418                 case fex_float:
1419                         *(float *)ea = r.val.f;
1420                         break;
1421 
1422                 case fex_double:
1423                         *(float *)ea = (float) r.val.d;
1424                         break;
1425 
1426                 case fex_ldouble:
1427                         *(float *)ea = (float) r.val.q;
1428                         break;
1429 
1430                 default:
1431                         break;
1432                 }
1433                 if (ex != FPE_FLTRES && (op & 8) != 0)
1434                         pop(uap);
1435                 return;
1436 
1437         case 0x310:
1438         case 0x318:
1439         case 0x350:
1440         case 0x358:
1441         case 0x390:
1442         case 0x398:
1443                 if (!ea)
1444                         return;
1445                 switch (r.type) {
1446                 case fex_int:
1447                         *(int *)ea = r.val.i;
1448                         break;
1449 
1450                 case fex_llong:
1451                         *(int *)ea = (int) r.val.l;
1452                         break;
1453 
1454                 case fex_float:
1455                         *(int *)ea = (int) r.val.f;
1456                         break;
1457 
1458                 case fex_double:
1459                         *(int *)ea = (int) r.val.d;
1460                         break;
1461 
1462                 case fex_ldouble:
1463                         *(int *)ea = (int) r.val.q;
1464                         break;
1465 
1466                 default:
1467                         break;
1468                 }
1469                 if (ex != FPE_FLTRES && (op & 8) != 0)
1470                         pop(uap);
1471                 return;
1472 
1473         case 0x510:
1474         case 0x518:
1475         case 0x550:
1476         case 0x558:
1477         case 0x590:
1478         case 0x598:
1479                 if (!ea)
1480                         return;
1481                 switch (r.type) {
1482                 case fex_int:
1483                         *(double *)ea = (double) r.val.i;
1484                         break;
1485 
1486                 case fex_llong:
1487                         *(double *)ea = (double) r.val.l;
1488                         break;
1489 
1490                 case fex_float:
1491                         *(double *)ea = (double) r.val.f;
1492                         break;
1493 
1494                 case fex_double:
1495                         *(double *)ea = r.val.d;
1496                         break;
1497 
1498                 case fex_ldouble:
1499                         *(double *)ea = (double) r.val.q;
1500                         break;
1501 
1502                 default:
1503                         break;
1504                 }
1505                 if (ex != FPE_FLTRES && (op & 8) != 0)
1506                         pop(uap);
1507                 return;
1508 
1509         case 0x710:
1510         case 0x718:
1511         case 0x750:
1512         case 0x758:
1513         case 0x790:
1514         case 0x798:
1515                 if (!ea)
1516                         return;
1517                 switch (r.type) {
1518                 case fex_int:
1519                         *(short *)ea = (short) r.val.i;
1520                         break;
1521 
1522                 case fex_llong:
1523                         *(short *)ea = (short) r.val.l;
1524                         break;
1525 
1526                 case fex_float:
1527                         *(short *)ea = (short) r.val.f;
1528                         break;
1529 
1530                 case fex_double:
1531                         *(short *)ea = (short) r.val.d;
1532                         break;
1533 
1534                 case fex_ldouble:
1535                         *(short *)ea = (short) r.val.q;
1536                         break;
1537 
1538                 default:
1539                         break;
1540                 }
1541                 if (ex != FPE_FLTRES && (op & 8) != 0)
1542                         pop(uap);
1543                 return;
1544 
1545         case 0x730:
1546         case 0x770:
1547         case 0x7b0:
1548                 /* fbstp; don't bother */
1549                 if (ea && ex != FPE_FLTRES)
1550                         pop(uap);
1551                 return;
1552 
1553         case 0x738:
1554         case 0x778:
1555         case 0x7b8:
1556                 if (!ea)
1557                         return;
1558                 switch (r.type) {
1559                 case fex_int:
1560                         *(long long *)ea = (long long) r.val.i;
1561                         break;
1562 
1563                 case fex_llong:
1564                         *(long long *)ea = r.val.l;
1565                         break;
1566 
1567                 case fex_float:
1568                         *(long long *)ea = (long long) r.val.f;
1569                         break;
1570 
1571                 case fex_double:
1572                         *(long long *)ea = (long long) r.val.d;
1573                         break;
1574 
1575                 case fex_ldouble:
1576                         *(long long *)ea = (long long) r.val.q;
1577                         break;
1578 
1579                 default:
1580                         break;
1581                 }
1582                 if (ex != FPE_FLTRES)
1583                         pop(uap);
1584                 return;
1585         }
1586 
1587         /* for all other instructions, the result goes into a register */
1588         switch (r.type) {
1589         case fex_int:
1590                 r.val.q = (long double) r.val.i;
1591                 break;
1592 
1593         case fex_llong:
1594                 r.val.q = (long double) r.val.l;
1595                 break;
1596 
1597         case fex_float:
1598                 r.val.q = (long double) r.val.f;
1599                 break;
1600 
1601         case fex_double:
1602                 r.val.q = (long double) r.val.d;
1603                 break;
1604 
1605         default:
1606                 break;
1607         }
1608 
1609         /* for load instructions, push the result onto the stack */
1610         switch (op & 0x7f8) {
1611         case 0x100:
1612         case 0x140:
1613         case 0x180:
1614         case 0x500:
1615         case 0x540:
1616         case 0x580:
1617                 if (ea)
1618                         push(r.val.q, uap);
1619                 return;
1620         }
1621 
1622         /* for all other instructions, if the exception is overflow,
1623            underflow, or inexact, the stack has already been updated */
1624         stack = (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES);
1625         switch (op & 0x7f8) {
1626         case 0x1f0: /* oddballs */
1627                 switch (op) {
1628                 case 0x1f1: /* fyl2x */
1629                 case 0x1f3: /* fpatan */
1630                 case 0x1f9: /* fyl2xp1 */
1631                         /* pop the stack, leaving the result in st */
1632                         if (!stack)
1633                                 pop(uap);
1634                         fpreg(uap, 0) = r.val.q;
1635                         return;
1636 
1637                 case 0x1f2: /* fpatan */
1638                         /* fptan pushes 1.0 afterward */
1639                         if (stack)
1640                                 fpreg(uap, 1) = r.val.q;
1641                         else {
1642                                 fpreg(uap, 0) = r.val.q;
1643                                 push(1.0L, uap);
1644                         }
1645                         return;
1646 
1647                 case 0x1f4: /* fxtract */
1648                 case 0x1fb: /* fsincos */
1649                         /* leave the supplied result in st */
1650                         if (stack)
1651                                 fpreg(uap, 0) = r.val.q;
1652                         else {
1653                                 fpreg(uap, 0) = 0.0; /* punt */
1654                                 push(r.val.q, uap);
1655                         }
1656                         return;
1657                 }
1658 
1659                 /* all others leave the stack alone and the result in st */
1660                 fpreg(uap, 0) = r.val.q;
1661                 return;
1662 
1663         case 0x4c0:
1664         case 0x4c8:
1665         case 0x4e0:
1666         case 0x4e8:
1667         case 0x4f0:
1668         case 0x4f8:
1669                 fpreg(uap, op & 7) = r.val.q;
1670                 return;
1671 
1672         case 0x6c0:
1673         case 0x6c8:
1674         case 0x6e0:
1675         case 0x6e8:
1676         case 0x6f0:
1677         case 0x6f8:
1678                 /* stack is popped afterward */
1679                 if (stack)
1680                         fpreg(uap, (op - 1) & 7) = r.val.q;
1681                 else {
1682                         fpreg(uap, op & 7) = r.val.q;
1683                         pop(uap);
1684                 }
1685                 return;
1686 
1687         default:
1688                 fpreg(uap, 0) = r.val.q;
1689                 return;
1690         }
1691 }