Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/spesh/graph.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* This is where the spesh stuff all begins. The logic in here takes bytecode
4
 * and builds a spesh graph from it. This is a CFG in SSA form. Transforming
5
 * to SSA involves computing dominance frontiers, done by the algorithm found
6
 * in http://www.cs.rice.edu/~keith/EMBED/dom.pdf. The SSA algorithm itself is
7
 * from http://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf. */
8
9
0
#define GET_I8(pc, idx)     *((MVMint8 *)((pc) + (idx)))
10
#define GET_UI8(pc, idx)    *((MVMuint8 *)((pc) + (idx)))
11
406k
#define GET_I16(pc, idx)    *((MVMint16 *)((pc) + (idx)))
12
2.14M
#define GET_UI16(pc, idx)   *((MVMuint16 *)((pc) + (idx)))
13
14
#define GET_I32(pc, idx)    *((MVMint32 *)((pc) + (idx)))
14
317k
#define GET_UI32(pc, idx)   *((MVMuint32 *)((pc) + (idx)))
15
0
#define GET_N32(pc, idx)    *((MVMnum32 *)((pc) + (idx)))
16
17
/* Allocate a piece of memory from the spesh graph's region
18
 * allocator. Deallocated when the spesh graph is. */
19
8.25M
void * MVM_spesh_alloc(MVMThreadContext *tc, MVMSpeshGraph *g, size_t bytes) {
20
8.25M
    return MVM_region_alloc(tc, &g->region_alloc, bytes);
21
8.25M
}
22
23
/* Looks up op info; doesn't sanity check, since we should be working on code
24
 * that already pass validation. */
25
1.34M
static const MVMOpInfo * get_op_info(MVMThreadContext *tc, MVMCompUnit *cu, MVMuint16 opcode) {
26
1.34M
    if (opcode < MVM_OP_EXT_BASE) {
27
1.34M
        return MVM_op_get_op(opcode);
28
1.34M
    }
29
0
    else {
30
0
        MVMuint16       index  = opcode - MVM_OP_EXT_BASE;
31
0
        MVMExtOpRecord *record = &cu->body.extops[index];
32
0
        return MVM_ext_resolve_extop_record(tc, record);
33
0
    }
34
1.34M
}
35
36
/* Records a de-optimization annotation and mapping pair. */
37
static void add_deopt_annotation(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins_node,
38
129k
                                 MVMuint8 *pc, MVMint32 type) {
39
129k
    /* Add an the annotations. */
40
129k
    MVMSpeshAnn *ann      = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
41
129k
    ann->type             = type;
42
129k
    ann->data.deopt_idx   = g->num_deopt_addrs;
43
129k
    ann->next             = ins_node->annotations;
44
129k
    ins_node->annotations = ann;
45
129k
46
129k
    /* Record PC in the deopt entries table. */
47
129k
    if (g->num_deopt_addrs == g->alloc_deopt_addrs) {
48
38.2k
        g->alloc_deopt_addrs += 4;
49
38.2k
        if (g->deopt_addrs)
50
25.1k
            g->deopt_addrs = MVM_realloc(g->deopt_addrs,
51
25.1k
                g->alloc_deopt_addrs * sizeof(MVMint32) * 2);
52
38.2k
        else
53
13.0k
            g->deopt_addrs = MVM_malloc(g->alloc_deopt_addrs * sizeof(MVMint32) * 2);
54
38.2k
    }
55
129k
    g->deopt_addrs[2 * g->num_deopt_addrs] = pc - g->bytecode;
56
129k
    g->num_deopt_addrs++;
57
129k
}
58
59
/* Finds the linearly previous basic block (not cheap, but uncommon). */
60
26.5k
MVMSpeshBB * MVM_spesh_graph_linear_prev(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *search) {
61
26.5k
    MVMSpeshBB *bb = g->entry;
62
3.70M
    while (bb) {
63
3.70M
        if (bb->linear_next == search)
64
26.5k
            return bb;
65
3.68M
        bb = bb->linear_next;
66
3.68M
    }
67
0
    return NULL;
68
26.5k
}
69
70
/* Builds the control flow graph, populating the passed spesh graph structure
71
 * with it. This also makes nodes for all of the instruction. */
