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