Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/spesh/inline.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Ensures that a given compilation unit has access to the specified extop. */
4
static void demand_extop(MVMThreadContext *tc, MVMCompUnit *target_cu, MVMCompUnit *source_cu,
5
0
                         const MVMOpInfo *info) {
6
0
    MVMExtOpRecord *extops;
7
0
    MVMuint16 i, num_extops;
8
0
9
0
    uv_mutex_lock(target_cu->body.inline_tweak_mutex);
10
0
11
0
    /* See if the target compunit already has the extop. */
12
0
    extops     = target_cu->body.extops;
13
0
    num_extops = target_cu->body.num_extops;
14
0
    for (i = 0; i < num_extops; i++)
15
0
        if (extops[i].info == info) {
16
0
            uv_mutex_unlock(target_cu->body.inline_tweak_mutex);
17
0
            return;
18
0
        }
19
0
20
0
    /* If not, need to add it. Locate it in the source CU. */
21
0
    extops     = source_cu->body.extops;
22
0
    num_extops = source_cu->body.num_extops;
23
0
    for (i = 0; i < num_extops; i++) {
24
0
        if (extops[i].info == info) {
25
0
            MVMuint32 orig_size = target_cu->body.num_extops * sizeof(MVMExtOpRecord);
26
0
            MVMuint32 new_size = (target_cu->body.num_extops + 1) * sizeof(MVMExtOpRecord);
27
0
            MVMExtOpRecord *new_extops = MVM_fixed_size_alloc(tc,
28
0
                tc->instance->fsa, new_size);
29
0
            memcpy(new_extops, target_cu->body.extops, orig_size);
30
0
            memcpy(&new_extops[target_cu->body.num_extops], &extops[i], sizeof(MVMExtOpRecord));
31
0
            if (target_cu->body.extops)
32
0
                MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa, orig_size,
33
0
                   target_cu->body.extops);
34
0
            target_cu->body.extops = new_extops;
35
0
            target_cu->body.num_extops++;
36
0
            uv_mutex_unlock(target_cu->body.inline_tweak_mutex);
37
0
            return;
38
0
        }
39
0
    }
40
0
41
0
    /* Didn't find it; should be impossible. */
42
0
    uv_mutex_unlock(target_cu->body.inline_tweak_mutex);
43
0
    MVM_oops(tc, "Spesh: inline failed to find source CU extop entry");
44
0
}
45
46
/* Sees if it will be possible to inline the target code ref, given we could
47
 * already identify a spesh candidate. Returns NULL if no inlining is possible
48
 * or a graph ready to be merged if it will be possible. */
49
MVMSpeshGraph * MVM_spesh_inline_try_get_graph(MVMThreadContext *tc, MVMSpeshGraph *inliner,
50
7.05k
                                               MVMCode *target, MVMSpeshCandidate *cand) {
51
7.05k
    MVMSpeshGraph *ig;
52
7.05k
    MVMSpeshBB    *bb;
53
7.05k
54
7.05k
    /* Check inlining is enabled. */
55
7.05k
    if (!tc->instance->spesh_inline_enabled)
56
0
        return NULL;
57
7.05k
58
7.05k
    /* Check bytecode size is within the inline limit. */
59
7.05k
    if (cand->bytecode_size > MVM_SPESH_MAX_INLINE_SIZE)
60
2.29k
        return NULL;
61
7.05k
62
7.05k
    /* Ensure that this isn't a recursive inlining. */
63
4.75k
    if (target->body.sf == inliner->sf)
64
0
        return NULL;
65
4.75k
66
4.75k
    /* Ensure they're from the same HLL. */
67
4.75k
    if (target->body.sf->body.cu->body.hll_config != inliner->sf->body.cu->body.hll_config)
68
0
        return NULL;
69
4.75k
70
4.75k
    /* Ensure the candidate isn't still logging. */
71
4.75k
    if (cand->sg)
72
88
        return NULL;
73
4.75k
74
4.75k
    /* Build graph from the already-specialized bytecode. */
75
4.66k
    ig = MVM_spesh_graph_create_from_cand(tc, target->body.sf, cand, 0);
76
4.66k
77
4.66k
    /* Traverse graph, looking for anything that might prevent inlining and
78
4.66k
     * also building usage counts up. */
79
4.66k
    bb = ig->entry;
80
17.9k
    while (bb) {
81
14.0k
        MVMSpeshIns *ins = bb->first_ins;
82
75.6k
        while (ins) {
83
62.4k
            /* Track usages. */
84
62.4k
            MVMint32 opcode = ins->info->opcode;
85
62.4k
            MVMint32 is_phi = opcode == MVM_SSA_PHI;
86
62.4k
            MVMuint8 i;
87
188k
            for (i = 0; i < ins->info->num_operands; i++)
88
126k
                if ((is_phi && i > 0)
89
110k
                    || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg))
90
50.4k
                    ig->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i].usages++;
91
62.4k
            if (opcode == MVM_OP_inc_i || opcode == MVM_OP_inc_u ||
92
62.4k
                    opcode == MVM_OP_dec_i || opcode == MVM_OP_dec_u)
93
0
                ig->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i - 1].usages++;
94
62.4k
95
62.4k
            /* Instruction may be marked directly as not being inlinable, in
96
62.4k
             * which case we're done. */
97
62.4k
            if (!is_phi && ins->info->no_inline)
98
321
                goto not_inlinable;
99
62.4k
100
62.4k
            /* If we have lexical access, make sure it's within the frame. */
101
62.0k
            if (ins->info->opcode == MVM_OP_getlex) {
102
439
                if (ins->operands[1].lex.outers > 0)
103
439
                    goto not_inlinable;
104
439
            }
105
61.6k
            else if (ins->info->opcode == MVM_OP_bindlex) {
106
2
                if (ins->operands[0].lex.outers > 0)
107
0
                    goto not_inlinable;
108
2
            }
109
62.0k
110
62.0k
            /* Check we don't have too many args for inlining to work out. */
111
61.6k
            if (ins->info->opcode == MVM_OP_sp_getarg_o ||
112
57.0k
                    ins->info->opcode == MVM_OP_sp_getarg_i ||
113
56.8k
                    ins->info->opcode == MVM_OP_sp_getarg_n ||
114
56.8k
                    ins->info->opcode == MVM_OP_sp_getarg_s) {
115
5.61k
                if (ins->operands[1].lit_i16 >= MAX_ARGS_FOR_OPT)
116
28
                    goto not_inlinable;
117
5.61k
            }
