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 }