1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1993-1998 by Symantec
6  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/glocal.d, backend/glocal.d)
10  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/glocal.d
11  */
12 
13 module dmd.backend.glocal;
14 
15 version (SCPP)
16     version = COMPILE;
17 version (MARS)
18     version = COMPILE;
19 
20 version (COMPILE)
21 {
22 
23 import core.stdc.stdio;
24 import core.stdc.stdlib;
25 import core.stdc..string;
26 import core.stdc.time;
27 
28 import dmd.backend.cc;
29 import dmd.backend.cdef;
30 import dmd.backend.code_x86;
31 import dmd.backend.oper;
32 import dmd.backend.global;
33 import dmd.backend.goh;
34 import dmd.backend.el;
35 import dmd.backend.ty;
36 import dmd.backend.type;
37 
38 import dmd.backend.barray;
39 import dmd.backend.dlist;
40 import dmd.backend.dvec;
41 
42 extern (C++):
43 
44 nothrow:
45 
46 int REGSIZE();
47 
48 
49 enum
50 {
51     LFvolatile     = 1,       // contains volatile or shared refs or defs
52     LFambigref     = 2,       // references ambiguous data
53     LFambigdef     = 4,       // defines ambiguous data
54     LFsymref       = 8,       // reference to symbol s
55     LFsymdef       = 0x10,    // definition of symbol s
56     LFunambigref   = 0x20,    // references unambiguous data other than s
57     LFunambigdef   = 0x40,    // defines unambiguous data other than s
58     LFinp          = 0x80,    // input from I/O port
59     LFoutp         = 0x100,   // output to I/O port
60     LFfloat        = 0x200,   // sets float flags and/or depends on
61                               // floating point settings
62 }
63 
64 struct loc_t
65 {
66     elem *e;
67     int flags;  // LFxxxxx
68 }
69 
70 
71 ///////////////////////////////
72 // This optimization attempts to replace sequences like:
73 //      x = func();
74 //      y = 3;
75 //      z = x + 5;
76 // with:
77 //      y = 3;
78 //      z = (x = func()) + 5;
79 // In other words, we attempt to localize expressions by moving them
80 // as near as we can to where they are used. This should minimize
81 // temporary generation and register usage.
82 
83 void localize()
84 {
85     if (debugc) printf("localize()\n");
86 
87     __gshared Barray!(loc_t) loctab;       // cache the array so it usually won't need reallocating
88 
89     // Table should not get any larger than the symbol table
90     loctab.setLength(globsym.symmax);
91 
92     foreach (b; BlockRange(startblock))       // for each block
93     {
94         loctab.setLength(0);                     // start over for each block
95         if (b.Belem &&
96             /* Overly broad way to account for the case:
97              * try
98              * { i++;
99              *   foo(); // throws exception
100              *   i++;   // shouldn't combine previous i++ with this one
101              * }
102              */
103             !b.Btry)
104         {
105             local_exp(loctab,b.Belem,0);
106         }
107     }
108 }
109 
110 //////////////////////////////////////
111 // Input:
112 //      goal    !=0 if we want the result of the expression
113 //
114 
115 private void local_exp(ref Barray!loc_t lt, elem *e, int goal)
116 {
117     Symbol *s;
118     elem *e1;
119     OPER op1;
120 
121 Loop:
122     elem_debug(e);
123     const op = e.Eoper;
124     switch (op)
125     {
126         case OPcomma:
127             local_exp(lt,e.EV.E1,0);
128             e = e.EV.E2;
129             goto Loop;
130 
131         case OPandand:
132         case OPoror:
133             local_exp(lt,e.EV.E1,1);
134             lt.setLength(0);         // we can do better than this, fix later
135             break;
136 
137         case OPcolon:
138         case OPcolon2:
139             lt.setLength(0);         // we can do better than this, fix later
140             break;
141 
142         case OPinfo:
143             if (e.EV.E1.Eoper == OPmark)
144             {   lt.setLength(0);
145                 e = e.EV.E2;
146                 goto Loop;
147             }
148             goto case_bin;
149 
150         case OPdtor:
151         case OPctor:
152         case OPdctor:
153             lt.setLength(0);         // don't move expressions across ctor/dtor
154             break;              // boundaries, it would goof up EH cleanup
155 
156         case OPddtor:
157             lt.setLength(0);         // don't move expressions across ctor/dtor
158                                 // boundaries, it would goof up EH cleanup
159             local_exp(lt,e.EV.E1,0);
160             lt.setLength(0);
161             break;
162 
163         case OPeq:
164         case OPstreq:
165             e1 = e.EV.E1;
166             local_exp(lt,e.EV.E2,1);
167             if (e1.Eoper == OPvar)
168             {   s = e1.EV.Vsym;
169                 if (s.Sflags & SFLunambig)
170                 {   local_symdef(lt, s);
171                     if (!goal)
172                         local_ins(lt, e);
173                 }
174                 else
175                     local_ambigdef(lt);
176             }
177             else
178             {
179                 assert(!OTleaf(e1.Eoper));
180                 local_exp(lt,e1.EV.E1,1);
181                 if (OTbinary(e1.Eoper))
182                     local_exp(lt,e1.EV.E2,1);
183                 local_ambigdef(lt);
184             }
185             break;
186 
187         case OPpostinc:
188         case OPpostdec:
189         case OPaddass:
190         case OPminass:
191         case OPmulass:
192         case OPdivass:
193         case OPmodass:
194         case OPashrass:
195         case OPshrass:
196         case OPshlass:
197         case OPandass:
198         case OPxorass:
199         case OPorass:
200         case OPcmpxchg:
201             if (ERTOL(e))
202             {   local_exp(lt,e.EV.E2,1);
203         case OPnegass:
204                 e1 = e.EV.E1;
205                 op1 = e1.Eoper;
206                 if (op1 != OPvar)
207                 {
208                     local_exp(lt,e1.EV.E1,1);
209                     if (OTbinary(op1))
210                         local_exp(lt,e1.EV.E2,1);
211                 }
212                 else if (lt.length && (op == OPaddass || op == OPxorass))
213                 {
214                     s = e1.EV.Vsym;
215                     for (uint u = 0; u < lt.length; u++)
216                     {   elem *em;
217 
218                         em = lt[u].e;
219                         if (em.Eoper == op &&
220                             em.EV.E1.EV.Vsym == s &&
221                             tysize(em.Ety) == tysize(e1.Ety) &&
222                             !tyfloating(em.Ety) &&
223                             em.EV.E1.EV.Voffset == e1.EV.Voffset &&
224                             !el_sideeffect(em.EV.E2)
225                            )
226                         {   // Change (x += a),(x += b) to
227                             // (x + a),(x += a + b)
228                             go.changes++;
229                             e.EV.E2 = el_bin(opeqtoop(op),e.EV.E2.Ety,em.EV.E2,e.EV.E2);
230                             em.Eoper = cast(ubyte)opeqtoop(op);
231                             em.EV.E2 = el_copytree(em.EV.E2);
232                             local_rem(lt, u);
233 
234                             debug if (debugc)
235                             {   printf("Combined equation ");
236                                 WReqn(e);
237                                 printf(";\n");
238                                 e = doptelem(e,GOALvalue);
239                             }
240 
241                             break;
242                         }
243                     }
244                 }
245             }
246             else
247             {
248                 e1 = e.EV.E1;
249                 op1 = e1.Eoper;
250                 if (op1 != OPvar)
251                 {
252                     local_exp(lt,e1.EV.E1,1);
253                     if (OTbinary(op1))
254                         local_exp(lt,e1.EV.E2,1);
255                 }
256                 if (lt.length)
257                 {   if (op1 == OPvar &&
258                         ((s = e1.EV.Vsym).Sflags & SFLunambig))
259                         local_symref(lt, s);
260                     else
261                         local_ambigref(lt);
262                 }
263                 local_exp(lt,e.EV.E2,1);
264             }
265             if (op1 == OPvar &&
266                 ((s = e1.EV.Vsym).Sflags & SFLunambig))
267             {   local_symref(lt, s);
268                 local_symdef(lt, s);
269                 if (op == OPaddass || op == OPxorass)
270                     local_ins(lt, e);
271             }
272             else if (lt.length)
273             {
274                 local_remove(lt, LFambigdef | LFambigref);
275             }
276             break;
277 
278         case OPstrlen:
279         case OPind:
280             local_exp(lt,e.EV.E1,1);
281             local_ambigref(lt);
282             break;
283 
284         case OPstrcmp:
285         case OPmemcmp:
286         case OPbt:
287             local_exp(lt,e.EV.E1,1);
288             local_exp(lt,e.EV.E2,1);
289             local_ambigref(lt);
290             break;
291 
292         case OPstrcpy:
293         case OPmemcpy:
294         case OPstrcat:
295         case OPcall:
296         case OPcallns:
297             local_exp(lt,e.EV.E2,1);
298             local_exp(lt,e.EV.E1,1);
299             goto Lrd;
300 
301         case OPstrctor:
302         case OPucall:
303         case OPucallns:
304             local_exp(lt,e.EV.E1,1);
305             goto Lrd;
306 
307         case OPbtc:
308         case OPbtr:
309         case OPbts:
310             local_exp(lt,e.EV.E1,1);
311             local_exp(lt,e.EV.E2,1);
312             goto Lrd;
313 
314         case OPasm:
315         Lrd:
316             local_remove(lt, LFfloat | LFambigref | LFambigdef);
317             break;
318 
319         case OPmemset:
320             local_exp(lt,e.EV.E2,1);
321             if (e.EV.E1.Eoper == OPvar)
322             {
323                 /* Don't want to rearrange (p = get(); p memset 0;)
324                  * as elemxxx() will rearrange it back.
325                  */
326                 s = e.EV.E1.EV.Vsym;
327                 if (s.Sflags & SFLunambig)
328                     local_symref(lt, s);
329                 else
330                     local_ambigref(lt);     // ambiguous reference
331             }
332             else
333                 local_exp(lt,e.EV.E1,1);
334             local_ambigdef(lt);
335             break;
336 
337         case OPvar:
338             s = e.EV.Vsym;
339             if (lt.length)
340             {
341                 // If potential candidate for replacement
342                 if (s.Sflags & SFLunambig)
343                 {
344                     foreach (const u; 0 .. lt.length)
345                     {
346                         auto em = lt[u].e;
347                         if (em.EV.E1.EV.Vsym == s &&
348                             (em.Eoper == OPeq || em.Eoper == OPstreq))
349                         {
350                             if (tysize(em.Ety) == tysize(e.Ety) &&
351                                 em.EV.E1.EV.Voffset == e.EV.Voffset &&
352                                 ((tyfloating(em.Ety) != 0) == (tyfloating(e.Ety) != 0) ||
353                                  /** Hack to fix https://issues.dlang.org/show_bug.cgi?id=10226
354                                   * Recognize assignments of float vectors to void16, as used by
355                                   * core.simd intrinsics. The backend type for void16 is Tschar16!
356                                   */
357                                  (tyvector(em.Ety) != 0) == (tyvector(e.Ety) != 0) && tybasic(e.Ety) == TYschar16) &&
358                                 /* Changing the Ety to a OPvecfill node means we're potentially generating
359                                  * wrong code.
360                                  * Ref: https://issues.dlang.org/show_bug.cgi?id=18034
361                                  */
362                                 (em.EV.E2.Eoper != OPvecfill || tybasic(e.Ety) == tybasic(em.Ety)) &&
363                                 !local_preserveAssignmentTo(em.EV.E1.Ety))
364                             {
365 
366                                 debug if (debugc)
367                                 {   printf("Moved equation ");
368                                     WReqn(em);
369                                     printf(";\n");
370                                 }
371 
372                                 go.changes++;
373                                 em.Ety = e.Ety;
374                                 el_copy(e,em);
375                                 em.EV.E1 = em.EV.E2 = null;
376                                 em.Eoper = OPconst;
377                             }
378                             local_rem(lt, u);
379                             break;
380                         }
381                     }
382                     local_symref(lt, s);
383                 }
384                 else
385                     local_ambigref(lt);     // ambiguous reference
386             }
387             break;
388 
389         case OPremquo:
390             if (e.EV.E1.Eoper != OPvar)
391                 goto case_bin;
392             s = e.EV.E1.EV.Vsym;
393             if (lt.length)
394             {
395                 if (s.Sflags & SFLunambig)
396                     local_symref(lt, s);
397                 else
398                     local_ambigref(lt);     // ambiguous reference
399             }
400             goal = 1;
401             e = e.EV.E2;
402             goto Loop;
403 
404         default:
405             if (OTcommut(e.Eoper))
406             {   // Since commutative operators may get their leaves
407                 // swapped, we eliminate any that may be affected by that.
408 
409                 for (uint u = 0; u < lt.length;)
410                 {
411                     const f = lt[u].flags;
412                     elem* eu = lt[u].e;
413                     s = eu.EV.E1.EV.Vsym;
414                     const f1 = local_getflags(e.EV.E1,s);
415                     const f2 = local_getflags(e.EV.E2,s);
416                     if (f1 & f2 & LFsymref ||   // if both reference or
417                         (f1 | f2) & LFsymdef || // either define
418                         f & LFambigref && (f1 | f2) & LFambigdef ||
419                         f & LFambigdef && (f1 | f2) & (LFambigref | LFambigdef)
420                        )
421                         local_rem(lt, u);
422                     else if (f & LFunambigdef && local_chkrem(e,eu.EV.E2))
423                         local_rem(lt, u);
424                     else
425                         u++;
426                 }
427             }
428             if (OTunary(e.Eoper))
429             {   goal = 1;
430                 e = e.EV.E1;
431                 goto Loop;
432             }
433         case_bin:
434             if (OTbinary(e.Eoper))
435             {   local_exp(lt,e.EV.E1,1);
436                 goal = 1;
437                 e = e.EV.E2;
438                 goto Loop;
439             }
440             break;
441     }   // end of switch (e.Eoper)
442 }
443 
444 ///////////////////////////////////
445 // Examine expression tree eu to see if it defines any variables
446 // that e refs or defs.
447 // Note that e is a binary operator.
448 // Returns:
449 //      true if it does
450 
451 private bool local_chkrem(elem *e,elem *eu)
452 {
453     while (1)
454     {
455         elem_debug(eu);
456         const op = eu.Eoper;
457         if (OTassign(op) && eu.EV.E1.Eoper == OPvar)
458         {
459             auto s = eu.EV.E1.EV.Vsym;
460             const f1 = local_getflags(e.EV.E1,s);
461             const f2 = local_getflags(e.EV.E2,s);
462             if ((f1 | f2) & (LFsymref | LFsymdef))      // if either reference or define
463                 return true;
464         }
465         if (OTbinary(op))
466         {
467             if (local_chkrem(e,eu.EV.E2))
468                 return true;
469         }
470         else if (!OTunary(op))
471             break;                      // leaf node
472         eu = eu.EV.E1;
473     }
474     return false;
475 }
476 
477 //////////////////////////////////////
478 // Add entry e to lt[]
479 
480 private void local_ins(ref Barray!loc_t lt, elem *e)
481 {
482     elem_debug(e);
483     if (e.EV.E1.Eoper == OPvar)
484     {
485         auto s = e.EV.E1.EV.Vsym;
486         symbol_debug(s);
487         if (s.Sflags & SFLunambig)     // if can only be referenced directly
488         {
489             const flags = local_getflags(e.EV.E2,null);
490             if (!(flags & (LFvolatile | LFinp | LFoutp)) &&
491                 !(e.EV.E1.Ety & (mTYvolatile | mTYshared)))
492             {
493                 // Add e to the candidate array
494                 //printf("local_ins('%s'), loctop = %d\n",s.Sident.ptr,lt.length);
495                 lt.push(loc_t(e, flags));
496             }
497         }
498     }
499 }
500 
501 //////////////////////////////////////
502 // Remove entry i from lt[], and then compress the table.
503 //
504 
505 private void local_rem(ref Barray!loc_t lt, size_t u)
506 {
507     //printf("local_rem(%u)\n",u);
508     lt.remove(u);
509 }
510 
511 //////////////////////////////////////
512 // Analyze and gather LFxxxx flags about expression e and symbol s.
513 
514 private int local_getflags(elem *e,Symbol *s)
515 {
516     elem_debug(e);
517     if (s)
518         symbol_debug(s);
519     int flags = 0;
520     while (1)
521     {
522         if (e.Ety & (mTYvolatile | mTYshared))
523             flags |= LFvolatile;
524         switch (e.Eoper)
525         {
526             case OPeq:
527             case OPstreq:
528                 if (e.EV.E1.Eoper == OPvar)
529                 {
530                     auto s1 = e.EV.E1.EV.Vsym;
531                     if (s1.Sflags & SFLunambig)
532                         flags |= (s1 == s) ? LFsymdef : LFunambigdef;
533                     else
534                         flags |= LFambigdef;
535                 }
536                 else
537                     flags |= LFambigdef;
538                 goto L1;
539 
540             case OPpostinc:
541             case OPpostdec:
542             case OPaddass:
543             case OPminass:
544             case OPmulass:
545             case OPdivass:
546             case OPmodass:
547             case OPashrass:
548             case OPshrass:
549             case OPshlass:
550             case OPandass:
551             case OPxorass:
552             case OPorass:
553             case OPcmpxchg:
554                 if (e.EV.E1.Eoper == OPvar)
555                 {
556                     auto s1 = e.EV.E1.EV.Vsym;
557                     if (s1.Sflags & SFLunambig)
558                         flags |= (s1 == s) ? LFsymdef | LFsymref
559                                            : LFunambigdef | LFunambigref;
560                     else
561                         flags |= LFambigdef | LFambigref;
562                 }
563                 else
564                     flags |= LFambigdef | LFambigref;
565             L1:
566                 flags |= local_getflags(e.EV.E2,s);
567                 e = e.EV.E1;
568                 break;
569 
570             case OPucall:
571             case OPucallns:
572             case OPcall:
573             case OPcallns:
574             case OPstrcat:
575             case OPstrcpy:
576             case OPmemcpy:
577             case OPbtc:
578             case OPbtr:
579             case OPbts:
580             case OPstrctor:
581                 flags |= LFambigref | LFambigdef;
582                 break;
583 
584             case OPmemset:
585                 flags |= LFambigdef;
586                 break;
587 
588             case OPvar:
589                 if (e.EV.Vsym == s)
590                     flags |= LFsymref;
591                 else if (!(e.EV.Vsym.Sflags & SFLunambig))
592                     flags |= LFambigref;
593                 break;
594 
595             case OPind:
596             case OPstrlen:
597             case OPstrcmp:
598             case OPmemcmp:
599             case OPbt:
600                 flags |= LFambigref;
601                 break;
602 
603             case OPinp:
604                 flags |= LFinp;
605                 break;
606 
607             case OPoutp:
608                 flags |= LFoutp;
609                 break;
610 
611             default:
612                 break;
613         }
614         if (OTunary(e.Eoper))
615         {
616             if (tyfloating(e.Ety))
617                 flags |= LFfloat;
618             e = e.EV.E1;
619         }
620         else if (OTbinary(e.Eoper))
621         {
622             if (tyfloating(e.Ety))
623                 flags |= LFfloat;
624             flags |= local_getflags(e.EV.E2,s);
625             e = e.EV.E1;
626         }
627         else
628             break;
629     }
630     return flags;
631 }
632 
633 //////////////////////////////////////
634 // Remove all entries with flags set.
635 //
636 
637 private void local_remove(ref Barray!loc_t lt, int flags)
638 {
639     for (uint u = 0; u < lt.length;)
640     {
641         if (lt[u].flags & flags)
642             local_rem(lt, u);
643         else
644             ++u;
645     }
646 }
647 
648 //////////////////////////////////////
649 // Ambiguous reference. Remove all with ambiguous defs
650 //
651 
652 private void local_ambigref(ref Barray!loc_t lt)
653 {
654     local_remove(lt, LFambigdef);
655 }
656 
657 //////////////////////////////////////
658 // Ambiguous definition. Remove all with ambiguous refs.
659 //
660 
661 private void local_ambigdef(ref Barray!loc_t lt)
662 {
663     local_remove(lt, LFambigref | LFambigdef);
664 }
665 
666 //////////////////////////////////////
667 // Reference to symbol.
668 // Remove any that define that symbol.
669 
670 private void local_symref(ref Barray!loc_t lt, Symbol *s)
671 {
672     symbol_debug(s);
673     for (uint u = 0; u < lt.length;)
674     {
675         if (local_getflags(lt[u].e,s) & LFsymdef)
676             local_rem(lt, u);
677         else
678             ++u;
679     }
680 }
681 
682 //////////////////////////////////////
683 // Definition of symbol.
684 // Remove any that reference that symbol.
685 
686 private void local_symdef(ref Barray!loc_t lt, Symbol *s)
687 {
688     symbol_debug(s);
689     for (uint u = 0; u < lt.length;)
690     {
691         if (local_getflags(lt[u].e,s) & (LFsymref | LFsymdef))
692             local_rem(lt, u);
693         else
694             ++u;
695     }
696 }
697 
698 /***************************************************
699  * See if we should preserve assignment to Symbol of type ty.
700  * Returns:
701  *      true if preserve assignment
702  * References:
703  *      https://issues.dlang.org/show_bug.cgi?id=13474
704  */
705 private bool local_preserveAssignmentTo(tym_t ty)
706 {
707     /* Need to preserve assignment if generating code using
708      * the x87, as that is the only way to get the x87 to
709      * convert to float/double precision.
710      */
711     if (config.inline8087 && !config.fpxmmregs)
712     {
713         switch (tybasic(ty))
714         {
715             case TYfloat:
716             case TYifloat:
717             case TYcfloat:
718             case TYdouble:
719             case TYidouble:
720             case TYdouble_alias:
721             case TYcdouble:
722                 return true;
723 
724             default:
725                 break;
726         }
727     }
728     return false;
729 }
730 
731 }