72
2.82M
#define MVM_CFG_BB_START    1
73
1.66M
#define MVM_CFG_BB_END      2
74
static void build_cfg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMStaticFrame *sf,
75
21.8k
                      MVMint32 *existing_deopts, MVMint32 num_existing_deopts) {
76
21.8k
    MVMSpeshBB  *cur_bb, *prev_bb;
77
21.8k
    MVMSpeshIns *last_ins;
78
21.8k
    MVMint64     i;
79
21.8k
    MVMint32     bb_idx;
80
21.8k
81
21.8k
    /* Temporary array of all MVMSpeshIns we create (one per instruction).
82
21.8k
     * Overestimate at size. Has the flat view, matching the bytecode. */
83
21.8k
    MVMSpeshIns **ins_flat = MVM_calloc(g->bytecode_size / 2, sizeof(MVMSpeshIns *));
84
21.8k
85
21.8k
    /* Temporary array where each byte in the input bytecode gets a 32-bit
86
21.8k
     * integer. This is used for two things:
87
21.8k
     * A) When we make the MVMSpeshIns for an instruction starting at the
88
21.8k
     *    byte, we put the instruction index (into ins_flat) in the slot,
89
21.8k
     *    shifting it by 2 bits to the left. We will use this to do fixups.
90
21.8k
     * B) The first bit is "I have an incoming branch" - that is, start of
91
21.8k
     *    a basic block. The second bit is "I can branch" - that is, end of
92
21.8k
     *    a basic block. It's possible to have both bits set.
93
21.8k
     * Anything that's just a zero has no instruction starting there. */
94
21.8k
    MVMuint32 *byte_to_ins_flags = MVM_calloc(g->bytecode_size, sizeof(MVMuint32));
95
21.8k
96
21.8k
    /* Instruction to basic block mapping. Initialized later. */
97
21.8k
    MVMSpeshBB **ins_to_bb = NULL;
98
21.8k
99
21.8k
    /* Make first pass through the bytecode. In this pass, we make MVMSpeshIns
100
21.8k
     * nodes for each instruction and set the start/end of block bits. Also
101
21.8k
     * set handler targets as basic block starters. */
102
21.8k
    MVMCompUnit *cu       = sf->body.cu;
103
21.8k
    MVMuint8    *pc       = g->bytecode;
104
21.8k
    MVMuint8    *end      = g->bytecode + g->bytecode_size;
105
21.8k
    MVMuint32    ins_idx  = 0;
106
21.8k
    MVMuint8     next_bbs = 1; /* Next iteration (here, first) starts a BB. */
107
27.0k
    for (i = 0; i < g->num_handlers; i++)
108
5.25k
        byte_to_ins_flags[g->handlers[i].goto_offset] |= MVM_CFG_BB_START;
109
1.34M
    while (pc < end) {
110
1.32M
        /* Look up op info. */
111
1.32M
        MVMuint16  opcode     = *(MVMuint16 *)pc;
112
1.32M
        MVMuint8  *args       = pc + 2;
113
1.32M
        MVMuint8   arg_size   = 0;
114
1.32M
        const MVMOpInfo *info = get_op_info(tc, cu, opcode);
115
1.32M
116
1.32M
        /* Create an instruction node, add it, and record its position. */
117
1.32M
        MVMSpeshIns *ins_node = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
118
1.32M
        ins_flat[ins_idx] = ins_node;
119
1.32M
        byte_to_ins_flags[pc - g->bytecode] |= ins_idx << 2;
120
1.32M
121
1.32M
        /* Did previous instruction end a basic block? */
122
1.32M
        if (next_bbs) {
123
231k
            byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_START;
124
231k
            next_bbs = 0;
125
231k
        }
126
1.32M
127
1.32M
        /* Also check we're not already a BB start due to being a branch
128
1.32M
         * target, in which case we should ensure our prior is marked as
129
1.32M
         * a BB end. */
130
1.09M
        else {
131
1.09M
            if (byte_to_ins_flags[pc - g->bytecode] & MVM_CFG_BB_START) {
132
75.6k
                MVMuint32 hunt = pc - g->bytecode;
133
538k
                while (!byte_to_ins_flags[--hunt]);
134
75.6k
                byte_to_ins_flags[hunt] |= MVM_CFG_BB_END;
135
75.6k
            }
136
1.09M
        }
137
1.32M
138
1.32M
        /* Store opcode */
139
1.32M
        ins_node->info = info;
140
1.32M
141
1.32M
        /* Go over operands. */
142
1.32M
        ins_node->operands = MVM_spesh_alloc(tc, g, info->num_operands * sizeof(MVMSpeshOperand));
143
4.17M
        for (i = 0; i < info->num_operands; i++) {
144
2.84M
            MVMuint8 flags = info->operands[i];
145
2.84M
            MVMuint8 rw    = flags & MVM_operand_rw_mask;
146
2.84M
            switch (rw) {
147
2.04M
            case MVM_operand_read_reg:
148
2.04M
            case MVM_operand_write_reg:
149
2.04M
                ins_node->operands[i].reg.orig = GET_UI16(args, arg_size);
150
2.04M
                arg_size += 2;
151
2.04M
                break;
152
29.9k
            case MVM_operand_read_lex:
153
29.9k
            case MVM_operand_write_lex:
154
29.9k
                ins_node->operands[i].lex.idx    = GET_UI16(args, arg_size);
155
29.9k
                ins_node->operands[i].lex.outers = GET_UI16(args, arg_size + 2);
156
29.9k
                arg_size += 4;
157
29.9k
                break;
158
770k
            case MVM_operand_literal: {
159
770k
                MVMuint32 type = flags & MVM_operand_type_mask;
160
770k
                switch (type) {
161
0
                case MVM_operand_int8:
162
0
                    ins_node->operands[i].lit_i8 = GET_I8(args, arg_size);
163
0
                    arg_size += 1;
164
0
                    break;
165
397k
                case MVM_operand_int16:
166
397k
                    ins_node->operands[i].lit_i16 = GET_I16(args, arg_size);
167
397k
                    arg_size += 2;
168
397k
                    break;
169
14
                case MVM_operand_int32:
170
14
                    ins_node->operands[i].lit_i32 = GET_I32(args, arg_size);
171
14
                    arg_size += 4;
172
14
                    break;
173
5.42k
                case MVM_operand_int64:
174
5.42k
                    ins_node->operands[i].lit_i64 = MVM_BC_get_I64(args, arg_size);
175
5.42k
                    arg_size += 8;
176
5.42k
                    break;
177
0
                case MVM_operand_num32:
178
0
                    ins_node->operands[i].lit_n32 = GET_N32(args, arg_size);
179
0
                    arg_size += 4;
180
0
                    break;
181
6
                case MVM_operand_num64:
182
6
                    ins_node->operands[i].lit_n64 = MVM_BC_get_N64(args, arg_size);
183
6
                    arg_size += 8;
184
6
                    break;
185
39.8k
                case MVM_operand_callsite:
186
39.8k
                    ins_node->operands[i].callsite_idx = GET_UI16(args, arg_size);
187
39.8k
                    arg_size += 2;
188
39.8k
                    break;
189
886
                case MVM_operand_coderef:
190
886
                    ins_node->operands[i].coderef_idx = GET_UI16(args, arg_size);
191
886
                    arg_size += 2;
192
886
                    break;
193
155k
                case MVM_operand_str:
194
155k
                    ins_node->operands[i].lit_str_idx = GET_UI32(args, arg_size);
195
155k
                    arg_size += 4;
196
155k
                    break;
197
162k
                case MVM_operand_ins: {
198
162k
                    /* Stash instruction offset. */
199
162k
                    MVMuint32 target = GET_UI32(args, arg_size);
200
162k
                    ins_node->operands[i].ins_offset = target;
201
162k
202
162k
                    /* This is a branching instruction, so it's a BB end. */
203
162k
                    byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END;
204
162k
205
162k
                    /* Its target is a BB start, and any previous instruction
206
162k
                     * we already passed needs marking as a BB end. */
207
162k
                    byte_to_ins_flags[target] |= MVM_CFG_BB_START;
208
162k
                    if (target > 0 && target < pc - g->bytecode) {
209
67.6k
                        while (!byte_to_ins_flags[--target]);
210
10.2k
                        byte_to_ins_flags[target] |= MVM_CFG_BB_END;
211
10.2k
                    }
212
162k
213
162k
                    /* Next instruction is also a BB start. */
214
162k
                    next_bbs = 1;
215
162k
216
162k
                    arg_size += 4;
217
162k
                    break;
218
0
                }
219
9.49k
                case MVM_operand_spesh_slot:
220
9.49k
                    ins_node->operands[i].lit_i16 = GET_I16(args, arg_size);
221
9.49k
                    arg_size += 2;
222
9.49k
                    break;
223
0
                default:
224
0
                    MVM_oops(tc,
225
0
                        "Spesh: unknown operand type %d in graph building (op %s)",
226
0
                        (int)type, ins_node->info->name);
227
770k
                }
228
770k
                break;
229
0
            default:
230
0
                break;
231
770k
            }
232
2.84M
            }
233
2.84M
        }
234
1.32M
235
1.32M
        /* We specially handle the jumplist case, which needs to mark all of
236
1.32M
         * the possible places we could jump to in the following instructions
237
1.32M
         * as starts of basic blocks. It is, in itself, the end of one. Note
238
1.32M
         * we jump to the instruction after the n jump points if none match,
239
1.32M
         * so that is marked too. */
240
1.32M
        if (opcode == MVM_OP_jumplist) {
241
416
            MVMint64 n = MVM_BC_get_I64(args, 0);
242
4.69k
            for (i = 0; i <= n; i++)
243
4.27k
                byte_to_ins_flags[(pc - g->bytecode) + 12 + i * 6] |= MVM_CFG_BB_START;
244
416
            byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END;
245
416
        }
246
1.32M
247
1.32M
        /* Invocations, returns, and throws are basic block ends. */
248
1.32M
        switch (opcode) {
249
68.7k
        case MVM_OP_invoke_v:
250
68.7k
        case MVM_OP_invoke_i:
251
68.7k
        case MVM_OP_invoke_n:
252
68.7k
        case MVM_OP_invoke_s:
253
68.7k
        case MVM_OP_invoke_o:
254
68.7k
        case MVM_OP_return_i:
255
68.7k
        case MVM_OP_return_n:
256
68.7k
        case MVM_OP_return_s:
257
68.7k
        case MVM_OP_return_o:
258
68.7k
        case MVM_OP_return:
259
68.7k
        case MVM_OP_throwdyn:
260
68.7k
        case MVM_OP_throwlex:
261
68.7k
        case MVM_OP_throwlexotic:
262
68.7k
        case MVM_OP_throwcatdyn:
263
68.7k
        case MVM_OP_throwcatlex:
264
68.7k
        case MVM_OP_throwcatlexotic:
265
68.7k
        case MVM_OP_die:
266
68.7k
        case MVM_OP_rethrow:
267
68.7k
        case MVM_OP_resume:
268
68.7k
            byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END;
269
68.7k
            next_bbs = 1;
270
68.7k
            break;
271
1.25M
        default:
272
1.25M
            break;
273
1.32M
        }
274
1.32M
275
1.32M
        /* Final instruction is basic block end. */
276
1.32M
        if (pc + 2 + arg_size == end)
277
21.8k
            byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END;
278
1.32M
279
1.32M
        /* Caculate next instruction's PC. */
280
1.32M
        pc += 2 + arg_size;
281
1.32M
282
1.32M
        /* If this is a deopt point opcode... */
283
1.32M
        if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_ONE))
284
86.6k
            add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_ONE_INS);
285
1.32M
        if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_ALL))
286
38.7k
            add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_ALL_INS);
287
1.32M
        if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_OSR))
288
4.03k
            add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_OSR);
289
1.32M
290
1.32M
        /* Go to next instruction. */
291
1.32M
        ins_idx++;
292
1.32M
    }
293
21.8k
294
21.8k
    /* Annotate instructions that are handler-significant. */
