1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 1987-1995 by Symantec 6 * Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cg87.d, backend/cg87.d) 10 */ 11 12 module dmd.backend.cg87; 13 14 version (SCPP) 15 version = COMPILE; 16 version (MARS) 17 version = COMPILE; 18 19 version (COMPILE) 20 { 21 22 import core.stdc.stdio; 23 import core.stdc.stdlib; 24 import core.stdc..string; 25 26 import dmd.backend.barray; 27 import dmd.backend.cc; 28 import dmd.backend.cdef; 29 import dmd.backend.code; 30 import dmd.backend.code_x86; 31 import dmd.backend.codebuilder; 32 import dmd.backend.mem; 33 import dmd.backend.el; 34 import dmd.backend.global; 35 import dmd.backend.oper; 36 import dmd.backend.ty; 37 import dmd.backend.evalu8 : el_toldoubled; 38 39 extern (C++): 40 41 nothrow: 42 43 // NOTE: this could be a TLS global which would allow this variable to be used in 44 // a multi-threaded version of the backend 45 __gshared Globals87 global87; 46 47 private: 48 49 int REGSIZE(); 50 51 private extern (D) uint mask(uint m) { return 1 << m; } 52 void callcdxxx(ref CodeBuilder cdb, elem *e, regm_t *pretregs, OPER op); 53 54 55 // Constants that the 8087 supports directly 56 // BUG: rewrite for 80 bit long doubles 57 enum PI = 3.14159265358979323846; 58 enum LOG2 = 0.30102999566398119521; 59 enum LN2 = 0.6931471805599453094172321; 60 enum LOG2T = 3.32192809488736234787; 61 enum LOG2E = 1.4426950408889634074; // 1/LN2 62 63 enum FWAIT = 0x9B; // FWAIT opcode 64 65 /* Mark variable referenced by e as not a register candidate */ 66 uint notreg(elem* e) { return e.EV.Vsym.Sflags &= ~GTregcand; } 67 68 /* Generate the appropriate ESC instruction */ 69 ubyte ESC(uint MF, uint b) { return cast(ubyte)(0xD8 + (MF << 1) + b); } 70 enum 71 { // Values for MF 72 MFfloat = 0, 73 MFlong = 1, 74 MFdouble = 2, 75 MFword = 3 76 } 77 78 /********************************* 79 */ 80 81 struct Dconst 82 { 83 int round; 84 Symbol *roundto0; 85 Symbol *roundtonearest; 86 } 87 88 private __gshared Dconst oldd; 89 90 enum NDPP = 0; // print out debugging info 91 bool NOSAHF() { return I64 || config.fpxmmregs; } // can't use SAHF instruction 92 93 enum CW_roundto0 = 0xFBF; 94 enum CW_roundtonearest = 0x3BF; 95 96 97 /********************************** 98 * When we need to temporarilly save 8087 registers, we record information 99 * about the save into an array of NDP structs. 100 */ 101 102 private void getlvalue87(ref CodeBuilder cdb,code *pcs,elem *e,regm_t keepmsk) 103 { 104 // the x87 instructions cannot read XMM registers 105 if (e.Eoper == OPvar || e.Eoper == OPrelconst) 106 e.EV.Vsym.Sflags &= ~GTregcand; 107 108 getlvalue(cdb, pcs, e, keepmsk); 109 if (ADDFWAIT()) 110 pcs.Iflags |= CFwait; 111 if (I32) 112 pcs.Iflags &= ~CFopsize; 113 else if (I64) 114 pcs.Irex &= ~REX_W; 115 } 116 117 /**************************************** 118 * Store/load to ndp save location i 119 */ 120 121 private void ndp_fstp(ref CodeBuilder cdb, int i, tym_t ty) 122 { 123 switch (tybasic(ty)) 124 { 125 case TYfloat: 126 case TYifloat: 127 case TYcfloat: 128 cdb.genc1(0xD9,modregrm(2,3,BPRM),FLndp,i); // FSTP m32real i[BP] 129 break; 130 131 case TYdouble: 132 case TYdouble_alias: 133 case TYidouble: 134 case TYcdouble: 135 cdb.genc1(0xDD,modregrm(2,3,BPRM),FLndp,i); // FSTP m64real i[BP] 136 break; 137 138 case TYldouble: 139 case TYildouble: 140 case TYcldouble: 141 cdb.genc1(0xDB,modregrm(2,7,BPRM),FLndp,i); // FSTP m80real i[BP] 142 break; 143 144 default: 145 assert(0); 146 } 147 } 148 149 private void ndp_fld(ref CodeBuilder cdb, int i, tym_t ty) 150 { 151 switch (tybasic(ty)) 152 { 153 case TYfloat: 154 case TYifloat: 155 case TYcfloat: 156 cdb.genc1(0xD9,modregrm(2,0,BPRM),FLndp,i); 157 break; 158 159 case TYdouble: 160 case TYdouble_alias: 161 case TYidouble: 162 case TYcdouble: 163 cdb.genc1(0xDD,modregrm(2,0,BPRM),FLndp,i); 164 break; 165 166 case TYldouble: 167 case TYildouble: 168 case TYcldouble: 169 cdb.genc1(0xDB,modregrm(2,5,BPRM),FLndp,i); // FLD m80real i[BP] 170 break; 171 172 default: 173 assert(0); 174 } 175 } 176 177 /************************** 178 * Return index of empty slot in global87.save[]. 179 */ 180 181 private int getemptyslot() 182 { 183 int i; 184 185 for (i = 0; i < global87.save.length; ++i) 186 if (global87.save[i].e == null) 187 return i; 188 189 global87.save.push(NDP()); 190 return i; 191 } 192 193 /********************************* 194 * Pop 8087 stack. 195 */ 196 197 void pop87() { pop87(__LINE__, __FILE__); } 198 199 void pop87(int line, const(char)* file) 200 { 201 int i; 202 203 if (NDPP) 204 printf("pop87(%s(%d): stackused=%d)\n", file, line, global87.stackused); 205 206 --global87.stackused; 207 assert(global87.stackused >= 0); 208 for (i = 0; i < global87.stack.length - 1; i++) 209 global87.stack[i] = global87.stack[i + 1]; 210 // end of stack is nothing 211 global87.stack[$ - 1] = NDP(); 212 } 213 214 215 /******************************* 216 * Push 8087 stack. Generate and return any code 217 * necessary to preserve anything that might run off the end of the stack. 218 */ 219 220 void push87(ref CodeBuilder cdb) { push87(cdb,__LINE__,__FILE__); } 221 222 void push87(ref CodeBuilder cdb, int line, const(char)* file) 223 { 224 // if we would lose the top register off of the stack 225 if (global87.stack[7].e != null) 226 { 227 int i = getemptyslot(); 228 global87.save[i] = global87.stack[7]; 229 cdb.genf2(0xD9,0xF6); // FDECSTP 230 genfwait(cdb); 231 ndp_fstp(cdb, i, global87.stack[7].e.Ety); // FSTP i[BP] 232 assert(global87.stackused == 8); 233 if (NDPP) printf("push87() : overflow\n"); 234 } 235 else 236 { 237 if (NDPP) printf("push87(%s(%d): %d)\n", file, line, global87.stackused); 238 global87.stackused++; 239 assert(global87.stackused <= 8); 240 } 241 // Shift the stack up 242 for (int i = 7; i > 0; i--) 243 global87.stack[i] = global87.stack[i - 1]; 244 global87.stack[0] = NDP(); 245 } 246 247 /***************************** 248 * Note elem e as being in ST(i) as being a value we want to keep. 249 */ 250 251 void note87(elem *e, uint offset, int i) 252 { 253 note87(e, offset, i, __LINE__); 254 } 255 256 void note87(elem *e, uint offset, int i, int linnum) 257 { 258 if (NDPP) 259 printf("note87(e = %p.%d, i = %d, stackused = %d, line = %d)\n",e,offset,i,global87.stackused,linnum); 260 261 static if (0) 262 { 263 if (global87.stack[i].e) 264 printf("global87.stack[%d].e = %p\n",i,global87.stack[i].e); 265 } 266 267 debug if (i >= global87.stackused) 268 { 269 printf("note87(e = %p.%d, i = %d, stackused = %d, line = %d)\n",e,offset,i,global87.stackused,linnum); 270 elem_print(e); 271 } 272 assert(i < global87.stackused); 273 274 while (e.Eoper == OPcomma) 275 e = e.EV.E2; 276 global87.stack[i].e = e; 277 global87.stack[i].offset = offset; 278 } 279 280 /**************************************************** 281 * Exchange two entries in 8087 stack. 282 */ 283 284 void xchg87(int i, int j) 285 { 286 NDP save; 287 288 save = global87.stack[i]; 289 global87.stack[i] = global87.stack[j]; 290 global87.stack[j] = save; 291 } 292 293 /**************************** 294 * Make sure that elem e is in register ST(i). Reload it if necessary. 295 * Input: 296 * i 0..3 8087 register number 297 * flag 1 don't bother with FXCH 298 */ 299 300 private void makesure87(ref CodeBuilder cdb,elem *e,uint offset,int i,uint flag) 301 { 302 makesure87(cdb,e,offset,i,flag,__LINE__); 303 } 304 305 private void makesure87(ref CodeBuilder cdb,elem *e,uint offset,int i,uint flag,int linnum) 306 { 307 debug if (NDPP) printf("makesure87(e=%p, offset=%d, i=%d, flag=%d, line=%d)\n",e,offset,i,flag,linnum); 308 309 while (e.Eoper == OPcomma) 310 e = e.EV.E2; 311 assert(e && i < 4); 312 L1: 313 if (global87.stack[i].e != e || global87.stack[i].offset != offset) 314 { 315 debug if (global87.stack[i].e) 316 printf("global87.stack[%d].e = %p, .offset = %d\n",i,global87.stack[i].e,global87.stack[i].offset); 317 318 assert(global87.stack[i].e == null); 319 int j; 320 for (j = 0; 1; j++) 321 { 322 if (j >= global87.save.length && e.Eoper == OPcomma) 323 { 324 e = e.EV.E2; // try right side 325 goto L1; 326 } 327 328 debug if (j >= global87.save.length) 329 printf("e = %p, global87.save.length = %llu\n",e, cast(ulong) global87.save.length); 330 331 assert(j < global87.save.length); 332 //printf("\tglobal87.save[%d] = %p, .offset = %d\n", j, global87.save[j].e, global87.save[j].offset); 333 if (e == global87.save[j].e && offset == global87.save[j].offset) 334 break; 335 } 336 push87(cdb); 337 genfwait(cdb); 338 ndp_fld(cdb, j, e.Ety); // FLD j[BP] 339 if (!(flag & 1)) 340 { 341 while (i != 0) 342 { 343 cdb.genf2(0xD9,0xC8 + i); // FXCH ST(i) 344 i--; 345 } 346 } 347 global87.save[j] = NDP(); // back in 8087 348 } 349 //global87.stack[i].e = null; 350 } 351 352 /**************************** 353 * Save in memory any values in the 8087 that we want to keep. 354 */ 355 356 void save87(ref CodeBuilder cdb) 357 { 358 bool any = false; 359 while (global87.stack[0].e && global87.stackused) 360 { 361 // Save it 362 int i = getemptyslot(); 363 if (NDPP) printf("saving %p in temporary global87.save[%d]\n",global87.stack[0].e,i); 364 global87.save[i] = global87.stack[0]; 365 366 genfwait(cdb); 367 ndp_fstp(cdb,i,global87.stack[0].e.Ety); // FSTP i[BP] 368 pop87(); 369 any = true; 370 } 371 if (any) // if any stores 372 genfwait(cdb); // wait for last one to finish 373 } 374 375 /****************************************** 376 * Save any noted values that would be destroyed by n pushes 377 */ 378 379 void save87regs(ref CodeBuilder cdb, uint n) 380 { 381 assert(n <= 7); 382 uint j = 8 - n; 383 if (global87.stackused > j) 384 { 385 for (uint k = 8; k > j; k--) 386 { 387 cdb.genf2(0xD9,0xF6); // FDECSTP 388 genfwait(cdb); 389 if (k <= global87.stackused) 390 { 391 int i = getemptyslot(); 392 ndp_fstp(cdb, i, global87.stack[k - 1].e.Ety); // FSTP i[BP] 393 global87.save[i] = global87.stack[k - 1]; 394 global87.stack[k - 1] = NDP(); 395 } 396 } 397 398 for (uint k = 8; k > j; k--) 399 { 400 if (k > global87.stackused) 401 { cdb.genf2(0xD9,0xF7); // FINCSTP 402 genfwait(cdb); 403 } 404 } 405 global87.stackused = j; 406 } 407 } 408 409 /***************************************************** 410 * Save/restore ST0 or ST01 411 */ 412 413 void gensaverestore87(regm_t regm, ref CodeBuilder cdbsave, ref CodeBuilder cdbrestore) 414 { 415 //printf("gensaverestore87(%s)\n", regm_str(regm)); 416 assert(regm == mST0 || regm == mST01); 417 418 int i = getemptyslot(); 419 global87.save[i].e = el_calloc(); // this blocks slot [i] for the life of this function 420 ndp_fstp(cdbsave, i, TYldouble); 421 422 CodeBuilder cdb2a; 423 cdb2a.ctor(); 424 ndp_fld(cdb2a, i, TYldouble); 425 426 if (regm == mST01) 427 { 428 int j = getemptyslot(); 429 global87.save[j].e = el_calloc(); 430 ndp_fstp(cdbsave, j, TYldouble); 431 ndp_fld(cdbrestore, j, TYldouble); 432 } 433 434 cdbrestore.append(cdb2a); 435 } 436 437 /************************************* 438 * Find which, if any, slot on stack holds elem e. 439 */ 440 441 private int cse_get(elem *e, uint offset) 442 { 443 int i; 444 445 for (i = 0; 1; i++) 446 { 447 if (i == global87.stackused) 448 { 449 i = -1; 450 //printf("cse not found\n"); 451 //elem_print(e); 452 break; 453 } 454 if (global87.stack[i].e == e && 455 global87.stack[i].offset == offset) 456 { //printf("cse found %d\n",i); 457 //elem_print(e); 458 break; 459 } 460 } 461 return i; 462 } 463 464 /************************************* 465 * Reload common subexpression. 466 */ 467 468 void comsub87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 469 { 470 //printf("comsub87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 471 // Look on 8087 stack 472 int i = cse_get(e, 0); 473 474 if (tycomplex(e.Ety)) 475 { 476 uint sz = tysize(e.Ety); 477 int j = cse_get(e, sz / 2); 478 if (i >= 0 && j >= 0) 479 { 480 push87(cdb); 481 push87(cdb); 482 cdb.genf2(0xD9,0xC0 + i); // FLD ST(i) 483 cdb.genf2(0xD9,0xC0 + j + 1); // FLD ST(j + 1) 484 fixresult_complex87(cdb,e,mST01,pretregs); 485 } 486 else 487 // Reload 488 loaddata(cdb,e,pretregs); 489 } 490 else 491 { 492 if (i >= 0) 493 { 494 push87(cdb); 495 cdb.genf2(0xD9,0xC0 + i); // FLD ST(i) 496 if (*pretregs & XMMREGS) 497 fixresult87(cdb,e,mST0,pretregs); 498 else 499 fixresult(cdb,e,mST0,pretregs); 500 } 501 else 502 // Reload 503 loaddata(cdb,e,pretregs); 504 } 505 } 506 507 508 /******************************* 509 * Decide if we need to gen an FWAIT. 510 */ 511 512 void genfwait(ref CodeBuilder cdb) 513 { 514 if (ADDFWAIT()) 515 cdb.gen1(FWAIT); 516 } 517 518 519 /*************************** 520 * Put the 8087 flags into the CPU flags. 521 */ 522 523 private void cg87_87topsw(ref CodeBuilder cdb) 524 { 525 /* Note that SAHF is not available on some early I64 processors 526 * and will cause a seg fault 527 */ 528 assert(!NOSAHF); 529 getregs(cdb,mAX); 530 if (config.target_cpu >= TARGET_80286) 531 cdb.genf2(0xDF,0xE0); // FSTSW AX 532 else 533 { 534 cdb.genfltreg(0xD8+5,7,0); // FSTSW floatreg[BP] 535 genfwait(cdb); // FWAIT 536 cdb.genfltreg(0x8A,4,1); // MOV AH,floatreg+1[BP] 537 } 538 cdb.gen1(0x9E); // SAHF 539 code_orflag(cdb.last(),CFpsw); 540 } 541 542 /***************************************** 543 * Jump to ctarget if condition code C2 is set. 544 */ 545 546 private void genjmpifC2(ref CodeBuilder cdb, code *ctarget) 547 { 548 if (NOSAHF) 549 { 550 getregs(cdb,mAX); 551 cdb.genf2(0xDF,0xE0); // FSTSW AX 552 cdb.genc2(0xF6,modregrm(3,0,4),4); // TEST AH,4 553 genjmp(cdb, JNE, FLcode, cast(block *)ctarget); // JNE ctarget 554 } 555 else 556 { 557 cg87_87topsw(cdb); 558 genjmp(cdb, JP, FLcode, cast(block *)ctarget); // JP ctarget 559 } 560 } 561 562 /*************************** 563 * Set the PSW based on the state of ST0. 564 * Input: 565 * pop if stack should be popped after test 566 * Returns: 567 * start of code appended to c. 568 */ 569 570 private void genftst(ref CodeBuilder cdb,elem *e,int pop) 571 { 572 if (NOSAHF) 573 { 574 push87(cdb); 575 cdb.gen2(0xD9,0xEE); // FLDZ 576 cdb.gen2(0xDF,0xE9); // FUCOMIP ST1 577 pop87(); 578 if (pop) 579 { 580 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 581 pop87(); 582 } 583 } 584 else if (config.flags4 & CFG4fastfloat) // if fast floating point 585 { 586 cdb.genf2(0xD9,0xE4); // FTST 587 cg87_87topsw(cdb); // put 8087 flags in CPU flags 588 if (pop) 589 { 590 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 591 pop87(); 592 } 593 } 594 else if (config.target_cpu >= TARGET_80386) 595 { 596 // FUCOMP doesn't raise exceptions on QNANs, unlike FTST 597 push87(cdb); 598 cdb.gen2(0xD9,0xEE); // FLDZ 599 cdb.gen2(pop ? 0xDA : 0xDD,0xE9); // FUCOMPP / FUCOMP 600 pop87(); 601 if (pop) 602 pop87(); 603 cg87_87topsw(cdb); // put 8087 flags in CPU flags 604 } 605 else 606 { 607 // Call library function which does not raise exceptions 608 regm_t regm = 0; 609 610 callclib(cdb,e,CLIB.ftest,®m,0); 611 if (pop) 612 { 613 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 614 pop87(); 615 } 616 } 617 } 618 619 /************************************* 620 * Determine if there is a special 8087 instruction to load 621 * constant e. 622 * Input: 623 * im 0 load real part 624 * 1 load imaginary part 625 * Returns: 626 * opcode if found 627 * 0 if not 628 */ 629 630 ubyte loadconst(elem *e, int im) 631 { 632 elem_debug(e); 633 assert(im == 0 || im == 1); 634 635 immutable float[7] fval = 636 [0.0,1.0,PI,LOG2T,LOG2E,LOG2,LN2]; 637 immutable double[7] dval = 638 [0.0,1.0,PI,LOG2T,LOG2E,LOG2,LN2]; 639 640 static if (real.sizeof < 10) 641 { 642 import dmd.root.longdouble; 643 immutable targ_ldouble[7] ldval = 644 [ld_zero,ld_one,ld_pi,ld_log2t,ld_log2e,ld_log2,ld_ln2]; 645 } 646 else 647 { 648 enum M_PI_L = 0x1.921fb54442d1846ap+1L; // 3.14159 fldpi 649 enum M_LOG2T_L = 0x1.a934f0979a3715fcp+1L; // 3.32193 fldl2t 650 enum M_LOG2E_L = 0x1.71547652b82fe178p+0L; // 1.4427 fldl2e 651 enum M_LOG2_L = 0x1.34413509f79fef32p-2L; // 0.30103 fldlg2 652 enum M_LN2_L = 0x1.62e42fefa39ef358p-1L; // 0.693147 fldln2 653 immutable targ_ldouble[7] ldval = 654 [0.0,1.0,M_PI_L,M_LOG2T_L,M_LOG2E_L,M_LOG2_L,M_LN2_L]; 655 } 656 657 immutable ubyte[7 + 1] opcode = 658 /* FLDZ,FLD1,FLDPI,FLDL2T,FLDL2E,FLDLG2,FLDLN2,0 */ 659 [0xEE,0xE8,0xEB,0xE9,0xEA,0xEC,0xED,0]; 660 661 int i; 662 targ_float f; 663 targ_double d; 664 targ_ldouble ld; 665 int sz; 666 int zero; 667 void *p; 668 immutable ubyte[16] zeros; 669 670 if (im == 0) 671 { 672 switch (tybasic(e.Ety)) 673 { 674 case TYfloat: 675 case TYifloat: 676 case TYcfloat: 677 f = e.EV.Vfloat; 678 sz = 4; 679 p = &f; 680 break; 681 682 case TYdouble: 683 case TYdouble_alias: 684 case TYidouble: 685 case TYcdouble: 686 d = e.EV.Vdouble; 687 sz = 8; 688 p = &d; 689 break; 690 691 case TYldouble: 692 case TYildouble: 693 case TYcldouble: 694 ld = e.EV.Vldouble; 695 sz = 10; 696 p = &ld; 697 break; 698 699 default: 700 assert(0); 701 } 702 } 703 else 704 { 705 switch (tybasic(e.Ety)) 706 { 707 case TYcfloat: 708 f = e.EV.Vcfloat.im; 709 sz = 4; 710 p = &f; 711 break; 712 713 case TYcdouble: 714 d = e.EV.Vcdouble.im; 715 sz = 8; 716 p = &d; 717 break; 718 719 case TYcldouble: 720 ld = e.EV.Vcldouble.im; 721 sz = 10; 722 p = &ld; 723 break; 724 725 default: 726 assert(0); 727 } 728 } 729 730 // Note that for this purpose, -0 is not regarded as +0, 731 // since FLDZ loads a +0 732 assert(sz <= zeros.length); 733 zero = (memcmp(p, zeros.ptr, sz) == 0); 734 if (zero && config.target_cpu >= TARGET_PentiumPro) 735 return 0xEE; // FLDZ is the only one with 1 micro-op 736 737 // For some reason, these instructions take more clocks 738 if (config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) 739 return 0; 740 741 if (zero) 742 return 0xEE; 743 744 for (i = 1; i < fval.length; i++) 745 { 746 switch (sz) 747 { 748 case 4: 749 if (fval[i] != f) 750 continue; 751 break; 752 case 8: 753 if (dval[i] != d) 754 continue; 755 break; 756 case 10: 757 if (ldval[i] != ld) 758 continue; 759 break; 760 default: 761 assert(0); 762 } 763 break; 764 } 765 return opcode[i]; 766 } 767 768 /****************************** 769 * Given the result of an expression is in retregs, 770 * generate necessary code to return result in *pretregs. 771 */ 772 773 774 void fixresult87(ref CodeBuilder cdb,elem *e,regm_t retregs,regm_t *pretregs) 775 { 776 //printf("fixresult87(e = %p, retregs = x%x, *pretregs = x%x)\n", e,retregs,*pretregs); 777 //printf("fixresult87(e = %p, retregs = %s, *pretregs = %s)\n", e,regm_str(retregs),regm_str(*pretregs)); 778 assert(!*pretregs || retregs); 779 780 if (*pretregs & mST01) 781 { 782 fixresult_complex87(cdb, e, retregs, pretregs); 783 return; 784 } 785 786 tym_t tym = tybasic(e.Ety); 787 uint sz = _tysize[tym]; 788 //printf("tym = x%x, sz = %d\n", tym, sz); 789 790 /* if retregs needs to be transferred into the 8087 */ 791 if (*pretregs & mST0 && retregs & (mBP | ALLREGS)) 792 { 793 debug if (sz > DOUBLESIZE) 794 { 795 elem_print(e); 796 printf("retregs = %s\n", regm_str(retregs)); 797 } 798 assert(sz <= DOUBLESIZE); 799 if (!I16) 800 { 801 802 if (*pretregs & mPSW) 803 { // Set flags 804 regm_t r = retregs | mPSW; 805 fixresult(cdb,e,retregs,&r); 806 } 807 push87(cdb); 808 if (sz == REGSIZE || (I64 && sz == 4)) 809 { 810 const reg = findreg(retregs); 811 cdb.genfltreg(STO,reg,0); // MOV fltreg,reg 812 cdb.genfltreg(0xD9,0,0); // FLD float ptr fltreg 813 } 814 else 815 { 816 const msreg = findregmsw(retregs); 817 const lsreg = findreglsw(retregs); 818 cdb.genfltreg(STO,lsreg,0); // MOV fltreg,lsreg 819 cdb.genfltreg(STO,msreg,4); // MOV fltreg+4,msreg 820 cdb.genfltreg(0xDD,0,0); // FLD double ptr fltreg 821 } 822 } 823 else 824 { 825 regm_t regm = (sz == FLOATSIZE) ? FLOATREGS : DOUBLEREGS; 826 regm |= *pretregs & mPSW; 827 fixresult(cdb,e,retregs,®m); 828 regm = 0; // don't worry about result from CLIB.xxx 829 callclib(cdb,e, 830 ((sz == FLOATSIZE) ? CLIB.fltto87 : CLIB.dblto87), 831 ®m,0); 832 } 833 } 834 else if (*pretregs & (mBP | ALLREGS) && retregs & mST0) 835 { 836 assert(sz <= DOUBLESIZE); 837 uint mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; 838 if (*pretregs & mPSW && !(retregs & mPSW)) 839 genftst(cdb,e,0); 840 // FSTP floatreg 841 pop87(); 842 cdb.genfltreg(ESC(mf,1),3,0); 843 genfwait(cdb); 844 reg_t reg; 845 allocreg(cdb,pretregs,®,(sz == FLOATSIZE) ? TYfloat : TYdouble); 846 if (sz == FLOATSIZE) 847 { 848 if (!I16) 849 cdb.genfltreg(LOD,reg,0); 850 else 851 { 852 cdb.genfltreg(LOD,reg,REGSIZE); 853 cdb.genfltreg(LOD,findreglsw(*pretregs),0); 854 } 855 } 856 else 857 { assert(sz == DOUBLESIZE); 858 if (I16) 859 { 860 cdb.genfltreg(LOD,AX,6); 861 cdb.genfltreg(LOD,BX,4); 862 cdb.genfltreg(LOD,CX,2); 863 cdb.genfltreg(LOD,DX,0); 864 } 865 else if (I32) 866 { 867 cdb.genfltreg(LOD,reg,REGSIZE); 868 cdb.genfltreg(LOD,findreglsw(*pretregs),0); 869 } 870 else // I64 871 { 872 cdb.genfltreg(LOD,reg,0); 873 code_orrex(cdb.last(), REX_W); 874 } 875 } 876 } 877 else if (*pretregs == 0 && retregs == mST0) 878 { 879 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 880 pop87(); 881 } 882 else 883 { 884 if (*pretregs & mPSW) 885 { 886 if (!(retregs & mPSW)) 887 { 888 genftst(cdb,e,!(*pretregs & (mST0 | XMMREGS))); // FTST 889 } 890 } 891 if (*pretregs & mST0 && retregs & XMMREGS) 892 { 893 assert(sz <= DOUBLESIZE); 894 uint mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; 895 // MOVD floatreg,XMM? 896 const reg = findreg(retregs); 897 cdb.genxmmreg(xmmstore(tym),reg,0,tym); 898 push87(cdb); 899 cdb.genfltreg(ESC(mf,1),0,0); // FLD float/double ptr fltreg 900 } 901 else if (retregs & mST0 && *pretregs & XMMREGS) 902 { 903 assert(sz <= DOUBLESIZE); 904 uint mf = (sz == FLOATSIZE) ? MFfloat : MFdouble; 905 // FSTP floatreg 906 pop87(); 907 cdb.genfltreg(ESC(mf,1),3,0); 908 genfwait(cdb); 909 // MOVD XMM?,floatreg 910 reg_t reg; 911 allocreg(cdb,pretregs,®,(sz == FLOATSIZE) ? TYfloat : TYdouble); 912 cdb.genxmmreg(xmmload(tym),reg,0,tym); 913 } 914 else 915 assert(!(*pretregs & mST0) || (retregs & mST0)); 916 } 917 if (*pretregs & mST0) 918 note87(e,0,0); 919 } 920 921 /******************************** 922 * Generate in-line 8087 code for the following operators: 923 * add 924 * min 925 * mul 926 * div 927 * cmp 928 */ 929 930 // Reverse the order that the op is done in 931 __gshared const ubyte[9] oprev = [ cast(ubyte)-1,0,1,2,3,5,4,7,6 ]; 932 933 void orth87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 934 { 935 //printf("orth87(+e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 936 // we could be evaluating / for side effects only 937 assert(*pretregs != 0); 938 939 elem *e1 = e.EV.E1; 940 elem *e2 = e.EV.E2; 941 uint sz2 = tysize(e1.Ety); 942 if (tycomplex(e1.Ety)) 943 sz2 /= 2; 944 945 OPER eoper = e.Eoper; 946 if (eoper == OPmul && e2.Eoper == OPconst && el_toldoubled(e.EV.E2) == 2.0L) 947 { 948 // Perform "mul 2.0" as fadd ST(0), ST 949 regm_t retregs = mST0; 950 codelem(cdb,e1,&retregs,false); 951 cdb.genf2(0xDC, 0xC0); // fadd ST(0), ST; 952 fixresult87(cdb,e,mST0,pretregs); // result is in ST(0). 953 freenode(e2); 954 return; 955 } 956 957 uint op; 958 if (OTrel(eoper)) 959 eoper = OPeqeq; 960 bool imaginary; 961 static uint X(OPER op, uint ty1, uint ty2) { return (op << 16) + ty1 * 256 + ty2; } 962 switch (X(eoper, tybasic(e1.Ety), tybasic(e2.Ety))) 963 { 964 case X(OPadd, TYfloat, TYfloat): 965 case X(OPadd, TYdouble, TYdouble): 966 case X(OPadd, TYdouble_alias, TYdouble_alias): 967 case X(OPadd, TYldouble, TYldouble): 968 case X(OPadd, TYldouble, TYdouble): 969 case X(OPadd, TYdouble, TYldouble): 970 case X(OPadd, TYifloat, TYifloat): 971 case X(OPadd, TYidouble, TYidouble): 972 case X(OPadd, TYildouble, TYildouble): 973 op = 0; // FADDP 974 break; 975 976 case X(OPmin, TYfloat, TYfloat): 977 case X(OPmin, TYdouble, TYdouble): 978 case X(OPmin, TYdouble_alias, TYdouble_alias): 979 case X(OPmin, TYldouble, TYldouble): 980 case X(OPmin, TYldouble, TYdouble): 981 case X(OPmin, TYdouble, TYldouble): 982 case X(OPmin, TYifloat, TYifloat): 983 case X(OPmin, TYidouble, TYidouble): 984 case X(OPmin, TYildouble, TYildouble): 985 op = 4; // FSUBP 986 break; 987 988 case X(OPmul, TYfloat, TYfloat): 989 case X(OPmul, TYdouble, TYdouble): 990 case X(OPmul, TYdouble_alias, TYdouble_alias): 991 case X(OPmul, TYldouble, TYldouble): 992 case X(OPmul, TYldouble, TYdouble): 993 case X(OPmul, TYdouble, TYldouble): 994 case X(OPmul, TYifloat, TYifloat): 995 case X(OPmul, TYidouble, TYidouble): 996 case X(OPmul, TYildouble, TYildouble): 997 case X(OPmul, TYfloat, TYifloat): 998 case X(OPmul, TYdouble, TYidouble): 999 case X(OPmul, TYldouble, TYildouble): 1000 case X(OPmul, TYifloat, TYfloat): 1001 case X(OPmul, TYidouble, TYdouble): 1002 case X(OPmul, TYildouble, TYldouble): 1003 op = 1; // FMULP 1004 break; 1005 1006 case X(OPdiv, TYfloat, TYfloat): 1007 case X(OPdiv, TYdouble, TYdouble): 1008 case X(OPdiv, TYdouble_alias, TYdouble_alias): 1009 case X(OPdiv, TYldouble, TYldouble): 1010 case X(OPdiv, TYldouble, TYdouble): 1011 case X(OPdiv, TYdouble, TYldouble): 1012 case X(OPdiv, TYifloat, TYifloat): 1013 case X(OPdiv, TYidouble, TYidouble): 1014 case X(OPdiv, TYildouble, TYildouble): 1015 op = 6; // FDIVP 1016 break; 1017 1018 case X(OPmod, TYfloat, TYfloat): 1019 case X(OPmod, TYdouble, TYdouble): 1020 case X(OPmod, TYdouble_alias, TYdouble_alias): 1021 case X(OPmod, TYldouble, TYldouble): 1022 case X(OPmod, TYfloat, TYifloat): 1023 case X(OPmod, TYdouble, TYidouble): 1024 case X(OPmod, TYldouble, TYildouble): 1025 case X(OPmod, TYifloat, TYifloat): 1026 case X(OPmod, TYidouble, TYidouble): 1027 case X(OPmod, TYildouble, TYildouble): 1028 case X(OPmod, TYifloat, TYfloat): 1029 case X(OPmod, TYidouble, TYdouble): 1030 case X(OPmod, TYildouble, TYldouble): 1031 op = cast(uint) -1; 1032 break; 1033 1034 case X(OPeqeq, TYfloat, TYfloat): 1035 case X(OPeqeq, TYdouble, TYdouble): 1036 case X(OPeqeq, TYdouble_alias, TYdouble_alias): 1037 case X(OPeqeq, TYldouble, TYldouble): 1038 case X(OPeqeq, TYifloat, TYifloat): 1039 case X(OPeqeq, TYidouble, TYidouble): 1040 case X(OPeqeq, TYildouble, TYildouble): 1041 { 1042 assert(OTrel(e.Eoper)); 1043 assert((*pretregs & mST0) == 0); 1044 regm_t retregs = mST0; 1045 codelem(cdb,e1,&retregs,false); 1046 note87(e1,0,0); 1047 regm_t resregm = mPSW; 1048 1049 if (rel_exception(e.Eoper) || config.flags4 & CFG4fastfloat) 1050 { 1051 if (e2.Eoper == OPconst && !boolres(e2)) 1052 { 1053 if (NOSAHF) 1054 { 1055 push87(cdb); 1056 cdb.gen2(0xD9,0xEE); // FLDZ 1057 cdb.gen2(0xDF,0xF1); // FCOMIP ST1 1058 pop87(); 1059 } 1060 else 1061 { 1062 cdb.genf2(0xD9,0xE4); // FTST 1063 cg87_87topsw(cdb); 1064 } 1065 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 1066 pop87(); 1067 } 1068 else if (NOSAHF) 1069 { 1070 note87(e1,0,0); 1071 load87(cdb,e2,0,&retregs,e1,-1); 1072 makesure87(cdb,e1,0,1,0); 1073 resregm = 0; 1074 //cdb.genf2(0xD9,0xC8 + 1); // FXCH ST1 1075 cdb.gen2(0xDF,0xF1); // FCOMIP ST1 1076 pop87(); 1077 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 1078 pop87(); 1079 } 1080 else 1081 { 1082 load87(cdb,e2, 0, pretregs, e1, 3); // FCOMPP 1083 } 1084 } 1085 else 1086 { 1087 if (e2.Eoper == OPconst && !boolres(e2) && 1088 config.target_cpu < TARGET_80386) 1089 { 1090 regm_t regm = 0; 1091 1092 callclib(cdb,e,CLIB.ftest0,®m,0); 1093 pop87(); 1094 } 1095 else 1096 { 1097 note87(e1,0,0); 1098 load87(cdb,e2,0,&retregs,e1,-1); 1099 makesure87(cdb,e1,0,1,0); 1100 resregm = 0; 1101 if (NOSAHF) 1102 { 1103 cdb.gen2(0xDF,0xE9); // FUCOMIP ST1 1104 pop87(); 1105 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 1106 pop87(); 1107 } 1108 else if (config.target_cpu >= TARGET_80386) 1109 { 1110 cdb.gen2(0xDA,0xE9); // FUCOMPP 1111 cg87_87topsw(cdb); 1112 pop87(); 1113 pop87(); 1114 } 1115 else 1116 // Call a function instead so that exceptions 1117 // are not generated. 1118 callclib(cdb,e,CLIB.fcompp,&resregm,0); 1119 } 1120 } 1121 1122 freenode(e2); 1123 return; 1124 } 1125 1126 case X(OPadd, TYcfloat, TYcfloat): 1127 case X(OPadd, TYcdouble, TYcdouble): 1128 case X(OPadd, TYcldouble, TYcldouble): 1129 case X(OPadd, TYcfloat, TYfloat): 1130 case X(OPadd, TYcdouble, TYdouble): 1131 case X(OPadd, TYcldouble, TYldouble): 1132 case X(OPadd, TYfloat, TYcfloat): 1133 case X(OPadd, TYdouble, TYcdouble): 1134 case X(OPadd, TYldouble, TYcldouble): 1135 goto Lcomplex; 1136 1137 case X(OPadd, TYifloat, TYcfloat): 1138 case X(OPadd, TYidouble, TYcdouble): 1139 case X(OPadd, TYildouble, TYcldouble): 1140 goto Lcomplex2; 1141 1142 case X(OPmin, TYcfloat, TYcfloat): 1143 case X(OPmin, TYcdouble, TYcdouble): 1144 case X(OPmin, TYcldouble, TYcldouble): 1145 case X(OPmin, TYcfloat, TYfloat): 1146 case X(OPmin, TYcdouble, TYdouble): 1147 case X(OPmin, TYcldouble, TYldouble): 1148 case X(OPmin, TYfloat, TYcfloat): 1149 case X(OPmin, TYdouble, TYcdouble): 1150 case X(OPmin, TYldouble, TYcldouble): 1151 goto Lcomplex; 1152 1153 case X(OPmin, TYifloat, TYcfloat): 1154 case X(OPmin, TYidouble, TYcdouble): 1155 case X(OPmin, TYildouble, TYcldouble): 1156 goto Lcomplex2; 1157 1158 case X(OPmul, TYcfloat, TYcfloat): 1159 case X(OPmul, TYcdouble, TYcdouble): 1160 case X(OPmul, TYcldouble, TYcldouble): 1161 goto Lcomplex; 1162 1163 case X(OPdiv, TYcfloat, TYcfloat): 1164 case X(OPdiv, TYcdouble, TYcdouble): 1165 case X(OPdiv, TYcldouble, TYcldouble): 1166 case X(OPdiv, TYfloat, TYcfloat): 1167 case X(OPdiv, TYdouble, TYcdouble): 1168 case X(OPdiv, TYldouble, TYcldouble): 1169 case X(OPdiv, TYifloat, TYcfloat): 1170 case X(OPdiv, TYidouble, TYcdouble): 1171 case X(OPdiv, TYildouble, TYcldouble): 1172 goto Lcomplex; 1173 1174 case X(OPdiv, TYifloat, TYfloat): 1175 case X(OPdiv, TYidouble, TYdouble): 1176 case X(OPdiv, TYildouble, TYldouble): 1177 op = 6; // FDIVP 1178 break; 1179 1180 Lcomplex: 1181 { 1182 loadComplex(cdb,e1); 1183 loadComplex(cdb,e2); 1184 makesure87(cdb, e1, sz2, 2, 0); 1185 makesure87(cdb, e1, 0, 3, 0); 1186 regm_t retregs = mST01; 1187 if (eoper == OPadd) 1188 { 1189 cdb.genf2(0xDE, 0xC0+2); // FADDP ST(2),ST 1190 cdb.genf2(0xDE, 0xC0+2); // FADDP ST(2),ST 1191 pop87(); 1192 pop87(); 1193 } 1194 else if (eoper == OPmin) 1195 { 1196 cdb.genf2(0xDE, 0xE8+2); // FSUBP ST(2),ST 1197 cdb.genf2(0xDE, 0xE8+2); // FSUBP ST(2),ST 1198 pop87(); 1199 pop87(); 1200 } 1201 else 1202 { 1203 int clib = eoper == OPmul ? CLIB.cmul : CLIB.cdiv; 1204 callclib(cdb, e, clib, &retregs, 0); 1205 } 1206 fixresult_complex87(cdb, e, retregs, pretregs); 1207 return; 1208 } 1209 1210 Lcomplex2: 1211 { 1212 regm_t retregs = mST0; 1213 codelem(cdb,e1, &retregs, false); 1214 note87(e1, 0, 0); 1215 loadComplex(cdb,e2); 1216 makesure87(cdb, e1, 0, 2, 0); 1217 retregs = mST01; 1218 if (eoper == OPadd) 1219 { 1220 cdb.genf2(0xDE, 0xC0+2); // FADDP ST(2),ST 1221 } 1222 else if (eoper == OPmin) 1223 { 1224 cdb.genf2(0xDE, 0xE8+2); // FSUBP ST(2),ST 1225 cdb.genf2(0xD9, 0xE0); // FCHS 1226 } 1227 else 1228 assert(0); 1229 pop87(); 1230 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 1231 fixresult_complex87(cdb, e, retregs, pretregs); 1232 return; 1233 } 1234 1235 case X(OPeqeq, TYcfloat, TYcfloat): 1236 case X(OPeqeq, TYcdouble, TYcdouble): 1237 case X(OPeqeq, TYcldouble, TYcldouble): 1238 case X(OPeqeq, TYcfloat, TYifloat): 1239 case X(OPeqeq, TYcdouble, TYidouble): 1240 case X(OPeqeq, TYcldouble, TYildouble): 1241 case X(OPeqeq, TYcfloat, TYfloat): 1242 case X(OPeqeq, TYcdouble, TYdouble): 1243 case X(OPeqeq, TYcldouble, TYldouble): 1244 case X(OPeqeq, TYifloat, TYcfloat): 1245 case X(OPeqeq, TYidouble, TYcdouble): 1246 case X(OPeqeq, TYildouble, TYcldouble): 1247 case X(OPeqeq, TYfloat, TYcfloat): 1248 case X(OPeqeq, TYdouble, TYcdouble): 1249 case X(OPeqeq, TYldouble, TYcldouble): 1250 case X(OPeqeq, TYfloat, TYifloat): 1251 case X(OPeqeq, TYdouble, TYidouble): 1252 case X(OPeqeq, TYldouble, TYildouble): 1253 case X(OPeqeq, TYifloat, TYfloat): 1254 case X(OPeqeq, TYidouble, TYdouble): 1255 case X(OPeqeq, TYildouble, TYldouble): 1256 { 1257 loadComplex(cdb,e1); 1258 loadComplex(cdb,e2); 1259 makesure87(cdb, e1, sz2, 2, 0); 1260 makesure87(cdb, e1, 0, 3, 0); 1261 regm_t retregs = 0; 1262 callclib(cdb, e, CLIB.ccmp, &retregs, 0); 1263 return; 1264 } 1265 1266 case X(OPadd, TYfloat, TYifloat): 1267 case X(OPadd, TYdouble, TYidouble): 1268 case X(OPadd, TYldouble, TYildouble): 1269 case X(OPadd, TYifloat, TYfloat): 1270 case X(OPadd, TYidouble, TYdouble): 1271 case X(OPadd, TYildouble, TYldouble): 1272 1273 case X(OPmin, TYfloat, TYifloat): 1274 case X(OPmin, TYdouble, TYidouble): 1275 case X(OPmin, TYldouble, TYildouble): 1276 case X(OPmin, TYifloat, TYfloat): 1277 case X(OPmin, TYidouble, TYdouble): 1278 case X(OPmin, TYildouble, TYldouble): 1279 { 1280 regm_t retregs = mST0; 1281 codelem(cdb,e1, &retregs, false); 1282 note87(e1, 0, 0); 1283 codelem(cdb,e2, &retregs, false); 1284 makesure87(cdb, e1, 0, 1, 0); 1285 if (eoper == OPmin) 1286 cdb.genf2(0xD9, 0xE0); // FCHS 1287 if (tyimaginary(e1.Ety)) 1288 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 1289 retregs = mST01; 1290 fixresult_complex87(cdb, e, retregs, pretregs); 1291 return; 1292 } 1293 1294 case X(OPadd, TYcfloat, TYifloat): 1295 case X(OPadd, TYcdouble, TYidouble): 1296 case X(OPadd, TYcldouble, TYildouble): 1297 op = 0; 1298 goto Lci; 1299 1300 case X(OPmin, TYcfloat, TYifloat): 1301 case X(OPmin, TYcdouble, TYidouble): 1302 case X(OPmin, TYcldouble, TYildouble): 1303 op = 4; 1304 goto Lci; 1305 1306 Lci: 1307 { 1308 loadComplex(cdb,e1); 1309 regm_t retregs = mST0; 1310 load87(cdb,e2,sz2,&retregs,e1,op); 1311 freenode(e2); 1312 retregs = mST01; 1313 makesure87(cdb, e1,0,1,0); 1314 fixresult_complex87(cdb,e, retregs, pretregs); 1315 return; 1316 } 1317 1318 case X(OPmul, TYcfloat, TYfloat): 1319 case X(OPmul, TYcdouble, TYdouble): 1320 case X(OPmul, TYcldouble, TYldouble): 1321 imaginary = false; 1322 goto Lcmul; 1323 1324 case X(OPmul, TYcfloat, TYifloat): 1325 case X(OPmul, TYcdouble, TYidouble): 1326 case X(OPmul, TYcldouble, TYildouble): 1327 imaginary = true; 1328 Lcmul: 1329 { 1330 loadComplex(cdb,e1); 1331 if (imaginary) 1332 { 1333 cdb.genf2(0xD9, 0xE0); // FCHS 1334 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 1335 if (elemisone(e2)) 1336 { 1337 freenode(e2); 1338 fixresult_complex87(cdb, e, mST01, pretregs); 1339 return; 1340 } 1341 } 1342 regm_t retregs = mST0; 1343 codelem(cdb,e2, &retregs, false); 1344 makesure87(cdb, e1, sz2, 1, 0); 1345 makesure87(cdb, e1, 0, 2, 0); 1346 cdb.genf2(0xDC,0xC8 + 2); // FMUL ST(2), ST 1347 cdb.genf2(0xDE,0xC8 + 1); // FMULP ST(1), ST 1348 pop87(); 1349 fixresult_complex87(cdb, e, mST01, pretregs); 1350 return; 1351 } 1352 1353 case X(OPmul, TYfloat, TYcfloat): 1354 case X(OPmul, TYdouble, TYcdouble): 1355 case X(OPmul, TYldouble, TYcldouble): 1356 imaginary = false; 1357 goto Lcmul2; 1358 1359 case X(OPmul, TYifloat, TYcfloat): 1360 case X(OPmul, TYidouble, TYcdouble): 1361 case X(OPmul, TYildouble, TYcldouble): 1362 imaginary = true; 1363 Lcmul2: 1364 { 1365 regm_t retregs = mST0; 1366 codelem(cdb,e1, &retregs, false); 1367 note87(e1, 0, 0); 1368 loadComplex(cdb,e2); 1369 makesure87(cdb, e1, 0, 2, 0); 1370 cdb.genf2(0xD9, imaginary ? 0xE0 : 0xC8 + 1); // FCHS / FXCH ST(1) 1371 cdb.genf2(0xD9,0xC8 + 2); // FXCH ST(2) 1372 cdb.genf2(0xDC,0xC8 + 2); // FMUL ST(2), ST 1373 cdb.genf2(0xDE,0xC8 + 1); // FMULP ST(1), ST 1374 pop87(); 1375 fixresult_complex87(cdb, e, mST01, pretregs); 1376 return; 1377 } 1378 1379 case X(OPdiv, TYcfloat, TYfloat): 1380 case X(OPdiv, TYcdouble, TYdouble): 1381 case X(OPdiv, TYcldouble, TYldouble): 1382 { 1383 loadComplex(cdb,e1); 1384 regm_t retregs = mST0; 1385 codelem(cdb,e2, &retregs, false); 1386 makesure87(cdb, e1, sz2, 1, 0); 1387 makesure87(cdb, e1, 0, 2, 0); 1388 cdb.genf2(0xDC,0xF8 + 2); // FDIV ST(2), ST 1389 cdb.genf2(0xDE,0xF8 + 1); // FDIVP ST(1), ST 1390 pop87(); 1391 fixresult_complex87(cdb, e, mST01, pretregs); 1392 return; 1393 } 1394 1395 case X(OPdiv, TYcfloat, TYifloat): 1396 case X(OPdiv, TYcdouble, TYidouble): 1397 case X(OPdiv, TYcldouble, TYildouble): 1398 { 1399 loadComplex(cdb,e1); 1400 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 1401 xchg87(0, 1); 1402 cdb.genf2(0xD9, 0xE0); // FCHS 1403 regm_t retregs = mST0; 1404 codelem(cdb,e2, &retregs, false); 1405 makesure87(cdb, e1, 0, 1, 0); 1406 makesure87(cdb, e1, sz2, 2, 0); 1407 cdb.genf2(0xDC,0xF8 + 2); // FDIV ST(2), ST 1408 cdb.genf2(0xDE,0xF8 + 1); // FDIVP ST(1), ST 1409 pop87(); 1410 fixresult_complex87(cdb, e, mST01, pretregs); 1411 return; 1412 } 1413 1414 case X(OPmod, TYcfloat, TYfloat): 1415 case X(OPmod, TYcdouble, TYdouble): 1416 case X(OPmod, TYcldouble, TYldouble): 1417 case X(OPmod, TYcfloat, TYifloat): 1418 case X(OPmod, TYcdouble, TYidouble): 1419 case X(OPmod, TYcldouble, TYildouble): 1420 { 1421 /* 1422 fld E1.re 1423 fld E1.im 1424 fld E2 1425 fxch ST(1) 1426 FM1: fprem 1427 fstsw word ptr sw 1428 fwait 1429 mov AH, byte ptr sw+1 1430 jp FM1 1431 fxch ST(2) 1432 FM2: fprem 1433 fstsw word ptr sw 1434 fwait 1435 mov AH, byte ptr sw+1 1436 jp FM2 1437 fstp ST(1) 1438 fxch ST(1) 1439 */ 1440 loadComplex(cdb,e1); 1441 regm_t retregs = mST0; 1442 codelem(cdb,e2, &retregs, false); 1443 makesure87(cdb, e1, sz2, 1, 0); 1444 makesure87(cdb, e1, 0, 2, 0); 1445 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 1446 1447 cdb.gen2(0xD9, 0xF8); // FPREM 1448 code *cfm1 = cdb.last(); 1449 genjmpifC2(cdb, cfm1); // JC2 FM1 1450 cdb.genf2(0xD9, 0xC8 + 2); // FXCH ST(2) 1451 1452 cdb.gen2(0xD9, 0xF8); // FPREM 1453 code *cfm2 = cdb.last(); 1454 1455 genjmpifC2(cdb, cfm2); // JC2 FM2 1456 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 1457 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 1458 1459 pop87(); 1460 fixresult_complex87(cdb, e, mST01, pretregs); 1461 return; 1462 } 1463 1464 default: 1465 1466 debug 1467 elem_print(e); 1468 1469 assert(0); 1470 } 1471 1472 int reverse = 0; 1473 int e2oper = e2.Eoper; 1474 1475 /* Move double-sized operand into the second position if there's a chance 1476 * it will allow combining a load with an operation (DMD Bugzilla 2905) 1477 */ 1478 if ( ((tybasic(e1.Ety) == TYdouble) 1479 && ((e1.Eoper == OPvar) || (e1.Eoper == OPconst)) 1480 && (tybasic(e2.Ety) != TYdouble)) || 1481 (e1.Eoper == OPconst) || 1482 (e1.Eoper == OPvar && 1483 ((e1.Ety & (mTYconst | mTYimmutable) && !OTleaf(e2oper)) || 1484 (e2oper == OPd_f && 1485 (e2.EV.E1.Eoper == OPs32_d || e2.EV.E1.Eoper == OPs64_d || e2.EV.E1.Eoper == OPs16_d) && 1486 e2.EV.E1.EV.E1.Eoper == OPvar 1487 ) || 1488 ((e2oper == OPs32_d || e2oper == OPs64_d || e2oper == OPs16_d) && 1489 e2.EV.E1.Eoper == OPvar 1490 ) 1491 ) 1492 ) 1493 ) 1494 { // Reverse order of evaluation 1495 e1 = e.EV.E2; 1496 e2 = e.EV.E1; 1497 op = oprev[op + 1]; 1498 reverse ^= 1; 1499 } 1500 1501 regm_t retregs1 = mST0; 1502 codelem(cdb,e1,&retregs1,false); 1503 note87(e1,0,0); 1504 1505 if (config.flags4 & CFG4fdivcall && e.Eoper == OPdiv) 1506 { 1507 regm_t retregs = mST0; 1508 load87(cdb,e2,0,&retregs,e1,-1); 1509 makesure87(cdb, e1,0,1,0); 1510 if (op == 7) // if reverse divide 1511 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 1512 callclib(cdb,e,CLIB.fdiv87,&retregs,0); 1513 pop87(); 1514 regm_t resregm = mST0; 1515 freenode(e2); 1516 fixresult87(cdb,e,resregm,pretregs); 1517 } 1518 else if (e.Eoper == OPmod) 1519 { 1520 /* 1521 * fld tbyte ptr y 1522 * fld tbyte ptr x // ST = x, ST1 = y 1523 * FM1: // We don't use fprem1 because for some inexplicable 1524 * // reason we get -5 when we do _modulo(15, 10) 1525 * fprem // ST = ST % ST1 1526 * fstsw word ptr sw 1527 * fwait 1528 * mov AH,byte ptr sw+1 // get msb of status word in AH 1529 * sahf // transfer to flags 1530 * jp FM1 // continue till ST < ST1 1531 * fstp ST(1) // leave remainder on stack 1532 */ 1533 regm_t retregs = mST0; 1534 load87(cdb,e2,0,&retregs,e1,-1); 1535 makesure87(cdb,e1,0,1,0); // now have x,y on stack; need y,x 1536 if (!reverse) // if not reverse modulo 1537 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 1538 1539 cdb.gen2(0xD9, 0xF8); // FM1: FPREM 1540 code *cfm1 = cdb.last(); 1541 genjmpifC2(cdb, cfm1); // JC2 FM1 1542 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 1543 1544 pop87(); 1545 freenode(e2); 1546 fixresult87(cdb,e,mST0,pretregs); 1547 } 1548 else 1549 { 1550 load87(cdb,e2,0,pretregs,e1,op); 1551 freenode(e2); 1552 } 1553 if (*pretregs & mST0) 1554 note87(e,0,0); 1555 //printf("orth87(-e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 1556 } 1557 1558 /***************************** 1559 * Load e into ST01. 1560 */ 1561 1562 private void loadComplex(ref CodeBuilder cdb,elem *e) 1563 { 1564 regm_t retregs; 1565 1566 int sz = tysize(e.Ety); 1567 switch (tybasic(e.Ety)) 1568 { 1569 case TYfloat: 1570 case TYdouble: 1571 case TYldouble: 1572 retregs = mST0; 1573 codelem(cdb,e,&retregs,false); 1574 // Convert to complex with a 0 for the imaginary part 1575 push87(cdb); 1576 cdb.gen2(0xD9,0xEE); // FLDZ 1577 break; 1578 1579 case TYifloat: 1580 case TYidouble: 1581 case TYildouble: 1582 // Convert to complex with a 0 for the real part 1583 push87(cdb); 1584 cdb.gen2(0xD9,0xEE); // FLDZ 1585 retregs = mST0; 1586 codelem(cdb,e,&retregs,false); 1587 break; 1588 1589 case TYcfloat: 1590 case TYcdouble: 1591 case TYcldouble: 1592 sz /= 2; 1593 retregs = mST01; 1594 codelem(cdb,e,&retregs,false); 1595 break; 1596 1597 default: 1598 assert(0); 1599 } 1600 note87(e, 0, 1); 1601 note87(e, sz, 0); 1602 } 1603 1604 /************************* 1605 * If op == -1, load expression e into ST0. 1606 * else compute (eleft op e), eleft is in ST0. 1607 * Must follow same logic as cmporder87(); 1608 */ 1609 1610 void load87(ref CodeBuilder cdb,elem *e,uint eoffset,regm_t *pretregs,elem *eleft,OPER op) 1611 { 1612 code cs; 1613 regm_t retregs; 1614 reg_t reg; 1615 uint mf1; 1616 ubyte ldop; 1617 int i; 1618 1619 if (NDPP) 1620 printf("+load87(e=%p, eoffset=%d, *pretregs=%s, eleft=%p, op=%d, stackused = %d)\n",e,eoffset,regm_str(*pretregs),eleft,op,global87.stackused); 1621 1622 assert(!(NOSAHF && op == 3)); 1623 elem_debug(e); 1624 if (ADDFWAIT()) 1625 cs.Iflags = CFwait; 1626 else 1627 cs.Iflags = 0; 1628 cs.Irex = 0; 1629 OPER opr = oprev[op + 1]; 1630 tym_t ty = tybasic(e.Ety); 1631 uint mf = (ty == TYfloat || ty == TYifloat || ty == TYcfloat) ? MFfloat : MFdouble; 1632 if ((ty == TYldouble || ty == TYildouble) && 1633 op != -1 && e.Eoper != OPd_ld) 1634 goto Ldefault; 1635 L5: 1636 switch (e.Eoper) 1637 { 1638 case OPcomma: 1639 docommas(cdb,&e); 1640 goto L5; 1641 1642 case OPvar: 1643 notreg(e); 1644 goto L2; 1645 1646 case OPind: 1647 L2: 1648 if (op != -1) 1649 { 1650 if (e.Ecount && e.Ecount != e.Ecomsub && 1651 (i = cse_get(e, 0)) >= 0) 1652 { 1653 immutable ubyte[8] b2 = [0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8]; 1654 1655 cdb.genf2(0xD8,b2[op] + i); // Fop ST(i) 1656 } 1657 else 1658 { 1659 getlvalue87(cdb,&cs,e,0); 1660 makesure87(cdb,eleft,eoffset,0,0); 1661 cs.Iop = ESC(mf,0); 1662 cs.Irm |= modregrm(0,op,0); 1663 cdb.gen(&cs); 1664 } 1665 } 1666 else 1667 { 1668 push87(cdb); 1669 switch (ty) 1670 { 1671 case TYfloat: 1672 case TYdouble: 1673 case TYifloat: 1674 case TYidouble: 1675 case TYcfloat: 1676 case TYcdouble: 1677 case TYdouble_alias: 1678 loadea(cdb,e,&cs,ESC(mf,1),0,0,0,0); // FLD var 1679 break; 1680 case TYldouble: 1681 case TYildouble: 1682 case TYcldouble: 1683 loadea(cdb,e,&cs,0xDB,5,0,0,0); // FLD var 1684 break; 1685 default: 1686 printf("ty = x%x\n", ty); 1687 assert(0); 1688 } 1689 note87(e,0,0); 1690 } 1691 break; 1692 1693 case OPd_f: 1694 case OPf_d: 1695 case OPd_ld: 1696 mf1 = (tybasic(e.EV.E1.Ety) == TYfloat || tybasic(e.EV.E1.Ety) == TYifloat) 1697 ? MFfloat : MFdouble; 1698 if (op != -1 && global87.stackused) 1699 note87(eleft,eoffset,0); // don't trash this value 1700 if (e.EV.E1.Eoper == OPvar || e.EV.E1.Eoper == OPind) 1701 { 1702 static if (1) 1703 { 1704 L4: 1705 getlvalue87(cdb,&cs,e.EV.E1,0); 1706 cs.Iop = ESC(mf1,0); 1707 if (op != -1) 1708 { 1709 cs.Irm |= modregrm(0,op,0); 1710 makesure87(cdb,eleft,eoffset,0,0); 1711 } 1712 else 1713 { 1714 cs.Iop |= 1; 1715 push87(cdb); 1716 } 1717 cdb.gen(&cs); // FLD / Fop 1718 } 1719 else 1720 { 1721 loadea(cdb,e.EV.E1,&cs,ESC(mf1,1),0,0,0,0); /* FLD e.EV.E1 */ 1722 } 1723 1724 // Variable cannot be put into a register anymore 1725 if (e.EV.E1.Eoper == OPvar) 1726 notreg(e.EV.E1); 1727 freenode(e.EV.E1); 1728 } 1729 else 1730 { 1731 retregs = mST0; 1732 codelem(cdb,e.EV.E1,&retregs,false); 1733 if (op != -1) 1734 { 1735 makesure87(cdb,eleft,eoffset,1,0); 1736 cdb.genf2(0xDE,modregrm(3,opr,1)); // FopRP 1737 pop87(); 1738 } 1739 } 1740 break; 1741 1742 case OPs64_d: 1743 if (e.EV.E1.Eoper == OPvar || 1744 (e.EV.E1.Eoper == OPind && e.EV.E1.Ecount == 0)) 1745 { 1746 getlvalue87(cdb,&cs,e.EV.E1,0); 1747 cs.Iop = 0xDF; 1748 push87(cdb); 1749 cs.Irm |= modregrm(0,5,0); 1750 cdb.gen(&cs); // FILD m64 1751 // Variable cannot be put into a register anymore 1752 if (e.EV.E1.Eoper == OPvar) 1753 notreg(e.EV.E1); 1754 freenode(e.EV.E1); 1755 } 1756 else if (I64) 1757 { 1758 retregs = ALLREGS; 1759 codelem(cdb,e.EV.E1,&retregs,false); 1760 reg = findreg(retregs); 1761 cdb.genfltreg(STO,reg,0); // MOV floatreg,reg 1762 code_orrex(cdb.last(), REX_W); 1763 push87(cdb); 1764 cdb.genfltreg(0xDF,5,0); // FILD long long ptr floatreg 1765 } 1766 else 1767 { 1768 retregs = ALLREGS; 1769 codelem(cdb,e.EV.E1,&retregs,false); 1770 reg = findreglsw(retregs); 1771 cdb.genfltreg(STO,reg,0); // MOV floatreg,reglsw 1772 reg = findregmsw(retregs); 1773 cdb.genfltreg(STO,reg,4); // MOV floatreg+4,regmsw 1774 push87(cdb); 1775 cdb.genfltreg(0xDF,5,0); // FILD long long ptr floatreg 1776 } 1777 if (op != -1) 1778 { 1779 makesure87(cdb,eleft,eoffset,1,0); 1780 cdb.genf2(0xDE,modregrm(3,opr,1)); // FopRP 1781 pop87(); 1782 } 1783 break; 1784 1785 case OPconst: 1786 ldop = loadconst(e, 0); 1787 if (ldop) 1788 { 1789 push87(cdb); 1790 cdb.genf2(0xD9,ldop); // FLDx 1791 if (op != -1) 1792 { 1793 cdb.genf2(0xDE,modregrm(3,opr,1)); // FopRP 1794 pop87(); 1795 } 1796 } 1797 else 1798 { 1799 assert(0); 1800 } 1801 break; 1802 1803 case OPu16_d: 1804 { 1805 /* This opcode should never be generated */ 1806 /* (probably shouldn't be for 16 bit code too) */ 1807 assert(!I32); 1808 1809 if (op != -1) 1810 note87(eleft,eoffset,0); // don't trash this value 1811 retregs = ALLREGS & mLSW; 1812 codelem(cdb,e.EV.E1,&retregs,false); 1813 regwithvalue(cdb,ALLREGS & mMSW,0,®,0); // 0-extend 1814 retregs |= mask(reg); 1815 mf1 = MFlong; 1816 goto L3; 1817 } 1818 1819 case OPs16_d: mf1 = MFword; goto L6; 1820 case OPs32_d: mf1 = MFlong; goto L6; 1821 L6: 1822 if (e.Ecount) 1823 goto Ldefault; 1824 if (op != -1) 1825 note87(eleft,eoffset,0); // don't trash this value 1826 if (e.EV.E1.Eoper == OPvar || 1827 (e.EV.E1.Eoper == OPind && e.EV.E1.Ecount == 0)) 1828 { 1829 goto L4; 1830 } 1831 else 1832 { 1833 retregs = ALLREGS; 1834 codelem(cdb,e.EV.E1,&retregs,false); 1835 L3: 1836 if (I16 && e.Eoper != OPs16_d) 1837 { 1838 /* MOV floatreg+2,reg */ 1839 reg = findregmsw(retregs); 1840 cdb.genfltreg(STO,reg,REGSIZE); 1841 retregs &= mLSW; 1842 } 1843 reg = findreg(retregs); 1844 cdb.genfltreg(STO,reg,0); // MOV floatreg,reg 1845 if (op != -1) 1846 { 1847 makesure87(cdb,eleft,eoffset,0,0); 1848 cdb.genfltreg(ESC(mf1,0),op,0); // Fop floatreg 1849 } 1850 else 1851 { 1852 /* FLD long ptr floatreg */ 1853 push87(cdb); 1854 cdb.genfltreg(ESC(mf1,1),0,0); 1855 } 1856 } 1857 break; 1858 default: 1859 Ldefault: 1860 retregs = mST0; 1861 codelem(cdb,e,&retregs,2); 1862 1863 if (op != -1) 1864 { 1865 makesure87(cdb,eleft,eoffset,1,(op == 0 || op == 1)); 1866 pop87(); 1867 if (op == 4 || op == 6) // sub or div 1868 { 1869 code *cl = cdb.last(); 1870 if (cl && cl.Iop == 0xD9 && cl.Irm == 0xC9) // FXCH ST(1) 1871 { cl.Iop = NOP; 1872 opr = op; // reverse operands 1873 } 1874 } 1875 cdb.genf2(0xDE,modregrm(3,opr,1)); // FopRP 1876 } 1877 break; 1878 } 1879 if (op == 3) // FCOMP 1880 { pop87(); // extra pop was done 1881 cg87_87topsw(cdb); 1882 } 1883 fixresult87(cdb,e,((op == 3) ? mPSW : mST0),pretregs); 1884 if (NDPP) 1885 printf("-load87(e=%p, eoffset=%d, *pretregs=%s, eleft=%p, op=%d, stackused = %d)\n",e,eoffset,regm_str(*pretregs),eleft,op,global87.stackused); 1886 } 1887 1888 /******************************** 1889 * Determine if a compare is to be done forwards (return 0) 1890 * or backwards (return 1). 1891 * Must follow same logic as load87(). 1892 */ 1893 1894 int cmporder87(elem *e) 1895 { 1896 //printf("cmporder87(%p)\n",e); 1897 L1: 1898 switch (e.Eoper) 1899 { 1900 case OPcomma: 1901 e = e.EV.E2; 1902 goto L1; 1903 1904 case OPd_f: 1905 case OPf_d: 1906 case OPd_ld: 1907 if (e.EV.E1.Eoper == OPvar || e.EV.E1.Eoper == OPind) 1908 goto ret0; 1909 else 1910 goto ret1; 1911 1912 case OPconst: 1913 if (loadconst(e, 0) || tybasic(e.Ety) == TYldouble 1914 || tybasic(e.Ety) == TYildouble) 1915 { 1916 //printf("ret 1, loadconst(e) = %d\n", loadconst(e)); 1917 goto ret1; 1918 } 1919 goto ret0; 1920 1921 case OPvar: 1922 case OPind: 1923 if (tybasic(e.Ety) == TYldouble || 1924 tybasic(e.Ety) == TYildouble) 1925 goto ret1; 1926 goto ret0; 1927 1928 case OPu16_d: 1929 case OPs16_d: 1930 case OPs32_d: 1931 goto ret0; 1932 1933 case OPs64_d: 1934 goto ret1; 1935 1936 default: 1937 goto ret1; 1938 } 1939 1940 ret1: 1941 return 1; 1942 1943 ret0: 1944 return 0; 1945 } 1946 1947 /******************************* 1948 * Perform an assignment to a long double/double/float. 1949 */ 1950 1951 void eq87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 1952 { 1953 code cs; 1954 opcode_t op1; 1955 uint op2; 1956 1957 //printf("+eq87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 1958 assert(e.Eoper == OPeq); 1959 regm_t retregs = mST0 | (*pretregs & mPSW); 1960 codelem(cdb,e.EV.E2,&retregs,false); 1961 tym_t ty1 = tybasic(e.EV.E1.Ety); 1962 switch (ty1) 1963 { 1964 case TYdouble_alias: 1965 case TYidouble: 1966 case TYdouble: op1 = ESC(MFdouble,1); op2 = 3; break; 1967 1968 case TYifloat: 1969 case TYfloat: op1 = ESC(MFfloat,1); op2 = 3; break; 1970 1971 case TYildouble: 1972 case TYldouble: op1 = 0xDB; op2 = 7; break; 1973 1974 default: 1975 assert(0); 1976 } 1977 if (*pretregs & (mST0 | ALLREGS | mBP | XMMREGS)) // if want result on stack too 1978 { 1979 if (ty1 == TYldouble || ty1 == TYildouble) 1980 { 1981 push87(cdb); 1982 cdb.genf2(0xD9,0xC0); // FLD ST(0) 1983 pop87(); 1984 } 1985 else 1986 op2 = 2; // FST e.EV.E1 1987 } 1988 else 1989 { // FSTP e.EV.E1 1990 pop87(); 1991 } 1992 1993 static if (0) 1994 { 1995 // Doesn't work if ST(0) gets saved to the stack by getlvalue() 1996 loadea(cdb,e.EV.E1,&cs,op1,op2,0,0,0); 1997 } 1998 else 1999 { 2000 cs.Irex = 0; 2001 cs.Iflags = 0; 2002 cs.Iop = op1; 2003 if (*pretregs & (mST0 | ALLREGS | mBP | XMMREGS)) // if want result on stack too 2004 { // Make sure it's still there 2005 elem *e2 = e.EV.E2; 2006 while (e2.Eoper == OPcomma) 2007 e2 = e2.EV.E2; 2008 note87(e2,0,0); 2009 getlvalue87(cdb, &cs, e.EV.E1, 0); 2010 makesure87(cdb,e2,0,0,1); 2011 } 2012 else 2013 { 2014 getlvalue87(cdb, &cs, e.EV.E1, 0); 2015 } 2016 cs.Irm |= modregrm(0,op2,0); // OR in reg field 2017 cdb.gen(&cs); 2018 if (tysize(TYldouble) == 12) 2019 { 2020 /* This deals with the fact that 10 byte reals really 2021 * occupy 12 bytes by zeroing the extra 2 bytes. 2022 */ 2023 if (op1 == 0xDB) 2024 { 2025 cs.Iop = 0xC7; // MOV EA+10,0 2026 NEWREG(cs.Irm, 0); 2027 cs.IEV1.Voffset += 10; 2028 cs.IFL2 = FLconst; 2029 cs.IEV2.Vint = 0; 2030 cs.Iflags |= CFopsize; 2031 cdb.gen(&cs); 2032 } 2033 } 2034 else if (tysize(TYldouble) == 16) 2035 { 2036 /* This deals with the fact that 10 byte reals really 2037 * occupy 16 bytes by zeroing the extra 6 bytes. 2038 */ 2039 if (op1 == 0xDB) 2040 { 2041 cs.Irex &= ~REX_W; 2042 cs.Iop = 0xC7; // MOV EA+10,0 2043 NEWREG(cs.Irm, 0); 2044 cs.IEV1.Voffset += 10; 2045 cs.IFL2 = FLconst; 2046 cs.IEV2.Vint = 0; 2047 cs.Iflags |= CFopsize; 2048 cdb.gen(&cs); 2049 2050 cs.IEV1.Voffset += 2; 2051 cs.Iflags &= ~CFopsize; 2052 cdb.gen(&cs); 2053 } 2054 } 2055 } 2056 genfwait(cdb); 2057 freenode(e.EV.E1); 2058 fixresult87(cdb,e,mST0 | mPSW,pretregs); 2059 } 2060 2061 /******************************* 2062 * Perform an assignment to a long double/double/float. 2063 */ 2064 2065 void complex_eq87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2066 { 2067 code cs; 2068 opcode_t op1; 2069 uint op2; 2070 uint sz; 2071 int fxch = 0; 2072 2073 //printf("complex_eq87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 2074 assert(e.Eoper == OPeq); 2075 cs.Iflags = ADDFWAIT() ? CFwait : 0; 2076 cs.Irex = 0; 2077 regm_t retregs = mST01 | (*pretregs & mPSW); 2078 codelem(cdb,e.EV.E2,&retregs,false); 2079 tym_t ty1 = tybasic(e.EV.E1.Ety); 2080 switch (ty1) 2081 { 2082 case TYcdouble: op1 = ESC(MFdouble,1); op2 = 3; break; 2083 case TYcfloat: op1 = ESC(MFfloat,1); op2 = 3; break; 2084 case TYcldouble: op1 = 0xDB; op2 = 7; break; 2085 default: 2086 assert(0); 2087 } 2088 if (*pretregs & (mST01 | mXMM0 | mXMM1)) // if want result on stack too 2089 { 2090 if (ty1 == TYcldouble) 2091 { 2092 push87(cdb); 2093 push87(cdb); 2094 cdb.genf2(0xD9,0xC0 + 1); // FLD ST(1) 2095 cdb.genf2(0xD9,0xC0 + 1); // FLD ST(1) 2096 pop87(); 2097 pop87(); 2098 } 2099 else 2100 { op2 = 2; // FST e.EV.E1 2101 fxch = 1; 2102 } 2103 } 2104 else 2105 { // FSTP e.EV.E1 2106 pop87(); 2107 pop87(); 2108 } 2109 sz = tysize(ty1) / 2; 2110 if (*pretregs & (mST01 | mXMM0 | mXMM1)) 2111 { 2112 cs.Iflags = 0; 2113 cs.Irex = 0; 2114 cs.Iop = op1; 2115 getlvalue87(cdb, &cs, e.EV.E1, 0); 2116 cs.IEV1.Voffset += sz; 2117 cs.Irm |= modregrm(0, op2, 0); 2118 makesure87(cdb,e.EV.E2, sz, 0, 0); 2119 cdb.gen(&cs); 2120 genfwait(cdb); 2121 makesure87(cdb,e.EV.E2, 0, 1, 0); 2122 } 2123 else 2124 { 2125 loadea(cdb,e.EV.E1,&cs,op1,op2,sz,0,0); 2126 genfwait(cdb); 2127 } 2128 if (fxch) 2129 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2130 cs.IEV1.Voffset -= sz; 2131 cdb.gen(&cs); 2132 if (fxch) 2133 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2134 if (tysize(TYldouble) == 12) 2135 { 2136 if (op1 == 0xDB) 2137 { 2138 cs.Iop = 0xC7; // MOV EA+10,0 2139 NEWREG(cs.Irm, 0); 2140 cs.IEV1.Voffset += 10; 2141 cs.IFL2 = FLconst; 2142 cs.IEV2.Vint = 0; 2143 cs.Iflags |= CFopsize; 2144 cdb.gen(&cs); 2145 cs.IEV1.Voffset += 12; 2146 cdb.gen(&cs); // MOV EA+22,0 2147 } 2148 } 2149 if (tysize(TYldouble) == 16) 2150 { 2151 if (op1 == 0xDB) 2152 { 2153 cs.Iop = 0xC7; // MOV EA+10,0 2154 NEWREG(cs.Irm, 0); 2155 cs.IEV1.Voffset += 10; 2156 cs.IFL2 = FLconst; 2157 cs.IEV2.Vint = 0; 2158 cs.Iflags |= CFopsize; 2159 cdb.gen(&cs); 2160 2161 cs.IEV1.Voffset += 2; 2162 cs.Iflags &= ~CFopsize; 2163 cdb.gen(&cs); 2164 2165 cs.IEV1.Voffset += 14; 2166 cs.Iflags |= CFopsize; 2167 cdb.gen(&cs); 2168 2169 cs.IEV1.Voffset += 2; 2170 cs.Iflags &= ~CFopsize; 2171 cdb.gen(&cs); 2172 } 2173 } 2174 genfwait(cdb); 2175 freenode(e.EV.E1); 2176 fixresult_complex87(cdb, e,mST01 | mPSW,pretregs); 2177 } 2178 2179 /******************************* 2180 * Perform an assignment while converting to integral type, 2181 * i.e. handle (e1 = (int) e2) 2182 */ 2183 2184 private void cnvteq87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2185 { 2186 code cs; 2187 opcode_t op1; 2188 uint op2; 2189 2190 assert(e.Eoper == OPeq); 2191 assert(!*pretregs); 2192 regm_t retregs = mST0; 2193 elem_debug(e.EV.E2); 2194 codelem(cdb,e.EV.E2.EV.E1,&retregs,false); 2195 2196 switch (e.EV.E2.Eoper) 2197 { case OPd_s16: 2198 op1 = ESC(MFword,1); 2199 op2 = 3; 2200 break; 2201 case OPd_s32: 2202 case OPd_u16: 2203 op1 = ESC(MFlong,1); 2204 op2 = 3; 2205 break; 2206 case OPd_s64: 2207 op1 = 0xDF; 2208 op2 = 7; 2209 break; 2210 default: 2211 assert(0); 2212 } 2213 freenode(e.EV.E2); 2214 2215 genfwait(cdb); 2216 genrnd(cdb, CW_roundto0); // FLDCW roundto0 2217 2218 pop87(); 2219 cs.Iflags = ADDFWAIT() ? CFwait : 0; 2220 if (e.EV.E1.Eoper == OPvar) 2221 notreg(e.EV.E1); // cannot be put in register anymore 2222 loadea(cdb,e.EV.E1,&cs,op1,op2,0,0,0); 2223 2224 genfwait(cdb); 2225 genrnd(cdb, CW_roundtonearest); // FLDCW roundtonearest 2226 2227 freenode(e.EV.E1); 2228 } 2229 2230 /********************************** 2231 * Perform +=, -=, *= and /= for doubles. 2232 */ 2233 2234 void opass87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2235 { 2236 code cs; 2237 uint op; 2238 opcode_t opld; 2239 opcode_t op1; 2240 uint op2; 2241 tym_t ty1 = tybasic(e.EV.E1.Ety); 2242 2243 switch (ty1) 2244 { 2245 case TYdouble_alias: 2246 case TYidouble: 2247 case TYdouble: op1 = ESC(MFdouble,1); op2 = 3; break; 2248 case TYifloat: 2249 case TYfloat: op1 = ESC(MFfloat,1); op2 = 3; break; 2250 case TYildouble: 2251 case TYldouble: op1 = 0xDB; op2 = 7; break; 2252 2253 case TYcfloat: 2254 case TYcdouble: 2255 case TYcldouble: 2256 if (e.Eoper == OPmodass) 2257 opmod_complex87(cdb, e, pretregs); 2258 else 2259 opass_complex87(cdb, e, pretregs); 2260 return; 2261 2262 default: 2263 assert(0); 2264 } 2265 switch (e.Eoper) 2266 { 2267 case OPpostinc: 2268 case OPaddass: op = 0 << 3; opld = 0xC1; break; // FADD 2269 case OPpostdec: 2270 case OPminass: op = 5 << 3; opld = 0xE1; /*0xE9;*/ break; // FSUBR 2271 case OPmulass: op = 1 << 3; opld = 0xC9; break; // FMUL 2272 case OPdivass: op = 7 << 3; opld = 0xF1; break; // FDIVR 2273 case OPmodass: break; 2274 default: assert(0); 2275 } 2276 regm_t retregs = mST0; 2277 codelem(cdb,e.EV.E2,&retregs,false); // evaluate rvalue 2278 note87(e.EV.E2,0,0); 2279 getlvalue87(cdb,&cs,e.EV.E1,e.Eoper==OPmodass?mAX:0); 2280 makesure87(cdb,e.EV.E2,0,0,0); 2281 if (config.flags4 & CFG4fdivcall && e.Eoper == OPdivass) 2282 { 2283 push87(cdb); 2284 cs.Iop = op1; 2285 if (ty1 == TYldouble || ty1 == TYildouble) 2286 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2287 cdb.gen(&cs); 2288 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2289 callclib(cdb,e,CLIB.fdiv87,&retregs,0); 2290 pop87(); 2291 } 2292 else if (e.Eoper == OPmodass) 2293 { 2294 /* 2295 * fld tbyte ptr y 2296 * fld tbyte ptr x // ST = x, ST1 = y 2297 * FM1: // We don't use fprem1 because for some inexplicable 2298 * // reason we get -5 when we do _modulo(15, 10) 2299 * fprem // ST = ST % ST1 2300 * fstsw word ptr sw 2301 * fwait 2302 * mov AH,byte ptr sw+1 // get msb of status word in AH 2303 * sahf // transfer to flags 2304 * jp FM1 // continue till ST < ST1 2305 * fstp ST(1) // leave remainder on stack 2306 */ 2307 code *c1; 2308 2309 push87(cdb); 2310 cs.Iop = op1; 2311 if (ty1 == TYldouble || ty1 == TYildouble) 2312 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2313 cdb.gen(&cs); // FLD e.EV.E1 2314 2315 cdb.gen2(0xD9, 0xF8); // FPREM 2316 code *cfm1 = cdb.last(); 2317 genjmpifC2(cdb, cfm1); // JC2 FM1 2318 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 2319 2320 pop87(); 2321 } 2322 else if (ty1 == TYldouble || ty1 == TYildouble) 2323 { 2324 push87(cdb); 2325 cs.Iop = op1; 2326 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2327 cdb.gen(&cs); // FLD e.EV.E1 2328 cdb.genf2(0xDE,opld); // FopP ST(1) 2329 pop87(); 2330 } 2331 else 2332 { 2333 cs.Iop = op1 & ~1; 2334 cs.Irm |= op; 2335 cdb.gen(&cs); // Fop e.EV.E1 2336 } 2337 if (*pretregs & mPSW) 2338 genftst(cdb,e,0); // FTST ST0 2339 // if want result in registers 2340 if (*pretregs & (mST0 | ALLREGS | mBP)) 2341 { 2342 if (ty1 == TYldouble || ty1 == TYildouble) 2343 { 2344 push87(cdb); 2345 cdb.genf2(0xD9,0xC0); // FLD ST(0) 2346 pop87(); 2347 } 2348 else 2349 op2 = 2; // FST e.EV.E1 2350 } 2351 else 2352 { // FSTP 2353 pop87(); 2354 } 2355 cs.Iop = op1; 2356 NEWREG(cs.Irm,op2); // FSTx e.EV.E1 2357 freenode(e.EV.E1); 2358 cdb.gen(&cs); 2359 genfwait(cdb); 2360 fixresult87(cdb,e,mST0 | mPSW,pretregs); 2361 } 2362 2363 /*********************************** 2364 * Perform %= where E1 is complex and E2 is real or imaginary. 2365 */ 2366 2367 private void opmod_complex87(ref CodeBuilder cdb, elem *e,regm_t *pretregs) 2368 { 2369 2370 /* fld E2 2371 fld E1.re 2372 FM1: fprem 2373 fstsw word ptr sw 2374 fwait 2375 mov AH, byte ptr sw+1 2376 jp FM1 2377 fxch ST(1) 2378 fld E1.im 2379 FM2: fprem 2380 fstsw word ptr sw 2381 fwait 2382 mov AH, byte ptr sw+1 2383 jp FM2 2384 fstp ST(1) 2385 */ 2386 2387 code cs; 2388 2389 tym_t ty1 = tybasic(e.EV.E1.Ety); 2390 uint sz2 = _tysize[ty1] / 2; 2391 2392 regm_t retregs = mST0; 2393 codelem(cdb,e.EV.E2,&retregs,false); // FLD E2 2394 note87(e.EV.E2,0,0); 2395 getlvalue87(cdb,&cs,e.EV.E1,0); 2396 makesure87(cdb,e.EV.E2,0,0,0); 2397 2398 push87(cdb); 2399 switch (ty1) 2400 { 2401 case TYcdouble: cs.Iop = ESC(MFdouble,1); break; 2402 case TYcfloat: cs.Iop = ESC(MFfloat,1); break; 2403 case TYcldouble: cs.Iop = 0xDB; cs.Irm |= modregrm(0, 5, 0); break; 2404 default: 2405 assert(0); 2406 } 2407 cdb.gen(&cs); // FLD E1.re 2408 2409 cdb.gen2(0xD9, 0xF8); // FPREM 2410 code *cfm1 = cdb.last(); 2411 genjmpifC2(cdb, cfm1); // JC2 FM1 2412 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 2413 2414 push87(cdb); 2415 cs.IEV1.Voffset += sz2; 2416 cdb.gen(&cs); // FLD E1.im 2417 2418 cdb.gen2(0xD9, 0xF8); // FPREM 2419 code *cfm2 = cdb.last(); 2420 genjmpifC2(cdb, cfm2); // JC2 FM2 2421 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 2422 2423 pop87(); 2424 2425 if (*pretregs & (mST01 | mPSW)) 2426 { 2427 cs.Irm |= modregrm(0, 2, 0); 2428 cdb.gen(&cs); // FST mreal.im 2429 cs.IEV1.Voffset -= sz2; 2430 cdb.gen(&cs); // FST mreal.re 2431 retregs = mST01; 2432 } 2433 else 2434 { 2435 cs.Irm |= modregrm(0, 3, 0); 2436 cdb.gen(&cs); // FSTP mreal.im 2437 cs.IEV1.Voffset -= sz2; 2438 cdb.gen(&cs); // FSTP mreal.re 2439 pop87(); 2440 pop87(); 2441 retregs = 0; 2442 } 2443 freenode(e.EV.E1); 2444 genfwait(cdb); 2445 fixresult_complex87(cdb,e,retregs,pretregs); 2446 } 2447 2448 /********************************** 2449 * Perform +=, -=, *= and /= for the lvalue being complex. 2450 */ 2451 2452 private void opass_complex87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2453 { 2454 regm_t retregs; 2455 regm_t idxregs; 2456 code cs; 2457 uint op; 2458 opcode_t op2; 2459 2460 tym_t ty1 = tybasic(e.EV.E1.Ety); 2461 uint sz2 = _tysize[ty1] / 2; 2462 switch (e.Eoper) 2463 { 2464 case OPpostinc: 2465 case OPaddass: op = 0 << 3; // FADD 2466 op2 = 0xC0; // FADDP ST(i),ST 2467 break; 2468 2469 case OPpostdec: 2470 case OPminass: op = 5 << 3; // FSUBR 2471 op2 = 0xE0; // FSUBRP ST(i),ST 2472 break; 2473 2474 case OPmulass: op = 1 << 3; // FMUL 2475 op2 = 0xC8; // FMULP ST(i),ST 2476 break; 2477 2478 case OPdivass: op = 7 << 3; // FDIVR 2479 op2 = 0xF0; // FDIVRP ST(i),ST 2480 break; 2481 2482 default: assert(0); 2483 } 2484 2485 if (!tycomplex(e.EV.E2.Ety) && 2486 (e.Eoper == OPmulass || e.Eoper == OPdivass)) 2487 { 2488 retregs = mST0; 2489 codelem(cdb,e.EV.E2, &retregs, false); 2490 note87(e.EV.E2, 0, 0); 2491 getlvalue87(cdb,&cs, e.EV.E1, 0); 2492 makesure87(cdb,e.EV.E2,0,0,0); 2493 push87(cdb); 2494 cdb.genf2(0xD9,0xC0); // FLD ST(0) 2495 goto L1; 2496 } 2497 else 2498 { 2499 loadComplex(cdb,e.EV.E2); 2500 getlvalue87(cdb,&cs,e.EV.E1,0); 2501 makesure87(cdb,e.EV.E2,sz2,0,0); 2502 makesure87(cdb,e.EV.E2,0,1,0); 2503 } 2504 2505 switch (e.Eoper) 2506 { 2507 case OPpostinc: 2508 case OPaddass: 2509 case OPpostdec: 2510 case OPminass: 2511 L1: 2512 if (ty1 == TYcldouble) 2513 { 2514 push87(cdb); 2515 push87(cdb); 2516 cs.Iop = 0xDB; 2517 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2518 cdb.gen(&cs); // FLD e.EV.E1.re 2519 cs.IEV1.Voffset += sz2; 2520 cdb.gen(&cs); // FLD e.EV.E1.im 2521 cdb.genf2(0xDE, op2 + 2); // FADDP/FSUBRP ST(2),ST 2522 cdb.genf2(0xDE, op2 + 2); // FADDP/FSUBRP ST(2),ST 2523 pop87(); 2524 pop87(); 2525 if (tyimaginary(e.EV.E2.Ety)) 2526 { 2527 if (e.Eoper == OPmulass) 2528 { 2529 cdb.genf2(0xD9, 0xE0); // FCHS 2530 cdb.genf2(0xD9, 0xC8+1); // FXCH ST(1) 2531 } 2532 else if (e.Eoper == OPdivass) 2533 { 2534 cdb.genf2(0xD9, 0xC8+1); // FXCH ST(1) 2535 cdb.genf2(0xD9, 0xE0); // FCHS 2536 } 2537 } 2538 L2: 2539 if (*pretregs & (mST01 | mPSW)) 2540 { 2541 push87(cdb); 2542 push87(cdb); 2543 cdb.genf2(0xD9,0xC1); // FLD ST(1) 2544 cdb.genf2(0xD9,0xC1); // FLD ST(1) 2545 retregs = mST01; 2546 } 2547 else 2548 retregs = 0; 2549 cs.Iop = 0xDB; 2550 cs.Irm |= modregrm(0,7,0); 2551 cdb.gen(&cs); // FSTP e.EV.E1.im 2552 cs.IEV1.Voffset -= sz2; 2553 cdb.gen(&cs); // FSTP e.EV.E1.re 2554 pop87(); 2555 pop87(); 2556 2557 } 2558 else 2559 { 2560 ubyte rmop = cast(ubyte)(cs.Irm | op); 2561 ubyte rmfst = cs.Irm | modregrm(0,2,0); 2562 ubyte rmfstp = cs.Irm | modregrm(0,3,0); 2563 ubyte iopfst = (ty1 == TYcfloat) ? 0xD9 : 0xDD; 2564 opcode_t iop = (ty1 == TYcfloat) ? 0xD8 : 0xDC; 2565 2566 cs.Iop = iop; 2567 cs.Irm = rmop; 2568 cs.IEV1.Voffset += sz2; 2569 cdb.gen(&cs); // FSUBR mreal.im 2570 if (tyimaginary(e.EV.E2.Ety) && (e.Eoper == OPmulass || e.Eoper == OPdivass)) 2571 { 2572 if (e.Eoper == OPmulass) 2573 cdb.genf2(0xD9, 0xE0); // FCHS 2574 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2575 cs.IEV1.Voffset -= sz2; 2576 cdb.gen(&cs); // FMUL mreal.re 2577 if (e.Eoper == OPdivass) 2578 cdb.genf2(0xD9, 0xE0); // FCHS 2579 if (*pretregs & (mST01 | mPSW)) 2580 { 2581 cs.Iop = iopfst; 2582 cs.Irm = rmfst; 2583 cs.IEV1.Voffset += sz2; 2584 cdb.gen(&cs); // FST mreal.im 2585 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2586 cs.IEV1.Voffset -= sz2; 2587 cdb.gen(&cs); // FST mreal.re 2588 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2589 retregs = mST01; 2590 } 2591 else 2592 { 2593 cs.Iop = iopfst; 2594 cs.Irm = rmfstp; 2595 cs.IEV1.Voffset += sz2; 2596 cdb.gen(&cs); // FSTP mreal.im 2597 pop87(); 2598 cs.IEV1.Voffset -= sz2; 2599 cdb.gen(&cs); // FSTP mreal.re 2600 pop87(); 2601 retregs = 0; 2602 } 2603 goto L3; 2604 } 2605 2606 if (*pretregs & (mST01 | mPSW)) 2607 { 2608 cs.Iop = iopfst; 2609 cs.Irm = rmfst; 2610 cdb.gen(&cs); // FST mreal.im 2611 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2612 cs.Iop = iop; 2613 cs.Irm = rmop; 2614 cs.IEV1.Voffset -= sz2; 2615 cdb.gen(&cs); // FSUBR mreal.re 2616 cs.Iop = iopfst; 2617 cs.Irm = rmfst; 2618 cdb.gen(&cs); // FST mreal.re 2619 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 2620 retregs = mST01; 2621 } 2622 else 2623 { 2624 cs.Iop = iopfst; 2625 cs.Irm = rmfstp; 2626 cdb.gen(&cs); // FSTP mreal.im 2627 pop87(); 2628 cs.Iop = iop; 2629 cs.Irm = rmop; 2630 cs.IEV1.Voffset -= sz2; 2631 cdb.gen(&cs); // FSUBR mreal.re 2632 cs.Iop = iopfst; 2633 cs.Irm = rmfstp; 2634 cdb.gen(&cs); // FSTP mreal.re 2635 pop87(); 2636 retregs = 0; 2637 } 2638 } 2639 L3: 2640 freenode(e.EV.E1); 2641 genfwait(cdb); 2642 fixresult_complex87(cdb,e,retregs,pretregs); 2643 return; 2644 2645 case OPmulass: 2646 push87(cdb); 2647 push87(cdb); 2648 if (ty1 == TYcldouble) 2649 { 2650 cs.Iop = 0xDB; 2651 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2652 cdb.gen(&cs); // FLD e.EV.E1.re 2653 cs.IEV1.Voffset += sz2; 2654 cdb.gen(&cs); // FLD e.EV.E1.im 2655 retregs = mST01; 2656 callclib(cdb, e, CLIB.cmul, &retregs, 0); 2657 goto L2; 2658 } 2659 else 2660 { 2661 cs.Iop = (ty1 == TYcfloat) ? 0xD9 : 0xDD; 2662 cs.Irm |= modregrm(0, 0, 0); // FLD tbyte ptr ... 2663 cdb.gen(&cs); // FLD e.EV.E1.re 2664 cs.IEV1.Voffset += sz2; 2665 cdb.gen(&cs); // FLD e.EV.E1.im 2666 retregs = mST01; 2667 callclib(cdb, e, CLIB.cmul, &retregs, 0); 2668 if (*pretregs & (mST01 | mPSW)) 2669 { 2670 cs.Irm |= modregrm(0, 2, 0); 2671 cdb.gen(&cs); // FST mreal.im 2672 cs.IEV1.Voffset -= sz2; 2673 cdb.gen(&cs); // FST mreal.re 2674 retregs = mST01; 2675 } 2676 else 2677 { 2678 cs.Irm |= modregrm(0, 3, 0); 2679 cdb.gen(&cs); // FSTP mreal.im 2680 cs.IEV1.Voffset -= sz2; 2681 cdb.gen(&cs); // FSTP mreal.re 2682 pop87(); 2683 pop87(); 2684 retregs = 0; 2685 } 2686 goto L3; 2687 } 2688 2689 case OPdivass: 2690 push87(cdb); 2691 push87(cdb); 2692 idxregs = idxregm(&cs); // mask of index regs used 2693 if (ty1 == TYcldouble) 2694 { 2695 cs.Iop = 0xDB; 2696 cs.Irm |= modregrm(0, 5, 0); // FLD tbyte ptr ... 2697 cdb.gen(&cs); // FLD e.EV.E1.re 2698 cdb.genf2(0xD9,0xC8 + 2); // FXCH ST(2) 2699 cs.IEV1.Voffset += sz2; 2700 cdb.gen(&cs); // FLD e.EV.E1.im 2701 cdb.genf2(0xD9,0xC8 + 2); // FXCH ST(2) 2702 retregs = mST01; 2703 callclib(cdb, e, CLIB.cdiv, &retregs, idxregs); 2704 goto L2; 2705 } 2706 else 2707 { 2708 cs.Iop = (ty1 == TYcfloat) ? 0xD9 : 0xDD; 2709 cs.Irm |= modregrm(0, 0, 0); // FLD tbyte ptr ... 2710 cdb.gen(&cs); // FLD e.EV.E1.re 2711 cdb.genf2(0xD9,0xC8 + 2); // FXCH ST(2) 2712 cs.IEV1.Voffset += sz2; 2713 cdb.gen(&cs); // FLD e.EV.E1.im 2714 cdb.genf2(0xD9,0xC8 + 2); // FXCH ST(2) 2715 retregs = mST01; 2716 callclib(cdb, e, CLIB.cdiv, &retregs, idxregs); 2717 if (*pretregs & (mST01 | mPSW)) 2718 { 2719 cs.Irm |= modregrm(0, 2, 0); 2720 cdb.gen(&cs); // FST mreal.im 2721 cs.IEV1.Voffset -= sz2; 2722 cdb.gen(&cs); // FST mreal.re 2723 retregs = mST01; 2724 } 2725 else 2726 { 2727 cs.Irm |= modregrm(0, 3, 0); 2728 cdb.gen(&cs); // FSTP mreal.im 2729 cs.IEV1.Voffset -= sz2; 2730 cdb.gen(&cs); // FSTP mreal.re 2731 pop87(); 2732 pop87(); 2733 retregs = 0; 2734 } 2735 goto L3; 2736 } 2737 2738 default: 2739 assert(0); 2740 } 2741 } 2742 2743 /************************** 2744 * OPnegass 2745 */ 2746 2747 void cdnegass87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2748 { 2749 regm_t retregs; 2750 uint op; 2751 2752 //printf("cdnegass87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 2753 elem *e1 = e.EV.E1; 2754 tym_t tyml = tybasic(e1.Ety); // type of lvalue 2755 int sz = _tysize[tyml]; 2756 2757 code cs; 2758 getlvalue87(cdb,&cs,e1,0); 2759 2760 /* If the EA is really an XMM register, modEA() will fail. 2761 * So disallow putting e1 into a register. 2762 * A better way would be to negate the XMM register in place. 2763 */ 2764 if (e1.Eoper == OPvar) 2765 e1.EV.Vsym.Sflags &= ~GTregcand; 2766 2767 modEA(cdb,&cs); 2768 cs.Irm |= modregrm(0,6,0); 2769 cs.Iop = 0x80; 2770 if (tysize(TYldouble) > 10) 2771 { 2772 if (tyml == TYldouble || tyml == TYildouble) 2773 cs.IEV1.Voffset += 10 - 1; 2774 else if (tyml == TYcldouble) 2775 cs.IEV1.Voffset += tysize(TYldouble) + 10 - 1; 2776 else 2777 cs.IEV1.Voffset += sz - 1; 2778 } 2779 else 2780 cs.IEV1.Voffset += sz - 1; 2781 cs.IFL2 = FLconst; 2782 cs.IEV2.Vuns = 0x80; 2783 cdb.gen(&cs); // XOR 7[EA],0x80 2784 if (tycomplex(tyml)) 2785 { 2786 cs.IEV1.Voffset -= sz / 2; 2787 cdb.gen(&cs); // XOR 7[EA],0x80 2788 } 2789 2790 if (*pretregs) 2791 { 2792 switch (tyml) 2793 { 2794 case TYifloat: 2795 case TYfloat: cs.Iop = 0xD9; op = 0; break; 2796 case TYidouble: 2797 case TYdouble: 2798 case TYdouble_alias: cs.Iop = 0xDD; op = 0; break; 2799 case TYildouble: 2800 case TYldouble: cs.Iop = 0xDB; op = 5; break; 2801 default: 2802 assert(0); 2803 } 2804 NEWREG(cs.Irm,op); 2805 cs.IEV1.Voffset -= sz - 1; 2806 push87(cdb); 2807 cdb.gen(&cs); // FLD EA 2808 retregs = mST0; 2809 } 2810 else 2811 retregs = 0; 2812 2813 freenode(e1); 2814 fixresult87(cdb,e,retregs,pretregs); 2815 } 2816 2817 /************************ 2818 * Take care of OPpostinc and OPpostdec. 2819 */ 2820 2821 void post87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 2822 { 2823 uint op; 2824 opcode_t op1; 2825 reg_t reg; 2826 2827 //printf("post87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 2828 code cs; 2829 assert(*pretregs); 2830 getlvalue87(cdb,&cs,e.EV.E1,0); 2831 tym_t ty1 = tybasic(e.EV.E1.Ety); 2832 switch (ty1) 2833 { 2834 case TYdouble_alias: 2835 case TYidouble: 2836 case TYdouble: 2837 case TYcdouble: op1 = ESC(MFdouble,1); reg = 0; break; 2838 case TYifloat: 2839 case TYfloat: 2840 case TYcfloat: op1 = ESC(MFfloat,1); reg = 0; break; 2841 case TYildouble: 2842 case TYldouble: 2843 case TYcldouble: op1 = 0xDB; reg = 5; break; 2844 default: 2845 assert(0); 2846 } 2847 NEWREG(cs.Irm, reg); 2848 if (reg == 5) 2849 reg = 7; 2850 else 2851 reg = 3; 2852 cs.Iop = op1; 2853 push87(cdb); 2854 cdb.gen(&cs); // FLD e.EV.E1 2855 if (tycomplex(ty1)) 2856 { 2857 uint sz = _tysize[ty1] / 2; 2858 2859 push87(cdb); 2860 cs.IEV1.Voffset += sz; 2861 cdb.gen(&cs); // FLD e.EV.E1 2862 regm_t retregs = mST0; // note kludge to only load real part 2863 codelem(cdb,e.EV.E2,&retregs,false); // load rvalue 2864 cdb.genf2(0xD8, // FADD/FSUBR ST,ST2 2865 (e.Eoper == OPpostinc) ? 0xC0 + 2 : 0xE8 + 2); 2866 NEWREG(cs.Irm,reg); 2867 pop87(); 2868 cs.IEV1.Voffset -= sz; 2869 cdb.gen(&cs); // FSTP e.EV.E1 2870 genfwait(cdb); 2871 freenode(e.EV.E1); 2872 fixresult_complex87(cdb, e, mST01, pretregs); 2873 return; 2874 } 2875 2876 if (*pretregs & (mST0 | ALLREGS | mBP | XMMREGS)) 2877 { // Want the result in a register 2878 push87(cdb); 2879 cdb.genf2(0xD9,0xC0); // FLD ST0 2880 } 2881 if (*pretregs & mPSW) // if result in flags 2882 genftst(cdb,e,0); // FTST ST0 2883 regm_t retregs = mST0; 2884 codelem(cdb,e.EV.E2,&retregs,false); // load rvalue 2885 pop87(); 2886 op = (e.Eoper == OPpostinc) ? modregrm(3,0,1) : modregrm(3,5,1); 2887 cdb.genf2(0xDE,op); // FADDP/FSUBRP ST1 2888 NEWREG(cs.Irm,reg); 2889 pop87(); 2890 cdb.gen(&cs); // FSTP e.EV.E1 2891 genfwait(cdb); 2892 freenode(e.EV.E1); 2893 fixresult87(cdb,e,mPSW | mST0,pretregs); 2894 } 2895 2896 /************************ 2897 * Do the following opcodes: 2898 * OPd_u64 2899 * OPld_u64 2900 */ 2901 void cdd_u64(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 2902 { 2903 assert(I32 || I64); 2904 assert(*pretregs); 2905 if (I32) 2906 cdd_u64_I32(cdb, e, pretregs); 2907 else 2908 cdd_u64_I64(cdb, e, pretregs); 2909 } 2910 2911 private void cdd_u64_I32(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 2912 { 2913 /* Generate: 2914 mov EDX,0x8000_0000 2915 mov floatreg+0,0 2916 mov floatreg+4,EDX 2917 mov floatreg+8,0x0FBF403e // (roundTo0<<16) | adjust 2918 fld real ptr floatreg // adjust (= 1/real.epsilon) 2919 fcomp 2920 fstsw AX 2921 fstcw floatreg+12 2922 fldcw floatreg+10 // roundTo0 2923 test AH,1 2924 jz L1 // jae L1 2925 2926 fld real ptr floatreg // adjust 2927 fsubp ST(1), ST 2928 fistp floatreg 2929 mov EAX,floatreg 2930 add EDX,floatreg+4 2931 fldcw floatreg+12 2932 jmp L2 2933 2934 L1: 2935 fistp floatreg 2936 mov EAX,floatreg 2937 mov EDX,floatreg+4 2938 fldcw floatreg+12 2939 L2: 2940 */ 2941 regm_t retregs = mST0; 2942 codelem(cdb,e.EV.E1, &retregs, false); 2943 tym_t tym = e.Ety; 2944 retregs = *pretregs; 2945 if (!retregs) 2946 retregs = ALLREGS; 2947 reg_t reg, reg2; 2948 allocreg(cdb,&retregs,®,tym); 2949 reg = findreglsw(retregs); 2950 reg2 = findregmsw(retregs); 2951 movregconst(cdb,reg2,0x80000000,0); 2952 getregs(cdb,mask(reg2) | mAX); 2953 2954 cdb.genfltreg(0xC7,0,0); 2955 code *cf1 = cdb.last(); 2956 cf1.IFL2 = FLconst; 2957 cf1.IEV2.Vint = 0; // MOV floatreg+0,0 2958 cdb.genfltreg(STO,reg2,4); // MOV floatreg+4,EDX 2959 cdb.genfltreg(0xC7,0,8); 2960 code *cf3 = cdb.last(); 2961 cf3.IFL2 = FLconst; 2962 cf3.IEV2.Vint = 0xFBF403E; // MOV floatreg+8,(roundTo0<<16)|adjust 2963 2964 push87(cdb); 2965 cdb.genfltreg(0xDB,5,0); // FLD real ptr floatreg 2966 cdb.gen2(0xD8,0xD9); // FCOMP 2967 pop87(); 2968 cdb.gen2(0xDF,0xE0); // FSTSW AX 2969 cdb.genfltreg(0xD9,7,12); // FSTCW floatreg+12 2970 cdb.genfltreg(0xD9,5,10); // FLDCW floatreg+10 2971 cdb.genc2(0xF6,modregrm(3,0,4),1); // TEST AH,1 2972 code *cnop1 = gennop(null); 2973 genjmp(cdb,JE,FLcode,cast(block *)cnop1); // JZ L1 2974 2975 cdb.genfltreg(0xDB,5,0); // FLD real ptr floatreg 2976 cdb.genf2(0xDE,0xE8+1); // FSUBP ST(1),ST 2977 cdb.genfltreg(0xDF,7,0); // FISTP dword ptr floatreg 2978 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 2979 cdb.genfltreg(0x03,reg2,4); // ADD reg,floatreg+4 2980 cdb.genfltreg(0xD9,5,12); // FLDCW floatreg+12 2981 code *cnop2 = gennop(null); 2982 genjmp(cdb,JMP,FLcode,cast(block *)cnop2); // JMP L2 2983 2984 cdb.append(cnop1); 2985 cdb.genfltreg(0xDF,7,0); // FISTP dword ptr floatreg 2986 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 2987 cdb.genfltreg(LOD,reg2,4); // MOV reg,floatreg+4 2988 cdb.genfltreg(0xD9,5,12); // FLDCW floatreg+12 2989 cdb.append(cnop2); 2990 2991 pop87(); 2992 fixresult(cdb,e,retregs,pretregs); 2993 } 2994 2995 private void cdd_u64_I64(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 2996 { 2997 /* Generate: 2998 mov EDX,0x8000_0000 2999 mov floatreg+0,0 3000 mov floatreg+4,EDX 3001 mov floatreg+8,0x0FBF403e // (roundTo0<<16) | adjust 3002 fld real ptr floatreg // adjust 3003 fcomp 3004 fstsw AX 3005 fstcw floatreg+12 3006 fldcw floatreg+10 // roundTo0 3007 test AH,1 3008 jz L1 // jae L1 3009 3010 fld real ptr floatreg // adjust 3011 fsubp ST(1), ST 3012 fistp floatreg 3013 mov RAX,floatreg 3014 shl RDX,32 3015 add RAX,RDX 3016 fldcw floatreg+12 3017 jmp L2 3018 3019 L1: 3020 fistp floatreg 3021 mov RAX,floatreg 3022 fldcw floatreg+12 3023 L2: 3024 */ 3025 regm_t retregs = mST0; 3026 codelem(cdb,e.EV.E1, &retregs, false); 3027 tym_t tym = e.Ety; 3028 retregs = *pretregs; 3029 if (!retregs) 3030 retregs = ALLREGS; 3031 reg_t reg; 3032 allocreg(cdb,&retregs,®,tym); 3033 regm_t regm2 = ALLREGS & ~retregs & ~mAX; 3034 reg_t reg2; 3035 allocreg(cdb,®m2,®2,tym); 3036 movregconst(cdb,reg2,0x80000000,0); 3037 getregs(cdb,mask(reg2) | mAX); 3038 3039 cdb.genfltreg(0xC7,0,0); 3040 code *cf1 = cdb.last(); 3041 cf1.IFL2 = FLconst; 3042 cf1.IEV2.Vint = 0; // MOV floatreg+0,0 3043 cdb.genfltreg(STO,reg2,4); // MOV floatreg+4,EDX 3044 cdb.genfltreg(0xC7,0,8); 3045 code *cf3 = cdb.last(); 3046 cf3.IFL2 = FLconst; 3047 cf3.IEV2.Vint = 0xFBF403E; // MOV floatreg+8,(roundTo0<<16)|adjust 3048 3049 push87(cdb); 3050 cdb.genfltreg(0xDB,5,0); // FLD real ptr floatreg 3051 cdb.gen2(0xD8,0xD9); // FCOMP 3052 pop87(); 3053 cdb.gen2(0xDF,0xE0); // FSTSW AX 3054 cdb.genfltreg(0xD9,7,12); // FSTCW floatreg+12 3055 cdb.genfltreg(0xD9,5,10); // FLDCW floatreg+10 3056 cdb.genc2(0xF6,modregrm(3,0,4),1); // TEST AH,1 3057 code *cnop1 = gennop(null); 3058 genjmp(cdb,JE,FLcode,cast(block *)cnop1); // JZ L1 3059 3060 cdb.genfltreg(0xDB,5,0); // FLD real ptr floatreg 3061 cdb.genf2(0xDE,0xE8+1); // FSUBP ST(1),ST 3062 cdb.genfltreg(0xDF,7,0); // FISTP dword ptr floatreg 3063 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 3064 code_orrex(cdb.last(), REX_W); 3065 cdb.genc2(0xC1,(REX_W << 16) | modregrmx(3,4,reg2),32); // SHL reg2,32 3066 cdb.gen2(0x03,(REX_W << 16) | modregxrmx(3,reg,reg2)); // ADD reg,reg2 3067 cdb.genfltreg(0xD9,5,12); // FLDCW floatreg+12 3068 code *cnop2 = gennop(null); 3069 genjmp(cdb,JMP,FLcode,cast(block *)cnop2); // JMP L2 3070 3071 cdb.append(cnop1); 3072 cdb.genfltreg(0xDF,7,0); // FISTP dword ptr floatreg 3073 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 3074 code_orrex(cdb.last(), REX_W); 3075 cdb.genfltreg(0xD9,5,12); // FLDCW floatreg+12 3076 cdb.append(cnop2); 3077 3078 pop87(); 3079 fixresult(cdb,e,retregs,pretregs); 3080 } 3081 3082 /************************ 3083 * Do the following opcodes: 3084 * OPd_u32 3085 */ 3086 void cdd_u32(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 3087 { 3088 assert(I32 || I64); 3089 3090 /* Generate: 3091 mov floatreg+8,0x0FBF0000 // (roundTo0<<16) 3092 fstcw floatreg+12 3093 fldcw floatreg+10 // roundTo0 3094 fistp floatreg 3095 fldcw floatreg+12 3096 mov EAX,floatreg 3097 */ 3098 regm_t retregs = mST0; 3099 codelem(cdb,e.EV.E1, &retregs, false); 3100 tym_t tym = e.Ety; 3101 retregs = *pretregs & ALLREGS; 3102 if (!retregs) 3103 retregs = ALLREGS; 3104 reg_t reg; 3105 allocreg(cdb,&retregs,®,tym); 3106 3107 cdb.genfltreg(0xC7,0,8); 3108 code *cf3 = cdb.last(); 3109 cf3.IFL2 = FLconst; 3110 cf3.IEV2.Vint = 0x0FBF0000; // MOV floatreg+8,(roundTo0<<16) 3111 3112 cdb.genfltreg(0xD9,7,12); // FSTCW floatreg+12 3113 cdb.genfltreg(0xD9,5,10); // FLDCW floatreg+10 3114 3115 cdb.genfltreg(0xDF,7,0); // FISTP dword ptr floatreg 3116 cdb.genfltreg(0xD9,5,12); // FLDCW floatreg+12 3117 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 3118 3119 pop87(); 3120 fixresult(cdb,e,retregs,pretregs); 3121 } 3122 3123 /************************ 3124 * Do the following opcodes: 3125 * OPd_s16 3126 * OPd_s32 3127 * OPd_u16 3128 * OPd_s64 3129 */ 3130 3131 void cnvt87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3132 { 3133 regm_t retregs; 3134 uint mf,rf; 3135 reg_t reg; 3136 int clib; 3137 3138 //printf("cnvt87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 3139 assert(*pretregs); 3140 tym_t tym = e.Ety; 3141 int sz = tysize(tym); 3142 int szoff = sz; 3143 3144 switch (e.Eoper) 3145 { 3146 case OPd_s16: 3147 clib = CLIB.dblint87; 3148 mf = ESC(MFword,1); 3149 rf = 3; 3150 break; 3151 3152 case OPd_u16: 3153 szoff = 4; 3154 goto case OPd_s32; 3155 3156 case OPd_s32: 3157 clib = CLIB.dbllng87; 3158 mf = ESC(MFlong,1); 3159 rf = 3; 3160 break; 3161 3162 case OPd_s64: 3163 clib = CLIB.dblllng; 3164 mf = 0xDF; 3165 rf = 7; 3166 break; 3167 3168 default: 3169 assert(0); 3170 } 3171 3172 if (I16) // C may change the default control word 3173 { 3174 if (clib == CLIB.dblllng) 3175 { retregs = I32 ? DOUBLEREGS_32 : DOUBLEREGS_16; 3176 codelem(cdb,e.EV.E1,&retregs,false); 3177 callclib(cdb,e,clib,pretregs,0); 3178 } 3179 else 3180 { retregs = mST0; //I32 ? DOUBLEREGS_32 : DOUBLEREGS_16; 3181 codelem(cdb,e.EV.E1,&retregs,false); 3182 callclib(cdb,e,clib,pretregs,0); 3183 pop87(); 3184 } 3185 } 3186 else if (1) 3187 { // Generate: 3188 // sub ESP,12 3189 // fstcw 8[ESP] 3190 // fldcw roundto0 3191 // fistp long64 ptr [ESP] 3192 // fldcw 8[ESP] 3193 // pop lsw 3194 // pop msw 3195 // add ESP,4 3196 3197 uint szpush = szoff + 2; 3198 if (config.flags3 & CFG3pic) 3199 szpush += 2; 3200 szpush = (szpush + REGSIZE - 1) & ~(REGSIZE - 1); 3201 3202 retregs = mST0; 3203 codelem(cdb,e.EV.E1,&retregs,false); 3204 3205 if (szpush == REGSIZE) 3206 cdb.gen1(0x50 + AX); // PUSH EAX 3207 else 3208 cod3_stackadj(cdb, szpush); 3209 genfwait(cdb); 3210 cdb.genc1(0xD9,modregrm(2,7,4) + 256*modregrm(0,4,SP),FLconst,szoff); // FSTCW szoff[ESP] 3211 3212 genfwait(cdb); 3213 3214 if (config.flags3 & CFG3pic) 3215 { 3216 cdb.genc(0xC7,modregrm(2,0,4) + 256*modregrm(0,4,SP),FLconst,szoff+2,FLconst,CW_roundto0); // MOV szoff+2[ESP], CW_roundto0 3217 code_orflag(cdb.last(), CFopsize); 3218 cdb.genc1(0xD9,modregrm(2,5,4) + 256*modregrm(0,4,SP),FLconst,szoff+2); // FLDCW szoff+2[ESP] 3219 } 3220 else 3221 genrnd(cdb, CW_roundto0); // FLDCW roundto0 3222 3223 pop87(); 3224 3225 genfwait(cdb); 3226 cdb.gen2sib(mf,modregrm(0,rf,4),modregrm(0,4,SP)); // FISTP [ESP] 3227 3228 retregs = *pretregs & (ALLREGS | mBP); 3229 if (!retregs) 3230 retregs = ALLREGS; 3231 allocreg(cdb,&retregs,®,tym); 3232 3233 genfwait(cdb); // FWAIT 3234 cdb.genc1(0xD9,modregrm(2,5,4) + 256*modregrm(0,4,SP),FLconst,szoff); // FLDCW szoff[ESP] 3235 3236 if (szoff > REGSIZE) 3237 { szpush -= REGSIZE; 3238 genpop(cdb,findreglsw(retregs)); // POP lsw 3239 } 3240 szpush -= REGSIZE; 3241 genpop(cdb,reg); // POP reg 3242 3243 if (szpush) 3244 cod3_stackadj(cdb, -szpush); 3245 fixresult(cdb,e,retregs,pretregs); 3246 } 3247 else 3248 { 3249 // This is incorrect. For -inf and nan, the 8087 returns the largest 3250 // negative int (0x80000....). For -inf, 0x7FFFF... should be returned, 3251 // and for nan, 0 should be returned. 3252 retregs = mST0; 3253 codelem(cdb,e.EV.E1,&retregs,false); 3254 3255 genfwait(cdb); 3256 genrnd(cdb, CW_roundto0); // FLDCW roundto0 3257 3258 pop87(); 3259 cdb.genfltreg(mf,rf,0); // FISTP floatreg 3260 retregs = *pretregs & (ALLREGS | mBP); 3261 if (!retregs) 3262 retregs = ALLREGS; 3263 allocreg(cdb,&retregs,®,tym); 3264 3265 genfwait(cdb); 3266 3267 if (sz > REGSIZE) 3268 { 3269 cdb.genfltreg(LOD,reg,REGSIZE); // MOV reg,floatreg + REGSIZE 3270 // MOV lsreg,floatreg 3271 cdb.genfltreg(LOD,findreglsw(retregs),0); 3272 } 3273 else 3274 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 3275 genrnd(cdb, CW_roundtonearest); // FLDCW roundtonearest 3276 fixresult(cdb,e,retregs,pretregs); 3277 } 3278 } 3279 3280 /************************ 3281 * Do OPrndtol. 3282 */ 3283 3284 void cdrndtol(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3285 { 3286 if (*pretregs == 0) 3287 { 3288 codelem(cdb,e.EV.E1,pretregs,false); 3289 return; 3290 } 3291 regm_t retregs = mST0; 3292 codelem(cdb,e.EV.E1,&retregs,false); 3293 3294 ubyte op1,op2; 3295 tym_t tym = e.Ety; 3296 uint sz = tysize(tym); 3297 switch (sz) 3298 { case 2: 3299 op1 = 0xDF; 3300 op2 = 3; 3301 break; 3302 case 4: 3303 op1 = 0xDB; 3304 op2 = 3; 3305 break; 3306 case 8: 3307 op1 = 0xDF; 3308 op2 = 7; 3309 break; 3310 default: 3311 assert(0); 3312 } 3313 3314 pop87(); 3315 cdb.genfltreg(op1,op2,0); // FISTP floatreg 3316 retregs = *pretregs & (ALLREGS | mBP); 3317 if (!retregs) 3318 retregs = ALLREGS; 3319 reg_t reg; 3320 allocreg(cdb,&retregs,®,tym); 3321 genfwait(cdb); // FWAIT 3322 if (tysize(tym) > REGSIZE) 3323 { 3324 cdb.genfltreg(LOD,reg,REGSIZE); // MOV reg,floatreg + REGSIZE 3325 // MOV lsreg,floatreg 3326 cdb.genfltreg(LOD,findreglsw(retregs),0); 3327 } 3328 else 3329 { 3330 cdb.genfltreg(LOD,reg,0); // MOV reg,floatreg 3331 if (tysize(tym) == 8 && I64) 3332 code_orrex(cdb.last(), REX_W); 3333 } 3334 fixresult(cdb,e,retregs,pretregs); 3335 } 3336 3337 /************************* 3338 * Do OPscale, OPyl2x, OPyl2xp1. 3339 */ 3340 3341 void cdscale(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3342 { 3343 assert(*pretregs != 0); 3344 3345 regm_t retregs = mST0; 3346 codelem(cdb,e.EV.E1,&retregs,false); 3347 note87(e.EV.E1,0,0); 3348 codelem(cdb,e.EV.E2,&retregs,false); 3349 makesure87(cdb,e.EV.E1,0,1,0); // now have x,y on stack; need y,x 3350 switch (e.Eoper) 3351 { 3352 case OPscale: 3353 cdb.genf2(0xD9,0xFD); // FSCALE 3354 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 3355 break; 3356 3357 case OPyl2x: 3358 cdb.genf2(0xD9,0xF1); // FYL2X 3359 break; 3360 3361 case OPyl2xp1: 3362 cdb.genf2(0xD9,0xF9); // FYL2XP1 3363 break; 3364 3365 default: 3366 assert(0); 3367 } 3368 pop87(); 3369 fixresult87(cdb,e,mST0,pretregs); 3370 } 3371 3372 3373 /********************************** 3374 * Unary -, absolute value, square root, sine, cosine 3375 */ 3376 3377 void neg87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3378 { 3379 //printf("neg87()\n"); 3380 3381 assert(*pretregs); 3382 opcode_t op; 3383 switch (e.Eoper) 3384 { case OPneg: op = 0xE0; break; 3385 case OPabs: op = 0xE1; break; 3386 case OPsqrt: op = 0xFA; break; 3387 case OPsin: op = 0xFE; break; 3388 case OPcos: op = 0xFF; break; 3389 case OPrint: op = 0xFC; break; // FRNDINT 3390 default: 3391 assert(0); 3392 } 3393 regm_t retregs = mST0; 3394 codelem(cdb,e.EV.E1,&retregs,false); 3395 cdb.genf2(0xD9,op); // FCHS/FABS/FSQRT/FSIN/FCOS/FRNDINT 3396 fixresult87(cdb,e,mST0,pretregs); 3397 } 3398 3399 /********************************** 3400 * Unary - for complex operands 3401 */ 3402 3403 void neg_complex87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3404 { 3405 assert(e.Eoper == OPneg); 3406 regm_t retregs = mST01; 3407 codelem(cdb,e.EV.E1,&retregs,false); 3408 cdb.genf2(0xD9,0xE0); // FCHS 3409 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 3410 cdb.genf2(0xD9,0xE0); // FCHS 3411 cdb.genf2(0xD9,0xC8 + 1); // FXCH ST(1) 3412 fixresult_complex87(cdb,e,mST01,pretregs); 3413 } 3414 3415 /********************************* 3416 */ 3417 3418 void cdind87(ref CodeBuilder cdb,elem *e,regm_t *pretregs) 3419 { 3420 //printf("cdind87(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); 3421 code cs; 3422 3423 getlvalue87(cdb,&cs,e,0); // get addressing mode 3424 if (*pretregs) 3425 { 3426 switch (tybasic(e.Ety)) 3427 { case TYfloat: 3428 case TYifloat: 3429 cs.Iop = 0xD9; 3430 break; 3431 3432 case TYidouble: 3433 case TYdouble: 3434 case TYdouble_alias: 3435 cs.Iop = 0xDD; 3436 break; 3437 3438 case TYildouble: 3439 case TYldouble: 3440 cs.Iop = 0xDB; 3441 cs.Irm |= modregrm(0,5,0); 3442 break; 3443 3444 default: 3445 assert(0); 3446 } 3447 push87(cdb); 3448 cdb.gen(&cs); // FLD EA 3449 fixresult87(cdb,e,mST0,pretregs); 3450 } 3451 } 3452 3453 /************************************ 3454 * Reset statics for another .obj file. 3455 */ 3456 3457 void cg87_reset() 3458 { 3459 memset(&oldd,0,oldd.sizeof); 3460 } 3461 3462 3463 /***************************************** 3464 * Initialize control word constants. 3465 */ 3466 3467 private void genrnd(ref CodeBuilder cdb, short cw) 3468 { 3469 if (config.flags3 & CFG3pic) 3470 { 3471 cdb.genfltreg(0xC7, 0, 0); // MOV floatreg, cw 3472 code *c1 = cdb.last(); 3473 c1.IFL2 = FLconst; 3474 c1.IEV2.Vuns = cw; 3475 3476 cdb.genfltreg(0xD9, 5, 0); // FLDCW floatreg 3477 } 3478 else 3479 { 3480 if (!oldd.round) // if not initialized 3481 { 3482 short cwi; 3483 3484 oldd.round = 1; 3485 3486 cwi = CW_roundto0; // round to 0 3487 oldd.roundto0 = out_readonly_sym(TYshort,&cwi,2); 3488 cwi = CW_roundtonearest; // round to nearest 3489 oldd.roundtonearest = out_readonly_sym(TYshort,&cwi,2); 3490 } 3491 Symbol *rnddir = (cw == CW_roundto0) ? oldd.roundto0 : oldd.roundtonearest; 3492 code cs; 3493 cs.Iop = 0xD9; 3494 cs.Iflags = CFoff; 3495 cs.Irex = 0; 3496 cs.IEV1.Vsym = rnddir; 3497 cs.IFL1 = rnddir.Sfl; 3498 cs.IEV1.Voffset = 0; 3499 cs.Irm = modregrm(0,5,BPRM); 3500 cdb.gen(&cs); 3501 } 3502 } 3503 3504 /************************* Complex Numbers *********************/ 3505 3506 /*************************** 3507 * Set the PSW based on the state of ST01. 3508 * Input: 3509 * pop if stack should be popped after test 3510 */ 3511 3512 private void genctst(ref CodeBuilder cdb,elem *e,int pop) 3513 { 3514 assert(pop == 0 || pop == 1); 3515 3516 // Generate: 3517 // if (NOSAHF && pop) 3518 // FLDZ 3519 // FUCOMIP 3520 // JNE L1 3521 // JP L1 // if NAN 3522 // FLDZ 3523 // FUCOMIP ST(2) 3524 // L1: 3525 // if (pop) 3526 // FPOP 3527 // FPOP 3528 // if (pop) 3529 // FLDZ 3530 // FUCOMPP 3531 // FSTSW AX 3532 // SAHF 3533 // FLDZ 3534 // FUCOMPP 3535 // JNE L1 3536 // JP L1 // if NAN 3537 // FSTSW AX 3538 // SAHF 3539 // L1: 3540 // else 3541 // FLDZ 3542 // FUCOM 3543 // FSTSW AX 3544 // SAHF 3545 // FUCOMP ST(2) 3546 // JNE L1 3547 // JP L1 // if NAN 3548 // FSTSW AX 3549 // SAHF 3550 // L1: 3551 // FUCOMP doesn't raise exceptions on QNANs, unlike FTST 3552 3553 CodeBuilder cdbnop; 3554 cdbnop.ctor(); 3555 cdbnop.gennop(); 3556 code *cnop = cdbnop.peek(); 3557 push87(cdb); 3558 cdb.gen2(0xD9,0xEE); // FLDZ 3559 if (NOSAHF) 3560 { 3561 cdb.gen2(0xDF,0xE9); // FUCOMIP 3562 pop87(); 3563 genjmp(cdb,JNE,FLcode,cast(block *) cnop); // JNE L1 3564 genjmp(cdb,JP, FLcode,cast(block *) cnop); // JP L1 3565 cdb.gen2(0xD9,0xEE); // FLDZ 3566 cdb.gen2(0xDF,0xEA); // FUCOMIP ST(2) 3567 if (pop) 3568 { 3569 cdbnop.genf2(0xDD,modregrm(3,3,0)); // FPOP 3570 cdbnop.genf2(0xDD,modregrm(3,3,0)); // FPOP 3571 pop87(); 3572 pop87(); 3573 } 3574 } 3575 else if (pop) 3576 { 3577 cdb.gen2(0xDA,0xE9); // FUCOMPP 3578 pop87(); 3579 pop87(); 3580 cg87_87topsw(cdb); // put 8087 flags in CPU flags 3581 cdb.gen2(0xD9,0xEE); // FLDZ 3582 cdb.gen2(0xDA,0xE9); // FUCOMPP 3583 pop87(); 3584 genjmp(cdb,JNE,FLcode,cast(block *) cnop); // JNE L1 3585 genjmp(cdb,JP, FLcode,cast(block *) cnop); // JP L1 3586 cg87_87topsw(cdb); // put 8087 flags in CPU flags 3587 } 3588 else 3589 { 3590 cdb.gen2(0xDD,0xE1); // FUCOM 3591 cg87_87topsw(cdb); // put 8087 flags in CPU flags 3592 cdb.gen2(0xDD,0xEA); // FUCOMP ST(2) 3593 pop87(); 3594 genjmp(cdb,JNE,FLcode,cast(block *) cnop); // JNE L1 3595 genjmp(cdb,JP, FLcode,cast(block *) cnop); // JP L1 3596 cg87_87topsw(cdb); // put 8087 flags in CPU flags 3597 } 3598 cdb.append(cdbnop); 3599 } 3600 3601 /****************************** 3602 * Given the result of an expression is in retregs, 3603 * generate necessary code to return result in *pretregs. 3604 */ 3605 3606 3607 void fixresult_complex87(ref CodeBuilder cdb,elem *e,regm_t retregs,regm_t *pretregs) 3608 { 3609 static if (0) 3610 { 3611 printf("fixresult_complex87(e = %p, retregs = %s, *pretregs = %s)\n", 3612 e,regm_str(retregs),regm_str(*pretregs)); 3613 } 3614 3615 assert(!*pretregs || retregs); 3616 tym_t tym = tybasic(e.Ety); 3617 uint sz = _tysize[tym]; 3618 3619 if (*pretregs == 0 && retregs == mST01) 3620 { 3621 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 3622 pop87(); 3623 cdb.genf2(0xDD,modregrm(3,3,0)); // FPOP 3624 pop87(); 3625 } 3626 else if (tym == TYllong) 3627 { 3628 // passing cfloat through register for I64 3629 assert(retregs & mST01, "this float expression is not implemented"); 3630 pop87(); 3631 cdb.genfltreg(ESC(MFfloat,1),BX,4); // FSTP floatreg 3632 pop87(); 3633 cdb.genfltreg(ESC(MFfloat,1),BX,0); // FSTP floatreg+4 3634 genfwait(cdb); 3635 const reg = findreg(*pretregs); 3636 getregs(cdb,reg); 3637 cdb.genfltreg(LOD, reg, 0); // MOV ECX,floatreg 3638 code_orrex(cdb.last(), REX_W); // extend to RCX 3639 } 3640 else if (tym == TYcfloat && *pretregs & (mAX|mDX) && retregs & mST01) 3641 { 3642 if (*pretregs & mPSW && !(retregs & mPSW)) 3643 genctst(cdb,e,0); // FTST 3644 pop87(); 3645 cdb.genfltreg(ESC(MFfloat,1),3,0); // FSTP floatreg 3646 genfwait(cdb); 3647 getregs(cdb,mDX|mAX); 3648 cdb.genfltreg(LOD, DX, 0); // MOV EDX,floatreg 3649 3650 pop87(); 3651 cdb.genfltreg(ESC(MFfloat,1),3,0); // FSTP floatreg 3652 genfwait(cdb); 3653 cdb.genfltreg(LOD, AX, 0); // MOV EAX,floatreg 3654 } 3655 else if (tym == TYcfloat && retregs & (mAX|mDX) && *pretregs & mST01) 3656 { 3657 push87(cdb); 3658 cdb.genfltreg(STO, AX, 0); // MOV floatreg, EAX 3659 cdb.genfltreg(0xD9, 0, 0); // FLD float ptr floatreg 3660 3661 push87(cdb); 3662 cdb.genfltreg(STO, DX, 0); // MOV floatreg, EDX 3663 cdb.genfltreg(0xD9, 0, 0); // FLD float ptr floatreg 3664 3665 if (*pretregs & mPSW) 3666 genctst(cdb,e,0); // FTST 3667 } 3668 else if ((tym == TYcfloat || tym == TYcdouble) && 3669 *pretregs & (mXMM0|mXMM1) && retregs & mST01) 3670 { 3671 tym_t tyf = tym == TYcfloat ? TYfloat : TYdouble; 3672 uint xop = xmmload(tyf); 3673 uint mf = tyf == TYfloat ? MFfloat : MFdouble; 3674 if (*pretregs & mPSW && !(retregs & mPSW)) 3675 genctst(cdb,e,0); // FTST 3676 pop87(); 3677 cdb.genfltreg(ESC(mf,1),3,0); // FSTP floatreg 3678 genfwait(cdb); 3679 getregs(cdb,mXMM0|mXMM1); 3680 cdb.genxmmreg(xop,XMM1,0,tyf); 3681 3682 pop87(); 3683 cdb.genfltreg(ESC(mf,1),3,0); // FSTP floatreg 3684 genfwait(cdb); 3685 cdb.genxmmreg(xop, XMM0, 0, tyf); // MOVD XMM0,floatreg 3686 } 3687 else if ((tym == TYcfloat || tym == TYcdouble) && 3688 retregs & (mXMM0|mXMM1) && *pretregs & mST01) 3689 { 3690 tym_t tyf = tym == TYcfloat ? TYfloat : TYdouble; 3691 uint xop = xmmstore(tyf); 3692 uint fop = tym == TYcfloat ? 0xD9 : 0xDD; 3693 push87(cdb); 3694 cdb.genfltreg(xop, XMM0-XMM0, 0); // STOS(SD) floatreg, XMM0 3695 checkSetVex(cdb.last(),tyf); 3696 cdb.genfltreg(fop, 0, 0); // FLD double ptr floatreg 3697 3698 push87(cdb); 3699 cdb.genxmmreg(xop, XMM1, 0, tyf); // MOV floatreg, XMM1 3700 cdb.genfltreg(fop, 0, 0); // FLD double ptr floatreg 3701 3702 if (*pretregs & mPSW) 3703 genctst(cdb,e,0); // FTST 3704 } 3705 else 3706 { if (*pretregs & mPSW) 3707 { if (!(retregs & mPSW)) 3708 { assert(retregs & mST01); 3709 genctst(cdb,e,!(*pretregs & mST01)); // FTST 3710 } 3711 } 3712 assert(!(*pretregs & mST01) || (retregs & mST01)); 3713 } 3714 if (*pretregs & mST01) 3715 { note87(e,0,1); 3716 note87(e,sz/2,0); 3717 } 3718 } 3719 3720 /***************************************** 3721 * Operators OPc_r and OPc_i 3722 */ 3723 3724 void cdconvt87(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 3725 { 3726 regm_t retregs = mST01; 3727 codelem(cdb,e.EV.E1, &retregs, false); 3728 switch (e.Eoper) 3729 { 3730 case OPc_r: 3731 cdb.genf2(0xDD,0xD8 + 0); // FPOP 3732 pop87(); 3733 break; 3734 3735 case OPc_i: 3736 cdb.genf2(0xDD,0xD8 + 1); // FSTP ST(1) 3737 pop87(); 3738 break; 3739 3740 default: 3741 assert(0); 3742 } 3743 retregs = mST0; 3744 fixresult87(cdb, e, retregs, pretregs); 3745 } 3746 3747 /************************************** 3748 * Load complex operand into ST01 or flags or both. 3749 */ 3750 3751 void cload87(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 3752 { 3753 //printf("e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 3754 //elem_print(e); 3755 assert(!I16); 3756 debug 3757 if (I32) 3758 { 3759 assert(config.inline8087); 3760 elem_debug(e); 3761 assert(*pretregs & (mST01 | mPSW)); 3762 assert(!(*pretregs & ~(mST01 | mPSW))); 3763 } 3764 3765 tym_t ty = tybasic(e.Ety); 3766 code cs = void; 3767 uint mf; 3768 uint sz; 3769 ubyte ldop; 3770 regm_t retregs; 3771 int i; 3772 3773 //printf("cload87(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); 3774 sz = _tysize[ty] / 2; 3775 memset(&cs, 0, cs.sizeof); 3776 if (ADDFWAIT()) 3777 cs.Iflags = CFwait; 3778 switch (ty) 3779 { 3780 case TYcfloat: mf = MFfloat; break; 3781 case TYcdouble: mf = MFdouble; break; 3782 case TYcldouble: break; 3783 default: assert(0); 3784 } 3785 switch (e.Eoper) 3786 { 3787 case OPvar: 3788 notreg(e); // never enregister this variable 3789 goto case OPind; 3790 3791 case OPind: 3792 push87(cdb); 3793 push87(cdb); 3794 switch (ty) 3795 { 3796 case TYcfloat: 3797 case TYcdouble: 3798 loadea(cdb,e,&cs,ESC(mf,1),0,0,0,0); // FLD var 3799 cs.IEV1.Voffset += sz; 3800 cdb.gen(&cs); 3801 break; 3802 3803 case TYcldouble: 3804 loadea(cdb,e,&cs,0xDB,5,0,0,0); // FLD var 3805 cs.IEV1.Voffset += sz; 3806 cdb.gen(&cs); 3807 break; 3808 3809 default: 3810 assert(0); 3811 } 3812 retregs = mST01; 3813 break; 3814 3815 case OPd_ld: 3816 case OPld_d: 3817 case OPf_d: 3818 case OPd_f: 3819 cload87(cdb,e.EV.E1, pretregs); 3820 freenode(e.EV.E1); 3821 return; 3822 3823 case OPconst: 3824 push87(cdb); 3825 push87(cdb); 3826 for (i = 0; i < 2; i++) 3827 { 3828 ldop = loadconst(e, i); 3829 if (ldop) 3830 { 3831 cdb.genf2(0xD9,ldop); // FLDx 3832 } 3833 else 3834 { 3835 assert(0); 3836 } 3837 } 3838 retregs = mST01; 3839 break; 3840 3841 default: 3842 debug elem_print(e); 3843 assert(0); 3844 } 3845 fixresult_complex87(cdb, e, retregs, pretregs); 3846 } 3847 3848 /********************************************** 3849 * Load OPpair or OPrpair into mST01 3850 */ 3851 void loadPair87(ref CodeBuilder cdb, elem *e, regm_t *pretregs) 3852 { 3853 assert(e.Eoper == OPpair || e.Eoper == OPrpair); 3854 regm_t retregs = mST0; 3855 codelem(cdb,e.EV.E1, &retregs, false); 3856 note87(e.EV.E1, 0, 0); 3857 codelem(cdb,e.EV.E2, &retregs, false); 3858 makesure87(cdb,e.EV.E1, 0, 1, 0); 3859 if (e.Eoper == OPrpair) 3860 cdb.genf2(0xD9, 0xC8 + 1); // FXCH ST(1) 3861 retregs = mST01; 3862 fixresult_complex87(cdb, e, retregs, pretregs); 3863 } 3864 3865 }