118
61.6k
119
61.6k
            /* Ext-ops need special care in inter-comp-unit inlines. */
120
61.6k
            if (ins->info->opcode == (MVMuint16)-1) {
121
0
                MVMCompUnit *target_cu = inliner->sf->body.cu;
122
0
                MVMCompUnit *source_cu = target->body.sf->body.cu;
123
0
                if (source_cu != target_cu)
124
0
                    demand_extop(tc, target_cu, source_cu, ins->info);
125
0
            }
126
61.6k
127
61.6k
            ins = ins->next;
128
61.6k
        }
129
13.2k
        bb = bb->linear_next;
130
13.2k
    }
131
4.66k
132
4.66k
    /* If we found nothing we can't inline, inlining is fine. */
133
3.88k
    return ig;
134
4.66k
135
4.66k
    /* If we can't find a way to inline, we end up here. */
136
788
  not_inlinable:
137
788
    MVM_spesh_graph_destroy(tc, ig);
138
788
    return NULL;
139
4.66k
}
140
141
/* Finds the deopt index of the return. */
142
3.88k
static MVMint32 return_deopt_idx(MVMThreadContext *tc, MVMSpeshIns *invoke_ins) {
143
3.88k
    MVMSpeshAnn *ann = invoke_ins->annotations;
144
3.88k
    while (ann) {
145
3.88k
        if (ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS)
146
3.88k
            return ann->data.deopt_idx;
147
0
        ann = ann->next;
148
0
    }
149
0
    MVM_oops(tc, "Spesh inline: return_deopt_idx failed");
150
0
}
151
152
/* The following routines fix references to per-compilation-unit things
153
 * that would be broken by inlining. */
