/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 | 555k | void MVM_spesh_manipulate_delete_ins(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
7 | 555k | /* Remove it from the double linked list. */ |
8 | 555k | MVMSpeshIns *prev = ins->prev; |
9 | 555k | MVMSpeshIns *next = ins->next; |
10 | 555k | if (prev) |
11 | 474k | prev->next = next; |
12 | 555k | else |
13 | 81.7k | bb->first_ins = next; |
14 | 555k | if (next) |
15 | 538k | next->prev = prev; |
16 | 555k | else |
17 | 17.4k | bb->last_ins = prev; |
18 | 555k | |
19 | 555k | /* Move it's annotations. */ |
20 | 606k | while (ins->annotations) { |
21 | 50.4k | MVMSpeshAnn *ann = ins->annotations; |
22 | 50.4k | MVMSpeshAnn *ann_next = ann->next; |
23 | 50.4k | switch (ann->type) { |
24 | 6.74k | case MVM_SPESH_ANN_FH_START: |
25 | 6.74k | case MVM_SPESH_ANN_FH_GOTO: |
26 | 6.74k | case MVM_SPESH_ANN_INLINE_START: |
27 | 6.74k | case MVM_SPESH_ANN_DEOPT_OSR: |
28 | 6.74k | if (!next && bb->linear_next) |
29 | 0 | next = bb->linear_next->first_ins; |
30 | 6.74k | if (next) { |
31 | 6.74k | ann->next = next->annotations; |
32 | 6.74k | next->annotations = ann; |
33 | 6.74k | } |
34 | 6.74k | break; |
35 | 43.6k | case MVM_SPESH_ANN_FH_END: |
36 | 43.6k | case MVM_SPESH_ANN_DEOPT_ONE_INS: |
37 | 43.6k | if (!prev) { |
38 | 24.5k | MVMSpeshBB *prev_bb = MVM_spesh_graph_linear_prev(tc, g, bb); |
39 | 24.5k | if (prev_bb) |
40 | 24.5k | prev = prev_bb->last_ins; |
41 | 24.5k | } |
42 | 43.6k | if (prev) { |
43 | 43.6k | ann->next = prev->annotations; |
44 | 43.6k | prev->annotations = ann; |
45 | 43.6k | } |
46 | 43.6k | break; |
47 | 50.4k | } |
48 | 50.4k | ins->annotations = ann_next; |
49 | 50.4k | } |
50 | 555k | } |
51 | | |
52 | 264k | void MVM_spesh_manipulate_insert_ins(MVMThreadContext *tc, MVMSpeshBB *bb, MVMSpeshIns *previous, MVMSpeshIns *to_insert) { |
53 | 264k | MVMSpeshIns *next; |
54 | 264k | if (previous) { |
55 | 86.1k | next = previous->next; |
56 | 86.1k | previous->next = to_insert; |
57 | 178k | } else { |
58 | 178k | next = bb->first_ins; |
59 | 178k | bb->first_ins = to_insert; |
60 | 178k | } |
61 | 264k | to_insert->next = next; |
62 | 264k | if (next) { |
63 | 255k | next->prev = to_insert; |
64 | 8.57k | } else { |
65 | 8.57k | bb->last_ins = to_insert; |
66 | 8.57k | } |
67 | 264k | to_insert->prev = previous; |
68 | 264k | } |
69 | | |
70 | | /* Inserts a goto. */ |
71 | 6.28k | void MVM_spesh_manipulate_insert_goto(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins, MVMSpeshBB *target) { |
72 | 6.28k | MVMSpeshIns *inserted_goto = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); |
73 | 6.28k | MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand )); |
74 | 6.28k | inserted_goto->info = MVM_op_get_op(MVM_OP_goto); |
75 | 6.28k | inserted_goto->operands = operands; |
76 | 6.28k | operands[0].ins_bb = target; |
77 | 6.28k | MVM_spesh_manipulate_insert_ins(tc, bb, ins, inserted_goto); |
78 | 6.28k | } |
79 | | |
80 | 8.48k | void MVM_spesh_manipulate_remove_successor(MVMThreadContext *tc, MVMSpeshBB *bb, MVMSpeshBB *succ) { |
81 | 8.48k | MVMSpeshBB ** const bb_succ = bb->succ; |
82 | 8.48k | MVMSpeshBB ** const succ_pred = succ->pred; |
83 | 8.48k | const MVMuint16 bb_num_succ = --bb->num_succ; |
84 | 8.48k | const MVMuint16 succ_num_pred = --succ->num_pred; |
85 | 8.48k | MVMuint16 i, k; |
86 | 8.48k | |
87 | 11.2k | for (i = 0; i <= bb_num_succ; i++) { |
88 | 11.2k | if (bb_succ[i] == succ) { |
89 | 8.48k | break; |
90 | 8.48k | } |
91 | 11.2k | } |
92 | 8.48k | |
93 | 8.48k | if (bb_succ[i] != succ) { |
94 | 0 | MVM_oops(tc, "Didn't find the successor to remove from a Spesh Basic Block"); |
95 | 0 | } |
96 | 8.48k | |
97 | 8.48k | /* Remove the succ from the list, shuffle other successors back in place */ |
98 | 14.1k | for (k = i; k < bb_num_succ; k++) { |
99 | 5.68k | bb_succ[k] = bb_succ[k + 1]; |
100 | 5.68k | } |
101 | 8.48k | |
102 | 8.48k | bb_succ[bb_num_succ] = NULL; |
103 | 8.48k | |
104 | 8.48k | /* Now hunt the bb in the succ's pred, so that we remove all traces of the connection */ |
105 | 8.84k | for (i = 0; i <= succ_num_pred; i++) { |
106 | 8.84k | if (succ_pred[i] == bb) { |
107 | 8.48k | break; |
108 | 8.48k | } |
109 | 8.84k | } |
110 | 8.48k | |
111 | 8.48k | if (succ_pred[i] != bb) { |
112 | 0 | MVM_oops(tc, "Didn't find the predecessor to remove from a Spesh Basic Block"); |
113 | 0 | } |
114 | 8.48k | |
115 | 13.5k | for (k = i; k < succ_num_pred; k++) { |
116 | 5.03k | succ_pred[k] = succ_pred[k + 1]; |
117 | 5.03k | } |
118 | 8.48k | |
119 | 8.48k | succ_pred[succ_num_pred] = NULL; |
120 | 8.48k | } |
121 | | |
122 | | /* Gets a temporary register of the specified kind to use in some transform. |
123 | | * Will only actaully extend the frame if needed; if an existing temproary |
124 | | * was requested and then released, then it will just use a new version of |
125 | | * that. */ |
126 | 6.63k | MVMSpeshOperand MVM_spesh_manipulate_get_temp_reg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 kind) { |
127 | 6.63k | MVMSpeshOperand result; |
128 | 6.63k | MVMSpeshFacts **new_facts; |
129 | 6.63k | MVMuint16 *new_fact_counts; |
130 | 6.63k | MVMuint16 i; |
131 | 6.63k | |
132 | 6.63k | /* First, see if we can find an existing free temporary; use it if so. */ |
133 | 9.18k | for (i = 0; i < g->num_temps; i++) { |
134 | 3.33k | if (g->temps[i].kind == kind && !g->temps[i].in_use) { |
135 | 785 | /* Add new facts slot. */ |
136 | 785 | MVMuint16 orig = g->temps[i].orig; |
137 | 785 | MVMSpeshFacts *new_fact_row = MVM_spesh_alloc(tc, g, |
138 | 785 | (g->fact_counts[orig] + 1) * sizeof(MVMSpeshFacts)); |
139 | 785 | memcpy(new_fact_row, g->facts[orig], |
140 | 785 | g->fact_counts[orig] * sizeof(MVMSpeshFacts)); |
141 | 785 | g->facts[orig] = new_fact_row; |
142 | 785 | g->fact_counts[orig]++; |
143 | 785 | |
144 | 785 | /* Mark it in use an add extra version. */ |
145 | 785 | g->temps[i].in_use++; |
146 | 785 | g->temps[i].i++; |
147 | 785 | |
148 | 785 | /* Produce and return result. */ |
149 | 785 | result.reg.orig = orig; |
150 | 785 | result.reg.i = g->temps[i].i; |
151 | 785 | return result; |
152 | 785 | } |
153 | 3.33k | } |
154 | 6.63k | |
155 | 6.63k | /* Make sure we've space in the temporaries store. */ |
156 | 5.84k | if (g->num_temps == g->alloc_temps) { |
157 | 3.76k | MVMSpeshTemporary *new_temps; |
158 | 3.76k | g->alloc_temps += 4; |
159 | 3.76k | new_temps = MVM_spesh_alloc(tc, g, g->alloc_temps * sizeof(MVMSpeshTemporary)); |
160 | 3.76k | if (g->num_temps) |
161 | 1 | memcpy(new_temps, g->temps, g->num_temps * sizeof(MVMSpeshTemporary)); |
162 | 3.76k | g->temps = new_temps; |
163 | 3.76k | } |
164 | 5.84k | |
165 | 5.84k | /* Allocate temporary and set up result. */ |
166 | 5.84k | g->temps[g->num_temps].orig = result.reg.orig = g->num_locals; |
167 | 5.84k | g->temps[g->num_temps].i = result.reg.i = 0; |
168 | 5.84k | g->temps[g->num_temps].kind = kind; |
169 | 5.84k | g->temps[g->num_temps].in_use = 1; |
170 | 5.84k | g->num_temps++; |
171 | 5.84k | |
172 | 5.84k | /* Add locals table entry. */ |
173 | 5.84k | if (!g->local_types) { |
174 | 3.48k | MVMint32 local_types_size = g->num_locals * sizeof(MVMuint16); |
175 | 3.48k | g->local_types = MVM_malloc(local_types_size); |
176 | 3.48k | memcpy(g->local_types, g->sf->body.local_types, local_types_size); |
177 | 3.48k | } |
178 | 5.84k | g->local_types = MVM_realloc(g->local_types, (g->num_locals + 1) * sizeof(MVMuint16)); |
179 | 5.84k | g->local_types[g->num_locals] = kind; |
180 | 5.84k | |
181 | 5.84k | /* Add facts table entry. */ |
182 | 5.84k | new_facts = MVM_spesh_alloc(tc, g, (g->num_locals + 1) * sizeof(MVMSpeshFacts *)); |
183 | 5.84k | new_fact_counts = MVM_spesh_alloc(tc, g, (g->num_locals + 1) * sizeof(MVMuint16)); |
184 | 5.84k | memcpy(new_facts, g->facts, g->num_locals * sizeof(MVMSpeshFacts *)); |
185 | 5.84k | memcpy(new_fact_counts, g->fact_counts, g->num_locals * sizeof(MVMuint16)); |
186 | 5.84k | new_facts[g->num_locals] = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshFacts)); |
187 | 5.84k | new_fact_counts[g->num_locals] = 1; |
188 | 5.84k | g->facts = new_facts; |
189 | 5.84k | g->fact_counts = new_fact_counts; |
190 | 5.84k | |
191 | 5.84k | /* Increment number of locals. */ |
192 | 5.84k | g->num_locals++; |
193 | 5.84k | |
194 | 5.84k | return result; |
195 | 6.63k | } |
196 | | |
197 | | /* Releases a temporary register, so it can be used again later. */ |
198 | 6.50k | void MVM_spesh_manipulate_release_temp_reg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand temp) { |
199 | 6.50k | MVMuint16 i; |
200 | 8.97k | for (i = 0; i < g->num_temps; i++) { |
201 | 8.97k | if (g->temps[i].orig == temp.reg.orig && g->temps[i].i == temp.reg.i) { |
202 | 6.50k | if (g->temps[i].in_use) |
203 | 6.50k | g->temps[i].in_use = 0; |
204 | 6.50k | else |
205 | 0 | MVM_oops(tc, "Spesh: releasing temp not in use"); |
206 | 6.50k | return; |
207 | 6.50k | } |
208 | 8.97k | } |
209 | 0 | MVM_oops(tc, "Spesh: releasing non-existing temp"); |
210 | 0 | } |
211 | | |
212 | | |
213 | 0 | MVMSpeshBB *MVM_spesh_manipulate_split_BB_at(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { |
214 | 0 | MVMSpeshBB *new_bb = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB)); |
215 | 0 | MVMSpeshBB *linear_next = bb->linear_next; |
216 | 0 |
|
217 | 0 | /* Step one: insert the new BB into the linear order */ |
218 | 0 | bb->linear_next = new_bb; |
219 | 0 | new_bb->linear_next = linear_next; |
220 | 0 |
|
221 | 0 | /* Step two: update all idx fields. */ |
222 | 0 | new_bb->idx = bb->idx + 1; |
223 | 0 | { |
224 | 0 | MVMSpeshBB *ptr = linear_next; |
225 | 0 | while (ptr != NULL) { |
226 | 0 | ptr->idx += 1; |
227 | 0 | ptr = ptr->linear_next; |
228 | 0 | } |
229 | 0 | } |
230 | 0 |
|
231 | 0 | /* Step three: fix up the dominator tree */ |
232 | 0 | new_bb->children = bb->children; |
233 | 0 | new_bb->num_children = bb->num_children; |
234 | 0 |
|
235 | 0 | /* We expect the user of this API to fill whatever BB the code |
236 | 0 | * will additionally branch into into the children list, as well. |
237 | 0 | * Hopefully, setting num_children to 2 makes the code crash in case |
238 | 0 | * that step has been forgotten. */ |
239 | 0 | bb->children = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *) * 2); |
240 | 0 | bb->num_children = 2; |
241 | 0 | bb->children[0] = new_bb; |
242 | 0 | bb->children[1] = 0; |
243 | 0 |
|
244 | 0 | /* Step three: fix up succs and preds */ |
245 | 0 | new_bb->pred = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *)); |
246 | 0 | new_bb->num_pred = 1; |
247 | 0 | new_bb->pred[0] = bb; |
248 | 0 |
|
249 | 0 | new_bb->succ = bb->succ; |
250 | 0 |
|
251 | 0 | /* We assume the reason for the split is to add a new succ in the middle |
252 | 0 | * which is why we allocate two slots instead of 1 */ |
253 | 0 | bb->succ = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *) * 2); |
254 | 0 | bb->num_succ = 2; |
255 | 0 | bb->succ[0] = new_bb; |
256 | 0 | bb->succ[1] = 0; |
257 | 0 |
|
258 | 0 | new_bb->initial_pc = bb->initial_pc; |
259 | 0 |
|
260 | 0 | new_bb->num_df = 0; |
261 | 0 |
|
262 | 0 | /* Last step: Transfer over the instructions after the split point */ |
263 | 0 | new_bb->last_ins = bb->last_ins; |
264 | 0 | bb->last_ins = ins->prev; |
265 | 0 | new_bb->first_ins = ins; |
266 | 0 | ins->prev->next = NULL; |
267 | 0 | ins->prev = NULL; |
268 | 0 |
|
269 | 0 | return new_bb; |
270 | 0 | } |