1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 2016-2020 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/gsroa.c, backend/gsroa.d) 9 */ 10 11 module dmd.backend.gsroa; 12 13 version (SCPP) 14 version = COMPILE; 15 version (MARS) 16 version = COMPILE; 17 18 version (COMPILE) 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.code_x86; 29 import dmd.backend.oper; 30 import dmd.backend.global; 31 import dmd.backend.el; 32 import dmd.backend.ty; 33 import dmd.backend.type; 34 35 import dmd.backend.dlist; 36 import dmd.backend.dvec; 37 38 extern (C++): 39 40 nothrow: 41 42 int REGSIZE(); 43 44 alias SLICESIZE = REGSIZE; // slices are all register-sized 45 enum MAXSLICES = 2; // max # of pieces we can slice an aggregate into 46 47 /* This 'slices' a two register wide aggregate into two separate register-sized variables, 48 * enabling much better enregistering. 49 * SROA (Scalar Replacement Of Aggregates) is the common term for this. 50 */ 51 52 struct SymInfo 53 { 54 bool canSlice; 55 bool accessSlice; // if Symbol was accessed as a slice 56 tym_t[MAXSLICES] ty; // type of each slice 57 SYMIDX si0; // index of first slice, the rest follow sequentially 58 } 59 60 /******************************** 61 * Gather information about slice-able variables by scanning e. 62 * Params: 63 * symtab = symbol table 64 * e = expression to scan 65 * sia = where to put gathered information 66 */ 67 extern (D) private void sliceStructs_Gather(const symtab_t* symtab, SymInfo[] sia, const(elem)* e) 68 { 69 while (1) 70 { 71 switch (e.Eoper) 72 { 73 case OPvar: 74 { 75 const si = e.EV.Vsym.Ssymnum; 76 if (si >= 0 && sia[si].canSlice) 77 { 78 assert(si < symtab.top); 79 const n = nthSlice(e); 80 const sz = getSize(e); 81 if (sz == 2 * SLICESIZE && !tyfv(e.Ety) && 82 tybasic(e.Ety) != TYldouble && tybasic(e.Ety) != TYildouble) 83 { 84 // Rewritten as OPpair later 85 } 86 else if (n != NOTSLICE) 87 { 88 if (!sia[si].accessSlice) 89 { 90 /* [1] default as pointer type 91 */ 92 foreach (ref ty; sia[si].ty) 93 ty = TYnptr; 94 95 const s = e.EV.Vsym; 96 const t = s.Stype; 97 if (tybasic(t.Tty) == TYstruct) 98 { 99 if (const targ1 = t.Ttag.Sstruct.Sarg1type) 100 if (const targ2 = t.Ttag.Sstruct.Sarg2type) 101 { 102 sia[si].ty[0] = targ1.Tty; 103 sia[si].ty[1] = targ2.Tty; 104 } 105 } 106 } 107 sia[si].accessSlice = true; 108 if (sz == SLICESIZE) 109 sia[si].ty[n] = tybasic(e.Ety); 110 } 111 else 112 { 113 sia[si].canSlice = false; 114 } 115 } 116 return; 117 } 118 119 default: 120 if (OTassign(e.Eoper)) 121 { 122 if (OTbinary(e.Eoper)) 123 sliceStructs_Gather(symtab, sia, e.EV.E2); 124 125 // Assignment to a whole var will disallow SROA 126 if (e.EV.E1.Eoper == OPvar) 127 { 128 const e1 = e.EV.E1; 129 const si = e1.EV.Vsym.Ssymnum; 130 if (si >= 0 && sia[si].canSlice) 131 { 132 assert(si < symtab.top); 133 if (nthSlice(e1) == NOTSLICE) 134 { 135 sia[si].canSlice = false; 136 } 137 // Disable SROA on OSX32 (because XMM registers?) 138 // https://issues.dlang.org/show_bug.cgi?id=15206 139 // https://github.com/dlang/dmd/pull/8034 140 else if (!(config.exe & EX_OSX)) 141 { 142 sliceStructs_Gather(symtab, sia, e.EV.E1); 143 } 144 } 145 return; 146 } 147 e = e.EV.E1; 148 break; 149 } 150 if (OTunary(e.Eoper)) 151 { 152 e = e.EV.E1; 153 break; 154 } 155 if (OTbinary(e.Eoper)) 156 { 157 sliceStructs_Gather(symtab, sia, e.EV.E2); 158 e = e.EV.E1; 159 break; 160 } 161 return; 162 } 163 } 164 } 165 166 /*********************************** 167 * Rewrite expression tree e based on info in sia[]. 168 * Params: 169 * symtab = symbol table 170 * sia = slicing info 171 * e = expression tree to rewrite in place 172 */ 173 extern (D) private void sliceStructs_Replace(symtab_t* symtab, const SymInfo[] sia, elem *e) 174 { 175 while (1) 176 { 177 switch (e.Eoper) 178 { 179 case OPvar: 180 { 181 Symbol *s = e.EV.Vsym; 182 const si = s.Ssymnum; 183 //printf("e: %d %d\n", si, sia[si].canSlice); 184 //elem_print(e); 185 if (si >= 0 && sia[si].canSlice) 186 { 187 const n = nthSlice(e); 188 if (getSize(e) == 2 * SLICESIZE) 189 { 190 // Rewrite e as (si0 OPpair si0+1) 191 elem *e1 = el_calloc(); 192 el_copy(e1, e); 193 e1.Ety = sia[si].ty[0]; 194 195 elem *e2 = el_calloc(); 196 el_copy(e2, e); 197 Symbol *s1 = symtab.tab[sia[si].si0 + 1]; // +1 for second slice 198 e2.Ety = sia[si].ty[1]; 199 e2.EV.Vsym = s1; 200 e2.EV.Voffset = 0; 201 202 e.Eoper = OPpair; 203 e.EV.E1 = e1; 204 e.EV.E2 = e2; 205 206 if (tycomplex(e.Ety)) 207 { 208 /* Ensure complex OPpair operands are floating point types 209 * because [1] may have defaulted them to a pointer type. 210 * https://issues.dlang.org/show_bug.cgi?id=18936 211 */ 212 tym_t tyop; 213 switch (tybasic(e.Ety)) 214 { 215 case TYcfloat: tyop = TYfloat; break; 216 case TYcdouble: tyop = TYdouble; break; 217 case TYcldouble: tyop = TYldouble; break; 218 default: 219 assert(0); 220 } 221 if (!tyfloating(e1.Ety)) 222 e1.Ety = tyop; 223 if (!tyfloating(e2.Ety)) 224 e2.Ety = tyop; 225 } 226 } 227 else if (n == 0) // the first slice of the symbol is the same as the original 228 { 229 } 230 else // the nth slice 231 { 232 e.EV.Vsym = symtab.tab[sia[si].si0 + n]; 233 e.EV.Voffset -= n * SLICESIZE; 234 //printf("replaced with:\n"); 235 //elem_print(e); 236 } 237 } 238 return; 239 } 240 241 default: 242 if (OTunary(e.Eoper)) 243 { 244 e = e.EV.E1; 245 break; 246 } 247 if (OTbinary(e.Eoper)) 248 { 249 sliceStructs_Replace(symtab, sia, e.EV.E2); 250 e = e.EV.E1; 251 break; 252 } 253 return; 254 } 255 } 256 } 257 258 void sliceStructs(symtab_t* symtab, block* startblock) 259 { 260 if (debugc) printf("sliceStructs() %s\n", funcsym_p.Sident.ptr); 261 const sia_length = symtab.top; 262 /* 3 is because it is used for two arrays, sia[] and sia2[]. 263 * sia2[] can grow to twice the size of sia[], as symbols can get split into two. 264 */ 265 debug 266 enum tmp_length = 3; 267 else 268 enum tmp_length = 6; 269 SymInfo[tmp_length] tmp = void; 270 SymInfo* sip; 271 if (sia_length <= tmp.length / 3) 272 sip = tmp.ptr; 273 else 274 { 275 sip = cast(SymInfo *)malloc(3 * sia_length * SymInfo.sizeof); 276 assert(sip); 277 } 278 SymInfo[] sia = sip[0 .. sia_length]; 279 SymInfo[] sia2 = sip[sia_length .. sia_length * 3]; 280 281 if (0) foreach (si; 0 .. symtab.top) 282 { 283 Symbol *s = symtab.tab[si]; 284 printf("[%d]: %p %d %s\n", si, s, cast(int)type_size(s.Stype), s.Sident.ptr); 285 } 286 287 bool anySlice = false; 288 foreach (si; 0 .. symtab.top) 289 { 290 Symbol *s = symtab.tab[si]; 291 //printf("slice1: %s\n", s.Sident.ptr); 292 293 if (!(s.Sflags & SFLunambig)) // if somebody took the address of s 294 { 295 sia[si].canSlice = false; 296 continue; 297 } 298 299 const sz = type_size(s.Stype); 300 if (sz != 2 * SLICESIZE || 301 tyfv(s.Stype.Tty) || tybasic(s.Stype.Tty) == TYhptr) // because there is no TYseg 302 { 303 sia[si].canSlice = false; 304 continue; 305 } 306 307 switch (s.Sclass) 308 { 309 case SCfastpar: 310 case SCregister: 311 case SCauto: 312 case SCshadowreg: 313 case SCparameter: 314 anySlice = true; 315 sia[si].canSlice = true; 316 sia[si].accessSlice = false; 317 // We can't slice whole XMM registers 318 if (tyxmmreg(s.Stype.Tty) && 319 isXMMreg(s.Spreg) && s.Spreg2 == NOREG) 320 { 321 sia[si].canSlice = false; 322 } 323 break; 324 325 case SCstack: 326 case SCpseudo: 327 case SCstatic: 328 case SCbprel: 329 sia[si].canSlice = false; 330 break; 331 332 default: 333 symbol_print(s); 334 assert(0); 335 } 336 } 337 338 if (!anySlice) 339 goto Ldone; 340 341 foreach (b; BlockRange(startblock)) 342 { 343 if (b.BC == BCasm) 344 goto Ldone; 345 if (b.Belem) 346 sliceStructs_Gather(symtab, sia, b.Belem); 347 } 348 349 { // scope needed because of goto skipping declarations 350 bool any = false; 351 int n = 0; // the number of symbols added 352 foreach (si; 0 .. sia_length) 353 { 354 sia2[si + n].canSlice = false; 355 if (sia[si].canSlice) 356 { 357 // If never did access it as a slice, don't slice 358 if (!sia[si].accessSlice) 359 { 360 sia[si].canSlice = false; 361 continue; 362 } 363 364 /* Split slice-able symbol sold into two symbols, 365 * (sold,snew) in adjacent slots in the symbol table. 366 */ 367 Symbol *sold = symtab.tab[si + n]; 368 369 const idlen = 2 + strlen(sold.Sident.ptr) + 2; 370 char *id = cast(char *)malloc(idlen + 1); 371 assert(id); 372 sprintf(id, "__%s_%d", sold.Sident.ptr, SLICESIZE); 373 if (debugc) printf("creating slice symbol %s\n", id); 374 Symbol *snew = symbol_calloc(id, cast(uint)idlen); 375 free(id); 376 snew.Sclass = sold.Sclass; 377 snew.Sfl = sold.Sfl; 378 snew.Sflags = sold.Sflags; 379 if (snew.Sclass == SCfastpar || snew.Sclass == SCshadowreg) 380 { 381 snew.Spreg = sold.Spreg2; 382 snew.Spreg2 = NOREG; 383 sold.Spreg2 = NOREG; 384 } 385 type_free(sold.Stype); 386 sold.Stype = type_fake(sia[si].ty[0]); 387 sold.Stype.Tcount++; 388 snew.Stype = type_fake(sia[si].ty[1]); 389 snew.Stype.Tcount++; 390 391 // insert snew into symtab.tab[si + n + 1] 392 symbol_insert(symtab, snew, si + n + 1); 393 394 sia2[si + n].canSlice = true; 395 sia2[si + n].si0 = si + n; 396 sia2[si + n].ty[] = sia[si].ty[]; 397 ++n; 398 any = true; 399 } 400 } 401 if (!any) 402 goto Ldone; 403 } 404 405 foreach (si; 0 .. symtab.top) 406 { 407 Symbol *s = symtab.tab[si]; 408 assert(s.Ssymnum == si); 409 } 410 411 foreach (b; BlockRange(startblock)) 412 { 413 if (b.Belem) 414 sliceStructs_Replace(symtab, sia2, b.Belem); 415 } 416 417 Ldone: 418 if (sip != tmp.ptr) 419 free(sip); 420 } 421 422 423 /************************************* 424 * Determine if `e` is a slice. 425 * Params: 426 * e = elem that may be a slice 427 * Returns: 428 * slice number if it is, NOTSLICE if not 429 */ 430 enum NOTSLICE = -1; 431 int nthSlice(const(elem)* e) 432 { 433 const sz = tysize(e.Ety); // not getSize(e) because type_fake(TYstruct) doesn't work 434 if (sz == -1) 435 return NOTSLICE; 436 const sliceSize = SLICESIZE; 437 438 /* See if e fits in a slice 439 */ 440 const lwr = e.EV.Voffset; 441 const upr = lwr + sz; 442 if (0 <= lwr && upr <= sliceSize) 443 return 0; 444 if (sliceSize < lwr && upr <= sliceSize * 2) 445 return 1; 446 447 return NOTSLICE; 448 } 449 450 /****************************************** 451 * Get size of an elem e. 452 */ 453 private int getSize(const(elem)* e) 454 { 455 int sz = tysize(e.Ety); 456 if (sz == -1 && e.ET && (tybasic(e.Ety) == TYstruct || tybasic(e.Ety) == TYarray)) 457 sz = cast(int)type_size(e.ET); 458 return sz; 459 } 460 461 }