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