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