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 }