1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1986-1998 by Symantec
6  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     Distributed under the Boost Software License, Version 1.0.
9  *              http://www.boost.org/LICENSE_1_0.txt
10  * Source:      https://github.com/dlang/dmd/blob/master/src/dmd/backend/go.d
11  */
12 
13 module dmd.backend.go;
14 
15 version (SPP)
16 {
17 }
18 else
19 {
20 
21 import core.stdc.stdio;
22 import core.stdc.stdlib;
23 import core.stdc.string;
24 import core.stdc.time;
25 
26 import dmd.backend.cc;
27 import dmd.backend.cdef;
28 import dmd.backend.oper;
29 import dmd.backend.global;
30 import dmd.backend.goh;
31 import dmd.backend.el;
32 import dmd.backend.ty;
33 import dmd.backend.type;
34 
35 import dmd.backend.dlist;
36 import dmd.backend.dvec;
37 
38 version (OSX)
39 {
40     /* Need this until the bootstrap compiler is upgraded
41      * https://github.com/dlang/druntime/pull/2237
42      */
43     enum clock_t CLOCKS_PER_SEC = 1_000_000; // was 100 until OSX 10.4/10.5
44 }
45 
46 extern (C++):
47 
48 nothrow:
49 
50 int os_clock();
51 
52 /* gdag.c */
53 void builddags();
54 void boolopt();
55 void opt_arraybounds();
56 
57 /* gflow.c */
58 void flowrd();
59 void flowlv();
60 void flowae();
61 void flowvbe();
62 void flowcp();
63 void flowae();
64 void genkillae();
65 void flowarraybounds();
66 int ae_field_affect(elem *lvalue,elem *e);
67 
68 /* glocal.c */
69 void localize();
70 
71 /* gloop.c */
72 int blockinit();
73 void compdom();
74 void loopopt();
75 void fillInDNunambig(vec_t v, elem *e);
76 void updaterd(elem *n,vec_t GEN,vec_t KILL);
77 
78 /* gother.c */
79 void rd_arraybounds();
80 void rd_free();
81 void constprop();
82 void copyprop();
83 void rmdeadass();
84 void elimass(elem *);
85 void deadvar();
86 void verybusyexp();
87 list_t listrds(vec_t, elem *, vec_t);
88 
89 /* gslice.c */
90 void sliceStructs(symtab_t*, block*);
91 
92 /***************************************************************************/
93 
94 extern (C) void mem_free(void* p);
95 
96 void go_term()
97 {
98     vec_free(go.defkill);
99     vec_free(go.starkill);
100     vec_free(go.vptrkill);
101     go.defnod.__dtor();
102     go.expnod.__dtor();
103     go.expblk.__dtor();
104 }
105 
106 debug
107 {
108                         // to print progress message and current trees set to
109                         // DEBUG_TREES to 1 and uncomment next 2 lines
110 //debug = DEBUG_TREES;
111 debug (DEBUG_TREES)
112     void dbg_optprint(const(char) *);
113 else
114     void dbg_optprint(const(char) *c)
115     {
116         // to print progress message, undo comment
117         // printf(c);
118     }
119 }
120 else
121 {
122     void dbg_optprint(const(char) *c) { }
123 }
124 
125 /**************************************
126  * Parse optimizer command line flag.
127  * Input:
128  *      cp      flag string
129  * Returns:
130  *      0       not recognized
131  *      !=0     recognized
132  */
133 
134 int go_flag(char *cp)
135 {
136     enum GL     // indices of various flags in flagtab[]
137     {
138         O,all,cnp,cp,cse,da,dc,dv,li,liv,local,loop,
139         none,o,reg,space,speed,time,tree,vbe,MAX
140     }
141     static immutable char*[GL.MAX] flagtab =
142     [   "O","all","cnp","cp","cse","da","dc","dv","li","liv","local","loop",
143         "none","o","reg","space","speed","time","tree","vbe"
144     ];
145     static immutable mftype[GL.MAX] flagmftab =
146     [   0,MFall,MFcnp,MFcp,MFcse,MFda,MFdc,MFdv,MFli,MFliv,MFlocal,MFloop,
147         0,0,MFreg,0,MFtime,MFtime,MFtree,MFvbe
148     ];
149 
150     //printf("go_flag('%s')\n", cp);
151     uint flag = binary(cp + 1,cast(const(char)**)flagtab.ptr,GL.MAX);
152     if (go.mfoptim == 0 && flag != -1)
153         go.mfoptim = MFall & ~MFvbe;
154 
155     if (*cp == '-')                     /* a regular -whatever flag     */
156     {                                   /* cp -> flag string            */
157         switch (flag)
158         {
159             case GL.all:
160             case GL.cnp:
161             case GL.cp:
162             case GL.dc:
163             case GL.da:
164             case GL.dv:
165             case GL.cse:
166             case GL.li:
167             case GL.liv:
168             case GL.local:
169             case GL.loop:
170             case GL.reg:
171             case GL.speed:
172             case GL.time:
173             case GL.tree:
174             case GL.vbe:
175                 go.mfoptim &= ~flagmftab[flag];    /* clear bits   */
176                 break;
177             case GL.o:
178             case GL.O:
179             case GL.none:
180                 go.mfoptim |= MFall & ~MFvbe;      // inverse of -all
181                 break;
182             case GL.space:
183                 go.mfoptim |= MFtime;      /* inverse of -time     */
184                 break;
185             case -1:                    /* not in flagtab[]     */
186                 goto badflag;
187             default:
188                 assert(0);
189         }
190     }
191     else if (*cp == '+')                /* a regular +whatever flag     */
192     {                           /* cp -> flag string            */
193         switch (flag)
194         {
195             case GL.all:
196             case GL.cnp:
197             case GL.cp:
198             case GL.dc:
199             case GL.da:
200             case GL.dv:
201             case GL.cse:
202             case GL.li:
203             case GL.liv:
204             case GL.local:
205             case GL.loop:
206             case GL.reg:
207             case GL.speed:
208             case GL.time:
209             case GL.tree:
210             case GL.vbe:
211                 go.mfoptim |= flagmftab[flag];     /* set bits     */
212                 break;
213             case GL.none:
214                 go.mfoptim &= ~MFall;      /* inverse of +all      */
215                 break;
216             case GL.space:
217                 go.mfoptim &= ~MFtime;     /* inverse of +time     */
218                 break;
219             case -1:                    /* not in flagtab[]     */
220                 goto badflag;
221             default:
222                 assert(0);
223         }
224     }
225     if (go.mfoptim)
226     {
227         go.mfoptim |= MFtree | MFdc;       // always do at least this much
228         config.flags4 |= (go.mfoptim & MFtime) ? CFG4speed : CFG4space;
229     }
230     else
231     {
232         config.flags4 &= ~CFG4optimized;
233     }
234     return 1;                   // recognized
235 
236 badflag:
237     return 0;
238 }
239 
240 debug (DEBUG_TREES)
241 {
242 void dbg_optprint(char *title)
243 {
244     block *b;
245     for (b = startblock; b; b = b.Bnext)
246         if (b.Belem)
247         {
248             printf("%s\n",title);
249             elem_print(b.Belem);
250         }
251 }
252 }
253 
254 /****************************
255  * Optimize function.
256  */
257 
258 void optfunc()
259 {
260 version (HTOD)
261 {
262 }
263 else
264 {
265     if (debugc) printf("optfunc()\n");
266     dbg_optprint("optfunc\n");
267 
268     debug if (debugb)
269     {
270         printf("................Before optimization.........\n");
271         WRfunc();
272     }
273 
274     if (localgot)
275     {   // Initialize with:
276         //      localgot = OPgot;
277         elem *e = el_long(TYnptr, 0);
278         e.Eoper = OPgot;
279         e = el_bin(OPeq, TYnptr, el_var(localgot), e);
280         startblock.Belem = el_combine(e, startblock.Belem);
281     }
282 
283     // Each pass through the loop can reduce only one level of comma expression.
284     // The infinite loop check needs to take this into account.
285     // Add 100 just to give optimizer more rope to try to converge.
286     int iterationLimit = 0;
287     for (block* b = startblock; b; b = b.Bnext)
288     {
289         if (!b.Belem)
290             continue;
291         int d = el_countCommas(b.Belem) + 100;
292         if (d > iterationLimit)
293             iterationLimit = d;
294     }
295 
296     // Some functions can take enormous amounts of time to optimize.
297     // We try to put a lid on it.
298     clock_t starttime = os_clock();
299     int iter = 0;           // iteration count
300     do
301     {
302         //printf("iter = %d\n", iter);
303         if (++iter > 200)
304         {   assert(iter < iterationLimit);      // infinite loop check
305             break;
306         }
307 version (MARS)
308         util_progress();
309 else
310         file_progress();
311 
312         //printf("optelem\n");
313         /* canonicalize the trees        */
314         foreach (b; BlockRange(startblock))
315             if (b.Belem)
316             {
317                 debug if (debuge)
318                 {
319                     printf("before\n");
320                     elem_print(b.Belem);
321                     //el_check(b.Belem);
322                 }
323 
324                 b.Belem = doptelem(b.Belem,bc_goal[b.BC] | GOALagain);
325 
326                 debug if (0 && debugf)
327                 {
328                     printf("after\n");
329                     elem_print(b.Belem);
330                 }
331             }
332         //printf("blockopt\n");
333         if (go.mfoptim & MFdc)
334             blockopt(0);                // do block optimization
335         out_regcand(&globsym);          // recompute register candidates
336         go.changes = 0;                 // no changes yet
337         sliceStructs(&globsym, startblock);
338         if (go.mfoptim & MFcnp)
339             constprop();                /* make relationals unsigned     */
340         if (go.mfoptim & (MFli | MFliv))
341             loopopt();                  /* remove loop invariants and    */
342                                         /* induction vars                */
343                                         /* do loop rotation              */
344         else
345             foreach (b; BlockRange(startblock))
346                 b.Bweight = 1;
347         dbg_optprint("boolopt\n");
348 
349         if (go.mfoptim & MFcnp)
350             boolopt();                  // optimize boolean values
351         if (go.changes && go.mfoptim & MFloop && (os_clock() - starttime) < 30 * CLOCKS_PER_SEC)
352             continue;
353 
354         if (go.mfoptim & MFcnp)
355             constprop();                /* constant propagation          */
356         if (go.mfoptim & MFcp)
357             copyprop();                 /* do copy propagation           */
358 
359         /* Floating point constants and string literals need to be
360          * replaced with loads from variables in read-only data.
361          * This can result in localgot getting needed.
362          */
363         Symbol *localgotsave = localgot;
364         for (block* b = startblock; b; b = b.Bnext)
365         {
366             if (b.Belem)
367             {
368                 b.Belem = doptelem(b.Belem,bc_goal[b.BC] | GOALstruct);
369                 if (b.Belem)
370                     b.Belem = el_convert(b.Belem);
371             }
372         }
373         if (localgot != localgotsave)
374         {   /* Looks like we did need localgot, initialize with:
375              *  localgot = OPgot;
376              */
377             elem *e = el_long(TYnptr, 0);
378             e.Eoper = OPgot;
379             e = el_bin(OPeq, TYnptr, el_var(localgot), e);
380             startblock.Belem = el_combine(e, startblock.Belem);
381         }
382 
383         /* localize() is after localgot, otherwise we wind up with
384          * more than one OPgot in a function, which mucks up OSX
385          * code generation which assumes at most one (localgotoffset).
386          */
387         if (go.mfoptim & MFlocal)
388             localize();                 // improve expression locality
389         if (go.mfoptim & MFda)
390             rmdeadass();                /* remove dead assignments       */
391 
392         if (debugc) printf("changes = %d\n", go.changes);
393         if (!(go.changes && go.mfoptim & MFloop && (os_clock() - starttime) < 30 * CLOCKS_PER_SEC))
394             break;
395     } while (1);
396     if (debugc) printf("%d iterations\n",iter);
397 
398     if (go.mfoptim & MFdc)
399         blockopt(1);                    // do block optimization
400 
401     for (block* b = startblock; b; b = b.Bnext)
402     {
403         if (b.Belem)
404             postoptelem(b.Belem);
405     }
406     if (go.mfoptim & MFvbe)
407         verybusyexp();              /* very busy expressions         */
408     if (go.mfoptim & MFcse)
409         builddags();                /* common subexpressions         */
410     if (go.mfoptim & MFdv)
411         deadvar();                  /* eliminate dead variables      */
412 
413     debug if (debugb)
414     {
415         printf(".............After optimization...........\n");
416         WRfunc();
417     }
418 
419     // Prepare for code generator
420     for (block* b = startblock; b; b = b.Bnext)
421     {
422         block_optimizer_free(b);
423     }
424 }
425 }
426 
427 }