1 /** 2 * Compute common subexpressions for non-optimized code generation 3 * 4 * Compiler implementation of the 5 * $(LINK2 https://www.dlang.org, D programming language). 6 * 7 * Compute common subexpressions for non-optimized builds. 8 * 9 * Copyright: Copyright (C) 1985-1995 by Symantec 10 * Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved 11 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 12 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 13 * Source: https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgcs.d 14 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/cgcs.d 15 */ 16 17 module dmd.backend.cgcs; 18 19 import core.stdc.stdio; 20 import core.stdc.stdlib; 21 22 import dmd.backend.cc; 23 import dmd.backend.cdef; 24 import dmd.backend.code; 25 import dmd.backend.el; 26 import dmd.backend.global; 27 import dmd.backend.oper; 28 import dmd.backend.ty; 29 import dmd.backend.type; 30 31 import dmd.backend.barray; 32 import dmd.backend.dlist; 33 import dmd.backend.dvec; 34 35 36 nothrow: 37 @safe: 38 39 /******************************* 40 * Do common subexpression elimination for non-optimized builds. 41 */ 42 43 @trusted 44 public extern (C++) void comsubs() 45 { 46 debug if (debugx) printf("comsubs(%p)\n",startblock); 47 48 version (SCPP) 49 { 50 if (errcnt) 51 return; 52 } 53 54 comsubs2(startblock, cgcsdata); 55 56 debug if (debugx) 57 printf("done with comsubs()\n"); 58 } 59 60 /******************************* 61 */ 62 63 @trusted 64 public extern (C++) void cgcs_term() 65 { 66 cgcsdata.term(); 67 debug debugw && printf("cgcs_term()\n"); 68 } 69 70 71 /***********************************************************************/ 72 73 private: 74 75 alias hash_t = uint; // for hash values 76 77 /******************************* 78 * Eliminate common subexpressions across extended basic blocks. 79 * String together as many blocks as we can. 80 */ 81 @trusted 82 void comsubs2(block* startblock, ref CGCS cgcs) 83 { 84 // No longer just compute Bcount - eliminate unreachable blocks too 85 block_compbcount(); // eliminate unreachable blocks 86 87 cgcs.start(); 88 89 block* bln; 90 for (block* bl = startblock; bl; bl = bln) 91 { 92 bln = bl.Bnext; 93 if (!bl.Belem) 94 continue; /* if no expression or no parents */ 95 96 // Count up n, the number of blocks in this extended basic block (EBB) 97 int n = 1; // always at least one block in EBB 98 auto blc = bl; 99 while (bln && list_nitems(bln.Bpred) == 1 && 100 ((blc.BC == BCiftrue && 101 blc.nthSucc(1) == bln) || 102 (blc.BC == BCgoto && blc.nthSucc(0) == bln) 103 ) && 104 bln.BC != BCasm // no CSE's extending across ASM blocks 105 ) 106 { 107 n++; // add block to EBB 108 blc = bln; 109 bln = blc.Bnext; 110 } 111 112 cgcs.reset(); 113 114 bln = bl; 115 while (n--) // while more blocks in EBB 116 { 117 debug if (debugx) 118 printf("cses for block %p\n",bln); 119 120 if (bln.Belem) 121 ecom(cgcs, bln.Belem); // do the tree 122 bln = bln.Bnext; 123 } 124 } 125 } 126 127 128 /********************************* 129 * Struct for each potential CSE 130 */ 131 132 struct HCS 133 { 134 elem* Helem; /// pointer to elem 135 hash_t Hhash; /// hash value for the elem 136 } 137 138 struct HCSArray 139 { 140 size_t touchstari; 141 size_t[2] touchfunci; 142 } 143 144 /************************************** 145 * All the global data for this module 146 */ 147 struct CGCS 148 { 149 Barray!HCS hcstab; // array of hcs's 150 HCSArray hcsarray; 151 152 // Use a bit vector for quick check if expression is possibly in hcstab[]. 153 // This results in much faster compiles when hcstab[] gets big. 154 vec_t csvec; // vector of used entries 155 enum CSVECDIM = 16_001; //8009 //3001 // dimension of csvec (should be prime) 156 157 nothrow: 158 159 /********************************* 160 * Initialize for this iteration. 161 */ 162 void start() 163 { 164 if (!csvec) 165 { 166 csvec = vec_calloc(CGCS.CSVECDIM); 167 } 168 } 169 170 /******************************* 171 * Reset for next time. 172 * hcstab[]'s storage is kept instead of reallocated. 173 */ 174 void reset() 175 { 176 vec_clear(csvec); // don't free it, recycle storage 177 hcstab.reset(); 178 hcsarray = HCSArray.init; 179 } 180 181 /********************************* 182 * All done for this compiler instance. 183 */ 184 void term() 185 { 186 vec_free(csvec); 187 csvec = null; 188 //hcstab.dtor(); // cache allocation for next iteration 189 } 190 191 /**************************** 192 * Add an elem to the common subexpression table. 193 */ 194 195 void push(elem *e, hash_t hash) 196 { 197 hcstab.push(HCS(e, hash)); 198 } 199 200 /******************************* 201 * Eliminate all common subexpressions. 202 */ 203 204 void touchall() 205 { 206 foreach (ref hcs; hcstab[]) 207 { 208 hcs.Helem = null; 209 } 210 const len = hcstab.length; 211 hcsarray.touchstari = len; 212 hcsarray.touchfunci[0] = len; 213 hcsarray.touchfunci[1] = len; 214 } 215 } 216 217 __gshared CGCS cgcsdata; 218 219 /************************* 220 * Eliminate common subexpressions for an element. 221 * Params: 222 * cgcs = cgcsdata 223 * pe = elem that is changed to previous elem if it's a CSE 224 */ 225 @trusted 226 void ecom(ref CGCS cgcs, ref elem* pe) 227 { 228 auto e = pe; 229 assert(e); 230 elem_debug(e); 231 debug assert(e.Ecount == 0); 232 //assert(e.Ecomsub == 0); 233 const tym = tybasic(e.Ety); 234 const op = e.Eoper; 235 switch (op) 236 { 237 case OPconst: 238 case OPrelconst: 239 break; 240 241 case OPvar: 242 if (e.EV.Vsym.ty() & mTYshared) 243 return; // don't cache shared variables 244 break; 245 246 case OPstreq: 247 case OPpostinc: 248 case OPpostdec: 249 case OPeq: 250 case OPaddass: 251 case OPminass: 252 case OPmulass: 253 case OPdivass: 254 case OPmodass: 255 case OPshrass: 256 case OPashrass: 257 case OPshlass: 258 case OPandass: 259 case OPxorass: 260 case OPorass: 261 case OPvecsto: 262 /* Reverse order of evaluation for double op=. This is so that */ 263 /* the pushing of the address of the second operand is easier. */ 264 /* However, with the 8087 we don't need the kludge. */ 265 if (op != OPeq && tym == TYdouble && !config.inline8087) 266 { 267 if (!OTleaf(e.EV.E1.Eoper)) 268 ecom(cgcs, e.EV.E1.EV.E1); 269 ecom(cgcs, e.EV.E2); 270 } 271 else 272 { 273 /* Don't mark the increment of an i++ or i-- as a CSE, if it */ 274 /* can be done with an INC or DEC instruction. */ 275 if (!(OTpost(op) && elemisone(e.EV.E2))) 276 ecom(cgcs, e.EV.E2); /* evaluate 2nd operand first */ 277 case OPnegass: 278 if (!OTleaf(e.EV.E1.Eoper)) /* if lvalue is an operator */ 279 { 280 if (e.EV.E1.Eoper != OPind) 281 elem_print(e); 282 assert(e.EV.E1.Eoper == OPind); 283 ecom(cgcs, e.EV.E1.EV.E1); 284 } 285 } 286 touchlvalue(cgcs, e.EV.E1); 287 if (!OTpost(op)) /* lvalue of i++ or i-- is not a cse*/ 288 { 289 const hash = cs_comphash(e.EV.E1); 290 vec_setbit(hash % CGCS.CSVECDIM,cgcs.csvec); 291 cgcs.push(e.EV.E1,hash); // add lvalue to cgcs.hcstab[] 292 } 293 return; 294 295 case OPbtc: 296 case OPbts: 297 case OPbtr: 298 case OPcmpxchg: 299 ecom(cgcs, e.EV.E1); 300 ecom(cgcs, e.EV.E2); 301 touchfunc(cgcs, 0); // indirect assignment 302 return; 303 304 case OPandand: 305 case OPoror: 306 { 307 ecom(cgcs, e.EV.E1); 308 const lengthSave = cgcs.hcstab.length; 309 auto hcsarraySave = cgcs.hcsarray; 310 ecom(cgcs, e.EV.E2); 311 cgcs.hcsarray = hcsarraySave; // no common subs by E2 312 cgcs.hcstab.setLength(lengthSave); 313 return; /* if comsub then logexp() will */ 314 } 315 316 case OPcond: 317 { 318 ecom(cgcs, e.EV.E1); 319 const lengthSave = cgcs.hcstab.length; 320 auto hcsarraySave = cgcs.hcsarray; 321 ecom(cgcs, e.EV.E2.EV.E1); // left condition 322 cgcs.hcsarray = hcsarraySave; // no common subs by E2 323 cgcs.hcstab.setLength(lengthSave); 324 ecom(cgcs, e.EV.E2.EV.E2); // right condition 325 cgcs.hcsarray = hcsarraySave; // no common subs by E2 326 cgcs.hcstab.setLength(lengthSave); 327 return; // can't be a common sub 328 } 329 330 case OPcall: 331 case OPcallns: 332 ecom(cgcs, e.EV.E2); /* eval right first */ 333 goto case OPucall; 334 335 case OPucall: 336 case OPucallns: 337 ecom(cgcs, e.EV.E1); 338 touchfunc(cgcs, 1); 339 return; 340 341 case OPstrpar: /* so we don't break logexp() */ 342 case OPinp: /* never CSE the I/O instruction itself */ 343 case OPprefetch: // don't CSE E2 or the instruction 344 ecom(cgcs, e.EV.E1); 345 goto case OPasm; 346 347 case OPasm: 348 case OPstrthis: // don't CSE these 349 case OPframeptr: 350 case OPgot: 351 case OPctor: 352 case OPdtor: 353 case OPdctor: 354 case OPmark: 355 return; 356 357 case OPddtor: 358 cgcs.touchall(); 359 ecom(cgcs, e.EV.E1); 360 cgcs.touchall(); 361 return; 362 363 case OPparam: 364 case OPoutp: 365 ecom(cgcs, e.EV.E1); 366 goto case OPinfo; 367 368 case OPinfo: 369 ecom(cgcs, e.EV.E2); 370 return; 371 372 case OPcomma: 373 ecom(cgcs, e.EV.E1); 374 ecom(cgcs, e.EV.E2); 375 return; 376 377 case OPremquo: 378 ecom(cgcs, e.EV.E1); 379 ecom(cgcs, e.EV.E2); 380 break; 381 382 case OPvp_fp: 383 case OPcvp_fp: 384 ecom(cgcs, e.EV.E1); 385 touchaccess(cgcs.hcstab, e); 386 break; 387 388 case OPind: 389 ecom(cgcs, e.EV.E1); 390 /* Generally, CSEing a *(double *) results in worse code */ 391 if (tyfloating(tym)) 392 return; 393 if (tybasic(e.EV.E1.Ety) == TYsharePtr) 394 return; 395 break; 396 397 case OPstrcpy: 398 case OPstrcat: 399 case OPmemcpy: 400 case OPmemset: 401 ecom(cgcs, e.EV.E2); 402 goto case OPsetjmp; 403 404 case OPsetjmp: 405 ecom(cgcs, e.EV.E1); 406 touchfunc(cgcs, 0); 407 return; 408 409 default: /* other operators */ 410 if (!OTbinary(e.Eoper)) 411 printf("e.Eoper: '%s'\n", oper_str(e.Eoper)); 412 assert(OTbinary(e.Eoper)); 413 goto case OPadd; 414 415 case OPadd: 416 case OPmin: 417 case OPmul: 418 case OPdiv: 419 case OPor: 420 case OPxor: 421 case OPand: 422 case OPeqeq: 423 case OPne: 424 case OPscale: 425 case OPyl2x: 426 case OPyl2xp1: 427 ecom(cgcs, e.EV.E1); 428 ecom(cgcs, e.EV.E2); 429 break; 430 431 case OPstring: 432 case OPaddr: 433 case OPbit: 434 printf("e.Eoper: '%s'\n", oper_str(e.Eoper)); 435 elem_print(e); 436 assert(0); /* optelem() should have removed these */ 437 438 // Explicitly list all the unary ops for speed 439 case OPnot: case OPcom: case OPneg: case OPuadd: 440 case OPabs: case OPrndtol: case OPrint: 441 case OPpreinc: case OPpredec: 442 case OPbool: case OPstrlen: case OPs16_32: case OPu16_32: 443 case OPs32_d: case OPu32_d: case OPd_s16: case OPs16_d: case OP32_16: 444 case OPf_d: 445 case OPld_d: 446 case OPc_r: case OPc_i: 447 case OPu8_16: case OPs8_16: case OP16_8: 448 case OPu32_64: case OPs32_64: case OP64_32: case OPmsw: 449 case OPu64_128: case OPs64_128: case OP128_64: 450 case OPs64_d: case OPd_u64: case OPu64_d: 451 case OPstrctor: case OPu16_d: case OPd_u16: 452 case OParrow: 453 case OPvoid: 454 case OPbsf: case OPbsr: case OPbswap: case OPpopcnt: case OPvector: 455 case OPld_u64: 456 case OPsqrt: case OPsin: case OPcos: 457 case OPoffset: case OPnp_fp: case OPnp_f16p: case OPf16p_np: 458 case OPvecfill: case OPtoprec: 459 ecom(cgcs, e.EV.E1); 460 break; 461 462 case OPd_ld: 463 return; 464 465 case OPd_f: 466 { 467 const op1 = e.EV.E1.Eoper; 468 if (config.fpxmmregs && 469 (op1 == OPs32_d || 470 I64 && (op1 == OPs64_d || op1 == OPu32_d)) 471 ) 472 ecom(cgcs, e.EV.E1.EV.E1); // e and e1 ops are fused (see xmmcnvt()) 473 else 474 ecom(cgcs, e.EV.E1); 475 break; 476 } 477 478 case OPd_s32: 479 case OPd_u32: 480 case OPd_s64: 481 if (e.EV.E1.Eoper == OPf_d && config.fpxmmregs) 482 ecom(cgcs, e.EV.E1.EV.E1); // e and e1 ops are fused (see xmmcnvt()); 483 else 484 ecom(cgcs, e.EV.E1); 485 break; 486 487 case OPhalt: 488 return; 489 } 490 491 /* don't CSE structures or unions or volatile stuff */ 492 if (tym == TYstruct || 493 tym == TYvoid || 494 e.Ety & mTYvolatile) 495 return; 496 if (tyfloating(tym) && config.inline8087) 497 { 498 /* can CSE XMM code, but not x87 499 */ 500 if (!(config.fpxmmregs && tyxmmreg(tym))) 501 return; 502 } 503 504 const hash = cs_comphash(e); /* must be AFTER leaves are done */ 505 506 /* Search for a match in hcstab[]. 507 * Search backwards, as most likely matches will be towards the end 508 * of the list. 509 */ 510 511 debug if (debugx) printf("elem: %p hash: %6d\n",e,hash); 512 int csveci = hash % CGCS.CSVECDIM; 513 if (vec_testbit(csveci,cgcs.csvec)) 514 { 515 foreach_reverse (i, ref hcs; cgcs.hcstab[]) 516 { 517 debug if (debugx) 518 printf("i: %2d Hhash: %6d Helem: %p\n", 519 cast(int) i,hcs.Hhash,hcs.Helem); 520 521 elem* ehash; 522 if (hash == hcs.Hhash && (ehash = hcs.Helem) != null) 523 { 524 /* if elems are the same and we still have room for more */ 525 if (el_match(e,ehash) && ehash.Ecount < 0xFF) 526 { 527 /* Make sure leaves are also common subexpressions 528 * to avoid false matches. 529 */ 530 if (!OTleaf(op)) 531 { 532 if (!e.EV.E1.Ecount) 533 continue; 534 if (OTbinary(op) && !e.EV.E2.Ecount) 535 continue; 536 } 537 ehash.Ecount++; 538 pe = ehash; 539 540 debug if (debugx) 541 printf("**MATCH** %p with %p\n",e,pe); 542 543 el_free(e); 544 return; 545 } 546 } 547 } 548 } 549 else 550 vec_setbit(csveci,cgcs.csvec); 551 cgcs.push(e,hash); // add this elem to hcstab[] 552 } 553 554 /************************** 555 * Compute hash function for elem e. 556 */ 557 558 @trusted 559 hash_t cs_comphash(const elem *e) 560 { 561 elem_debug(e); 562 const op = e.Eoper; 563 hash_t hash = (e.Ety & (mTYbasic | mTYconst | mTYvolatile)) + (op << 8); 564 if (!OTleaf(op)) 565 { 566 hash += cast(hash_t) e.EV.E1; 567 if (OTbinary(op)) 568 hash += cast(hash_t) e.EV.E2; 569 } 570 else 571 { 572 hash += e.EV.Vint; 573 if (op == OPvar || op == OPrelconst) 574 hash += cast(hash_t) e.EV.Vsym; 575 } 576 return hash; 577 } 578 579 /*************************** 580 * "touch" the elem. 581 * If it is a pointer, "touch" all the suspects 582 * who could be pointed to. 583 * Eliminate common subs that are indirect loads. 584 */ 585 586 @trusted 587 void touchlvalue(ref CGCS cgcs, const elem* e) 588 { 589 if (e.Eoper == OPind) /* if indirect store */ 590 { 591 /* NOTE: Some types of array assignments do not need 592 * to touch all variables. (Like a[5], where a is an 593 * array instead of a pointer.) 594 */ 595 596 touchfunc(cgcs, 0); 597 return; 598 } 599 600 foreach_reverse (ref hcs; cgcs.hcstab[]) 601 { 602 if (hcs.Helem && 603 hcs.Helem.EV.Vsym == e.EV.Vsym) 604 hcs.Helem = null; 605 } 606 607 if (!(e.Eoper == OPvar || e.Eoper == OPrelconst)) 608 { 609 elem_print(e); 610 assert(0); 611 } 612 switch (e.EV.Vsym.Sclass) 613 { 614 case SC.regpar: 615 case SC.register: 616 case SC.pseudo: 617 break; 618 619 case SC.auto_: 620 case SC.parameter: 621 case SC.fastpar: 622 case SC.shadowreg: 623 case SC.bprel: 624 if (e.EV.Vsym.Sflags & SFLunambig) 625 break; 626 goto case SC.static_; 627 628 case SC.static_: 629 case SC.extern_: 630 case SC.global: 631 case SC.locstat: 632 case SC.comdat: 633 case SC.inline: 634 case SC.sinline: 635 case SC.einline: 636 case SC.comdef: 637 touchstar(cgcs); 638 break; 639 640 default: 641 elem_print(e); 642 symbol_print(e.EV.Vsym); 643 assert(0); 644 } 645 } 646 647 /************************** 648 * "touch" variables that could be changed by a function call or 649 * an indirect assignment. 650 * Eliminate any subexpressions that are "starred" (they need to 651 * be recomputed). 652 * Params: 653 * flag = If 1, then this is a function call. 654 * If 0, then this is an indirect assignment. 655 */ 656 657 @trusted 658 void touchfunc(ref CGCS cgcs, int flag) 659 { 660 661 //printf("touchfunc(%d)\n", flag); 662 //pe = &cgcs.hcstab[0]; printf("pe = %p, petop = %p\n",pe,petop); 663 assert(cgcs.hcsarray.touchfunci[flag] <= cgcs.hcstab.length); 664 665 foreach (ref pe; cgcs.hcstab[cgcs.hcsarray.touchfunci[flag] .. cgcs.hcstab.length]) 666 { 667 const he = pe.Helem; 668 if (!he) 669 continue; 670 switch (he.Eoper) 671 { 672 case OPvar: 673 if (Symbol_isAffected(*he.EV.Vsym)) 674 { 675 pe.Helem = null; 676 continue; 677 } 678 break; 679 680 case OPind: 681 if (tybasic(he.EV.E1.Ety) == TYimmutPtr) 682 break; 683 goto Ltouch; 684 685 case OPstrlen: 686 case OPstrcmp: 687 case OPmemcmp: 688 case OPbt: 689 goto Ltouch; 690 691 case OPvp_fp: 692 case OPcvp_fp: 693 if (flag == 0) /* function calls destroy vptrfptr's, */ 694 break; /* not indirect assignments */ 695 Ltouch: 696 pe.Helem = null; 697 break; 698 699 default: 700 break; 701 } 702 } 703 cgcs.hcsarray.touchfunci[flag] = cgcs.hcstab.length; 704 } 705 706 707 /******************************* 708 * Eliminate all common subexpressions that 709 * do any indirection ("starred" elems). 710 */ 711 712 @trusted 713 void touchstar(ref CGCS cgcs) 714 { 715 foreach (ref hcs; cgcs.hcstab[cgcs.hcsarray.touchstari .. $]) 716 { 717 const e = hcs.Helem; 718 if (e && 719 (e.Eoper == OPind && tybasic(e.EV.E1.Ety) != TYimmutPtr || 720 e.Eoper == OPbt) ) 721 hcs.Helem = null; 722 } 723 cgcs.hcsarray.touchstari = cgcs.hcstab.length; 724 } 725 726 /***************************************** 727 * Eliminate any common subexpressions that could be modified 728 * if a handle pointer access occurs. 729 */ 730 731 @trusted 732 void touchaccess(ref Barray!HCS hcstab, const elem *ev) pure nothrow 733 { 734 const ev1 = ev.EV.E1; 735 foreach (ref hcs; hcstab[]) 736 { 737 const e = hcs.Helem; 738 /* Invalidate any previous handle pointer accesses that */ 739 /* are not accesses of ev. */ 740 if (e && (e.Eoper == OPvp_fp || e.Eoper == OPcvp_fp) && e.EV.E1 != ev1) 741 hcs.Helem = null; 742 } 743 }