154
static void fix_callsite(MVMThreadContext *tc, MVMSpeshGraph *inliner,
155
200
                         MVMSpeshGraph *inlinee, MVMSpeshOperand *to_fix) {
156
200
    to_fix->callsite_idx = MVM_cu_callsite_add(tc, inliner->sf->body.cu,
157
200
        inlinee->sf->body.cu->body.callsites[to_fix->callsite_idx]);
158
200
}
159
static void fix_coderef(MVMThreadContext *tc, MVMSpeshGraph *inliner,
160
0
                        MVMSpeshGraph *inlinee, MVMSpeshOperand *to_fix) {
161
0
    MVM_oops(tc, "Spesh inline: fix_coderef NYI");
162
0
}
163
static void fix_str(MVMThreadContext *tc, MVMSpeshGraph *inliner,
164
441
                    MVMSpeshGraph *inlinee, MVMSpeshOperand *to_fix) {
165
441
    to_fix->lit_str_idx = MVM_cu_string_add(tc, inliner->sf->body.cu,
166
441
        MVM_cu_string(tc, inlinee->sf->body.cu, to_fix->lit_str_idx));
167
441
}
168
static void fix_wval(MVMThreadContext *tc, MVMSpeshGraph *inliner,
169
1.31k
                     MVMSpeshGraph *inlinee, MVMSpeshIns *to_fix) {
170
1.31k
    /* Resolve object, then just put it into a spesh slot. (Could do some
171
1.31k
     * smarter things like trying to see if the SC is referenced by both
172
1.31k
     * compilation units, too.) */
173
1.31k
    MVMCompUnit *cu  = inlinee->sf->body.cu;
174
1.31k
    MVMint16     dep = to_fix->operands[1].lit_i16;
175
1.31k
    MVMint64     idx = to_fix->info->opcode == MVM_OP_wval
176
1.31k
        ? to_fix->operands[2].lit_i16
177
0
        : to_fix->operands[2].lit_i64;
178
1.31k
    if (dep >= 0 && dep < cu->body.num_scs) {
179
1.31k
        MVMSerializationContext *sc = MVM_sc_get_sc(tc, cu, dep);
180
1.31k
        if (sc) {
181
1.31k
            MVMObject *obj = MVM_sc_get_object(tc, sc, idx);
182
1.31k
            MVMint16   ss  = MVM_spesh_add_spesh_slot(tc, inliner, (MVMCollectable *)obj);
183
1.31k
            to_fix->info   = MVM_op_get_op(MVM_OP_sp_getspeshslot);
184
1.31k
            to_fix->operands[1].lit_i16 = ss;
185
1.31k
        }
186
0
        else {
187
0
            MVM_oops(tc,
188
0
                "Spesh inline: SC not yet resolved; lookup failed");
189
0
        }
190
1.31k
    }
191
0
    else {
192
0
        MVM_oops(tc,
193
0
            "Spesh inline: invalid SC index found");
194
0
    }
195
1.31k
}
196
197
/* Resizes the handlers table, making a copy if needed. */
198
850
static void resize_handlers_table(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMuint32 new_handler_count) {
199
850
    if (inliner->handlers == inliner->sf->body.handlers) {
200
302
        /* Original handlers table; need a copy. */
201
302
        MVMFrameHandler *new_handlers = MVM_malloc(new_handler_count * sizeof(MVMFrameHandler));
202
302
        memcpy(new_handlers, inliner->handlers,
203
302
            inliner->num_handlers * sizeof(MVMFrameHandler));
204
302
        inliner->handlers = new_handlers;
205
302
    }
206
548
    else {
207
548
        /* Probably already did some inlines into this frame; resize. */
208
548
        inliner->handlers = MVM_realloc(inliner->handlers,
209
548
            new_handler_count * sizeof(MVMFrameHandler));
210
548
    }
211
850
}
212
213
/* Merges the inlinee's spesh graph into the inliner. */
214
static void merge_graph(MVMThreadContext *tc, MVMSpeshGraph *inliner,
215
                 MVMSpeshGraph *inlinee, MVMCode *inlinee_code,
216
3.88k
                 MVMSpeshIns *invoke_ins) {
217
3.88k
    MVMSpeshFacts **merged_facts;
218
3.88k
    MVMuint16      *merged_fact_counts;
219
3.88k
    MVMint32        i, total_inlines, orig_deopt_addrs;
220
3.88k
    MVMSpeshBB     *inlinee_first_bb = NULL, *inlinee_last_bb = NULL;
221
3.88k
    MVMint32        active_handlers_at_invoke = 0;
222
3.88k
223
3.88k
    /* If the inliner and inlinee are from different compilation units, we
224
3.88k
     * potentially have to fix up extra things. */
225
3.88k
    MVMint32 same_comp_unit = inliner->sf->body.cu == inlinee->sf->body.cu;
226
3.88k
227
3.88k
    /* Renumber the locals, lexicals, and basic blocks of the inlinee; also
228
3.88k
     * re-write any indexes in annotations that need it. */
229
3.88k
    MVMSpeshBB *bb = inlinee->entry;
230
16.2k
    while (bb) {
231
12.4k
        MVMSpeshIns *ins = bb->first_ins;
232
69.1k
        while (ins) {
233
56.7k
            MVMuint16    opcode = ins->info->opcode;
234
56.7k
            MVMSpeshAnn *ann    = ins->annotations;
235
62.1k
            while (ann) {
236
5.45k
                switch (ann->type) {
237
0
                case MVM_SPESH_ANN_FH_START:
238
0
                case MVM_SPESH_ANN_FH_END:
239
0
                case MVM_SPESH_ANN_FH_GOTO:
240
0
                    ann->data.frame_handler_index += inliner->num_handlers;
241
0
                    break;
242
4.70k
                case MVM_SPESH_ANN_DEOPT_INLINE:
243
4.70k
                    ann->data.deopt_idx += inliner->num_deopt_addrs;
244
4.70k
                    break;
245
750
                case MVM_SPESH_ANN_INLINE_START:
246
750
                case MVM_SPESH_ANN_INLINE_END:
247
750
                    ann->data.inline_idx += inliner->num_inlines;
248
750
                    break;
249
5.45k
                }
250
5.45k
                ann = ann->next;
251
5.45k
            }
252
56.7k
253
56.7k
            if (opcode == MVM_SSA_PHI) {
254
31.4k
                for (i = 0; i < ins->info->num_operands; i++)
255
23.6k
                    ins->operands[i].reg.orig += inliner->num_locals;
256
7.86k
            }
257
48.8k
            else {
258
143k
                for (i = 0; i < ins->info->num_operands; i++) {
259
94.7k
                    MVMuint8 flags = ins->info->operands[i];
260
94.7k
                    switch (flags & MVM_operand_rw_mask) {
261
64.9k
                    case MVM_operand_read_reg:
262
64.9k
                    case MVM_operand_write_reg:
263
64.9k
                        ins->operands[i].reg.orig += inliner->num_locals;
264
64.9k
                        break;
265
2
                    case MVM_operand_read_lex:
266
2
                    case MVM_operand_write_lex:
267
2
                        ins->operands[i].lex.idx += inliner->num_lexicals;
268
2
                        break;
269
29.7k
                    default: {
270
29.7k
                        MVMuint32 type = flags & MVM_operand_type_mask;
271
29.7k
                        if (type == MVM_operand_spesh_slot) {
272
8.51k
                            ins->operands[i].lit_i16 += inliner->num_spesh_slots;
273
8.51k
                        }
274
21.2k
                        else if (type == MVM_operand_callsite) {
275
839
                            if (!same_comp_unit)
276
200
                                fix_callsite(tc, inliner, inlinee, &(ins->operands[i]));
277
839
                        }
278
20.3k
                        else if (type == MVM_operand_coderef) {
279
0
                            if (!same_comp_unit)
280
0
                                fix_coderef(tc, inliner, inlinee, &(ins->operands[i]));
281
0
                        }
282
20.3k
                        else if (type == MVM_operand_str) {
283
542
                            if (!same_comp_unit)
284
441
                                fix_str(tc, inliner, inlinee, &(ins->operands[i]));
285
542
                        }
286
29.7k
                        break;
287
2
                        }
288
94.7k
                    }
289
94.7k
                }
290
48.8k
            }
291
56.7k
292
56.7k
            /* Since inlining eliminates the caller/callee distinction, we
293
56.7k
             * need skip going up a caller when resolving exceptions in a
294
56.7k
             * caller-relative way. */
295
56.7k
            if (ins->info->opcode == MVM_OP_throwpayloadlexcaller)
296
0
                ins->info = MVM_op_get_op(MVM_OP_throwpayloadlex);
297
56.7k
298
56.7k
            ins = ins->next;
299
56.7k
        }
300
12.4k
        bb->idx += inliner->num_bbs - 1; /* -1 as we won't include entry */
301
12.4k
        bb->inlined = 1;
302
12.4k
        if (!bb->linear_next)
303
3.88k
            inlinee_last_bb = bb;
304
12.4k
        bb = bb->linear_next;
305
12.4k
    }
306
3.88k
307
3.88k
    /* Incorporate the basic blocks by concatening them onto the end of the
308
3.88k
     * linear_next chain of the inliner; skip the inlinee's fake entry BB. */
309
3.88k
    bb = inliner->entry;
310
505k
    while (bb) {
311
501k
        if (!bb->linear_next) {
312
3.88k
            /* Found the end; insert and we're done. */
313
3.88k
            bb->linear_next = inlinee_first_bb = inlinee->entry->linear_next;
314
3.88k
            bb = NULL;
315
3.88k
        }
316
497k
        else {
317
497k
            bb = bb->linear_next;
318
497k
        }
319
501k
    }
320
3.88k
321
3.88k
    /* Merge facts. */
322
3.88k
    merged_facts = MVM_spesh_alloc(tc, inliner,
323
3.88k
        (inliner->num_locals + inlinee->num_locals) * sizeof(MVMSpeshFacts *));
324
3.88k
    memcpy(merged_facts, inliner->facts,
325
3.88k
        inliner->num_locals * sizeof(MVMSpeshFacts *));
326
3.88k
    memcpy(merged_facts + inliner->num_locals, inlinee->facts,
327
3.88k
        inlinee->num_locals * sizeof(MVMSpeshFacts *));
328
3.88k
    inliner->facts = merged_facts;
329
3.88k
    merged_fact_counts = MVM_spesh_alloc(tc, inliner,
330
3.88k
        (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16));
331
3.88k
    memcpy(merged_fact_counts, inliner->fact_counts,
332
3.88k
        inliner->num_locals * sizeof(MVMuint16));
333
3.88k
    memcpy(merged_fact_counts + inliner->num_locals, inlinee->fact_counts,
334
3.88k
        inlinee->num_locals * sizeof(MVMuint16));
335
3.88k
    inliner->fact_counts = merged_fact_counts;
336
3.88k
337
3.88k
    /* Copy over spesh slots. */
338
13.1k
    for (i = 0; i < inlinee->num_spesh_slots; i++)
339
9.25k
        MVM_spesh_add_spesh_slot(tc, inliner, inlinee->spesh_slots[i]);
340
3.88k
341
3.88k
    /* If they are from separate compilation units, make another pass through
342
3.88k
     * to fix up on wvals. Note we can't do this in the first pass as we must
343
3.88k
     * not modify the spesh slots once we've got started with the rewrites.
344
3.88k
     * Now we've resolved all that, we're good to map wvals elsewhere into
345
3.88k
     * some extra spesh slots. */
346
3.88k
    if (!same_comp_unit) {
347
2.55k
        bb = inlinee->entry;
348
10.3k
        while (bb) {
349
7.82k
            MVMSpeshIns *ins = bb->first_ins;
350
41.7k
            while (ins) {
351
33.8k
                MVMuint16 opcode = ins->info->opcode;
352
33.8k
                if (opcode == MVM_OP_wval || opcode == MVM_OP_wval_wide)
353
1.31k
                    fix_wval(tc, inliner, inlinee, ins);
354
33.8k
                ins = ins->next;
355
33.8k
            }
356
7.82k
            bb = bb->linear_next;
357
7.82k
        }
358
2.55k
    }
359
3.88k
360
3.88k
    /* Merge de-opt tables, if needed. */
361
3.88k
    orig_deopt_addrs = inliner->num_deopt_addrs;
362
3.88k
    if (inlinee->num_deopt_addrs) {
363
1.69k
        assert(inlinee->deopt_addrs != inliner->deopt_addrs);
364
1.69k
        inliner->alloc_deopt_addrs += inlinee->alloc_deopt_addrs;
365
1.69k
        if (inliner->deopt_addrs)
366
1.69k
            inliner->deopt_addrs = MVM_realloc(inliner->deopt_addrs,
367
1.69k
                inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2);
368
1.69k
        else
369
0
            inliner->deopt_addrs = MVM_malloc(inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2);
370
1.69k
        memcpy(inliner->deopt_addrs + inliner->num_deopt_addrs * 2,
371
1.69k
            inlinee->deopt_addrs, inlinee->alloc_deopt_addrs * sizeof(MVMint32) * 2);
372
1.69k
        inliner->num_deopt_addrs += inlinee->num_deopt_addrs;
373
1.69k
    }
374
3.88k
375
3.88k
    /* Merge inlines table, and add us an entry too. */
376
3.88k
    total_inlines = inliner->num_inlines + inlinee->num_inlines + 1;
377
3.88k
    inliner->inlines = inliner->num_inlines
378
1.38k
        ? MVM_realloc(inliner->inlines, total_inlines * sizeof(MVMSpeshInline))
379
2.49k
        : MVM_malloc(total_inlines * sizeof(MVMSpeshInline));
380
3.88k
    if (inlinee->num_inlines)
381
375
        memcpy(inliner->inlines + inliner->num_inlines, inlinee->inlines,
382
375
            inlinee->num_inlines * sizeof(MVMSpeshInline));
383
4.25k
    for (i = inliner->num_inlines; i < total_inlines - 1; i++) {
384
375
        inliner->inlines[i].locals_start += inliner->num_locals;
385
375
        inliner->inlines[i].lexicals_start += inliner->num_lexicals;
386
375
        inliner->inlines[i].return_deopt_idx += orig_deopt_addrs;
387
375
    }
388
3.88k
    inliner->inlines[total_inlines - 1].code           = inlinee_code;
389
3.88k
    inliner->inlines[total_inlines - 1].g              = inlinee;
390
3.88k
    inliner->inlines[total_inlines - 1].locals_start   = inliner->num_locals;
391
3.88k
    inliner->inlines[total_inlines - 1].lexicals_start = inliner->num_lexicals;
392
3.88k
    switch (invoke_ins->info->opcode) {
393
406
    case MVM_OP_invoke_v:
394
406
        inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_VOID;
395
406
        break;
396
3.47k
    case MVM_OP_invoke_o:
397
3.47k
        inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig;
398
3.47k
        inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_OBJ;
399
3.47k
        break;
400
0
    case MVM_OP_invoke_i:
401
0
        inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig;
402
0
        inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_INT;
403
0
        break;
404
0
    case MVM_OP_invoke_n:
405
0
        inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig;
406
0
        inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_NUM;
407
0
        break;
408
0
    case MVM_OP_invoke_s:
409
0
        inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig;
410
0
        inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_STR;
411
0
        break;
412
0
    default:
413
0
        MVM_oops(tc, "Spesh inline: unknown invoke instruction");
414
3.88k
    }
415
3.88k
    inliner->inlines[total_inlines - 1].return_deopt_idx = return_deopt_idx(tc, invoke_ins);
416
3.88k
    inliner->num_inlines = total_inlines;
417
3.88k
418
3.88k
    /* Create/update per-specialization local and lexical type maps. */
419
3.88k
    if (!inliner->local_types && inliner->num_locals) {
420
2.23k
        MVMint32 local_types_size = inliner->num_locals * sizeof(MVMuint16);
421
2.23k
        inliner->local_types = MVM_malloc(local_types_size);
422
2.23k
        memcpy(inliner->local_types, inliner->sf->body.local_types, local_types_size);
423
2.23k
    }
424
3.88k
    inliner->local_types = MVM_realloc(inliner->local_types,
425
3.88k
        (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16));
426
3.88k
    if (inlinee->num_locals)
427
3.88k
        memcpy(inliner->local_types + inliner->num_locals,
428
3.30k
            inlinee->local_types ? inlinee->local_types : inlinee->sf->body.local_types,
429
3.88k
            inlinee->num_locals * sizeof(MVMuint16));
430
3.88k
    if (!inliner->lexical_types && inliner->num_lexicals) {
431
541
        MVMint32 lexical_types_size = inliner->num_lexicals * sizeof(MVMuint16);
432
541
        inliner->lexical_types = MVM_malloc(lexical_types_size);
433
541
        memcpy(inliner->lexical_types, inliner->sf->body.lexical_types, lexical_types_size);
434
541
    }
435
3.88k
    inliner->lexical_types = MVM_realloc(inliner->lexical_types,
436
3.88k
        (inliner->num_lexicals + inlinee->num_lexicals) * sizeof(MVMuint16));
437
3.88k
    if (inlinee->num_lexicals)
438
1
        memcpy(inliner->lexical_types + inliner->num_lexicals,
439
1
            inlinee->lexical_types ? inlinee->lexical_types : inlinee->sf->body.lexical_types,
440
1
            inlinee->num_lexicals * sizeof(MVMuint16));
441
3.88k
442
3.88k
    /* Merge handlers from inlinee. */
443
3.88k
    if (inlinee->num_handlers) {
444
0
        MVMuint32 total_handlers = inliner->num_handlers + inlinee->num_handlers;
445
0
        resize_handlers_table(tc, inliner, total_handlers);
446
0
        memcpy(inliner->handlers + inliner->num_handlers, inlinee->handlers,
447
0
            inlinee->num_handlers * sizeof(MVMFrameHandler));
448
0
        for (i = inliner->num_handlers; i < total_handlers; i++) {
449
0
            inliner->handlers[i].block_reg += inliner->num_locals;
450
0
            inliner->handlers[i].label_reg += inliner->num_locals;
451
0
            if (inliner->sf != inlinee->sf->body.outer)
452
0
                inliner->handlers[i].inlined_and_not_lexical = 1;
453
0
        }
454
0
    }
455
3.88k
456
3.88k
    /* If the inliner has handlers in effect at the point of the call that we
457
3.88k
     * are inlining, then we duplicate those and place them surrounding the
458
3.88k
     * inlinee, but with the goto still pointing to the original location.
459
3.88k
     * This means that we can still do a linear scan when searching for an
460
3.88k
     * exception handler, and don't have to try the (costly and fiddly) matter
461
3.88k
     * of trying to traverse the post-inlined call chain. */
462
3.88k
    if (inliner->sf->body.num_handlers) {
463
1.29k
        /* Walk inliner looking for handlers in effect at the point we hit the
464
1.29k
         * invoke instruction we're currently inlining; also record all of the
465
1.29k
         * instructions where the handler "goto" annotation lives. */
466
1.29k
        MVMuint32 orig_handlers = inliner->sf->body.num_handlers;
467
1.29k
        MVMuint8 *active = MVM_spesh_alloc(tc, inliner, orig_handlers);
468
1.29k
        MVMSpeshIns **handler_goto_ins = MVM_spesh_alloc(tc, inliner,
469
1.29k
            orig_handlers * sizeof(MVMSpeshIns *));
470
1.29k
        MVMint32 found_invoke = 0;
471
1.29k
        bb = inliner->entry;
472
355k
        while (bb && !bb->inlined) {
473
354k
            MVMSpeshIns *ins = bb->first_ins;
474
2.60M
            while (ins) {
475
2.25M
                MVMSpeshAnn *ann = ins->annotations;
476
2.42M
                while (ann) {
477
170k
                    if (ann->type == MVM_SPESH_ANN_FH_GOTO) {
478
5.99k
                        if (ann->data.frame_handler_index < orig_handlers)
479
4.04k
                            handler_goto_ins[ann->data.frame_handler_index] = ins;
480
5.99k
                    }
481
164k
                    else if (!found_invoke) {
482
60.0k
                        /* Only update these to the point we found the invoke
483
60.0k
                         * being inlined, so it serves as a snapshot of what
484
60.0k
                         * is active. */
485
60.0k
                        if (ann->type == MVM_SPESH_ANN_FH_START)
486
2.72k
                            active[ann->data.frame_handler_index] = 1;
487
57.2k
                        else if (ann->type == MVM_SPESH_ANN_FH_END)
488
1.77k
                            active[ann->data.frame_handler_index] = 0;
489
60.0k
                    }
490
170k
                    ann = ann->next;
491
170k
                }
492
2.25M
                if (ins == invoke_ins) {
493
1.29k
                    /* Found it; see if we have any handlers active. If so, we
494
1.29k
                     * will continue walking to collect goto annotations. */
495
1.29k
                    found_invoke = 1;
496
7.38k
                    for (i = 0; i < orig_handlers; i++)
497
6.09k
                        active_handlers_at_invoke += active[i];
498
1.29k
                    if (!active_handlers_at_invoke)
499
441
                        break;
500
1.29k
                }
501
2.25M
                ins = ins->next;
502
2.25M
            }
503
354k
            if (found_invoke && !active_handlers_at_invoke)
504
441
                break;
505
354k
            bb = bb->linear_next;
506
354k
        }
507
1.29k
508
1.29k
        /* If we found handlers active at the point of invoke, duplicate them
509
1.29k
         * in the handlers table and add annotations. */
510
1.29k
        if (active_handlers_at_invoke) {
511
850
            MVMuint32 insert_pos = inliner->num_handlers + inlinee->num_handlers;
512
850
            resize_handlers_table(tc, inliner, insert_pos + active_handlers_at_invoke);
513
4.47k
            for (i = orig_handlers - 1; i >= 0; i--) {
514
3.62k
                if (active[i]) {
515
954
                    /* Add handler start annotation to first inlinee instruction. */
516
954
                    MVMSpeshAnn *new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn));
517
954
                    new_ann->type = MVM_SPESH_ANN_FH_START;
518
954
                    new_ann->data.frame_handler_index = insert_pos;
519
954
                    new_ann->next = inlinee_first_bb->first_ins->annotations;
520
954
                    inlinee_first_bb->first_ins->annotations = new_ann;
521
954
522
954
                    /* Add handler end annotation to last inlinee instruction. */
523
954
                    new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn));
524
954
                    new_ann->type = MVM_SPESH_ANN_FH_END;
525
954
                    new_ann->data.frame_handler_index = insert_pos;
526
954
                    new_ann->next = inlinee_last_bb->last_ins->annotations;
527
954
                    inlinee_last_bb->last_ins->annotations = new_ann;
528
954
529
954
                    /* Add handler goto annotation to original target in inliner. */
530
954
                    new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn));
