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