295
27.0k
    for (i = 0; i < g->num_handlers; i++) {
296
5.25k
        MVMSpeshIns *start_ins = ins_flat[byte_to_ins_flags[g->handlers[i].start_offset] >> 2];
297
5.25k
        MVMSpeshIns *end_ins   = ins_flat[byte_to_ins_flags[g->handlers[i].end_offset] >> 2];
298
5.25k
        MVMSpeshIns *goto_ins  = ins_flat[byte_to_ins_flags[g->handlers[i].goto_offset] >> 2];
299
5.25k
        MVMSpeshAnn *start_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
300
5.25k
        MVMSpeshAnn *end_ann   = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
301
5.25k
        MVMSpeshAnn *goto_ann  = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
302
5.25k
303
5.25k
        start_ann->next = start_ins->annotations;
304
5.25k
        start_ann->type = MVM_SPESH_ANN_FH_START;
305
5.25k
        start_ann->data.frame_handler_index = i;
306
5.25k
        start_ins->annotations = start_ann;
307
5.25k
308
5.25k
        end_ann->next = end_ins->annotations;
309
5.25k
        end_ann->type = MVM_SPESH_ANN_FH_END;
310
5.25k
        end_ann->data.frame_handler_index = i;
311
5.25k
        end_ins->annotations = end_ann;
312
5.25k
313
5.25k
        goto_ann->next = goto_ins->annotations;
314
5.25k
        goto_ann->type = MVM_SPESH_ANN_FH_GOTO;
315
5.25k
        goto_ann->data.frame_handler_index = i;
316
5.25k
        goto_ins->annotations = goto_ann;
317
5.25k
    }
318
21.8k
319
21.8k
    /* Annotate instructions that are inline start/end points. */
320
22.2k
    for (i = 0; i < g->num_inlines; i++) {
321
387
        MVMSpeshIns *start_ins = ins_flat[byte_to_ins_flags[g->inlines[i].start] >> 2];
322
387
        MVMSpeshIns *end_ins   = ins_flat[byte_to_ins_flags[g->inlines[i].end] >> 2];
323
387
        MVMSpeshAnn *start_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
324
387
        MVMSpeshAnn *end_ann   = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
325
387
326
387
        start_ann->next = start_ins->annotations;
327
387
        start_ann->type = MVM_SPESH_ANN_INLINE_START;
328
387
        start_ann->data.inline_idx = i;
329
387
        start_ins->annotations = start_ann;
330
387
331
387
        end_ann->next = end_ins->annotations;
332
387
        end_ann->type = MVM_SPESH_ANN_INLINE_END;
333
387
        end_ann->data.inline_idx = i;
334
387
        end_ins->annotations = end_ann;
335
387
    }
336
21.8k
337
21.8k
    /* Now for the second pass, where we assemble the basic blocks. Also we
338
21.8k
     * build a lookup table of instructions that start a basic block to that
339
21.8k
     * basic block, for the final CFG construction. We make the entry block a
340
21.8k
     * special one, containing a noop; it will have any exception handler
341
21.8k
     * targets linked from it, so they show up in the graph. */
342
21.8k
    g->entry                  = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB));
343
21.8k
    g->entry->first_ins       = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
344
21.8k
    g->entry->first_ins->info = get_op_info(tc, cu, 0);
345
21.8k
    g->entry->last_ins        = g->entry->first_ins;
346
21.8k
    g->entry->idx             = 0;
347
21.8k
    cur_bb                    = NULL;
348
21.8k
    prev_bb                   = g->entry;
349
21.8k
    last_ins                  = NULL;
350
21.8k
    ins_to_bb                 = MVM_calloc(ins_idx, sizeof(MVMSpeshBB *));
351
21.8k
    ins_idx                   = 0;
352
21.8k
    bb_idx                    = 1;
353
9.10M
    for (i = 0; i < g->bytecode_size; i++) {
354
9.07M
        MVMSpeshIns *cur_ins;
355
9.07M
356
9.07M
        /* Skip zeros; no instruction here. */
357
9.07M
        if (!byte_to_ins_flags[i])
358
7.75M
            continue;
359
9.07M
360
9.07M
        /* Get current instruction. */
361
1.32M
        cur_ins = ins_flat[byte_to_ins_flags[i] >> 2];
362
1.32M
363
1.32M
        /* Start of a basic block? */
364
1.32M
        if (byte_to_ins_flags[i] & MVM_CFG_BB_START) {
365
310k
            /* Should not already be in a basic block. */
366
310k
            if (cur_bb) {
367
0
                MVM_spesh_graph_destroy(tc, g);
368
0
                MVM_oops(tc, "Spesh: confused during basic block analysis (in block)");
369
0
            }
370
310k
371
310k
            /* Create it, and set first instruction and index. */
372
310k
            cur_bb = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB));
373
310k
            cur_bb->first_ins = cur_ins;
374
310k
            cur_bb->idx = bb_idx;
375
310k
            cur_bb->initial_pc = i;
376
310k
            bb_idx++;
377
310k
378
310k
            /* Record instruction -> BB start mapping. */
379
310k
            ins_to_bb[ins_idx] = cur_bb;
380
310k
381
310k
            /* Link it to the previous one. */
382
310k
            prev_bb->linear_next = cur_bb;
383
310k
        }
384
1.32M
385
1.32M
        /* Should always be in a BB at this point. */
386
1.32M
        if (!cur_bb) {
387
0
            MVM_spesh_graph_destroy(tc, g);
388
0
            MVM_oops(tc, "Spesh: confused during basic block analysis (no block)");
389
0
        }
390
1.32M
391
1.32M
        /* Add instruction into double-linked per-block instruction list. */
392
1.32M
        if (last_ins) {
393
1.01M
            last_ins->next = cur_ins;
394
1.01M
            cur_ins->prev = last_ins;
395
1.01M
        }
396
1.32M
        last_ins = cur_ins;
397
1.32M
398
1.32M
        /* End of a basic block? */
399
1.32M
        if (byte_to_ins_flags[i] & MVM_CFG_BB_END) {
400
310k
            cur_bb->last_ins = cur_ins;
401
310k
            prev_bb  = cur_bb;
402
310k
            cur_bb   = NULL;
403
310k
            last_ins = NULL;
404
310k
        }
405
1.32M
406
1.32M
        ins_idx++;
407
1.32M
    }
408
21.8k
    g->num_bbs = bb_idx;
409
21.8k
410
21.8k
    /* Finally, link the basic blocks up to form a CFG. Along the way, any of
411
21.8k
     * the instruction operands get the target BB stored. */
412
21.8k
    cur_bb = g->entry;
413
354k
    while (cur_bb) {
414
332k
        /* If it's the first block, it's a special case; successors are the
415
332k
         * real successor and all exception handlers. */
416
332k
        if (cur_bb == g->entry) {
417
21.8k
            cur_bb->num_succ = 1 + g->num_handlers;
418
21.8k
            cur_bb->succ     = MVM_spesh_alloc(tc, g, cur_bb->num_succ * sizeof(MVMSpeshBB *));
419
21.8k
            cur_bb->succ[0]  = cur_bb->linear_next;
420
27.0k
            for (i = 0; i < g->num_handlers; i++) {
421
5.25k
                MVMuint32 offset = g->handlers[i].goto_offset;
422
5.25k
                cur_bb->succ[i + 1] = ins_to_bb[byte_to_ins_flags[offset] >> 2];
423
5.25k
            }
424
21.8k
        }
425
332k
426
332k
        /* Otherwise, consider the last instruction, to see how we leave the BB. */
427
310k
        else {
428
310k
            switch (cur_bb->last_ins->info->opcode) {
429
416
                case MVM_OP_jumplist: {
430
416
                    /* Jumplist, so successors are next N+1 basic blocks. */
431
416
                    MVMint64    num_bbs   = cur_bb->last_ins->operands[0].lit_i64 + 1;
432
416
                    MVMSpeshBB *bb_to_add = cur_bb->linear_next;
433
416
                    cur_bb->succ          = MVM_spesh_alloc(tc, g, num_bbs * sizeof(MVMSpeshBB *));
434
4.69k
                    for (i = 0; i < num_bbs; i++) {
435
4.27k
                        cur_bb->succ[i] = bb_to_add;
436
4.27k
                        bb_to_add = bb_to_add->linear_next;
437
4.27k
                    }
438
416
                    cur_bb->num_succ = num_bbs;
439
416
                }
440
416
                break;
441
59.1k
                case MVM_OP_goto: {
442
59.1k
                    /* Unconditional branch, so one successor. */
443
59.1k
                    MVMuint32   offset = cur_bb->last_ins->operands[0].ins_offset;
444
59.1k
                    MVMSpeshBB *tgt    = ins_to_bb[byte_to_ins_flags[offset] >> 2];
445
59.1k
                    cur_bb->succ       = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *));
446
59.1k
                    cur_bb->succ[0]    = tgt;
447
59.1k
                    cur_bb->num_succ   = 1;
448
59.1k
                    cur_bb->last_ins->operands[0].ins_bb = tgt;
449
59.1k
                }
450
59.1k
                break;
451
251k
                default: {
452
251k
                    /* Probably conditional branch, so two successors: one from
453
251k
                     * the instruction, another from fall-through. Or may just be
454
251k
                     * a non-branch that exits for other reasons. */
455
251k
                    cur_bb->succ = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshBB *));
