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