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