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