456
761k
                    for (i = 0; i < cur_bb->last_ins->info->num_operands; i++) {
457
510k
                        if (cur_bb->last_ins->info->operands[i] == MVM_operand_ins) {
458
103k
                            MVMuint32 offset = cur_bb->last_ins->operands[i].ins_offset;
459
103k
                            cur_bb->succ[0] = ins_to_bb[byte_to_ins_flags[offset] >> 2];
460
103k
                            cur_bb->num_succ++;
461
103k
                            cur_bb->last_ins->operands[i].ins_bb = cur_bb->succ[0];
462
103k
                        }
463
510k
                    }
464
251k
                    if (cur_bb->num_succ > 1) {
465
0
                        /* If we ever get instructions with multiple targets, this
466
0
                         * area of the code needs an update. */
467
0
                        MVM_spesh_graph_destroy(tc, g);
468
0
                        MVM_oops(tc, "Spesh: unhandled multi-target branch");
469
0
                    }
470
251k
                    if (cur_bb->linear_next) {
471
229k
                        cur_bb->succ[cur_bb->num_succ] = cur_bb->linear_next;
472
229k
                        cur_bb->num_succ++;
473
229k
                    }
474
251k
                }
475
251k
                break;
476
310k
            }
477
310k
        }
478
332k
479
332k
        /* Move on to next block. */
480
332k
        cur_bb = cur_bb->linear_next;
481
332k
    }
482
21.8k
483
21.8k
    /* If we're building the graph for optimized bytecode, insert existing
484
21.8k
     * deopt points. */
485
21.8k
    if (existing_deopts) {
486
10.3k
        for (i = 0; i < num_existing_deopts; i ++) {
487
7.91k
            if (existing_deopts[2 * i + 1] >= 0) {
488
7.75k
                MVMSpeshIns *post_ins     = ins_flat[byte_to_ins_flags[existing_deopts[2 * i + 1]] >> 2];
489
5.72k
                MVMSpeshIns *deopt_ins    = post_ins->prev ? post_ins->prev :
490
2.02k
                    MVM_spesh_graph_linear_prev(tc, g,
491
2.02k
                        ins_to_bb[byte_to_ins_flags[existing_deopts[2 * i + 1]] >> 2])->last_ins;
492
7.75k
                MVMSpeshAnn *deopt_ann    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
493
7.75k
                deopt_ann->next           = deopt_ins->annotations;
494
7.75k
                deopt_ann->type           = MVM_SPESH_ANN_DEOPT_INLINE;
495
7.75k
                deopt_ann->data.deopt_idx = i;
496
7.75k
                deopt_ins->annotations    = deopt_ann;
497
7.75k
            }
498
7.91k
        }
499
2.39k
    }
500
21.8k
501
21.8k
    /* Clear up the temporary arrays. */
502
21.8k
    MVM_free(byte_to_ins_flags);
503
21.8k
    MVM_free(ins_flat);
504
21.8k
    MVM_free(ins_to_bb);
505
21.8k
}
506
507
/* Inserts nulling of object reigsters. A later stage of the optimizer will
508
 * throw out any that are unrequired, leaving only those that cover (rare)
509
 * "register read before assigned" cases. (We can thus just start off with
510
 * them NULL, since zeroed memory is cheaper than copying a VMNulls in to
511
 * place). */
512
140k
static MVMint32 is_handler_reg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 reg) {
513
140k
    MVMuint32 num_handlers = g->num_handlers;
514
140k
    MVMuint32 i;
515
288k
    for (i = 0; i < num_handlers; i++)
516
148k
        if (g->handlers[i].action == MVM_EX_ACTION_INVOKE)
517
158
            if (g->handlers[i].block_reg == reg)
518
7
                return 1;
519
140k
    return 0;
520
140k
}
521
17.1k
static void insert_object_null_instructions(MVMThreadContext *tc, MVMSpeshGraph *g) {
522
17.1k
    MVMSpeshBB *insert_bb = g->entry->linear_next;
523
17.1k
    MVMuint16 *local_types = g->sf->body.local_types;
524
17.1k
    MVMuint16  num_locals = g->sf->body.num_locals;
525
17.1k
    MVMuint16 i;
526
239k
    for (i = 0; i < num_locals; i++) {
527
222k
        if (local_types[i] == MVM_reg_obj && !is_handler_reg(tc, g, i)) {
528
140k
            MVMSpeshIns *null_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
529
140k
            null_ins->info = MVM_op_get_op(MVM_OP_null);
530
140k
            null_ins->operands = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
531
140k
            null_ins->operands[0].reg.orig = i;
532
140k
            MVM_spesh_manipulate_insert_ins(tc, insert_bb, NULL, null_ins);
533
140k
        }
534
222k
    }
535
17.1k
}
536
537
/* Eliminates any unreachable basic blocks (that is, dead code). Not having
538
 * to consider them any further simplifies all that follows. */
539
21.8k
static void eliminate_dead(MVMThreadContext *tc, MVMSpeshGraph *g) {
540
21.8k
    /* Iterate to fixed point. */
541
21.8k
    MVMint8  *seen     = MVM_malloc(g->num_bbs);
542
21.8k
    MVMint32  orig_bbs = g->num_bbs;
543
21.8k
    MVMint8   death    = 1;
544
43.7k
    while (death) {
545
21.9k
        /* First pass: mark every basic block that is the entry point or the
546
21.9k
         * successor of some other block. */
547
21.9k
        MVMSpeshBB *cur_bb = g->entry;
548
21.9k
        memset(seen, 0, g->num_bbs);
549
21.9k
        seen[0] = 1;
550
354k
        while (cur_bb) {
551
332k
            MVMuint16 i;
552
756k
            for (i = 0; i < cur_bb->num_succ; i++)
553
423k
                seen[cur_bb->succ[i]->idx] = 1;
554
332k
            cur_bb = cur_bb->linear_next;
555
332k
        }
556
21.9k
557
21.9k
        /* Second pass: eliminate dead BBs from consideration. */
558
21.9k
        death = 0;
559
21.9k
        cur_bb = g->entry;
560
332k
        while (cur_bb->linear_next) {
561
310k
            if (!seen[cur_bb->linear_next->idx]) {
562
96
                cur_bb->linear_next = cur_bb->linear_next->linear_next;
563
96
                g->num_bbs--;
564
96
                death = 1;
565
96
            }
566
310k
            cur_bb = cur_bb->linear_next;
567
310k
        }
568
21.9k
    }
569
21.8k
    MVM_free(seen);
570
21.8k
571
21.8k
    /* If we removed some, need to re-number so they're consecutive, for the
572
21.8k
     * post-order and dominance calcs to be happy. */
573
21.8k
    if (g->num_bbs != orig_bbs) {
574
75
        MVMint32    new_idx  = 0;
575
75
        MVMSpeshBB *cur_bb   = g->entry;
576
443
        while (cur_bb) {
577
368
            cur_bb->idx = new_idx;
578
368
            new_idx++;
579
368
            cur_bb = cur_bb->linear_next;
580
368
        }
581
75
    }
582
21.8k
}
583
584
/* Annotates the control flow graph with predecessors. */
585
21.8k
static void add_predecessors(MVMThreadContext *tc, MVMSpeshGraph *g) {
586
21.8k
    MVMSpeshBB *cur_bb = g->entry;
587
354k
    while (cur_bb) {
588
332k
        MVMuint16 i;
589
755k
        for (i = 0; i < cur_bb->num_succ; i++) {
590
423k
            MVMSpeshBB  *tgt = cur_bb->succ[i];
591
423k
            MVMSpeshBB **new_pred = MVM_spesh_alloc(tc, g,
592
423k
                (tgt->num_pred + 1) * sizeof(MVMSpeshBB *));
593
423k
            if (tgt->num_pred)
594
112k
                memcpy(new_pred, tgt->pred, tgt->num_pred * sizeof(MVMSpeshBB *));
595
423k
            new_pred[tgt->num_pred] = cur_bb;
596
423k
            tgt->pred = new_pred;
597
423k
            tgt->num_pred++;
598
423k
        }
599
332k
        cur_bb = cur_bb->linear_next;
600
332k
    }
601
21.8k
}
602
603
/* Produces an array of the basic blocks, sorted in reverse postorder from
604
 * the entry point. */
