/home/travis/build/MoarVM/MoarVM/src/spesh/manipulate.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* This file contains various routines for manipulating a spesh graph, such |
4 | | * as adding/removing/replacing instructions. */ |
5 | | |
6 | | /* Deletes an instruction, and does any fact changes as a result. */ |
7 | | void MVM_spesh_manipulate_delete_ins(MVMThreadContext *tc, MVMSpeshGraph *g, |
8 | 486k | MVMSpeshBB *bb, MVMSpeshIns *ins) { |
9 | 486k | MVMSpeshIns *prev, *next; |
10 | 486k | |
11 | 486k | /* If the instruction is in an already dead basic block, nothing to do. */ |
12 | 486k | if (bb->dead) |
13 | 17 | return; |
14 | 486k | |
15 | 486k | /* Remove it from the double linked list. */ |
16 | 486k | prev = ins->prev; |
17 | 486k | next = ins->next; |
18 | 486k | if (prev) |
19 | 432k | prev->next = next; |
20 | 486k | else |
21 | 54.3k | bb->first_ins = next; |
22 | 486k | if (next) |
23 | 456k | next->prev = prev; |
24 | 486k | else |
25 | 29.4k | bb->last_ins = prev; |
26 | 486k | |
27 | 486k | /* Move its annotations. */ |
28 | 573k | while (ins->annotations) { |
29 | 87.3k | MVMSpeshAnn *ann = ins->annotations; |
30 | 87.3k | MVMSpeshAnn *ann_next = ann->next; |
31 | 87.3k | switch (ann->type) { |
32 | 16.0k | case MVM_SPESH_ANN_FH_START: |
33 | 16.0k | case MVM_SPESH_ANN_FH_GOTO: |
34 | 16.0k | case MVM_SPESH_ANN_INLINE_START: |
35 | 16.0k | case MVM_SPESH_ANN_DEOPT_OSR: |
36 | 16.0k | /* These move to the next instruction. */ |
37 | 16.0k | if (!next) { |
38 | 7.63k | MVMSpeshBB *dest_bb = bb->linear_next; |
39 | 7.63k | while (dest_bb && !dest_bb->first_ins) |
40 | 0 | dest_bb = dest_bb->linear_next; |
41 | 7.63k | if (dest_bb) |
42 | 7.63k | next = dest_bb->first_ins; |
43 | 7.63k | } |
44 | 16.0k | if (next) { |
45 | 16.0k | ann->next = next->annotations; |
46 | 16.0k | next->annotations = ann; |
47 | 16.0k | } |
48 | 16.0k | break; |
49 | 15.1k | case MVM_SPESH_ANN_INLINE_END: |
50 | 15.1k | case MVM_SPESH_ANN_FH_END: |
51 | 15.1k | /* This moves to the previous instruction. */ |
52 | 15.1k | if (!prev) { |
53 | 0 | MVMSpeshBB *prev_bb = MVM_spesh_graph_linear_prev(tc, g, bb); |
54 | 0 | while (prev_bb && !prev_bb->last_ins) |
55 | 0 | prev_bb = MVM_spesh_graph_linear_prev(tc, g, prev_bb); |
56 | 0 | if (prev_bb) |
57 | 0 | prev = prev_bb->last_ins; |
58 | 0 | } |
59 | 15.1k | if (prev) { |
60 | 15.1k | ann->next = prev->annotations; |
61 | 15.1k | prev->annotations = ann; |
62 | 15.1k | } |
63 | 15.1k | break; |
64 | 37.4k | case MVM_SPESH_ANN_DEOPT_ONE_INS: |
65 | 37.4k | /* This moves to the previous instruction, but we need to put |
66 | 37.4k | * it on the end of the list, so the earlier deopt point will |
67 | 37.4k | * win when searching for deopt points. Otherwise, we can |
68 | 37.4k | * deopt to a later place than we should have. */ |
69 | 37.4k | if (!prev) { |
70 | 6.26k | MVMSpeshBB *prev_bb = MVM_spesh_graph_linear_prev(tc, g, bb); |
71 | 6.26k | while (prev_bb && !prev_bb->last_ins) |
72 | 0 | prev_bb = MVM_spesh_graph_linear_prev(tc, g, prev_bb); |
73 | 6.26k | if (prev_bb) |
74 | 6.26k | prev = prev_bb->last_ins; |
75 | 6.26k | } |
76 | 37.4k | if (prev) { |
77 | 37.4k | MVMSpeshAnn *append_to = prev->annotations; |
78 | 48.7k | while (append_to && append_to->next) |
79 | 11.3k | append_to = append_to->next; |
80 | 37.4k | if (append_to) |
81 | 24.3k | append_to->next = ann; |
82 | 37.4k | else |
83 | 13.0k | prev->annotations = ann; |
84 | 37.4k | ann->next = NULL; |
85 | 37.4k | } |
86 | 37.4k | break; |
87 | 87.3k | } |
88 | 87.3k | ins->annotations = ann_next; |
89 | 87.3k | } |
90 | 486k | |
91 | 486k | MVM_spesh_manipulate_cleanup_ins_deps(tc, g, ins); |
92 | 486k | } |
93 | | |
94 | | /* When deleting an instruction, we can mark any writes of the instruction as |
95 | | * dead, and also decrement the usage counts on anything that is read. This is |
96 | | * called by MVM_spesh_manipulate_delete_ins, but provided separately for when |
97 | | * an instruction goes away by virtue of a whole basic block dying. */ |
98 | 520k | void MVM_spesh_manipulate_cleanup_ins_deps(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { |
99 | 520k | if (ins->info->opcode == MVM_SSA_PHI) { |
100 | 255k | MVMint32 i; |
101 | 255k | MVM_spesh_get_facts(tc, g, ins->operands[0])->dead_writer = 1; |
102 | 6.66M | for (i = 1; i < ins->info->num_operands; i++) |
103 | 6.40M | MVM_spesh_get_facts(tc, g, ins->operands[i])->usages--; |
104 | 255k | } |
105 | 265k | else { |
106 | 265k | MVMint32 i; |
107 | 683k | for (i = 0; i < ins->info->num_operands; i++) { |
108 | 418k | MVMint32 rw = ins->info->operands[i] & MVM_operand_rw_mask; |
109 | 418k | if (rw == MVM_operand_write_reg) |
110 | 177k | MVM_spesh_get_facts(tc, g, ins->operands[i])->dead_writer = 1; |
111 | 241k | else if (rw == MVM_operand_read_reg) |
112 | 99.7k | MVM_spesh_get_facts(tc, g, ins->operands[i])->usages--; |
113 | 418k | } |
114 | 265k | } |
115 | 520k | } |
116 | | |
117 | | /* Inserts an instruction after the specified instruciton, or at the start of |
118 | | * the basic block if the instruction is NULL. */ |
119 | 196k | void MVM_spesh_manipulate_insert_ins(MVMThreadContext *tc, MVMSpeshBB *bb, MVMSpeshIns *previous, MVMSpeshIns *to_insert) { |
120 | 196k | MVMSpeshIns *next; |
121 | 196k | if (previous) { |
122 | 55.7k | next = previous->next; |
123 | 55.7k | previous->next = to_insert; |
124 | 140k | } else { |
125 | 140k | next = bb->first_ins; |
126 | 140k | bb->first_ins = to_insert; |
127 | 140k | } |
128 | 196k | to_insert->next = next; |
129 | 196k | if (next) { |
130 | 178k | next->prev = to_insert; |
131 | 17.9k | } else { |
132 | 17.9k | bb->last_ins = to_insert; |
133 | 17.9k | } |
134 | 196k | to_insert->prev = previous; |
135 | 196k | } |
136 | | |
137 | | /* Inserts a goto. */ |
138 | 8.81k | void MVM_spesh_manipulate_insert_goto(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins, MVMSpeshBB *target) { |
139 | 8.81k | MVMSpeshIns *inserted_goto = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
140 | 8.81k | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand )); |
141 | 8.81k | inserted_goto->info = MVM_op_get_op(MVM_OP_goto); |
142 | 8.81k | inserted_goto->operands = operands; |
143 | 8.81k | operands[0].ins_bb = target; |
144 | 8.81k | MVM_spesh_manipulate_insert_ins(tc, bb, ins, inserted_goto); |
145 | 8.81k | } |
146 | | |
147 | | /* Adds a successor to a basic block, also adding to the list of |
148 | | * predecessors of the added successor. */ |
149 | | void MVM_spesh_manipulate_add_successor(MVMThreadContext *tc, MVMSpeshGraph *g, |
150 | 0 | MVMSpeshBB *bb, MVMSpeshBB *succ) { |
151 | 0 | MVMSpeshBB **new_succ, **new_pred; |
152 | 0 |
|
153 | 0 | /* Add to successors. */ |
154 | 0 | new_succ = MVM_spesh_alloc(tc, g, (bb->num_succ + 1) * sizeof(MVMSpeshBB *)); |
155 | 0 | if (bb->num_succ) |
156 | 0 | memcpy(new_succ, bb->succ, bb->num_succ * sizeof(MVMSpeshBB *)); |
157 | 0 | new_succ[bb->num_succ] = succ; |
158 | 0 | bb->succ = new_succ; |
159 | 0 | bb->num_succ++; |
160 | 0 |
|
161 | 0 | /* And to successor's predecessors. */ |
162 | 0 | new_pred = MVM_spesh_alloc(tc, g, (succ->num_pred + 1) * sizeof(MVMSpeshBB *)); |
163 | 0 | if (succ->num_pred) |
164 | 0 | memcpy(new_pred, succ->pred, succ->num_pred * sizeof(MVMSpeshBB *)); |
165 | 0 | new_pred[succ->num_pred] = bb; |
166 | 0 | succ->pred = new_pred; |
167 | 0 | succ->num_pred++; |
168 | 0 | } |
169 | | |
170 | | /* Removes a successor to a basic block, also removing it from the list of |
171 | | * predecessors. */ |
172 | 33.0k | void MVM_spesh_manipulate_remove_successor(MVMThreadContext *tc, MVMSpeshBB *bb, MVMSpeshBB *succ) { |
173 | 33.0k | MVMSpeshBB ** const bb_succ = bb->succ; |
174 | 33.0k | MVMSpeshBB ** const succ_pred = succ->pred; |
175 | 33.0k | const MVMuint16 bb_num_succ = --bb->num_succ; |
176 | 33.0k | const MVMuint16 succ_num_pred = --succ->num_pred; |
177 | 33.0k | MVMuint16 i, k; |
178 | 33.0k | |
179 | 62.4k | for (i = 0; i <= bb_num_succ; i++) { |
180 | 62.4k | if (bb_succ[i] == succ) { |
181 | 33.0k | break; |
182 | 33.0k | } |
183 | 62.4k | } |
184 | 33.0k | |
185 | 33.0k | if (bb_succ[i] != succ) { |
186 | 0 | MVM_oops(tc, "Didn't find the successor to remove from a Spesh Basic Block"); |
187 | 0 | } |
188 | 33.0k | |
189 | 33.0k | /* Remove the succ from the list, shuffle other successors back in place. */ |
190 | 84.1k | for (k = i; k < bb_num_succ; k++) { |
191 | 51.0k | bb_succ[k] = bb_succ[k + 1]; |
192 | 51.0k | } |
193 | 33.0k | |
194 | 33.0k | bb_succ[bb_num_succ] = NULL; |
195 | 33.0k | |
196 | 33.0k | /* Now hunt the bb in the succ's pred, so that we remove all traces of the connection. */ |
197 | 1.92M | for (i = 0; i <= succ_num_pred; i++) { |
198 | 1.92M | if (succ_pred[i] == bb) { |
199 | 33.0k | break; |
200 | 33.0k | } |
201 | 1.92M | } |
202 | 33.0k | |
203 | 33.0k | if (succ_pred[i] != bb) { |
204 | 0 | MVM_oops(tc, "Didn't find the predecessor to remove from a Spesh Basic Block"); |
205 | 0 | } |
206 | 33.0k | |
207 | 1.95M | for (k = i; k < succ_num_pred; k++) { |
208 | 1.91M | succ_pred[k] = succ_pred[k + 1]; |
209 | 1.91M | } |
210 | 33.0k | |
211 | 33.0k | succ_pred[succ_num_pred] = NULL; |
212 | 33.0k | } |
213 | | |
214 | | /* Removes successors from a basic block that point to handlers. |
215 | | Useful for optimizations that turn throwish ops into non-throwing ones. */ |
216 | 63.2k | void MVM_spesh_manipulate_remove_handler_successors(MVMThreadContext *tc, MVMSpeshBB *bb) { |
217 | 63.2k | int i; |
218 | 89.9k | for (i = 0; i < bb->num_handler_succ; i++) { |
219 | 26.7k | MVM_spesh_manipulate_remove_successor(tc, bb, bb->handler_succ[i]); |
220 | 26.7k | bb->handler_succ[i] = NULL; |
221 | 26.7k | } |
222 | 63.2k | bb->num_handler_succ = 0; |
223 | 63.2k | } |
224 | | |
225 | | /* Gets a temporary register of the specified kind to use in some transform. |
226 | | * Will only actually extend the frame if needed; if an existing temporary |
227 | | * was requested and then released, then it will just use a new version of |
228 | | * that. */ |
229 | 18.2k | MVMSpeshOperand MVM_spesh_manipulate_get_temp_reg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 kind) { |
230 | 18.2k | MVMSpeshOperand result; |
231 | 18.2k | MVMSpeshFacts **new_facts; |
232 | 18.2k | MVMuint16 *new_fact_counts; |
233 | 18.2k | MVMuint16 i; |
234 | 18.2k | |
235 | 18.2k | /* First, see if we can find an existing free temporary; use it if so. */ |
236 | 26.9k | for (i = 0; i < g->num_temps; i++) { |
237 | 19.3k | if (g->temps[i].kind == kind && !g->temps[i].in_use) { |
238 | 10.6k | /* Add new facts slot. */ |
239 | 10.6k | MVMuint16 orig = g->temps[i].orig; |
240 | 10.6k | MVMSpeshFacts *new_fact_row = MVM_spesh_alloc(tc, g, |
241 | 10.6k | (g->fact_counts[orig] + 1) * sizeof(MVMSpeshFacts)); |
242 | 10.6k | memcpy(new_fact_row, g->facts[orig], |
243 | 10.6k | g->fact_counts[orig] * sizeof(MVMSpeshFacts)); |
244 | 10.6k | g->facts[orig] = new_fact_row; |
245 | 10.6k | g->fact_counts[orig]++; |
246 | 10.6k | |
247 | 10.6k | /* Mark it in use and add extra version. */ |
248 | 10.6k | g->temps[i].in_use++; |
249 | 10.6k | g->temps[i].i++; |
250 | 10.6k | |
251 | 10.6k | /* Produce and return result. */ |
252 | 10.6k | result.reg.orig = orig; |
253 | 10.6k | result.reg.i = g->temps[i].i; |
254 | 10.6k | return result; |
255 | 10.6k | } |
256 | 19.3k | } |
257 | 18.2k | |
258 | 18.2k | /* Make sure we've space in the temporaries store. */ |
259 | 7.60k | if (g->num_temps == g->alloc_temps) { |
260 | 4.75k | MVMSpeshTemporary *new_temps; |
261 | 4.75k | g->alloc_temps += 4; |
262 | 4.75k | new_temps = MVM_spesh_alloc(tc, g, g->alloc_temps * sizeof(MVMSpeshTemporary)); |
263 | 4.75k | if (g->num_temps) |
264 | 0 | memcpy(new_temps, g->temps, g->num_temps * sizeof(MVMSpeshTemporary)); |
265 | 4.75k | g->temps = new_temps; |
266 | 4.75k | } |
267 | 7.60k | |
268 | 7.60k | /* Allocate temporary and set up result. */ |
269 | 7.60k | g->temps[g->num_temps].orig = result.reg.orig = g->num_locals; |
270 | 7.60k | g->temps[g->num_temps].i = result.reg.i = 0; |
271 | 7.60k | g->temps[g->num_temps].kind = kind; |
272 | 7.60k | g->temps[g->num_temps].in_use = 1; |
273 | 7.60k | g->num_temps++; |
274 | 7.60k | |
275 | 7.60k | /* Add locals table entry. */ |
276 | 7.60k | if (!g->local_types) { |
277 | 4.45k | MVMint32 local_types_size = g->num_locals * sizeof(MVMuint16); |
278 | 4.45k | g->local_types = MVM_malloc(local_types_size); |
279 | 4.45k | memcpy(g->local_types, g->sf->body.local_types, local_types_size); |
280 | 4.45k | } |
281 | 7.60k | g->local_types = MVM_realloc(g->local_types, (g->num_locals + 1) * sizeof(MVMuint16)); |
282 | 7.60k | g->local_types[g->num_locals] = kind; |
283 | 7.60k | |
284 | 7.60k | /* Add facts table entry. */ |
285 | 7.60k | new_facts = MVM_spesh_alloc(tc, g, (g->num_locals + 1) * sizeof(MVMSpeshFacts *)); |
286 | 7.60k | new_fact_counts = MVM_spesh_alloc(tc, g, (g->num_locals + 1) * sizeof(MVMuint16)); |
287 | 7.60k | memcpy(new_facts, g->facts, g->num_locals * sizeof(MVMSpeshFacts *)); |
288 | 7.60k | memcpy(new_fact_counts, g->fact_counts, g->num_locals * sizeof(MVMuint16)); |
289 | 7.60k | new_facts[g->num_locals] = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshFacts)); |
290 | 7.60k | new_fact_counts[g->num_locals] = 1; |
291 | 7.60k | g->facts = new_facts; |
292 | 7.60k | g->fact_counts = new_fact_counts; |
293 | 7.60k | |
294 | 7.60k | /* Increment number of locals. */ |
295 | 7.60k | g->num_locals++; |
296 | 7.60k | |
297 | 7.60k | return result; |
298 | 18.2k | } |
299 | | |
300 | | /* Releases a temporary register, so it can be used again later. */ |
301 | 18.2k | void MVM_spesh_manipulate_release_temp_reg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand temp) { |
302 | 18.2k | MVMuint16 i; |
303 | 26.9k | for (i = 0; i < g->num_temps; i++) { |
304 | 26.9k | if (g->temps[i].orig == temp.reg.orig && g->temps[i].i == temp.reg.i) { |
305 | 18.2k | if (g->temps[i].in_use) |
306 | 18.2k | g->temps[i].in_use = 0; |
307 | 18.2k | else |
308 | 0 | MVM_oops(tc, "Spesh: releasing temp not in use"); |
309 | 18.2k | return; |
310 | 18.2k | } |
311 | 26.9k | } |
312 | 0 | MVM_oops(tc, "Spesh: releasing non-existing temp"); |
313 | 0 | } |
314 | | |
315 | | |
316 | 0 | MVMSpeshBB *MVM_spesh_manipulate_split_BB_at(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
317 | 0 | MVMSpeshBB *new_bb = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB)); |
318 | 0 | MVMSpeshBB *linear_next = bb->linear_next; |
319 | 0 |
|
320 | 0 | /* Step one: insert the new BB into the linear order. */ |
321 | 0 | bb->linear_next = new_bb; |
322 | 0 | new_bb->linear_next = linear_next; |
323 | 0 |
|
324 | 0 | /* Step two: update all idx fields. */ |
325 | 0 | new_bb->idx = bb->idx + 1; |
326 | 0 | { |
327 | 0 | MVMSpeshBB *ptr = linear_next; |
328 | 0 | while (ptr != NULL) { |
329 | 0 | ptr->idx += 1; |
330 | 0 | ptr = ptr->linear_next; |
331 | 0 | } |
332 | 0 | } |
333 | 0 |
|
334 | 0 | /* Step three: fix up the dominator tree. */ |
335 | 0 | new_bb->children = bb->children; |
336 | 0 | new_bb->num_children = bb->num_children; |
337 | 0 |
|
338 | 0 | /* We expect the user of this API to fill whatever BB the code |
339 | 0 | * will additionally branch into into the children list, as well. |
340 | 0 | * Hopefully, setting num_children to 2 makes the code crash in case |
341 | 0 | * that step has been forgotten. */ |
342 | 0 | bb->children = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *) * 2); |
343 | 0 | bb->num_children = 2; |
344 | 0 | bb->children[0] = new_bb; |
345 | 0 | bb->children[1] = 0; |
346 | 0 |
|
347 | 0 | /* Step three: fix up succs and preds. */ |
348 | 0 | new_bb->pred = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *)); |
349 | 0 | new_bb->num_pred = 1; |
350 | 0 | new_bb->pred[0] = bb; |
351 | 0 |
|
352 | 0 | new_bb->succ = bb->succ; |
353 | 0 |
|
354 | 0 | /* We assume the reason for the split is to add a new succ in the middle |
355 | 0 | * which is why we allocate two slots instead of 1. */ |
356 | 0 | bb->succ = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *) * 2); |
357 | 0 | bb->num_succ = 2; |
358 | 0 | bb->succ[0] = new_bb; |
359 | 0 | bb->succ[1] = 0; |
360 | 0 |
|
361 | 0 | new_bb->initial_pc = bb->initial_pc; |
362 | 0 |
|
363 | 0 | new_bb->num_df = 0; |
364 | 0 |
|
365 | 0 | /* Last step: Transfer over the instructions after the split point. */ |
366 | 0 | new_bb->last_ins = bb->last_ins; |
367 | 0 | bb->last_ins = ins->prev; |
368 | 0 | new_bb->first_ins = ins; |
369 | 0 | ins->prev->next = NULL; |
370 | 0 | ins->prev = NULL; |
371 | 0 |
|
372 | 0 | return new_bb; |
373 | 0 | } |