531
954
                    new_ann->type = MVM_SPESH_ANN_FH_GOTO;
532
954
                    new_ann->data.frame_handler_index = insert_pos;
533
954
                    new_ann->next = handler_goto_ins[i]->annotations;
534
954
                    handler_goto_ins[i]->annotations = new_ann;
535
954
536
954
                    /* Copy handler entry to new slot. */
537
954
                    memcpy(inliner->handlers + insert_pos, inliner->handlers + i,
538
954
                        sizeof(MVMFrameHandler));
539
954
                    insert_pos++;
540
954
                }
541
3.62k
            }
542
850
        }
543
1.29k
    }
544
3.88k
545
3.88k
    /* Update total locals, lexicals, basic blocks, and handlers of the
546
3.88k
     * inliner. */
547
3.88k
    inliner->num_bbs      += inlinee->num_bbs - 1;
548
3.88k
    inliner->num_locals   += inlinee->num_locals;
549
3.88k
    inliner->num_lexicals += inlinee->num_lexicals;
550
3.88k
    inliner->num_handlers += inlinee->num_handlers + active_handlers_at_invoke;
551
3.88k
}
552
553
/* Tweak the successor of a BB, also updating the target BBs pred. */
554
7.76k
static void tweak_succ(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshBB *new_succ) {
555
7.76k
    if (bb->num_succ == 0) {
556
3.50k
        bb->succ = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *));