605
332k
static void dfs(MVMSpeshBB **rpo, MVMint32 *insert_pos, MVMuint8 *seen, MVMSpeshBB *bb) {
606
332k
    MVMint32 i;
607
332k
    seen[bb->idx] = 1;
608
755k
    for (i = 0; i < bb->num_succ; i++) {
609
423k
        MVMSpeshBB *succ = bb->succ[i];
610
423k
        if (!seen[succ->idx])
611
310k
            dfs(rpo, insert_pos, seen, succ);
612
423k
    }
613
332k
    rpo[*insert_pos] = bb;
614
332k
    bb->rpo_idx = *insert_pos;
615
332k
    (*insert_pos)--;
616
332k
}
617
21.8k
static MVMSpeshBB ** reverse_postorder(MVMThreadContext *tc, MVMSpeshGraph *g) {
618
21.8k
    MVMSpeshBB **rpo  = MVM_calloc(g->num_bbs, sizeof(MVMSpeshBB *));
619
21.8k
    MVMuint8    *seen = MVM_calloc(g->num_bbs, 1);
620
21.8k
    MVMint32     ins  = g->num_bbs - 1;
621
21.8k
    dfs(rpo, &ins, seen, g->entry);
622
21.8k
    MVM_free(seen);
623
21.8k
    if (ins != -1) {
624
0
        char *dump_msg = MVM_spesh_dump(tc, g);
625
0
        printf("%s", dump_msg);
626
0
        MVM_free(dump_msg);
627
0
        MVM_spesh_graph_destroy(tc, g);
628
0
        MVM_oops(tc, "Spesh: reverse postorder calculation failed");
629
0
    }
630
21.8k
    return rpo;
631
21.8k
}
632
633
/* 2-finger intersection algorithm, to find new immediate dominator. */
634
724k
static void iter_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB **rpo, MVMint32 *doms, MVMint32 iters) {
635
724k
    if (iters > 100000) {
636
0
#ifdef NDEBUG
637
        MVMint32 k;
638
        char *dump_msg = MVM_spesh_dump(tc, g);
639
        printf("%s", dump_msg);
640
        MVM_free(dump_msg);
641
        printf("RPO: ");
642
        for (k = 0; k < g->num_bbs; k++)
643
            printf("%d, ", rpo[k]->idx);
644
        printf("\n");
645
        printf("Doms: ");
646
        for (k = 0; k < g->num_bbs; k++)
647
            printf("%d (%d), ", doms[k], doms[k] >= 0 ? rpo[doms[k]]->idx : -1);
648
        printf("\n");
649
#endif
650
0
        MVM_spesh_graph_destroy(tc, g);
651
0
        MVM_oops(tc, "Spesh: dominator intersection went infinite");
652
0
    }
653
724k
}
654
229k
static MVMint32 intersect(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB **rpo, MVMint32 *doms, MVMint32 finger1, MVMint32 finger2) {
655
229k
    MVMint32 iters = 0;
656
464k
    while (finger1 != finger2) {
657
797k
        while (finger1 > finger2) {
658
561k
            iter_check(tc, g, rpo, doms, iters++);
659
561k
            finger1 = doms[finger1];
660
561k
        }
661
398k
        while (finger2 > finger1) {
662
163k
            iter_check(tc, g, rpo, doms, iters++);
663
163k
            finger2 = doms[finger2];
664
163k
        }
665
235k
    }
666
229k
    return finger1;
667
229k
}
668
669
/* Computes dominator information about the basic blocks. */
670
21.8k
static MVMint32 * compute_dominators(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB **rpo) {
671
21.8k
    MVMint32 i, j, changed;
672
21.8k
673
21.8k
    /* Create result list, with all initialized to undefined (use -1, as it's
674
21.8k
     * not a valid basic block index). Start node dominates itself. */
675
21.8k
    MVMint32 *doms = MVM_malloc(g->num_bbs * sizeof(MVMint32));
676
21.8k
    doms[0] = 0;
677
332k
    for (i = 1; i < g->num_bbs; i++)
678
310k
        doms[i] = -1;
679
21.8k
680
21.8k
    /* Iterate to fixed point. */
681
21.8k
    changed = 1;
682
65.8k
    while (changed) {
683
43.9k
        changed = 0;
684
43.9k
685
43.9k
        /* Visit all except the start node in reverse postorder. */
686
694k
        for (i = 1; i < g->num_bbs; i++) {
687
650k
            MVMSpeshBB *b = rpo[i];
688
650k
689
650k
            /* See if there's a better dominator. */
690
650k
            MVMint32 chosen_pred = -1;
691
650k
            MVMint32 new_idom;
692
650k
            for (j = 0; j < b->num_pred; j++) {
693
650k
                new_idom = b->pred[j]->rpo_idx;
694
650k
                if (doms[new_idom] != -1)
695
650k
                {
696
650k
                    chosen_pred = j;
697
650k
                    break;
698
650k
                }
699
650k
            }
700
650k
            if (chosen_pred == -1) {
701
0
                MVM_spesh_graph_destroy(tc, g);
702
0
                MVM_oops(tc, "Spesh: could not find processed initial dominator");
703
0
            }
704
1.54M
            for (j = 0; j < b->num_pred; j++) {
705
889k
                if (j != chosen_pred) {
706
239k
                    MVMint32 p_idx = b->pred[j]->rpo_idx;
707
239k
                    if (doms[p_idx] != -1)
708
229k
                        new_idom = intersect(tc, g, rpo, doms, p_idx, new_idom);
709
239k
                }
710
889k
            }
711
650k
            if (doms[i] != new_idom) {
712
311k
                doms[i] = new_idom;
713
311k
                changed = 1;
714
311k
            }
715
650k
        }
716
43.9k
    }
717
21.8k
718
21.8k
    return doms;
719
21.8k
}
720
721
/* Builds the dominance tree children lists for each node. */
722
310k
static void add_child(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *target, MVMSpeshBB *to_add) {
723
310k
    MVMSpeshBB **new_children;
724
310k
    MVMint32 i;
725
310k
726
310k
    /* Already in the child list? */
727
555k
    for (i = 0; i < target->num_children; i++)
728
245k
        if (target->children[i] == to_add)
729
0
            return;
730
310k
731
310k
    /* Nope, so insert. */
732
310k
    new_children = MVM_spesh_alloc(tc, g, (target->num_children + 1) * sizeof(MVMSpeshBB *));
733
310k
    if (target->num_children)
734
141k
        memcpy(new_children, target->children, target->num_children * sizeof(MVMSpeshBB *));
735
310k
    new_children[target->num_children] = to_add;
736
310k
    target->children = new_children;
737
310k
    target->num_children++;
738
310k
}
739
21.8k
static void add_children(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB **rpo, MVMint32 *doms) {
740
21.8k
    MVMint32 i;
741
354k
    for (i = 0; i < g->num_bbs; i++) {
742
332k
        MVMSpeshBB *bb   = rpo[i];
743
332k
        MVMint32    idom = doms[i];
744
332k
        if (idom != i)
745
310k
            add_child(tc, g, rpo[idom], bb);
746
332k
    }
747
21.8k
}
748
749
/* Builds the dominance frontier set for each node. */
750
366k
static void add_to_frontier_set(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *target, MVMSpeshBB *to_add) {
751
366k
    MVMSpeshBB **new_df;
752
366k
    MVMint32 i;
753
366k
754
366k
    /* Already in the set? */
755
508k
    for (i = 0; i < target->num_df; i++)
756
214k
        if (target->df[i] == to_add)
757
72.7k
            return;
758
366k
759
366k
    /* Nope, so insert. */
760
293k
    new_df = MVM_spesh_alloc(tc, g, (target->num_df + 1) * sizeof(MVMSpeshBB *));
761
293k
    if (target->num_df)
762
35.6k
        memcpy(new_df, target->df, target->num_df * sizeof(MVMSpeshBB *));
763
293k
    new_df[target->num_df] = to_add;
764
293k
    target->df = new_df;
765
293k
    target->num_df++;
766
293k
}
767
21.8k
static void add_dominance_frontiers(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB **rpo, MVMint32 *doms) {
768
21.8k
    MVMint32 j;
769
21.8k
    MVMSpeshBB *b = g->entry;
770
354k
    while (b) {
771
332k
        if (b->num_pred >= 2) { /* Thus it's a join point */
772
297k
            for (j = 0; j < b->num_pred; j++) {
773
205k
                MVMint32 runner      = b->pred[j]->rpo_idx;
774
205k
                MVMint32 finish_line = doms[b->rpo_idx];
775
571k
                while (runner != finish_line) {
776
366k
                    add_to_frontier_set(tc, g, rpo[runner], b);
777
366k
                    runner = doms[runner];
778
366k
                }
779
205k
            }
780
92.4k
        }
781
332k
        b = b->linear_next;
782
332k
    }
783
21.8k
}
784
785
/* Per-local SSA info. */
786
typedef struct {
787
    /* Nodes that assign to the variable. */
788
    MVMSpeshBB **ass_nodes;
789
    MVMuint16    num_ass_nodes;
790
791
    /* Count of processed assignments aka. C(V). */
792
    MVMint32 count;
793
794
    /* Stack of integers aka. S(V). */
795
    MVMint32 *stack;
796
    MVMint32  stack_top;
797
    MVMint32  stack_alloc;
798
} SSAVarInfo;
799
800
/* Creates an SSAVarInfo for each local, initializing it with a list of nodes
801
 * that assign to the local. */
