1 /**
2 * Compiler implementation of the
3 * $(LINK2 http://www.dlang.org, D programming language).
4 *
5 * Manage the memory allocated on the runtime stack to save Common Subexpressions (CSE).
6 *
7 * Copyright: Copyright (C) 1985-1998 by Symantec
8 * Copyright (C) 2000-2021 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: $(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 moduledmd.backend.cgcse;
16 17 version (SCPP)
18 version = COMPILE;
19 version (MARS)
20 version = COMPILE;
21 22 version (COMPILE)
23 {
24 25 importcore.stdc.stdio;
26 importcore.stdc.stdlib;
27 importcore.stdc.string;
28 29 importdmd.backend.cc;
30 importdmd.backend.cdef;
31 importdmd.backend.code;
32 importdmd.backend.code_x86;
33 importdmd.backend.el;
34 importdmd.backend.global;
35 importdmd.backend.ty;
36 37 importdmd.backend.barray;
38 39 extern (C++):
40 nothrow:
41 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 enumCSEload = 1; // set if the CSE was ever loaded52 enumCSEsimple = 2; // CSE can be regenerated easily53 54 structCSE55 {
56 elem* e; // pointer to elem57 codecsimple; // if CSEsimple, this is the code to regenerate it58 regm_tregm; // mask of register stored there59 intslot; // slot number60 ubyteflags; // flag bytes61 62 nothrow:
63 64 /************************
65 * Initialize at function entry.
66 */67 staticvoidinitialize()
68 {
69 csextab.setLength(64); // reserve some space70 }
71 72 /************************
73 * Start for generating code for this function.
74 * After ending generation, call finish().
75 */76 staticvoidstart()
77 {
78 csextab.setLength(0); // no entries in table yet79 slotSize = REGSIZE;
80 alignment_ = REGSIZE;
81 }
82 83 /*******************************
84 * Create and add a new CSE entry.
85 * Returns:
86 * pointer to created entry
87 */88 staticCSE* add()
89 {
90 foreach (refcse; csextab)
91 {
92 if (cse.e == null) // can share with previously used one93 {
94 cse.flags &= CSEload;
95 return &cse;
96 }
97 }
98 99 // create new one100 constslot = cast(int)csextab.length;
101 CSEcse;
102 cse.slot = slot;
103 csextab.push(cse);
104 return &csextab[slot];
105 }
106 107 /********************************
108 * Update slot size and alignment to worst case.
109 *
110 * A bit wasteful of stack space.
111 * Params: e = elem with a size and an alignment
112 */113 staticvoidupdateSizeAndAlign(elem* e)
114 {
115 if (I16)
116 return;
117 constsz = tysize(e.Ety);
118 if (slotSize < sz)
119 slotSize = sz;
120 constalignsize = el_alignsize(e);
121 122 staticif (0)
123 printf("set slot size = %d, sz = %d, al = %d, ty = x%x, %s\n",
124 slotSize, cast(int)sz, cast(int)alignsize,
125 cast(int)tybasic(e.Ety), funcsym_p.Sident.ptr);
126 127 if (alignsize >= 16 && TARGET_STACKALIGN >= 16)
128 {
129 alignment_ = alignsize;
130 STACKALIGN = alignsize;
131 enforcealign = true;
132 }
133 }
134 135 /****
136 * Get range of all CSEs filtered by matching `e`,
137 * starting with most recent.
138 * Params: e = elem to match
139 * Returns:
140 * input range
141 */142 staticautofilter(constelem* e)
143 {
144 structRange145 {
146 constelem* e;
147 inti;
148 149 nothrow:
150 151 boolempty()
152 {
153 while (i)
154 {
155 if (csextab[i - 1].e == e)
156 returnfalse;
157 --i;
158 }
159 returntrue;
160 }
161 162 refCSEfront() { returncsextab[i - 1]; }
163 164 voidpopFront() { --i; }
165 }
166 167 returnRange(e, cast(int)csextab.length);
168 }
169 170 /*********************
171 * Remove instances of `e` from CSE table.
172 * Params: e = elem to remove
173 */174 staticvoidremove(constelem* e)
175 {
176 foreach (refcse; csextab[])
177 {
178 if (cse.e == e)
179 cse.e = null;
180 }
181 }
182 183 /************************
184 * Create mask of registers from CSEs that refer to `e`.
185 * Params: e = elem to match
186 * Returns:
187 * mask
188 */189 staticregm_tmask(constelem* e)
190 {
191 regm_tresult = 0;
192 foreach (refcse; csextab[])
193 {
194 if (cse.e)
195 elem_debug(cse.e);
196 if (cse.e == e)
197 result |= cse.regm;
198 }
199 returnresult;
200 }
201 202 /***
203 * Finish generating code for this function.
204 *
205 * Get rid of unused cse temporaries by shrinking the array.
206 * References: loaded()
207 */208 staticvoidfinish()
209 {
210 while (csextab.length != 0 && (csextab[csextab.length - 1].flags & CSEload) == 0)
211 csextab.setLength(csextab.length - 1);
212 }
213 214 /**** The rest of the functions can be called only after finish() ****/215 216 /******************
217 * Returns:
218 * total size used by CSE's
219 */220 staticuintsize()
221 {
222 returncast(uint)csextab.length * CSE.slotSize;
223 }
224 225 /*********************
226 * Returns:
227 * alignment needed for CSE region of the stack
228 */229 staticuintalignment()
230 {
231 returnalignment_;
232 }
233 234 /// Returns: offset of slot i from start of CSE region235 staticuintoffset(inti)
236 {
237 returni * slotSize;
238 }
239 240 /// Returns: true if CSE was ever loaded241 staticboolloaded(inti)
242 {
243 returni < csextab.length && // array could be shrunk for non-CSEload entries244 (csextab[i].flags & CSEload);
245 }
246 247 private:
248 __gshared:
249 Barray!CSEcsextab; // CSE table (allocated for each function)250 uintslotSize; // size of each slot in table251 uintalignment_; // alignment for the table252 }
253 254 255 /********************
256 * The above implementation of CSE is inefficient:
257 * 1. the optimization to not store CSE's that are never reloaded is based on the slot number,
258 * not the CSE. This means that when a slot is shared among multiple CSEs, it is treated
259 * as "reloaded" even if only one of the CSEs in that slot is reloaded.
260 * 2. updateSizeAndAlign should only be run when reloading when (1) is fixed.
261 * 3. all slots are aligned to worst case alignment of any slot.
262 * 4. unused slots still get memory allocated to them if they aren't at the end of the
263 * slot table.
264 *
265 * The slot number should be unique to each CSE, and allocation of actual slots should be
266 * done after the code is generated, not during generation.
267 */268 269 270 }