557
3.50k
        bb->num_succ = 1;
558
3.50k
    }
559
7.76k
    if (bb->num_succ == 1)
560
7.76k
        bb->succ[0] = new_succ;
561
7.76k
    else
562
0
        MVM_oops(tc, "Spesh inline: unexpected num_succ");
563
7.76k
    if (new_succ->num_pred == 0) {
564
0
        new_succ->pred = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *));
565
0
        new_succ->num_pred = 1;
566
0
        new_succ->pred[0] = bb;
567
0
    }
568
7.76k
    else {
569
7.76k
        MVMint32 found = 0;
570
7.76k
        MVMint32 i;
571
8.09k
        for (i = 0; i < new_succ->num_pred; i++)
572
8.09k
            if (new_succ->pred[i]->idx + 1 == new_succ->idx) {
573
7.76k
                new_succ->pred[i] = bb;
574
7.76k
                found = 1;
575
7.76k
                break;
576
7.76k
            }
577
7.76k
        if (!found)
578
0
            MVM_oops(tc,
579
0
                "Spesh inline: could not find appropriate pred to update\n");
580
7.76k
    }
581
7.76k
}
582
583
/* Finds return instructions and re-writes them into gotos, doing any needed
584
 * boxing or unboxing. */
585
2.07k
static void return_to_set(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *return_ins, MVMSpeshOperand target) {
586
2.07k
    MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
587
2.07k
    operands[0]               = target;
588
2.07k
    operands[1]               = return_ins->operands[0];
589
2.07k
    return_ins->info          = MVM_op_get_op(MVM_OP_set);
590
2.07k
    return_ins->operands      = operands;
591
2.07k
}
592
593
static void return_to_box(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *return_bb,
594
                   MVMSpeshIns *return_ins, MVMSpeshOperand target,
595
1.40k
                   MVMuint16 box_type_op, MVMuint16 box_op) {
596
1.40k
    /* Create and insert boxing instruction after current return instruction. */
597
1.40k
    MVMSpeshIns      *box_ins     = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
598
1.40k
    MVMSpeshOperand *box_operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
599
1.40k
    box_ins->info                 = MVM_op_get_op(box_op);
600
1.40k
    box_ins->operands             = box_operands;
601
1.40k
    box_operands[0]               = target;
602
1.40k
    box_operands[1]               = return_ins->operands[0];
603
1.40k
    box_operands[2]               = target;
604
1.40k
    MVM_spesh_manipulate_insert_ins(tc, return_bb, return_ins, box_ins);
605
1.40k
606
1.40k
    /* Now turn return instruction node into lookup of appropraite box
607
1.40k
     * type. */
608
1.40k
    return_ins->info        = MVM_op_get_op(box_type_op);
609
1.40k
    return_ins->operands[0] = target;
610
1.40k
}
611
612
static void rewrite_int_return(MVMThreadContext *tc, MVMSpeshGraph *g,
613
                        MVMSpeshBB *return_bb, MVMSpeshIns *return_ins,
614
1.42k
                        MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) {
615
1.42k
    switch (invoke_ins->info->opcode) {
616
404
    case MVM_OP_invoke_v:
617
404
        MVM_spesh_manipulate_delete_ins(tc, g, return_bb, return_ins);
618
404
        break;
619
0
    case MVM_OP_invoke_i:
620
0
        return_to_set(tc, g, return_ins, invoke_ins->operands[0]);
621
0
        break;
622
1.02k
    case MVM_OP_invoke_o:
623
1.02k
        return_to_box(tc, g, return_bb, return_ins, invoke_ins->operands[0],
624
1.02k
            MVM_OP_hllboxtype_i, MVM_OP_box_i);
625
1.02k
        break;
626
0
    default:
627
0
        MVM_oops(tc,
628
0
            "Spesh inline: unhandled case of return_i");
629
1.42k
    }
630
1.42k
}
631
static void rewrite_num_return(MVMThreadContext *tc, MVMSpeshGraph *g,
632
                        MVMSpeshBB *return_bb, MVMSpeshIns *return_ins,
633
0
                        MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) {
634
0
    switch (invoke_ins->info->opcode) {
635
0
    case MVM_OP_invoke_v:
636
0
        MVM_spesh_manipulate_delete_ins(tc, g, return_bb, return_ins);
637
0
        break;
638
0
    case MVM_OP_invoke_n:
639
0
        return_to_set(tc, g, return_ins, invoke_ins->operands[0]);
640
0
        break;
641
0
    case MVM_OP_invoke_o:
642
0
        return_to_box(tc, g, return_bb, return_ins, invoke_ins->operands[0],
643
0
            MVM_OP_hllboxtype_n, MVM_OP_box_n);
644
0
        break;
645
0
    default:
646
0
        MVM_oops(tc,
647
0
            "Spesh inline: unhandled case of return_n");
648
0
    }
649
0
}
650
651
static void rewrite_str_return(MVMThreadContext *tc, MVMSpeshGraph *g,
652
                        MVMSpeshBB *return_bb, MVMSpeshIns *return_ins,
653
378
                        MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) {
654
378
    switch (invoke_ins->info->opcode) {
655
0
    case MVM_OP_invoke_v:
656
0
        MVM_spesh_manipulate_delete_ins(tc, g, return_bb, return_ins);
657
0
        break;
658
0
    case MVM_OP_invoke_s:
659
0
        return_to_set(tc, g, return_ins, invoke_ins->operands[0]);
660
0
        break;
661
378
    case MVM_OP_invoke_o:
662
378
        return_to_box(tc, g, return_bb, return_ins, invoke_ins->operands[0],
663
378
            MVM_OP_hllboxtype_s, MVM_OP_box_s);
664
378
        break;
665
0
    default:
666
0
        MVM_oops(tc,
667
0
            "Spesh inline: unhandled case of return_s");
668
378
    }
669
378
}
670
671
static void rewrite_obj_return(MVMThreadContext *tc, MVMSpeshGraph *g,
672
                        MVMSpeshBB *return_bb, MVMSpeshIns *return_ins,
673
2.07k
                        MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) {
674
2.07k
    switch (invoke_ins->info->opcode) {
675
2
    case MVM_OP_invoke_v:
676
2
        MVM_spesh_manipulate_delete_ins(tc, g, return_bb, return_ins);
677
2
        break;
678
2.07k
    case MVM_OP_invoke_o:
679
2.07k
        return_to_set(tc, g, return_ins, invoke_ins->operands[0]);
680
2.07k
        break;
681
0
    default:
682
0
        MVM_oops(tc,
683
0
            "Spesh inline: unhandled case of return_o");
684
2.07k
    }
685
2.07k
}
686
687
static void rewrite_returns(MVMThreadContext *tc, MVMSpeshGraph *inliner,
688
                     MVMSpeshGraph *inlinee, MVMSpeshBB *invoke_bb,
689
3.88k
                     MVMSpeshIns *invoke_ins) {
690
3.88k
    /* Locate return instructions. */
691
3.88k
    MVMSpeshBB *bb = inlinee->entry;
692
16.2k
    while (bb) {
693
12.4k
        MVMSpeshIns *ins = bb->first_ins;
694
74.4k
        while (ins) {
695
62.0k
            MVMuint16 opcode = ins->info->opcode;
696
62.0k
            switch (opcode) {
697
0
            case MVM_OP_return:
698
0
                if (invoke_ins->info->opcode == MVM_OP_invoke_v) {
699
0
                    MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins,
700
0
                        invoke_bb->succ[0]);
701
0
                    tweak_succ(tc, inliner, bb, invoke_bb->succ[0]);
702
0
                }
703
0
                else {
704
0
                    MVM_oops(tc,
705
0
                        "Spesh inline: return_v/invoke_[!v] mismatch");
706
0
                }
707
0
                break;
708
1.42k
            case MVM_OP_return_i:
709
1.42k
                MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins,
710
1.42k
                    invoke_bb->succ[0]);
711
1.42k
                tweak_succ(tc, inliner, bb, invoke_bb->succ[0]);
712
1.42k
                rewrite_int_return(tc, inliner, bb, ins, invoke_bb, invoke_ins);
713
1.42k
                break;
714
0
            case MVM_OP_return_n:
715
0
                MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins,
716
0
                    invoke_bb->succ[0]);
717
0
                tweak_succ(tc, inliner, bb, invoke_bb->succ[0]);
718
0
                rewrite_num_return(tc, inliner, bb, ins, invoke_bb, invoke_ins);
719
0
                break;
720
378
            case MVM_OP_return_s:
721
378
                MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins,
722
378
                    invoke_bb->succ[0]);
