1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Compute common subexpressions for non-optimized builds.
6  *
7  * Copyright:   Copyright (C) 1985-1995 by Symantec
8  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
9  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
10  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11  * Source:      https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgcs.d
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/cgcs.d
13  */
14 
15 module dmd.backend.cgcs;
16 
17 version (SPP)
18 {
19 }
20 else
21 {
22 
23 import core.stdc.stdio;
24 import core.stdc.stdlib;
25 
26 import dmd.backend.cc;
27 import dmd.backend.cdef;
28 import dmd.backend.code;
29 import dmd.backend.el;
30 import dmd.backend.global;
31 import dmd.backend.oper;
32 import dmd.backend.ty;
33 import dmd.backend.type;
34 
35 import dmd.backend.barray;
36 import dmd.backend.dlist;
37 import dmd.backend.dvec;
38 
39 
40 nothrow:
41 
42 /*******************************
43  * Eliminate common subexpressions across extended basic blocks.
44  * String together as many blocks as we can.
45  */
46 
47 public extern (C++) void comsubs()
48 {
49     //static int xx;
50     //printf("comsubs() %d\n", ++xx);
51     //debugx = (xx == 37);
52 
53     debug if (debugx) printf("comsubs(%p)\n",startblock);
54 
55     // No longer do we just compute Bcount. We now eliminate unreachable
56     // blocks.
57     block_compbcount();                   // eliminate unreachable blocks
58 
59     version (SCPP)
60     {
61         if (errcnt)
62             return;
63     }
64 
65     if (!csvec)
66     {
67         csvec = vec_calloc(CSVECDIM);
68     }
69 
70     block* bln;
71     for (block* bl = startblock; bl; bl = bln)
72     {
73         bln = bl.Bnext;
74         if (!bl.Belem)
75             continue;                   /* if no expression or no parents       */
76 
77         // Count up n, the number of blocks in this extended basic block (EBB)
78         int n = 1;                      // always at least one block in EBB
79         auto blc = bl;
80         while (bln && list_nitems(bln.Bpred) == 1 &&
81                ((blc.BC == BCiftrue &&
82                  blc.nthSucc(1) == bln) ||
83                 (blc.BC == BCgoto && blc.nthSucc(0) == bln)
84                ) &&
85                bln.BC != BCasm         // no CSE's extending across ASM blocks
86               )
87         {
88             n++;                    // add block to EBB
89             blc = bln;
90             bln = blc.Bnext;
91         }
92         vec_clear(csvec);
93         hcstab.setLength(0);
94         hcsarray.touchstari = 0;
95         hcsarray.touchfunci[0] = 0;
96         hcsarray.touchfunci[1] = 0;
97         bln = bl;
98         while (n--)                     // while more blocks in EBB
99         {
100             debug if (debugx)
101                 printf("cses for block %p\n",bln);
102 
103             if (bln.Belem)
104                 ecom(&bln.Belem);  // do the tree
105             bln = bln.Bnext;
106         }
107     }
108 
109     debug if (debugx)
110         printf("done with comsubs()\n");
111 }
112 
113 /*******************************
114  */
115 
116 public extern (C++) void cgcs_term()
117 {
118     vec_free(csvec);
119     csvec = null;
120     debug debugw && printf("freeing hcstab\n");
121     //hcstab.dtor();  // cache allocation for next iteration
122 }
123 
124 
125 /***********************************************************************/
126 
127 private:
128 
129 alias hash_t = uint;    // for hash values
130 
131 /*********************************
132  * Struct for each elem:
133  *      Helem   pointer to elem
134  *      Hhash   hash value for the elem
135  */
136 
137 struct HCS
138 {
139     elem* Helem;
140     hash_t Hhash;
141 }
142 
143 struct HCSArray
144 {
145     uint touchstari;
146     uint[2] touchfunci;
147 }
148 
149 __gshared
150 {
151     Barray!HCS hcstab;           // array of hcs's
152     HCSArray hcsarray;
153 
154     // Use a bit vector for quick check if expression is possibly in hcstab[].
155     // This results in much faster compiles when hcstab[] gets big.
156     vec_t csvec;                 // vector of used entries
157     enum CSVECDIM = 16_001; //8009 //3001     // dimension of csvec (should be prime)
158 }
159 
160 
161 /*************************
162  * Eliminate common subexpressions for an element.
163  */
164 
165 void ecom(elem **pe)
166 {
167     auto e = *pe;
168     assert(e);
169     elem_debug(e);
170     debug assert(e.Ecount == 0);
171     //assert(e.Ecomsub == 0);
172     const tym = tybasic(e.Ety);
173     const op = e.Eoper;
174     switch (op)
175     {
176         case OPconst:
177         case OPrelconst:
178             break;
179 
180         case OPvar:
181             if (e.EV.Vsym.ty() & mTYshared)
182                 return;         // don't cache shared variables
183             break;
184 
185         case OPstreq:
186         case OPpostinc:
187         case OPpostdec:
188         case OPeq:
189         case OPaddass:
190         case OPminass:
191         case OPmulass:
192         case OPdivass:
193         case OPmodass:
194         case OPshrass:
195         case OPashrass:
196         case OPshlass:
197         case OPandass:
198         case OPxorass:
199         case OPorass:
200         case OPvecsto:
201             /* Reverse order of evaluation for double op=. This is so that  */
202             /* the pushing of the address of the second operand is easier.  */
203             /* However, with the 8087 we don't need the kludge.             */
204             if (op != OPeq && tym == TYdouble && !config.inline8087)
205             {
206                 if (!OTleaf(e.EV.E1.Eoper))
207                     ecom(&e.EV.E1.EV.E1);
208                 ecom(&e.EV.E2);
209             }
210             else
211             {
212                 /* Don't mark the increment of an i++ or i-- as a CSE, if it */
213                 /* can be done with an INC or DEC instruction.               */
214                 if (!(OTpost(op) && elemisone(e.EV.E2)))
215                     ecom(&e.EV.E2);           /* evaluate 2nd operand first   */
216         case OPnegass:
217                 if (!OTleaf(e.EV.E1.Eoper))             /* if lvalue is an operator     */
218                 {
219                     if (e.EV.E1.Eoper != OPind)
220                         elem_print(e);
221                     assert(e.EV.E1.Eoper == OPind);
222                     ecom(&(e.EV.E1.EV.E1));
223                 }
224             }
225             touchlvalue(e.EV.E1);
226             if (!OTpost(op))                /* lvalue of i++ or i-- is not a cse*/
227             {
228                 const hash = cs_comphash(e.EV.E1);
229                 vec_setbit(hash % CSVECDIM,csvec);
230                 addhcstab(e.EV.E1,hash);              // add lvalue to hcstab[]
231             }
232             return;
233 
234         case OPbtc:
235         case OPbts:
236         case OPbtr:
237         case OPcmpxchg:
238             ecom(&e.EV.E1);
239             ecom(&e.EV.E2);
240             touchfunc(0);                   // indirect assignment
241             return;
242 
243         case OPandand:
244         case OPoror:
245         {
246             ecom(&e.EV.E1);
247             const lengthSave = hcstab.length;
248             auto hcsarraySave = hcsarray;
249             ecom(&e.EV.E2);
250             hcsarray = hcsarraySave;        // no common subs by E2
251             hcstab.setLength(lengthSave);
252             return;                         /* if comsub then logexp() will */
253         }
254 
255         case OPcond:
256         {
257             ecom(&e.EV.E1);
258             const lengthSave = hcstab.length;
259             auto hcsarraySave = hcsarray;
260             ecom(&e.EV.E2.EV.E1);               // left condition
261             hcsarray = hcsarraySave;        // no common subs by E2
262             hcstab.setLength(lengthSave);
263             ecom(&e.EV.E2.EV.E2);               // right condition
264             hcsarray = hcsarraySave;        // no common subs by E2
265             hcstab.setLength(lengthSave);
266             return;                         // can't be a common sub
267         }
268 
269         case OPcall:
270         case OPcallns:
271             ecom(&e.EV.E2);                   /* eval right first             */
272             goto case OPucall;
273 
274         case OPucall:
275         case OPucallns:
276             ecom(&e.EV.E1);
277             touchfunc(1);
278             return;
279 
280         case OPstrpar:                      /* so we don't break logexp()   */
281         case OPinp:                 /* never CSE the I/O instruction itself */
282         case OPprefetch:            // don't CSE E2 or the instruction
283             ecom(&e.EV.E1);
284             goto case OPasm;
285 
286         case OPasm:
287         case OPstrthis:             // don't CSE these
288         case OPframeptr:
289         case OPgot:
290         case OPctor:
291         case OPdtor:
292         case OPdctor:
293         case OPmark:
294             return;
295 
296         case OPddtor:
297             touchall();
298             ecom(&e.EV.E1);
299             touchall();
300             return;
301 
302         case OPparam:
303         case OPoutp:
304             ecom(&e.EV.E1);
305             goto case OPinfo;
306 
307         case OPinfo:
308             ecom(&e.EV.E2);
309             return;
310 
311         case OPcomma:
312             ecom(&e.EV.E1);
313             ecom(&e.EV.E2);
314             return;
315 
316         case OPremquo:
317             ecom(&e.EV.E1);
318             ecom(&e.EV.E2);
319             break;
320 
321         case OPvp_fp:
322         case OPcvp_fp:
323             ecom(&e.EV.E1);
324             touchaccess(hcstab, e);
325             break;
326 
327         case OPind:
328             ecom(&e.EV.E1);
329             /* Generally, CSEing a *(double *) results in worse code        */
330             if (tyfloating(tym))
331                 return;
332             if (tybasic(e.EV.E1.Ety) == TYsharePtr)
333                 return;
334             break;
335 
336         case OPstrcpy:
337         case OPstrcat:
338         case OPmemcpy:
339         case OPmemset:
340             ecom(&e.EV.E2);
341             goto case OPsetjmp;
342 
343         case OPsetjmp:
344             ecom(&e.EV.E1);
345             touchfunc(0);
346             return;
347 
348         default:                            /* other operators */
349             if (!OTbinary(e.Eoper))
350                WROP(e.Eoper);
351             assert(OTbinary(e.Eoper));
352             goto case OPadd;
353 
354         case OPadd:
355         case OPmin:
356         case OPmul:
357         case OPdiv:
358         case OPor:
359         case OPxor:
360         case OPand:
361         case OPeqeq:
362         case OPne:
363         case OPscale:
364         case OPyl2x:
365         case OPyl2xp1:
366             ecom(&e.EV.E1);
367             ecom(&e.EV.E2);
368             break;
369 
370         case OPstring:
371         case OPaddr:
372         case OPbit:
373             WROP(e.Eoper);
374             elem_print(e);
375             assert(0);              /* optelem() should have removed these  */
376             /* NOTREACHED */
377 
378         // Explicitly list all the unary ops for speed
379         case OPnot: case OPcom: case OPneg: case OPuadd:
380         case OPabs: case OPrndtol: case OPrint:
381         case OPpreinc: case OPpredec:
382         case OPbool: case OPstrlen: case OPs16_32: case OPu16_32:
383         case OPs32_d: case OPu32_d: case OPd_s16: case OPs16_d: case OP32_16:
384         case OPf_d:
385         case OPld_d:
386         case OPc_r: case OPc_i:
387         case OPu8_16: case OPs8_16: case OP16_8:
388         case OPu32_64: case OPs32_64: case OP64_32: case OPmsw:
389         case OPu64_128: case OPs64_128: case OP128_64:
390         case OPs64_d: case OPd_u64: case OPu64_d:
391         case OPstrctor: case OPu16_d: case OPd_u16:
392         case OParrow:
393         case OPvoid:
394         case OPbsf: case OPbsr: case OPbswap: case OPpopcnt: case OPvector:
395         case OPld_u64:
396         case OPsqrt: case OPsin: case OPcos:
397         case OPoffset: case OPnp_fp: case OPnp_f16p: case OPf16p_np:
398         case OPvecfill: case OPtoprec:
399             ecom(&e.EV.E1);
400             break;
401 
402         case OPd_ld:
403             return;
404 
405         case OPd_f:
406         {
407             const op1 = e.EV.E1.Eoper;
408             if (config.fpxmmregs &&
409                 (op1 == OPs32_d ||
410                  I64 && (op1 == OPs64_d || op1 == OPu32_d))
411                )
412                 ecom(&e.EV.E1.EV.E1);   // e and e1 ops are fused (see xmmcnvt())
413             else
414                 ecom(&e.EV.E1);
415             break;
416         }
417 
418         case OPd_s32:
419         case OPd_u32:
420         case OPd_s64:
421             if (e.EV.E1.Eoper == OPf_d && config.fpxmmregs)
422                 ecom(&e.EV.E1.EV.E1);   // e and e1 ops are fused (see xmmcnvt());
423             else
424                 ecom(&e.EV.E1);
425             break;
426 
427         case OPhalt:
428             return;
429     }
430 
431     /* don't CSE structures or unions or volatile stuff   */
432     if (tym == TYstruct ||
433         tym == TYvoid ||
434         e.Ety & mTYvolatile)
435         return;
436     if (tyfloating(tym) && config.inline8087)
437     {
438         /* can CSE XMM code, but not x87
439          */
440         if (!(config.fpxmmregs && tyxmmreg(tym)))
441             return;
442     }
443 
444     const hash = cs_comphash(e);                /* must be AFTER leaves are done */
445 
446     /* Search for a match in hcstab[].
447      * Search backwards, as most likely matches will be towards the end
448      * of the list.
449      */
450 
451     debug if (debugx) printf("elem: %p hash: %6d\n",e,hash);
452     int csveci = hash % CSVECDIM;
453     if (vec_testbit(csveci,csvec))
454     {
455         foreach_reverse (i, ref hcs; hcstab[])
456         {
457             debug if (debugx)
458                 printf("i: %2d Hhash: %6d Helem: %p\n",
459                        cast(int) i,hcs.Hhash,hcs.Helem);
460 
461             elem* ehash;
462             if (hash == hcs.Hhash && (ehash = hcs.Helem) != null)
463             {
464                 /* if elems are the same and we still have room for more    */
465                 if (el_match(e,ehash) && ehash.Ecount < 0xFF)
466                 {
467                     /* Make sure leaves are also common subexpressions
468                      * to avoid false matches.
469                      */
470                     if (!OTleaf(op))
471                     {
472                         if (!e.EV.E1.Ecount)
473                             continue;
474                         if (OTbinary(op) && !e.EV.E2.Ecount)
475                             continue;
476                     }
477                     ehash.Ecount++;
478                     *pe = ehash;
479 
480                     debug if (debugx)
481                         printf("**MATCH** %p with %p\n",e,*pe);
482 
483                     el_free(e);
484                     return;
485                 }
486             }
487         }
488     }
489     else
490         vec_setbit(csveci,csvec);
491     addhcstab(e,hash);                    // add this elem to hcstab[]
492 }
493 
494 /**************************
495  * Compute hash function for elem e.
496  */
497 
498 hash_t cs_comphash(const elem *e)
499 {
500     elem_debug(e);
501     const op = e.Eoper;
502     hash_t hash = (e.Ety & (mTYbasic | mTYconst | mTYvolatile)) + (op << 8);
503     if (!OTleaf(op))
504     {
505         hash += cast(size_t) e.EV.E1;
506         if (OTbinary(op))
507             hash += cast(size_t) e.EV.E2;
508     }
509     else
510     {
511         hash += e.EV.Vint;
512         if (op == OPvar || op == OPrelconst)
513             hash += cast(size_t) e.EV.Vsym;
514     }
515     return hash;
516 }
517 
518 /****************************
519  * Add an elem to the common subexpression table.
520  */
521 
522 void addhcstab(elem *e, hash_t hash)
523 {
524     hcstab.push(HCS(e, hash));
525 }
526 
527 /***************************
528  * "touch" the elem.
529  * If it is a pointer, "touch" all the suspects
530  * who could be pointed to.
531  * Eliminate common subs that are indirect loads.
532  */
533 
534 void touchlvalue(elem *e)
535 {
536     if (e.Eoper == OPind)                /* if indirect store            */
537     {
538         /* NOTE: Some types of array assignments do not need
539          * to touch all variables. (Like a[5], where a is an
540          * array instead of a pointer.)
541          */
542 
543         touchfunc(0);
544         return;
545     }
546 
547     foreach_reverse (ref hcs; hcstab[])
548     {
549         if (hcs.Helem &&
550             hcs.Helem.EV.Vsym == e.EV.Vsym)
551             hcs.Helem = null;
552     }
553 
554     if (!(e.Eoper == OPvar || e.Eoper == OPrelconst))
555         elem_print(e);
556     assert(e.Eoper == OPvar || e.Eoper == OPrelconst);
557     switch (e.EV.Vsym.Sclass)
558     {
559         case SCregpar:
560         case SCregister:
561         case SCpseudo:
562             break;
563 
564         case SCauto:
565         case SCparameter:
566         case SCfastpar:
567         case SCshadowreg:
568         case SCbprel:
569             if (e.EV.Vsym.Sflags & SFLunambig)
570                 break;
571             goto case SCstatic;
572 
573         case SCstatic:
574         case SCextern:
575         case SCglobal:
576         case SClocstat:
577         case SCcomdat:
578         case SCinline:
579         case SCsinline:
580         case SCeinline:
581         case SCcomdef:
582             touchstar();
583             break;
584 
585         default:
586             elem_print(e);
587             symbol_print(e.EV.Vsym);
588             assert(0);
589     }
590 }
591 
592 /**************************
593  * "touch" variables that could be changed by a function call or
594  * an indirect assignment.
595  * Eliminate any subexpressions that are "starred" (they need to
596  * be recomputed).
597  * Params:
598  *      flag =  If 1, then this is a function call.
599  *              If 0, then this is an indirect assignment.
600  */
601 
602 void touchfunc(int flag)
603 {
604 
605     //printf("touchfunc(%d)\n", flag);
606     HCS *petop = hcstab.ptr + hcstab.length;
607     //pe = &hcstab[0]; printf("pe = %p, petop = %p\n",pe,petop);
608     assert(hcsarray.touchfunci[flag] <= hcstab.length);
609     for (HCS *pe = hcstab.ptr + hcsarray.touchfunci[flag]; pe < petop; pe++)
610     {
611         elem *he = pe.Helem;
612         if (!he)
613             continue;
614         switch (he.Eoper)
615         {
616             case OPvar:
617                 if (Symbol_isAffected(*he.EV.Vsym))
618                 {
619                     pe.Helem = null;
620                     continue;
621                 }
622                 break;
623 
624             case OPind:
625                 if (tybasic(he.EV.E1.Ety) == TYimmutPtr)
626                     break;
627                 goto Ltouch;
628 
629             case OPstrlen:
630             case OPstrcmp:
631             case OPmemcmp:
632             case OPbt:
633                 goto Ltouch;
634 
635             case OPvp_fp:
636             case OPcvp_fp:
637                 if (flag == 0)          /* function calls destroy vptrfptr's, */
638                     break;              /* not indirect assignments     */
639             Ltouch:
640                 pe.Helem = null;
641                 break;
642 
643             default:
644                 break;
645         }
646     }
647     hcsarray.touchfunci[flag] = cast(uint)hcstab.length;
648 }
649 
650 
651 /*******************************
652  * Eliminate all common subexpressions that
653  * do any indirection ("starred" elems).
654  */
655 
656 void touchstar()
657 {
658     foreach (ref hcs; hcstab[hcsarray.touchstari .. $])
659     {
660         const e = hcs.Helem;
661         if (e &&
662                (e.Eoper == OPind && tybasic(e.EV.E1.Ety) != TYimmutPtr ||
663                 e.Eoper == OPbt) )
664             hcs.Helem = null;
665     }
666     hcsarray.touchstari = cast(uint)hcstab.length;
667 }
668 
669 /*******************************
670  * Eliminate all common subexpressions.
671  */
672 
673 void touchall()
674 {
675     foreach (ref hcs; hcstab[])
676     {
677         hcs.Helem = null;
678     }
679     hcsarray.touchstari    = cast(uint)hcstab.length;
680     hcsarray.touchfunci[0] = cast(uint)hcstab.length;
681     hcsarray.touchfunci[1] = cast(uint)hcstab.length;
682 }
683 
684 /*****************************************
685  * Eliminate any common subexpressions that could be modified
686  * if a handle pointer access occurs.
687  */
688 
689 void touchaccess(ref Barray!HCS hcstab, const elem *ev) pure nothrow
690 {
691     const ev1 = ev.EV.E1;
692     foreach (ref hcs; hcstab[])
693     {
694         const e = hcs.Helem;
695         /* Invalidate any previous handle pointer accesses that */
696         /* are not accesses of ev.                              */
697         if (e && (e.Eoper == OPvp_fp || e.Eoper == OPcvp_fp) && e.EV.E1 != ev1)
698             hcs.Helem = null;
699     }
700 }
701 
702 }