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 }