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