/home/travis/build/MoarVM/MoarVM/src/spesh/optimize.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* This is where the main optimization work on a spesh graph takes place, |
4 | | * using facts discovered during analysis. */ |
5 | | |
6 | | /* Writes to stderr about each inline that we perform. */ |
7 | | #define MVM_LOG_INLINES 0 |
8 | | |
9 | | /* Obtains facts for an operand, just directly accessing them without |
10 | | * inferring any kind of usage. */ |
11 | 13.0M | static MVMSpeshFacts * get_facts_direct(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) { |
12 | 13.0M | return &g->facts[o.reg.orig][o.reg.i]; |
13 | 13.0M | } |
14 | | |
15 | | /* Obtains facts for an operand, indicating they are being used. */ |
16 | 294k | MVMSpeshFacts * MVM_spesh_get_and_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) { |
17 | 294k | MVMSpeshFacts *facts = get_facts_direct(tc, g, o); |
18 | 294k | MVM_spesh_use_facts(tc, g, facts); |
19 | 294k | return facts; |
20 | 294k | } |
21 | | |
22 | | /* Obtains facts for an operand, but doesn't (yet) indicate usefulness */ |
23 | 579k | MVMSpeshFacts * MVM_spesh_get_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) { |
24 | 579k | return get_facts_direct(tc, g, o); |
25 | 579k | } |
26 | | |
27 | | /* Mark facts for an operand as being relied upon */ |
28 | 490k | void MVM_spesh_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshFacts *facts) { |
29 | 490k | if (facts->flags & MVM_SPESH_FACT_FROM_LOG_GUARD) |
30 | 78.1k | g->log_guards[facts->log_guard].used = 1; |
31 | 490k | if (facts->flags & MVM_SPESH_FACT_MERGED_WITH_LOG_GUARD) { |
32 | 3.78k | MVMSpeshIns *thePHI = facts->writer; |
33 | 3.78k | MVMuint32 op_i; |
34 | 3.78k | |
35 | 12.4k | for (op_i = 1; op_i < thePHI->info->num_operands; op_i++) { |
36 | 8.62k | MVM_spesh_get_and_use_facts(tc, g, thePHI->operands[op_i]); |
37 | 8.62k | } |
38 | 3.78k | } |
39 | 490k | } |
40 | | |
41 | | /* Obtains a string constant. */ |
42 | 63.7k | MVMString * MVM_spesh_get_string(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) { |
43 | 63.7k | return MVM_cu_string(tc, g->sf->body.cu, o.lit_str_idx); |
44 | 63.7k | } |
45 | | |
46 | | /* Copy facts between two register operands. */ |
47 | | static void copy_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand to, |
48 | 336k | MVMSpeshOperand from) { |
49 | 336k | MVMSpeshFacts *tfacts = get_facts_direct(tc, g, to); |
50 | 336k | MVMSpeshFacts *ffacts = get_facts_direct(tc, g, from); |
51 | 336k | tfacts->flags = ffacts->flags; |
52 | 336k | tfacts->type = ffacts->type; |
53 | 336k | tfacts->decont_type = ffacts->decont_type; |
54 | 336k | tfacts->value = ffacts->value; |
55 | 336k | tfacts->log_guard = ffacts->log_guard; |
56 | 336k | } |
57 | | |
58 | | /* Adds a value into a spesh slot and returns its index. |
59 | | * If a spesh slot already holds this value, return that instead */ |
60 | 28.0k | MVMint16 MVM_spesh_add_spesh_slot_try_reuse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) { |
61 | 28.0k | MVMint16 prev_slot; |
62 | 456k | for (prev_slot = 0; prev_slot < g->num_spesh_slots; prev_slot++) { |
63 | 439k | if (g->spesh_slots[prev_slot] == c) |
64 | 11.6k | return prev_slot; |
65 | 439k | } |
66 | 16.4k | return MVM_spesh_add_spesh_slot(tc, g, c); |
67 | 28.0k | } |
68 | | |
69 | | /* Adds a value into a spesh slot and returns its index. */ |
70 | 122k | MVMint16 MVM_spesh_add_spesh_slot(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) { |
71 | 122k | if (g->num_spesh_slots >= g->alloc_spesh_slots) { |
72 | 22.2k | g->alloc_spesh_slots += 8; |
73 | 22.2k | if (g->spesh_slots) |
74 | 10.1k | g->spesh_slots = MVM_realloc(g->spesh_slots, |
75 | 10.1k | g->alloc_spesh_slots * sizeof(MVMCollectable *)); |
76 | 22.2k | else |
77 | 12.0k | g->spesh_slots = MVM_malloc(g->alloc_spesh_slots * sizeof(MVMCollectable *)); |
78 | 22.2k | } |
79 | 122k | g->spesh_slots[g->num_spesh_slots] = c; |
80 | 122k | return g->num_spesh_slots++; |
81 | 122k | } |
82 | | |
83 | | static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, |
84 | | MVMSpeshIns *ins, MVMint32 type_operand); |
85 | | |
86 | 2.76k | static void optimize_findmeth_s_perhaps_constant(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
87 | 2.76k | MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]); |
88 | 2.76k | |
89 | 2.76k | if (name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
90 | 2.31k | if (name_facts->writer && name_facts->writer->info->opcode == MVM_OP_const_s) { |
91 | 2.31k | name_facts->usages--; |
92 | 2.31k | ins->info = MVM_op_get_op(MVM_OP_findmeth); |
93 | 2.31k | ins->operands[2].lit_i64 = 0; |
94 | 2.31k | ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx; |
95 | 2.31k | MVM_spesh_use_facts(tc, g, name_facts); |
96 | 2.31k | } |
97 | 2.31k | } |
98 | 2.76k | } |
99 | | |
100 | | /* Performs optimization on a method lookup. If we know the type that we'll |
101 | | * be dispatching on, resolve it right off. If not, add a cache. */ |
102 | 27.9k | static void optimize_method_lookup(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
103 | 27.9k | /* See if we can resolve the method right off due to knowing the type. */ |
104 | 27.9k | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
105 | 27.9k | MVMint32 resolved = 0; |
106 | 27.9k | if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
107 | 17.0k | /* Try to resolve. */ |
108 | 17.0k | MVMString *name = MVM_spesh_get_string(tc, g, ins->operands[2]); |
109 | 17.0k | MVMObject *meth = MVM_spesh_try_find_method(tc, obj_facts->type, name); |
110 | 17.0k | if (!MVM_is_null(tc, meth)) { |
111 | 16.3k | /* Could compile-time resolve the method. Add it in a spesh slot. */ |
112 | 16.3k | MVMint16 ss = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)meth); |
113 | 16.3k | |
114 | 16.3k | /* Tweak facts for the target, given we know the method. */ |
115 | 16.3k | MVMSpeshFacts *meth_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]); |
116 | 16.3k | meth_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
117 | 16.3k | meth_facts->value.o = meth; |
118 | 16.3k | |
119 | 16.3k | /* Update the instruction to grab the spesh slot. */ |
120 | 16.3k | ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); |
121 | 16.3k | ins->operands[1].lit_i16 = ss; |
122 | 16.3k | |
123 | 16.3k | resolved = 1; |
124 | 16.3k | |
125 | 16.3k | MVM_spesh_use_facts(tc, g, obj_facts); |
126 | 16.3k | obj_facts->usages--; |
127 | 16.3k | } |
128 | 17.0k | } |
129 | 27.9k | |
130 | 27.9k | /* If not, add space to cache a single type/method pair, to save hash |
131 | 27.9k | * lookups in the (common) monomorphic case, and rewrite to caching |
132 | 27.9k | * version of the instruction. */ |
133 | 27.9k | if (!resolved) { |
134 | 11.6k | MVMSpeshOperand *orig_o = ins->operands; |
135 | 11.6k | ins->info = MVM_op_get_op(MVM_OP_sp_findmeth); |
136 | 11.6k | ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand)); |
137 | 11.6k | memcpy(ins->operands, orig_o, 3 * sizeof(MVMSpeshOperand)); |
138 | 11.6k | ins->operands[3].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, NULL); |
139 | 11.6k | MVM_spesh_add_spesh_slot(tc, g, NULL); |
140 | 11.6k | } |
141 | 27.9k | } |
142 | | |
143 | | /* Sees if we can resolve an istype at compile time. */ |
144 | 1.53k | static void optimize_istype(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
145 | 1.53k | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
146 | 1.53k | MVMSpeshFacts *type_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]); |
147 | 1.53k | MVMSpeshFacts *result_facts; |
148 | 1.53k | |
149 | 1.53k | if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && |
150 | 1.44k | obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
151 | 368 | MVMint32 result; |
152 | 368 | if (!MVM_6model_try_cache_type_check(tc, obj_facts->type, type_facts->type, &result)) |
153 | 107 | return; |
154 | 261 | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
155 | 261 | result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
156 | 261 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
157 | 261 | ins->operands[1].lit_i16 = result; |
158 | 261 | result_facts->value.i = result; |
159 | 261 | |
160 | 261 | obj_facts->usages--; |
161 | 261 | type_facts->usages--; |
162 | 261 | MVM_spesh_use_facts(tc, g, obj_facts); |
163 | 261 | MVM_spesh_use_facts(tc, g, type_facts); |
164 | 261 | } |
165 | 1.53k | } |
166 | | |
167 | 1.62k | static void optimize_is_reprid(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
168 | 1.62k | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
169 | 1.62k | MVMuint32 wanted_repr_id; |
170 | 1.62k | MVMuint64 result_value; |
171 | 1.62k | |
172 | 1.62k | if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)) { |
173 | 783 | return; |
174 | 783 | } |
175 | 1.62k | |
176 | 840 | switch (ins->info->opcode) { |
177 | 273 | case MVM_OP_islist: wanted_repr_id = MVM_REPR_ID_VMArray; break; |
178 | 441 | case MVM_OP_ishash: wanted_repr_id = MVM_REPR_ID_MVMHash; break; |
179 | 0 | case MVM_OP_isint: wanted_repr_id = MVM_REPR_ID_P6int; break; |
180 | 0 | case MVM_OP_isnum: wanted_repr_id = MVM_REPR_ID_P6num; break; |
181 | 126 | case MVM_OP_isstr: wanted_repr_id = MVM_REPR_ID_P6str; break; |
182 | 0 | default: return; |
183 | 840 | } |
184 | 840 | |
185 | 840 | MVM_spesh_use_facts(tc, g, obj_facts); |
186 | 840 | |
187 | 840 | result_value = REPR(obj_facts->type)->ID == wanted_repr_id; |
188 | 840 | |
189 | 840 | if (result_value == 0) { |
190 | 553 | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
191 | 553 | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
192 | 553 | ins->operands[1].lit_i16 = 0; |
193 | 553 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
194 | 553 | result_facts->value.i = 0; |
195 | 287 | } else { |
196 | 287 | ins->info = MVM_op_get_op(MVM_OP_isnonnull); |
197 | 287 | } |
198 | 840 | } |
199 | | |
200 | 882 | static void optimize_gethow(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
201 | 882 | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
202 | 882 | MVMObject *how_obj = NULL; |
203 | 882 | if (obj_facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE)) |
204 | 837 | how_obj = MVM_spesh_try_get_how(tc, obj_facts->type); |
205 | 882 | /* There may be other valid ways to get the facts (known value?) */ |
206 | 882 | if (how_obj) { |
207 | 764 | MVMSpeshFacts *how_facts; |
208 | 764 | /* Transform gethow lookup to spesh slot lookup */ |
209 | 764 | MVMint16 spesh_slot = MVM_spesh_add_spesh_slot_try_reuse(tc, g, (MVMCollectable*)how_obj); |
210 | 764 | MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--; |
211 | 764 | ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); |
212 | 764 | ins->operands[1].lit_i16 = spesh_slot; |
213 | 764 | /* Store facts about the value in the write operand */ |
214 | 764 | how_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
215 | 764 | how_facts->flags |= (MVM_SPESH_FACT_KNOWN_VALUE | MVM_SPESH_FACT_KNOWN_TYPE); |
216 | 764 | how_facts->value.o = how_obj; |
217 | 764 | how_facts->type = STABLE(how_obj)->WHAT; |
218 | 764 | } |
219 | 882 | } |
220 | | |
221 | | |
222 | | /* Sees if we can resolve an isconcrete at compile time. */ |
223 | 5.35k | static void optimize_isconcrete(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
224 | 5.35k | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
225 | 5.35k | if (obj_facts->flags & (MVM_SPESH_FACT_CONCRETE | MVM_SPESH_FACT_TYPEOBJ)) { |
226 | 3.30k | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
227 | 3.30k | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
228 | 3.30k | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
229 | 3.30k | result_facts->value.i = obj_facts->flags & MVM_SPESH_FACT_CONCRETE ? 1 : 0; |
230 | 3.30k | ins->operands[1].lit_i16 = result_facts->value.i; |
231 | 3.30k | |
232 | 3.30k | MVM_spesh_use_facts(tc, g, obj_facts); |
233 | 3.30k | MVM_spesh_facts_depend(tc, g, result_facts, obj_facts); |
234 | 3.30k | |
235 | 3.30k | obj_facts->usages--; |
236 | 3.30k | } |
237 | 5.35k | } |
238 | | |
239 | 0 | static void optimize_exception_ops(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
240 | 0 | MVMuint16 op = ins->info->opcode; |
241 | 0 |
|
242 | 0 | if (op == MVM_OP_newexception) { |
243 | 0 | MVMSpeshOperand target = ins->operands[0]; |
244 | 0 | MVMObject *type = tc->instance->boot_types.BOOTException; |
245 | 0 | MVMSTable *st = STABLE(type); |
246 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_fastcreate); |
247 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
248 | 0 | ins->operands[0] = target; |
249 | 0 | ins->operands[1].lit_i16 = st->size; |
250 | 0 | ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)st); |
251 | 0 | } else { |
252 | 0 | /* |
253 | 0 | MVMSpeshFacts *target_facts; |
254 | 0 | */ |
255 | 0 |
|
256 | 0 | /* XXX This currently still causes problems. */ |
257 | 0 | return; |
258 | 0 |
|
259 | 0 | /* |
260 | 0 | switch (op) { |
261 | 0 | case MVM_OP_bindexmessage: |
262 | 0 | case MVM_OP_bindexpayload: { |
263 | 0 | MVMSpeshOperand target = ins->operands[0]; |
264 | 0 | MVMSpeshOperand value = ins->operands[1]; |
265 | 0 | target_facts = MVM_spesh_get_facts(tc, g, target); |
266 | 0 |
|
267 | 0 | if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) |
268 | 0 | || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException)) |
269 | 0 | break; |
270 | 0 |
|
271 | 0 | ins->info = MVM_op_get_op(op == MVM_OP_bindexmessage ? MVM_OP_sp_bind_s : MVM_OP_sp_bind_o); |
272 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
273 | 0 | ins->operands[0] = target; |
274 | 0 | ins->operands[1].lit_i16 = op == MVM_OP_bindexmessage ? offsetof(MVMException, body.message) |
275 | 0 | : offsetof(MVMException, body.payload); |
276 | 0 | ins->operands[2] = value; |
277 | 0 | break; |
278 | 0 | } |
279 | 0 | case MVM_OP_bindexcategory: { |
280 | 0 | MVMSpeshOperand target = ins->operands[0]; |
281 | 0 | MVMSpeshOperand category = ins->operands[1]; |
282 | 0 | target_facts = MVM_spesh_get_facts(tc, g, target); |
283 | 0 |
|
284 | 0 | if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) |
285 | 0 | || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException)) |
286 | 0 | break; |
287 | 0 |
|
288 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_bind_i32); |
289 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
290 | 0 | ins->operands[0] = target; |
291 | 0 | ins->operands[1].lit_i16 = offsetof(MVMException, body.category); |
292 | 0 | ins->operands[2] = category; |
293 | 0 | break; |
294 | 0 | } |
295 | 0 | case MVM_OP_getexmessage: |
296 | 0 | case MVM_OP_getexpayload: { |
297 | 0 | MVMSpeshOperand destination = ins->operands[0]; |
298 | 0 | MVMSpeshOperand target = ins->operands[1]; |
299 | 0 | target_facts = MVM_spesh_get_facts(tc, g, target); |
300 | 0 |
|
301 | 0 | if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) |
302 | 0 | || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException)) |
303 | 0 | break; |
304 | 0 |
|
305 | 0 | ins->info = MVM_op_get_op(op == MVM_OP_getexmessage ? MVM_OP_sp_get_s : MVM_OP_sp_get_o); |
306 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
307 | 0 | ins->operands[0] = destination; |
308 | 0 | ins->operands[1] = target; |
309 | 0 | ins->operands[2].lit_i16 = op == MVM_OP_getexmessage ? offsetof(MVMException, body.message) |
310 | 0 | : offsetof(MVMException, body.payload); |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | case MVM_OP_getexcategory: { |
314 | 0 | MVMSpeshOperand destination = ins->operands[0]; |
315 | 0 | MVMSpeshOperand target = ins->operands[1]; |
316 | 0 | target_facts = MVM_spesh_get_facts(tc, g, target); |
317 | 0 |
|
318 | 0 | if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) |
319 | 0 | || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException)) |
320 | 0 | break; |
321 | 0 |
|
322 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_get_i32); |
323 | 0 | ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
324 | 0 | ins->operands[0] = destination; |
325 | 0 | ins->operands[1] = target; |
326 | 0 | ins->operands[2].lit_i16 = offsetof(MVMException, body.category); |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | */ |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | /* iffy ops that operate on a known value register can turn into goto |
335 | | * or be dropped. */ |
336 | 55.9k | static void optimize_iffy(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins, MVMSpeshBB *bb) { |
337 | 55.9k | MVMSpeshFacts *flag_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
338 | 55.9k | MVMuint8 negated_op; |
339 | 55.9k | MVMuint8 truthvalue; |
340 | 55.9k | |
341 | 55.9k | switch (ins->info->opcode) { |
342 | 18.3k | case MVM_OP_if_i: |
343 | 18.3k | case MVM_OP_if_s: |
344 | 18.3k | case MVM_OP_if_n: |
345 | 18.3k | case MVM_OP_if_o: |
346 | 18.3k | case MVM_OP_ifnonnull: |
347 | 18.3k | negated_op = 0; |
348 | 18.3k | break; |
349 | 37.5k | case MVM_OP_unless_i: |
350 | 37.5k | case MVM_OP_unless_s: |
351 | 37.5k | case MVM_OP_unless_n: |
352 | 37.5k | case MVM_OP_unless_o: |
353 | 37.5k | negated_op = 1; |
354 | 37.5k | break; |
355 | 0 | default: |
356 | 0 | return; |
357 | 55.9k | } |
358 | 55.9k | |
359 | 55.9k | if (flag_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
360 | 2.85k | switch (ins->info->opcode) { |
361 | 2.85k | case MVM_OP_if_i: |
362 | 2.85k | case MVM_OP_unless_i: |
363 | 2.85k | truthvalue = flag_facts->value.i; |
364 | 2.85k | break; |
365 | 0 | case MVM_OP_if_o: |
366 | 0 | case MVM_OP_unless_o: { |
367 | 0 | MVMObject *objval = flag_facts->value.o; |
368 | 0 | MVMBoolificationSpec *bs = objval->st->boolification_spec; |
369 | 0 | MVMRegister resultreg; |
370 | 0 | switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) { |
371 | 0 | case MVM_BOOL_MODE_UNBOX_INT: |
372 | 0 | case MVM_BOOL_MODE_UNBOX_NUM: |
373 | 0 | case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY: |
374 | 0 | case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY_OR_ZERO: |
375 | 0 | case MVM_BOOL_MODE_BIGINT: |
376 | 0 | case MVM_BOOL_MODE_ITER: |
377 | 0 | case MVM_BOOL_MODE_HAS_ELEMS: |
378 | 0 | case MVM_BOOL_MODE_NOT_TYPE_OBJECT: |
379 | 0 | MVM_coerce_istrue(tc, objval, &resultreg, NULL, NULL, 0); |
380 | 0 | truthvalue = resultreg.i64; |
381 | 0 | break; |
382 | 0 | case MVM_BOOL_MODE_CALL_METHOD: |
383 | 0 | default: |
384 | 0 | return; |
385 | 0 | } |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | case MVM_OP_if_n: |
389 | 0 | case MVM_OP_unless_n: |
390 | 0 | truthvalue = flag_facts->value.n != 0.0; |
391 | 0 | break; |
392 | 0 | default: |
393 | 0 | return; |
394 | 2.85k | } |
395 | 2.85k | |
396 | 2.85k | MVM_spesh_use_facts(tc, g, flag_facts); |
397 | 2.85k | flag_facts->usages--; |
398 | 2.85k | |
399 | 1.59k | truthvalue = truthvalue ? 1 : 0; |
400 | 2.85k | if (truthvalue != negated_op) { |
401 | 1.66k | /* this conditional can be turned into an unconditional jump */ |
402 | 1.66k | ins->info = MVM_op_get_op(MVM_OP_goto); |
403 | 1.66k | ins->operands[0] = ins->operands[1]; |
404 | 1.66k | |
405 | 1.66k | /* since we have an unconditional jump now, we can remove the successor |
406 | 1.66k | * that's in the linear_next */ |
407 | 1.66k | MVM_spesh_manipulate_remove_successor(tc, bb, bb->linear_next); |
408 | 1.19k | } else { |
409 | 1.19k | /* this conditional can be dropped completely */ |
410 | 1.19k | MVM_spesh_manipulate_remove_successor(tc, bb, ins->operands[1].ins_bb); |
411 | 1.19k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
412 | 1.19k | } |
413 | 2.85k | return; |
414 | 2.85k | } |
415 | 55.9k | /* Sometimes our code-gen ends up boxing an integer and immediately |
416 | 55.9k | * calling if_o or unless_o on it. If we if_i/unless_i/... instead, |
417 | 55.9k | * we can get rid of the unboxing and perhaps the boxing as well. */ |
418 | 53.0k | if ((ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o) |
419 | 7.33k | && flag_facts->flags & MVM_SPESH_FACT_KNOWN_BOX_SRC && flag_facts->writer) { |
420 | 16 | /* We may have to go through several layers of set instructions to find |
421 | 16 | * the proper writer. */ |
422 | 16 | MVMSpeshIns *cur = flag_facts->writer; |
423 | 37 | while (cur && cur->info->opcode == MVM_OP_set) { |
424 | 21 | cur = MVM_spesh_get_facts(tc, g, cur->operands[1])->writer; |
425 | 21 | } |
426 | 16 | |
427 | 16 | if (cur) { |
428 | 16 | MVMSpeshIns *safety_cur; |
429 | 16 | MVMuint8 orig_operand_type = cur->info->operands[1] & MVM_operand_type_mask; |
430 | 16 | MVMuint8 succ = 0; |
431 | 16 | |
432 | 16 | /* now we have to be extra careful. any operation that writes to |
433 | 16 | * our "unboxed flag" register (in any register version) will be |
434 | 16 | * trouble. Also, we'd have to take more care with PHI nodes, |
435 | 16 | * which we'll just consider immediate failure for now. */ |
436 | 16 | |
437 | 16 | safety_cur = ins; |
438 | 141 | while (safety_cur) { |
439 | 131 | if (safety_cur == cur) { |
440 | 1 | /* If we've made it to here without finding anything |
441 | 1 | * dangerous, we can consider this optimization |
442 | 1 | * a winner. */ |
443 | 1 | break; |
444 | 1 | } |
445 | 130 | if (safety_cur->info->opcode == MVM_SSA_PHI) { |
446 | 5 | /* Oh dear god in heaven! A PHI! */ |
447 | 5 | safety_cur = NULL; |
448 | 5 | break; |
449 | 5 | } |
450 | 125 | if (((safety_cur->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg) |
451 | 91 | && (safety_cur->operands[0].reg.orig == cur->operands[1].reg.orig)) { |
452 | 0 | /* Someone's clobbering our register between the boxing and |
453 | 0 | * our attempt to unbox it. we shall give up. |
454 | 0 | * Maybe in the future we can be clever/sneaky and use |
455 | 0 | * some other register for bridging the gap? */ |
456 | 0 | safety_cur = NULL; |
457 | 0 | break; |
458 | 0 | } |
459 | 125 | safety_cur = safety_cur->prev; |
460 | 125 | } |
461 | 16 | |
462 | 16 | if (safety_cur) { |
463 | 1 | switch (orig_operand_type) { |
464 | 1 | case MVM_operand_int64: |
465 | 1 | ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i); |
466 | 1 | succ = 1; |
467 | 1 | break; |
468 | 0 | case MVM_operand_num64: |
469 | 0 | ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_n : MVM_OP_if_n); |
470 | 0 | succ = 1; |
471 | 0 | break; |
472 | 0 | case MVM_operand_str: |
473 | 0 | ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_s : MVM_OP_if_s); |
474 | 0 | succ = 1; |
475 | 0 | break; |
476 | 1 | } |
477 | 1 | |
478 | 1 | if (succ) { |
479 | 1 | ins->operands[0] = cur->operands[1]; |
480 | 1 | flag_facts->usages--; |
481 | 1 | MVM_spesh_get_and_use_facts(tc, g, cur->operands[1])->usages++; |
482 | 1 | optimize_iffy(tc, g, ins, bb); |
483 | 1 | return; |
484 | 1 | } |
485 | 1 | } |
486 | 16 | } |
487 | 16 | } |
488 | 53.0k | if (flag_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && flag_facts->type) { |
489 | 1.02k | if (ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o) { |
490 | 1.02k | MVMObject *type = flag_facts->type; |
491 | 1.02k | MVMBoolificationSpec *bs = type->st->boolification_spec; |
492 | 1.02k | MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64); |
493 | 1.02k | |
494 | 1.02k | MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
495 | 1.02k | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); |
496 | 1.02k | |
497 | 1.02k | MVMuint8 guaranteed_concrete = flag_facts->flags & MVM_SPESH_FACT_CONCRETE; |
498 | 1.02k | |
499 | 1.02k | switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) { |
500 | 0 | case MVM_BOOL_MODE_ITER: |
501 | 0 | if (!guaranteed_concrete) |
502 | 0 | return; |
503 | 0 | if (flag_facts->flags & MVM_SPESH_FACT_ARRAY_ITER) { |
504 | 0 | new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_arr); |
505 | 0 | } else if (flag_facts->flags & MVM_SPESH_FACT_HASH_ITER) { |
506 | 0 | new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_hash); |
507 | 0 | } else { |
508 | 0 | new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter); |
509 | 0 | } |
510 | 0 | break; |
511 | 757 | case MVM_BOOL_MODE_UNBOX_INT: |
512 | 757 | if (!guaranteed_concrete) |
513 | 0 | return; |
514 | 757 | new_ins->info = MVM_op_get_op(MVM_OP_unbox_i); |
515 | 757 | break; |
516 | 757 | /* we need to change the register type for our temporary register for this. |
517 | 757 | case MVM_BOOL_MODE_UNBOX_NUM: |
518 | 757 | new_ins->info = MVM_op_get_op(MVM_OP_unbox_i); |
519 | 757 | break; |
520 | 757 | */ |
521 | 0 | case MVM_BOOL_MODE_BIGINT: |
522 | 0 | if (!guaranteed_concrete) |
523 | 0 | return; |
524 | 0 | new_ins->info = MVM_op_get_op(MVM_OP_bool_I); |
525 | 0 | break; |
526 | 33 | case MVM_BOOL_MODE_HAS_ELEMS: |
527 | 33 | if (!guaranteed_concrete) |
528 | 0 | return; |
529 | 33 | new_ins->info = MVM_op_get_op(MVM_OP_elems); |
530 | 33 | break; |
531 | 124 | case MVM_BOOL_MODE_NOT_TYPE_OBJECT: |
532 | 124 | new_ins->info = MVM_op_get_op(MVM_OP_isconcrete); |
533 | 124 | break; |
534 | 112 | default: |
535 | 112 | return; |
536 | 1.02k | } |
537 | 1.02k | |
538 | 914 | operands[0] = temp; |
539 | 914 | operands[1] = ins->operands[0]; |
540 | 914 | new_ins->operands = operands; |
541 | 914 | |
542 | 725 | ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i); |
543 | 914 | ins->operands[0] = temp; |
544 | 914 | |
545 | 914 | MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, new_ins); |
546 | 914 | |
547 | 914 | MVM_spesh_get_facts(tc, g, temp)->usages++; |
548 | 914 | |
549 | 914 | MVM_spesh_use_facts(tc, g, flag_facts); |
550 | 914 | |
551 | 914 | MVM_spesh_manipulate_release_temp_reg(tc, g, temp); |
552 | 0 | } else { |
553 | 0 | return; |
554 | 0 | } |
555 | 52.0k | } else { |
556 | 52.0k | return; |
557 | 52.0k | } |
558 | 53.0k | |
559 | 53.0k | } |
560 | | |
561 | | /* objprimspec can be done at spesh-time if we know the type of something. |
562 | | * Another thing is, that if we rely on the type being known, we'll be assured |
563 | | * we'll have a guard that promises the object in question to be non-null. */ |
564 | 24 | static void optimize_objprimspec(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
565 | 24 | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
566 | 24 | |
567 | 24 | if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) { |
568 | 21 | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
569 | 21 | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
570 | 21 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
571 | 21 | result_facts->value.i = REPR(obj_facts->type)->get_storage_spec(tc, STABLE(obj_facts->type))->boxed_primitive; |
572 | 21 | ins->operands[1].lit_i16 = result_facts->value.i; |
573 | 21 | |
574 | 21 | MVM_spesh_use_facts(tc, g, obj_facts); |
575 | 21 | obj_facts->usages--; |
576 | 21 | } |
577 | 24 | } |
578 | | |
579 | | /* Optimizes a hllize instruction away if the type is known and already in the |
580 | | * right HLL, by turning it into a set. */ |
581 | 0 | static void optimize_hllize(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
582 | 0 | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
583 | 0 | if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) { |
584 | 0 | if (STABLE(obj_facts->type)->hll_owner == g->sf->body.cu->body.hll_config) { |
585 | 0 | ins->info = MVM_op_get_op(MVM_OP_set); |
586 | 0 |
|
587 | 0 | MVM_spesh_use_facts(tc, g, obj_facts); |
588 | 0 |
|
589 | 0 | copy_facts(tc, g, ins->operands[0], ins->operands[1]); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | | /* Turns a decont into a set, if we know it's not needed. Also make sure we |
595 | | * propagate any needed information. */ |
596 | 123k | static void optimize_decont(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
597 | 123k | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
598 | 123k | if (obj_facts->flags & (MVM_SPESH_FACT_DECONTED | MVM_SPESH_FACT_TYPEOBJ)) { |
599 | 86.9k | ins->info = MVM_op_get_op(MVM_OP_set); |
600 | 86.9k | |
601 | 86.9k | MVM_spesh_use_facts(tc, g, obj_facts); |
602 | 86.9k | |
603 | 86.9k | copy_facts(tc, g, ins->operands[0], ins->operands[1]); |
604 | 86.9k | } |
605 | 36.3k | else { |
606 | 36.3k | if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) { |
607 | 875 | MVMSTable *stable = STABLE(obj_facts->type); |
608 | 875 | MVMContainerSpec const *contspec = stable->container_spec; |
609 | 875 | if (contspec && contspec->fetch_never_invokes && contspec->spesh) { |
610 | 0 | contspec->spesh(tc, stable, g, bb, ins); |
611 | 0 | MVM_spesh_use_facts(tc, g, obj_facts); |
612 | 0 | } |
613 | 875 | } |
614 | 36.3k | |
615 | 36.3k | if (!MVM_spesh_facts_decont_blocked_by_alias(tc, g, ins)) { |
616 | 21.0k | MVMSpeshFacts *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
617 | 21.0k | int set_facts = 0; |
618 | 21.0k | if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) { |
619 | 0 | res_facts->type = obj_facts->decont_type; |
620 | 0 | res_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
621 | 0 | set_facts = 1; |
622 | 0 | } |
623 | 21.0k | if (obj_facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) { |
624 | 0 | res_facts->flags |= MVM_SPESH_FACT_CONCRETE; |
625 | 0 | set_facts = 1; |
626 | 0 | } |
627 | 21.0k | else if (obj_facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) { |
628 | 0 | res_facts->flags |= MVM_SPESH_FACT_TYPEOBJ; |
629 | 0 | set_facts = 1; |
630 | 0 | } |
631 | 21.0k | if (set_facts) |
632 | 0 | MVM_spesh_facts_depend(tc, g, res_facts, obj_facts); |
633 | 21.0k | } |
634 | 36.3k | } |
635 | 123k | } |
636 | | |
637 | | /* Checks like iscont, iscont_[ins] and isrwcont can be done at spesh time */ |
638 | 0 | static void optimize_container_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
639 | 0 | if (ins->info->opcode == MVM_OP_isrwcont) { |
640 | 0 | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
641 | 0 |
|
642 | 0 | if (facts->flags & MVM_SPESH_FACT_RW_CONT) { |
643 | 0 | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
644 | 0 | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
645 | 0 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
646 | 0 | result_facts->value.i = 1; |
647 | 0 | ins->operands[1].lit_i16 = 1; |
648 | 0 |
|
649 | 0 | MVM_spesh_use_facts(tc, g, facts); |
650 | 0 | facts->usages--; |
651 | 0 | } |
652 | 0 | } |
653 | 0 | } |
654 | | |
655 | | /* Optimize away assertparamcheck if we know it will pass. */ |
656 | 0 | static void optimize_assertparamcheck(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
657 | 0 | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
658 | 0 | if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE && facts->value.i) { |
659 | 0 | MVM_spesh_use_facts(tc, g, facts); |
660 | 0 | facts->usages--; |
661 | 0 | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | 475 | static void optimize_can_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
666 | 475 | /* This used to cause problems, Spesh: failed to fix up handlers (-1, 110, 110) */ |
667 | 475 | MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
668 | 475 | MVMString *method_name; |
669 | 475 | MVMint64 can_result; |
670 | 475 | |
671 | 475 | if (ins->info->opcode == MVM_OP_can_s) { |
672 | 475 | MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]); |
673 | 475 | if (!(name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)) { |
674 | 160 | return; |
675 | 160 | } |
676 | 315 | method_name = name_facts->value.s; |
677 | 315 | |
678 | 315 | name_facts->usages--; |
679 | 315 | ins->info = MVM_op_get_op(MVM_OP_can); |
680 | 315 | ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx; |
681 | 0 | } else { |
682 | 0 | method_name = MVM_spesh_get_string(tc, g, ins->operands[2]); |
683 | 0 | } |
684 | 475 | |
685 | 315 | if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) || !obj_facts->type) { |
686 | 68 | return; |
687 | 68 | } |
688 | 315 | |
689 | 247 | if (MVM_is_null(tc, obj_facts->type)) |
690 | 87 | can_result = 0; /* VMNull can't have any methods. */ |
691 | 247 | else |
692 | 160 | can_result = MVM_spesh_try_can_method(tc, obj_facts->type, method_name); |
693 | 247 | |
694 | 247 | if (can_result == -1) { |
695 | 0 | return; |
696 | 247 | } else { |
697 | 247 | MVMSpeshFacts *result_facts; |
698 | 247 | |
699 | 247 | if (ins->info->opcode == MVM_OP_can_s) |
700 | 0 | MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--; |
701 | 247 | |
702 | 247 | result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
703 | 247 | ins->info = MVM_op_get_op(MVM_OP_const_i64_16); |
704 | 247 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
705 | 247 | ins->operands[1].lit_i16 = can_result; |
706 | 247 | result_facts->value.i = can_result; |
707 | 247 | |
708 | 247 | obj_facts->usages--; |
709 | 247 | MVM_spesh_use_facts(tc, g, obj_facts); |
710 | 247 | } |
711 | 247 | } |
712 | | |
713 | | /* If we have a const_i and a coerce_in, we can emit a const_n instead. */ |
714 | 6.58k | static void optimize_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
715 | 6.58k | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
716 | 6.58k | |
717 | 6.58k | if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
718 | 1.45k | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
719 | 1.45k | MVMnum64 result = facts->value.i; |
720 | 1.45k | |
721 | 1.45k | MVM_spesh_use_facts(tc, g, facts); |
722 | 1.45k | facts->usages--; |
723 | 1.45k | |
724 | 1.45k | ins->info = MVM_op_get_op(MVM_OP_const_n64); |
725 | 1.45k | ins->operands[1].lit_n64 = result; |
726 | 1.45k | |
727 | 1.45k | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
728 | 1.45k | result_facts->value.n = result; |
729 | 1.45k | } |
730 | 6.58k | } |
731 | | |
732 | | /* If we know the type of a significant operand, we might try to specialize by |
733 | | * representation. */ |
734 | | static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, |
735 | 130k | MVMSpeshIns *ins, MVMint32 type_operand) { |
736 | 130k | /* Immediately mark guards as used, as the JIT would like to devirtualize |
737 | 130k | * repr ops later and we don't want guards to be thrown out before that */ |
738 | 130k | MVMSpeshFacts *facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[type_operand]); |
739 | 130k | if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && facts->type) |
740 | 90.8k | if (REPR(facts->type)->spesh) { |
741 | 76.7k | REPR(facts->type)->spesh(tc, STABLE(facts->type), g, bb, ins); |
742 | 76.7k | MVM_spesh_use_facts(tc, g, facts); |
743 | 76.7k | } |
744 | 130k | } |
745 | | |
746 | | /* smrt_strify and smrt_numify can turn into unboxes, but at least |
747 | | * for smrt_numify it's "complicated". Also, later when we know how |
748 | | * to put new invocations into spesh'd code, we could make direct |
749 | | * invoke calls to the .Str and .Num methods. |
750 | | */ |
751 | 19.8k | static void optimize_smart_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
752 | 19.8k | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
753 | 19.8k | |
754 | 19.8k | MVMuint16 is_strify = ins->info->opcode == MVM_OP_smrt_strify; |
755 | 19.8k | |
756 | 19.8k | if (facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE) && facts->type) { |
757 | 9.52k | const MVMStorageSpec *ss; |
758 | 9.52k | MVMint64 can_result; |
759 | 9.52k | |
760 | 9.52k | ss = REPR(facts->type)->get_storage_spec(tc, STABLE(facts->type)); |
761 | 9.52k | |
762 | 9.52k | if (is_strify && ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) { |
763 | 2.72k | MVM_spesh_use_facts(tc, g, facts); |
764 | 2.72k | |
765 | 2.72k | ins->info = MVM_op_get_op(MVM_OP_unbox_s); |
766 | 2.72k | /* And now that we have a repr op, we can try to optimize |
767 | 2.72k | * it even further. */ |
768 | 2.72k | optimize_repr_op(tc, g, bb, ins, 1); |
769 | 2.72k | |
770 | 2.72k | return; |
771 | 2.72k | } |
772 | 6.79k | can_result = MVM_spesh_try_can_method(tc, facts->type, |
773 | 5.77k | is_strify ? tc->instance->str_consts.Str : tc->instance->str_consts.Num); |
774 | 6.79k | |
775 | 6.79k | if (can_result == -1) { |
776 | 6.28k | /* Couldn't safely figure out if the type has a Str method or not. */ |
777 | 6.28k | return; |
778 | 513 | } else if (can_result == 0) { |
779 | 462 | MVM_spesh_use_facts(tc, g, facts); |
780 | 462 | /* We can't .Str this object, so we'll duplicate the "guessing" |
781 | 462 | * logic from smrt_strify here to remove indirection. */ |
782 | 462 | if (is_strify && REPR(facts->type)->ID == MVM_REPR_ID_MVMException) { |
783 | 0 | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 3); |
784 | 0 | MVMSpeshOperand *old_opers = ins->operands; |
785 | 0 |
|
786 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_get_s); |
787 | 0 |
|
788 | 0 | ins->operands = operands; |
789 | 0 |
|
790 | 0 | operands[0] = old_opers[0]; |
791 | 0 | operands[1] = old_opers[1]; |
792 | 0 | operands[2].lit_i16 = offsetof( MVMException, body.message ); |
793 | 462 | } else if(ss->can_box & (MVM_STORAGE_SPEC_CAN_BOX_NUM | MVM_STORAGE_SPEC_CAN_BOX_INT)) { |
794 | 0 | MVMuint16 register_type = |
795 | 0 | ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT ? MVM_reg_int64 : MVM_reg_num64; |
796 | 0 |
|
797 | 0 | MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
798 | 0 | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); |
799 | 0 | MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, register_type); |
800 | 0 | MVMSpeshOperand orig_dst = ins->operands[0]; |
801 | 0 |
|
802 | 0 | ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_unbox_n : MVM_OP_unbox_i); |
803 | 0 | ins->operands[0] = temp; |
804 | 0 |
|
805 | 0 | if (is_strify) |
806 | 0 | new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_coerce_ns : MVM_OP_coerce_is); |
807 | 0 | else |
808 | 0 | new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_set : MVM_OP_coerce_in); |
809 | 0 | new_ins->operands = operands; |
810 | 0 | operands[0] = orig_dst; |
811 | 0 | operands[1] = temp; |
812 | 0 |
|
813 | 0 | /* We can directly "eliminate" a set instruction here. */ |
814 | 0 | if (new_ins->info->opcode != MVM_OP_set) { |
815 | 0 | MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins); |
816 | 0 |
|
817 | 0 | MVM_spesh_get_facts(tc, g, temp)->usages++; |
818 | 0 | } else { |
819 | 0 | ins->operands[0] = orig_dst; |
820 | 0 | } |
821 | 0 |
|
822 | 0 | /* Finally, let's try to optimize the unboxing REPROp. */ |
823 | 0 | optimize_repr_op(tc, g, bb, ins, 1); |
824 | 0 |
|
825 | 0 | /* And as a last clean-up step, we release the temporary register. */ |
826 | 0 | MVM_spesh_manipulate_release_temp_reg(tc, g, temp); |
827 | 0 |
|
828 | 0 | return; |
829 | 462 | } else if (!is_strify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray || |
830 | 390 | (REPR(facts->type)->ID == MVM_REPR_ID_MVMHash))) { |
831 | 390 | /* A smrt_numify on an array or hash can be replaced by an |
832 | 390 | * elems operation, that can then be optimized by our |
833 | 390 | * versatile and dilligent friend optimize_repr_op. */ |
834 | 390 | |
835 | 390 | MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
836 | 390 | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); |
837 | 390 | MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64); |
838 | 390 | MVMSpeshOperand orig_dst = ins->operands[0]; |
839 | 390 | |
840 | 390 | ins->info = MVM_op_get_op(MVM_OP_elems); |
841 | 390 | ins->operands[0] = temp; |
842 | 390 | |
843 | 390 | new_ins->info = MVM_op_get_op(MVM_OP_coerce_in); |
844 | 390 | new_ins->operands = operands; |
845 | 390 | operands[0] = orig_dst; |
846 | 390 | operands[1] = temp; |
847 | 390 | |
848 | 390 | MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins); |
849 | 390 | |
850 | 390 | optimize_repr_op(tc, g, bb, ins, 1); |
851 | 390 | |
852 | 390 | MVM_spesh_get_facts(tc, g, temp)->usages++; |
853 | 390 | MVM_spesh_manipulate_release_temp_reg(tc, g, temp); |
854 | 390 | return; |
855 | 390 | } |
856 | 51 | } else if (can_result == 1) { |
857 | 51 | /* When we know how to generate additional callsites, we could |
858 | 51 | * make an invocation to .Str or .Num here and perhaps have it |
859 | 51 | * in-lined. */ |
860 | 51 | } |
861 | 6.79k | } |
862 | 19.8k | } |
863 | | |
864 | | /* boolification has a major indirection, which we can spesh away. |
865 | | * Afterwards, we may be able to spesh even further, so we defer |
866 | | * to other optimization methods. */ |
867 | 861 | static void optimize_istrue_isfalse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
868 | 861 | MVMuint8 negated_op; |
869 | 861 | MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
870 | 861 | if (ins->info->opcode == MVM_OP_istrue) { |
871 | 491 | negated_op = 0; |
872 | 370 | } else if (ins->info->opcode == MVM_OP_isfalse) { |
873 | 370 | negated_op = 1; |
874 | 0 | } else { |
875 | 0 | return; |
876 | 0 | } |
877 | 861 | |
878 | 861 | /* Let's try to figure out the boolification spec. */ |
879 | 861 | if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
880 | 331 | MVMBoolificationSpec *bs = STABLE(facts->type)->boolification_spec; |
881 | 331 | MVMSpeshOperand orig = ins->operands[0]; |
882 | 331 | MVMSpeshOperand temp; |
883 | 331 | |
884 | 331 | if (negated_op) |
885 | 197 | temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64); |
886 | 331 | |
887 | 244 | switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) { |
888 | 176 | case MVM_BOOL_MODE_UNBOX_INT: |
889 | 176 | /* This optimization can only handle values known to be concrete. */ |
890 | 176 | if (!(facts->flags & MVM_SPESH_FACT_CONCRETE)) { |
891 | 0 | return; |
892 | 0 | } |
893 | 176 | /* We can just unbox the int and pretend it's a bool. */ |
894 | 176 | ins->info = MVM_op_get_op(MVM_OP_unbox_i); |
895 | 176 | if (negated_op) |
896 | 175 | ins->operands[0] = temp; |
897 | 176 | /* And then we might be able to optimize this even further. */ |
898 | 176 | optimize_repr_op(tc, g, bb, ins, 1); |
899 | 176 | break; |
900 | 96 | case MVM_BOOL_MODE_NOT_TYPE_OBJECT: |
901 | 96 | /* This is the same as isconcrete. */ |
902 | 96 | ins->info = MVM_op_get_op(MVM_OP_isconcrete); |
903 | 96 | if (negated_op) |
904 | 9 | ins->operands[0] = temp; |
905 | 96 | /* And now defer another bit of optimization */ |
906 | 96 | optimize_isconcrete(tc, g, ins); |
907 | 96 | break; |
908 | 176 | /* TODO implement MODE_UNBOX_NUM and the string ones */ |
909 | 59 | default: |
910 | 59 | return; |
911 | 331 | } |
912 | 331 | /* Now we can take care of the negation. */ |
913 | 272 | if (negated_op) { |
914 | 184 | MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
915 | 184 | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); |
916 | 184 | MVMSpeshFacts *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
917 | 184 | |
918 | 184 | /* This is a bit naughty with regards to the SSA form, but |
919 | 184 | * we'll hopefully get away with it until we have a proper |
920 | 184 | * way to get new registers crammed in the middle of things */ |
921 | 184 | new_ins->info = MVM_op_get_op(MVM_OP_not_i); |
922 | 184 | new_ins->operands = operands; |
923 | 184 | operands[0] = orig; |
924 | 184 | operands[1] = temp; |
925 | 184 | MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins); |
926 | 184 | |
927 | 184 | MVM_spesh_get_facts(tc, g, temp)->usages++; |
928 | 184 | |
929 | 184 | /* If there's a known value, update the fact. */ |
930 | 184 | if (res_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) |
931 | 9 | res_facts->value.i = !res_facts->value.i; |
932 | 184 | |
933 | 184 | MVM_spesh_manipulate_release_temp_reg(tc, g, temp); |
934 | 184 | } |
935 | 272 | |
936 | 272 | MVM_spesh_use_facts(tc, g, facts); |
937 | 272 | } |
938 | 861 | } |
939 | | |
940 | | /* Checks if we have specialized on the invocant - useful to know for some |
941 | | * optimizations. */ |
942 | 15.6k | static MVMint32 specialized_on_invocant(MVMThreadContext *tc, MVMSpeshGraph *g) { |
943 | 15.6k | MVMint32 i; |
944 | 15.6k | for (i = 0; i < g->num_arg_guards; i++) |
945 | 15.6k | if (g->arg_guards[i].slot == 0) |
946 | 15.6k | return 1; |
947 | 0 | return 0; |
948 | 15.6k | } |
949 | | |
950 | | /* Optimizes away a lexical lookup when we know the value won't change from |
951 | | * the logged one. */ |
952 | | static void optimize_getlex_known(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, |
953 | 17.5k | MVMSpeshIns *ins) { |
954 | 17.5k | /* Ensure we have a log instruction following this one. */ |
955 | 17.5k | if (ins->next && ins->next->info->opcode == MVM_OP_sp_log) { |
956 | 17.5k | /* Locate logged object. */ |
957 | 17.5k | MVMuint16 log_slot = ins->next->operands[1].lit_i16 * MVM_SPESH_LOG_RUNS; |
958 | 17.5k | MVMCollectable *log_obj = g->log_slots[log_slot]; |
959 | 17.5k | if (log_obj) { |
960 | 11.4k | MVMSpeshFacts *facts; |
961 | 11.4k | |
962 | 11.4k | /* Place in a spesh slot. */ |
963 | 11.4k | MVMuint16 ss = MVM_spesh_add_spesh_slot_try_reuse(tc, g, log_obj); |
964 | 11.4k | |
965 | 11.4k | /* Delete logging instruction. */ |
966 | 11.4k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins->next); |
967 | 11.4k | |
968 | 11.4k | /* Transform lookup instruction into spesh slot read. */ |
969 | 11.4k | MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--; |
970 | 11.4k | ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); |
971 | 11.4k | ins->operands[1].lit_i16 = ss; |
972 | 11.4k | |
973 | 11.4k | /* Set up facts. */ |
974 | 11.4k | facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
975 | 11.4k | facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_KNOWN_VALUE; |
976 | 11.4k | facts->type = STABLE(log_obj)->WHAT; |
977 | 11.4k | facts->value.o = (MVMObject *)log_obj; |
978 | 11.4k | if (IS_CONCRETE(log_obj)) { |
979 | 114 | facts->flags |= MVM_SPESH_FACT_CONCRETE; |
980 | 114 | if (!STABLE(log_obj)->container_spec) |
981 | 114 | facts->flags |= MVM_SPESH_FACT_DECONTED; |
982 | 114 | } |
983 | 11.3k | else { |
984 | 11.3k | facts->flags |= MVM_SPESH_FACT_TYPEOBJ; |
985 | 11.3k | } |
986 | 11.4k | } |
987 | 17.5k | } |
988 | 17.5k | } |
989 | | |
990 | | /* Determines if there's a matching spesh candidate for a callee and a given |
991 | | * set of argument info. */ |
992 | 14.8k | static MVMint32 try_find_spesh_candidate(MVMThreadContext *tc, MVMCode *code, MVMSpeshCallInfo *arg_info) { |
993 | 14.8k | MVMStaticFrameBody *sfb = &(code->body.sf->body); |
994 | 14.8k | MVMint32 num_spesh = sfb->num_spesh_candidates; |
995 | 14.8k | MVMint32 i, j; |
996 | 27.1k | for (i = 0; i < num_spesh; i++) { |
997 | 19.3k | MVMSpeshCandidate *cand = &sfb->spesh_candidates[i]; |
998 | 19.3k | if (cand->cs == arg_info->cs) { |
999 | 14.9k | /* Matching callsite, now see if we have enough information to |
1000 | 14.9k | * test the guards. */ |
1001 | 14.9k | MVMint32 guard_failed = 0; |
1002 | 28.5k | for (j = 0; j < cand->num_guards; j++) { |
1003 | 21.5k | MVMint32 slot = cand->guards[j].slot; |
1004 | 21.5k | MVMSpeshFacts *facts = slot < MAX_ARGS_FOR_OPT ? arg_info->arg_facts[slot] : NULL; |
1005 | 21.5k | MVMSTable *want_st = (MVMSTable *)cand->guards[j].match; |
1006 | 21.5k | if (!facts) { |
1007 | 31 | guard_failed = 1; |
1008 | 31 | break; |
1009 | 31 | } |
1010 | 21.4k | switch (cand->guards[j].kind) { |
1011 | 18.2k | case MVM_SPESH_GUARD_CONC: |
1012 | 18.2k | if (!(facts->flags & MVM_SPESH_FACT_CONCRETE) || |
1013 | 14.1k | !(facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) || |
1014 | 14.1k | STABLE(facts->type) != want_st) |
1015 | 7.42k | guard_failed = 1; |
1016 | 18.2k | break; |
1017 | 3.19k | case MVM_SPESH_GUARD_TYPE: |
1018 | 3.19k | if (!(facts->flags & MVM_SPESH_FACT_TYPEOBJ) || |
1019 | 2.79k | !(facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) || |
1020 | 2.79k | STABLE(facts->type) != want_st) |
1021 | 454 | guard_failed = 1; |
1022 | 3.19k | break; |
1023 | 0 | case MVM_SPESH_GUARD_DC_CONC: |
1024 | 0 | if (!(facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) || |
1025 | 0 | !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) || |
1026 | 0 | STABLE(facts->decont_type) != want_st) |
1027 | 0 | guard_failed = 1; |
1028 | 0 | break; |
1029 | 0 | case MVM_SPESH_GUARD_DC_TYPE: |
1030 | 0 | if (!(facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) || |
1031 | 0 | !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) || |
1032 | 0 | STABLE(facts->decont_type) != want_st) |
1033 | 0 | guard_failed = 1; |
1034 | 0 | break; |
1035 | 0 | case MVM_SPESH_GUARD_DC_CONC_RW: |
1036 | 0 | if (!(facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) || |
1037 | 0 | !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) || |
1038 | 0 | STABLE(facts->decont_type) != want_st || |
1039 | 0 | !(facts->flags & MVM_SPESH_FACT_RW_CONT)) |
1040 | 0 | guard_failed = 1; |
1041 | 0 | break; |
1042 | 0 | case MVM_SPESH_GUARD_DC_TYPE_RW: |
1043 | 0 | if (!(facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) || |
1044 | 0 | !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) || |
1045 | 0 | STABLE(facts->decont_type) != want_st || |
1046 | 0 | !(facts->flags & MVM_SPESH_FACT_RW_CONT)) |
1047 | 0 | guard_failed = 1; |
1048 | 0 | break; |
1049 | 0 | default: |
1050 | 0 | guard_failed = 1; |
1051 | 0 | break; |
1052 | 21.4k | } |
1053 | 21.4k | if (guard_failed) |
1054 | 7.87k | break; |
1055 | 21.4k | } |
1056 | 14.9k | if (!guard_failed) |
1057 | 7.05k | return i; |
1058 | 14.9k | } |
1059 | 19.3k | } |
1060 | 7.81k | return -1; |
1061 | 14.8k | } |
1062 | | |
1063 | | /* Drives optimization of a call. */ |
1064 | | static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, |
1065 | 32.1k | MVMSpeshIns *ins, MVMint32 callee_idx, MVMSpeshCallInfo *arg_info) { |
1066 | 32.1k | /* Ensure we know what we're going to be invoking. */ |
1067 | 32.1k | MVMSpeshFacts *callee_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[callee_idx]); |
1068 | 32.1k | if (callee_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
1069 | 16.4k | MVMObject *code = callee_facts->value.o; |
1070 | 16.4k | MVMObject *target = NULL; |
1071 | 16.4k | if (REPR(code)->ID == MVM_REPR_ID_MVMCode) { |
1072 | 114 | /* Already have a code object we know we'll call. */ |
1073 | 114 | target = code; |
1074 | 114 | } |
1075 | 16.3k | else if (STABLE(code)->invocation_spec) { |
1076 | 16.3k | /* What kind of invocation will it be? */ |
1077 | 16.3k | MVMInvocationSpec *is = STABLE(code)->invocation_spec; |
1078 | 16.3k | if (!MVM_is_null(tc, is->md_class_handle)) { |
1079 | 15.7k | /* Multi-dispatch. Check if this is a dispatch where we can |
1080 | 15.7k | * use the cache directly. */ |
1081 | 15.7k | MVMRegister dest; |
1082 | 15.7k | REPR(code)->attr_funcs.get_attribute(tc, |
1083 | 15.7k | STABLE(code), code, OBJECT_BODY(code), |
1084 | 15.7k | is->md_class_handle, is->md_valid_attr_name, |
1085 | 15.7k | is->md_valid_hint, &dest, MVM_reg_int64); |
1086 | 15.7k | if (dest.i64) { |
1087 | 787 | /* Yes. Try to obtain the cache. */ |
1088 | 787 | REPR(code)->attr_funcs.get_attribute(tc, |
1089 | 787 | STABLE(code), code, OBJECT_BODY(code), |
1090 | 787 | is->md_class_handle, is->md_cache_attr_name, |
1091 | 787 | is->md_cache_hint, &dest, MVM_reg_obj); |
1092 | 787 | if (!MVM_is_null(tc, dest.o)) { |
1093 | 787 | MVMObject *found = MVM_multi_cache_find_spesh(tc, dest.o, arg_info); |
1094 | 787 | if (found) { |
1095 | 256 | /* Found it. Is it a code object already, or do we |
1096 | 256 | * have futher unpacking to do? */ |
1097 | 256 | if (REPR(found)->ID == MVM_REPR_ID_MVMCode) { |
1098 | 0 | target = found; |
1099 | 0 | } |
1100 | 256 | else if (STABLE(found)->invocation_spec) { |
1101 | 256 | MVMInvocationSpec *m_is = STABLE(found)->invocation_spec; |
1102 | 256 | if (!MVM_is_null(tc, m_is->class_handle)) { |
1103 | 256 | REPR(found)->attr_funcs.get_attribute(tc, |
1104 | 256 | STABLE(found), found, OBJECT_BODY(found), |
1105 | 256 | is->class_handle, is->attr_name, |
1106 | 256 | is->hint, &dest, MVM_reg_obj); |
1107 | 256 | if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode) |
1108 | 256 | target = dest.o; |
1109 | 256 | } |
1110 | 256 | } |
1111 | 256 | } |
1112 | 787 | } |
1113 | 787 | } |
1114 | 14.9k | else if (!MVM_is_null(tc, is->class_handle)) { |
1115 | 14.9k | /* This type of code object supports multi-dispatch, |
1116 | 14.9k | * but we actually have a single dispatch routine. */ |
1117 | 14.9k | MVMRegister dest; |
1118 | 14.9k | REPR(code)->attr_funcs.get_attribute(tc, |
1119 | 14.9k | STABLE(code), code, OBJECT_BODY(code), |
1120 | 14.9k | is->class_handle, is->attr_name, |
1121 | 14.9k | is->hint, &dest, MVM_reg_obj); |
1122 | 14.9k | if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode) |
1123 | 14.9k | target = dest.o; |
1124 | 14.9k | } |
1125 | 15.7k | } |
1126 | 649 | else if (!MVM_is_null(tc, is->class_handle)) { |
1127 | 649 | /* Single dispatch; retrieve the code object. */ |
1128 | 649 | MVMRegister dest; |
1129 | 649 | REPR(code)->attr_funcs.get_attribute(tc, |
1130 | 649 | STABLE(code), code, OBJECT_BODY(code), |
1131 | 649 | is->class_handle, is->attr_name, |
1132 | 649 | is->hint, &dest, MVM_reg_obj); |
1133 | 649 | if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode) |
1134 | 649 | target = dest.o; |
1135 | 649 | } |
1136 | 16.3k | } |
1137 | 16.4k | |
1138 | 16.4k | /* If we resolved to something better than the code object, then add |
1139 | 16.4k | * the resolved item in a spesh slot and insert a lookup. */ |
1140 | 16.4k | if (target && target != code && !((MVMCode *)target)->body.is_compiler_stub) { |
1141 | 15.8k | MVMSpeshIns *pa_ins = arg_info->prepargs_ins; |
1142 | 15.8k | MVMSpeshIns *ss_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); |
1143 | 15.8k | ss_ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); |
1144 | 15.8k | ss_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand)); |
1145 | 15.8k | ss_ins->operands[0] = ins->operands[callee_idx]; |
1146 | 15.8k | ss_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g, |
1147 | 15.8k | (MVMCollectable *)target); |
1148 | 15.8k | /* Basically, we're inserting between arg* and invoke_*. |
1149 | 15.8k | * Since invoke_* directly uses the code in the register, |
1150 | 15.8k | * the register must have held the code during the arg* |
1151 | 15.8k | * instructions as well, because none of {prepargs, arg*} |
1152 | 15.8k | * can manipulate the register that holds the code. |
1153 | 15.8k | * |
1154 | 15.8k | * To make a long story very short, I think it should be |
1155 | 15.8k | * safe to move the sp_getspeshslot to /before/ the |
1156 | 15.8k | * prepargs instruction. And this is very convenient for |
1157 | 15.8k | * me, as it allows me to treat set of prepargs, arg*, |
1158 | 15.8k | * invoke, as a /single node/, and this greatly simplifies |
1159 | 15.8k | * invoke JIT compilation */ |
1160 | 15.8k | |
1161 | 15.8k | MVM_spesh_manipulate_insert_ins(tc, bb, pa_ins->prev, ss_ins); |
1162 | 15.8k | /* XXX TODO: Do this differently so we can eliminate the original |
1163 | 15.8k | * lookup of the enclosing code object also. */ |
1164 | 15.8k | } |
1165 | 16.4k | |
1166 | 16.4k | /* See if we can point the call at a particular specialization. */ |
1167 | 16.4k | if (target && ((MVMCode *)target)->body.sf->body.instrumentation_level == tc->instance->instrumentation_level) { |
1168 | 14.8k | MVMCode *target_code = (MVMCode *)target; |
1169 | 14.8k | MVMint32 spesh_cand = try_find_spesh_candidate(tc, target_code, arg_info); |
1170 | 14.8k | if (spesh_cand >= 0) { |
1171 | 7.05k | /* Yes. Will we be able to inline? */ |
1172 | 7.05k | MVMSpeshGraph *inline_graph = MVM_spesh_inline_try_get_graph(tc, g, |
1173 | 7.05k | target_code, &target_code->body.sf->body.spesh_candidates[spesh_cand]); |
1174 | 7.05k | if (inline_graph) { |
1175 | 3.88k | /* Yes, have inline graph, so go ahead and do it. */ |
1176 | 3.88k | #if MVM_LOG_INLINES |
1177 | | char *c_name_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.name); |
1178 | | char *c_cuid_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.cuuid); |
1179 | | char *c_name_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.name); |
1180 | | char *c_cuid_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.cuuid); |
1181 | | printf("Can inline %s (%s) into %s (%s)\n", |
1182 | | c_name_i, c_cuid_i, c_name_t, c_cuid_t); |
1183 | | MVM_free(c_name_i); |
1184 | | MVM_free(c_cuid_i); |
1185 | | MVM_free(c_name_t); |
1186 | | MVM_free(c_cuid_t); |
1187 | | #endif |
1188 | 3.88k | MVM_spesh_inline(tc, g, arg_info, bb, ins, inline_graph, target_code); |
1189 | 3.88k | } |
1190 | 3.17k | else { |
1191 | 3.17k | /* Can't inline, so just identify candidate. */ |
1192 | 3.17k | MVMSpeshOperand *new_operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand)); |
1193 | 3.17k | if (ins->info->opcode == MVM_OP_invoke_v) { |
1194 | 204 | new_operands[0] = ins->operands[0]; |
1195 | 204 | new_operands[1].lit_i16 = spesh_cand; |
1196 | 204 | ins->operands = new_operands; |
1197 | 204 | ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_v); |
1198 | 204 | } |
1199 | 2.96k | else { |
1200 | 2.96k | new_operands[0] = ins->operands[0]; |
1201 | 2.96k | new_operands[1] = ins->operands[1]; |
1202 | 2.96k | new_operands[2].lit_i16 = spesh_cand; |
1203 | 2.96k | ins->operands = new_operands; |
1204 | 2.96k | switch (ins->info->opcode) { |
1205 | 0 | case MVM_OP_invoke_i: |
1206 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_i); |
1207 | 0 | break; |
1208 | 0 | case MVM_OP_invoke_n: |
1209 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_n); |
1210 | 0 | break; |
1211 | 0 | case MVM_OP_invoke_s: |
1212 | 0 | ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_s); |
1213 | 0 | break; |
1214 | 2.96k | case MVM_OP_invoke_o: |
1215 | 2.96k | ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_o); |
1216 | 2.96k | break; |
1217 | 0 | default: |
1218 | 0 | MVM_oops(tc, "Spesh: unhandled invoke instruction"); |
1219 | 2.96k | } |
1220 | 2.96k | } |
1221 | 3.17k | } |
1222 | 7.05k | } |
1223 | 14.8k | } |
1224 | 16.4k | } |
1225 | 32.1k | } |
1226 | | |
1227 | 0 | static void optimize_coverage_log(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
1228 | 0 | char *cache = (char *)ins->operands[3].lit_i64; |
1229 | 0 | MVMint32 cache_idx = ins->operands[2].lit_i32; |
1230 | 0 |
|
1231 | 0 | if (cache[cache_idx] != 0) { |
1232 | 0 | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | | /* Optimizes an extension op. */ |
1237 | 0 | static void optimize_extop(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
1238 | 0 | MVMExtOpRecord *extops = g->sf->body.cu->body.extops; |
1239 | 0 | MVMuint16 num_extops = g->sf->body.cu->body.num_extops; |
1240 | 0 | MVMuint16 i; |
1241 | 0 | for (i = 0; i < num_extops; i++) { |
1242 | 0 | if (extops[i].info == ins->info) { |
1243 | 0 | /* Found op; call its spesh function, if any. */ |
1244 | 0 | if (extops[i].spesh) |
1245 | 0 | extops[i].spesh(tc, g, bb, ins); |
1246 | 0 | return; |
1247 | 0 | } |
1248 | 0 | } |
1249 | 0 | } |
1250 | | |
1251 | 0 | static void optimize_uniprop_ops(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
1252 | 0 | MVMSpeshFacts *arg1_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); |
1253 | 0 | MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
1254 | 0 | if (arg1_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
1255 | 0 | if (ins->info->opcode == MVM_OP_unipropcode) { |
1256 | 0 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
1257 | 0 | result_facts->value.i = (MVMint64)MVM_unicode_name_to_property_code(tc, arg1_facts->value.s); |
1258 | 0 | ins->info = MVM_op_get_op(MVM_OP_const_i64); |
1259 | 0 | ins->operands[1].lit_i64 = result_facts->value.i; |
1260 | 0 | arg1_facts->usages--; |
1261 | 0 | } else if (ins->info->opcode == MVM_OP_unipvalcode) { |
1262 | 0 | MVMSpeshFacts *arg2_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]); |
1263 | 0 |
|
1264 | 0 | if (arg2_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { |
1265 | 0 | result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; |
1266 | 0 | result_facts->value.i = (MVMint64)MVM_unicode_name_to_property_value_code(tc, arg1_facts->value.i, arg2_facts->value.s); |
1267 | 0 | ins->info = MVM_op_get_op(MVM_OP_const_i64); |
1268 | 0 | ins->operands[1].lit_i64 = result_facts->value.i; |
1269 | 0 | arg1_facts->usages--; |
1270 | 0 | arg2_facts->usages--; |
1271 | 0 | } |
1272 | 0 | } |
1273 | 0 | } |
1274 | 0 | } |
1275 | | |
1276 | | /* If something is only kept alive because we log its allocation, kick out |
1277 | | * the allocation logging and let the op that creates it die. |
1278 | | */ |
1279 | 0 | static void optimize_prof_allocated(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
1280 | 0 | MVMSpeshFacts *logee_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); |
1281 | 0 | if (logee_facts->usages == 1) { |
1282 | 0 | logee_facts->usages = 0; |
1283 | 0 | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1284 | 0 | /* This check should always succeed, but just in case ... */ |
1285 | 0 | if (logee_facts->writer) |
1286 | 0 | MVM_spesh_manipulate_delete_ins(tc, g, bb, logee_facts->writer); |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | | /* Tries to optimize a throwcat instruction. Note that within a given frame |
1291 | | * (we don't consider inlines here) the throwcat instructions all have the |
1292 | | * same semantics. */ |
1293 | 394 | static void optimize_throwcat(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
1294 | 394 | /* First, see if we have any goto handlers for this category. */ |
1295 | 394 | MVMint32 *handlers_found = MVM_malloc(g->sf->body.num_handlers * sizeof(MVMint32)); |
1296 | 394 | MVMint32 num_found = 0; |
1297 | 394 | MVMuint32 category = (MVMuint32)ins->operands[1].lit_i64; |
1298 | 394 | MVMint32 i; |
1299 | 4.92k | for (i = 0; i < g->sf->body.num_handlers; i++) |
1300 | 4.53k | if (g->sf->body.handlers[i].action == MVM_EX_ACTION_GOTO) |
1301 | 4.17k | if (g->sf->body.handlers[i].category_mask & category) |
1302 | 1.39k | handlers_found[num_found++] = i; |
1303 | 394 | |
1304 | 394 | /* If we found any appropriate handlers, we'll now do a scan through the |
1305 | 394 | * graph to see if we're in the scope of any of them. Note we can't keep |
1306 | 394 | * track of this in optimize_bb as it walks the dominance children, but |
1307 | 394 | * we need a linear view. */ |
1308 | 394 | if (num_found) { |
1309 | 382 | MVMint32 *in_handlers = MVM_calloc(g->sf->body.num_handlers, sizeof(MVMint32)); |
1310 | 382 | MVMSpeshBB **goto_bbs = MVM_calloc(g->sf->body.num_handlers, sizeof(MVMSpeshBB *)); |
1311 | 382 | MVMSpeshBB *search_bb = g->entry; |
1312 | 382 | MVMint32 picked = -1; |
1313 | 88.3k | while (search_bb && !search_bb->inlined) { |
1314 | 88.3k | MVMSpeshIns *search_ins = search_bb->first_ins; |
1315 | 814k | while (search_ins) { |
1316 | 726k | /* Track handlers. */ |
1317 | 726k | MVMSpeshAnn *ann = search_ins->annotations; |
1318 | 762k | while (ann) { |
1319 | 35.9k | switch (ann->type) { |
1320 | 3.93k | case MVM_SPESH_ANN_FH_START: |
1321 | 3.93k | in_handlers[ann->data.frame_handler_index] = 1; |
1322 | 3.93k | break; |
1323 | 1.96k | case MVM_SPESH_ANN_FH_END: |
1324 | 1.96k | in_handlers[ann->data.frame_handler_index] = 0; |
1325 | 1.96k | break; |
1326 | 3.67k | case MVM_SPESH_ANN_FH_GOTO: |
1327 | 3.67k | if (ann->data.frame_handler_index < g->sf->body.num_handlers) { |
1328 | 3.37k | goto_bbs[ann->data.frame_handler_index] = search_bb; |
1329 | 3.37k | if (picked >= 0 && ann->data.frame_handler_index == picked) |
1330 | 374 | goto search_over; |
1331 | 3.37k | } |
1332 | 3.30k | break; |
1333 | 35.9k | } |
1334 | 35.5k | ann = ann->next; |
1335 | 35.5k | } |
1336 | 726k | |
1337 | 726k | /* Is this instruction the one we're trying to optimize? */ |
1338 | 726k | if (search_ins == ins) { |
1339 | 382 | /* See if we're in any acceptable handler (rely on the |
1340 | 382 | * table being pre-sorted by nesting depth here, just like |
1341 | 382 | * normal exception handler search does). */ |
1342 | 1.04k | for (i = 0; i < num_found; i++) { |
1343 | 1.04k | if (in_handlers[handlers_found[i]]) { |
1344 | 382 | /* Got it! If we already found its goto target, we |
1345 | 382 | * can finish the search. */ |
1346 | 382 | picked = handlers_found[i]; |
1347 | 382 | if (goto_bbs[picked]) |
1348 | 8 | goto search_over; |
1349 | 374 | break; |
1350 | 382 | } |
1351 | 1.04k | } |
1352 | 382 | } |
1353 | 726k | |
1354 | 726k | search_ins = search_ins->next; |
1355 | 726k | } |
1356 | 87.9k | search_bb = search_bb->linear_next; |
1357 | 87.9k | } |
1358 | 382 | search_over: |
1359 | 382 | |
1360 | 382 | /* If we picked a handler and know where it should goto, we can do the |
1361 | 382 | * rewrite into a goto. */ |
1362 | 382 | if (picked >=0 && goto_bbs[picked]) { |
1363 | 382 | ins->info = MVM_op_get_op(MVM_OP_goto); |
1364 | 382 | ins->operands[0].ins_bb = goto_bbs[picked]; |
1365 | 382 | bb->succ[0] = goto_bbs[picked]; |
1366 | 382 | } |
1367 | 382 | |
1368 | 382 | MVM_free(in_handlers); |
1369 | 382 | MVM_free(goto_bbs); |
1370 | 382 | } |
1371 | 394 | |
1372 | 394 | MVM_free(handlers_found); |
1373 | 394 | } |
1374 | | |
1375 | 559k | static void analyze_phi(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
1376 | 559k | MVMuint32 operand; |
1377 | 559k | MVMint32 common_flags; |
1378 | 559k | MVMObject *common_type; |
1379 | 559k | MVMObject *common_decont_type; |
1380 | 559k | MVMuint32 needs_merged_with_log_guard = 0; |
1381 | 559k | MVMSpeshFacts *target_facts = get_facts_direct(tc, g, ins->operands[0]); |
1382 | 559k | |
1383 | 559k | common_flags = get_facts_direct(tc, g, ins->operands[1])->flags; |
1384 | 559k | common_type = get_facts_direct(tc, g, ins->operands[1])->type; |
1385 | 559k | common_decont_type = get_facts_direct(tc, g, ins->operands[1])->decont_type; |
1386 | 559k | |
1387 | 559k | needs_merged_with_log_guard = common_flags & MVM_SPESH_FACT_FROM_LOG_GUARD; |
1388 | 559k | |
1389 | 1.50M | for(operand = 2; operand < ins->info->num_operands; operand++) { |
1390 | 947k | common_flags = common_flags & get_facts_direct(tc, g, ins->operands[operand])->flags; |
1391 | 832k | common_type = common_type == get_facts_direct(tc, g, ins->operands[operand])->type && common_type ? common_type : NULL; |
1392 | 947k | common_decont_type = common_decont_type == get_facts_direct(tc, g, ins->operands[operand])->decont_type && common_decont_type ? common_decont_type : NULL; |
1393 | 947k | |
1394 | 947k | /* We have to be a bit more careful if one or more of the facts we're |
1395 | 947k | * merging came from a log guard, as that means we'll have to propagate |
1396 | 947k | * the information what guards have been relied upon back "outwards" |
1397 | 947k | * through the PHI node we've merged stuff with. */ |
1398 | 947k | if (get_facts_direct(tc, g, ins->operands[operand])->flags & MVM_SPESH_FACT_FROM_LOG_GUARD) |
1399 | 18.8k | needs_merged_with_log_guard = 1; |
1400 | 947k | } |
1401 | 559k | |
1402 | 559k | if (common_flags) { |
1403 | 35.4k | /*fprintf(stderr, "at a PHI node of %d operands: ", ins->info->num_operands);*/ |
1404 | 35.4k | if (common_flags & MVM_SPESH_FACT_KNOWN_TYPE) { |
1405 | 15.6k | /*fprintf(stderr, "type ");*/ |
1406 | 15.6k | if (common_type) { |
1407 | 6.02k | /*fprintf(stderr, "(same type) ");*/ |
1408 | 6.02k | target_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE; |
1409 | 6.02k | target_facts->type = common_type; |
1410 | 6.02k | } |
1411 | 15.6k | /*else fprintf(stderr, "(diverging type) ");*/ |
1412 | 15.6k | } |
1413 | 35.4k | /*if (common_flags & MVM_SPESH_FACT_KNOWN_VALUE) fprintf(stderr, "value ");*/ |
1414 | 35.4k | if (common_flags & MVM_SPESH_FACT_DECONTED) { |
1415 | 26.4k | /*fprintf(stderr, "deconted ");*/ |
1416 | 26.4k | target_facts->flags |= MVM_SPESH_FACT_DECONTED; |
1417 | 26.4k | } |
1418 | 35.4k | if (common_flags & MVM_SPESH_FACT_CONCRETE) { |
1419 | 5.33k | /*fprintf(stderr, "concrete ");*/ |
1420 | 5.33k | target_facts->flags |= MVM_SPESH_FACT_CONCRETE; |
1421 | 5.33k | } |
1422 | 35.4k | if (common_flags & MVM_SPESH_FACT_TYPEOBJ) { |
1423 | 1.95k | /*fprintf(stderr, "type_object ");*/ |
1424 | 1.95k | } |
1425 | 35.4k | if (common_flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) { |
1426 | 0 | /*fprintf(stderr, "decont_type ");*/ |
1427 | 0 | if (common_decont_type) { |
1428 | 0 | /*fprintf(stderr, "(same type) ");*/ |
1429 | 0 | target_facts->flags |= MVM_SPESH_FACT_KNOWN_DECONT_TYPE; |
1430 | 0 | target_facts->decont_type = common_decont_type; |
1431 | 0 | } |
1432 | 0 | /*else fprintf(stderr, "(diverging type) ");*/ |
1433 | 0 | } |
1434 | 35.4k | if (common_flags & MVM_SPESH_FACT_DECONT_CONCRETE) { |
1435 | 0 | /*fprintf(stderr, "decont_concrete ");*/ |
1436 | 0 | target_facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE; |
1437 | 0 | } |
1438 | 35.4k | if (common_flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) { |
1439 | 0 | /*fprintf(stderr, "decont_typeobj ");*/ |
1440 | 0 | target_facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ; |
1441 | 0 | } |
1442 | 35.4k | if (common_flags & MVM_SPESH_FACT_RW_CONT) { |
1443 | 0 | /*fprintf(stderr, "rw_cont ");*/ |
1444 | 0 | target_facts->flags |= MVM_SPESH_FACT_RW_CONT; |
1445 | 0 | } |
1446 | 35.4k | /*if (common_flags & MVM_SPESH_FACT_FROM_LOG_GUARD) fprintf(stderr, "from_log_guard ");*/ |
1447 | 35.4k | /*if (common_flags & MVM_SPESH_FACT_HASH_ITER) fprintf(stderr, "hash_iter ");*/ |
1448 | 35.4k | /*if (common_flags & MVM_SPESH_FACT_ARRAY_ITER) fprintf(stderr, "array_iter ");*/ |
1449 | 35.4k | /*if (common_flags & MVM_SPESH_FACT_KNOWN_BOX_SRC) fprintf(stderr, "box_source ");*/ |
1450 | 35.4k | /*fprintf(stderr, "\n");*/ |
1451 | 35.4k | |
1452 | 35.4k | if (needs_merged_with_log_guard) { |
1453 | 6.31k | target_facts->flags |= MVM_SPESH_FACT_MERGED_WITH_LOG_GUARD; |
1454 | 6.31k | } |
1455 | 523k | } else { |
1456 | 523k | /*fprintf(stderr, "a PHI node of %d operands had no intersecting flags\n", ins->info->num_operands);*/ |
1457 | 523k | return; |
1458 | 523k | } |
1459 | 559k | } |
1460 | | /* Visits the blocks in dominator tree order, recursively. */ |
1461 | 263k | static void optimize_bb(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) { |
1462 | 263k | MVMSpeshCallInfo arg_info; |
1463 | 263k | MVMint32 i; |
1464 | 263k | |
1465 | 263k | /* Look for instructions that are interesting to optimize. */ |
1466 | 263k | MVMSpeshIns *ins = bb->first_ins; |
1467 | 2.05M | while (ins) { |
1468 | 1.79M | switch (ins->info->opcode) { |
1469 | 559k | case MVM_SSA_PHI: |
1470 | 559k | analyze_phi(tc, g, ins); |
1471 | 559k | break; |
1472 | 120k | case MVM_OP_set: |
1473 | 120k | copy_facts(tc, g, ins->operands[0], ins->operands[1]); |
1474 | 120k | break; |
1475 | 861 | case MVM_OP_istrue: |
1476 | 861 | case MVM_OP_isfalse: |
1477 | 861 | optimize_istrue_isfalse(tc, g, bb, ins); |
1478 | 861 | break; |
1479 | 55.9k | case MVM_OP_if_i: |
1480 | 55.9k | case MVM_OP_unless_i: |
1481 | 55.9k | case MVM_OP_if_n: |
1482 | 55.9k | case MVM_OP_unless_n: |
1483 | 55.9k | case MVM_OP_if_o: |
1484 | 55.9k | case MVM_OP_unless_o: |
1485 | 55.9k | optimize_iffy(tc, g, ins, bb); |
1486 | 55.9k | break; |
1487 | 32.1k | case MVM_OP_prepargs: |
1488 | 32.1k | arg_info.cs = g->sf->body.cu->body.callsites[ins->operands[0].callsite_idx]; |
1489 | 32.1k | arg_info.prepargs_ins = ins; |
1490 | 32.1k | arg_info.prepargs_bb = bb; |
1491 | 32.1k | break; |
1492 | 65.7k | case MVM_OP_arg_i: |
1493 | 65.7k | case MVM_OP_arg_n: |
1494 | 65.7k | case MVM_OP_arg_s: |
1495 | 65.7k | case MVM_OP_arg_o: { |
1496 | 65.7k | MVMint16 idx = ins->operands[0].lit_i16; |
1497 | 65.7k | if (idx < MAX_ARGS_FOR_OPT) { |
1498 | 63.6k | arg_info.arg_is_const[idx] = 0; |
1499 | 63.6k | arg_info.arg_facts[idx] = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]); |
1500 | 63.6k | arg_info.arg_ins[idx] = ins; |
1501 | 63.6k | } |
1502 | 65.7k | break; |
1503 | 65.7k | } |
1504 | 3.75k | case MVM_OP_argconst_i: |
1505 | 3.75k | case MVM_OP_argconst_n: |
1506 | 3.75k | case MVM_OP_argconst_s: { |
1507 | 3.75k | MVMint16 idx = ins->operands[0].lit_i16; |
1508 | 3.75k | if (idx < MAX_ARGS_FOR_OPT) { |
1509 | 3.49k | arg_info.arg_is_const[idx] = 1; |
1510 | 3.49k | arg_info.arg_ins[idx] = ins; |
1511 | 3.49k | } |
1512 | 3.75k | break; |
1513 | 3.75k | } |
1514 | 6.58k | case MVM_OP_coerce_in: |
1515 | 6.58k | optimize_coerce(tc, g, bb, ins); |
1516 | 6.58k | break; |
1517 | 19.8k | case MVM_OP_smrt_numify: |
1518 | 19.8k | case MVM_OP_smrt_strify: |
1519 | 19.8k | optimize_smart_coerce(tc, g, bb, ins); |
1520 | 19.8k | break; |
1521 | 696 | case MVM_OP_invoke_v: |
1522 | 696 | optimize_call(tc, g, bb, ins, 0, &arg_info); |
1523 | 696 | break; |
1524 | 31.4k | case MVM_OP_invoke_i: |
1525 | 31.4k | case MVM_OP_invoke_n: |
1526 | 31.4k | case MVM_OP_invoke_s: |
1527 | 31.4k | case MVM_OP_invoke_o: |
1528 | 31.4k | optimize_call(tc, g, bb, ins, 1, &arg_info); |
1529 | 31.4k | break; |
1530 | 394 | case MVM_OP_throwcatdyn: |
1531 | 394 | case MVM_OP_throwcatlex: |
1532 | 394 | case MVM_OP_throwcatlexotic: |
1533 | 394 | optimize_throwcat(tc, g, bb, ins); |
1534 | 394 | break; |
1535 | 1.62k | case MVM_OP_islist: |
1536 | 1.62k | case MVM_OP_ishash: |
1537 | 1.62k | case MVM_OP_isint: |
1538 | 1.62k | case MVM_OP_isnum: |
1539 | 1.62k | case MVM_OP_isstr: |
1540 | 1.62k | optimize_is_reprid(tc, g, ins); |
1541 | 1.62k | break; |
1542 | 2.76k | case MVM_OP_findmeth_s: |
1543 | 2.76k | optimize_findmeth_s_perhaps_constant(tc, g, ins); |
1544 | 2.76k | if (ins->info->opcode == MVM_OP_findmeth_s) |
1545 | 450 | break; |
1546 | 27.9k | case MVM_OP_findmeth: |
1547 | 27.9k | optimize_method_lookup(tc, g, ins); |
1548 | 27.9k | break; |
1549 | 475 | case MVM_OP_can: |
1550 | 475 | case MVM_OP_can_s: |
1551 | 475 | optimize_can_op(tc, g, bb, ins); |
1552 | 475 | break; |
1553 | 882 | case MVM_OP_gethow: |
1554 | 882 | optimize_gethow(tc, g, ins); |
1555 | 882 | break; |
1556 | 5.25k | case MVM_OP_isconcrete: |
1557 | 5.25k | optimize_isconcrete(tc, g, ins); |
1558 | 5.25k | break; |
1559 | 1.53k | case MVM_OP_istype: |
1560 | 1.53k | optimize_istype(tc, g, ins); |
1561 | 1.53k | break; |
1562 | 24 | case MVM_OP_objprimspec: |
1563 | 24 | optimize_objprimspec(tc, g, ins); |
1564 | 24 | break; |
1565 | 0 | case MVM_OP_unipropcode: |
1566 | 0 | case MVM_OP_unipvalcode: |
1567 | 0 | optimize_uniprop_ops(tc, g, bb, ins); |
1568 | 0 | break; |
1569 | 27.6k | case MVM_OP_unshift_i: |
1570 | 27.6k | case MVM_OP_unshift_n: |
1571 | 27.6k | case MVM_OP_unshift_s: |
1572 | 27.6k | case MVM_OP_unshift_o: |
1573 | 27.6k | case MVM_OP_bindkey_i: |
1574 | 27.6k | case MVM_OP_bindkey_n: |
1575 | 27.6k | case MVM_OP_bindkey_s: |
1576 | 27.6k | case MVM_OP_bindkey_o: |
1577 | 27.6k | case MVM_OP_bindpos_i: |
1578 | 27.6k | case MVM_OP_bindpos_n: |
1579 | 27.6k | case MVM_OP_bindpos_s: |
1580 | 27.6k | case MVM_OP_bindpos_o: |
1581 | 27.6k | case MVM_OP_pop_i: |
1582 | 27.6k | case MVM_OP_pop_n: |
1583 | 27.6k | case MVM_OP_pop_s: |
1584 | 27.6k | case MVM_OP_pop_o: |
1585 | 27.6k | case MVM_OP_deletekey: |
1586 | 27.6k | case MVM_OP_setelemspos: |
1587 | 27.6k | case MVM_OP_splice: |
1588 | 27.6k | case MVM_OP_bindattr_i: |
1589 | 27.6k | case MVM_OP_bindattr_n: |
1590 | 27.6k | case MVM_OP_bindattr_s: |
1591 | 27.6k | case MVM_OP_bindattr_o: |
1592 | 27.6k | case MVM_OP_bindattrs_i: |
1593 | 27.6k | case MVM_OP_bindattrs_n: |
1594 | 27.6k | case MVM_OP_bindattrs_s: |
1595 | 27.6k | case MVM_OP_bindattrs_o: |
1596 | 27.6k | case MVM_OP_assign_i: |
1597 | 27.6k | case MVM_OP_assign_n: |
1598 | 27.6k | optimize_repr_op(tc, g, bb, ins, 0); |
1599 | 27.6k | break; |
1600 | 88.9k | case MVM_OP_atpos_i: |
1601 | 88.9k | case MVM_OP_atpos_n: |
1602 | 88.9k | case MVM_OP_atpos_s: |
1603 | 88.9k | case MVM_OP_atpos_o: |
1604 | 88.9k | case MVM_OP_atkey_i: |
1605 | 88.9k | case MVM_OP_atkey_n: |
1606 | 88.9k | case MVM_OP_atkey_s: |
1607 | 88.9k | case MVM_OP_atkey_o: |
1608 | 88.9k | case MVM_OP_elems: |
1609 | 88.9k | case MVM_OP_shift_i: |
1610 | 88.9k | case MVM_OP_shift_n: |
1611 | 88.9k | case MVM_OP_shift_s: |
1612 | 88.9k | case MVM_OP_shift_o: |
1613 | 88.9k | case MVM_OP_push_i: |
1614 | 88.9k | case MVM_OP_push_n: |
1615 | 88.9k | case MVM_OP_push_s: |
1616 | 88.9k | case MVM_OP_push_o: |
1617 | 88.9k | case MVM_OP_existskey: |
1618 | 88.9k | case MVM_OP_existspos: |
1619 | 88.9k | case MVM_OP_getattr_i: |
1620 | 88.9k | case MVM_OP_getattr_n: |
1621 | 88.9k | case MVM_OP_getattr_s: |
1622 | 88.9k | case MVM_OP_getattr_o: |
1623 | 88.9k | case MVM_OP_getattrs_i: |
1624 | 88.9k | case MVM_OP_getattrs_n: |
1625 | 88.9k | case MVM_OP_getattrs_s: |
1626 | 88.9k | case MVM_OP_getattrs_o: |
1627 | 88.9k | case MVM_OP_decont_i: |
1628 | 88.9k | case MVM_OP_decont_n: |
1629 | 88.9k | case MVM_OP_decont_s: |
1630 | 88.9k | case MVM_OP_decont_u: |
1631 | 88.9k | case MVM_OP_create: |
1632 | 88.9k | optimize_repr_op(tc, g, bb, ins, 1); |
1633 | 88.9k | break; |
1634 | 10.6k | case MVM_OP_box_i: |
1635 | 10.6k | case MVM_OP_box_n: |
1636 | 10.6k | case MVM_OP_box_s: |
1637 | 10.6k | optimize_repr_op(tc, g, bb, ins, 2); |
1638 | 10.6k | break; |
1639 | 0 | case MVM_OP_newexception: |
1640 | 0 | case MVM_OP_bindexmessage: |
1641 | 0 | case MVM_OP_bindexpayload: |
1642 | 0 | case MVM_OP_getexmessage: |
1643 | 0 | case MVM_OP_getexpayload: |
1644 | 0 | optimize_exception_ops(tc, g, bb, ins); |
1645 | 0 | break; |
1646 | 0 | case MVM_OP_hllize: |
1647 | 0 | optimize_hllize(tc, g, ins); |
1648 | 0 | break; |
1649 | 123k | case MVM_OP_decont: |
1650 | 123k | optimize_decont(tc, g, bb, ins); |
1651 | 123k | break; |
1652 | 0 | case MVM_OP_assertparamcheck: |
1653 | 0 | optimize_assertparamcheck(tc, g, bb, ins); |
1654 | 0 | break; |
1655 | 1.91k | case MVM_OP_getlexstatic_o: |
1656 | 1.91k | optimize_getlex_known(tc, g, bb, ins); |
1657 | 1.91k | break; |
1658 | 15.6k | case MVM_OP_getlexperinvtype_o: |
1659 | 15.6k | if (specialized_on_invocant(tc, g)) |
1660 | 15.6k | optimize_getlex_known(tc, g, bb, ins); |
1661 | 15.6k | break; |
1662 | 0 | case MVM_OP_isrwcont: |
1663 | 0 | optimize_container_check(tc, g, bb, ins); |
1664 | 0 | break; |
1665 | 37.1k | case MVM_OP_sp_log: |
1666 | 37.1k | case MVM_OP_sp_osrfinalize: |
1667 | 37.1k | /* Left-over log instruction that didn't become a guard, or OSR |
1668 | 37.1k | * finalize instruction; just delete it. */ |
1669 | 37.1k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1670 | 37.1k | break; |
1671 | 0 | case MVM_OP_prof_enter: |
1672 | 0 | /* Profiling entered from spesh should indicate so. */ |
1673 | 0 | ins->info = MVM_op_get_op(MVM_OP_prof_enterspesh); |
1674 | 0 | break; |
1675 | 0 | case MVM_OP_coverage_log: |
1676 | 0 | /* A coverage_log op that has already fired can be thrown out. */ |
1677 | 0 | optimize_coverage_log(tc, g, bb, ins); |
1678 | 552k | default: |
1679 | 552k | if (ins->info->opcode == (MVMuint16)-1) |
1680 | 0 | optimize_extop(tc, g, bb, ins); |
1681 | 1.79M | } |
1682 | 1.79M | |
1683 | 1.79M | |
1684 | 1.79M | ins = ins->next; |
1685 | 1.79M | } |
1686 | 263k | |
1687 | 263k | /* Visit children. */ |
1688 | 513k | for (i = 0; i < bb->num_children; i++) |
1689 | 249k | optimize_bb(tc, g, bb->children[i]); |
1690 | 263k | } |
1691 | | |
1692 | | /* Eliminates any unused instructions. */ |
1693 | 13.8k | static void eliminate_dead_ins(MVMThreadContext *tc, MVMSpeshGraph *g) { |
1694 | 13.8k | /* Keep eliminating to a fixed point. */ |
1695 | 13.8k | MVMint8 death = 1; |
1696 | 56.3k | while (death) { |
1697 | 42.4k | MVMSpeshBB *bb = g->entry; |
1698 | 42.4k | death = 0; |
1699 | 1.19M | while (bb && !bb->inlined) { |
1700 | 1.15M | MVMSpeshIns *ins = bb->last_ins; |
1701 | 7.72M | while (ins) { |
1702 | 6.57M | MVMSpeshIns *prev = ins->prev; |
1703 | 6.57M | if (ins->info->opcode == MVM_SSA_PHI) { |
1704 | 1.88M | MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[0]); |
1705 | 1.88M | if (facts->usages == 0) { |
1706 | 234k | /* Propagate non-usage. */ |
1707 | 234k | MVMint32 i; |
1708 | 872k | for (i = 1; i < ins->info->num_operands; i++) |
1709 | 637k | get_facts_direct(tc, g, ins->operands[i])->usages--; |
1710 | 234k | |
1711 | 234k | /* Remove this phi. */ |
1712 | 234k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1713 | 234k | death = 1; |
1714 | 234k | } |
1715 | 1.88M | } |
1716 | 4.68M | else if (ins->info->pure) { |
1717 | 2.84M | /* Sanity check to make sure it's a write reg as first operand. */ |
1718 | 2.84M | if ((ins->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg) { |
1719 | 2.84M | MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[0]); |
1720 | 2.84M | if (facts->usages == 0) { |
1721 | 150k | /* Propagate non-usage. */ |
1722 | 150k | MVMint32 i; |
1723 | 226k | for (i = 1; i < ins->info->num_operands; i++) |
1724 | 75.8k | if ((ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg) |
1725 | 23.3k | get_facts_direct(tc, g, ins->operands[i])->usages--; |
1726 | 150k | |
1727 | 150k | /* Remove this instruction. */ |
1728 | 150k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1729 | 150k | death = 1; |
1730 | 150k | } |
1731 | 2.84M | } |
1732 | 2.84M | } |
1733 | 6.57M | ins = prev; |
1734 | 6.57M | } |
1735 | 1.15M | bb = bb->linear_next; |
1736 | 1.15M | } |
1737 | 42.4k | } |
1738 | 13.8k | } |
1739 | | |
1740 | 263k | static void second_pass(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) { |
1741 | 263k | MVMint32 i; |
1742 | 263k | |
1743 | 263k | /* Look for instructions that are interesting to optimize. */ |
1744 | 263k | MVMSpeshIns *ins = bb->first_ins; |
1745 | 1.63M | while (ins) { |
1746 | 1.36M | if (ins->prev && ins->info->opcode == MVM_OP_set) { |
1747 | 128k | /* We may have turned some complex instruction into a simple set |
1748 | 128k | * in the big switch/case up there, but we wouldn't have called |
1749 | 128k | * "copy_facts" on the registers yet, so we have to do it here |
1750 | 128k | * unless we want to lose some important facts */ |
1751 | 128k | copy_facts(tc, g, ins->operands[0], ins->operands[1]); |
1752 | 128k | |
1753 | 128k | /* Due to shoddy code-gen followed by spesh discarding lots of ops, |
1754 | 128k | * we get quite a few redundant set instructions. |
1755 | 128k | * They are not costly, but we can easily kick them out. */ |
1756 | 128k | if (ins->operands[0].reg.orig == ins->operands[1].reg.orig) { |
1757 | 5.16k | MVMSpeshIns *previous = ins->prev; |
1758 | 5.16k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1759 | 5.16k | ins = previous; |
1760 | 123k | } else if (ins->prev->info->opcode == MVM_OP_set) { |
1761 | 5.50k | if (ins->operands[0].reg.i == ins->prev->operands[1].reg.i + 1 && |
1762 | 1.93k | ins->operands[0].reg.orig == ins->prev->operands[1].reg.orig && |
1763 | 1.53k | ins->operands[1].reg.i == ins->prev->operands[0].reg.i && |
1764 | 1.25k | ins->operands[1].reg.orig == ins->prev->operands[0].reg.orig) { |
1765 | 499 | MVMSpeshIns *previous = ins->prev; |
1766 | 499 | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1767 | 499 | ins = previous; |
1768 | 499 | } |
1769 | 117k | } else if ((ins->prev->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg && |
1770 | 86.3k | ins->prev->operands[0].reg.orig == ins->operands[1].reg.orig && |
1771 | 69.8k | ins->prev->operands[0].reg.i == ins->operands[1].reg.i) { |
1772 | 69.8k | /* If a regular operation is immediately followed by a set, |
1773 | 69.8k | * we have to look at the usages of the intermediate register |
1774 | 69.8k | * and make sure it's only ever read by the set, and not, for |
1775 | 69.8k | * example, required by a deopt barrier to have a copy of the |
1776 | 69.8k | * value. */ |
1777 | 69.8k | MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[1]); |
1778 | 69.8k | if (facts->usages <= 1) { |
1779 | 38.0k | /* Cool, we can move the register into the original ins |
1780 | 38.0k | * and throw out the set instruction. */ |
1781 | 38.0k | MVMSpeshIns *previous = ins->prev; |
1782 | 38.0k | ins->prev->operands[0].reg = ins->operands[0].reg; |
1783 | 38.0k | |
1784 | 38.0k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); |
1785 | 38.0k | ins = previous; |
1786 | 38.0k | facts->usages--; |
1787 | 38.0k | } |
1788 | 69.8k | } |
1789 | 1.24M | } else if (ins->prev && ins->info->opcode == MVM_OP_sp_getspeshslot && ins->prev->info->opcode == ins->info->opcode) { |
1790 | 14.7k | /* Sometimes we emit two getspeshslots in a row that write into the |
1791 | 14.7k | * exact same register. that's clearly wasteful and we can save a |
1792 | 14.7k | * tiny shred of code size here. |
1793 | 14.7k | * However, since spesh slots are also involved in GC, we can also |
1794 | 14.7k | * null the spesh slot that we throw out. */ |
1795 | 14.7k | if (ins->operands[0].reg.orig == ins->prev->operands[0].reg.orig) { |
1796 | 14.7k | g->spesh_slots[ins->prev->operands[1].lit_i16] = NULL; |
1797 | 14.7k | |
1798 | 14.7k | MVM_spesh_manipulate_delete_ins(tc, g, bb, ins->prev); |
1799 | 14.7k | } |
1800 | 1.22M | } else if (ins->info->opcode == MVM_OP_prof_allocated) { |
1801 | 0 | optimize_prof_allocated(tc, g, bb, ins); |
1802 | 0 | } |
1803 | 1.36M | |
1804 | 1.36M | ins = ins->next; |
1805 | 1.36M | } |
1806 | 263k | /* Visit children. */ |
1807 | 513k | for (i = 0; i < bb->num_children; i++) |
1808 | 249k | second_pass(tc, g, bb->children[i]); |
1809 | 263k | } |
1810 | | |
1811 | | /* Eliminates any unreachable basic blocks (that is, dead code). Not having |
1812 | | * to consider them any further simplifies all that follows. */ |
1813 | 5.34k | static MVMint64 has_handler_anns(MVMThreadContext *tc, MVMSpeshBB *bb) { |
1814 | 5.34k | MVMSpeshIns *ins = bb->first_ins; |
1815 | 31.8k | while (ins) { |
1816 | 26.5k | MVMSpeshAnn *ann = ins->annotations; |
1817 | 31.5k | while (ann) { |
1818 | 5.02k | switch (ann->type) { |
1819 | 0 | case MVM_SPESH_ANN_FH_START: |
1820 | 0 | case MVM_SPESH_ANN_FH_END: |
1821 | 0 | case MVM_SPESH_ANN_FH_GOTO: |
1822 | 0 | return 1; |
1823 | 5.02k | } |
1824 | 5.02k | ann = ann->next; |
1825 | 5.02k | } |
1826 | 26.5k | ins = ins->next; |
1827 | 26.5k | } |
1828 | 5.34k | return 0; |
1829 | 5.34k | } |
1830 | 13.8k | static void eliminate_dead_bbs(MVMThreadContext *tc, MVMSpeshGraph *g) { |
1831 | 13.8k | /* Iterate to fixed point. */ |
1832 | 13.8k | MVMint8 *seen = MVM_malloc(g->num_bbs); |
1833 | 13.8k | MVMint32 orig_bbs = g->num_bbs; |
1834 | 13.8k | MVMint8 death = 1; |
1835 | 32.2k | while (death) { |
1836 | 18.4k | /* First pass: mark every basic block that is the entry point or the |
1837 | 18.4k | * successor of some other block. */ |
1838 | 18.4k | MVMSpeshBB *cur_bb = g->entry; |
1839 | 18.4k | memset(seen, 0, g->num_bbs); |
1840 | 18.4k | seen[0] = 1; |
1841 | 381k | while (cur_bb) { |
1842 | 362k | MVMuint16 i; |
1843 | 820k | for (i = 0; i < cur_bb->num_succ; i++) |
1844 | 457k | seen[cur_bb->succ[i]->idx] = 1; |
1845 | 362k | cur_bb = cur_bb->linear_next; |
1846 | 362k | } |
1847 | 18.4k | |
1848 | 18.4k | /* Second pass: eliminate dead BBs from consideration. Do not get |
1849 | 18.4k | * rid of any that are from inlines or that contain handler related |
1850 | 18.4k | * annotations. */ |
1851 | 18.4k | death = 0; |
1852 | 18.4k | cur_bb = g->entry; |
1853 | 357k | while (cur_bb->linear_next) { |
1854 | 339k | MVMSpeshBB *death_cand = cur_bb->linear_next; |
1855 | 339k | if (!seen[death_cand->idx]) { |
1856 | 5.36k | if (!death_cand->inlined && !has_handler_anns(tc, death_cand)) { |
1857 | 5.34k | cur_bb->linear_next = cur_bb->linear_next->linear_next; |
1858 | 5.34k | g->num_bbs--; |
1859 | 5.34k | death = 1; |
1860 | 5.34k | } |
1861 | 5.36k | } |
1862 | 339k | cur_bb = cur_bb->linear_next; |
1863 | 339k | } |
1864 | 18.4k | } |
1865 | 13.8k | MVM_free(seen); |
1866 | 13.8k | |
1867 | 13.8k | if (g->num_bbs != orig_bbs) { |
1868 | 2.66k | MVMint32 new_idx = 0; |
1869 | 2.66k | MVMSpeshBB *cur_bb = g->entry; |
1870 | 52.8k | while (cur_bb) { |
1871 | 50.2k | cur_bb->idx = new_idx; |
1872 | 50.2k | new_idx++; |
1873 | 50.2k | cur_bb = cur_bb->linear_next; |
1874 | 50.2k | } |
1875 | 2.66k | } |
1876 | 13.8k | } |
1877 | | |
1878 | | /* Goes through the various log-based guard instructions and removes any that |
1879 | | * are not being made use of. */ |
1880 | 13.8k | static void eliminate_unused_log_guards(MVMThreadContext *tc, MVMSpeshGraph *g) { |
1881 | 13.8k | MVMint32 i; |
1882 | 44.9k | for (i = 0; i < g->num_log_guards; i++) |
1883 | 31.1k | if (!g->log_guards[i].used) |
1884 | 13.7k | MVM_spesh_manipulate_delete_ins(tc, g, g->log_guards[i].bb, |
1885 | 13.7k | g->log_guards[i].ins); |
1886 | 13.8k | } |
1887 | | |
1888 | | /* Drives the overall optimization work taking place on a spesh graph. */ |
1889 | 13.8k | void MVM_spesh_optimize(MVMThreadContext *tc, MVMSpeshGraph *g) { |
1890 | 13.8k | optimize_bb(tc, g, g->entry); |
1891 | 13.8k | eliminate_dead_ins(tc, g); |
1892 | 13.8k | eliminate_dead_bbs(tc, g); |
1893 | 13.8k | eliminate_unused_log_guards(tc, g); |
1894 | 13.8k | second_pass(tc, g, g->entry); |
1895 | 13.8k | } |