723
378
                tweak_succ(tc, inliner, bb, invoke_bb->succ[0]);
724
378
                rewrite_str_return(tc, inliner, bb, ins, invoke_bb, invoke_ins);
725
378
                break;
726
2.07k
            case MVM_OP_return_o:
727
2.07k
                MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins,
728
2.07k
                    invoke_bb->succ[0]);
729
2.07k
                tweak_succ(tc, inliner, bb, invoke_bb->succ[0]);
730
2.07k
                rewrite_obj_return(tc, inliner, bb, ins, invoke_bb, invoke_ins);
731
2.07k
                break;
732
62.0k
            }
733
62.0k
            ins = ins->next;
734
62.0k
        }
735
12.4k
        bb = bb->linear_next;
736
12.4k
    }
737
3.88k
}
738
739
/* Re-writes argument passing and parameter taking instructions to simple
740
 * register set operations. */
741
static void rewrite_args(MVMThreadContext *tc, MVMSpeshGraph *inliner,
742
                  MVMSpeshGraph *inlinee, MVMSpeshBB *invoke_bb,
743
3.88k
                  MVMSpeshCallInfo *call_info) {
744
3.88k
    /* Look for param-taking instructions. Track what arg instructions we
745
3.88k
     * use in the process. */
746
3.88k
    MVMSpeshBB *bb = inlinee->entry;
747
16.2k
    while (bb) {
748
12.4k
        MVMSpeshIns *ins = bb->first_ins;
749
74.0k
        while (ins) {
750
61.5k
            MVMuint16    opcode = ins->info->opcode;
751
61.5k
            MVMSpeshIns *next   = ins->next;
752
61.5k
            switch (opcode) {
753
4.58k
            case MVM_OP_sp_getarg_o:
754
4.58k
            case MVM_OP_sp_getarg_i:
755
4.58k
            case MVM_OP_sp_getarg_n:
756
4.58k
            case MVM_OP_sp_getarg_s: {
757
4.58k
                MVMuint16    idx     = ins->operands[1].lit_i16;
758
4.58k
                MVMSpeshIns *arg_ins = call_info->arg_ins[idx];
759
4.58k
                switch (arg_ins->info->opcode) {
760
4.58k
                case MVM_OP_arg_i:
761
4.58k
                case MVM_OP_arg_n:
762
4.58k
                case MVM_OP_arg_s:
763
4.58k
                case MVM_OP_arg_o:
764
4.58k
                    /* Arg passer just becomes a set instruction; delete the
765
4.58k
                     * parameter-taking instruction. */
766
4.58k
                    arg_ins->info        = MVM_op_get_op(MVM_OP_set);
767
4.58k
                    arg_ins->operands[0] = ins->operands[0];
768
4.58k
                    MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins);
769
4.58k
                    MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++;
770
4.58k
                    break;
771
0
                case MVM_OP_argconst_i:
772
0
                    arg_ins->info        = MVM_op_get_op(MVM_OP_const_i64);
773
0
                    arg_ins->operands[0] = ins->operands[0];
774
0
                    MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins);
775
0
                    MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++;
776
0
                    break;
777
0
                case MVM_OP_argconst_n:
778
0
                    arg_ins->info        = MVM_op_get_op(MVM_OP_const_n64);
779
0
                    arg_ins->operands[0] = ins->operands[0];
780
0
                    MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins);
781
0
                    MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++;
782
0
                    break;
783
0
                case MVM_OP_argconst_s:
784
0
                    arg_ins->info        = MVM_op_get_op(MVM_OP_const_s);
785
0
                    arg_ins->operands[0] = ins->operands[0];
786
0
                    MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins);
787
0
                    MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++;
788
0
                    break;
789
0
                default:
790
0
                    MVM_oops(tc,
791
0
                        "Spesh inline: unhandled arg instruction %d",
792
0
                        arg_ins->info->opcode);
793
4.58k
                }
794
4.58k
                break;
795
4.58k
            }
