1 /** 2 * Other global optimizations 3 * 4 * Copyright: Copyright (C) 1986-1998 by Symantec 5 * Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 7 * License: Distributed under the Boost Software License, Version 1.0. 8 * https://www.boost.org/LICENSE_1_0.txt 9 * Source: https://github.com/dlang/dmd/blob/master/src/dmd/backend/gother.d 10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/gother.d 11 * Documentation: https://dlang.org/phobos/dmd_backend_gother.html 12 */ 13 14 module dmd.backend.gother; 15 16 version (SCPP) 17 version = COMPILE; 18 version (MARS) 19 version = COMPILE; 20 21 version (COMPILE) 22 { 23 24 import core.stdc.stdio; 25 import core.stdc.stdlib; 26 import core.stdc.time; 27 28 import dmd.backend.cc; 29 import dmd.backend.cdef; 30 import dmd.backend.code_x86; 31 import dmd.backend.oper; 32 import dmd.backend.global; 33 import dmd.backend.goh; 34 import dmd.backend.el; 35 import dmd.backend.symtab; 36 import dmd.backend.ty; 37 import dmd.backend.type; 38 39 import dmd.backend.barray; 40 import dmd.backend.dlist; 41 import dmd.backend.dvec; 42 43 nothrow: 44 @safe: 45 46 @trusted 47 char symbol_isintab(const Symbol *s) { return sytab[s.Sclass] & SCSS; } 48 49 extern (C++): 50 51 version (SCPP) 52 import parser; 53 54 version (MARS) 55 import dmd.backend.errors; 56 57 /**********************************************************************/ 58 59 alias Elemdatas = Rarray!(Elemdata); 60 61 // Lists to help identify ranges of variables 62 struct Elemdata 63 { 64 nothrow: 65 elem *pelem; // the elem in question 66 block *pblock; // which block it's in 67 Barray!(elem*) rdlist; // list of definition elems for *pelem 68 69 /*************************** 70 * Reset memory so this allocation can be re-used. 71 */ 72 @trusted 73 void reset() 74 { 75 rdlist.reset(); 76 } 77 78 /****************** 79 * Initialize instance at ed. 80 */ 81 void emplace(elem *e,block *b) 82 { 83 this.pelem = e; 84 this.pblock = b; 85 } 86 } 87 88 /******************************** 89 * Find `e` in Elemdata list. 90 * Params: 91 * e = elem to find 92 * Returns: 93 * Elemdata entry if found, 94 * null if not 95 */ 96 @trusted 97 Elemdata* find(ref Elemdatas eds, elem *e) 98 { 99 foreach (ref edl; eds) 100 { 101 if (edl.pelem == e) 102 return &edl; 103 } 104 return null; 105 } 106 107 /***************** 108 * Free list of Elemdata's. 109 */ 110 111 private void elemdatafree(ref Elemdatas eds) 112 { 113 foreach (ref ed; eds) 114 ed.reset(); 115 eds.reset(); 116 } 117 118 private __gshared 119 { 120 Elemdatas eqeqlist; // array of Elemdata's of OPeqeq & OPne elems 121 Elemdatas rellist; // array of Elemdata's of relop elems 122 Elemdatas inclist; // array of Elemdata's of increment elems 123 } 124 125 /*************************** Constant Propagation ***************************/ 126 127 128 /************************** 129 * Constant propagation. 130 * Also detects use of variable before any possible def. 131 */ 132 133 @trusted 134 void constprop() 135 { 136 rd_compute(); 137 intranges(rellist, inclist); // compute integer ranges 138 eqeqranges(eqeqlist); // see if we can eliminate some relationals 139 elemdatafree(eqeqlist); 140 elemdatafree(rellist); 141 elemdatafree(inclist); 142 } 143 144 /************************************ 145 * Compute reaching definitions. 146 * Note: RD vectors are destroyed by this. 147 */ 148 149 private __gshared block *thisblock; 150 151 @trusted 152 private void rd_compute() 153 { 154 if (debugc) printf("constprop()\n"); 155 assert(dfo); 156 flowrd(); /* compute reaching definitions (rd) */ 157 if (go.defnod.length == 0) /* if no reaching defs */ 158 return; 159 assert(rellist.length == 0 && inclist.length == 0 && eqeqlist.length == 0); 160 block_clearvisit(); 161 foreach (b; dfo[]) // for each block 162 { 163 switch (b.BC) 164 { 165 case BCjcatch: 166 case BC_finally: 167 case BC_lpad: 168 case BCasm: 169 case BCcatch: 170 block_visit(b); 171 break; 172 173 default: 174 break; 175 } 176 } 177 178 foreach (i, b; dfo[]) // for each block 179 { 180 thisblock = b; 181 182 //printf("block %d Bin ",i); vec_println(b.Binrd); 183 //printf(" Bout "); vec_println(b.Boutrd); 184 185 if (b.Bflags & BFLvisited) 186 continue; // not reliable for this block 187 if (b.Belem) 188 { 189 conpropwalk(b.Belem,b.Binrd); 190 191 debug 192 if (!(vec_equal(b.Binrd,b.Boutrd))) 193 { 194 int j; 195 196 printf("block %d Binrd ", cast(int) i); vec_println(b.Binrd); 197 printf(" Boutrd "); vec_println(b.Boutrd); 198 WReqn(b.Belem); 199 printf("\n"); 200 vec_xorass(b.Binrd,b.Boutrd); 201 j = cast(int)vec_index(0,b.Binrd); 202 WReqn(go.defnod[j].DNelem); 203 printf("\n"); 204 } 205 206 assert(vec_equal(b.Binrd,b.Boutrd)); 207 } 208 } 209 } 210 211 /*************************** 212 * Support routine for constprop(). 213 * Visit each elem in order 214 * If elem is a reference to a variable, and 215 * all the reaching defs of that variable are 216 * defining it to be a specific constant, 217 * Replace reference with that constant. 218 * Generate warning if no reaching defs for a 219 * variable, and the variable is on the stack 220 * or in a register. 221 * If elem is an assignment or function call or OPasm 222 * Modify vector of reaching defs. 223 */ 224 @trusted 225 private void conpropwalk(elem *n,vec_t IN) 226 { 227 vec_t L,R; 228 elem *t; 229 230 assert(n && IN); 231 //printf("conpropwalk()\n"),elem_print(n); 232 const op = n.Eoper; 233 if (op == OPcolon || op == OPcolon2) 234 { 235 L = vec_clone(IN); 236 switch (el_returns(n.EV.E1) * 2 | el_returns(n.EV.E2)) 237 { 238 case 3: // E1 and E2 return 239 conpropwalk(n.EV.E1,L); 240 conpropwalk(n.EV.E2,IN); 241 vec_orass(IN,L); // IN = L | R 242 break; 243 244 case 2: // E1 returns 245 conpropwalk(n.EV.E1,IN); 246 conpropwalk(n.EV.E2,L); 247 break; 248 249 case 1: // E2 returns 250 conpropwalk(n.EV.E1,L); 251 conpropwalk(n.EV.E2,IN); 252 break; 253 254 case 0: // neither returns 255 conpropwalk(n.EV.E1,L); 256 vec_copy(L,IN); 257 conpropwalk(n.EV.E2,L); 258 break; 259 260 default: 261 break; 262 } 263 vec_free(L); 264 } 265 else if (op == OPandand || op == OPoror) 266 { 267 conpropwalk(n.EV.E1,IN); 268 R = vec_clone(IN); 269 conpropwalk(n.EV.E2,R); 270 if (el_returns(n.EV.E2)) 271 vec_orass(IN,R); // IN |= R 272 vec_free(R); 273 } 274 else if (OTunary(op)) 275 goto L3; 276 else if (ERTOL(n)) 277 { 278 conpropwalk(n.EV.E2,IN); 279 L3: 280 t = n.EV.E1; 281 if (OTassign(op)) 282 { 283 if (t.Eoper == OPvar) 284 { 285 // Note that the following ignores OPnegass 286 if (OTopeq(op) && sytab[t.EV.Vsym.Sclass] & SCRD) 287 { 288 Barray!(elem*) rdl; 289 listrds(IN,t,null,&rdl); 290 if (!(config.flags & CFGnowarning)) // if warnings are enabled 291 chkrd(t,rdl); 292 if (auto e = chkprop(t,rdl)) 293 { // Replace (t op= exp) with (t = e op exp) 294 295 e = el_copytree(e); 296 e.Ety = t.Ety; 297 n.EV.E2 = el_bin(opeqtoop(op),n.Ety,e,n.EV.E2); 298 n.Eoper = OPeq; 299 } 300 rdl.dtor(); 301 } 302 } 303 else 304 conpropwalk(t,IN); 305 } 306 else 307 conpropwalk(t,IN); 308 } 309 else if (OTbinary(op)) 310 { 311 if (OTassign(op)) 312 { t = n.EV.E1; 313 if (t.Eoper != OPvar) 314 conpropwalk(t,IN); 315 } 316 else 317 conpropwalk(n.EV.E1,IN); 318 conpropwalk(n.EV.E2,IN); 319 } 320 321 // Collect data for subsequent optimizations 322 if (OTbinary(op) && n.EV.E1.Eoper == OPvar && n.EV.E2.Eoper == OPconst) 323 { 324 switch (op) 325 { 326 case OPlt: 327 case OPgt: 328 case OPle: 329 case OPge: 330 // Collect compare elems and their rd's in the rellist list 331 if (tyintegral(n.EV.E1.Ety) && 332 tyintegral(n.EV.E2.Ety) 333 ) 334 { 335 //printf("appending to rellist\n"); elem_print(n); 336 //printf("\trellist IN: "); vec_print(IN); printf("\n"); 337 auto pdata = rellist.push(); 338 pdata.emplace(n,thisblock); 339 listrds(IN, n.EV.E1, null, &pdata.rdlist); 340 } 341 break; 342 343 case OPaddass: 344 case OPminass: 345 case OPpostinc: 346 case OPpostdec: 347 // Collect increment elems and their rd's in the inclist list 348 if (tyintegral(n.EV.E1.Ety)) 349 { 350 //printf("appending to inclist\n"); elem_print(n); 351 //printf("\tinclist IN: "); vec_print(IN); printf("\n"); 352 auto pdata = inclist.push(); 353 pdata.emplace(n,thisblock); 354 listrds(IN, n.EV.E1, null, &pdata.rdlist); 355 } 356 break; 357 358 case OPne: 359 case OPeqeq: 360 // Collect compare elems and their rd's in the rellist list 361 if (tyintegral(n.EV.E1.Ety) && !tyvector(n.Ety)) 362 { //printf("appending to eqeqlist\n"); elem_print(n); 363 auto pdata = eqeqlist.push(); 364 pdata.emplace(n,thisblock); 365 listrds(IN, n.EV.E1, null, &pdata.rdlist); 366 } 367 break; 368 369 default: 370 break; 371 } 372 } 373 374 375 if (OTdef(op)) /* if definition elem */ 376 updaterd(n,IN,null); /* then update IN vector */ 377 378 /* now we get to the part that checks to see if we can */ 379 /* propagate a constant. */ 380 if (op == OPvar && sytab[n.EV.Vsym.Sclass] & SCRD) 381 { 382 //printf("const prop: %s\n", n.EV.Vsym.Sident.ptr); 383 Barray!(elem*) rdl; 384 listrds(IN,n,null,&rdl); 385 386 if (!(config.flags & CFGnowarning)) // if warnings are enabled 387 chkrd(n,rdl); 388 elem *e = chkprop(n,rdl); 389 if (e) 390 { tym_t nty; 391 392 nty = n.Ety; 393 el_copy(n,e); 394 n.Ety = nty; // retain original type 395 } 396 rdl.dtor(); 397 } 398 } 399 400 /****************************** 401 * Give error if there are no reaching defs for variable v. 402 */ 403 404 @trusted 405 private void chkrd(elem *n, Barray!(elem*) rdlist) 406 { 407 Symbol *sv; 408 int unambig; 409 410 sv = n.EV.Vsym; 411 assert(sytab[sv.Sclass] & SCRD); 412 if (sv.Sflags & SFLnord) // if already printed a warning 413 return; 414 if (sv.ty() & (mTYvolatile | mTYshared)) 415 return; 416 unambig = sv.Sflags & SFLunambig; 417 foreach (d; rdlist) 418 { 419 elem_debug(d); 420 if (d.Eoper == OPasm) /* OPasm elems ruin everything */ 421 return; 422 if (OTassign(d.Eoper)) 423 { 424 if (d.EV.E1.Eoper == OPvar) 425 { 426 if (d.EV.E1.EV.Vsym == sv) 427 return; 428 } 429 else if (!unambig) 430 return; 431 } 432 else 433 { 434 if (!unambig) 435 return; 436 } 437 } 438 439 // If there are any asm blocks, don't print the message 440 foreach (b; dfo[]) 441 if (b.BC == BCasm) 442 return; 443 444 // If variable contains bit fields, don't print message (because if 445 // bit field is the first set, then we get a spurious warning). 446 // STL uses 0 sized structs to transmit type information, so 447 // don't complain about them being used before set. 448 if (type_struct(sv.Stype)) 449 { 450 if (sv.Stype.Ttag.Sstruct.Sflags & (STRbitfields | STR0size)) 451 return; 452 } 453 454 static if (0) 455 { 456 // If variable is zero length static array, don't print message. 457 // BUG: Suppress error even if variable is initialized with void. 458 if (sv.Stype.Tty == TYarray && sv.Stype.Tdim == 0) 459 { 460 printf("sv.Sident = %s\n", sv.Sident); 461 return; 462 } 463 } 464 465 version (SCPP) 466 { 467 { OutBuffer buf; 468 char *p2; 469 470 type_tostring(&buf, sv.Stype); 471 buf.writeByte(' '); 472 buf.write(sv.Sident.ptr); 473 p2 = buf.toString(); 474 warerr(WM.WM_used_b4_set, p2); // variable used before set 475 } 476 } 477 478 //version (MARS) 479 version(none) 480 { 481 /* Watch out for: 482 void test() 483 { 484 void[0] x; 485 auto y = x; 486 } 487 */ 488 if (type_size(sv.Stype) != 0) 489 { 490 error(n.Esrcpos.Sfilename, n.Esrcpos.Slinnum, n.Esrcpos.Scharnum, 491 "variable %s used before set", sv.Sident.ptr); 492 } 493 } 494 495 sv.Sflags |= SFLnord; // no redundant messages 496 //elem_print(n); 497 } 498 499 /********************************** 500 * Look through the vector of reaching defs (IN) to see 501 * if all defs of n are of the same constant. If so, replace 502 * n with that constant. 503 * Bit fields are gross, so don't propagate anything with assignments 504 * to a bit field. 505 * Note the flaw in the reaching def vector. There is currently no way 506 * to detect RDs from when the function is invoked, i.e. RDs for parameters, 507 * statics and globals. This could be fixed by adding dummy defs for 508 * them before startblock, but we just kludge it and don't propagate 509 * stuff for them. 510 * Returns: 511 * null do not propagate constant 512 * e constant elem that we should replace n with 513 */ 514 515 @trusted 516 private elem * chkprop(elem *n, Barray!(elem*) rdlist) 517 { 518 elem *foundelem = null; 519 int unambig; 520 Symbol *sv; 521 tym_t nty; 522 uint nsize; 523 targ_size_t noff; 524 525 //printf("checkprop: "); WReqn(n); printf("\n"); 526 assert(n && n.Eoper == OPvar); 527 elem_debug(n); 528 sv = n.EV.Vsym; 529 assert(sytab[sv.Sclass] & SCRD); 530 nty = n.Ety; 531 if (!tyscalar(nty)) 532 goto noprop; 533 nsize = cast(uint)size(nty); 534 noff = n.EV.Voffset; 535 unambig = sv.Sflags & SFLunambig; 536 foreach (d; rdlist) 537 { 538 elem_debug(d); 539 540 //printf("\trd: "); WReqn(d); printf("\n"); 541 if (d.Eoper == OPasm) /* OPasm elems ruin everything */ 542 goto noprop; 543 544 // Runs afoul of Buzilla 4506 545 /*if (OTassign(d.Eoper) && EBIN(d))*/ // if assignment elem 546 547 if (OTassign(d.Eoper)) // if assignment elem 548 { 549 elem *t = d.EV.E1; 550 551 if (t.Eoper == OPvar) 552 { 553 assert(t.EV.Vsym == sv); 554 555 if (d.Eoper == OPstreq || 556 !tyscalar(t.Ety)) 557 goto noprop; // not worth bothering with these cases 558 559 if (d.Eoper == OPnegass) 560 goto noprop; // don't bother with this case, either 561 562 /* Everything must match or we must skip this variable */ 563 /* (in case of assigning to overlapping unions, etc.) */ 564 if (t.EV.Voffset != noff || 565 /* If sizes match, we are ok */ 566 size(t.Ety) != nsize && 567 !(d.EV.E2.Eoper == OPconst && size(t.Ety) > nsize && !tyfloating(d.EV.E2.Ety))) 568 goto noprop; 569 } 570 else 571 { 572 if (unambig) /* unambiguous assignments only */ 573 continue; 574 goto noprop; 575 } 576 if (d.Eoper != OPeq) 577 goto noprop; 578 } 579 else /* must be a call elem */ 580 { 581 if (unambig) 582 continue; 583 else 584 goto noprop; /* could be affected */ 585 } 586 587 if (d.EV.E2.Eoper == OPconst || d.EV.E2.Eoper == OPrelconst) 588 { 589 if (foundelem) /* already found one */ 590 { /* then they must be the same */ 591 if (!el_match(foundelem,d.EV.E2)) 592 goto noprop; 593 } 594 else /* else this is it */ 595 foundelem = d.EV.E2; 596 } 597 else 598 goto noprop; 599 } 600 601 if (foundelem) /* if we got one */ 602 { /* replace n with foundelem */ 603 debug if (debugc) 604 { 605 printf("const prop ("); 606 WReqn(n); 607 printf(" replaced by "); 608 WReqn(foundelem); 609 printf("), %p to %p\n",foundelem,n); 610 } 611 go.changes++; 612 return foundelem; 613 } 614 noprop: 615 return null; 616 } 617 618 /*********************************** 619 * Find all the reaching defs of OPvar e. 620 * Params: 621 * IN = vector of definition nodes 622 * e = OPvar 623 * f = if not null, set RD bits in it 624 * rdlist = if not null, append reaching defs to it 625 */ 626 627 @trusted 628 void listrds(vec_t IN,elem *e,vec_t f, Barray!(elem*)* rdlist) 629 { 630 uint i; 631 uint unambig; 632 Symbol *s; 633 uint nsize; 634 targ_size_t noff; 635 tym_t ty; 636 637 //printf("listrds: "); WReqn(e); printf("\n"); 638 assert(IN); 639 assert(e.Eoper == OPvar); 640 s = e.EV.Vsym; 641 ty = e.Ety; 642 if (tyscalar(ty)) 643 nsize = cast(uint)size(ty); 644 noff = e.EV.Voffset; 645 unambig = s.Sflags & SFLunambig; 646 if (f) 647 vec_clear(f); 648 for (i = 0; (i = cast(uint) vec_index(i, IN)) < go.defnod.length; ++i) 649 { 650 elem *d = go.defnod[i].DNelem; 651 //printf("\tlooking at "); WReqn(d); printf("\n"); 652 const op = d.Eoper; 653 if (op == OPasm) // assume ASM elems define everything 654 goto listit; 655 if (OTassign(op)) 656 { elem *t = d.EV.E1; 657 658 if (t.Eoper == OPvar && t.EV.Vsym == s) 659 { if (op == OPstreq) 660 goto listit; 661 if (!tyscalar(ty) || !tyscalar(t.Ety)) 662 goto listit; 663 // If t does not overlap e, then it doesn't affect things 664 if (noff + nsize > t.EV.Voffset && 665 t.EV.Voffset + size(t.Ety) > noff) 666 goto listit; // it's an assignment to s 667 } 668 else if (t.Eoper != OPvar && !unambig) 669 goto listit; /* assignment through pointer */ 670 } 671 else if (!unambig) 672 goto listit; /* probably a function call */ 673 continue; 674 675 listit: 676 //printf("\tlisting "); WReqn(d); printf("\n"); 677 if (f) 678 vec_setbit(i,f); 679 else 680 (*rdlist).push(d); // add the definition node 681 } 682 } 683 684 /******************************************** 685 * Look at reaching defs for expressions of the form (v == c) and (v != c). 686 * If all definitions of v are c or are not c, then we can replace the 687 * expression with 1 or 0. 688 * Params: 689 * eqeqlist = array of == and != expressions 690 */ 691 692 @trusted 693 private void eqeqranges(ref Elemdatas eqeqlist) 694 { 695 Symbol *v; 696 int sz; 697 elem *e; 698 targ_llong c; 699 int result; 700 701 foreach (ref rel; eqeqlist) 702 { 703 e = rel.pelem; 704 v = e.EV.E1.EV.Vsym; 705 if (!(sytab[v.Sclass] & SCRD)) 706 continue; 707 sz = tysize(e.EV.E1.Ety); 708 c = el_tolong(e.EV.E2); 709 710 result = -1; // result not known yet 711 foreach (erd; rel.rdlist) 712 { 713 elem *erd1; 714 int szrd; 715 int tmp; 716 717 elem_debug(erd); 718 if (erd.Eoper != OPeq || 719 (erd1 = erd.EV.E1).Eoper != OPvar || 720 erd.EV.E2.Eoper != OPconst 721 ) 722 goto L1; 723 szrd = tysize(erd1.Ety); 724 if (erd1.EV.Voffset + szrd <= e.EV.E1.EV.Voffset || 725 e.EV.E1.EV.Voffset + sz <= erd1.EV.Voffset) 726 continue; // doesn't affect us, skip it 727 if (szrd != sz || e.EV.E1.EV.Voffset != erd1.EV.Voffset) 728 goto L1; // overlapping - forget it 729 730 tmp = (c == el_tolong(erd.EV.E2)); 731 if (result == -1) 732 result = tmp; 733 else if (result != tmp) 734 goto L1; 735 } 736 if (result >= 0) 737 { 738 //printf("replacing with %d\n",result); 739 el_free(e.EV.E1); 740 el_free(e.EV.E2); 741 e.EV.Vint = (e.Eoper == OPeqeq) ? result : result ^ 1; 742 e.Eoper = OPconst; 743 } 744 L1: 745 } 746 } 747 748 /****************************** 749 * Examine rellist and inclist to determine if any of the signed compare 750 * elems in rellist can be replace by unsigned compares. 751 * Params: 752 * rellist = array of relationals in function 753 * inclist = array of increment elems in function 754 */ 755 756 @trusted 757 private void intranges(ref Elemdatas rellist, ref Elemdatas inclist) 758 { 759 block *rb; 760 block *ib; 761 Symbol *v; 762 elem *rdeq; 763 elem *rdinc; 764 uint incop,relatop; 765 targ_llong initial,increment,final_; 766 767 if (debugc) printf("intranges()\n"); 768 static if (0) 769 { 770 foreach (int i, ref rel; rellist) 771 { 772 printf("[%d] rel.pelem: ", i); WReqn(rel.pelem); printf("\n"); 773 } 774 } 775 776 foreach (ref rel; rellist) 777 { 778 rb = rel.pblock; 779 //printf("rel.pelem: "); WReqn(rel.pelem); printf("\n"); 780 assert(rel.pelem.EV.E1.Eoper == OPvar); 781 v = rel.pelem.EV.E1.EV.Vsym; 782 783 // RD info is only reliable for registers and autos 784 if (!(sytab[v.Sclass] & SCRD)) 785 continue; 786 787 /* Look for two rd's: an = and an increment */ 788 if (rel.rdlist.length != 2) 789 continue; 790 rdeq = rel.rdlist[1]; 791 if (rdeq.Eoper != OPeq) 792 { rdinc = rdeq; 793 rdeq = rel.rdlist[0]; 794 if (rdeq.Eoper != OPeq) 795 continue; 796 } 797 else 798 rdinc = rel.rdlist[0]; 799 800 static if (0) 801 { 802 printf("\neq: "); WReqn(rdeq); printf("\n"); 803 printf("rel: "); WReqn(rel.pelem); printf("\n"); 804 printf("inc: "); WReqn(rdinc); printf("\n"); 805 } 806 807 incop = rdinc.Eoper; 808 if (!OTpost(incop) && incop != OPaddass && incop != OPminass) 809 continue; 810 811 /* lvalues should be unambiguous defs */ 812 if (rdeq.EV.E1.Eoper != OPvar || rdinc.EV.E1.Eoper != OPvar) 813 continue; 814 /* rvalues should be constants */ 815 if (rdeq.EV.E2.Eoper != OPconst || rdinc.EV.E2.Eoper != OPconst) 816 continue; 817 818 /* Ensure that the only defs reaching the increment elem (rdinc) */ 819 /* are rdeq and rdinc. */ 820 foreach (ref iel; inclist) 821 { 822 elem *rd1; 823 elem *rd2; 824 825 ib = iel.pblock; 826 if (iel.pelem != rdinc) 827 continue; /* not our increment elem */ 828 if (iel.rdlist.length != 2) 829 { 830 //printf("!= 2\n"); 831 break; 832 } 833 rd1 = iel.rdlist[0]; 834 rd2 = iel.rdlist[1]; 835 /* The rd's for the relational elem (rdeq,rdinc) must be */ 836 /* the same as the rd's for tne increment elem (rd1,rd2). */ 837 if (rd1 == rdeq && rd2 == rdinc || rd1 == rdinc && rd2 == rdeq) 838 goto found; 839 } 840 goto nextrel; 841 842 found: 843 // Check that all paths from rdinc to rdinc must pass through rdrel 844 { 845 int i; 846 847 // ib: block of increment 848 // rb: block of relational 849 i = loopcheck(ib,ib,rb); 850 block_clearvisit(); 851 if (i) 852 continue; 853 } 854 855 /* Gather initial, increment, and final values for loop */ 856 initial = el_tolong(rdeq.EV.E2); 857 increment = el_tolong(rdinc.EV.E2); 858 if (incop == OPpostdec || incop == OPminass) 859 increment = -increment; 860 relatop = rel.pelem.Eoper; 861 final_ = el_tolong(rel.pelem.EV.E2); 862 //printf("initial = %d, increment = %d, final_ = %d\n",initial,increment,final_); 863 864 /* Determine if we can make the relational an unsigned */ 865 if (initial >= 0) 866 { 867 if (final_ == 0 && relatop == OPge) 868 { 869 /* if the relation is (i >= 0) there is likely some dependency 870 * on switching sign, so do not make it unsigned 871 */ 872 } 873 else if (final_ >= initial) 874 { 875 if (increment > 0 && ((final_ - initial) % increment) == 0) 876 goto makeuns; 877 } 878 else if (final_ >= 0) 879 { /* 0 <= final_ < initial */ 880 if (increment < 0 && ((final_ - initial) % increment) == 0 && 881 !(final_ + increment < 0 && 882 (relatop == OPge || relatop == OPlt) 883 ) 884 ) 885 { 886 makeuns: 887 if (!tyuns(rel.pelem.EV.E2.Ety)) 888 { 889 rel.pelem.EV.E2.Ety = touns(rel.pelem.EV.E2.Ety); 890 rel.pelem.Nflags |= NFLtouns; 891 892 debug 893 if (debugc) 894 { WReqn(rel.pelem); 895 printf(" made unsigned, initial = %lld, increment = %lld," ~ 896 " final_ = %lld\n",cast(long)initial,cast(long)increment,cast(long)final_); 897 } 898 go.changes++; 899 } 900 901 static if (0) 902 { 903 // Eliminate loop if it is empty 904 if (relatop == OPlt && 905 rb.BC == BCiftrue && 906 list_block(rb.Bsucc) == rb && 907 rb.Belem.Eoper == OPcomma && 908 rb.Belem.EV.E1 == rdinc && 909 rb.Belem.EV.E2 == rel.pelem 910 ) 911 { 912 rel.pelem.Eoper = OPeq; 913 rel.pelem.Ety = rel.pelem.EV.E1.Ety; 914 rb.BC = BCgoto; 915 list_subtract(&rb.Bsucc,rb); 916 list_subtract(&rb.Bpred,rb); 917 918 debug 919 if (debugc) 920 { 921 WReqn(rel.pelem); 922 printf(" eliminated loop\n"); 923 } 924 925 go.changes++; 926 } 927 } 928 } 929 } 930 } 931 932 nextrel: 933 } 934 } 935 936 /****************************** 937 * Look for initialization and increment expressions in loop. 938 * Very similar to intranges(). 939 * Params: 940 * rellist = list of relationals in function 941 * inclist = list of increment elems in function. 942 * erel = loop compare expression of the form (v < c) 943 * rdeq = set to loop initialization of v 944 * rdinc = set to loop increment of v 945 * Returns: 946 * false if cannot find rdeq or rdinc 947 */ 948 949 @trusted 950 private bool returnResult(bool result) 951 { 952 elemdatafree(eqeqlist); 953 elemdatafree(rellist); 954 elemdatafree(inclist); 955 return result; 956 } 957 958 @trusted 959 bool findloopparameters(elem* erel, ref elem* rdeq, ref elem* rdinc) 960 { 961 if (debugc) printf("findloopparameters()\n"); 962 const bool log = false; 963 964 assert(erel.EV.E1.Eoper == OPvar); 965 Symbol* v = erel.EV.E1.EV.Vsym; 966 967 // RD info is only reliable for registers and autos 968 if (!(sytab[v.Sclass] & SCRD)) 969 return false; 970 971 rd_compute(); // compute rellist, inclist, eqeqlist 972 973 /* Find `erel` in `rellist` 974 */ 975 Elemdata* rel = rellist.find(erel); 976 if (!rel) 977 { 978 if (log) printf("\trel not found\n"); 979 return returnResult(false); 980 } 981 982 block* rb = rel.pblock; 983 //printf("rel.pelem: "); WReqn(rel.pelem); printf("\n"); 984 985 986 // Look for one reaching definition: an increment 987 if (rel.rdlist.length != 1) 988 { 989 if (log) printf("\tnitems = %d\n", cast(int)rel.rdlist.length); 990 return returnResult(false); 991 } 992 993 rdinc = rel.rdlist[0]; 994 995 static if (0) 996 { 997 printf("\neq: "); WReqn(rdeq); printf("\n"); 998 printf("rel: "); WReqn(rel.pelem); printf("\n"); 999 printf("inc: "); WReqn(rdinc); printf("\n"); 1000 } 1001 1002 uint incop = rdinc.Eoper; 1003 if (!OTpost(incop) && incop != OPaddass && incop != OPminass) 1004 { 1005 if (log) printf("\tnot += or -=\n"); 1006 return returnResult(false); 1007 } 1008 1009 Elemdata* iel = inclist.find(rdinc); 1010 if (!iel) 1011 { 1012 if (log) printf("\trdinc not found\n"); 1013 return returnResult(false); 1014 } 1015 1016 /* The increment should have two reaching definitions: 1017 * the initialization 1018 * the increment itself 1019 * We already have the increment (as rdinc), but need the initialization (rdeq) 1020 */ 1021 if (iel.rdlist.length != 2) 1022 { 1023 if (log) printf("nitems != 2\n"); 1024 return returnResult(false); 1025 } 1026 elem *rd1 = iel.rdlist[0]; 1027 elem *rd2 = iel.rdlist[1]; 1028 if (rd1 == rdinc) 1029 rdeq = rd2; 1030 else if (rd2 == rdinc) 1031 rdeq = rd1; 1032 else 1033 { 1034 if (log) printf("\tnot (rdeq,rdinc)\n"); 1035 return returnResult(false); 1036 } 1037 1038 // lvalues should be unambiguous defs 1039 if (rdeq.Eoper != OPeq || rdeq.EV.E1.Eoper != OPvar || rdinc.EV.E1.Eoper != OPvar) 1040 { 1041 if (log) printf("\tnot OPvar\n"); 1042 return returnResult(false); 1043 } 1044 1045 // rvalues should be constants 1046 if (rdeq.EV.E2.Eoper != OPconst || rdinc.EV.E2.Eoper != OPconst) 1047 { 1048 if (log) printf("\tnot OPconst\n"); 1049 return returnResult(false); 1050 } 1051 1052 /* Check that all paths from rdinc to rdinc must pass through rdrel 1053 * iel.pblock = block of increment 1054 * rel.pblock = block of relational 1055 */ 1056 int i = loopcheck(iel.pblock,iel.pblock,rel.pblock); 1057 block_clearvisit(); 1058 if (i) 1059 { 1060 if (log) printf("\tnot loopcheck()\n"); 1061 return returnResult(false); 1062 } 1063 1064 return returnResult(true); 1065 } 1066 1067 /*********************** 1068 * Return true if there is a path from start to inc without 1069 * passing through rel. 1070 */ 1071 1072 @trusted 1073 private int loopcheck(block *start,block *inc,block *rel) 1074 { 1075 if (!(start.Bflags & BFLvisited)) 1076 { start.Bflags |= BFLvisited; /* guarantee eventual termination */ 1077 foreach (list; ListRange(start.Bsucc)) 1078 { 1079 block *b = cast(block *) list_ptr(list); 1080 if (b != rel && (b == inc || loopcheck(b,inc,rel))) 1081 return true; 1082 } 1083 } 1084 return false; 1085 } 1086 1087 /**************************** 1088 * Do copy propagation. 1089 * Copy propagation elems are of the form OPvar=OPvar, and they are 1090 * in go.expnod[]. 1091 */ 1092 1093 1094 @trusted 1095 void copyprop() 1096 { 1097 out_regcand(&globsym); 1098 if (debugc) printf("copyprop()\n"); 1099 assert(dfo); 1100 1101 Louter: 1102 while (1) 1103 { 1104 flowcp(); /* compute available copy statements */ 1105 if (go.exptop <= 1) 1106 return; // none available 1107 static if (0) 1108 { 1109 foreach (i; 1 .. go.exptop) 1110 { 1111 printf("go.expnod[%d] = (",i); 1112 WReqn(go.expnod[i]); 1113 printf(");\n"); 1114 } 1115 } 1116 foreach (i, b; dfo[]) // for each block 1117 { 1118 if (b.Belem) 1119 { 1120 bool recalc; 1121 static if (0) 1122 { 1123 printf("B%d, elem (",i); 1124 WReqn(b.Belem); printf(")\nBin "); 1125 vec_println(b.Bin); 1126 recalc = copyPropWalk(b.Belem,b.Bin); 1127 printf("Bino "); 1128 vec_println(b.Bin); 1129 printf("Bout "); 1130 vec_println(b.Bout); 1131 } 1132 else 1133 { 1134 recalc = copyPropWalk(b.Belem,b.Bin); 1135 } 1136 /*assert(vec_equal(b.Bin,b.Bout)); */ 1137 /* The previous assert() is correct except */ 1138 /* for the following case: */ 1139 /* a=b; d=a; a=b; */ 1140 /* The vectors don't match because the */ 1141 /* equations changed to: */ 1142 /* a=b; d=b; a=b; */ 1143 /* and the d=b copy elem now reaches the end */ 1144 /* of the block (the d=a elem didn't). */ 1145 if (recalc) 1146 continue Louter; 1147 } 1148 } 1149 return; 1150 } 1151 } 1152 1153 /***************************** 1154 * Walk tree n, doing copy propagation as we go. 1155 * Keep IN up to date. 1156 * Params: 1157 * n = tree to walk & do copy propagation in 1158 * IN = vector of live copy expressions, updated as progress is made 1159 * Returns: 1160 * true if need to recalculate data flow equations and try again 1161 */ 1162 1163 @trusted 1164 private bool copyPropWalk(elem *n,vec_t IN) 1165 { 1166 bool recalc = false; 1167 int nocp = 0; 1168 1169 void cpwalk(elem* n, vec_t IN) 1170 { 1171 assert(n && IN); 1172 /*chkvecdim(go.exptop,0);*/ 1173 if (recalc) 1174 return; 1175 1176 elem *t; 1177 const op = n.Eoper; 1178 if (op == OPcolon || op == OPcolon2) 1179 { 1180 vec_t L = vec_clone(IN); 1181 cpwalk(n.EV.E1,L); 1182 cpwalk(n.EV.E2,IN); 1183 vec_andass(IN,L); // IN = L & R 1184 vec_free(L); 1185 } 1186 else if (op == OPandand || op == OPoror) 1187 { 1188 cpwalk(n.EV.E1,IN); 1189 vec_t L = vec_clone(IN); 1190 cpwalk(n.EV.E2,L); 1191 vec_andass(IN,L); // IN = L & R 1192 vec_free(L); 1193 } 1194 else if (OTunary(op)) 1195 { 1196 t = n.EV.E1; 1197 if (OTassign(op)) 1198 { 1199 if (t.Eoper == OPind) 1200 cpwalk(t.EV.E1,IN); 1201 } 1202 else if (op == OPctor || op == OPdtor) 1203 { 1204 /* This kludge is necessary because in except_pop() 1205 * an el_match is done on the lvalue. If copy propagation 1206 * changes the OPctor but not the corresponding OPdtor, 1207 * then the match won't happen and except_pop() 1208 * will fail. 1209 */ 1210 nocp++; 1211 cpwalk(t,IN); 1212 nocp--; 1213 } 1214 else 1215 cpwalk(t,IN); 1216 } 1217 else if (OTassign(op)) 1218 { 1219 cpwalk(n.EV.E2,IN); 1220 t = n.EV.E1; 1221 if (t.Eoper == OPind) 1222 cpwalk(t,IN); 1223 else 1224 { 1225 debug if (t.Eoper != OPvar) elem_print(n); 1226 assert(t.Eoper == OPvar); 1227 } 1228 } 1229 else if (ERTOL(n)) 1230 { 1231 cpwalk(n.EV.E2,IN); 1232 cpwalk(n.EV.E1,IN); 1233 } 1234 else if (OTbinary(op)) 1235 { 1236 cpwalk(n.EV.E1,IN); 1237 cpwalk(n.EV.E2,IN); 1238 } 1239 1240 if (OTdef(op)) // if definition elem 1241 { 1242 int ambig; /* true if ambiguous def */ 1243 1244 ambig = !OTassign(op) || t.Eoper == OPind; 1245 uint i; 1246 for (i = 0; (i = cast(uint) vec_index(i, IN)) < go.exptop; ++i) // for each active copy elem 1247 { 1248 Symbol *v; 1249 1250 if (op == OPasm) 1251 goto clr; 1252 1253 /* If this elem could kill the lvalue or the rvalue, */ 1254 /* Clear bit in IN. */ 1255 v = go.expnod[i].EV.E1.EV.Vsym; 1256 if (ambig) 1257 { 1258 if (Symbol_isAffected(*v)) 1259 goto clr; 1260 } 1261 else 1262 { 1263 if (v == t.EV.Vsym) 1264 goto clr; 1265 } 1266 1267 v = go.expnod[i].EV.E2.EV.Vsym; 1268 if (ambig) 1269 { 1270 if (Symbol_isAffected(*v)) 1271 goto clr; 1272 } 1273 else 1274 { 1275 if (v == t.EV.Vsym) 1276 goto clr; 1277 } 1278 continue; 1279 1280 clr: /* this copy elem is not available */ 1281 vec_clearbit(i,IN); /* so remove it from the vector */ 1282 } /* foreach */ 1283 1284 /* If this is a copy elem in go.expnod[] */ 1285 /* Set bit in IN. */ 1286 if ((op == OPeq || op == OPstreq) && n.EV.E1.Eoper == OPvar && 1287 n.EV.E2.Eoper == OPvar && n.Eexp) 1288 vec_setbit(n.Eexp,IN); 1289 } 1290 else if (op == OPvar && !nocp) // if reference to variable v 1291 { 1292 Symbol *v = n.EV.Vsym; 1293 1294 //printf("Checking copyprop for '%s', ty=x%x\n", v.Sident.ptr,n.Ety); 1295 symbol_debug(v); 1296 const ty = n.Ety; 1297 uint sz = tysize(n.Ety); 1298 if (sz == -1 && !tyfunc(n.Ety)) 1299 sz = cast(uint)type_size(v.Stype); 1300 1301 elem *foundelem = null; 1302 Symbol *f; 1303 for (uint i = 0; (i = cast(uint) vec_index(i, IN)) < go.exptop; ++i) // for all active copy elems 1304 { 1305 elem* c = go.expnod[i]; 1306 assert(c); 1307 1308 uint csz = tysize(c.EV.E1.Ety); 1309 if (c.Eoper == OPstreq) 1310 csz = cast(uint)type_size(c.ET); 1311 assert(cast(int)csz >= 0); 1312 1313 //printf(" looking at: ("); WReqn(c); printf("), ty=x%x\n",c.EV.E1.Ety); 1314 /* Not only must symbol numbers match, but */ 1315 /* offsets too (in case of arrays) and sizes */ 1316 /* (in case of unions). */ 1317 if (v == c.EV.E1.EV.Vsym && 1318 n.EV.Voffset >= c.EV.E1.EV.Voffset && 1319 n.EV.Voffset + sz <= c.EV.E1.EV.Voffset + csz) 1320 { 1321 if (foundelem) 1322 { 1323 if (c.EV.E2.EV.Vsym != f) 1324 goto noprop; 1325 } 1326 else 1327 { 1328 foundelem = c; 1329 f = foundelem.EV.E2.EV.Vsym; 1330 } 1331 } 1332 } 1333 if (foundelem) /* if we can do the copy prop */ 1334 { 1335 debug if (debugc) 1336 { 1337 printf("Copyprop, '%s'(%d) replaced with '%s'(%d)\n", 1338 (v.Sident[0]) ? cast(char *)v.Sident.ptr : "temp".ptr, cast(int) v.Ssymnum, 1339 (f.Sident[0]) ? cast(char *)f.Sident.ptr : "temp".ptr, cast(int) f.Ssymnum); 1340 } 1341 1342 type *nt = n.ET; 1343 targ_size_t noffset = n.EV.Voffset; 1344 el_copy(n,foundelem.EV.E2); 1345 n.Ety = ty; // retain original type 1346 n.ET = nt; 1347 n.EV.Voffset += noffset - foundelem.EV.E1.EV.Voffset; 1348 1349 /* original => rewrite 1350 * v = f 1351 * g = v => g = f 1352 * f = x 1353 * d = g => d = f !!error 1354 * Therefore, if n appears as an rvalue in go.expnod[], then recalc 1355 */ 1356 for (size_t j = 1; j < go.exptop; ++j) 1357 { 1358 //printf("go.expnod[%d]: ", j); elem_print(go.expnod[j]); 1359 if (go.expnod[j].EV.E2 == n) 1360 { 1361 recalc = true; 1362 break; 1363 } 1364 } 1365 1366 go.changes++; 1367 } 1368 //else printf("not found\n"); 1369 noprop: 1370 { } 1371 } 1372 } 1373 1374 cpwalk(n, IN); 1375 return recalc; 1376 } 1377 1378 /******************************** 1379 * Remove dead assignments. Those are assignments to a variable v 1380 * for which there are no subsequent uses of v. 1381 */ 1382 1383 private __gshared 1384 { 1385 Barray!(elem*) assnod; /* array of pointers to asg elems */ 1386 vec_t ambigref; /* vector of assignment elems that */ 1387 /* are referenced when an ambiguous */ 1388 /* reference is done (as in *p or call) */ 1389 } 1390 1391 @trusted 1392 void rmdeadass() 1393 { 1394 if (debugc) printf("rmdeadass()\n"); 1395 flowlv(); /* compute live variables */ 1396 foreach (b; dfo[]) // for each block b 1397 { 1398 if (!b.Belem) /* if no elems at all */ 1399 continue; 1400 if (b.Btry) // if in try-block guarded body 1401 continue; 1402 const assnum = numasg(b.Belem); // # of assignment elems 1403 if (assnum == 0) // if no assignment elems 1404 continue; 1405 1406 assnod.setLength(assnum); // pre-allocate sufficient room 1407 vec_t DEAD = vec_calloc(assnum); 1408 vec_t POSS = vec_calloc(assnum); 1409 1410 ambigref = vec_calloc(assnum); 1411 assnod.setLength(0); 1412 accumda(b.Belem,DEAD,POSS); // fill assnod[], compute DEAD and POSS 1413 assert(assnum == assnod.length); 1414 vec_free(ambigref); 1415 1416 vec_orass(POSS,DEAD); /* POSS |= DEAD */ 1417 for (uint j = 0; (j = cast(uint) vec_index(j, POSS)) < assnum; ++j) // for each possible dead asg. 1418 { 1419 Symbol *v; /* v = target of assignment */ 1420 elem *n; 1421 elem *nv; 1422 1423 n = assnod[j]; 1424 nv = n.EV.E1; 1425 v = nv.EV.Vsym; 1426 if (!symbol_isintab(v)) // not considered 1427 continue; 1428 //printf("assnod[%d]: ",j); WReqn(n); printf("\n"); 1429 //printf("\tPOSS\n"); 1430 /* If not positively dead but v is live on a */ 1431 /* successor to b, then v is live. */ 1432 //printf("\tDEAD=%d, live=%d\n",vec_testbit(j,DEAD),vec_testbit(v.Ssymnum,b.Boutlv)); 1433 if (!vec_testbit(j,DEAD) && vec_testbit(v.Ssymnum,b.Boutlv)) 1434 continue; 1435 /* volatile/shared variables are not dead */ 1436 if ((v.ty() | nv.Ety) & (mTYvolatile | mTYshared)) 1437 continue; 1438 1439 /* Do not mix up floating and integer variables 1440 * https://issues.dlang.org/show_bug.cgi?id=20363 1441 */ 1442 if (OTbinary(n.Eoper)) 1443 { 1444 elem* e2 = n.EV.E2; 1445 if (e2.Eoper == OPvar && 1446 config.fpxmmregs && 1447 !tyfloating(v.Stype.Tty) != !tyfloating(e2.EV.Vsym.Stype.Tty) 1448 ) 1449 continue; 1450 } 1451 1452 debug if (debugc) 1453 { 1454 printf("dead assignment ("); 1455 WReqn(n); 1456 if (vec_testbit(j,DEAD)) 1457 printf(") DEAD\n"); 1458 else 1459 printf(") Boutlv\n"); 1460 } 1461 elimass(n); 1462 go.changes++; 1463 } /* foreach */ 1464 vec_free(DEAD); 1465 vec_free(POSS); 1466 } /* for */ 1467 } 1468 1469 /*************************** 1470 * Remove side effect of assignment elem. 1471 */ 1472 1473 @trusted 1474 void elimass(elem *n) 1475 { elem *e1; 1476 1477 switch (n.Eoper) 1478 { 1479 case OPvecsto: 1480 n.EV.E2.Eoper = OPcomma; 1481 goto case OPeq; 1482 1483 case OPeq: 1484 case OPstreq: 1485 /* (V=e) => (random constant,e) */ 1486 /* Watch out for (a=b=c) stuff! */ 1487 /* Don't screw up assnod[]. */ 1488 n.Eoper = OPcomma; 1489 n.Ety |= n.EV.E2.Ety & (mTYconst | mTYvolatile | mTYimmutable | mTYshared 1490 | mTYfar 1491 ); 1492 n.EV.E1.Eoper = OPconst; 1493 break; 1494 1495 /* Convert (V op= e) to (V op e) */ 1496 case OPaddass: 1497 case OPminass: 1498 case OPmulass: 1499 case OPdivass: 1500 case OPorass: 1501 case OPandass: 1502 case OPxorass: 1503 case OPmodass: 1504 case OPshlass: 1505 case OPshrass: 1506 case OPashrass: 1507 n.Eoper = cast(ubyte)opeqtoop(n.Eoper); 1508 break; 1509 1510 case OPpostinc: /* (V i++ c) => V */ 1511 case OPpostdec: /* (V i-- c) => V */ 1512 e1 = n.EV.E1; 1513 el_free(n.EV.E2); 1514 el_copy(n,e1); 1515 el_free(e1); 1516 break; 1517 1518 case OPnegass: 1519 n.Eoper = OPneg; 1520 break; 1521 1522 case OPbtc: 1523 case OPbtr: 1524 case OPbts: 1525 n.Eoper = OPbt; 1526 break; 1527 1528 case OPcmpxchg: 1529 n.Eoper = OPcomma; 1530 n.EV.E2.Eoper = OPcomma; 1531 break; 1532 1533 default: 1534 assert(0); 1535 } 1536 } 1537 1538 /************************ 1539 * Compute number of =,op=,i++,i--,--i,++i elems. 1540 * (Unambiguous assignments only. Ambiguous ones would always be live.) 1541 * Some compilers generate better code for ?: than if-then-else. 1542 */ 1543 1544 @trusted 1545 private uint numasg(elem *e) 1546 { 1547 assert(e); 1548 if (OTassign(e.Eoper) && e.EV.E1.Eoper == OPvar) 1549 { 1550 e.Nflags |= NFLassign; 1551 return 1 + numasg(e.EV.E1) + (OTbinary(e.Eoper) ? numasg(e.EV.E2) : 0); 1552 } 1553 e.Nflags &= ~NFLassign; 1554 return OTunary(e.Eoper) ? numasg(e.EV.E1) : 1555 OTbinary(e.Eoper) ? numasg(e.EV.E1) + numasg(e.EV.E2) : 0; 1556 } 1557 1558 /****************************** 1559 * Tree walk routine for rmdeadass(). 1560 * DEAD = assignments which are dead 1561 * POSS = assignments which are possibly dead 1562 * The algorithm is basically: 1563 * if we have an assignment to v, 1564 * for all defs of v in POSS 1565 * set corresponding bits in DEAD 1566 * set bit for this def in POSS 1567 * if we have a reference to v, 1568 * clear all bits in POSS that are refs of v 1569 */ 1570 1571 @trusted 1572 private void accumda(elem *n,vec_t DEAD, vec_t POSS) 1573 { 1574 LtailRecurse: 1575 assert(n && DEAD && POSS); 1576 const op = n.Eoper; 1577 switch (op) 1578 { 1579 case OPcolon: 1580 case OPcolon2: 1581 { 1582 vec_t Pl = vec_clone(POSS); 1583 vec_t Pr = vec_clone(POSS); 1584 vec_t Dl = vec_calloc(vec_numbits(POSS)); 1585 vec_t Dr = vec_calloc(vec_numbits(POSS)); 1586 accumda(n.EV.E1,Dl,Pl); 1587 accumda(n.EV.E2,Dr,Pr); 1588 1589 /* D |= P & (Dl & Dr) | ~P & (Dl | Dr) */ 1590 /* P = P & (Pl & Pr) | ~P & (Pl | Pr) */ 1591 /* = Pl & Pr | ~P & (Pl | Pr) */ 1592 const vecdim = cast(uint)vec_dim(DEAD); 1593 for (uint i = 0; i < vecdim; i++) 1594 { 1595 DEAD[i] |= (POSS[i] & Dl[i] & Dr[i]) | 1596 (~POSS[i] & (Dl[i] | Dr[i])); 1597 POSS[i] = (Pl[i] & Pr[i]) | (~POSS[i] & (Pl[i] | Pr[i])); 1598 } 1599 vec_free(Pl); vec_free(Pr); vec_free(Dl); vec_free(Dr); 1600 break; 1601 } 1602 1603 case OPandand: 1604 case OPoror: 1605 { 1606 accumda(n.EV.E1,DEAD,POSS); 1607 // Substituting into the above equations Pl=P and Dl=0: 1608 // D |= Dr - P 1609 // P = Pr 1610 vec_t Pr = vec_clone(POSS); 1611 vec_t Dr = vec_calloc(vec_numbits(POSS)); 1612 accumda(n.EV.E2,Dr,Pr); 1613 vec_subass(Dr,POSS); 1614 vec_orass(DEAD,Dr); 1615 vec_copy(POSS,Pr); 1616 vec_free(Pr); vec_free(Dr); 1617 break; 1618 } 1619 1620 case OPvar: 1621 { 1622 Symbol *v = n.EV.Vsym; 1623 targ_size_t voff = n.EV.Voffset; 1624 uint vsize = tysize(n.Ety); 1625 1626 // We have a reference. Clear all bits in POSS that 1627 // could be referenced. 1628 1629 foreach (const i; 0 .. cast(uint)assnod.length) 1630 { 1631 elem *ti = assnod[i].EV.E1; 1632 if (v == ti.EV.Vsym && 1633 ((vsize == -1 || tysize(ti.Ety) == -1) || 1634 // If symbol references overlap 1635 (voff + vsize > ti.EV.Voffset && 1636 ti.EV.Voffset + tysize(ti.Ety) > voff) 1637 ) 1638 ) 1639 { 1640 vec_clearbit(i,POSS); 1641 } 1642 } 1643 break; 1644 } 1645 1646 case OPasm: // reference everything 1647 foreach (const i; 0 .. cast(uint)assnod.length) 1648 vec_clearbit(i,POSS); 1649 break; 1650 1651 case OPbt: 1652 accumda(n.EV.E1,DEAD,POSS); 1653 accumda(n.EV.E2,DEAD,POSS); 1654 vec_subass(POSS,ambigref); // remove possibly refed 1655 break; 1656 1657 case OPind: 1658 case OPucall: 1659 case OPucallns: 1660 case OPvp_fp: 1661 accumda(n.EV.E1,DEAD,POSS); 1662 vec_subass(POSS,ambigref); // remove possibly refed 1663 // assignments from list 1664 // of possibly dead ones 1665 break; 1666 1667 case OPconst: 1668 break; 1669 1670 case OPcall: 1671 case OPcallns: 1672 case OPmemcpy: 1673 case OPstrcpy: 1674 case OPmemset: 1675 accumda(n.EV.E2,DEAD,POSS); 1676 goto case OPstrlen; 1677 1678 case OPstrlen: 1679 accumda(n.EV.E1,DEAD,POSS); 1680 vec_subass(POSS,ambigref); // remove possibly refed 1681 // assignments from list 1682 // of possibly dead ones 1683 break; 1684 1685 case OPstrcat: 1686 case OPstrcmp: 1687 case OPmemcmp: 1688 accumda(n.EV.E1,DEAD,POSS); 1689 accumda(n.EV.E2,DEAD,POSS); 1690 vec_subass(POSS,ambigref); // remove possibly refed 1691 // assignments from list 1692 // of possibly dead ones 1693 break; 1694 1695 default: 1696 if (OTassign(op)) 1697 { 1698 elem *t; 1699 1700 if (ERTOL(n)) 1701 accumda(n.EV.E2,DEAD,POSS); 1702 t = n.EV.E1; 1703 // if not (v = expression) then gen refs of left tree 1704 if (op != OPeq && op != OPstreq) 1705 accumda(n.EV.E1,DEAD,POSS); 1706 else if (OTunary(t.Eoper)) // if (*e = expression) 1707 accumda(t.EV.E1,DEAD,POSS); 1708 else if (OTbinary(t.Eoper)) 1709 { 1710 accumda(t.EV.E1,DEAD,POSS); 1711 accumda(t.EV.E2,DEAD,POSS); 1712 } 1713 if (!ERTOL(n) && op != OPnegass) 1714 accumda(n.EV.E2,DEAD,POSS); 1715 1716 // if unambiguous assignment, post all possibilities 1717 // to DEAD 1718 if ((op == OPeq || op == OPstreq) && t.Eoper == OPvar) 1719 { 1720 uint tsz = tysize(t.Ety); 1721 if (n.Eoper == OPstreq) 1722 tsz = cast(uint)type_size(n.ET); 1723 foreach (const i; 0 .. cast(uint)assnod.length) 1724 { 1725 elem *ti = assnod[i].EV.E1; 1726 1727 uint tisz = tysize(ti.Ety); 1728 if (assnod[i].Eoper == OPstreq) 1729 tisz = cast(uint)type_size(assnod[i].ET); 1730 1731 // There may be some problem with this next 1732 // statement with unions. 1733 if (ti.EV.Vsym == t.EV.Vsym && 1734 ti.EV.Voffset == t.EV.Voffset && 1735 tisz == tsz && 1736 !(t.Ety & (mTYvolatile | mTYshared)) && 1737 //t.EV.Vsym.Sflags & SFLunambig && 1738 vec_testbit(i,POSS)) 1739 { 1740 vec_setbit(i,DEAD); 1741 } 1742 } 1743 } 1744 1745 // if assignment operator, post this def to POSS 1746 if (n.Nflags & NFLassign) 1747 { 1748 const i = cast(uint)assnod.length; 1749 vec_setbit(i,POSS); 1750 1751 // if variable could be referenced by a pointer 1752 // or a function call, mark the assignment in 1753 // ambigref 1754 if (!(t.EV.Vsym.Sflags & SFLunambig)) 1755 { 1756 vec_setbit(i,ambigref); 1757 1758 debug if (debugc) 1759 { 1760 printf("ambiguous lvalue: "); 1761 WReqn(n); 1762 printf("\n"); 1763 } 1764 } 1765 1766 assnod.push(n); 1767 } 1768 } 1769 else if (OTrtol(op)) 1770 { 1771 accumda(n.EV.E2,DEAD,POSS); 1772 n = n.EV.E1; 1773 goto LtailRecurse; // accumda(n.EV.E1,DEAD,POSS); 1774 } 1775 else if (OTbinary(op)) 1776 { 1777 accumda(n.EV.E1,DEAD,POSS); 1778 n = n.EV.E2; 1779 goto LtailRecurse; // accumda(n.EV.E2,DEAD,POSS); 1780 } 1781 else if (OTunary(op)) 1782 { 1783 n = n.EV.E1; 1784 goto LtailRecurse; // accumda(n.EV.E1,DEAD,POSS); 1785 } 1786 break; 1787 } 1788 } 1789 1790 1791 /*************************** 1792 * Mark all dead variables. Only worry about register candidates. 1793 * Compute live ranges for register candidates. 1794 * Be careful not to compute live ranges for members of structures (CLMOS). 1795 */ 1796 @trusted 1797 void deadvar() 1798 { 1799 assert(dfo); 1800 1801 /* First, mark each candidate as dead. */ 1802 /* Initialize vectors for live ranges. */ 1803 for (SYMIDX i = 0; i < globsym.length; i++) 1804 { 1805 Symbol *s = globsym[i]; 1806 1807 if (s.Sflags & SFLunambig) 1808 { 1809 s.Sflags |= SFLdead; 1810 if (s.Sflags & GTregcand) 1811 { 1812 s.Srange = vec_realloc(s.Srange, dfo.length); 1813 vec_clear(s.Srange); 1814 } 1815 } 1816 } 1817 1818 /* Go through trees and "liven" each one we see. */ 1819 foreach (i, b; dfo[]) 1820 if (b.Belem) 1821 dvwalk(b.Belem,cast(uint)i); 1822 1823 /* Compute live variables. Set bit for block in live range */ 1824 /* if variable is in the IN set for that block. */ 1825 flowlv(); /* compute live variables */ 1826 for (SYMIDX i = 0; i < globsym.length; i++) 1827 { 1828 if (globsym[i].Srange /*&& globsym[i].Sclass != CLMOS*/) 1829 foreach (j, b; dfo[]) 1830 if (vec_testbit(i,b.Binlv)) 1831 vec_setbit(cast(uint)j,globsym[i].Srange); 1832 } 1833 1834 /* Print results */ 1835 for (SYMIDX i = 0; i < globsym.length; i++) 1836 { 1837 char *p; 1838 Symbol *s = globsym[i]; 1839 1840 if (s.Sflags & SFLdead && s.Sclass != SC.parameter && s.Sclass != SC.regpar) 1841 s.Sflags &= ~GTregcand; // do not put dead variables in registers 1842 debug 1843 { 1844 p = cast(char *) s.Sident.ptr ; 1845 if (s.Sflags & SFLdead) 1846 if (debugc) printf("Symbol %d '%s' is dead\n",cast(int) i,p); 1847 if (debugc && s.Srange /*&& s.Sclass != CLMOS*/) 1848 { 1849 printf("Live range for %d '%s': ",cast(int) i,p); 1850 vec_println(s.Srange); 1851 } 1852 } 1853 } 1854 } 1855 1856 1857 /***************************** 1858 * Tree walk support routine for deadvar(). 1859 * Input: 1860 * n = elem to look at 1861 * i = block index 1862 */ 1863 @trusted 1864 private void dvwalk(elem *n,uint i) 1865 { 1866 for (; true; n = n.EV.E1) 1867 { 1868 assert(n); 1869 if (n.Eoper == OPvar || n.Eoper == OPrelconst) 1870 { 1871 Symbol *s = n.EV.Vsym; 1872 1873 s.Sflags &= ~SFLdead; 1874 if (s.Srange) 1875 vec_setbit(i,s.Srange); 1876 } 1877 else if (!OTleaf(n.Eoper)) 1878 { 1879 if (OTbinary(n.Eoper)) 1880 dvwalk(n.EV.E2,i); 1881 continue; 1882 } 1883 break; 1884 } 1885 } 1886 1887 /********************************* 1888 * Optimize very busy expressions (VBEs). 1889 */ 1890 1891 private __gshared vec_t blockseen; /* which blocks we have visited */ 1892 1893 @trusted 1894 void verybusyexp() 1895 { 1896 elem **pn; 1897 uint j,l; 1898 1899 if (debugc) printf("verybusyexp()\n"); 1900 flowvbe(); /* compute VBEs */ 1901 if (go.exptop <= 1) return; /* if no VBEs */ 1902 assert(go.expblk.length); 1903 if (blockinit()) 1904 return; // can't handle ASM blocks 1905 compdom(); /* compute dominators */ 1906 /*setvecdim(go.exptop);*/ 1907 genkillae(); /* compute Bgen and Bkill for */ 1908 /* AEs */ 1909 /*chkvecdim(go.exptop,0);*/ 1910 blockseen = vec_calloc(dfo.length); 1911 1912 /* Go backwards through dfo so that VBEs are evaluated as */ 1913 /* close as possible to where they are used. */ 1914 foreach_reverse (i, b; dfo[]) // for each block 1915 { 1916 int done; 1917 1918 /* Do not hoist things to blocks that do not */ 1919 /* divide the flow of control. */ 1920 1921 switch (b.BC) 1922 { 1923 case BCiftrue: 1924 case BCswitch: 1925 break; 1926 1927 default: 1928 continue; 1929 } 1930 1931 /* Find pointer to last statement in current elem */ 1932 pn = &(b.Belem); 1933 if (*pn) 1934 { 1935 while ((*pn).Eoper == OPcomma) 1936 pn = &((*pn).EV.E2); 1937 /* If last statement has side effects, */ 1938 /* don't do these VBEs. Potentially we */ 1939 /* could by assigning the result to */ 1940 /* a temporary, and rewriting the tree */ 1941 /* from (n) to (T=n,T) and installing */ 1942 /* the VBE as (T=n,VBE,T). This */ 1943 /* may not buy us very much, so we will */ 1944 /* just skip it for now. */ 1945 /*if (sideeffect(*pn))*/ 1946 if (!(*pn).Eexp) 1947 continue; 1948 } 1949 1950 /* Eliminate all elems that have already been */ 1951 /* hoisted (indicated by go.expnod[] == 0). */ 1952 /* Constants are not useful as VBEs. */ 1953 /* Eliminate all elems from Bout that are not in blocks */ 1954 /* that are dominated by b. */ 1955 static if (0) 1956 { 1957 printf("block %d Bout = ",i); 1958 vec_println(b.Bout); 1959 } 1960 done = true; 1961 for (j = 0; (j = cast(uint) vec_index(j, b.Bout)) < go.exptop; ++j) 1962 { 1963 if (go.expnod[j] == null || 1964 !!OTleaf(go.expnod[j].Eoper) || 1965 !dom(b,go.expblk[j])) 1966 vec_clearbit(j,b.Bout); 1967 else 1968 done = false; 1969 } 1970 if (done) continue; 1971 1972 /* Eliminate from Bout all elems that are killed by */ 1973 /* a block between b and that elem. */ 1974 static if (0) 1975 { 1976 printf("block %d Bout = ",i); 1977 vec_println(b.Bout); 1978 } 1979 for (j = 0; (j = cast(uint) vec_index(j, b.Bout)) < go.exptop; ++j) 1980 { 1981 vec_clear(blockseen); 1982 foreach (bl; ListRange(go.expblk[j].Bpred)) 1983 { 1984 if (killed(j,list_block(bl),b)) 1985 { 1986 vec_clearbit(j,b.Bout); 1987 break; 1988 } 1989 } 1990 } 1991 1992 /* For each elem still left, make sure that there */ 1993 /* exists a path from b to j along which there is */ 1994 /* no other use of j (else it would be a CSE, and */ 1995 /* it would be a waste of time to hoist it). */ 1996 static if (0) 1997 { 1998 printf("block %d Bout = ",i); 1999 vec_println(b.Bout); 2000 } 2001 2002 for (j = 0; (j = cast(uint) vec_index(j, b.Bout)) < go.exptop; ++j) 2003 { 2004 vec_clear(blockseen); 2005 foreach (bl; ListRange(go.expblk[j].Bpred)) 2006 { 2007 if (ispath(j,list_block(bl),b)) 2008 goto L2; 2009 } 2010 vec_clearbit(j,b.Bout); /* thar ain't no path */ 2011 L2: 2012 } 2013 2014 2015 /* For each elem that appears more than once in Bout */ 2016 /* We have a VBE. */ 2017 static if (0) 2018 { 2019 printf("block %d Bout = ",i); 2020 vec_println(b.Bout); 2021 } 2022 2023 for (j = 0; (j = cast(uint) vec_index(j, b.Bout)) < go.exptop; ++j) 2024 { 2025 uint k; 2026 for (k = j + 1; k < go.exptop; k++) 2027 { if (vec_testbit(k,b.Bout) && 2028 el_match(go.expnod[j],go.expnod[k])) 2029 goto foundvbe; 2030 } 2031 continue; /* no VBE here */ 2032 2033 foundvbe: /* we got one */ 2034 debug 2035 { 2036 if (debugc) 2037 { printf("VBE %d,%d, block %d (",j,k, cast(int) i); 2038 WReqn(go.expnod[j]); 2039 printf(");\n"); 2040 } 2041 } 2042 *pn = el_bin(OPcomma,(*pn).Ety, 2043 el_copytree(go.expnod[j]),*pn); 2044 2045 /* Mark all the vbe elems found but one (the */ 2046 /* go.expnod[j] one) so that the expression will */ 2047 /* only be hoisted again if other occurrences */ 2048 /* of the expression are found later. This */ 2049 /* will substitute for the fact that the */ 2050 /* el_copytree() expression does not appear in go.expnod[]. */ 2051 l = k; 2052 do 2053 { 2054 if (k == l || (vec_testbit(k,b.Bout) && 2055 el_match(go.expnod[j],go.expnod[k]))) 2056 { 2057 /* Fix so nobody else will */ 2058 /* vbe this elem */ 2059 go.expnod[k] = null; 2060 vec_clearbit(k,b.Bout); 2061 } 2062 } while (++k < go.exptop); 2063 go.changes++; 2064 } /* foreach */ 2065 } /* for */ 2066 vec_free(blockseen); 2067 } 2068 2069 /**************************** 2070 * Return true if elem j is killed somewhere 2071 * between b and bp. 2072 */ 2073 2074 @trusted 2075 private int killed(uint j,block *bp,block *b) 2076 { 2077 if (bp == b || vec_testbit(bp.Bdfoidx,blockseen)) 2078 return false; 2079 if (vec_testbit(j,bp.Bkill)) 2080 return true; 2081 vec_setbit(bp.Bdfoidx,blockseen); /* mark as visited */ 2082 foreach (bl; ListRange(bp.Bpred)) 2083 if (killed(j,list_block(bl),b)) 2084 return true; 2085 return false; 2086 } 2087 2088 /*************************** 2089 * Return true if there is a path from b to bp along which 2090 * elem j is not used. 2091 * Input: 2092 * b . block where we want to put the VBE 2093 * bp . block somewhere between b and block containing j 2094 * j = VBE expression elem candidate (index into go.expnod[]) 2095 */ 2096 2097 @trusted 2098 private int ispath(uint j,block *bp,block *b) 2099 { 2100 /*chkvecdim(go.exptop,0);*/ 2101 if (bp == b) return true; /* the trivial case */ 2102 if (vec_testbit(bp.Bdfoidx,blockseen)) 2103 return false; /* already seen this block */ 2104 vec_setbit(bp.Bdfoidx,blockseen); /* we've visited this block */ 2105 2106 /* false if elem j is used in block bp (and reaches the end */ 2107 /* of bp, indicated by it being an AE in Bgen) */ 2108 uint i; 2109 for (i = 0; (i = cast(uint) vec_index(i, bp.Bgen)) < go.exptop; ++i) // look thru used expressions 2110 { 2111 if (i != j && go.expnod[i] && el_match(go.expnod[i],go.expnod[j])) 2112 return false; 2113 } 2114 2115 /* Not used in bp, see if there is a path through a predecessor */ 2116 /* of bp */ 2117 foreach (bl; ListRange(bp.Bpred)) 2118 if (ispath(j,list_block(bl),b)) 2119 return true; 2120 2121 return false; /* j is used along all paths */ 2122 } 2123 2124 }