1 /**
2  * Manage the memory allocated on the runtime stack to save Common Subexpressions (CSE).
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1985-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/cgcse.d, backend/cgcse.d)
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/cgcse.d
13  */
14 
15 module dmd.backend.cgcse;
16 
17 import core.stdc.stdio;
18 import core.stdc.stdlib;
19 import core.stdc.string;
20 
21 import dmd.backend.cc;
22 import dmd.backend.cdef;
23 import dmd.backend.code;
24 import dmd.backend.code_x86;
25 import dmd.backend.el;
26 import dmd.backend.global;
27 import dmd.backend.ty;
28 
29 import dmd.backend.barray;
30 
31 nothrow:
32 @safe:
33 
34 /****************************
35  * Table of common subexpressions stored on the stack.
36  *      csextab[]       array of info on saved CSEs
37  *      CSEpe           pointer to saved elem
38  *      CSEregm         mask of register that was saved (so for multi-
39  *                      register variables we know which part we have)
40  */
41 
42 enum CSEload       = 1;       // set if the CSE was ever loaded
43 enum CSEsimple     = 2;       // CSE can be regenerated easily
44 
45 struct CSE
46 {
47     elem*   e;              // pointer to elem
48     code    csimple;        // if CSEsimple, this is the code to regenerate it
49     regm_t  regm;           // mask of register stored there
50     int     slot;           // slot number
51     ubyte   flags;          // flag bytes
52 
53   nothrow:
54 
55     /************************
56      * Initialize at function entry.
57      */
58     @trusted
59     static void initialize()
60     {
61         csextab.setLength(64);  // reserve some space
62     }
63 
64     /************************
65      * Start for generating code for this function.
66      * After ending generation, call finish().
67      */
68     @trusted
69     static void start()
70     {
71         csextab.setLength(0);               // no entries in table yet
72         slotSize = REGSIZE;
73         alignment_ = REGSIZE;
74     }
75 
76     /*******************************
77      * Create and add a new CSE entry.
78      * Returns:
79      *  pointer to created entry
80      */
81     @trusted
82     static CSE* add()
83     {
84         foreach (ref cse; csextab)
85         {
86             if (cse.e == null)  // can share with previously used one
87             {
88                 cse.flags &= CSEload;
89                 return &cse;
90             }
91         }
92 
93         // create new one
94         const slot = cast(int)csextab.length;
95         CSE cse;
96         cse.slot = slot;
97         csextab.push(cse);
98         return &csextab[slot];
99     }
100 
101     /********************************
102      * Update slot size and alignment to worst case.
103      *
104      * A bit wasteful of stack space.
105      * Params: e = elem with a size and an alignment
106      */
107     @trusted
108     static void updateSizeAndAlign(elem* e)
109     {
110         if (I16)
111             return;
112         const sz = tysize(e.Ety);
113         if (slotSize < sz)
114             slotSize = sz;
115         const alignsize = el_alignsize(e);
116 
117         static if (0)
118         printf("set slot size = %d, sz = %d, al = %d, ty = x%x, %s\n",
119             slotSize, cast(int)sz, cast(int)alignsize,
120             cast(int)tybasic(e.Ety), funcsym_p.Sident.ptr);
121 
122         if (alignsize >= 16 && TARGET_STACKALIGN >= 16)
123         {
124             alignment_ = alignsize;
125             STACKALIGN = alignsize;
126             enforcealign = true;
127         }
128     }
129 
130     /****
131      * Get range of all CSEs filtered by matching `e`,
132      * starting with most recent.
133      * Params: e = elem to match
134      * Returns:
135      *  input range
136      */
137     @trusted
138     static auto filter(const elem* e)
139     {
140         struct Range
141         {
142             const elem* e;
143             int i;
144 
145           nothrow:
146 
147             bool empty()
148             {
149                 while (i)
150                 {
151                     if (csextab[i - 1].e == e)
152                         return false;
153                     --i;
154                 }
155                 return true;
156             }
157 
158             ref CSE front() { return csextab[i - 1]; }
159 
160             void popFront() { --i; }
161         }
162 
163         return Range(e, cast(int)csextab.length);
164     }
165 
166     /*********************
167      * Remove instances of `e` from CSE table.
168      * Params: e = elem to remove
169      */
170     @trusted
171     static void remove(const elem* e)
172     {
173         foreach (ref cse; csextab[])
174         {
175             if (cse.e == e)
176                 cse.e = null;
177         }
178     }
179 
180     /************************
181      * Create mask of registers from CSEs that refer to `e`.
182      * Params: e = elem to match
183      * Returns:
184      *  mask
185      */
186     @trusted
187     static regm_t mask(const elem* e)
188     {
189         regm_t result = 0;
190         foreach (ref cse; csextab[])
191         {
192             if (cse.e)
193                 elem_debug(cse.e);
194             if (cse.e == e)
195                 result |= cse.regm;
196         }
197         return result;
198     }
199 
200     /***
201      * Finish generating code for this function.
202      *
203      * Get rid of unused cse temporaries by shrinking the array.
204      * References: loaded()
205      */
206     @trusted
207     static void finish()
208     {
209         while (csextab.length != 0 && (csextab[csextab.length - 1].flags & CSEload) == 0)
210             csextab.setLength(csextab.length - 1);
211     }
212 
213     /**** The rest of the functions can be called only after finish() ****/
214 
215     /******************
216      * Returns:
217      *    total size used by CSE's
218      */
219     @trusted
220     static uint size()
221     {
222         return cast(uint)csextab.length * CSE.slotSize;
223     }
224 
225     /*********************
226      * Returns:
227      *  alignment needed for CSE region of the stack
228      */
229     @trusted
230     static uint alignment()
231     {
232         return alignment_;
233     }
234 
235     /// Returns: offset of slot i from start of CSE region
236     @trusted
237     static uint offset(int i)
238     {
239         return i * slotSize;
240     }
241 
242     /// Returns: true if CSE was ever loaded
243     @trusted
244     static bool loaded(int i)
245     {
246         return i < csextab.length &&   // array could be shrunk for non-CSEload entries
247                (csextab[i].flags & CSEload);
248     }
249 
250   private:
251   __gshared:
252     Barray!CSE csextab;     // CSE table (allocated for each function)
253     uint slotSize;          // size of each slot in table
254     uint alignment_;        // alignment for the table
255 }
256 
257 
258 /********************
259  * The above implementation of CSE is inefficient:
260  * 1. the optimization to not store CSE's that are never reloaded is based on the slot number,
261  * not the CSE. This means that when a slot is shared among multiple CSEs, it is treated
262  * as "reloaded" even if only one of the CSEs in that slot is reloaded.
263  * 2. updateSizeAndAlign should only be run when reloading when (1) is fixed.
264  * 3. all slots are aligned to worst case alignment of any slot.
265  * 4. unused slots still get memory allocated to them if they aren't at the end of the
266  * slot table.
267  *
268  * The slot number should be unique to each CSE, and allocation of actual slots should be
269  * done after the code is generated, not during generation.
270  */