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