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