/
graph.h
296 lines (235 loc) · 9.73 KB
/
graph.h
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#define MVMPhiNodeCacheSize 48
#define MVMPhiNodeCacheSparseBegin 32
/* Top level of a spesh graph, representing a particular static frame (and
* potentially having others inlined into it). */
struct MVMSpeshGraph {
/* The static frame this is the spesh graph for. */
MVMStaticFrame *sf;
/* The callsite this spesh graph has been tailored to. */
MVMCallsite *cs;
/* The bytecode we're building the graph out of. */
MVMuint8 *bytecode;
/* Exception handler map for that bytecode. */
MVMFrameHandler *handlers;
/* Handlers that have become unreachable due to dead code removal. */
MVMint8 *unreachable_handlers;
/* The size of the bytecode we're building the graph out of. */
MVMuint32 bytecode_size;
/* Number of exception handlers. */
MVMuint32 num_handlers;
/* The entry basic block. */
MVMSpeshBB *entry;
/* Gathered facts about each version of a local (top-level array is per
* local, then array hanging off it is per version). */
MVMSpeshFacts **facts;
/* Number of fact entries per local. */
MVMuint16 *fact_counts;
/* Log-based guards added. */
MVMSpeshLogGuard *log_guards;
/* Number of log-based guards we have. */
MVMint32 num_log_guards;
/* Region allocator for spesh nodes */
MVMRegionAlloc region_alloc;
/* Values placed in spesh slots. */
MVMCollectable **spesh_slots;
/* Number of spesh slots we have used and allocated. */
MVMuint16 num_spesh_slots;
MVMuint16 alloc_spesh_slots;
/* De-opt indexes, as pairs of integers. The first integer, set when we
* build the graph, is the return address in the original bytecode. The
* code-gen phase for the specialized bytecode will fill in the second
* integers afterwards, which are the return address in the specialized
* bytecode. */
MVMint32 *deopt_addrs;
MVMuint32 num_deopt_addrs;
MVMuint32 alloc_deopt_addrs;
/* Bit field of named args used to put in place during deopt, since we
* don't typically don't update the array in specialized code. */
MVMuint64 deopt_named_used_bit_field;
/* Deopt points that are considered to be always in use, even if they
* are not on a deopting instruction. */
MVMuint32 *always_retained_deopt_idxs;
MVMuint32 num_always_retained_deopt_idxs;
/* Deopt information produced by escape analysis and scalar replacement. */
MVMSpeshPEADeopt deopt_pea;
/* Table of information about inlines, laid out in order of nesting
* depth. Thus, going through the table in order and finding when we
* are within the bounds will show up each call frame that needs to
* be created in deopt. */
MVMSpeshInline *inlines;
MVMuint32 num_inlines;
/* Number of basic blocks we have. */
MVMuint32 num_bbs;
/* The list of local types (only set up if we do inlines). */
MVMuint16 *local_types;
/* The list of lexical types (only set up if we do inlines). */
MVMuint16 *lexical_types;
/* The total number of locals, accounting for any inlining done and
* added temporaries. */
MVMuint16 num_locals;
/* The total number of lexicals, accounting for any inlining done. */
MVMuint16 num_lexicals;
/* Temporary local registers added to aid transformations, along with a
* count of the number we have and have allocated space for so far. */
MVMuint16 num_temps;
MVMuint16 alloc_temps;
MVMSpeshTemporary *temps;
/* We need to create new MVMOpInfo structs for each number of
* arguments a PHI node can take. We cache them here, so that we
* allocate fewer of them across our spesh alloc blocks.
*/
MVMOpInfo *phi_infos;
/* If this graph was formed from a spesh candidate rather than an
* original static frame, the candidate will be stored here. */
MVMSpeshCandidate *cand;
/* Did we specialize on the invocant type? */
MVMuint8 specialized_on_invocant;
/* Do we set a dispatcher? */
MVMuint8 sets_dispatcher;
};
/* A temporary register, added to support transformations. */
struct MVMSpeshTemporary {
/* The number of the local along with the current SSA index. */
MVMuint16 orig;
MVMuint16 i;
/* The SSA index currently loaned out. */
MVMuint16 used_i;
/* What kind of register is it? */
MVMuint16 kind;
/* Is it currently in use? */
MVMuint16 in_use;
};
/* A basic block in the graph (sequences of instructions where control will
* always enter at the start and leave at the end). */
struct MVMSpeshBB {
/* Head/tail of doubly linked list of instructions. */
MVMSpeshIns *first_ins;
MVMSpeshIns *last_ins;
/* Basic blocks we may go to after this one. */
MVMSpeshBB **succ;
/* Basic blocks that we may arrive into this one from. */
MVMSpeshBB **pred;
/* Children in the dominator tree. */
MVMSpeshBB **children;
/* Dominance frontier set. */
MVMSpeshBB **df;
/* Basic blocks that we may go to if we throw. */
MVMSpeshBB **handler_succ;
/* Counts for the above, grouped together to avoid alignment holes. */
MVMuint16 num_succ;
MVMuint16 num_pred;
MVMuint16 num_children;
MVMuint16 num_df;
MVMuint16 num_handler_succ;
/* The next basic block in original linear code order. */
MVMSpeshBB *linear_next;
/* Index (just an ascending integer along the linear_next chain), used as
* the block identifier in dominance computation and for debug output. */
MVMint32 idx;
/* The block's reverse post-order index, assinged when computing
* dominance. */
MVMint32 rpo_idx;
/* We cache the instruction pointer of the very first instruction so that
* we can output a line number for every BB */
MVMuint32 initial_pc;
/* Is this block an inlining of another one? */
MVMint8 inlined;
/* Is this basic block part of a jump list? */
MVMint8 jumplist;
/* Is this basic block dead (removed due to being unreachable)? */
MVMint8 dead;
};
/* The SSA phi instruction. */
#define MVM_SSA_PHI 32767
/* An instruction in the spesh graph. */
struct MVMSpeshIns {
/* Instruction information. */
const MVMOpInfo *info;
/* Operand information. */
MVMSpeshOperand *operands;
/* Previous and next instructions, within a basic block boundary. */
MVMSpeshIns *prev;
MVMSpeshIns *next;
/* Any annotations on the instruction. */
MVMSpeshAnn *annotations;
};
/* Union type of operands in a spesh instruction; the op info and phase of the
* optimizer we're in determines which of these we look at. */
union MVMSpeshOperand {
MVMint64 lit_i64;
MVMint32 lit_i32;
MVMuint16 lit_ui32;
MVMint16 lit_i16;
MVMuint16 lit_ui16;
MVMint8 lit_i8;
MVMnum64 lit_n64;
MVMnum32 lit_n32;
MVMuint32 lit_str_idx;
MVMuint16 callsite_idx;
MVMuint16 coderef_idx;
MVMuint32 ins_offset;
MVMSpeshBB *ins_bb;
struct {
MVMuint16 idx;
MVMuint16 outers;
} lex;
struct {
MVMint32 i; /* SSA-computed version. */
MVMuint16 orig; /* Original register number. */
} reg;
};
/* Annotations base. */
struct MVMSpeshAnn {
/* The next annotation in the chain, if any. */
MVMSpeshAnn *next;
/* The type of annotation we have. */
MVMint32 type;
/* Data (meaning depends on type). */
union {
MVMuint32 frame_handler_index;
MVMint32 deopt_idx;
MVMint32 inline_idx;
MVMuint32 bytecode_offset;
struct {
MVMuint32 filename_string_index;
MVMuint32 line_number;
} lineno;
char *comment;
} data;
};
/* Annotation types. */
#define MVM_SPESH_ANN_FH_START 1
#define MVM_SPESH_ANN_FH_END 2
#define MVM_SPESH_ANN_FH_GOTO 3
#define MVM_SPESH_ANN_DEOPT_ONE_INS 4
#define MVM_SPESH_ANN_DEOPT_ALL_INS 5
#define MVM_SPESH_ANN_INLINE_START 6
#define MVM_SPESH_ANN_INLINE_END 7
#define MVM_SPESH_ANN_DEOPT_INLINE 8
#define MVM_SPESH_ANN_DEOPT_OSR 9
#define MVM_SPESH_ANN_LINENO 10
#define MVM_SPESH_ANN_LOGGED 11
#define MVM_SPESH_ANN_DEOPT_SYNTH 12
#define MVM_SPESH_ANN_COMMENT 4096
/* Functions to create/destroy the spesh graph. */
MVMSpeshGraph * MVM_spesh_graph_create(MVMThreadContext *tc, MVMStaticFrame *sf,
MVMuint32 cfg_only, MVMuint32 insert_object_nulls);
MVMSpeshGraph * MVM_spesh_graph_create_from_cand(MVMThreadContext *tc, MVMStaticFrame *sf,
MVMSpeshCandidate *cand, MVMuint32 cfg_only, MVMSpeshIns ***deopt_usage_ins_out);
MVMSpeshBB * MVM_spesh_graph_linear_prev(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *search);
void MVM_spesh_graph_grow_deopt_table(MVMThreadContext *tc, MVMSpeshGraph *g);
MVMint32 MVM_spesh_graph_add_deopt_annotation(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshIns *ins_node, MVMuint32 deopt_target, MVMint32 type);
MVMSpeshBB ** MVM_spesh_graph_reverse_postorder(MVMThreadContext *tc, MVMSpeshGraph *g);
void MVM_spesh_graph_recompute_dominance(MVMThreadContext *tc, MVMSpeshGraph *g);
void MVM_spesh_graph_mark(MVMThreadContext *tc, MVMSpeshGraph *g, MVMGCWorklist *worklist);
void MVM_spesh_graph_describe(MVMThreadContext *tc, MVMSpeshGraph *g, MVMHeapSnapshotState *snapshot);
void MVM_spesh_graph_destroy(MVMThreadContext *tc, MVMSpeshGraph *g);
MVM_PUBLIC void * MVM_spesh_alloc(MVMThreadContext *tc, MVMSpeshGraph *g, size_t bytes);
MVMOpInfo *get_phi(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint32 nrargs);
MVM_PUBLIC void MVM_spesh_graph_add_comment(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshIns *ins, const char *fmt, ...);
MVM_STATIC_INLINE MVMuint32 MVM_spesh_is_inc_dec_op(MVMuint16 opcode) {
return opcode == MVM_OP_inc_i || opcode == MVM_OP_dec_i ||
opcode == MVM_OP_inc_u || opcode == MVM_OP_dec_u;
}