796
61.5k
            }
797
61.5k
            ins = next;
798
61.5k
        }
799
12.4k
        bb = bb->linear_next;
800
12.4k
    }
801
3.88k
802
3.88k
    {
803
3.88k
    MVMSpeshIns *arg_ins = call_info->prepargs_ins->next;
804
3.88k
    /* If there's some args that are not fetched by our inlinee,
805
3.88k
     * we have to kick them out, as arg_* ops are only valid between
806
3.88k
     * a prepargs and invoke_* op. */
807
12.7k
    while (arg_ins) {
808
8.86k
        MVMuint16    opcode = arg_ins->info->opcode;
809
8.86k
        MVMSpeshIns *next   = arg_ins->next;
810
8.86k
        switch (opcode) {
811
258
            case MVM_OP_arg_i:
812
258
            case MVM_OP_arg_n:
813
258
            case MVM_OP_arg_s:
814
258
            case MVM_OP_arg_o:
815
258
                MVM_spesh_get_facts(tc, inliner, arg_ins->operands[1])->usages--;
816
258
                /* fallthrough */
817
402
            case MVM_OP_argconst_i:
818
402
            case MVM_OP_argconst_n:
819
402
            case MVM_OP_argconst_s:
820
402
                MVM_spesh_manipulate_delete_ins(tc, inliner, call_info->prepargs_bb, arg_ins);
821
402
                break;
822
4.58k
            case MVM_OP_set:
823
4.58k
                break;
824
3.88k
            case MVM_OP_invoke_i:
825
3.88k
            case MVM_OP_invoke_n:
826
3.88k
            case MVM_OP_invoke_s:
827
3.88k
            case MVM_OP_invoke_o:
828
3.88k
            case MVM_OP_invoke_v:
829
3.88k
            default:
830
3.88k
                next = NULL;
831
8.86k
        }
832
8.86k
        arg_ins = next;
833
8.86k
    }
834
3.88k
    }
