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