802
21.8k
static SSAVarInfo * initialize_ssa_var_info(MVMThreadContext *tc, MVMSpeshGraph *g) {
803
21.8k
    SSAVarInfo *var_info = MVM_calloc(g->num_locals, sizeof(SSAVarInfo));
804
21.8k
    MVMint32 i;
805
21.8k
806
21.8k
    /* Visit all instructions, looking for local writes. */
807
21.8k
    MVMSpeshBB *bb = g->entry;
808
354k
    while (bb) {
809
332k
        MVMSpeshIns *ins = bb->first_ins;
810
1.82M
        while (ins) {
811
4.47M
            for (i = 0; i < ins->info->num_operands; i++) {
812
2.98M
                if ((ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg) {
813
1.06M
                    MVMuint16 written = ins->operands[i].reg.orig;
814
1.06M
                    MVMint32  found   = 0;
815
1.06M
                    MVMint32  j;
816
6.83M
                    for (j = 0; j < var_info[written].num_ass_nodes; j++)
817
6.04M
                        if (var_info[written].ass_nodes[j] == bb) {
818
269k
                            found = 1;
819
269k
                            break;
820
269k
                        }
821
1.06M
                    if (!found) {
822
793k
                        if (var_info[written].num_ass_nodes % 8 == 0) {
823
278k
                            MVMint32 new_size = var_info[written].num_ass_nodes + 8;
824
278k
                            var_info[written].ass_nodes = MVM_realloc(
825
278k
                                var_info[written].ass_nodes,
826
278k
                                new_size * sizeof(MVMSpeshBB *));
827
278k
                        }
828
793k
                        var_info[written].ass_nodes[var_info[written].num_ass_nodes] = bb;
829
793k
                        var_info[written].num_ass_nodes++;
830
793k
                    }
831
1.06M
                }
832
2.98M
            }
833
1.48M
            ins = ins->next;
834
1.48M
        }
835
332k
        bb = bb->linear_next;
836
332k
    }
837
21.8k
838
21.8k
    /* Set stack top to -1 sentinel for all nodes, and count = 1 (as we may
839
21.8k
     * read the default value of a register). */
840
279k
    for (i = 0; i < g->num_locals; i++) {
841
257k
        var_info[i].count     = 1;
842
257k
        var_info[i].stack_top = -1;
843
257k
    }
844
21.8k
845
21.8k
    return var_info;
846
21.8k
}
847
848
669k
MVMOpInfo *get_phi(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint32 nrargs) {
849
669k
    MVMOpInfo *result = NULL;
850
669k
851
669k
    /* Check number of args to phi isn't huge. */
852
669k
    if (nrargs > 0xFFFF)
853
0
        MVM_panic(1, "Spesh: SSA calculation failed; cannot allocate enormous PHI node");
854
669k
855
669k
    /* Up to 64 args, almost every number is represented, but after that
856
669k
     * we have a sparse array through which we must search */
857
669k
    if (nrargs - 2 < MVMPhiNodeCacheSparseBegin) {
858
669k
        result = &g->phi_infos[nrargs - 2];
859
0
    } else {
860
0
        MVMint32 cache_idx;
861
0
862
0
        for (cache_idx = MVMPhiNodeCacheSparseBegin; !result && cache_idx < MVMPhiNodeCacheSize; cache_idx++) {
863
0
            if (g->phi_infos[cache_idx].opcode == MVM_SSA_PHI) {
864
0
                if (g->phi_infos[cache_idx].num_operands == nrargs) {
865
0
                    result = &g->phi_infos[cache_idx];
866
0
                }
867
0
            } else {
868
0
                result = &g->phi_infos[cache_idx];
869
0
            }
870
0
        }
871
0
    }
872
669k
873
669k
    if (result == NULL) {
874
0
        result = MVM_spesh_alloc(tc, g, sizeof(MVMOpInfo));
875
0
        result->opcode = 0;
876
0
    }
877
669k
878
669k
    if (result->opcode != MVM_SSA_PHI) {
879
17.2k
        result->opcode       = MVM_SSA_PHI;
880
17.2k
        result->name         = "PHI";
881
17.2k
        result->num_operands = nrargs;
882
17.2k
    }
883
669k
884
669k
    return result;
885
669k
}
886
887
/* Inserts SSA phi functions at the required places in the graph. */
888
669k
static void place_phi(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMint32 n, MVMuint16 var) {
889
669k
    MVMint32     i;
890
669k
    MVMOpInfo   *phi_op  = get_phi(tc, g, n + 1);
891
669k
    MVMSpeshIns *ins     = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
892
669k
    ins->info            = phi_op;
893
669k
    ins->operands        = MVM_spesh_alloc(tc, g, phi_op->num_operands * sizeof(MVMSpeshOperand));
894
3.13M
    for (i = 0; i < phi_op->num_operands; i++)
895
2.46M
        ins->operands[i].reg.orig = var;
896
669k
    ins->next           = bb->first_ins;
897
669k
    bb->first_ins->prev = ins;
898
669k
    bb->first_ins       = ins;
899
669k
}
900
21.8k
static void insert_phi_functions(MVMThreadContext *tc, MVMSpeshGraph *g, SSAVarInfo *var_info) {
901
21.8k
    MVMint32    *has_already  = MVM_calloc(g->num_bbs, sizeof(MVMint32));
902
21.8k
    MVMint32    *work         = MVM_calloc(g->num_bbs, sizeof(MVMint32));
903
21.8k
    MVMSpeshBB **worklist     = MVM_calloc(g->num_bbs, sizeof(MVMSpeshBB *));
904
21.8k
    MVMint32     worklist_top = 0;
905
21.8k
    MVMint32     iter_count   = 0;
906
21.8k
907
21.8k
    /* Go over all locals. */
908
21.8k
    MVMint32 var, i, j, found;
909
279k
    for (var = 0; var < g->num_locals; var++) {
910
257k
        /* Move to next iteration. */
911
257k
        iter_count++;
912
257k
913
257k
        /* Add blocks assigning to this variable to the worklist. */
914
1.05M
        for (i = 0; i < var_info[var].num_ass_nodes; i++) {
915
793k
            MVMSpeshBB *bb = var_info[var].ass_nodes[i];
916
793k
            work[bb->idx] = iter_count;
917
793k
            worklist[worklist_top++] = bb; /* Algo unions, but ass_nodes unique */
918
793k
        }
919
257k
920
257k
        /* Process the worklist. */
921
1.65M
        while (worklist_top) {
922
1.39M
            MVMSpeshBB *x = worklist[--worklist_top];
923
2.70M
            for (i = 0; i < x->num_df; i++) {
924
1.31M
                MVMSpeshBB *y = x->df[i];
925
1.31M
                if (has_already[y->idx] < iter_count) {
926
669k
                    /* Place phi function, and mark we have. */
927
669k
                    place_phi(tc, g, y, y->num_pred, var);
928
669k
                    has_already[y->idx] = iter_count;
929
669k
930
669k
                    /* Add this block to worklist if needed. */
931
669k
                    if (work[y->idx] < iter_count) {
932
598k
                        work[y->idx] = iter_count;
933
598k
                        found = 0;
934
4.01M
                        for (j = 0; j < worklist_top; j++)
935
3.41M
                            if (worklist[j] == y) {
936
0
                                found = 1;
937
0
                                break;
938
0
                            }
939
598k
                        if (!found)
940
598k
                            worklist[worklist_top++] = y;
941
598k
                    }
942
669k
                }
943
1.31M
            }
944
1.39M
        }
945
257k
    }
946
21.8k
947
21.8k
    MVM_free(has_already);
948
21.8k
    MVM_free(work);
949
21.8k
    MVM_free(worklist);
950
21.8k
}
951
952
/* Renames the local variables such that we end up with SSA form. */
953
423k
static MVMint32 which_pred(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *y, MVMSpeshBB *x) {
954
423k
    MVMint32 i;
955
597k
    for (i = 0; i < y->num_pred; i++)
956
597k
        if (y->pred[i] == x)
957
423k
            return i;
958
0
    MVM_spesh_graph_destroy(tc, g);
959
0
    MVM_oops(tc, "Spesh: which_pred failed to find x");
960
0
}
961
332k
static void rename_locals(MVMThreadContext *tc, MVMSpeshGraph *g, SSAVarInfo *var_info, MVMSpeshBB *x) {
962
332k
    MVMint32 i;
963
332k
964
332k
    /* Visit instructions and do renames in normal (non-phi) instructions. */
965
332k
    MVMSpeshIns *a = x->first_ins;
966
2.49M
    while (a) {
967
2.15M
        /* Rename reads, provided it's not a PHI. */
968
2.15M
        MVMint32 is_phi = a->info->opcode == MVM_SSA_PHI;
969
2.15M
        if (!is_phi) {
970
4.47M
            for (i = 0; i < a->info->num_operands; i++) {
971
2.98M
                if ((a->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg) {
972
1.12M
                    MVMuint16 orig = a->operands[i].reg.orig;
973
1.12M
                    MVMint32  st   = var_info[orig].stack_top;
974
1.12M
                    if (st >= 0)
975
1.12M
                        a->operands[i].reg.i = var_info[orig].stack[st];
976
1.12M
                    else
977
0
                        a->operands[i].reg.i = 0;
978
1.12M
                }
979
2.98M
            }
980
1.48M
        }
981
2.15M
982
2.15M
        /* Rename writes. */
983
5.14M
        for (i = 0; i < a->info->num_operands; i++) {
984
3.65M
            if (is_phi || (a->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg) {
985
1.73M
                MVMuint16 orig = a->operands[i].reg.orig;
986
1.73M
                MVMint32 reg_i = var_info[orig].count;
987
1.73M
                a->operands[i].reg.i = reg_i;
988
1.73M
                if (var_info[orig].stack_top + 1 >= var_info[orig].stack_alloc) {
989
269k
                    if (var_info[orig].stack_alloc)
990
19.5k
                        var_info[orig].stack_alloc *= 2;
991
269k
                    else
992
249k
                        var_info[orig].stack_alloc = 8;
993
269k
                    var_info[orig].stack = MVM_realloc(var_info[orig].stack,
994
269k
                        var_info[orig].stack_alloc * sizeof(MVMint32));
995
269k
                }
996
1.73M
                var_info[orig].stack[++var_info[orig].stack_top] = reg_i;
997
1.73M
                var_info[orig].count++;
998
1.73M
            }
999
3.65M
            if (is_phi)
1000
669k
                break;
1001
3.65M
        }
1002
2.15M
1003
2.15M
        a = a->next;
1004
2.15M
    }
1005
332k
1006
332k
    /* Visit successors and update their phi functions. */
1007
755k
    for (i = 0; i < x->num_succ; i++) {
1008
423k
        MVMSpeshBB  *y = x->succ[i];
1009
423k
        MVMint32     j = which_pred(tc, g, y, x);
1010
423k
        MVMSpeshIns *p = y->first_ins;
1011
2.22M
        while (p && p->info->opcode == MVM_SSA_PHI) {
1012
1.79M
            MVMuint16 orig = p->operands[j + 1].reg.orig;
1013
1.79M
            MVMint32  st   = var_info[orig].stack_top;
1014
1.79M
            if (st >= 0)
1015
1.46M
                p->operands[j + 1].reg.i = var_info[orig].stack[st];
1016
1.79M
            else
1017
329k
                p->operands[j + 1].reg.i = 0;
1018
1.79M
            p = p->next;
1019
1.79M
        }
1020
423k
    }
1021
332k
1022
332k
    /* Rename for all the children in the dominator tree. */
1023
642k
    for (i = 0; i < x->num_children; i++)
1024
310k
        rename_locals(tc, g, var_info, x->children[i]);
1025
332k
1026
332k
    /* Go over assignments and pop new variable names. */
1027
332k
    a = x->first_ins;
1028
2.49M
    while (a) {
1029
2.15M
        MVMint32 is_phi = a->info->opcode == MVM_SSA_PHI;
1030
5.14M
        for (i = 0; i < a->info->num_operands; i++) {
1031
3.65M
            if (is_phi || (a->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg) {
1032
1.73M
                MVMuint16 orig = a->operands[i].reg.orig;
1033
1.73M
                var_info[orig].stack_top--;
1034
1.73M
            }
1035
3.65M
            if (is_phi)
1036
669k
                break;
1037
3.65M
        }
1038
2.15M
        a = a->next;
1039
2.15M
    }
1040
332k
}
1041
1042
/* Transforms a spesh graph into SSA form. After this, the graph will have all
1043
 * register accesses given an SSA "version", and phi instructions inserted as
1044
 * needed. */
1045
21.8k
static void ssa(MVMThreadContext *tc, MVMSpeshGraph *g) {
1046
21.8k
    SSAVarInfo *var_info;
1047
21.8k
    MVMint32 i, num_locals;
1048
21.8k
1049
21.8k
    /* Compute dominance frontiers. */
1050
21.8k
    MVMSpeshBB **rpo  = reverse_postorder(tc, g);
1051
21.8k
    MVMint32    *doms = compute_dominators(tc, g, rpo);
1052
21.8k
    add_children(tc, g, rpo, doms);
1053
21.8k
    add_dominance_frontiers(tc, g, rpo, doms);
1054
21.8k
    MVM_free(rpo);
1055
21.8k
    MVM_free(doms);
1056
21.8k
1057
21.8k
    /* Initialize per-local data for SSA analysis. */
1058
21.8k
    var_info = initialize_ssa_var_info(tc, g);
1059
21.8k
1060
21.8k
    /* Compute SSA itself. */
1061
21.8k
    insert_phi_functions(tc, g, var_info);
1062
21.8k
    rename_locals(tc, g, var_info, g->entry);
1063
21.8k
1064
21.8k
    /* Allocate space for spesh facts for each local; clean up stacks while
1065
21.8k
     * we're at it. */
1066
21.8k
    num_locals     = g->num_locals;
1067
21.8k
    g->facts       = MVM_spesh_alloc(tc, g, num_locals * sizeof(MVMSpeshFacts *));
1068
21.8k
    g->fact_counts = MVM_spesh_alloc(tc, g, num_locals * sizeof(MVMuint16));
1069
279k
    for (i = 0; i < num_locals; i++) {
1070
257k
        g->fact_counts[i] = var_info[i].count;
1071
257k
        g->facts[i]       = MVM_spesh_alloc(tc, g, var_info[i].count * sizeof(MVMSpeshFacts));
1072
257k
        if (var_info[i].stack_alloc) {
1073
249k
            MVM_free(var_info[i].stack);
1074
249k
            MVM_free(var_info[i].ass_nodes);
1075
249k
        }
1076
257k
    }
1077
21.8k
    MVM_free(var_info);
1078
21.8k
}
1079
1080
/* Takes a static frame and creates a spesh graph for it. */
1081
MVMSpeshGraph * MVM_spesh_graph_create(MVMThreadContext *tc, MVMStaticFrame *sf,
1082
17.1k
        MVMuint32 cfg_only, MVMuint32 insert_object_nulls) {
1083
17.1k
    /* Create top-level graph object. */
1084
17.1k
    MVMSpeshGraph *g = MVM_calloc(1, sizeof(MVMSpeshGraph));
1085
17.1k
    g->sf            = sf;
1086
17.1k
    g->bytecode      = sf->body.bytecode;
1087
17.1k
    g->bytecode_size = sf->body.bytecode_size;
1088
17.1k
    g->handlers      = sf->body.handlers;
1089
17.1k
    g->num_handlers  = sf->body.num_handlers;
1090
17.1k
    g->num_locals    = sf->body.num_locals;
1091
17.1k
    g->num_lexicals  = sf->body.num_lexicals;
1092
17.1k
    g->phi_infos     = MVM_spesh_alloc(tc, g, MVMPhiNodeCacheSize * sizeof(MVMOpInfo));
1093
17.1k
1094
17.1k
    /* Ensure the frame is validated, since we'll rely on this. */
1095
17.1k
    if (sf->body.instrumentation_level == 0) {
1096
0
        MVM_spesh_graph_destroy(tc, g);
1097
0
        MVM_oops(tc, "Spesh: cannot build CFG from unvalidated frame");
1098
0
    }
1099
17.1k
1100
17.1k
    /* Build the CFG out of the static frame, and transform it to SSA. */
1101
17.1k
    build_cfg(tc, g, sf, NULL, 0);
1102
17.1k
    if (insert_object_nulls)
1103
17.1k
        insert_object_null_instructions(tc, g);
1104
17.1k
    if (!cfg_only) {
1105
17.1k
        eliminate_dead(tc, g);
1106
17.1k
        add_predecessors(tc, g);
1107
17.1k
        ssa(tc, g);
1108
17.1k
    }
1109
17.1k
1110
17.1k
    /* Hand back the completed graph. */
1111
17.1k
    return g;
1112
17.1k
}
1113
1114
/* Takes a static frame and creates a spesh graph for it. */
1115
MVMSpeshGraph * MVM_spesh_graph_create_from_cand(MVMThreadContext *tc, MVMStaticFrame *sf,
1116
4.66k
                                                 MVMSpeshCandidate *cand, MVMuint32 cfg_only) {
1117
4.66k
    /* Create top-level graph object. */
1118
4.66k
    MVMSpeshGraph *g     = MVM_calloc(1, sizeof(MVMSpeshGraph));
1119
4.66k
    g->sf                = sf;
1120
4.66k
    g->bytecode          = cand->bytecode;
1121
4.66k
    g->bytecode_size     = cand->bytecode_size;
1122
4.66k
    g->handlers          = cand->handlers;
1123
4.66k
    g->num_handlers      = cand->num_handlers;
1124
4.66k
    g->num_locals        = cand->num_locals;
1125
4.66k
    g->num_lexicals      = cand->num_lexicals;
1126
4.66k
    g->inlines           = cand->inlines;
1127
4.66k
    g->num_inlines       = cand->num_inlines;
1128
4.66k
    g->deopt_addrs       = cand->deopts;
1129
4.66k
    g->num_deopt_addrs   = cand->num_deopts;
1130
4.66k
    g->alloc_deopt_addrs = cand->num_deopts;
1131
4.66k
    g->local_types       = cand->local_types;
1132
4.66k
    g->lexical_types     = cand->lexical_types;
1133
4.66k
    g->spesh_slots       = cand->spesh_slots;
1134
4.66k
    g->num_spesh_slots   = cand->num_spesh_slots;
1135
4.66k
    g->phi_infos         = MVM_spesh_alloc(tc, g, MVMPhiNodeCacheSize * sizeof(MVMOpInfo));
1136
4.66k
    g->cand              = cand;
1137
4.66k
1138
4.66k
    /* Ensure the frame is validated, since we'll rely on this. */
1139
4.66k
    if (sf->body.instrumentation_level == 0) {
1140
0
        MVM_spesh_graph_destroy(tc, g);
1141
0
        MVM_oops(tc, "Spesh: cannot build CFG from unvalidated frame");
1142
0
    }
1143
4.66k
1144
4.66k
    /* Build the CFG out of the static frame, and transform it to SSA. */
1145
4.66k
    build_cfg(tc, g, sf, cand->deopts, cand->num_deopts);
1146
4.66k
    if (!cfg_only) {
1147
4.66k
        eliminate_dead(tc, g);
1148
4.66k
        add_predecessors(tc, g);
1149
4.66k
        ssa(tc, g);
1150
4.66k
    }
1151
4.66k
1152
4.66k
    /* Hand back the completed graph. */
1153
4.66k
    return g;
1154
4.66k
}
1155
1156
/* Marks GCables held in a spesh graph. */
1157
1.97k
void MVM_spesh_graph_mark(MVMThreadContext *tc, MVMSpeshGraph *g, MVMGCWorklist *worklist) {
1158
1.97k
    MVMuint16 i, j, num_locals, num_facts, *local_types;
1159
1.97k
1160
1.97k
    /* Mark static frame. */
1161
1.97k
    MVM_gc_worklist_add(tc, worklist, &g->sf);
1162
1.97k
1163
1.97k
    /* Mark facts. */
1164
1.97k
    num_locals = g->num_locals;
1165
1.62k
    local_types = g->local_types ? g->local_types : g->sf->body.local_types;
1166
25.5k
    for (i = 0; i < num_locals; i++) {
1167
23.6k
        num_facts = g->fact_counts[i];
1168
199k
        for (j = 0; j < num_facts; j++) {
1169
175k
            MVMint32 flags = g->facts[i][j].flags;
1170
175k
            if (flags & MVM_SPESH_FACT_KNOWN_TYPE)
1171
3.28k
                MVM_gc_worklist_add(tc, worklist, &(g->facts[i][j].type));
1172
175k
            if (flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE)
1173
0
                MVM_gc_worklist_add(tc, worklist, &(g->facts[i][j].decont_type));
1174
175k
            if (flags & MVM_SPESH_FACT_KNOWN_VALUE) {
1175
0
                if (local_types[i] == MVM_reg_obj)
1176
0
                    MVM_gc_worklist_add(tc, worklist, &(g->facts[i][j].value.o));
1177
0
                else if (local_types[i] == MVM_reg_str)
1178
0
                    MVM_gc_worklist_add(tc, worklist, &(g->facts[i][j].value.s));
1179
0
            }
1180
175k
        }
1181
23.6k
    }
1182
1.97k
}
1183
1184
/* Destroys a spesh graph, deallocating all its associated memory. */
1185
18.5k
void MVM_spesh_graph_destroy(MVMThreadContext *tc, MVMSpeshGraph *g) {
1186
18.5k
    /* Free all of the allocated node memory. */
1187
18.5k
    MVM_region_destroy(tc, &g->region_alloc);
1188
18.5k
    /* Free handlers array, if different from the static frame. */
1189
18.5k
    if (!g->cand && g->handlers != g->sf->body.handlers)
1190
302
        MVM_free(g->handlers);
1191
18.5k
1192
18.5k
    /* Free the graph itself. */
1193
18.5k
    MVM_free(g);
1194
18.5k
}