835
3.88k
836
3.88k
    /* Delete the prepargs instruction. */
837
3.88k
    MVM_spesh_manipulate_delete_ins(tc, inliner, invoke_bb, call_info->prepargs_ins);
838
3.88k
}
839
840
/* Annotates first and last instruction in post-processed inlinee with start
841
 * and end inline annotations. */
842
static void annotate_inline_start_end(MVMThreadContext *tc, MVMSpeshGraph *inliner,
843
3.88k
                               MVMSpeshGraph *inlinee, MVMint32 idx) {
844
3.88k
    /* Annotate first instruction. */
845
3.88k
    MVMSpeshAnn *start_ann     = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn));
846
3.88k
    MVMSpeshBB *bb             = inlinee->entry->succ[0];
847
3.88k
    start_ann->next            = bb->first_ins->annotations;
848
3.88k
    start_ann->type            = MVM_SPESH_ANN_INLINE_START;
849
3.88k
    start_ann->data.inline_idx = idx;
850
3.88k
    bb->first_ins->annotations = start_ann;
851
3.88k
852
3.88k
    /* Now look for last instruction and annotate it. */
853
12.4k
    while (bb) {
854
8.53k
        if (!bb->linear_next) {
855
3.88k
            MVMSpeshAnn *end_ann      = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn));
856
3.88k
            end_ann->next             = bb->last_ins->annotations;
857
3.88k
            end_ann->type             = MVM_SPESH_ANN_INLINE_END;
858
3.88k
            end_ann->data.inline_idx  = idx;
859
3.88k
            bb->last_ins->annotations = end_ann;
860
3.88k
        }
861
8.53k
        bb = bb->linear_next;
862
8.53k
    }
863
3.88k
}
864
865
/* Drives the overall inlining process. */
866
void MVM_spesh_inline(MVMThreadContext *tc, MVMSpeshGraph *inliner,
867
                      MVMSpeshCallInfo *call_info, MVMSpeshBB *invoke_bb,
868
                      MVMSpeshIns *invoke_ins, MVMSpeshGraph *inlinee,
869
3.88k
                      MVMCode *inlinee_code) {
870
3.88k
    /* Merge inlinee's graph into the inliner. */
871
3.88k
    merge_graph(tc, inliner, inlinee, inlinee_code, invoke_ins);
872
3.88k
873
3.88k
    /* If we're profiling, note it's an inline. */
874
3.88k
    if (inlinee->entry->linear_next->first_ins->info->opcode == MVM_OP_prof_enterspesh) {
875
0
        MVMSpeshIns *profenter         = inlinee->entry->linear_next->first_ins;
876
0
        profenter->info                = MVM_op_get_op(MVM_OP_prof_enterinline);
877
0
        profenter->operands            = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshOperand));
878
0
        profenter->operands[0].lit_i16 = MVM_spesh_add_spesh_slot(tc, inliner,
879
0
            (MVMCollectable *)inlinee->sf);
880
0
    }
881
3.88k
882
3.88k
    /* Re-write returns to a set and goto. */
883
3.88k
    rewrite_returns(tc, inliner, inlinee, invoke_bb, invoke_ins);
884
3.88k
885
3.88k
    /* Re-write the argument passing instructions to poke values into the
886
3.88k
     * appropriate slots. */
887
3.88k
    rewrite_args(tc, inliner, inlinee, invoke_bb, call_info);
888
3.88k
889
3.88k
    /* Annotate first and last instruction with inline table annotations. */
890
3.88k
    annotate_inline_start_end(tc, inliner, inlinee, inliner->num_inlines - 1);
891
3.88k
892
3.88k
    /* Finally, turn the invoke instruction into a goto. */
893
3.88k
    invoke_ins->info = MVM_op_get_op(MVM_OP_goto);
894
3.88k
    invoke_ins->operands[0].ins_bb = inlinee->entry->linear_next;
895
3.88k
    tweak_succ(tc, inliner, invoke_bb, inlinee->entry->linear_next);
896
3.88k
}