1 /** 2 * Manipulating basic blocks and their edges. 3 * 4 * Copyright: Copyright (C) 1986-1997 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: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/blockopt.d, backend/blockopt.d) 9 * Documentation: https://dlang.org/phobos/dmd_backend_blockopt.html 10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/blockopt.d 11 */ 12 13 module dmd.backend.blockopt; 14 15 version (SCPP) 16 version = COMPILE; 17 else version (HTOD) 18 version = COMPILE; 19 20 import core.stdc.stdio; 21 import core.stdc.string; 22 import core.stdc.time; 23 import core.stdc.stdlib; 24 25 import dmd.backend.cc; 26 import dmd.backend.cdef; 27 import dmd.backend.oper; 28 import dmd.backend.dlist; 29 import dmd.backend.dvec; 30 import dmd.backend.el; 31 import dmd.backend.mem; 32 import dmd.backend.type; 33 import dmd.backend.global; 34 import dmd.backend.goh; 35 import dmd.backend.code; 36 import dmd.backend.ty; 37 38 import dmd.backend.barray; 39 40 version (COMPILE) 41 { 42 import parser; 43 import iasm; 44 import precomp; 45 } 46 47 48 version (SCPP) 49 enum SCPP_OR_NTEXCEPTIONS = true; 50 else static if (NTEXCEPTIONS) 51 enum SCPP_OR_NTEXCEPTIONS = true; 52 else 53 enum SCPP_OR_NTEXCEPTIONS = false; 54 55 extern(C++): 56 57 nothrow: 58 @safe: 59 60 extern (C) void *mem_fcalloc(size_t numbytes); // tk/mem.c 61 extern (C) void mem_free(void*); // tk/mem.c 62 63 alias MEM_PH_FREE = mem_free; 64 65 version (HTOD) 66 void *util_realloc(void* p, size_t n, size_t size); 67 else 68 import dmd.backend.gflow : util_realloc; 69 70 __gshared 71 { 72 block *startblock; // beginning block of function 73 // (can have no predecessors) 74 75 Barray!(block*) dfo; // array of depth first order 76 77 block *curblock; // current block being read in 78 block *block_last; // last block read in 79 80 block *block_freelist; 81 82 block blkzero; // storage allocator 83 } 84 85 @trusted 86 pragma(inline, true) block *block_calloc_i() 87 { 88 block *b; 89 90 if (block_freelist) 91 { 92 b = block_freelist; 93 block_freelist = b.Bnext; 94 *b = blkzero; 95 } 96 else 97 b = cast(block *) mem_calloc(block.sizeof); 98 return b; 99 } 100 101 block *block_calloc() 102 { 103 return block_calloc_i(); 104 } 105 106 /********************************* 107 */ 108 109 __gshared goal_t[BCMAX] bc_goal; 110 111 @trusted 112 void block_init() 113 { 114 for (size_t i = 0; i < BCMAX; i++) 115 bc_goal[i] = GOALvalue; 116 117 bc_goal[BCgoto] = GOALnone; 118 bc_goal[BCret ] = GOALnone; 119 bc_goal[BCexit] = GOALnone; 120 121 bc_goal[BCiftrue] = GOALflags; 122 } 123 124 /********************************* 125 */ 126 127 @trusted 128 void block_term() 129 { 130 while (block_freelist) 131 { 132 block *b = block_freelist.Bnext; 133 mem_free(block_freelist); 134 block_freelist = b; 135 } 136 } 137 138 /************************** 139 * Finish up this block and start the next one. 140 */ 141 142 version (MARS) 143 { 144 @trusted 145 void block_next(Blockx *bctx,int bc,block *bn) 146 { 147 bctx.curblock.BC = cast(ubyte) bc; 148 block_last = bctx.curblock; 149 if (!bn) 150 bn = block_calloc_i(); 151 bctx.curblock.Bnext = bn; // next block 152 bctx.curblock = bctx.curblock.Bnext; // new current block 153 bctx.curblock.Btry = bctx.tryblock; 154 bctx.curblock.Bflags |= bctx.flags; 155 } 156 } 157 else 158 { 159 @trusted 160 void block_next(int bc,block *bn) 161 { 162 curblock.BC = cast(ubyte) bc; 163 curblock.Bsymend = globsym.length; 164 block_last = curblock; 165 if (!bn) 166 bn = block_calloc_i(); 167 curblock.Bnext = bn; // next block 168 curblock = curblock.Bnext; // new current block 169 curblock.Bsymstart = globsym.length; 170 curblock.Btry = pstate.STbtry; 171 } 172 173 void block_next() 174 { 175 block_next(cast(BC)curblock.BC,null); 176 } 177 } 178 179 /************************** 180 * Finish up this block and start the next one. 181 */ 182 183 version (MARS) 184 { 185 block *block_goto(Blockx *bx,int bc,block *bn) 186 { 187 block *b; 188 189 b = bx.curblock; 190 block_next(bx,bc,bn); 191 b.appendSucc(bx.curblock); 192 return bx.curblock; 193 } 194 } 195 196 /**************************** 197 * Goto a block named gotolbl. 198 * Start a new block that is labelled by newlbl. 199 */ 200 201 version (COMPILE) 202 { 203 204 void block_goto() 205 { 206 block_goto(block_calloc()); 207 } 208 209 void block_goto(block *bn) 210 { 211 block_goto(bn,bn); 212 } 213 214 void block_goto(block *bgoto,block *bnew) 215 { 216 BC bc; 217 218 assert(bgoto); 219 curblock.appendSucc(bgoto); 220 if (curblock.Bcode) // If there is already code in the block 221 // then this is an ASM block 222 bc = BCasm; 223 else 224 bc = BCgoto; // fall thru to next block 225 block_next(bc,bnew); 226 } 227 228 } 229 230 /********************************** 231 * Replace block numbers with block pointers. 232 */ 233 234 @trusted 235 void block_ptr() 236 { 237 //printf("block_ptr()\n"); 238 239 uint numblks = 0; 240 for (block *b = startblock; b; b = b.Bnext) /* for each block */ 241 { 242 b.Bblknum = numblks; 243 numblks++; 244 } 245 } 246 247 /******************************* 248 * Build predecessor list (Bpred) for each block. 249 */ 250 251 @trusted 252 void block_pred() 253 { 254 //printf("block_pred()\n"); 255 for (block *b = startblock; b; b = b.Bnext) // for each block 256 list_free(&b.Bpred,FPNULL); 257 258 for (block *b = startblock; b; b = b.Bnext) // for each block 259 { 260 //printf("b = %p, BC = %s\n", b, bc_str(b.BC)); 261 foreach (bp; ListRange(b.Bsucc)) 262 { /* for each successor to b */ 263 //printf("\tbs = %p\n",list_block(bp)); 264 assert(list_block(bp)); 265 list_prepend(&(list_block(bp).Bpred),b); 266 } 267 } 268 assert(startblock.Bpred == null); /* startblock has no preds */ 269 } 270 271 /******************************************** 272 * Clear visit. 273 */ 274 275 @trusted 276 void block_clearvisit() 277 { 278 for (block *b = startblock; b; b = b.Bnext) // for each block 279 b.Bflags &= ~BFLvisited; // mark as unvisited 280 } 281 282 /******************************************** 283 * Visit block and each of its predecessors. 284 */ 285 286 void block_visit(block *b) 287 { 288 b.Bflags |= BFLvisited; 289 foreach (l; ListRange(b.Bsucc)) 290 { 291 block *bs = list_block(l); 292 assert(bs); 293 if ((bs.Bflags & BFLvisited) == 0) // if not visited 294 block_visit(bs); 295 } 296 } 297 298 /***************************** 299 * Compute number of parents (Bcount) of each basic block. 300 */ 301 @trusted 302 void block_compbcount() 303 { 304 block_clearvisit(); 305 block_visit(startblock); // visit all reachable blocks 306 elimblks(); // eliminate unvisited blocks 307 } 308 309 /******************************* 310 * Free list of blocks. 311 */ 312 313 void blocklist_free(block **pb) 314 { 315 block *bn; 316 for (block *b = *pb; b; b = bn) 317 { 318 bn = b.Bnext; 319 block_free(b); 320 } 321 *pb = null; 322 } 323 324 /******************************** 325 * Free optimizer gathered data. 326 */ 327 328 @trusted 329 void block_optimizer_free(block *b) 330 { 331 static void vfree(ref vec_t v) { vec_free(v); v = null; } 332 vfree(b.Bdom); 333 vfree(b.Binrd); 334 vfree(b.Boutrd); 335 vfree(b.Binlv); 336 vfree(b.Boutlv); 337 vfree(b.Bin); 338 vfree(b.Bout); 339 vfree(b.Bgen); 340 vfree(b.Bkill); 341 vfree(b.Bout2); 342 vfree(b.Bgen2); 343 vfree(b.Bkill2); 344 345 // memset(&b->_BLU,0,sizeof(b->_BLU)); 346 } 347 348 /**************************** 349 * Free a block. 350 */ 351 352 @trusted 353 void block_free(block *b) 354 { 355 assert(b); 356 if (b.Belem) 357 el_free(b.Belem); 358 list_free(&b.Bsucc,FPNULL); 359 list_free(&b.Bpred,FPNULL); 360 if (OPTIMIZER) 361 block_optimizer_free(b); 362 switch (b.BC) 363 { 364 case BCswitch: 365 case BCifthen: 366 case BCjmptab: 367 version (MARS) 368 { 369 free(b.Bswitch); 370 } 371 else 372 { 373 MEM_PH_FREE(b.Bswitch); 374 } 375 break; 376 377 version (SCPP) 378 { 379 case BCcatch: 380 type_free(b.Bcatchtype); 381 break; 382 } 383 384 version (MARS) 385 { 386 case BCjcatch: 387 free(b.actionTable); 388 break; 389 } 390 391 case BCasm: 392 version (HTOD) {} else 393 { 394 code_free(b.Bcode); 395 } 396 break; 397 398 default: 399 break; 400 } 401 b.Bnext = block_freelist; 402 block_freelist = b; 403 } 404 405 /**************************** 406 * Hydrate/dehydrate a list of blocks. 407 */ 408 409 version (COMPILE) 410 { 411 static if (HYDRATE) 412 { 413 @trusted 414 void blocklist_hydrate(block **pb) 415 { 416 while (isdehydrated(*pb)) 417 { 418 /*printf("blocklist_hydrate(*pb = %p) =",*pb);*/ 419 block *b = cast(block *)ph_hydrate(cast(void**)pb); 420 /*printf(" %p\n",b);*/ 421 el_hydrate(&b.Belem); 422 list_hydrate(&b.Bsucc,FPNULL); 423 list_hydrate(&b.Bpred,FPNULL); 424 cast(void) ph_hydrate(cast(void**)&b.Btry); 425 cast(void) ph_hydrate(cast(void**)&b.Bendscope); 426 symbol_hydrate(&b.Binitvar); 427 switch (b.BC) 428 { 429 case BCtry: 430 symbol_hydrate(&b.catchvar); 431 break; 432 433 case BCcatch: 434 type_hydrate(&b.Bcatchtype); 435 break; 436 437 case BCswitch: 438 ph_hydrate(cast(void**)&b.Bswitch); 439 break; 440 441 case BC_finally: 442 //(void) ph_hydrate(&b.B_ret); 443 break; 444 445 case BC_lpad: 446 symbol_hydrate(&b.flag); 447 break; 448 449 case BCasm: 450 version (HTOD) {} else 451 { 452 code_hydrate(&b.Bcode); 453 } 454 break; 455 456 default: 457 break; 458 } 459 filename_translate(&b.Bsrcpos); 460 srcpos_hydrate(&b.Bsrcpos); 461 pb = &b.Bnext; 462 } 463 } 464 } 465 466 static if (DEHYDRATE) 467 { 468 @trusted 469 void blocklist_dehydrate(block **pb) 470 { 471 block *b; 472 473 while ((b = *pb) != null && !isdehydrated(b)) 474 { 475 version (DEBUG_XSYMGEN) 476 { 477 if (xsym_gen && ph_in_head(b)) 478 return; 479 } 480 481 /*printf("blocklist_dehydrate(*pb = %p) =",b);*/ 482 ph_dehydrate(pb); 483 /*printf(" %p\n",*pb);*/ 484 el_dehydrate(&b.Belem); 485 list_dehydrate(&b.Bsucc,FPNULL); 486 list_dehydrate(&b.Bpred,FPNULL); 487 ph_dehydrate(&b.Btry); 488 ph_dehydrate(&b.Bendscope); 489 symbol_dehydrate(&b.Binitvar); 490 switch (b.BC) 491 { 492 case BCtry: 493 symbol_dehydrate(&b.catchvar); 494 break; 495 496 case BCcatch: 497 type_dehydrate(&b.Bcatchtype); 498 break; 499 500 case BCswitch: 501 ph_dehydrate(&b.Bswitch); 502 break; 503 504 case BC_finally: 505 //ph_dehydrate(&b.B_ret); 506 break; 507 508 case BC_lpad: 509 symbol_dehydrate(&b.flag); 510 break; 511 512 case BCasm: 513 code_dehydrate(&b.Bcode); 514 break; 515 516 default: 517 break; 518 } 519 srcpos_dehydrate(&b.Bsrcpos); 520 pb = &b.Bnext; 521 } 522 } 523 } 524 } 525 526 /**************************** 527 * Append elem to the elems comprising the current block. 528 * Read in an expression and put it in curblock.Belem. 529 * If there is one already there, create a tree like: 530 * , 531 * / \ 532 * old e 533 */ 534 535 @trusted 536 void block_appendexp(block *b,elem *e) 537 { 538 version (MARS) {} 539 else assert(PARSER); 540 541 if (e) 542 { 543 assert(b); 544 elem_debug(e); 545 elem **pe = &b.Belem; 546 elem *ec = *pe; 547 if (ec != null) 548 { 549 type *t = e.ET; 550 551 if (t) 552 type_debug(t); 553 elem_debug(e); 554 version (MARS) 555 { 556 tym_t ty = e.Ety; 557 558 elem_debug(e); 559 /* Build tree such that (a,b) => (a,(b,e)) */ 560 while (ec.Eoper == OPcomma) 561 { 562 ec.Ety = ty; 563 ec.ET = t; 564 pe = &(ec.EV.E2); 565 ec = *pe; 566 } 567 e = el_bin(OPcomma,ty,ec,e); 568 e.ET = t; 569 } 570 else 571 { 572 /* Build tree such that (a,b) => (a,(b,e)) */ 573 while (ec.Eoper == OPcomma) 574 { 575 el_settype(ec,t); 576 pe = &(ec.EV.E2); 577 ec = *pe; 578 } 579 e = el_bint(OPcomma,t,ec,e); 580 } 581 } 582 *pe = e; 583 } 584 } 585 586 /************************ 587 * Mark curblock as initializing Symbol s. 588 */ 589 590 version (COMPILE) 591 { 592 593 //#undef block_initvar 594 595 void block_initvar(Symbol *s) 596 { 597 symbol_debug(s); 598 curblock.Binitvar = s; 599 } 600 601 } 602 603 /******************* 604 * Mark end of function. 605 * flag: 606 * 0 do a "return" 607 * 1 do a "return 0" 608 */ 609 610 @trusted 611 void block_endfunc(int flag) 612 { 613 curblock.Bsymend = globsym.length; 614 curblock.Bendscope = curblock; 615 if (flag) 616 { 617 elem *e = el_longt(tstypes[TYint], 0); 618 block_appendexp(curblock, e); 619 curblock.BC = BCretexp; // put a return at the end 620 } 621 else 622 curblock.BC = BCret; // put a return at the end 623 curblock = null; // undefined from now on 624 block_last = null; 625 } 626 627 /****************************** 628 * Perform branch optimization on basic blocks. 629 */ 630 631 @trusted 632 void blockopt(int iter) 633 { 634 if (OPTIMIZER) 635 { 636 blassertsplit(); // only need this once 637 638 int iterationLimit = 200; 639 if (iterationLimit < dfo.length) 640 iterationLimit = cast(int)dfo.length; 641 int count = 0; 642 do 643 { 644 //printf("changes = %d, count = %d, dfo.length = %d\n",go.changes,count,dfo.length); 645 go.changes = 0; 646 bropt(); // branch optimization 647 brrear(); // branch rearrangement 648 blident(); // combine identical blocks 649 blreturn(); // split out return blocks 650 bltailmerge(); // do tail merging 651 brtailrecursion(); // do tail recursion 652 brcombine(); // convert graph to expressions 653 blexit(); 654 if (iter >= 2) 655 brmin(); // minimize branching 656 version (MARS) 657 // Switched to one block per Statement, do not undo it 658 enum merge = false; 659 else 660 enum merge = true; 661 662 do 663 { 664 compdfo(dfo, startblock); // compute depth first order (DFO) 665 elimblks(); /* remove blocks not in DFO */ 666 assert(count < iterationLimit); 667 count++; 668 } while (merge && mergeblks()); // merge together blocks 669 } while (go.changes); 670 671 debug if (debugw) 672 { 673 WRfunc("After blockopt()", funcsym_p, startblock); 674 } 675 } 676 else 677 { 678 debug 679 { 680 numberBlocks(startblock); 681 } 682 683 /* canonicalize the trees */ 684 for (block *b = startblock; b; b = b.Bnext) 685 { 686 debug if (debugb) 687 { 688 printf("before doptelem():\n"); 689 WRblock(b); 690 } 691 692 if (b.Belem) 693 { 694 b.Belem = doptelem(b.Belem,bc_goal[b.BC] | GOALstruct); 695 if (b.Belem) 696 b.Belem = el_convert(b.Belem); 697 } 698 699 debug if (debugb) 700 { 701 printf("after optelem():\n"); 702 WRblock(b); 703 } 704 } 705 if (localgot) 706 { // Initialize with: 707 // localgot = OPgot; 708 elem *e = el_long(TYnptr, 0); 709 e.Eoper = OPgot; 710 e = el_bin(OPeq, TYnptr, el_var(localgot), e); 711 startblock.Belem = el_combine(e, startblock.Belem); 712 } 713 714 bropt(); /* branch optimization */ 715 brrear(); /* branch rearrangement */ 716 comsubs(); /* eliminate common subexpressions */ 717 718 debug if (debugb) 719 { 720 WRfunc("After blockopt()", funcsym_p, startblock); 721 } 722 } 723 } 724 725 /*********************************** 726 * Try to remove control structure. 727 * That is, try to resolve if-else, goto and return statements 728 * into &&, || and ?: combinations. 729 */ 730 731 @trusted 732 void brcombine() 733 { 734 debug if (debugc) printf("brcombine()\n"); 735 //WRfunc("brcombine()", funcsym_p, startblock); 736 737 if (funcsym_p.Sfunc.Fflags3 & (Fcppeh | Fnteh)) 738 { // Don't mess up extra EH info by eliminating blocks 739 return; 740 } 741 742 do 743 { 744 int anychanges = 0; 745 for (block *b = startblock; b; b = b.Bnext) // for each block 746 { 747 /* Look for [e1 IFFALSE L3,L2] L2: [e2 GOTO L3] L3: [e3] */ 748 /* Replace with [(e1 && e2),e3] */ 749 ubyte bc = b.BC; 750 if (bc == BCiftrue) 751 { 752 block *b2 = b.nthSucc(0); 753 block *b3 = b.nthSucc(1); 754 755 if (list_next(b2.Bpred)) // if more than one predecessor 756 continue; 757 if (b2 == b3) 758 continue; 759 if (b2 == startblock) 760 continue; 761 if (!PARSER && b2.Belem && !OTleaf(b2.Belem.Eoper)) 762 continue; 763 764 ubyte bc2 = b2.BC; 765 if (bc2 == BCgoto && 766 b3 == b2.nthSucc(0)) 767 { 768 b.BC = BCgoto; 769 if (b2.Belem) 770 { 771 int op = OPandand; 772 b.Belem = PARSER ? el_bint(op,tstypes[TYint],b.Belem,b2.Belem) 773 : el_bin(op,TYint,b.Belem,b2.Belem); 774 b2.Belem = null; 775 } 776 list_subtract(&(b.Bsucc),b2); 777 list_subtract(&(b2.Bpred),b); 778 debug if (debugc) printf("brcombine(): if !e1 then e2 => e1 || e2\n"); 779 anychanges++; 780 } 781 else if (list_next(b3.Bpred) || b3 == startblock) 782 continue; 783 else if ((bc2 == BCretexp && b3.BC == BCretexp) 784 //|| (bc2 == BCret && b3.BC == BCret) 785 ) 786 { 787 if (PARSER) 788 { 789 type *t = (bc2 == BCretexp) ? b2.Belem.ET : tstypes[TYvoid]; 790 elem *e = el_bint(OPcolon2,t,b2.Belem,b3.Belem); 791 b.Belem = el_bint(OPcond,t,b.Belem,e); 792 } 793 else 794 { 795 if (!OTleaf(b3.Belem.Eoper)) 796 continue; 797 tym_t ty = (bc2 == BCretexp) ? b2.Belem.Ety : cast(tym_t) TYvoid; 798 elem *e = el_bin(OPcolon2,ty,b2.Belem,b3.Belem); 799 b.Belem = el_bin(OPcond,ty,b.Belem,e); 800 } 801 b.BC = bc2; 802 b.Belem.ET = b2.Belem.ET; 803 b2.Belem = null; 804 b3.Belem = null; 805 list_free(&b.Bsucc,FPNULL); 806 list_subtract(&(b2.Bpred),b); 807 list_subtract(&(b3.Bpred),b); 808 debug if (debugc) printf("brcombine(): if e1 return e2 else return e3 => ret e1?e2:e3\n"); 809 anychanges++; 810 } 811 else if (bc2 == BCgoto && 812 b3.BC == BCgoto && 813 b2.nthSucc(0) == b3.nthSucc(0)) 814 { 815 block *bsucc = b2.nthSucc(0); 816 if (b2.Belem) 817 { 818 elem *e; 819 if (PARSER) 820 { 821 if (b3.Belem) 822 { 823 e = el_bint(OPcolon2,b2.Belem.ET, 824 b2.Belem,b3.Belem); 825 e = el_bint(OPcond,e.ET,b.Belem,e); 826 } 827 else 828 { 829 int op = OPandand; 830 e = el_bint(op,tstypes[TYint],b.Belem,b2.Belem); 831 } 832 } 833 else 834 { 835 if (b3.Belem) 836 { 837 if (!OTleaf(b3.Belem.Eoper)) 838 continue; 839 e = el_bin(OPcolon2,b2.Belem.Ety, 840 b2.Belem,b3.Belem); 841 e = el_bin(OPcond,e.Ety,b.Belem,e); 842 e.ET = b2.Belem.ET; 843 } 844 else 845 { 846 int op = OPandand; 847 e = el_bin(op,TYint,b.Belem,b2.Belem); 848 } 849 } 850 b2.Belem = null; 851 b.Belem = e; 852 } 853 else if (b3.Belem) 854 { 855 int op = OPoror; 856 b.Belem = PARSER ? el_bint(op,tstypes[TYint],b.Belem,b3.Belem) 857 : el_bin(op,TYint,b.Belem,b3.Belem); 858 } 859 b.BC = BCgoto; 860 b3.Belem = null; 861 list_free(&b.Bsucc,FPNULL); 862 863 b.appendSucc(bsucc); 864 list_append(&bsucc.Bpred,b); 865 866 list_free(&(b2.Bpred),FPNULL); 867 list_free(&(b2.Bsucc),FPNULL); 868 list_free(&(b3.Bpred),FPNULL); 869 list_free(&(b3.Bsucc),FPNULL); 870 b2.BC = BCret; 871 b3.BC = BCret; 872 list_subtract(&(bsucc.Bpred),b2); 873 list_subtract(&(bsucc.Bpred),b3); 874 debug if (debugc) printf("brcombine(): if e1 goto e2 else goto e3 => ret e1?e2:e3\n"); 875 anychanges++; 876 } 877 } 878 else if (bc == BCgoto && PARSER) 879 { 880 block *b2 = b.nthSucc(0); 881 if (!list_next(b2.Bpred) && b2.BC != BCasm // if b is only parent 882 && b2 != startblock 883 && b2.BC != BCtry 884 && b2.BC != BC_try 885 && b.Btry == b2.Btry 886 ) 887 { 888 if (b2.Belem) 889 { 890 if (PARSER) 891 { 892 block_appendexp(b,b2.Belem); 893 } 894 else if (b.Belem) 895 b.Belem = el_bin(OPcomma,b2.Belem.Ety,b.Belem,b2.Belem); 896 else 897 b.Belem = b2.Belem; 898 b2.Belem = null; 899 } 900 list_subtract(&b.Bsucc,b2); 901 list_subtract(&b2.Bpred,b); 902 903 /* change predecessor of successors of b2 from b2 to b */ 904 foreach (bl; ListRange(b2.Bsucc)) 905 { 906 list_t bp; 907 for (bp = list_block(bl).Bpred; bp; bp = list_next(bp)) 908 { 909 if (list_block(bp) == b2) 910 bp.ptr = cast(void *)b; 911 } 912 } 913 914 b.BC = b2.BC; 915 b.BS = b2.BS; 916 b.Bsucc = b2.Bsucc; 917 b2.Bsucc = null; 918 b2.BC = BCret; /* a harmless one */ 919 debug if (debugc) printf("brcombine(): %p goto %p eliminated\n",b,b2); 920 anychanges++; 921 } 922 } 923 } 924 if (anychanges) 925 { go.changes++; 926 continue; 927 } 928 } while (0); 929 } 930 931 /*********************** 932 * Branch optimization. 933 */ 934 935 @trusted 936 private void bropt() 937 { 938 debug if (debugc) printf("bropt()\n"); 939 assert(!PARSER); 940 for (block *b = startblock; b; b = b.Bnext) // for each block 941 { 942 elem **pn = &(b.Belem); 943 if (OPTIMIZER && *pn) 944 while ((*pn).Eoper == OPcomma) 945 pn = &((*pn).EV.E2); 946 947 elem *n = *pn; 948 949 /* look for conditional that never returns */ 950 if (n && tybasic(n.Ety) == TYnoreturn && b.BC != BCexit) 951 { 952 b.BC = BCexit; 953 // Exit block has no successors, so remove them 954 foreach (bp; ListRange(b.Bsucc)) 955 { 956 list_subtract(&(list_block(bp).Bpred),b); 957 } 958 list_free(&b.Bsucc, FPNULL); 959 debug if (debugc) printf("CHANGE: noreturn becomes BCexit\n"); 960 go.changes++; 961 continue; 962 } 963 964 if (b.BC == BCiftrue) 965 { 966 assert(n); 967 /* Replace IF (!e) GOTO ... with */ 968 /* IF OPnot (e) GOTO ... */ 969 if (n.Eoper == OPnot) 970 { 971 tym_t tym; 972 973 tym = n.EV.E1.Ety; 974 *pn = el_selecte1(n); 975 (*pn).Ety = tym; 976 for (n = b.Belem; n.Eoper == OPcomma; n = n.EV.E2) 977 n.Ety = tym; 978 b.Bsucc = list_reverse(b.Bsucc); 979 debug if (debugc) printf("CHANGE: if (!e)\n"); 980 go.changes++; 981 } 982 983 /* Take care of IF (constant) */ 984 block *db; 985 if (iftrue(n)) /* if elem is true */ 986 { 987 // select first succ 988 db = b.nthSucc(1); 989 goto L1; 990 } 991 else if (iffalse(n)) 992 { 993 // select second succ 994 db = b.nthSucc(0); 995 996 L1: 997 list_subtract(&(b.Bsucc),db); 998 list_subtract(&(db.Bpred),b); 999 b.BC = BCgoto; 1000 /* delete elem if it has no side effects */ 1001 b.Belem = doptelem(b.Belem,GOALnone | GOALagain); 1002 debug if (debugc) printf("CHANGE: if (const)\n"); 1003 go.changes++; 1004 } 1005 1006 /* Look for both destinations being the same */ 1007 else if (b.nthSucc(0) == 1008 b.nthSucc(1)) 1009 { 1010 b.BC = BCgoto; 1011 db = b.nthSucc(0); 1012 list_subtract(&(b.Bsucc),db); 1013 list_subtract(&(db.Bpred),b); 1014 debug if (debugc) printf("CHANGE: if (e) goto L1; else goto L1;\n"); 1015 go.changes++; 1016 } 1017 } 1018 else if (b.BC == BCswitch) 1019 { /* see we can evaluate this switch now */ 1020 while (n.Eoper == OPcomma) 1021 n = n.EV.E2; 1022 if (n.Eoper != OPconst) 1023 continue; 1024 assert(tyintegral(n.Ety)); 1025 targ_llong value = el_tolong(n); 1026 targ_llong* pv = b.Bswitch; // ptr to switch data 1027 assert(pv); 1028 uint ncases = cast(uint) *pv++; // # of cases 1029 uint i = 1; // first case 1030 while (1) 1031 { 1032 if (i > ncases) 1033 { 1034 i = 0; // select default 1035 break; 1036 } 1037 if (*pv++ == value) 1038 break; // found it 1039 i++; // next case 1040 } 1041 /* the ith entry in Bsucc is the one we want */ 1042 block *db = b.nthSucc(i); 1043 /* delete predecessors of successors (!) */ 1044 foreach (bl; ListRange(b.Bsucc)) 1045 if (i--) // if not ith successor 1046 { 1047 void *p = list_subtract(&(list_block(bl).Bpred),b); 1048 assert(p == b); 1049 } 1050 1051 /* dump old successor list and create a new one */ 1052 list_free(&b.Bsucc,FPNULL); 1053 b.appendSucc(db); 1054 b.BC = BCgoto; 1055 b.Belem = doptelem(b.Belem,GOALnone | GOALagain); 1056 debug if (debugc) printf("CHANGE: switch (const)\n"); 1057 go.changes++; 1058 } 1059 } 1060 } 1061 1062 /********************************* 1063 * Do branch rearrangement. 1064 */ 1065 1066 @trusted 1067 private void brrear() 1068 { 1069 debug if (debugc) printf("brrear()\n"); 1070 for (block *b = startblock; b; b = b.Bnext) // for each block 1071 { 1072 foreach (bl; ListRange(b.Bsucc)) 1073 { /* For each transfer of control block pointer */ 1074 int iter = 0; 1075 1076 block *bt = list_block(bl); 1077 1078 /* If it is a transfer to a block that consists */ 1079 /* of nothing but an unconditional transfer, */ 1080 /* Replace transfer address with that */ 1081 /* transfer address. */ 1082 /* Note: There are certain kinds of infinite */ 1083 /* loops which we avoid by putting a lid on */ 1084 /* the number of iterations. */ 1085 1086 version (SCPP) 1087 { 1088 static if (NTEXCEPTIONS) 1089 enum additionalAnd = "b.Btry == bt.Btry && 1090 bt.Btry == bt.nthSucc(0).Btry"; 1091 else 1092 enum additionalAnd = "b.Btry == bt.Btry"; 1093 } 1094 else static if (NTEXCEPTIONS) 1095 enum additionalAnd = "b.Btry == bt.Btry && 1096 bt.Btry == bt.nthSucc(0).Btry"; 1097 else 1098 enum additionalAnd = "true"; 1099 1100 while (bt.BC == BCgoto && !bt.Belem && 1101 mixin(additionalAnd) && 1102 (OPTIMIZER || !(bt.Bsrcpos.Slinnum && configv.addlinenumbers)) && 1103 ++iter < 10) 1104 { 1105 bl.ptr = list_ptr(bt.Bsucc); 1106 if (bt.Bsrcpos.Slinnum && !b.Bsrcpos.Slinnum) 1107 b.Bsrcpos = bt.Bsrcpos; 1108 b.Bflags |= bt.Bflags; 1109 list_append(&(list_block(bl).Bpred),b); 1110 list_subtract(&(bt.Bpred),b); 1111 debug if (debugc) printf("goto.goto\n"); 1112 bt = list_block(bl); 1113 } 1114 1115 // Bsucc after the first are the targets of 1116 // jumps, calls and loops, and as such to do this right 1117 // we need to traverse the Bcode list and fix up 1118 // the IEV2.Vblock pointers. 1119 if (b.BC == BCasm) 1120 break; 1121 } 1122 1123 static if(0) 1124 { 1125 /* Replace cases of */ 1126 /* IF (e) GOTO L1 ELSE L2 */ 1127 /* L1: */ 1128 /* with */ 1129 /* IF OPnot (e) GOTO L2 ELSE L1 */ 1130 /* L1: */ 1131 1132 if (b.BC == BCiftrue || b.BC == BCiffalse) 1133 { 1134 block *bif = b.nthSucc(0); 1135 block *belse = b.nthSucc(1); 1136 1137 if (bif == b.Bnext) 1138 { 1139 b.BC ^= BCiffalse ^ BCiftrue; /* toggle */ 1140 b.setNthSucc(0, belse); 1141 b.setNthSucc(1, bif); 1142 b.Bflags |= bif.Bflags & BFLvisited; 1143 debug if (debugc) printf("if (e) L1 else L2\n"); 1144 } 1145 } 1146 } 1147 } /* for */ 1148 } 1149 1150 /************************* 1151 * Compute depth first order (DFO). 1152 * Equivalent to Aho & Ullman Fig. 13.8. 1153 * Blocks not in dfo[] are unreachable. 1154 * Params: 1155 * dfo = array to fill in in DFO 1156 * startblock = list of blocks 1157 */ 1158 @safe 1159 void compdfo(ref Barray!(block*) dfo, block* startblock) 1160 { 1161 debug if (debugc) printf("compdfo()\n"); 1162 debug assert(OPTIMIZER); 1163 block_clearvisit(); 1164 debug assert(!PARSER); 1165 dfo.setLength(0); 1166 1167 /****************************** 1168 * Add b's successors to dfo[], then b 1169 */ 1170 void walkDFO(block *b) 1171 { 1172 assert(b); 1173 b.Bflags |= BFLvisited; // executed at least once 1174 1175 foreach (bl; ListRange(b.Bsucc)) // for each successor 1176 { 1177 block *bs = list_block(bl); 1178 assert(bs); 1179 if ((bs.Bflags & BFLvisited) == 0) // if not visited 1180 walkDFO(bs); 1181 } 1182 1183 dfo.push(b); 1184 } 1185 1186 1187 dfo.setLength(0); 1188 walkDFO(startblock); 1189 1190 // Reverse the array 1191 if (dfo.length) 1192 { 1193 size_t i = 0; 1194 size_t k = dfo.length - 1; 1195 while (i < k) 1196 { 1197 auto b = dfo[k]; 1198 dfo[k] = dfo[i]; 1199 dfo[i] = b; 1200 ++i; 1201 --k; 1202 } 1203 1204 foreach (j, b; dfo[]) 1205 b.Bdfoidx = cast(uint)j; 1206 } 1207 1208 static if (0) debug 1209 { 1210 foreach (i, b; dfo[]) 1211 printf("dfo[%d] = %p\n", cast(int)i, b); 1212 } 1213 } 1214 1215 1216 /************************* 1217 * Remove blocks not marked as visited (they are not in dfo[]). 1218 * A block is not in dfo[] if not visited. 1219 */ 1220 1221 @trusted 1222 private void elimblks() 1223 { 1224 debug if (debugc) printf("elimblks()\n"); 1225 block *bf = null; 1226 block *b; 1227 for (block **pb = &startblock; (b = *pb) != null;) 1228 { 1229 if (((b.Bflags & BFLvisited) == 0) // if block is not visited 1230 && ((b.Bflags & BFLlabel) == 0) // need label offset 1231 ) 1232 { 1233 /* for each marked successor S to b */ 1234 /* remove b from S.Bpred. */ 1235 /* Presumably all predecessors to b are unmarked also. */ 1236 foreach (s; ListRange(b.Bsucc)) 1237 { 1238 assert(list_block(s)); 1239 if (list_block(s).Bflags & BFLvisited) /* if it is marked */ 1240 list_subtract(&(list_block(s).Bpred),b); 1241 } 1242 if (b.Balign && b.Bnext && b.Balign > b.Bnext.Balign) 1243 b.Bnext.Balign = b.Balign; 1244 *pb = b.Bnext; // remove from linked list 1245 1246 b.Bnext = bf; 1247 bf = b; // prepend to deferred list to free 1248 debug if (debugc) printf("CHANGE: block %p deleted\n",b); 1249 go.changes++; 1250 } 1251 else 1252 pb = &((*pb).Bnext); 1253 } 1254 1255 // Do deferred free's of the blocks 1256 for ( ; bf; bf = b) 1257 { 1258 b = bf.Bnext; 1259 block_free(bf); 1260 } 1261 1262 debug if (debugc) printf("elimblks done\n"); 1263 } 1264 1265 /********************************** 1266 * Merge together blocks where the first block is a goto to the next 1267 * block and the next block has only the first block as a predecessor. 1268 * Example: 1269 * e1; GOTO L2; 1270 * L2: return e2; 1271 * becomes: 1272 * L2: return (e1 , e2); 1273 * Returns: 1274 * # of merged blocks 1275 */ 1276 1277 @trusted 1278 private int mergeblks() 1279 { 1280 int merge = 0; 1281 1282 assert(OPTIMIZER); 1283 debug if (debugc) printf("mergeblks()\n"); 1284 foreach (b; dfo[]) 1285 { 1286 if (b.BC == BCgoto) 1287 { block *bL2 = list_block(b.Bsucc); 1288 1289 if (b == bL2) 1290 { 1291 Lcontinue: 1292 continue; 1293 } 1294 assert(bL2.Bpred); 1295 if (!list_next(bL2.Bpred) && bL2 != startblock) 1296 { 1297 if (b == bL2 || bL2.BC == BCasm) 1298 continue; 1299 1300 if (bL2.BC == BCtry || 1301 bL2.BC == BC_try || 1302 b.Btry != bL2.Btry) 1303 continue; 1304 version (SCPP) 1305 { 1306 // If any predecessors of b are BCasm, don't merge. 1307 foreach (bl; ListRange(b.Bpred)) 1308 { 1309 if (list_block(bl).BC == BCasm) 1310 goto Lcontinue; 1311 } 1312 } 1313 1314 /* JOIN the elems */ 1315 elem *e = el_combine(b.Belem,bL2.Belem); 1316 if (b.Belem && bL2.Belem) 1317 e = doptelem(e,bc_goal[bL2.BC] | GOALagain); 1318 bL2.Belem = e; 1319 b.Belem = null; 1320 1321 /* Remove b from predecessors of bL2 */ 1322 list_free(&(bL2.Bpred),FPNULL); 1323 bL2.Bpred = b.Bpred; 1324 b.Bpred = null; 1325 /* Remove bL2 from successors of b */ 1326 list_free(&b.Bsucc,FPNULL); 1327 1328 /* fix up successor list of predecessors */ 1329 foreach (bl; ListRange(bL2.Bpred)) 1330 { 1331 foreach (bs; ListRange(list_block(bl).Bsucc)) 1332 if (list_block(bs) == b) 1333 bs.ptr = cast(void *)bL2; 1334 } 1335 1336 merge++; 1337 debug if (debugc) printf("block %p merged with %p\n",b,bL2); 1338 1339 if (b == startblock) 1340 { /* bL2 is the new startblock */ 1341 debug if (debugc) printf("bL2 is new startblock\n"); 1342 /* Remove bL2 from list of blocks */ 1343 for (block **pb = &startblock; 1; pb = &(*pb).Bnext) 1344 { 1345 assert(*pb); 1346 if (*pb == bL2) 1347 { 1348 *pb = bL2.Bnext; 1349 break; 1350 } 1351 } 1352 1353 /* And relink bL2 at the start */ 1354 bL2.Bnext = startblock.Bnext; 1355 startblock = bL2; // new start 1356 1357 block_free(b); 1358 break; // dfo[] is now invalid 1359 } 1360 } 1361 } 1362 } 1363 return merge; 1364 } 1365 1366 /******************************* 1367 * Combine together blocks that are identical. 1368 */ 1369 1370 @trusted 1371 private void blident() 1372 { 1373 debug if (debugc) printf("blident()\n"); 1374 assert(startblock); 1375 1376 version (SCPP) 1377 { 1378 // Determine if any asm blocks 1379 int anyasm = 0; 1380 for (block *bn = startblock; bn; bn = bn.Bnext) 1381 { 1382 if (bn.BC == BCasm) 1383 { anyasm = 1; 1384 break; 1385 } 1386 } 1387 } 1388 1389 block *bnext; 1390 for (block *bn = startblock; bn; bn = bnext) 1391 { 1392 bnext = bn.Bnext; 1393 if (bn.Bflags & BFLnomerg) 1394 continue; 1395 1396 for (block *b = bnext; b; b = b.Bnext) 1397 { 1398 /* Blocks are identical if: */ 1399 /* BC match */ 1400 /* not optimized for time or it's a return */ 1401 /* (otherwise looprotate() is undone) */ 1402 /* successors match */ 1403 /* elems match */ 1404 static if (SCPP_OR_NTEXCEPTIONS) 1405 bool additionalAnd = b.Btry == bn.Btry; 1406 else 1407 enum additionalAnd = true; 1408 if (b.BC == bn.BC && 1409 //(!OPTIMIZER || !(go.mfoptim & MFtime) || !b.Bsucc) && 1410 (!OPTIMIZER || !(b.Bflags & BFLnomerg) || !b.Bsucc) && 1411 list_equal(b.Bsucc,bn.Bsucc) && 1412 additionalAnd && 1413 el_match(b.Belem,bn.Belem) 1414 ) 1415 { /* Eliminate block bn */ 1416 switch (b.BC) 1417 { 1418 case BCswitch: 1419 if (memcmp(b.Bswitch,bn.Bswitch,list_nitems(bn.Bsucc) * (*bn.Bswitch).sizeof)) 1420 continue; 1421 break; 1422 1423 case BCtry: 1424 case BCcatch: 1425 case BCjcatch: 1426 case BC_try: 1427 case BC_finally: 1428 case BC_lpad: 1429 case BCasm: 1430 Lcontinue: 1431 continue; 1432 1433 default: 1434 break; 1435 } 1436 assert(!b.Bcode); 1437 1438 foreach (bl; ListRange(bn.Bpred)) 1439 { 1440 block *bp = list_block(bl); 1441 if (bp.BC == BCasm) 1442 // Can't do this because of jmp's and loop's 1443 goto Lcontinue; 1444 } 1445 1446 static if(0) // && SCPP 1447 { 1448 // Predecessors must all be at the same btry level. 1449 if (bn.Bpred) 1450 { 1451 block *bp = list_block(bn.Bpred); 1452 btry = bp.Btry; 1453 if (bp.BC == BCtry) 1454 btry = bp; 1455 } 1456 else 1457 btry = null; 1458 1459 foreach (bl; ListRange(b.Bpred)) 1460 { 1461 block *bp = list_block(bl); 1462 if (bp.BC != BCtry) 1463 bp = bp.Btry; 1464 if (btry != bp) 1465 goto Lcontinue; 1466 } 1467 } 1468 1469 // if bn is startblock, eliminate b instead of bn 1470 if (bn == startblock) 1471 { 1472 goto Lcontinue; // can't handle predecessors to startblock 1473 // unreachable code 1474 //bn = b; 1475 //b = startblock; /* swap b and bn */ 1476 } 1477 1478 version (SCPP) 1479 { 1480 // Don't do it if any predecessors are ASM blocks, since 1481 // we'd have to walk the code list to fix up any jmps. 1482 if (anyasm) 1483 { 1484 foreach (bl; ListRange(bn.Bpred)) 1485 { 1486 block *bp = list_block(bl); 1487 if (bp.BC == BCasm) 1488 goto Lcontinue; 1489 foreach (bls; ListRange(bp.Bsucc)) 1490 if (list_block(bls) == bn && 1491 list_block(bls).BC == BCasm) 1492 goto Lcontinue; 1493 } 1494 } 1495 } 1496 1497 /* Change successors to predecessors of bn to point to */ 1498 /* b instead of bn */ 1499 foreach (bl; ListRange(bn.Bpred)) 1500 { 1501 block *bp = list_block(bl); 1502 foreach (bls; ListRange(bp.Bsucc)) 1503 if (list_block(bls) == bn) 1504 { bls.ptr = cast(void *)b; 1505 list_prepend(&b.Bpred,bp); 1506 } 1507 } 1508 1509 /* Entirely remove predecessor list from bn. */ 1510 /* elimblks() will delete bn entirely. */ 1511 list_free(&(bn.Bpred),FPNULL); 1512 1513 debug 1514 { 1515 assert(bn.BC != BCcatch); 1516 if (debugc) 1517 printf("block B%d (%p) removed, it was same as B%d (%p)\n", 1518 bn.Bdfoidx,bn,b.Bdfoidx,b); 1519 } 1520 1521 go.changes++; 1522 break; 1523 } 1524 } 1525 } 1526 } 1527 1528 /********************************** 1529 * Split out return blocks so the returns can be combined into a 1530 * single block by blident(). 1531 */ 1532 1533 @trusted 1534 private void blreturn() 1535 { 1536 if (!(go.mfoptim & MFtime)) /* if optimized for space */ 1537 { 1538 int retcount = 0; // number of return counts 1539 1540 /* Find last return block */ 1541 for (block *b = startblock; b; b = b.Bnext) 1542 { 1543 if (b.BC == BCret) 1544 retcount++; 1545 if (b.BC == BCasm) 1546 return; // mucks up blident() 1547 } 1548 1549 if (retcount < 2) /* quit if nothing to combine */ 1550 return; 1551 1552 /* Split return blocks */ 1553 for (block *b = startblock; b; b = b.Bnext) 1554 { 1555 if (b.BC != BCret) 1556 continue; 1557 static if (SCPP_OR_NTEXCEPTIONS) 1558 { 1559 // If no other blocks with the same Btry, don't split 1560 version (SCPP) 1561 { 1562 auto ifCondition = config.flags3 & CFG3eh; 1563 } 1564 else 1565 { 1566 enum ifCondition = true; 1567 } 1568 if (ifCondition) 1569 { 1570 for (block *b2 = startblock; b2; b2 = b2.Bnext) 1571 { 1572 if (b2.BC == BCret && b != b2 && b.Btry == b2.Btry) 1573 goto L1; 1574 } 1575 continue; 1576 } 1577 L1: 1578 } 1579 if (b.Belem) 1580 { /* Split b into a goto and a b */ 1581 debug if (debugc) 1582 printf("blreturn: splitting block B%d\n",b.Bdfoidx); 1583 1584 block *bn = block_calloc(); 1585 bn.BC = BCret; 1586 bn.Bnext = b.Bnext; 1587 static if(SCPP_OR_NTEXCEPTIONS) 1588 { 1589 bn.Btry = b.Btry; 1590 } 1591 b.BC = BCgoto; 1592 b.Bnext = bn; 1593 list_append(&b.Bsucc,bn); 1594 list_append(&bn.Bpred,b); 1595 1596 b = bn; 1597 } 1598 } 1599 1600 blident(); /* combine return blocks */ 1601 } 1602 } 1603 1604 /***************************************** 1605 * Convert comma-expressions into an array of expressions. 1606 */ 1607 1608 @trusted 1609 extern (D) 1610 private void bl_enlist2(ref Barray!(elem*) elems, elem* e) 1611 { 1612 if (e) 1613 { 1614 elem_debug(e); 1615 if (e.Eoper == OPcomma) 1616 { 1617 bl_enlist2(elems, e.EV.E1); 1618 bl_enlist2(elems, e.EV.E2); 1619 e.EV.E1 = e.EV.E2 = null; 1620 el_free(e); 1621 } 1622 else 1623 elems.push(e); 1624 } 1625 } 1626 1627 @trusted 1628 private list_t bl_enlist(elem *e) 1629 { 1630 list_t el = null; 1631 1632 if (e) 1633 { 1634 elem_debug(e); 1635 if (e.Eoper == OPcomma) 1636 { 1637 list_t el2 = bl_enlist(e.EV.E1); 1638 el = bl_enlist(e.EV.E2); 1639 e.EV.E1 = e.EV.E2 = null; 1640 el_free(e); 1641 1642 /* Append el2 list to el */ 1643 assert(el); 1644 list_t pl; 1645 for (pl = el; list_next(pl); pl = list_next(pl)) 1646 {} 1647 pl.next = el2; 1648 } 1649 else 1650 list_prepend(&el,e); 1651 } 1652 return el; 1653 } 1654 1655 /***************************************** 1656 * Take a list of expressions and convert it back into an expression tree. 1657 */ 1658 1659 extern (D) 1660 private elem* bl_delist2(elem*[] elems) 1661 { 1662 elem* result = null; 1663 foreach (e; elems) 1664 { 1665 result = el_combine(result, e); 1666 } 1667 return result; 1668 } 1669 1670 @trusted 1671 private elem * bl_delist(list_t el) 1672 { 1673 elem *e = null; 1674 foreach (els; ListRange(el)) 1675 e = el_combine(list_elem(els),e); 1676 list_free(&el,FPNULL); 1677 return e; 1678 } 1679 1680 /***************************************** 1681 * Do tail merging. 1682 */ 1683 1684 @trusted 1685 private void bltailmerge() 1686 { 1687 debug if (debugc) printf("bltailmerge()\n"); 1688 assert(!PARSER && OPTIMIZER); 1689 if (!(go.mfoptim & MFtime)) /* if optimized for space */ 1690 { 1691 /* Split each block into a reversed linked list of elems */ 1692 for (block *b = startblock; b; b = b.Bnext) 1693 b.Blist = bl_enlist(b.Belem); 1694 1695 /* Search for two blocks that have the same successor list. 1696 If the first expressions both lists are the same, split 1697 off a new block with that expression in it. 1698 */ 1699 static if (SCPP_OR_NTEXCEPTIONS) 1700 enum additionalAnd = "b.Btry == bn.Btry"; 1701 else 1702 enum additionalAnd = "true"; 1703 for (block *b = startblock; b; b = b.Bnext) 1704 { 1705 if (!b.Blist) 1706 continue; 1707 elem *e = list_elem(b.Blist); 1708 elem_debug(e); 1709 for (block *bn = b.Bnext; bn; bn = bn.Bnext) 1710 { 1711 elem *en; 1712 if (b.BC == bn.BC && 1713 list_equal(b.Bsucc,bn.Bsucc) && 1714 bn.Blist && 1715 el_match(e,(en = list_elem(bn.Blist))) && 1716 mixin(additionalAnd) 1717 ) 1718 { 1719 switch (b.BC) 1720 { 1721 case BCswitch: 1722 if (memcmp(b.Bswitch,bn.Bswitch,list_nitems(bn.Bsucc) * (*bn.Bswitch).sizeof)) 1723 continue; 1724 break; 1725 1726 case BCtry: 1727 case BCcatch: 1728 case BCjcatch: 1729 case BC_try: 1730 case BC_finally: 1731 case BC_lpad: 1732 case BCasm: 1733 continue; 1734 1735 default: 1736 break; 1737 } 1738 1739 /* We've got a match */ 1740 1741 /* Create a new block, bnew, which will be the 1742 merged block. Physically locate it just after bn. 1743 */ 1744 debug if (debugc) 1745 printf("tail merging: %p and %p\n", b, bn); 1746 1747 block *bnew = block_calloc(); 1748 bnew.Bnext = bn.Bnext; 1749 bnew.BC = b.BC; 1750 static if (SCPP_OR_NTEXCEPTIONS) 1751 { 1752 bnew.Btry = b.Btry; 1753 } 1754 if (bnew.BC == BCswitch) 1755 { 1756 bnew.Bswitch = b.Bswitch; 1757 b.Bswitch = null; 1758 bn.Bswitch = null; 1759 } 1760 bn.Bnext = bnew; 1761 1762 /* The successor list to bnew is the same as b's was */ 1763 bnew.Bsucc = b.Bsucc; 1764 b.Bsucc = null; 1765 list_free(&bn.Bsucc,FPNULL); 1766 1767 /* Update the predecessor list of the successor list 1768 of bnew, from b to bnew, and removing bn 1769 */ 1770 foreach (bl; ListRange(bnew.Bsucc)) 1771 { 1772 list_subtract(&list_block(bl).Bpred,b); 1773 list_subtract(&list_block(bl).Bpred,bn); 1774 list_append(&list_block(bl).Bpred,bnew); 1775 } 1776 1777 /* The predecessors to bnew are b and bn */ 1778 list_append(&bnew.Bpred,b); 1779 list_append(&bnew.Bpred,bn); 1780 1781 /* The successors to b and bn are bnew */ 1782 b.BC = BCgoto; 1783 bn.BC = BCgoto; 1784 list_append(&b.Bsucc,bnew); 1785 list_append(&bn.Bsucc,bnew); 1786 1787 go.changes++; 1788 1789 /* Find all the expressions we can merge */ 1790 do 1791 { 1792 list_append(&bnew.Blist,e); 1793 el_free(en); 1794 list_pop(&b.Blist); 1795 list_pop(&bn.Blist); 1796 if (!b.Blist) 1797 goto nextb; 1798 e = list_elem(b.Blist); 1799 if (!bn.Blist) 1800 break; 1801 en = list_elem(bn.Blist); 1802 } while (el_match(e,en)); 1803 } 1804 } 1805 nextb: 1806 } 1807 1808 /* Recombine elem lists into expression trees */ 1809 for (block *b = startblock; b; b = b.Bnext) 1810 b.Belem = bl_delist(b.Blist); 1811 } 1812 } 1813 1814 /********************************** 1815 * Rearrange blocks to minimize jmp's. 1816 */ 1817 1818 @trusted 1819 private void brmin() 1820 { 1821 version (SCPP) 1822 { 1823 // Dunno how this may mess up generating EH tables. 1824 if (config.flags3 & CFG3eh) // if EH turned on 1825 return; 1826 } 1827 1828 debug if (debugc) printf("brmin()\n"); 1829 debug assert(startblock); 1830 for (block *b = startblock.Bnext; b; b = b.Bnext) 1831 { 1832 block *bnext = b.Bnext; 1833 if (!bnext) 1834 break; 1835 foreach (bl; ListRange(b.Bsucc)) 1836 { 1837 block *bs = list_block(bl); 1838 if (bs == bnext) 1839 goto L1; 1840 } 1841 1842 // b is a block which does not have bnext as a successor. 1843 // Look for a successor of b for which everyone must jmp to. 1844 1845 foreach (bl; ListRange(b.Bsucc)) 1846 { 1847 block *bs = list_block(bl); 1848 block *bn; 1849 foreach (blp; ListRange(bs.Bpred)) 1850 { 1851 block *bsp = list_block(blp); 1852 if (bsp.Bnext == bs) 1853 goto L2; 1854 } 1855 1856 // Move bs so it is the Bnext of b 1857 for (bn = bnext; 1; bn = bn.Bnext) 1858 { 1859 if (!bn) 1860 goto L2; 1861 if (bn.Bnext == bs) 1862 break; 1863 } 1864 bn.Bnext = null; 1865 b.Bnext = bs; 1866 for (bn = bs; bn.Bnext; bn = bn.Bnext) 1867 {} 1868 bn.Bnext = bnext; 1869 debug if (debugc) printf("Moving block %p to appear after %p\n",bs,b); 1870 go.changes++; 1871 break; 1872 1873 L2: 1874 } 1875 1876 1877 L1: 1878 } 1879 } 1880 1881 /******************************** 1882 * Check integrity of blocks. 1883 */ 1884 1885 static if(0) 1886 { 1887 1888 @trusted 1889 private void block_check() 1890 { 1891 for (block *b = startblock; b; b = b.Bnext) 1892 { 1893 int nsucc = list_nitems(b.Bsucc); 1894 int npred = list_nitems(b.Bpred); 1895 switch (b.BC) 1896 { 1897 case BCgoto: 1898 assert(nsucc == 1); 1899 break; 1900 1901 case BCiftrue: 1902 assert(nsucc == 2); 1903 break; 1904 } 1905 1906 foreach (bl; ListRange(b.Bsucc)) 1907 { 1908 block *bs = list_block(bl); 1909 1910 foreach (bls; ListRange(bs.Bpred)) 1911 { 1912 assert(bls); 1913 if (list_block(bls) == b) 1914 break; 1915 } 1916 } 1917 } 1918 } 1919 1920 } 1921 1922 /*************************************** 1923 * Do tail recursion. 1924 */ 1925 1926 @trusted 1927 private void brtailrecursion() 1928 { 1929 version (SCPP) 1930 { 1931 // if (tyvariadic(funcsym_p.Stype)) 1932 return; 1933 return; // haven't dealt with struct params, and ctors/dtors 1934 } 1935 if (funcsym_p.Sfunc.Fflags3 & Fnotailrecursion) 1936 return; 1937 if (localgot) 1938 { /* On OSX, tail recursion will result in two OPgot's: 1939 int status5; 1940 struct MyStruct5 { } 1941 void rec5(int i, MyStruct5 s) 1942 { 1943 if( i > 0 ) 1944 { status5++; 1945 rec5(i-1, s); 1946 } 1947 } 1948 */ 1949 1950 return; 1951 } 1952 1953 for (block *b = startblock; b; b = b.Bnext) 1954 { 1955 if (b.BC == BC_try) 1956 return; 1957 elem **pe = &b.Belem; 1958 block *bn = null; 1959 if (*pe && 1960 (b.BC == BCret || 1961 b.BC == BCretexp || 1962 (b.BC == BCgoto && (bn = list_block(b.Bsucc)).Belem == null && 1963 bn.BC == BCret) 1964 ) 1965 ) 1966 { 1967 if (el_anyframeptr(*pe)) // if any OPframeptr's 1968 return; 1969 1970 static elem** skipCommas(elem** pe) 1971 { 1972 while ((*pe).Eoper == OPcomma) 1973 pe = &(*pe).EV.E2; 1974 return pe; 1975 } 1976 1977 pe = skipCommas(pe); 1978 1979 elem *e = *pe; 1980 1981 static bool isCandidate(elem* e) 1982 { 1983 e = *skipCommas(&e); 1984 if (e.Eoper == OPcond) 1985 return isCandidate(e.EV.E2.EV.E1) || isCandidate(e.EV.E2.EV.E2); 1986 1987 return OTcall(e.Eoper) && 1988 e.EV.E1.Eoper == OPvar && 1989 e.EV.E1.EV.Vsym == funcsym_p; 1990 } 1991 1992 if (e.Eoper == OPcond && 1993 (isCandidate(e.EV.E2.EV.E1) || isCandidate(e.EV.E2.EV.E2))) 1994 { 1995 /* Split OPcond into a BCiftrue block and two return blocks 1996 */ 1997 block* b1 = block_calloc(); 1998 block* b2 = block_calloc(); 1999 2000 b1.Belem = e.EV.E2.EV.E1; 2001 e.EV.E2.EV.E1 = null; 2002 2003 b2.Belem = e.EV.E2.EV.E2; 2004 e.EV.E2.EV.E2 = null; 2005 2006 *pe = e.EV.E1; 2007 e.EV.E1 = null; 2008 el_free(e); 2009 2010 if (b.BC == BCgoto) 2011 { 2012 list_subtract(&b.Bsucc, bn); 2013 list_subtract(&bn.Bpred, b); 2014 list_append(&b1.Bsucc, bn); 2015 list_append(&bn.Bpred, b1); 2016 list_append(&b2.Bsucc, bn); 2017 list_append(&bn.Bpred, b2); 2018 } 2019 2020 list_append(&b.Bsucc, b1); 2021 list_append(&b1.Bpred, b); 2022 list_append(&b.Bsucc, b2); 2023 list_append(&b2.Bpred, b); 2024 2025 b1.BC = b.BC; 2026 b2.BC = b.BC; 2027 b.BC = BCiftrue; 2028 2029 b2.Bnext = b.Bnext; 2030 b1.Bnext = b2; 2031 b.Bnext = b1; 2032 continue; 2033 } 2034 2035 if (OTcall(e.Eoper) && 2036 e.EV.E1.Eoper == OPvar && 2037 e.EV.E1.EV.Vsym == funcsym_p) 2038 { 2039 //printf("before:\n"); 2040 //elem_print(*pe); 2041 if (OTunary(e.Eoper)) 2042 { 2043 *pe = el_long(TYint,0); 2044 } 2045 else 2046 { 2047 int si = 0; 2048 elem *e2 = null; 2049 *pe = assignparams(&e.EV.E2,&si,&e2); 2050 *pe = el_combine(*pe,e2); 2051 } 2052 el_free(e); 2053 //printf("after:\n"); 2054 //elem_print(*pe); 2055 2056 if (b.BC == BCgoto) 2057 { 2058 list_subtract(&b.Bsucc,bn); 2059 list_subtract(&bn.Bpred,b); 2060 } 2061 b.BC = BCgoto; 2062 list_append(&b.Bsucc,startblock); 2063 list_append(&startblock.Bpred,b); 2064 2065 // Create a new startblock, bs, because startblock cannot 2066 // have predecessors. 2067 block *bs = block_calloc(); 2068 bs.BC = BCgoto; 2069 bs.Bnext = startblock; 2070 list_append(&bs.Bsucc,startblock); 2071 list_append(&startblock.Bpred,bs); 2072 startblock = bs; 2073 2074 debug if (debugc) printf("tail recursion\n"); 2075 go.changes++; 2076 } 2077 } 2078 } 2079 } 2080 2081 /***************************************** 2082 * Convert parameter expression to assignment statements. 2083 */ 2084 2085 @trusted 2086 private elem * assignparams(elem **pe,int *psi,elem **pe2) 2087 { 2088 elem *e = *pe; 2089 2090 if (e.Eoper == OPparam) 2091 { 2092 elem *ea = null; 2093 elem *eb = null; 2094 elem *e2 = assignparams(&e.EV.E2,psi,&eb); 2095 elem *e1 = assignparams(&e.EV.E1,psi,&ea); 2096 e.EV.E1 = null; 2097 e.EV.E2 = null; 2098 e = el_combine(e1,e2); 2099 *pe2 = el_combine(eb,ea); 2100 } 2101 else 2102 { 2103 int si = *psi; 2104 type *t; 2105 2106 assert(si < globsym.length); 2107 Symbol *sp = globsym[si]; 2108 Symbol *s = symbol_genauto(sp.Stype); 2109 s.Sfl = FLauto; 2110 int op = OPeq; 2111 if (e.Eoper == OPstrpar) 2112 { 2113 op = OPstreq; 2114 t = e.ET; 2115 elem *ex = e; 2116 e = e.EV.E1; 2117 ex.EV.E1 = null; 2118 el_free(ex); 2119 } 2120 elem *es = el_var(s); 2121 es.Ety = e.Ety; 2122 e = el_bin(op,TYvoid,es,e); 2123 if (op == OPstreq) 2124 e.ET = t; 2125 *pe2 = el_bin(op,TYvoid,el_var(sp),el_copytree(es)); 2126 (*pe2).EV.E1.Ety = es.Ety; 2127 if (op == OPstreq) 2128 (*pe2).ET = t; 2129 *psi = ++si; 2130 *pe = null; 2131 } 2132 return e; 2133 } 2134 2135 /**************************************************** 2136 * Eliminate empty loops. 2137 */ 2138 2139 @trusted 2140 private void emptyloops() 2141 { 2142 debug if (debugc) printf("emptyloops()\n"); 2143 for (block *b = startblock; b; b = b.Bnext) 2144 { 2145 if (b.BC == BCiftrue && 2146 list_block(b.Bsucc) == b && 2147 list_nitems(b.Bpred) == 2) 2148 { 2149 // Find predecessor to b 2150 block *bpred = list_block(b.Bpred); 2151 if (bpred == b) 2152 bpred = list_block(list_next(b.Bpred)); 2153 if (!bpred.Belem) 2154 continue; 2155 2156 // Find einit 2157 elem *einit; 2158 for (einit = bpred.Belem; einit.Eoper == OPcomma; einit = einit.EV.E2) 2159 { } 2160 if (einit.Eoper != OPeq || 2161 einit.EV.E2.Eoper != OPconst || 2162 einit.EV.E1.Eoper != OPvar) 2163 continue; 2164 2165 // Look for ((i += 1) < limit) 2166 elem *erel = b.Belem; 2167 if (erel.Eoper != OPlt || 2168 erel.EV.E2.Eoper != OPconst || 2169 erel.EV.E1.Eoper != OPaddass) 2170 continue; 2171 2172 elem *einc = erel.EV.E1; 2173 if (einc.EV.E2.Eoper != OPconst || 2174 einc.EV.E1.Eoper != OPvar || 2175 !el_match(einc.EV.E1,einit.EV.E1)) 2176 continue; 2177 2178 if (!tyintegral(einit.EV.E1.Ety) || 2179 el_tolong(einc.EV.E2) != 1 || 2180 el_tolong(einit.EV.E2) >= el_tolong(erel.EV.E2) 2181 ) 2182 continue; 2183 2184 { 2185 erel.Eoper = OPeq; 2186 erel.Ety = erel.EV.E1.Ety; 2187 erel.EV.E1 = el_selecte1(erel.EV.E1); 2188 b.BC = BCgoto; 2189 list_subtract(&b.Bsucc,b); 2190 list_subtract(&b.Bpred,b); 2191 2192 debug if (debugc) 2193 { 2194 WReqn(erel); 2195 printf(" eliminated loop\n"); 2196 } 2197 2198 go.changes++; 2199 } 2200 } 2201 } 2202 } 2203 2204 /****************************************** 2205 * Determine if function has any side effects. 2206 * This means, determine if all the function does is return a value; 2207 * no extraneous definitions or effects or exceptions. 2208 * A function with no side effects can be CSE'd. It does not reference 2209 * statics or indirect references. 2210 */ 2211 2212 @trusted 2213 private void funcsideeffects() 2214 { 2215 version (MARS) 2216 { 2217 //printf("funcsideeffects('%s')\n",funcsym_p.Sident); 2218 for (block *b = startblock; b; b = b.Bnext) 2219 { 2220 if (b.Belem && funcsideeffect_walk(b.Belem)) 2221 { 2222 //printf(" function '%s' has side effects\n",funcsym_p.Sident); 2223 return; 2224 } 2225 } 2226 funcsym_p.Sfunc.Fflags3 |= Fnosideeff; 2227 //printf(" function '%s' has no side effects\n",funcsym_p.Sident); 2228 } 2229 } 2230 2231 version (MARS) 2232 { 2233 2234 @trusted 2235 private int funcsideeffect_walk(elem *e) 2236 { 2237 assert(e); 2238 elem_debug(e); 2239 if (typemask(e) & (mTYvolatile | mTYshared)) 2240 return 1; 2241 int op = e.Eoper; 2242 switch (op) 2243 { 2244 case OPcall: 2245 case OPucall: 2246 Symbol *s; 2247 if (e.EV.E1.Eoper == OPvar && 2248 tyfunc((s = e.EV.E1.EV.Vsym).Stype.Tty) && 2249 ((s.Sfunc && s.Sfunc.Fflags3 & Fnosideeff) || s == funcsym_p) 2250 ) 2251 break; 2252 goto Lside; 2253 2254 // Note: we should allow assignments to local variables as 2255 // not being a 'side effect'. 2256 2257 default: 2258 assert(op < OPMAX); 2259 return OTsideff(op) || 2260 (OTunary(op) && funcsideeffect_walk(e.EV.E1)) || 2261 (OTbinary(op) && (funcsideeffect_walk(e.EV.E1) || 2262 funcsideeffect_walk(e.EV.E2))); 2263 } 2264 return 0; 2265 2266 Lside: 2267 return 1; 2268 } 2269 2270 } 2271 2272 /******************************* 2273 * Determine if there are any OPframeptr's in the tree. 2274 */ 2275 2276 @trusted 2277 private int el_anyframeptr(elem *e) 2278 { 2279 while (1) 2280 { 2281 if (OTunary(e.Eoper)) 2282 e = e.EV.E1; 2283 else if (OTbinary(e.Eoper)) 2284 { 2285 if (el_anyframeptr(e.EV.E2)) 2286 return 1; 2287 e = e.EV.E1; 2288 } 2289 else if (e.Eoper == OPframeptr) 2290 return 1; 2291 else 2292 break; 2293 } 2294 return 0; 2295 } 2296 2297 2298 /************************************** 2299 * Split off asserts into their very own BCexit 2300 * blocks after the end of the function. 2301 * This is because assert calls are never in a hot branch. 2302 */ 2303 2304 @trusted 2305 private void blassertsplit() 2306 { 2307 debug if (debugc) printf("blassertsplit()\n"); 2308 Barray!(elem*) elems; 2309 for (block *b = startblock; b; b = b.Bnext) 2310 { 2311 /* Not sure of effect of jumping out of a try block 2312 */ 2313 if (b.Btry) 2314 continue; 2315 2316 if (b.BC == BCexit) 2317 continue; 2318 2319 elems.reset(); 2320 bl_enlist2(elems, b.Belem); 2321 auto earray = elems[]; 2322 L1: 2323 int dctor = 0; 2324 2325 int accumDctor(elem *e) 2326 { 2327 while (1) 2328 { 2329 if (OTunary(e.Eoper)) 2330 { 2331 e = e.EV.E1; 2332 continue; 2333 } 2334 else if (OTbinary(e.Eoper)) 2335 { 2336 accumDctor(e.EV.E1); 2337 e = e.EV.E2; 2338 continue; 2339 } 2340 else if (e.Eoper == OPdctor) 2341 ++dctor; 2342 else if (e.Eoper == OPddtor) 2343 --dctor; 2344 break; 2345 } 2346 return dctor; 2347 } 2348 2349 foreach (i, e; earray) 2350 { 2351 if (!(dctor == 0 && // don't split block between a dctor..ddtor pair 2352 e.Eoper == OPoror && e.EV.E2.Eoper == OPcall && e.EV.E2.EV.E1.Eoper == OPvar)) 2353 { 2354 accumDctor(e); 2355 continue; 2356 } 2357 Symbol *f = e.EV.E2.EV.E1.EV.Vsym; 2358 if (!(f.Sflags & SFLexit)) 2359 { 2360 accumDctor(e); 2361 continue; 2362 } 2363 2364 if (accumDctor(e.EV.E1)) 2365 { 2366 accumDctor(e.EV.E2); 2367 continue; 2368 } 2369 2370 // Create exit block 2371 block *bexit = block_calloc(); 2372 bexit.BC = BCexit; 2373 bexit.Belem = e.EV.E2; 2374 2375 /* Append bexit to block list 2376 */ 2377 for (block *bx = b; 1; ) 2378 { 2379 block* bxn = bx.Bnext; 2380 if (!bxn) 2381 { 2382 bx.Bnext = bexit; 2383 break; 2384 } 2385 bx = bxn; 2386 } 2387 2388 earray[i] = e.EV.E1; 2389 e.EV.E1 = null; 2390 e.EV.E2 = null; 2391 el_free(e); 2392 2393 /* Split b into two blocks, [b,b2] 2394 */ 2395 block *b2 = block_calloc(); 2396 b2.Bnext = b.Bnext; 2397 b.Bnext = b2; 2398 b2.BC = b.BC; 2399 b2.BS = b.BS; 2400 2401 b.Belem = bl_delist2(earray[0 .. i + 1]); 2402 2403 /* Transfer successors of b to b2. 2404 * Fix up predecessors of successors to b2 to point to b2 instead of b 2405 */ 2406 b2.Bsucc = b.Bsucc; 2407 b.Bsucc = null; 2408 foreach (b2sl; ListRange(b2.Bsucc)) 2409 { 2410 block *b2s = list_block(b2sl); 2411 foreach (b2spl; ListRange(b2s.Bpred)) 2412 { 2413 if (list_block(b2spl) == b) 2414 b2spl.ptr = cast(void *)b2; 2415 } 2416 } 2417 2418 b.BC = BCiftrue; 2419 assert(b.Belem); 2420 list_append(&b.Bsucc, b2); 2421 list_append(&b2.Bpred, b); 2422 list_append(&b.Bsucc, bexit); 2423 list_append(&bexit.Bpred, b); 2424 2425 b = b2; 2426 earray = earray[i + 1 .. earray.length]; // rest of expressions go into b2 2427 debug if (debugc) 2428 { 2429 printf(" split off assert\n"); 2430 } 2431 go.changes++; 2432 goto L1; 2433 } 2434 b.Belem = bl_delist2(earray); 2435 if (b.BC == BCretexp && !b.Belem) 2436 b.Belem = el_long(TYint, 1); 2437 2438 } 2439 elems.dtor(); 2440 } 2441 2442 /************************************************* 2443 * Detect exit blocks and move them to the end. 2444 */ 2445 @trusted 2446 private void blexit() 2447 { 2448 debug if (debugc) printf("blexit()\n"); 2449 2450 Barray!(block*) bexits; 2451 for (block *b = startblock; b; b = b.Bnext) 2452 { 2453 /* Not sure of effect of jumping out of a try block 2454 */ 2455 if (b.Btry) 2456 continue; 2457 2458 if (b.BC == BCexit) 2459 continue; 2460 2461 if (!b.Belem || el_returns(b.Belem)) 2462 continue; 2463 2464 b.BC = BCexit; 2465 2466 foreach (bsl; ListRange(b.Bsucc)) 2467 { 2468 block *bs = list_block(bsl); 2469 list_subtract(&bs.Bpred, b); 2470 } 2471 list_free(&b.Bsucc, FPNULL); 2472 2473 if (b != startblock && b.Bnext) 2474 bexits.push(b); 2475 2476 debug if (debugc) 2477 printf(" to exit block\n"); 2478 go.changes++; 2479 } 2480 2481 /* Move all the newly detected Bexit blocks in bexits[] to the end 2482 */ 2483 2484 /* First remove them from the list of blocks 2485 */ 2486 size_t i = 0; 2487 block** pb = &startblock.Bnext; 2488 while (1) 2489 { 2490 if (i == bexits.length) 2491 break; 2492 2493 if (*pb == bexits[i]) 2494 { 2495 *pb = (*pb).Bnext; 2496 ++i; 2497 } 2498 else 2499 pb = &(*pb).Bnext; 2500 } 2501 2502 /* Advance pb to point to the last Bnext 2503 */ 2504 while (*pb) 2505 pb = &(*pb).Bnext; 2506 2507 /* Append the bexits[] to the end 2508 */ 2509 foreach (b; bexits[]) 2510 { 2511 *pb = b; 2512 pb = &b.Bnext; 2513 } 2514 *pb = null; 2515 2516 bexits.dtor(); 2517 }