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