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