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