Coverage Report

Created: 2018-07-03 15:31

/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
}