/home/travis/build/MoarVM/MoarVM/src/spesh/facts.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* The code in this file walks the spesh graph, recording facts we discover |
4 | | * about each version of each local variable, and propagating the info as it |
5 | | * can. */ |
6 | | |
7 | | /* Copies facts from one var to another. */ |
8 | | static void copy_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 to_orig, |
9 | 202k | MVMuint16 to_i, MVMuint16 from_orig, MVMuint16 from_i) { |
10 | 202k | MVMSpeshFacts *tfacts = &g->facts[to_orig][to_i]; |
11 | 202k | MVMSpeshFacts *ffacts = &g->facts[from_orig][from_i]; |
12 | 202k | tfacts->flags = ffacts->flags; |
13 | 202k | tfacts->type = ffacts->type; |
14 | 202k | tfacts->decont_type = ffacts->decont_type; |
15 | 202k | tfacts->value = ffacts->value; |
16 | 202k | tfacts->log_guard = ffacts->log_guard; |
17 | 202k | } |
18 | | |
19 | | /* Called when one set of facts depend on another, allowing any log guard |
20 | | * that is to thank to be marked used as needed later on. */ |
21 | | void MVM_spesh_facts_depend(MVMThreadContext *tc, MVMSpeshGraph *g, |
22 | 20.3k | MVMSpeshFacts *target, MVMSpeshFacts *source) { |
23 | 20.3k | if (source->flags & MVM_SPESH_FACT_FROM_LOG_GUARD) { |
24 | 1.79k | target->flags |= MVM_SPESH_FACT_FROM_LOG_GUARD; |
25 | 1.79k | target->log_guard = source->log_guard; |
26 | 1.79k | } |
27 | 20.3k | } |
28 | | |
29 | | /* Handles object-creating instructions. */ |
30 | | static void create_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 obj_orig, |
31 | 17.1k | MVMuint16 obj_i, MVMuint16 type_orig, MVMuint16 type_i) { |
32 | 17.1k | MVMSpeshFacts *type_facts = &(g->facts[type_orig][type_i]); |
33 | 17.1k | MVMSpeshFacts *obj_facts = &(g->facts[obj_orig][obj_i]); |
34 | 17.1k | |
35 | 17.1k | /* The type is carried. */ |
36 | 17.1k | if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
37 | 17.0k | obj_facts->type = type_facts->type; |
38 | 17.0k | obj_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
39 | 17.0k | MVM_spesh_facts_depend(tc, g, obj_facts, type_facts); |
40 | 17.0k | } |
41 | 17.1k | |
42 | 17.1k | /* We know it's a concrete object. */ |
43 | 17.1k | obj_facts->flags |= MVM_SPESH_FACT_CONCRETE; |
44 | 17.1k | |
45 | 17.1k | /* If we know the type object, then we can check to see if |
46 | 17.1k | * it's a container type. */ |
47 | 17.1k | if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
48 | 17.0k | MVMObject *type = type_facts->type; |
49 | 17.0k | if (type && !STABLE(type)->container_spec) |
50 | 17.0k | obj_facts->flags |= MVM_SPESH_FACT_DECONTED; |
51 | 17.0k | } |
52 | 17.1k | } |
53 | | |
54 | | static void create_facts_with_type(MVMThreadContext *tc, MVMSpeshGraph *g, |
55 | | MVMuint16 obj_orig, MVMuint16 obj_i, |
56 | 0 | MVMObject *type) { |
57 | 0 | MVMSpeshFacts *obj_facts = &(g->facts[obj_orig][obj_i]); |
58 | 0 |
|
59 | 0 | /* The type is carried. */ |
60 | 0 | obj_facts->type = type; |
61 | 0 | obj_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
62 | 0 |
|
63 | 0 | /* We know it's a concrete object. */ |
64 | 0 | obj_facts->flags |= MVM_SPESH_FACT_CONCRETE; |
65 | 0 |
|
66 | 0 | /* If we know the type object, then we can check to see if |
67 | 0 | * it's a container type. */ |
68 | 0 | if (type && !STABLE(type)->container_spec) |
69 | 0 | obj_facts->flags |= MVM_SPESH_FACT_DECONTED; |
70 | 0 | } |
71 | | |
72 | | /* Adds facts from knowing the exact value being put into an object local. */ |
73 | | static void object_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 tgt_orig, |
74 | 68.2k | MVMuint16 tgt_i, MVMObject *obj) { |
75 | 68.2k | /* Ensure it's non-null. */ |
76 | 68.2k | if (!obj) |
77 | 3.15k | return; |
78 | 68.2k | |
79 | 68.2k | /* Set the value itself. */ |
80 | 65.0k | g->facts[tgt_orig][tgt_i].value.o = obj; |
81 | 65.0k | g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
82 | 65.0k | |
83 | 65.0k | /* We also know the type. */ |
84 | 65.0k | g->facts[tgt_orig][tgt_i].type = STABLE(obj)->WHAT; |
85 | 65.0k | g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
86 | 65.0k | |
87 | 65.0k | /* Set concreteness and decontainerized flags. */ |
88 | 65.0k | if (IS_CONCRETE(obj)) { |
89 | 202 | g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_CONCRETE; |
90 | 202 | if (!STABLE(obj)->container_spec) |
91 | 202 | g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_DECONTED; |
92 | 202 | } |
93 | 64.8k | else { |
94 | 64.8k | g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_TYPEOBJ | MVM_SPESH_FACT_DECONTED; |
95 | 64.8k | } |
96 | 65.0k | } |
97 | | |
98 | | /* Checks if there's a possible aliasing operation that could cause the |
99 | | * facts about the contents of a container to be invalid by the instruction |
100 | | * under consideration. Assumes the instruction is a decont with argument 1 |
101 | | * being the thing to decontainerize. */ |
102 | 159k | MVMint32 MVM_spesh_facts_decont_blocked_by_alias(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
103 | 159k | /* No facts or no writer means we can't know it's safe; block. */ |
104 | 159k | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
105 | 159k | if (!facts || !facts->writer) |
106 | 0 | return 1; |
107 | 159k | |
108 | 159k | /* Walk backwards over instructions. */ |
109 | 513k | while (ins->prev) { |
110 | 440k | ins = ins->prev; |
111 | 440k | |
112 | 440k | /* If we found the writer without anything blocking, we're good. */ |
113 | 440k | if (ins == facts->writer) |
114 | 79.7k | return 0; |
115 | 440k | |
116 | 440k | /* If there's an operation that may alias, blocked. */ |
117 | 360k | switch (ins->info->opcode) { |
118 | 6.06k | case MVM_OP_bindattr_o: |
119 | 6.06k | case MVM_OP_bindattrs_o: |
120 | 6.06k | case MVM_OP_assign: |
121 | 6.06k | case MVM_OP_assignunchecked: |
122 | 6.06k | case MVM_OP_assign_i: |
123 | 6.06k | case MVM_OP_assign_n: |
124 | 6.06k | case MVM_OP_assign_s: |
125 | 6.06k | case MVM_OP_sp_bind_o: |
126 | 6.06k | case MVM_OP_sp_p6obind_o: |
127 | 6.06k | return 1; |
128 | 360k | } |
129 | 360k | } |
130 | 159k | |
131 | 159k | /* We didn't find the writer in this basic block, meaning an invocation |
132 | 159k | * may have made it unsafe. Block. */ |
133 | 73.8k | return 1; |
134 | 159k | } |
135 | | |
136 | | /* Propagates information relating to decontainerization. */ |
137 | | static void decont_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins, |
138 | | MVMuint16 out_orig, MVMuint16 out_i, MVMuint16 in_orig, |
139 | 123k | MVMuint16 in_i) { |
140 | 123k | MVMSpeshFacts *out_facts = &(g->facts[out_orig][out_i]); |
141 | 123k | MVMSpeshFacts *in_facts = &(g->facts[in_orig][in_i]); |
142 | 123k | |
143 | 123k | /* If we know the original is decontainerized already, just copy its |
144 | 123k | * info. */ |
145 | 123k | MVMint32 in_flags = in_facts->flags; |
146 | 123k | if (in_flags & MVM_SPESH_FACT_DECONTED) |
147 | 82.1k | copy_facts(tc, g, out_orig, out_i, in_orig, in_i); |
148 | 123k | |
149 | 123k | /* We know the result is decontainerized. */ |
150 | 123k | out_facts->flags |= MVM_SPESH_FACT_DECONTED; |
151 | 123k | |
152 | 123k | /* We may also know the original was containerized, and have some facts |
153 | 123k | * about its contents. */ |
154 | 123k | if (!MVM_spesh_facts_decont_blocked_by_alias(tc, g, ins)) { |
155 | 58.6k | if (in_flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) { |
156 | 0 | out_facts->type = in_facts->decont_type; |
157 | 0 | out_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
158 | 0 | } |
159 | 58.6k | if (in_flags & MVM_SPESH_FACT_DECONT_CONCRETE) |
160 | 0 | out_facts->flags |= MVM_SPESH_FACT_CONCRETE; |
161 | 58.6k | else if (in_flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) |
162 | 0 | out_facts->flags |= MVM_SPESH_FACT_TYPEOBJ; |
163 | 58.6k | if (in_flags & (MVM_SPESH_FACT_KNOWN_DECONT_TYPE | |
164 | 58.6k | MVM_SPESH_FACT_DECONT_CONCRETE | |
165 | 58.6k | MVM_SPESH_FACT_DECONT_TYPEOBJ)) |
166 | 0 | MVM_spesh_facts_depend(tc, g, out_facts, in_facts); |
167 | 58.6k | } |
168 | 123k | } |
169 | | |
170 | | /* Looks up a wval and adds information based on it. */ |
171 | | static void wval_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 tgt_orig, |
172 | 52.8k | MVMuint16 tgt_i, MVMuint16 dep, MVMint64 idx) { |
173 | 52.8k | MVMCompUnit *cu = g->sf->body.cu; |
174 | 52.8k | if (dep < cu->body.num_scs) { |
175 | 52.8k | MVMSerializationContext *sc = MVM_sc_get_sc(tc, cu, dep); |
176 | 52.8k | if (sc) |
177 | 52.8k | object_facts(tc, g, tgt_orig, tgt_i, MVM_sc_try_get_object(tc, sc, idx)); |
178 | 52.8k | } |
179 | 52.8k | } |
180 | | |
181 | | /* Let's figure out what exact type of iter we'll get from an iter op */ |
182 | | static void iter_facts(MVMThreadContext *tc, MVMSpeshGraph *g, |
183 | | MVMuint16 out_orig, MVMuint16 out_i, |
184 | 1.28k | MVMuint16 in_orig, MVMuint16 in_i) { |
185 | 1.28k | MVMSpeshFacts *out_facts = &(g->facts[out_orig][out_i]); |
186 | 1.28k | MVMSpeshFacts *in_facts = &(g->facts[in_orig][in_i]); |
187 | 1.28k | |
188 | 1.28k | if (in_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
189 | 528 | switch (REPR(in_facts->type)->ID) { |
190 | 134 | case MVM_REPR_ID_VMArray: |
191 | 134 | out_facts->type = g->sf->body.cu->body.hll_config->array_iterator_type; |
192 | 134 | out_facts->flags |= MVM_SPESH_FACT_ARRAY_ITER; |
193 | 134 | break; |
194 | 125 | case MVM_REPR_ID_MVMHash: |
195 | 125 | case MVM_REPR_ID_MVMContext: |
196 | 125 | out_facts->type = g->sf->body.cu->body.hll_config->hash_iterator_type; |
197 | 125 | out_facts->flags |= MVM_SPESH_FACT_HASH_ITER; |
198 | 125 | break; |
199 | 269 | default: |
200 | 269 | return; |
201 | 528 | } |
202 | 259 | out_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE; |
203 | 259 | } |
204 | 1.28k | |
205 | 1.28k | } |
206 | | |
207 | | /* constant ops on literals give us a specialize-time-known value */ |
208 | 78.8k | static void literal_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
209 | 78.8k | MVMSpeshFacts *tgt_facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]; |
210 | 78.8k | switch (ins->info->opcode) { |
211 | 0 | case MVM_OP_const_i64: |
212 | 0 | tgt_facts->value.i = ins->operands[1].lit_i64; |
213 | 0 | break; |
214 | 0 | case MVM_OP_const_i32: |
215 | 0 | tgt_facts->value.i = ins->operands[1].lit_i32; |
216 | 0 | break; |
217 | 0 | case MVM_OP_const_i16: |
218 | 0 | tgt_facts->value.i = ins->operands[1].lit_i16; |
219 | 0 | break; |
220 | 0 | case MVM_OP_const_i8: |
221 | 0 | tgt_facts->value.i = ins->operands[1].lit_i8; |
222 | 0 | break; |
223 | 0 | case MVM_OP_const_n32: |
224 | 0 | tgt_facts->value.n = ins->operands[1].lit_n32; |
225 | 0 | break; |
226 | 0 | case MVM_OP_const_n64: |
227 | 0 | tgt_facts->value.n = ins->operands[1].lit_n64; |
228 | 0 | break; |
229 | 10 | case MVM_OP_const_i64_32: |
230 | 10 | tgt_facts->value.i = ins->operands[1].lit_i32; |
231 | 10 | break; |
232 | 27.3k | case MVM_OP_const_i64_16: |
233 | 27.3k | tgt_facts->value.i = ins->operands[1].lit_i16; |
234 | 27.3k | break; |
235 | 51.5k | case MVM_OP_const_s: |
236 | 51.5k | tgt_facts->value.s = MVM_cu_string(tc, g->sf->body.cu, |
237 | 51.5k | ins->operands[1].lit_str_idx); |
238 | 51.5k | break; |
239 | 0 | default: |
240 | 0 | return; |
241 | 78.8k | } |
242 | 78.8k | tgt_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
243 | 78.8k | } |
244 | | |
245 | | /* Discover facts from extops. */ |
246 | 0 | static void discover_extop(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
247 | 0 | MVMExtOpRecord *extops = g->sf->body.cu->body.extops; |
248 | 0 | MVMuint16 num_extops = g->sf->body.cu->body.num_extops; |
249 | 0 | MVMuint16 i; |
250 | 0 | for (i = 0; i < num_extops; i++) { |
251 | 0 | if (extops[i].info == ins->info) { |
252 | 0 | /* Found op; call its discovery function, if any. */ |
253 | 0 | if (extops[i].discover) |
254 | 0 | extops[i].discover(tc, g, ins); |
255 | 0 | return; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | } |
259 | | /* Allocates space for keeping track of guards inserted from logging, and |
260 | | * their usage. */ |
261 | 13.8k | static void allocate_log_guard_table(MVMThreadContext *tc, MVMSpeshGraph *g) { |
262 | 13.8k | g->log_guards = MVM_spesh_alloc(tc, g, g->num_log_slots * sizeof(MVMSpeshLogGuard)); |
263 | 13.8k | } |
264 | | |
265 | | /* Check for stability of what was logged, and if it looks sane then add facts |
266 | | * and turn the log instruction into a */ |
267 | 61.0k | static void log_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
268 | 61.0k | MVMObject *stable_value = NULL; |
269 | 61.0k | MVMObject *stable_cont = NULL; |
270 | 61.0k | MVMSpeshFacts *facts; |
271 | 61.0k | |
272 | 61.0k | /* See if all the recorded facts match up; a NULL means there was a code |
273 | 61.0k | * path that never reached making a log entry. */ |
274 | 61.0k | MVMuint16 log_start = ins->operands[1].lit_i16 * MVM_SPESH_LOG_RUNS; |
275 | 61.0k | MVMuint16 i; |
276 | 538k | for (i = log_start; i < log_start + MVM_SPESH_LOG_RUNS; i++) { |
277 | 479k | MVMObject *consider = (MVMObject *)g->log_slots[i]; |
278 | 479k | if (consider) { |
279 | 233k | if (!stable_value) { |
280 | 33.1k | stable_value = consider; |
281 | 33.1k | } |
282 | 200k | else if (STABLE(stable_value) != STABLE(consider) |
283 | 198k | || IS_CONCRETE(stable_value) != IS_CONCRETE(consider)) { |
284 | 2.07k | stable_value = NULL; |
285 | 2.07k | break; |
286 | 2.07k | } |
287 | 233k | } |
288 | 479k | } |
289 | 61.0k | if (!stable_value) |
290 | 29.9k | return; |
291 | 61.0k | |
292 | 61.0k | /* If the value is a container type, need to look inside of it. */ |
293 | 31.1k | if (STABLE(stable_value)->container_spec && IS_CONCRETE(stable_value)) { |
294 | 0 | MVMContainerSpec const *contspec = STABLE(stable_value)->container_spec; |
295 | 0 | if (!contspec->fetch_never_invokes) |
296 | 0 | return; |
297 | 0 | stable_cont = stable_value; |
298 | 0 | stable_value = NULL; |
299 | 0 | for (i = log_start; i < log_start + MVM_SPESH_LOG_RUNS; i++) { |
300 | 0 | MVMRegister r; |
301 | 0 | contspec->fetch(tc, stable_cont, &r); |
302 | 0 | if (r.o) { |
303 | 0 | if (!stable_value) { |
304 | 0 | stable_value = r.o; |
305 | 0 | } |
306 | 0 | else if (STABLE(stable_value) != STABLE(r.o) |
307 | 0 | || IS_CONCRETE(stable_value) != IS_CONCRETE(r.o)) { |
308 | 0 | stable_value = NULL; |
309 | 0 | break; |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | 0 | if (!stable_value) |
314 | 0 | return; |
315 | 0 | } |
316 | 31.1k | |
317 | 31.1k | /* Produce a guard op and set facts. */ |
318 | 31.1k | if (stable_cont) { |
319 | 0 | MVMSpeshOperand reg = ins->operands[0]; |
320 | 0 | MVMContainerSpec *cs = (MVMContainerSpec *) STABLE(stable_cont)->container_spec; |
321 | 0 | facts = &g->facts[reg.reg.orig][reg.reg.i]; |
322 | 0 | facts->type = STABLE(stable_cont)->WHAT; |
323 | 0 | facts->flags |= (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE | |
324 | 0 | MVM_SPESH_FACT_KNOWN_DECONT_TYPE); |
325 | 0 | facts->decont_type = STABLE(stable_value)->WHAT; |
326 | 0 |
|
327 | 0 | /* If this is a native container, we get away with testing |
328 | 0 | * against the STABLE only, as the NativeRef REPR has all |
329 | 0 | * interesting values in its REPRData. */ |
330 | 0 | if (cs->can_store(tc, stable_cont) && |
331 | 0 | (MVM_6model_container_iscont_i(tc, stable_cont) || |
332 | 0 | MVM_6model_container_iscont_n(tc, stable_cont) || |
333 | 0 | MVM_6model_container_iscont_s(tc, stable_cont))) { |
334 | 0 | facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]; |
335 | 0 | /*facts->type = STABLE(stable_value)->WHAT;*/ |
336 | 0 | facts->flags |= MVM_SPESH_FACT_RW_CONT; |
337 | 0 |
|
338 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_guardconc); |
339 | 0 |
|
340 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand)); |
341 | 0 | ins->operands[0] = reg; |
342 | 0 | ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_cont)); |
343 | 0 | } else { |
344 | 0 | if (cs->can_store(tc, stable_cont)) { |
345 | 0 | /* We could do stability testing on rw-ness too, but it's quite |
346 | 0 | * unlikely we'll have codepaths with a mix of readable and |
347 | 0 | * writable containers. */ |
348 | 0 | facts->flags |= MVM_SPESH_FACT_RW_CONT; |
349 | 0 | if (IS_CONCRETE(stable_value)) { |
350 | 0 | facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE; |
351 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_guardrwconc); |
352 | 0 | } |
353 | 0 | else { |
354 | 0 | facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ; |
355 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_guardrwtype); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | else { |
359 | 0 | if (IS_CONCRETE(stable_value)) { |
360 | 0 | facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE; |
361 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_guardcontconc); |
362 | 0 | } |
363 | 0 | else { |
364 | 0 | facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ; |
365 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_guardconttype); |
366 | 0 | } |
367 | 0 | } |
368 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
369 | 0 | ins->operands[0] = reg; |
370 | 0 | ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_cont)); |
371 | 0 | ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_value)); |
372 | 0 | } |
373 | 0 | } |
374 | 31.1k | else { |
375 | 31.1k | facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]; |
376 | 31.1k | facts->type = STABLE(stable_value)->WHAT; |
377 | 31.1k | facts->flags |= (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_DECONTED); |
378 | 31.1k | if (IS_CONCRETE(stable_value)) { |
379 | 27.9k | facts->flags |= MVM_SPESH_FACT_CONCRETE; |
380 | 27.9k | ins->info = MVM_op_get_op(MVM_OP_sp_guardconc); |
381 | 27.9k | } |
382 | 3.12k | else { |
383 | 3.12k | facts->flags |= MVM_SPESH_FACT_TYPEOBJ; |
384 | 3.12k | ins->info = MVM_op_get_op(MVM_OP_sp_guardtype); |
385 | 3.12k | } |
386 | 31.1k | ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_value)); |
387 | 31.1k | } |
388 | 31.1k | |
389 | 31.1k | /* Add entry in log guards table, and mark facts as depending on it. */ |
390 | 31.1k | g->log_guards[g->num_log_guards].ins = ins; |
391 | 31.1k | g->log_guards[g->num_log_guards].bb = bb; |
392 | 31.1k | facts->flags |= MVM_SPESH_FACT_FROM_LOG_GUARD; |
393 | 31.1k | facts->log_guard = g->num_log_guards; |
394 | 31.1k | g->num_log_guards++; |
395 | 31.1k | } |
396 | | |
397 | | /* Visits the blocks in dominator tree order, recursively. */ |
398 | | static void add_bb_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, |
399 | 263k | MVMint32 cur_deopt_idx) { |
400 | 263k | MVMint32 i, is_phi; |
401 | 263k | |
402 | 263k | /* Look for instructions that provide or propagate facts. */ |
403 | 263k | MVMSpeshIns *ins = bb->first_ins; |
404 | 2.06M | while (ins) { |
405 | 1.80M | /* See if there's a deopt annotation, and sync cur_deopt_idx. */ |
406 | 1.80M | MVMSpeshAnn *ann = ins->annotations; |
407 | 1.82M | while (ann) { |
408 | 120k | if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS || |
409 | 104k | ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS) { |
410 | 104k | cur_deopt_idx = ann->data.deopt_idx; |
411 | 104k | break; |
412 | 104k | } |
413 | 16.6k | ann = ann->next; |
414 | 16.6k | } |
415 | 1.80M | |
416 | 1.80M | /* Look through operands for reads and writes. */ |
417 | 1.80M | is_phi = ins->info->opcode == MVM_SSA_PHI; |
418 | 6.40M | for (i = 0; i < ins->info->num_operands; i++) { |
419 | 4.59M | /* Reads need usage tracking; if the read is after a deopt point |
420 | 4.59M | * relative to the write then give it an extra usage bump. */ |
421 | 4.59M | if ((is_phi && i > 0) |
422 | 3.09M | || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg)) { |
423 | 2.50M | MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]); |
424 | 1.43M | facts->usages += facts->deopt_idx == cur_deopt_idx ? 1 : 2; |
425 | 2.50M | } |
426 | 4.59M | |
427 | 4.59M | /* Writes need the current deopt index and the writing instruction |
428 | 4.59M | * to be specified. */ |
429 | 4.59M | if ((is_phi && i == 0) |
430 | 4.03M | || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg)) { |
431 | 1.41M | MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]); |
432 | 1.41M | facts->deopt_idx = cur_deopt_idx; |
433 | 1.41M | facts->writer = ins; |
434 | 1.41M | } |
435 | 4.59M | } |
436 | 1.80M | |
437 | 1.80M | /* Look for ops that are fact-interesting. */ |
438 | 1.80M | switch (ins->info->opcode) { |
439 | 6.97k | case MVM_OP_inc_i: |
440 | 6.97k | case MVM_OP_inc_u: |
441 | 6.97k | case MVM_OP_dec_i: |
442 | 6.97k | case MVM_OP_dec_u: |
443 | 6.97k | /* These all read as well as write a value, so bump usages. */ |
444 | 6.97k | g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i - 1].usages++; |
445 | 6.97k | break; |
446 | 120k | case MVM_OP_set: |
447 | 120k | copy_facts(tc, g, |
448 | 120k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
449 | 120k | ins->operands[1].reg.orig, ins->operands[1].reg.i); |
450 | 120k | break; |
451 | 6.43k | case MVM_OP_create: |
452 | 6.43k | create_facts(tc, g, |
453 | 6.43k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
454 | 6.43k | ins->operands[1].reg.orig, ins->operands[1].reg.i); |
455 | 6.43k | break; |
456 | 10.6k | case MVM_OP_box_s: |
457 | 10.6k | case MVM_OP_box_i: |
458 | 10.6k | case MVM_OP_box_n: { |
459 | 10.6k | MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]); |
460 | 10.6k | create_facts(tc, g, |
461 | 10.6k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
462 | 10.6k | ins->operands[2].reg.orig, ins->operands[2].reg.i); |
463 | 10.6k | target_facts->flags |= MVM_SPESH_FACT_KNOWN_BOX_SRC; |
464 | 10.6k | break; |
465 | 10.6k | } |
466 | 0 | case MVM_OP_add_I: |
467 | 0 | case MVM_OP_sub_I: |
468 | 0 | case MVM_OP_mul_I: |
469 | 0 | case MVM_OP_div_I: |
470 | 0 | case MVM_OP_mod_I: |
471 | 0 | create_facts(tc, g, |
472 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
473 | 0 | ins->operands[3].reg.orig, ins->operands[3].reg.i); |
474 | 0 | break; |
475 | 0 | case MVM_OP_neg_I: |
476 | 0 | case MVM_OP_abs_I: |
477 | 0 | create_facts(tc, g, |
478 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
479 | 0 | ins->operands[2].reg.orig, ins->operands[2].reg.i); |
480 | 0 | break; |
481 | 0 | case MVM_OP_bootint: |
482 | 0 | object_facts(tc, g, |
483 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
484 | 0 | tc->instance->boot_types.BOOTInt); |
485 | 0 | break; |
486 | 0 | case MVM_OP_bootnum: |
487 | 0 | object_facts(tc, g, |
488 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
489 | 0 | tc->instance->boot_types.BOOTNum); |
490 | 0 | break; |
491 | 0 | case MVM_OP_bootstr: |
492 | 0 | object_facts(tc, g, |
493 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
494 | 0 | tc->instance->boot_types.BOOTStr); |
495 | 0 | break; |
496 | 0 | case MVM_OP_bootarray: |
497 | 0 | object_facts(tc, g, |
498 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
499 | 0 | tc->instance->boot_types.BOOTArray); |
500 | 0 | break; |
501 | 429 | case MVM_OP_bootintarray: |
502 | 429 | object_facts(tc, g, |
503 | 429 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
504 | 429 | tc->instance->boot_types.BOOTIntArray); |
505 | 429 | break; |
506 | 0 | case MVM_OP_bootnumarray: |
507 | 0 | object_facts(tc, g, |
508 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
509 | 0 | tc->instance->boot_types.BOOTNumArray); |
510 | 0 | break; |
511 | 21 | case MVM_OP_bootstrarray: |
512 | 21 | object_facts(tc, g, |
513 | 21 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
514 | 21 | tc->instance->boot_types.BOOTStrArray); |
515 | 21 | break; |
516 | 0 | case MVM_OP_boothash: |
517 | 0 | object_facts(tc, g, |
518 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
519 | 0 | tc->instance->boot_types.BOOTHash); |
520 | 0 | break; |
521 | 8.65k | case MVM_OP_hllboxtype_i: |
522 | 8.65k | object_facts(tc, g, |
523 | 8.65k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
524 | 8.65k | g->sf->body.cu->body.hll_config->int_box_type); |
525 | 8.65k | break; |
526 | 263 | case MVM_OP_hllboxtype_n: |
527 | 263 | object_facts(tc, g, |
528 | 263 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
529 | 263 | g->sf->body.cu->body.hll_config->num_box_type); |
530 | 263 | break; |
531 | 1.77k | case MVM_OP_hllboxtype_s: |
532 | 1.77k | object_facts(tc, g, |
533 | 1.77k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
534 | 1.77k | g->sf->body.cu->body.hll_config->str_box_type); |
535 | 1.77k | break; |
536 | 3.31k | case MVM_OP_hlllist: |
537 | 3.31k | object_facts(tc, g, |
538 | 3.31k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
539 | 3.31k | g->sf->body.cu->body.hll_config->slurpy_array_type); |
540 | 3.31k | break; |
541 | 914 | case MVM_OP_hllhash: |
542 | 914 | object_facts(tc, g, |
543 | 914 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
544 | 914 | g->sf->body.cu->body.hll_config->slurpy_hash_type); |
545 | 914 | break; |
546 | 123k | case MVM_OP_decont: |
547 | 123k | decont_facts(tc, g, ins, |
548 | 123k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
549 | 123k | ins->operands[1].reg.orig, ins->operands[1].reg.i); |
550 | 123k | break; |
551 | 52.8k | case MVM_OP_wval: |
552 | 52.8k | wval_facts(tc, g, |
553 | 52.8k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
554 | 52.8k | ins->operands[1].lit_i16, ins->operands[2].lit_i16); |
555 | 52.8k | break; |
556 | 0 | case MVM_OP_wval_wide: |
557 | 0 | wval_facts(tc, g, |
558 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
559 | 0 | ins->operands[1].lit_i16, ins->operands[2].lit_i64); |
560 | 0 | break; |
561 | 1.28k | case MVM_OP_iter: |
562 | 1.28k | iter_facts(tc, g, |
563 | 1.28k | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
564 | 1.28k | ins->operands[1].reg.orig, ins->operands[1].reg.i); |
565 | 1.28k | break; |
566 | 0 | case MVM_OP_newexception: |
567 | 0 | create_facts_with_type(tc, g, |
568 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
569 | 0 | tc->instance->boot_types.BOOTException); |
570 | 0 | break; |
571 | 0 | case MVM_OP_getlexref_i: |
572 | 0 | case MVM_OP_getlexref_i32: |
573 | 0 | case MVM_OP_getlexref_i16: |
574 | 0 | case MVM_OP_getlexref_i8: |
575 | 0 | case MVM_OP_getlexref_u32: |
576 | 0 | case MVM_OP_getlexref_u16: |
577 | 0 | case MVM_OP_getlexref_u8: |
578 | 0 | create_facts_with_type(tc, g, |
579 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
580 | 0 | g->sf->body.cu->body.hll_config->int_lex_ref); |
581 | 0 | break; |
582 | 0 | case MVM_OP_getlexref_n: |
583 | 0 | case MVM_OP_getlexref_n32: |
584 | 0 | create_facts_with_type(tc, g, |
585 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
586 | 0 | g->sf->body.cu->body.hll_config->num_lex_ref); |
587 | 0 | break; |
588 | 0 | case MVM_OP_getlexref_s: |
589 | 0 | create_facts_with_type(tc, g, |
590 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
591 | 0 | g->sf->body.cu->body.hll_config->str_lex_ref); |
592 | 0 | break; |
593 | 0 | case MVM_OP_getattrref_i: |
594 | 0 | case MVM_OP_getattrsref_i: |
595 | 0 | create_facts_with_type(tc, g, |
596 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
597 | 0 | g->sf->body.cu->body.hll_config->int_attr_ref); |
598 | 0 | break; |
599 | 0 | case MVM_OP_getattrref_n: |
600 | 0 | case MVM_OP_getattrsref_n: |
601 | 0 | create_facts_with_type(tc, g, |
602 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
603 | 0 | g->sf->body.cu->body.hll_config->num_attr_ref); |
604 | 0 | break; |
605 | 0 | case MVM_OP_getattrref_s: |
606 | 0 | case MVM_OP_getattrsref_s: |
607 | 0 | create_facts_with_type(tc, g, |
608 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
609 | 0 | g->sf->body.cu->body.hll_config->str_attr_ref); |
610 | 0 | break; |
611 | 0 | case MVM_OP_atposref_i: |
612 | 0 | create_facts_with_type(tc, g, |
613 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
614 | 0 | g->sf->body.cu->body.hll_config->int_pos_ref); |
615 | 0 | break; |
616 | 0 | case MVM_OP_atposref_n: |
617 | 0 | create_facts_with_type(tc, g, |
618 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
619 | 0 | g->sf->body.cu->body.hll_config->num_pos_ref); |
620 | 0 | break; |
621 | 0 | case MVM_OP_atposref_s: |
622 | 0 | create_facts_with_type(tc, g, |
623 | 0 | ins->operands[0].reg.orig, ins->operands[0].reg.i, |
624 | 0 | g->sf->body.cu->body.hll_config->str_pos_ref); |
625 | 0 | break; |
626 | 0 |
|
627 | 78.8k | case MVM_OP_const_i64: |
628 | 78.8k | case MVM_OP_const_i32: |
629 | 78.8k | case MVM_OP_const_i16: |
630 | 78.8k | case MVM_OP_const_i8: |
631 | 78.8k | case MVM_OP_const_n64: |
632 | 78.8k | case MVM_OP_const_n32: |
633 | 78.8k | case MVM_OP_const_i64_32: |
634 | 78.8k | case MVM_OP_const_i64_16: |
635 | 78.8k | case MVM_OP_const_s: |
636 | 78.8k | literal_facts(tc, g, ins); |
637 | 78.8k | break; |
638 | 78.5k | case MVM_OP_sp_log: { |
639 | 78.5k | MVMuint16 po = ins->prev |
640 | 47.1k | ? ins->prev->info->opcode |
641 | 31.3k | : bb->pred[0]->last_ins->info->opcode; |
642 | 78.5k | if (po != MVM_OP_getlexstatic_o && po != MVM_OP_getlexperinvtype_o) |
643 | 61.0k | log_facts(tc, g, bb, ins); |
644 | 78.5k | break; |
645 | 78.8k | } |
646 | 1.30M | default: |
647 | 1.30M | if (ins->info->opcode == (MVMuint16)-1) |
648 | 0 | discover_extop(tc, g, ins); |
649 | 1.80M | } |
650 | 1.80M | ins = ins->next; |
651 | 1.80M | } |
652 | 263k | |
653 | 263k | /* Visit children. */ |
654 | 513k | for (i = 0; i < bb->num_children; i++) |
655 | 249k | add_bb_facts(tc, g, bb->children[i], cur_deopt_idx); |
656 | 263k | } |
657 | | |
658 | | /* Exception handlers that use a block to store the handler must not have the |
659 | | * instructions that install the block eliminated. This tweaks the usage of |
660 | | * them. */ |
661 | 13.8k | static void tweak_block_handler_usage(MVMThreadContext *tc, MVMSpeshGraph *g) { |
662 | 13.8k | MVMint32 i; |
663 | 18.2k | for (i = 0; i < g->sf->body.num_handlers; i++) { |
664 | 4.35k | if (g->sf->body.handlers[i].action == MVM_EX_ACTION_INVOKE) |
665 | 7 | g->facts[g->sf->body.handlers[i].block_reg][1].usages++; |
666 | 4.35k | } |
667 | 13.8k | } |
668 | | |
669 | | /* Kicks off fact discovery from the top of the (dominator) tree. */ |
670 | 13.8k | void MVM_spesh_facts_discover(MVMThreadContext *tc, MVMSpeshGraph *g) { |
671 | 13.8k | allocate_log_guard_table(tc, g); |
672 | 13.8k | add_bb_facts(tc, g, g->entry, -1); |
673 | 13.8k | tweak_block_handler_usage(tc, g); |
674 | 13.8k | } |