Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/spesh/optimize.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* This is where the main optimization work on a spesh graph takes place,
4
 * using facts discovered during analysis. */
5
6
/* Writes to stderr about each inline that we perform. */
7
#define MVM_LOG_INLINES 0
8
9
/* Obtains facts for an operand, just directly accessing them without
10
 * inferring any kind of usage. */
11
13.0M
static MVMSpeshFacts * get_facts_direct(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
12
13.0M
    return &g->facts[o.reg.orig][o.reg.i];
13
13.0M
}
14
15
/* Obtains facts for an operand, indicating they are being used. */
16
294k
MVMSpeshFacts * MVM_spesh_get_and_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
17
294k
    MVMSpeshFacts *facts = get_facts_direct(tc, g, o);
18
294k
    MVM_spesh_use_facts(tc, g, facts);
19
294k
    return facts;
20
294k
}
21
22
/* Obtains facts for an operand, but doesn't (yet) indicate usefulness */
23
579k
MVMSpeshFacts * MVM_spesh_get_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
24
579k
    return get_facts_direct(tc, g, o);
25
579k
}
26
27
/* Mark facts for an operand as being relied upon */
28
490k
void MVM_spesh_use_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshFacts *facts) {
29
490k
    if (facts->flags & MVM_SPESH_FACT_FROM_LOG_GUARD)
30
78.1k
        g->log_guards[facts->log_guard].used = 1;
31
490k
    if (facts->flags & MVM_SPESH_FACT_MERGED_WITH_LOG_GUARD) {
32
3.78k
        MVMSpeshIns *thePHI = facts->writer;
33
3.78k
        MVMuint32 op_i;
34
3.78k
35
12.4k
        for (op_i = 1; op_i < thePHI->info->num_operands; op_i++) {
36
8.62k
            MVM_spesh_get_and_use_facts(tc, g, thePHI->operands[op_i]);
37
8.62k
        }
38
3.78k
    }
39
490k
}
40
41
/* Obtains a string constant. */
42
63.7k
MVMString * MVM_spesh_get_string(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o) {
43
63.7k
    return MVM_cu_string(tc, g->sf->body.cu, o.lit_str_idx);
44
63.7k
}
45
46
/* Copy facts between two register operands. */
47
static void copy_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand to,
48
336k
                       MVMSpeshOperand from) {
49
336k
    MVMSpeshFacts *tfacts = get_facts_direct(tc, g, to);
50
336k
    MVMSpeshFacts *ffacts = get_facts_direct(tc, g, from);
51
336k
    tfacts->flags         = ffacts->flags;
52
336k
    tfacts->type          = ffacts->type;
53
336k
    tfacts->decont_type   = ffacts->decont_type;
54
336k
    tfacts->value         = ffacts->value;
55
336k
    tfacts->log_guard     = ffacts->log_guard;
56
336k
}
57
58
/* Adds a value into a spesh slot and returns its index.
59
 * If a spesh slot already holds this value, return that instead */
60
28.0k
MVMint16 MVM_spesh_add_spesh_slot_try_reuse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) {
61
28.0k
    MVMint16 prev_slot;
62
456k
    for (prev_slot = 0; prev_slot < g->num_spesh_slots; prev_slot++) {
63
439k
        if (g->spesh_slots[prev_slot] == c)
64
11.6k
            return prev_slot;
65
439k
    }
66
16.4k
    return MVM_spesh_add_spesh_slot(tc, g, c);
67
28.0k
}
68
69
/* Adds a value into a spesh slot and returns its index. */
70
122k
MVMint16 MVM_spesh_add_spesh_slot(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCollectable *c) {
71
122k
    if (g->num_spesh_slots >= g->alloc_spesh_slots) {
72
22.2k
        g->alloc_spesh_slots += 8;
73
22.2k
        if (g->spesh_slots)
74
10.1k
            g->spesh_slots = MVM_realloc(g->spesh_slots,
75
10.1k
                g->alloc_spesh_slots * sizeof(MVMCollectable *));
76
22.2k
        else
77
12.0k
            g->spesh_slots = MVM_malloc(g->alloc_spesh_slots * sizeof(MVMCollectable *));
78
22.2k
    }
79
122k
    g->spesh_slots[g->num_spesh_slots] = c;
80
122k
    return g->num_spesh_slots++;
81
122k
}
82
83
static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
84
                             MVMSpeshIns *ins, MVMint32 type_operand);
85
86
2.76k
static void optimize_findmeth_s_perhaps_constant(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
87
2.76k
    MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
88
2.76k
89
2.76k
    if (name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
90
2.31k
        if (name_facts->writer && name_facts->writer->info->opcode == MVM_OP_const_s) {
91
2.31k
            name_facts->usages--;
92
2.31k
            ins->info = MVM_op_get_op(MVM_OP_findmeth);
93
2.31k
            ins->operands[2].lit_i64 = 0;
94
2.31k
            ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx;
95
2.31k
            MVM_spesh_use_facts(tc, g, name_facts);
96
2.31k
        }
97
2.31k
    }
98
2.76k
}
99
100
/* Performs optimization on a method lookup. If we know the type that we'll
101
 * be dispatching on, resolve it right off. If not, add a cache. */
102
27.9k
static void optimize_method_lookup(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
103
27.9k
    /* See if we can resolve the method right off due to knowing the type. */
104
27.9k
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
105
27.9k
    MVMint32 resolved = 0;
106
27.9k
    if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
107
17.0k
        /* Try to resolve. */
108
17.0k
        MVMString *name = MVM_spesh_get_string(tc, g, ins->operands[2]);
109
17.0k
        MVMObject *meth = MVM_spesh_try_find_method(tc, obj_facts->type, name);
110
17.0k
        if (!MVM_is_null(tc, meth)) {
111
16.3k
            /* Could compile-time resolve the method. Add it in a spesh slot. */
112
16.3k
            MVMint16 ss = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)meth);
113
16.3k
114
16.3k
            /* Tweak facts for the target, given we know the method. */
115
16.3k
            MVMSpeshFacts *meth_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
116
16.3k
            meth_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
117
16.3k
            meth_facts->value.o = meth;
118
16.3k
119
16.3k
            /* Update the instruction to grab the spesh slot. */
120
16.3k
            ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
121
16.3k
            ins->operands[1].lit_i16 = ss;
122
16.3k
123
16.3k
            resolved = 1;
124
16.3k
125
16.3k
            MVM_spesh_use_facts(tc, g, obj_facts);
126
16.3k
            obj_facts->usages--;
127
16.3k
        }
128
17.0k
    }
129
27.9k
130
27.9k
    /* If not, add space to cache a single type/method pair, to save hash
131
27.9k
     * lookups in the (common) monomorphic case, and rewrite to caching
132
27.9k
     * version of the instruction. */
133
27.9k
    if (!resolved) {
134
11.6k
        MVMSpeshOperand *orig_o = ins->operands;
135
11.6k
        ins->info = MVM_op_get_op(MVM_OP_sp_findmeth);
136
11.6k
        ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
137
11.6k
        memcpy(ins->operands, orig_o, 3 * sizeof(MVMSpeshOperand));
138
11.6k
        ins->operands[3].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, NULL);
139
11.6k
        MVM_spesh_add_spesh_slot(tc, g, NULL);
140
11.6k
    }
141
27.9k
}
142
143
/* Sees if we can resolve an istype at compile time. */
144
1.53k
static void optimize_istype(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
145
1.53k
    MVMSpeshFacts *obj_facts  = MVM_spesh_get_facts(tc, g, ins->operands[1]);
146
1.53k
    MVMSpeshFacts *type_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
147
1.53k
    MVMSpeshFacts *result_facts;
148
1.53k
149
1.53k
    if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE &&
150
1.44k
         obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
151
368
        MVMint32 result;
152
368
        if (!MVM_6model_try_cache_type_check(tc, obj_facts->type, type_facts->type, &result))
153
107
            return;
154
261
        ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
155
261
        result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
156
261
        result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
157
261
        ins->operands[1].lit_i16 = result;
158
261
        result_facts->value.i  = result;
159
261
160
261
        obj_facts->usages--;
161
261
        type_facts->usages--;
162
261
        MVM_spesh_use_facts(tc, g, obj_facts);
163
261
        MVM_spesh_use_facts(tc, g, type_facts);
164
261
    }
165
1.53k
}
166
167
1.62k
static void optimize_is_reprid(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
168
1.62k
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
169
1.62k
    MVMuint32 wanted_repr_id;
170
1.62k
    MVMuint64 result_value;
171
1.62k
172
1.62k
    if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)) {
173
783
        return;
174
783
    }
175
1.62k
176
840
    switch (ins->info->opcode) {
177
273
        case MVM_OP_islist: wanted_repr_id = MVM_REPR_ID_VMArray; break;
178
441
        case MVM_OP_ishash: wanted_repr_id = MVM_REPR_ID_MVMHash; break;
179
0
        case MVM_OP_isint:  wanted_repr_id = MVM_REPR_ID_P6int; break;
180
0
        case MVM_OP_isnum:  wanted_repr_id = MVM_REPR_ID_P6num; break;
181
126
        case MVM_OP_isstr:  wanted_repr_id = MVM_REPR_ID_P6str; break;
182
0
        default:            return;
183
840
    }
184
840
185
840
    MVM_spesh_use_facts(tc, g, obj_facts);
186
840
187
840
    result_value = REPR(obj_facts->type)->ID == wanted_repr_id;
188
840
189
840
    if (result_value == 0) {
190
553
        MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
191
553
        ins->info = MVM_op_get_op(MVM_OP_const_i64_16);
192
553
        ins->operands[1].lit_i16 = 0;
193
553
        result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
194
553
        result_facts->value.i = 0;
195
287
    } else {
196
287
        ins->info = MVM_op_get_op(MVM_OP_isnonnull);
197
287
    }
198
840
}
199
200
882
static void optimize_gethow(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
201
882
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
202
882
    MVMObject       *how_obj = NULL;
203
882
    if (obj_facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE))
204
837
        how_obj = MVM_spesh_try_get_how(tc, obj_facts->type);
205
882
    /* There may be other valid ways to get the facts (known value?) */
206
882
    if (how_obj) {
207
764
        MVMSpeshFacts *how_facts;
208
764
        /* Transform gethow lookup to spesh slot lookup */
209
764
        MVMint16 spesh_slot = MVM_spesh_add_spesh_slot_try_reuse(tc, g, (MVMCollectable*)how_obj);
210
764
        MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
211
764
        ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
212
764
        ins->operands[1].lit_i16 = spesh_slot;
213
764
        /* Store facts about the value in the write operand */
214
764
        how_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
215
764
        how_facts->flags  |= (MVM_SPESH_FACT_KNOWN_VALUE | MVM_SPESH_FACT_KNOWN_TYPE);
216
764
        how_facts->value.o = how_obj;
217
764
        how_facts->type    = STABLE(how_obj)->WHAT;
218
764
    }
219
882
}
220
221
222
/* Sees if we can resolve an isconcrete at compile time. */
223
5.35k
static void optimize_isconcrete(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
224
5.35k
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
225
5.35k
    if (obj_facts->flags & (MVM_SPESH_FACT_CONCRETE | MVM_SPESH_FACT_TYPEOBJ)) {
226
3.30k
        MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
227
3.30k
        ins->info                   = MVM_op_get_op(MVM_OP_const_i64_16);
228
3.30k
        result_facts->flags        |= MVM_SPESH_FACT_KNOWN_VALUE;
229
3.30k
        result_facts->value.i       = obj_facts->flags & MVM_SPESH_FACT_CONCRETE ? 1 : 0;
230
3.30k
        ins->operands[1].lit_i16    = result_facts->value.i;
231
3.30k
232
3.30k
        MVM_spesh_use_facts(tc, g, obj_facts);
233
3.30k
        MVM_spesh_facts_depend(tc, g, result_facts, obj_facts);
234
3.30k
235
3.30k
        obj_facts->usages--;
236
3.30k
    }
237
5.35k
}
238
239
0
static void optimize_exception_ops(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
240
0
    MVMuint16 op = ins->info->opcode;
241
0
242
0
    if (op == MVM_OP_newexception) {
243
0
        MVMSpeshOperand target   = ins->operands[0];
244
0
        MVMObject      *type     = tc->instance->boot_types.BOOTException;
245
0
        MVMSTable      *st       = STABLE(type);
246
0
        ins->info                = MVM_op_get_op(MVM_OP_sp_fastcreate);
247
0
        ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
248
0
        ins->operands[0]         = target;
249
0
        ins->operands[1].lit_i16 = st->size;
250
0
        ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)st);
251
0
    } else {
252
0
        /*
253
0
        MVMSpeshFacts *target_facts;
254
0
        */
255
0
256
0
        /* XXX This currently still causes problems. */
257
0
        return;
258
0
259
0
        /*
260
0
        switch (op) {
261
0
        case MVM_OP_bindexmessage:
262
0
        case MVM_OP_bindexpayload: {
263
0
            MVMSpeshOperand target   = ins->operands[0];
264
0
            MVMSpeshOperand value    = ins->operands[1];
265
0
            target_facts             = MVM_spesh_get_facts(tc, g, target);
266
0
267
0
            if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
268
0
                || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
269
0
                break;
270
0
271
0
            ins->info                = MVM_op_get_op(op == MVM_OP_bindexmessage ? MVM_OP_sp_bind_s : MVM_OP_sp_bind_o);
272
0
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
273
0
            ins->operands[0]         = target;
274
0
            ins->operands[1].lit_i16 = op == MVM_OP_bindexmessage ? offsetof(MVMException, body.message)
275
0
                                                                  : offsetof(MVMException, body.payload);
276
0
            ins->operands[2]         = value;
277
0
            break;
278
0
        }
279
0
        case MVM_OP_bindexcategory: {
280
0
            MVMSpeshOperand target   = ins->operands[0];
281
0
            MVMSpeshOperand category = ins->operands[1];
282
0
            target_facts             = MVM_spesh_get_facts(tc, g, target);
283
0
284
0
            if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
285
0
                || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
286
0
                break;
287
0
288
0
            ins->info                = MVM_op_get_op(MVM_OP_sp_bind_i32);
289
0
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
290
0
            ins->operands[0]         = target;
291
0
            ins->operands[1].lit_i16 = offsetof(MVMException, body.category);
292
0
            ins->operands[2]         = category;
293
0
            break;
294
0
        }
295
0
        case MVM_OP_getexmessage:
296
0
        case MVM_OP_getexpayload: {
297
0
            MVMSpeshOperand destination = ins->operands[0];
298
0
            MVMSpeshOperand target      = ins->operands[1];
299
0
            target_facts                = MVM_spesh_get_facts(tc, g, target);
300
0
301
0
            if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
302
0
                || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
303
0
                break;
304
0
305
0
            ins->info                = MVM_op_get_op(op == MVM_OP_getexmessage ? MVM_OP_sp_get_s : MVM_OP_sp_get_o);
306
0
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
307
0
            ins->operands[0]         = destination;
308
0
            ins->operands[1]         = target;
309
0
            ins->operands[2].lit_i16 = op == MVM_OP_getexmessage ? offsetof(MVMException, body.message)
310
0
                                                                 : offsetof(MVMException, body.payload);
311
0
            break;
312
0
        }
313
0
        case MVM_OP_getexcategory: {
314
0
            MVMSpeshOperand destination = ins->operands[0];
315
0
            MVMSpeshOperand target      = ins->operands[1];
316
0
            target_facts                = MVM_spesh_get_facts(tc, g, target);
317
0
318
0
            if (!(target_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE)
319
0
                || !(REPR(target_facts->type)->ID == MVM_REPR_ID_MVMException))
320
0
                break;
321
0
322
0
            ins->info                = MVM_op_get_op(MVM_OP_sp_get_i32);
323
0
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
324
0
            ins->operands[0]         = destination;
325
0
            ins->operands[1]         = target;
326
0
            ins->operands[2].lit_i16 = offsetof(MVMException, body.category);
327
0
            break;
328
0
        }
329
0
        }
330
0
        */
331
0
    }
332
0
}
333
334
/* iffy ops that operate on a known value register can turn into goto
335
 * or be dropped. */
336
55.9k
static void optimize_iffy(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins, MVMSpeshBB *bb) {
337
55.9k
    MVMSpeshFacts *flag_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
338
55.9k
    MVMuint8 negated_op;
339
55.9k
    MVMuint8 truthvalue;
340
55.9k
341
55.9k
    switch (ins->info->opcode) {
342
18.3k
        case MVM_OP_if_i:
343
18.3k
        case MVM_OP_if_s:
344
18.3k
        case MVM_OP_if_n:
345
18.3k
        case MVM_OP_if_o:
346
18.3k
        case MVM_OP_ifnonnull:
347
18.3k
            negated_op = 0;
348
18.3k
            break;
349
37.5k
        case MVM_OP_unless_i:
350
37.5k
        case MVM_OP_unless_s:
351
37.5k
        case MVM_OP_unless_n:
352
37.5k
        case MVM_OP_unless_o:
353
37.5k
            negated_op = 1;
354
37.5k
            break;
355
0
        default:
356
0
            return;
357
55.9k
    }
358
55.9k
359
55.9k
    if (flag_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
360
2.85k
        switch (ins->info->opcode) {
361
2.85k
            case MVM_OP_if_i:
362
2.85k
            case MVM_OP_unless_i:
363
2.85k
                truthvalue = flag_facts->value.i;
364
2.85k
                break;
365
0
            case MVM_OP_if_o:
366
0
            case MVM_OP_unless_o: {
367
0
                MVMObject *objval = flag_facts->value.o;
368
0
                MVMBoolificationSpec *bs = objval->st->boolification_spec;
369
0
                MVMRegister resultreg;
370
0
                switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
371
0
                    case MVM_BOOL_MODE_UNBOX_INT:
372
0
                    case MVM_BOOL_MODE_UNBOX_NUM:
373
0
                    case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY:
374
0
                    case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY_OR_ZERO:
375
0
                    case MVM_BOOL_MODE_BIGINT:
376
0
                    case MVM_BOOL_MODE_ITER:
377
0
                    case MVM_BOOL_MODE_HAS_ELEMS:
378
0
                    case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
379
0
                        MVM_coerce_istrue(tc, objval, &resultreg, NULL, NULL, 0);
380
0
                        truthvalue = resultreg.i64;
381
0
                        break;
382
0
                    case MVM_BOOL_MODE_CALL_METHOD:
383
0
                    default:
384
0
                        return;
385
0
                }
386
0
                break;
387
0
            }
388
0
            case MVM_OP_if_n:
389
0
            case MVM_OP_unless_n:
390
0
                truthvalue = flag_facts->value.n != 0.0;
391
0
                break;
392
0
            default:
393
0
                return;
394
2.85k
        }
395
2.85k
396
2.85k
        MVM_spesh_use_facts(tc, g, flag_facts);
397
2.85k
        flag_facts->usages--;
398
2.85k
399
1.59k
        truthvalue = truthvalue ? 1 : 0;
400
2.85k
        if (truthvalue != negated_op) {
401
1.66k
            /* this conditional can be turned into an unconditional jump */
402
1.66k
            ins->info = MVM_op_get_op(MVM_OP_goto);
403
1.66k
            ins->operands[0] = ins->operands[1];
404
1.66k
405
1.66k
            /* since we have an unconditional jump now, we can remove the successor
406
1.66k
             * that's in the linear_next */
407
1.66k
            MVM_spesh_manipulate_remove_successor(tc, bb, bb->linear_next);
408
1.19k
        } else {
409
1.19k
            /* this conditional can be dropped completely */
410
1.19k
            MVM_spesh_manipulate_remove_successor(tc, bb, ins->operands[1].ins_bb);
411
1.19k
            MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
412
1.19k
        }
413
2.85k
        return;
414
2.85k
    }
415
55.9k
    /* Sometimes our code-gen ends up boxing an integer and immediately
416
55.9k
     * calling if_o or unless_o on it. If we if_i/unless_i/... instead,
417
55.9k
     * we can get rid of the unboxing and perhaps the boxing as well. */
418
53.0k
    if ((ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o)
419
7.33k
            && flag_facts->flags & MVM_SPESH_FACT_KNOWN_BOX_SRC && flag_facts->writer) {
420
16
        /* We may have to go through several layers of set instructions to find
421
16
         * the proper writer. */
422
16
        MVMSpeshIns *cur = flag_facts->writer;
423
37
        while (cur && cur->info->opcode == MVM_OP_set) {
424
21
            cur = MVM_spesh_get_facts(tc, g, cur->operands[1])->writer;
425
21
        }
426
16
427
16
        if (cur) {
428
16
            MVMSpeshIns *safety_cur;
429
16
            MVMuint8 orig_operand_type = cur->info->operands[1] & MVM_operand_type_mask;
430
16
            MVMuint8 succ = 0;
431
16
432
16
            /* now we have to be extra careful. any operation that writes to
433
16
             * our "unboxed flag" register (in any register version) will be
434
16
             * trouble. Also, we'd have to take more care with PHI nodes,
435
16
             * which we'll just consider immediate failure for now. */
436
16
437
16
            safety_cur = ins;
438
141
            while (safety_cur) {
439
131
                if (safety_cur == cur) {
440
1
                    /* If we've made it to here without finding anything
441
1
                     * dangerous, we can consider this optimization
442
1
                     * a winner. */
443
1
                    break;
444
1
                }
445
130
                if (safety_cur->info->opcode == MVM_SSA_PHI) {
446
5
                    /* Oh dear god in heaven! A PHI! */
447
5
                    safety_cur = NULL;
448
5
                    break;
449
5
                }
450
125
                if (((safety_cur->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg)
451
91
                    && (safety_cur->operands[0].reg.orig == cur->operands[1].reg.orig)) {
452
0
                    /* Someone's clobbering our register between the boxing and
453
0
                     * our attempt to unbox it. we shall give up.
454
0
                     * Maybe in the future we can be clever/sneaky and use
455
0
                     * some other register for bridging the gap? */
456
0
                    safety_cur = NULL;
457
0
                    break;
458
0
                }
459
125
                safety_cur = safety_cur->prev;
460
125
            }
461
16
462
16
            if (safety_cur) {
463
1
                switch (orig_operand_type) {
464
1
                    case MVM_operand_int64:
465
1
                        ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i);
466
1
                        succ = 1;
467
1
                        break;
468
0
                    case MVM_operand_num64:
469
0
                        ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_n : MVM_OP_if_n);
470
0
                        succ = 1;
471
0
                        break;
472
0
                    case MVM_operand_str:
473
0
                        ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_s : MVM_OP_if_s);
474
0
                        succ = 1;
475
0
                        break;
476
1
                }
477
1
478
1
                if (succ) {
479
1
                    ins->operands[0] = cur->operands[1];
480
1
                    flag_facts->usages--;
481
1
                    MVM_spesh_get_and_use_facts(tc, g, cur->operands[1])->usages++;
482
1
                    optimize_iffy(tc, g, ins, bb);
483
1
                    return;
484
1
                }
485
1
            }
486
16
        }
487
16
    }
488
53.0k
    if (flag_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && flag_facts->type) {
489
1.02k
        if (ins->info->opcode == MVM_OP_if_o || ins->info->opcode == MVM_OP_unless_o) {
490
1.02k
            MVMObject *type            = flag_facts->type;
491
1.02k
            MVMBoolificationSpec *bs   = type->st->boolification_spec;
492
1.02k
            MVMSpeshOperand  temp      = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
493
1.02k
494
1.02k
            MVMSpeshIns     *new_ins   = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
495
1.02k
            MVMSpeshOperand *operands  = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
496
1.02k
497
1.02k
            MVMuint8 guaranteed_concrete = flag_facts->flags & MVM_SPESH_FACT_CONCRETE;
498
1.02k
499
1.02k
            switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
500
0
                case MVM_BOOL_MODE_ITER:
501
0
                    if (!guaranteed_concrete)
502
0
                        return;
503
0
                    if (flag_facts->flags & MVM_SPESH_FACT_ARRAY_ITER) {
504
0
                        new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_arr);
505
0
                    } else if (flag_facts->flags & MVM_SPESH_FACT_HASH_ITER) {
506
0
                        new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter_hash);
507
0
                    } else {
508
0
                        new_ins->info = MVM_op_get_op(MVM_OP_sp_boolify_iter);
509
0
                    }
510
0
                    break;
511
757
                case MVM_BOOL_MODE_UNBOX_INT:
512
757
                    if (!guaranteed_concrete)
513
0
                        return;
514
757
                    new_ins->info = MVM_op_get_op(MVM_OP_unbox_i);
515
757
                    break;
516
757
                /* we need to change the register type for our temporary register for this.
517
757
                case MVM_BOOL_MODE_UNBOX_NUM:
518
757
                    new_ins->info = MVM_op_get_op(MVM_OP_unbox_i);
519
757
                    break;
520
757
                    */
521
0
                case MVM_BOOL_MODE_BIGINT:
522
0
                    if (!guaranteed_concrete)
523
0
                        return;
524
0
                    new_ins->info = MVM_op_get_op(MVM_OP_bool_I);
525
0
                    break;
526
33
                case MVM_BOOL_MODE_HAS_ELEMS:
527
33
                    if (!guaranteed_concrete)
528
0
                        return;
529
33
                    new_ins->info = MVM_op_get_op(MVM_OP_elems);
530
33
                    break;
531
124
                case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
532
124
                    new_ins->info = MVM_op_get_op(MVM_OP_isconcrete);
533
124
                    break;
534
112
                default:
535
112
                    return;
536
1.02k
            }
537
1.02k
538
914
            operands[0] = temp;
539
914
            operands[1] = ins->operands[0];
540
914
            new_ins->operands = operands;
541
914
542
725
            ins->info = MVM_op_get_op(negated_op ? MVM_OP_unless_i : MVM_OP_if_i);
543
914
            ins->operands[0] = temp;
544
914
545
914
            MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, new_ins);
546
914
547
914
            MVM_spesh_get_facts(tc, g, temp)->usages++;
548
914
549
914
            MVM_spesh_use_facts(tc, g, flag_facts);
550
914
551
914
            MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
552
0
        } else {
553
0
            return;
554
0
        }
555
52.0k
    } else {
556
52.0k
        return;
557
52.0k
    }
558
53.0k
559
53.0k
}
560
561
/* objprimspec can be done at spesh-time if we know the type of something.
562
 * Another thing is, that if we rely on the type being known, we'll be assured
563
 * we'll have a guard that promises the object in question to be non-null. */
564
24
static void optimize_objprimspec(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
565
24
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
566
24
567
24
    if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
568
21
        MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
569
21
        ins->info                   = MVM_op_get_op(MVM_OP_const_i64_16);
570
21
        result_facts->flags        |= MVM_SPESH_FACT_KNOWN_VALUE;
571
21
        result_facts->value.i       = REPR(obj_facts->type)->get_storage_spec(tc, STABLE(obj_facts->type))->boxed_primitive;
572
21
        ins->operands[1].lit_i16    = result_facts->value.i;
573
21
574
21
        MVM_spesh_use_facts(tc, g, obj_facts);
575
21
        obj_facts->usages--;
576
21
    }
577
24
}
578
579
/* Optimizes a hllize instruction away if the type is known and already in the
580
 * right HLL, by turning it into a set. */
581
0
static void optimize_hllize(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
582
0
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
583
0
    if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
584
0
        if (STABLE(obj_facts->type)->hll_owner == g->sf->body.cu->body.hll_config) {
585
0
            ins->info = MVM_op_get_op(MVM_OP_set);
586
0
587
0
            MVM_spesh_use_facts(tc, g, obj_facts);
588
0
589
0
            copy_facts(tc, g, ins->operands[0], ins->operands[1]);
590
0
        }
591
0
    }
592
0
}
593
594
/* Turns a decont into a set, if we know it's not needed. Also make sure we
595
 * propagate any needed information. */
596
123k
static void optimize_decont(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
597
123k
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
598
123k
    if (obj_facts->flags & (MVM_SPESH_FACT_DECONTED | MVM_SPESH_FACT_TYPEOBJ)) {
599
86.9k
        ins->info = MVM_op_get_op(MVM_OP_set);
600
86.9k
601
86.9k
        MVM_spesh_use_facts(tc, g, obj_facts);
602
86.9k
603
86.9k
        copy_facts(tc, g, ins->operands[0], ins->operands[1]);
604
86.9k
    }
605
36.3k
    else {
606
36.3k
        if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
607
875
            MVMSTable *stable = STABLE(obj_facts->type);
608
875
            MVMContainerSpec const *contspec = stable->container_spec;
609
875
            if (contspec && contspec->fetch_never_invokes && contspec->spesh) {
610
0
                contspec->spesh(tc, stable, g, bb, ins);
611
0
                MVM_spesh_use_facts(tc, g, obj_facts);
612
0
            }
613
875
        }
614
36.3k
615
36.3k
        if (!MVM_spesh_facts_decont_blocked_by_alias(tc, g, ins)) {
616
21.0k
            MVMSpeshFacts *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
617
21.0k
            int set_facts = 0;
618
21.0k
            if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
619
0
                res_facts->type   = obj_facts->decont_type;
620
0
                res_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
621
0
                set_facts = 1;
622
0
            }
623
21.0k
            if (obj_facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) {
624
0
                res_facts->flags |= MVM_SPESH_FACT_CONCRETE;
625
0
                set_facts = 1;
626
0
            }
627
21.0k
            else if (obj_facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) {
628
0
                res_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
629
0
                set_facts = 1;
630
0
            }
631
21.0k
            if (set_facts)
632
0
                MVM_spesh_facts_depend(tc, g, res_facts, obj_facts);
633
21.0k
        }
634
36.3k
    }
635
123k
}
636
637
/* Checks like iscont, iscont_[ins] and isrwcont can be done at spesh time */
638
0
static void optimize_container_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
639
0
    if (ins->info->opcode == MVM_OP_isrwcont) {
640
0
        MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
641
0
642
0
        if (facts->flags & MVM_SPESH_FACT_RW_CONT) {
643
0
            MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
644
0
            ins->info                   = MVM_op_get_op(MVM_OP_const_i64_16);
645
0
            result_facts->flags        |= MVM_SPESH_FACT_KNOWN_VALUE;
646
0
            result_facts->value.i       = 1;
647
0
            ins->operands[1].lit_i16    = 1;
648
0
649
0
            MVM_spesh_use_facts(tc, g, facts);
650
0
            facts->usages--;
651
0
        }
652
0
    }
653
0
}
654
655
/* Optimize away assertparamcheck if we know it will pass. */
656
0
static void optimize_assertparamcheck(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
657
0
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
658
0
    if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE && facts->value.i) {
659
0
        MVM_spesh_use_facts(tc, g, facts);
660
0
        facts->usages--;
661
0
        MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
662
0
    }
663
0
}
664
665
475
static void optimize_can_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
666
475
    /* This used to cause problems, Spesh: failed to fix up handlers (-1, 110, 110) */
667
475
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
668
475
    MVMString *method_name;
669
475
    MVMint64 can_result;
670
475
671
475
    if (ins->info->opcode == MVM_OP_can_s) {
672
475
        MVMSpeshFacts *name_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
673
475
        if (!(name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)) {
674
160
            return;
675
160
        }
676
315
        method_name = name_facts->value.s;
677
315
678
315
        name_facts->usages--;
679
315
        ins->info = MVM_op_get_op(MVM_OP_can);
680
315
        ins->operands[2].lit_str_idx = name_facts->writer->operands[1].lit_str_idx;
681
0
    } else {
682
0
        method_name = MVM_spesh_get_string(tc, g, ins->operands[2]);
683
0
    }
684
475
685
315
    if (!(obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) || !obj_facts->type) {
686
68
        return;
687
68
    }
688
315
689
247
    if (MVM_is_null(tc, obj_facts->type))
690
87
        can_result = 0; /* VMNull can't have any methods. */
691
247
    else
692
160
        can_result = MVM_spesh_try_can_method(tc, obj_facts->type, method_name);
693
247
694
247
    if (can_result == -1) {
695
0
        return;
696
247
    } else {
697
247
        MVMSpeshFacts *result_facts;
698
247
699
247
        if (ins->info->opcode == MVM_OP_can_s)
700
0
            MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
701
247
702
247
        result_facts                = MVM_spesh_get_facts(tc, g, ins->operands[0]);
703
247
        ins->info                   = MVM_op_get_op(MVM_OP_const_i64_16);
704
247
        result_facts->flags        |= MVM_SPESH_FACT_KNOWN_VALUE;
705
247
        ins->operands[1].lit_i16    = can_result;
706
247
        result_facts->value.i       = can_result;
707
247
708
247
        obj_facts->usages--;
709
247
        MVM_spesh_use_facts(tc, g, obj_facts);
710
247
    }
711
247
}
712
713
/* If we have a const_i and a coerce_in, we can emit a const_n instead. */
714
6.58k
static void optimize_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
715
6.58k
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
716
6.58k
717
6.58k
    if (facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
718
1.45k
        MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
719
1.45k
        MVMnum64 result = facts->value.i;
720
1.45k
721
1.45k
        MVM_spesh_use_facts(tc, g, facts);
722
1.45k
        facts->usages--;
723
1.45k
724
1.45k
        ins->info = MVM_op_get_op(MVM_OP_const_n64);
725
1.45k
        ins->operands[1].lit_n64 = result;
726
1.45k
727
1.45k
        result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
728
1.45k
        result_facts->value.n = result;
729
1.45k
    }
730
6.58k
}
731
732
/* If we know the type of a significant operand, we might try to specialize by
733
 * representation. */
734
static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
735
130k
                             MVMSpeshIns *ins, MVMint32 type_operand) {
736
130k
    /* Immediately mark guards as used, as the JIT would like to devirtualize
737
130k
     * repr ops later and we don't want guards to be thrown out before that */
738
130k
    MVMSpeshFacts *facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[type_operand]);
739
130k
    if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && facts->type)
740
90.8k
        if (REPR(facts->type)->spesh) {
741
76.7k
            REPR(facts->type)->spesh(tc, STABLE(facts->type), g, bb, ins);
742
76.7k
            MVM_spesh_use_facts(tc, g, facts);
743
76.7k
        }
744
130k
}
745
746
/* smrt_strify and smrt_numify can turn into unboxes, but at least
747
 * for smrt_numify it's "complicated". Also, later when we know how
748
 * to put new invocations into spesh'd code, we could make direct
749
 * invoke calls to the .Str and .Num methods.
750
 */
751
19.8k
static void optimize_smart_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
752
19.8k
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
753
19.8k
754
19.8k
    MVMuint16 is_strify = ins->info->opcode == MVM_OP_smrt_strify;
755
19.8k
756
19.8k
    if (facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE) && facts->type) {
757
9.52k
        const MVMStorageSpec *ss;
758
9.52k
        MVMint64 can_result;
759
9.52k
760
9.52k
        ss = REPR(facts->type)->get_storage_spec(tc, STABLE(facts->type));
761
9.52k
762
9.52k
        if (is_strify && ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) {
763
2.72k
            MVM_spesh_use_facts(tc, g, facts);
764
2.72k
765
2.72k
            ins->info = MVM_op_get_op(MVM_OP_unbox_s);
766
2.72k
            /* And now that we have a repr op, we can try to optimize
767
2.72k
             * it even further. */
768
2.72k
            optimize_repr_op(tc, g, bb, ins, 1);
769
2.72k
770
2.72k
            return;
771
2.72k
        }
772
6.79k
        can_result = MVM_spesh_try_can_method(tc, facts->type,
773
5.77k
                is_strify ? tc->instance->str_consts.Str : tc->instance->str_consts.Num);
774
6.79k
775
6.79k
        if (can_result == -1) {
776
6.28k
            /* Couldn't safely figure out if the type has a Str method or not. */
777
6.28k
            return;
778
513
        } else if (can_result == 0) {
779
462
            MVM_spesh_use_facts(tc, g, facts);
780
462
            /* We can't .Str this object, so we'll duplicate the "guessing"
781
462
             * logic from smrt_strify here to remove indirection. */
782
462
            if (is_strify && REPR(facts->type)->ID == MVM_REPR_ID_MVMException) {
783
0
                MVMSpeshOperand *operands  = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 3);
784
0
                MVMSpeshOperand *old_opers = ins->operands;
785
0
786
0
                ins->info = MVM_op_get_op(MVM_OP_sp_get_s);
787
0
788
0
                ins->operands = operands;
789
0
790
0
                operands[0] = old_opers[0];
791
0
                operands[1] = old_opers[1];
792
0
                operands[2].lit_i16 = offsetof( MVMException, body.message );
793
462
            } else if(ss->can_box & (MVM_STORAGE_SPEC_CAN_BOX_NUM | MVM_STORAGE_SPEC_CAN_BOX_INT)) {
794
0
                MVMuint16 register_type =
795
0
                    ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT ? MVM_reg_int64 : MVM_reg_num64;
796
0
797
0
                MVMSpeshIns     *new_ins   = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
798
0
                MVMSpeshOperand *operands  = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
799
0
                MVMSpeshOperand  temp      = MVM_spesh_manipulate_get_temp_reg(tc, g, register_type);
800
0
                MVMSpeshOperand  orig_dst  = ins->operands[0];
801
0
802
0
                ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_unbox_n : MVM_OP_unbox_i);
803
0
                ins->operands[0] = temp;
804
0
805
0
                if (is_strify)
806
0
                    new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_coerce_ns : MVM_OP_coerce_is);
807
0
                else
808
0
                    new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_set : MVM_OP_coerce_in);
809
0
                new_ins->operands = operands;
810
0
                operands[0] = orig_dst;
811
0
                operands[1] = temp;
812
0
813
0
                /* We can directly "eliminate" a set instruction here. */
814
0
                if (new_ins->info->opcode != MVM_OP_set) {
815
0
                    MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
816
0
817
0
                    MVM_spesh_get_facts(tc, g, temp)->usages++;
818
0
                } else {
819
0
                    ins->operands[0] = orig_dst;
820
0
                }
821
0
822
0
                /* Finally, let's try to optimize the unboxing REPROp. */
823
0
                optimize_repr_op(tc, g, bb, ins, 1);
824
0
825
0
                /* And as a last clean-up step, we release the temporary register. */
826
0
                MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
827
0
828
0
                return;
829
462
            } else if (!is_strify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray ||
830
390
                                     (REPR(facts->type)->ID == MVM_REPR_ID_MVMHash))) {
831
390
                /* A smrt_numify on an array or hash can be replaced by an
832
390
                 * elems operation, that can then be optimized by our
833
390
                 * versatile and dilligent friend optimize_repr_op. */
834
390
835
390
                MVMSpeshIns     *new_ins   = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
836
390
                MVMSpeshOperand *operands  = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
837
390
                MVMSpeshOperand  temp      = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
838
390
                MVMSpeshOperand  orig_dst  = ins->operands[0];
839
390
840
390
                ins->info = MVM_op_get_op(MVM_OP_elems);
841
390
                ins->operands[0] = temp;
842
390
843
390
                new_ins->info = MVM_op_get_op(MVM_OP_coerce_in);
844
390
                new_ins->operands = operands;
845
390
                operands[0] = orig_dst;
846
390
                operands[1] = temp;
847
390
848
390
                MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
849
390
850
390
                optimize_repr_op(tc, g, bb, ins, 1);
851
390
852
390
                MVM_spesh_get_facts(tc, g, temp)->usages++;
853
390
                MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
854
390
                return;
855
390
            }
856
51
        } else if (can_result == 1) {
857
51
            /* When we know how to generate additional callsites, we could
858
51
             * make an invocation to .Str or .Num here and perhaps have it
859
51
             * in-lined. */
860
51
        }
861
6.79k
    }
862
19.8k
}
863
864
/* boolification has a major indirection, which we can spesh away.
865
 * Afterwards, we may be able to spesh even further, so we defer
866
 * to other optimization methods. */
867
861
static void optimize_istrue_isfalse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
868
861
    MVMuint8 negated_op;
869
861
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
870
861
    if (ins->info->opcode == MVM_OP_istrue) {
871
491
        negated_op = 0;
872
370
    } else if (ins->info->opcode == MVM_OP_isfalse) {
873
370
        negated_op = 1;
874
0
    } else {
875
0
        return;
876
0
    }
877
861
878
861
    /* Let's try to figure out the boolification spec. */
879
861
    if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
880
331
        MVMBoolificationSpec *bs = STABLE(facts->type)->boolification_spec;
881
331
        MVMSpeshOperand  orig    = ins->operands[0];
882
331
        MVMSpeshOperand  temp;
883
331
884
331
        if (negated_op)
885
197
           temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64);
886
331
887
244
        switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
888
176
            case MVM_BOOL_MODE_UNBOX_INT:
889
176
                /* This optimization can only handle values known to be concrete. */
890
176
                if (!(facts->flags & MVM_SPESH_FACT_CONCRETE)) {
891
0
                    return;
892
0
                }
893
176
                /* We can just unbox the int and pretend it's a bool. */
894
176
                ins->info = MVM_op_get_op(MVM_OP_unbox_i);
895
176
                if (negated_op)
896
175
                    ins->operands[0] = temp;
897
176
                /* And then we might be able to optimize this even further. */
898
176
                optimize_repr_op(tc, g, bb, ins, 1);
899
176
                break;
900
96
            case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
901
96
                /* This is the same as isconcrete. */
902
96
                ins->info = MVM_op_get_op(MVM_OP_isconcrete);
903
96
                if (negated_op)
904
9
                    ins->operands[0] = temp;
905
96
                /* And now defer another bit of optimization */
906
96
                optimize_isconcrete(tc, g, ins);
907
96
                break;
908
176
            /* TODO implement MODE_UNBOX_NUM and the string ones */
909
59
            default:
910
59
                return;
911
331
        }
912
331
        /* Now we can take care of the negation. */
913
272
        if (negated_op) {
914
184
            MVMSpeshIns     *new_ins   = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
915
184
            MVMSpeshOperand *operands  = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);
916
184
            MVMSpeshFacts   *res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
917
184
918
184
            /* This is a bit naughty with regards to the SSA form, but
919
184
             * we'll hopefully get away with it until we have a proper
920
184
             * way to get new registers crammed in the middle of things */
921
184
            new_ins->info = MVM_op_get_op(MVM_OP_not_i);
922
184
            new_ins->operands = operands;
923
184
            operands[0] = orig;
924
184
            operands[1] = temp;
925
184
            MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
926
184
927
184
            MVM_spesh_get_facts(tc, g, temp)->usages++;
928
184
929
184
            /* If there's a known value, update the fact. */
930
184
            if (res_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)
931
9
                res_facts->value.i = !res_facts->value.i;
932
184
933
184
            MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
934
184
        }
935
272
936
272
        MVM_spesh_use_facts(tc, g, facts);
937
272
    }
938
861
}
939
940
/* Checks if we have specialized on the invocant - useful to know for some
941
 * optimizations. */
942
15.6k
static MVMint32 specialized_on_invocant(MVMThreadContext *tc, MVMSpeshGraph *g) {
943
15.6k
    MVMint32 i;
944
15.6k
    for (i = 0; i < g->num_arg_guards; i++)
945
15.6k
        if (g->arg_guards[i].slot == 0)
946
15.6k
            return 1;
947
0
    return 0;
948
15.6k
}
949
950
/* Optimizes away a lexical lookup when we know the value won't change from
951
 * the logged one. */
952
static void optimize_getlex_known(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
953
17.5k
                                  MVMSpeshIns *ins) {
954
17.5k
    /* Ensure we have a log instruction following this one. */
955
17.5k
    if (ins->next && ins->next->info->opcode == MVM_OP_sp_log) {
956
17.5k
        /* Locate logged object. */
957
17.5k
        MVMuint16       log_slot = ins->next->operands[1].lit_i16 * MVM_SPESH_LOG_RUNS;
958
17.5k
        MVMCollectable *log_obj  = g->log_slots[log_slot];
959
17.5k
        if (log_obj) {
960
11.4k
            MVMSpeshFacts *facts;
961
11.4k
962
11.4k
            /* Place in a spesh slot. */
963
11.4k
            MVMuint16 ss = MVM_spesh_add_spesh_slot_try_reuse(tc, g, log_obj);
964
11.4k
965
11.4k
            /* Delete logging instruction. */
966
11.4k
            MVM_spesh_manipulate_delete_ins(tc, g, bb, ins->next);
967
11.4k
968
11.4k
            /* Transform lookup instruction into spesh slot read. */
969
11.4k
            MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
970
11.4k
            ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
971
11.4k
            ins->operands[1].lit_i16 = ss;
972
11.4k
973
11.4k
            /* Set up facts. */
974
11.4k
            facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
975
11.4k
            facts->flags  |= MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_KNOWN_VALUE;
976
11.4k
            facts->type    = STABLE(log_obj)->WHAT;
977
11.4k
            facts->value.o = (MVMObject *)log_obj;
978
11.4k
            if (IS_CONCRETE(log_obj)) {
979
114
                facts->flags |= MVM_SPESH_FACT_CONCRETE;
980
114
                if (!STABLE(log_obj)->container_spec)
981
114
                    facts->flags |= MVM_SPESH_FACT_DECONTED;
982
114
            }
983
11.3k
            else {
984
11.3k
                facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
985
11.3k
            }
986
11.4k
        }
987
17.5k
    }
988
17.5k
}
989
990
/* Determines if there's a matching spesh candidate for a callee and a given
991
 * set of argument info. */
992
14.8k
static MVMint32 try_find_spesh_candidate(MVMThreadContext *tc, MVMCode *code, MVMSpeshCallInfo *arg_info) {
993
14.8k
    MVMStaticFrameBody *sfb = &(code->body.sf->body);
994
14.8k
    MVMint32 num_spesh      = sfb->num_spesh_candidates;
995
14.8k
    MVMint32 i, j;
996
27.1k
    for (i = 0; i < num_spesh; i++) {
997
19.3k
        MVMSpeshCandidate *cand = &sfb->spesh_candidates[i];
998
19.3k
        if (cand->cs == arg_info->cs) {
999
14.9k
            /* Matching callsite, now see if we have enough information to
1000
14.9k
             * test the guards. */
1001
14.9k
            MVMint32 guard_failed = 0;
1002
28.5k
            for (j = 0; j < cand->num_guards; j++) {
1003
21.5k
                MVMint32       slot    = cand->guards[j].slot;
1004
21.5k
                MVMSpeshFacts *facts   = slot < MAX_ARGS_FOR_OPT ? arg_info->arg_facts[slot] : NULL;
1005
21.5k
                MVMSTable     *want_st = (MVMSTable *)cand->guards[j].match;
1006
21.5k
                if (!facts) {
1007
31
                    guard_failed = 1;
1008
31
                    break;
1009
31
                }
1010
21.4k
                switch (cand->guards[j].kind) {
1011
18.2k
                case MVM_SPESH_GUARD_CONC:
1012
18.2k
                    if (!(facts->flags & MVM_SPESH_FACT_CONCRETE) ||
1013
14.1k
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) ||
1014
14.1k
                            STABLE(facts->type) != want_st)
1015
7.42k
                        guard_failed = 1;
1016
18.2k
                    break;
1017
3.19k
                case MVM_SPESH_GUARD_TYPE:
1018
3.19k
                    if (!(facts->flags & MVM_SPESH_FACT_TYPEOBJ) ||
1019
2.79k
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) ||
1020
2.79k
                            STABLE(facts->type) != want_st)
1021
454
                        guard_failed = 1;
1022
3.19k
                    break;
1023
0
                case MVM_SPESH_GUARD_DC_CONC:
1024
0
                    if (!(facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) ||
1025
0
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) ||
1026
0
                            STABLE(facts->decont_type) != want_st)
1027
0
                        guard_failed = 1;
1028
0
                    break;
1029
0
                case MVM_SPESH_GUARD_DC_TYPE:
1030
0
                    if (!(facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) ||
1031
0
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) ||
1032
0
                            STABLE(facts->decont_type) != want_st)
1033
0
                        guard_failed = 1;
1034
0
                    break;
1035
0
                case MVM_SPESH_GUARD_DC_CONC_RW:
1036
0
                    if (!(facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE) ||
1037
0
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) ||
1038
0
                            STABLE(facts->decont_type) != want_st ||
1039
0
                            !(facts->flags & MVM_SPESH_FACT_RW_CONT))
1040
0
                        guard_failed = 1;
1041
0
                    break;
1042
0
                case MVM_SPESH_GUARD_DC_TYPE_RW:
1043
0
                    if (!(facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) ||
1044
0
                            !(facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) ||
1045
0
                            STABLE(facts->decont_type) != want_st ||
1046
0
                            !(facts->flags & MVM_SPESH_FACT_RW_CONT))
1047
0
                        guard_failed = 1;
1048
0
                    break;
1049
0
                default:
1050
0
                    guard_failed = 1;
1051
0
                    break;
1052
21.4k
                }
1053
21.4k
                if (guard_failed)
1054
7.87k
                    break;
1055
21.4k
            }
1056
14.9k
            if (!guard_failed)
1057
7.05k
                return i;
1058
14.9k
        }
1059
19.3k
    }
1060
7.81k
    return -1;
1061
14.8k
}
1062
1063
/* Drives optimization of a call. */
1064
static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
1065
32.1k
                          MVMSpeshIns *ins, MVMint32 callee_idx, MVMSpeshCallInfo *arg_info) {
1066
32.1k
    /* Ensure we know what we're going to be invoking. */
1067
32.1k
    MVMSpeshFacts *callee_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[callee_idx]);
1068
32.1k
    if (callee_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
1069
16.4k
        MVMObject *code   = callee_facts->value.o;
1070
16.4k
        MVMObject *target = NULL;
1071
16.4k
        if (REPR(code)->ID == MVM_REPR_ID_MVMCode) {
1072
114
            /* Already have a code object we know we'll call. */
1073
114
            target = code;
1074
114
        }
1075
16.3k
        else if (STABLE(code)->invocation_spec) {
1076
16.3k
            /* What kind of invocation will it be? */
1077
16.3k
            MVMInvocationSpec *is = STABLE(code)->invocation_spec;
1078
16.3k
            if (!MVM_is_null(tc, is->md_class_handle)) {
1079
15.7k
                /* Multi-dispatch. Check if this is a dispatch where we can
1080
15.7k
                 * use the cache directly. */
1081
15.7k
                MVMRegister dest;
1082
15.7k
                REPR(code)->attr_funcs.get_attribute(tc,
1083
15.7k
                    STABLE(code), code, OBJECT_BODY(code),
1084
15.7k
                    is->md_class_handle, is->md_valid_attr_name,
1085
15.7k
                    is->md_valid_hint, &dest, MVM_reg_int64);
1086
15.7k
                if (dest.i64) {
1087
787
                    /* Yes. Try to obtain the cache. */
1088
787
                    REPR(code)->attr_funcs.get_attribute(tc,
1089
787
                        STABLE(code), code, OBJECT_BODY(code),
1090
787
                        is->md_class_handle, is->md_cache_attr_name,
1091
787
                        is->md_cache_hint, &dest, MVM_reg_obj);
1092
787
                    if (!MVM_is_null(tc, dest.o)) {
1093
787
                        MVMObject *found = MVM_multi_cache_find_spesh(tc, dest.o, arg_info);
1094
787
                        if (found) {
1095
256
                            /* Found it. Is it a code object already, or do we
1096
256
                             * have futher unpacking to do? */
1097
256
                            if (REPR(found)->ID == MVM_REPR_ID_MVMCode) {
1098
0
                                target = found;
1099
0
                            }
1100
256
                            else if (STABLE(found)->invocation_spec) {
1101
256
                                MVMInvocationSpec *m_is = STABLE(found)->invocation_spec;
1102
256
                                if (!MVM_is_null(tc, m_is->class_handle)) {
1103
256
                                    REPR(found)->attr_funcs.get_attribute(tc,
1104
256
                                        STABLE(found), found, OBJECT_BODY(found),
1105
256
                                        is->class_handle, is->attr_name,
1106
256
                                        is->hint, &dest, MVM_reg_obj);
1107
256
                                    if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode)
1108
256
                                        target = dest.o;
1109
256
                                }
1110
256
                            }
1111
256
                        }
1112
787
                    }
1113
787
                }
1114
14.9k
                else if (!MVM_is_null(tc, is->class_handle)) {
1115
14.9k
                    /* This type of code object supports multi-dispatch,
1116
14.9k
                     * but we actually have a single dispatch routine. */
1117
14.9k
                    MVMRegister dest;
1118
14.9k
                    REPR(code)->attr_funcs.get_attribute(tc,
1119
14.9k
                        STABLE(code), code, OBJECT_BODY(code),
1120
14.9k
                        is->class_handle, is->attr_name,
1121
14.9k
                        is->hint, &dest, MVM_reg_obj);
1122
14.9k
                    if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode)
1123
14.9k
                        target = dest.o;
1124
14.9k
                }
1125
15.7k
            }
1126
649
            else if (!MVM_is_null(tc, is->class_handle)) {
1127
649
                /* Single dispatch; retrieve the code object. */
1128
649
                MVMRegister dest;
1129
649
                REPR(code)->attr_funcs.get_attribute(tc,
1130
649
                    STABLE(code), code, OBJECT_BODY(code),
1131
649
                    is->class_handle, is->attr_name,
1132
649
                    is->hint, &dest, MVM_reg_obj);
1133
649
                if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode)
1134
649
                    target = dest.o;
1135
649
            }
1136
16.3k
        }
1137
16.4k
1138
16.4k
        /* If we resolved to something better than the code object, then add
1139
16.4k
         * the resolved item in a spesh slot and insert a lookup. */
1140
16.4k
        if (target && target != code && !((MVMCode *)target)->body.is_compiler_stub) {
1141
15.8k
            MVMSpeshIns *pa_ins = arg_info->prepargs_ins;
1142
15.8k
            MVMSpeshIns *ss_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
1143
15.8k
            ss_ins->info        = MVM_op_get_op(MVM_OP_sp_getspeshslot);
1144
15.8k
            ss_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
1145
15.8k
            ss_ins->operands[0] = ins->operands[callee_idx];
1146
15.8k
            ss_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
1147
15.8k
                (MVMCollectable *)target);
1148
15.8k
            /* Basically, we're inserting between arg* and invoke_*.
1149
15.8k
             * Since invoke_* directly uses the code in the register,
1150
15.8k
             * the register must have held the code during the arg*
1151
15.8k
             * instructions as well, because none of {prepargs, arg*}
1152
15.8k
             * can manipulate the register that holds the code.
1153
15.8k
             *
1154
15.8k
             * To make a long story very short, I think it should be
1155
15.8k
             * safe to move the sp_getspeshslot to /before/ the
1156
15.8k
             * prepargs instruction. And this is very convenient for
1157
15.8k
             * me, as it allows me to treat set of prepargs, arg*,
1158
15.8k
             * invoke, as a /single node/, and this greatly simplifies
1159
15.8k
             * invoke JIT compilation */
1160
15.8k
1161
15.8k
            MVM_spesh_manipulate_insert_ins(tc, bb, pa_ins->prev, ss_ins);
1162
15.8k
            /* XXX TODO: Do this differently so we can eliminate the original
1163
15.8k
             * lookup of the enclosing code object also. */
1164
15.8k
        }
1165
16.4k
1166
16.4k
        /* See if we can point the call at a particular specialization. */
1167
16.4k
        if (target && ((MVMCode *)target)->body.sf->body.instrumentation_level == tc->instance->instrumentation_level) {
1168
14.8k
            MVMCode *target_code  = (MVMCode *)target;
1169
14.8k
            MVMint32 spesh_cand = try_find_spesh_candidate(tc, target_code, arg_info);
1170
14.8k
            if (spesh_cand >= 0) {
1171
7.05k
                /* Yes. Will we be able to inline? */
1172
7.05k
                MVMSpeshGraph *inline_graph = MVM_spesh_inline_try_get_graph(tc, g,
1173
7.05k
                    target_code, &target_code->body.sf->body.spesh_candidates[spesh_cand]);
1174
7.05k
                if (inline_graph) {
1175
3.88k
                    /* Yes, have inline graph, so go ahead and do it. */
1176
3.88k
#if MVM_LOG_INLINES
1177
                    char *c_name_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.name);
1178
                    char *c_cuid_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.cuuid);
1179
                    char *c_name_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.name);
1180
                    char *c_cuid_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.cuuid);
1181
                    printf("Can inline %s (%s) into %s (%s)\n",
1182
                        c_name_i, c_cuid_i, c_name_t, c_cuid_t);
1183
                    MVM_free(c_name_i);
1184
                    MVM_free(c_cuid_i);
1185
                    MVM_free(c_name_t);
1186
                    MVM_free(c_cuid_t);
1187
#endif
1188
3.88k
                    MVM_spesh_inline(tc, g, arg_info, bb, ins, inline_graph, target_code);
1189
3.88k
                }
1190
3.17k
                else {
1191
3.17k
                    /* Can't inline, so just identify candidate. */
1192
3.17k
                    MVMSpeshOperand *new_operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
1193
3.17k
                    if (ins->info->opcode == MVM_OP_invoke_v) {
1194
204
                        new_operands[0]         = ins->operands[0];
1195
204
                        new_operands[1].lit_i16 = spesh_cand;
1196
204
                        ins->operands           = new_operands;
1197
204
                        ins->info               = MVM_op_get_op(MVM_OP_sp_fastinvoke_v);
1198
204
                    }
1199
2.96k
                    else {
1200
2.96k
                        new_operands[0]         = ins->operands[0];
1201
2.96k
                        new_operands[1]         = ins->operands[1];
1202
2.96k
                        new_operands[2].lit_i16 = spesh_cand;
1203
2.96k
                        ins->operands           = new_operands;
1204
2.96k
                        switch (ins->info->opcode) {
1205
0
                        case MVM_OP_invoke_i:
1206
0
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_i);
1207
0
                            break;
1208
0
                        case MVM_OP_invoke_n:
1209
0
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_n);
1210
0
                            break;
1211
0
                        case MVM_OP_invoke_s:
1212
0
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_s);
1213
0
                            break;
1214
2.96k
                        case MVM_OP_invoke_o:
1215
2.96k
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_o);
1216
2.96k
                            break;
1217
0
                        default:
1218
0
                            MVM_oops(tc, "Spesh: unhandled invoke instruction");
1219
2.96k
                        }
1220
2.96k
                    }
1221
3.17k
                }
1222
7.05k
            }
1223
14.8k
        }
1224
16.4k
    }
1225
32.1k
}
1226
1227
0
static void optimize_coverage_log(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1228
0
    char *cache        = (char *)ins->operands[3].lit_i64;
1229
0
    MVMint32 cache_idx = ins->operands[2].lit_i32;
1230
0
1231
0
    if (cache[cache_idx] != 0) {
1232
0
        MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1233
0
    }
1234
0
}
1235
1236
/* Optimizes an extension op. */
1237
0
static void optimize_extop(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1238
0
    MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
1239
0
    MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
1240
0
    MVMuint16       i;
1241
0
    for (i = 0; i < num_extops; i++) {
1242
0
        if (extops[i].info == ins->info) {
1243
0
            /* Found op; call its spesh function, if any. */
1244
0
            if (extops[i].spesh)
1245
0
                extops[i].spesh(tc, g, bb, ins);
1246
0
            return;
1247
0
        }
1248
0
    }
1249
0
}
1250
1251
0
static void optimize_uniprop_ops(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1252
0
    MVMSpeshFacts *arg1_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
1253
0
    MVMSpeshFacts *result_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
1254
0
    if (arg1_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
1255
0
        if (ins->info->opcode == MVM_OP_unipropcode) {
1256
0
            result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
1257
0
            result_facts->value.i = (MVMint64)MVM_unicode_name_to_property_code(tc, arg1_facts->value.s);
1258
0
            ins->info = MVM_op_get_op(MVM_OP_const_i64);
1259
0
            ins->operands[1].lit_i64 = result_facts->value.i;
1260
0
            arg1_facts->usages--;
1261
0
        } else if (ins->info->opcode == MVM_OP_unipvalcode) {
1262
0
            MVMSpeshFacts *arg2_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]);
1263
0
1264
0
            if (arg2_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
1265
0
                result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
1266
0
                result_facts->value.i = (MVMint64)MVM_unicode_name_to_property_value_code(tc, arg1_facts->value.i, arg2_facts->value.s);
1267
0
                ins->info = MVM_op_get_op(MVM_OP_const_i64);
1268
0
                ins->operands[1].lit_i64 = result_facts->value.i;
1269
0
                arg1_facts->usages--;
1270
0
                arg2_facts->usages--;
1271
0
            }
1272
0
        }
1273
0
}
1274
0
}
1275
1276
/* If something is only kept alive because we log its allocation, kick out
1277
 * the allocation logging and let the op that creates it die.
1278
 */
1279
0
static void optimize_prof_allocated(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1280
0
    MVMSpeshFacts *logee_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
1281
0
    if (logee_facts->usages == 1) {
1282
0
        logee_facts->usages = 0;
1283
0
        MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1284
0
        /* This check should always succeed, but just in case ... */
1285
0
        if (logee_facts->writer)
1286
0
            MVM_spesh_manipulate_delete_ins(tc, g, bb, logee_facts->writer);
1287
0
    }
1288
0
}
1289
1290
/* Tries to optimize a throwcat instruction. Note that within a given frame
1291
 * (we don't consider inlines here) the throwcat instructions all have the
1292
 * same semantics. */
1293
394
static void optimize_throwcat(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1294
394
    /* First, see if we have any goto handlers for this category. */
1295
394
    MVMint32 *handlers_found = MVM_malloc(g->sf->body.num_handlers * sizeof(MVMint32));
1296
394
    MVMint32  num_found      = 0;
1297
394
    MVMuint32 category       = (MVMuint32)ins->operands[1].lit_i64;
1298
394
    MVMint32  i;
1299
4.92k
    for (i = 0; i < g->sf->body.num_handlers; i++)
1300
4.53k
        if (g->sf->body.handlers[i].action == MVM_EX_ACTION_GOTO)
1301
4.17k
            if (g->sf->body.handlers[i].category_mask & category)
1302
1.39k
                handlers_found[num_found++] = i;
1303
394
1304
394
    /* If we found any appropriate handlers, we'll now do a scan through the
1305
394
     * graph to see if we're in the scope of any of them. Note we can't keep
1306
394
     * track of this in optimize_bb as it walks the dominance children, but
1307
394
     * we need a linear view. */
1308
394
    if (num_found) {
1309
382
        MVMint32    *in_handlers = MVM_calloc(g->sf->body.num_handlers, sizeof(MVMint32));
1310
382
        MVMSpeshBB **goto_bbs    = MVM_calloc(g->sf->body.num_handlers, sizeof(MVMSpeshBB *));
1311
382
        MVMSpeshBB  *search_bb   = g->entry;
1312
382
        MVMint32     picked      = -1;
1313
88.3k
        while (search_bb && !search_bb->inlined) {
1314
88.3k
            MVMSpeshIns *search_ins = search_bb->first_ins;
1315
814k
            while (search_ins) {
1316
726k
                /* Track handlers. */
1317
726k
                MVMSpeshAnn *ann = search_ins->annotations;
1318
762k
                while (ann) {
1319
35.9k
                    switch (ann->type) {
1320
3.93k
                    case MVM_SPESH_ANN_FH_START:
1321
3.93k
                        in_handlers[ann->data.frame_handler_index] = 1;
1322
3.93k
                        break;
1323
1.96k
                    case MVM_SPESH_ANN_FH_END:
1324
1.96k
                        in_handlers[ann->data.frame_handler_index] = 0;
1325
1.96k
                        break;
1326
3.67k
                    case MVM_SPESH_ANN_FH_GOTO:
1327
3.67k
                        if (ann->data.frame_handler_index < g->sf->body.num_handlers) {
1328
3.37k
                            goto_bbs[ann->data.frame_handler_index] = search_bb;
1329
3.37k
                            if (picked >= 0 && ann->data.frame_handler_index == picked)
1330
374
                                goto search_over;
1331
3.37k
                        }
1332
3.30k
                        break;
1333
35.9k
                    }
1334
35.5k
                    ann = ann->next;
1335
35.5k
                }
1336
726k
1337
726k
                /* Is this instruction the one we're trying to optimize? */
1338
726k
                if (search_ins == ins) {
1339
382
                    /* See if we're in any acceptable handler (rely on the
1340
382
                     * table being pre-sorted by nesting depth here, just like
1341
382
                     * normal exception handler search does). */
1342
1.04k
                    for (i = 0; i < num_found; i++) {
1343
1.04k
                        if (in_handlers[handlers_found[i]]) {
1344
382
                            /* Got it! If we already found its goto target, we
1345
382
                             * can finish the search. */
1346
382
                            picked = handlers_found[i];
1347
382
                            if (goto_bbs[picked])
1348
8
                                goto search_over;
1349
374
                            break;
1350
382
                        }
1351
1.04k
                    }
1352
382
                }
1353
726k
1354
726k
                search_ins = search_ins->next;
1355
726k
            }
1356
87.9k
            search_bb = search_bb->linear_next;
1357
87.9k
        }
1358
382
      search_over:
1359
382
1360
382
        /* If we picked a handler and know where it should goto, we can do the
1361
382
         * rewrite into a goto. */
1362
382
        if (picked >=0 && goto_bbs[picked]) {
1363
382
            ins->info               = MVM_op_get_op(MVM_OP_goto);
1364
382
            ins->operands[0].ins_bb = goto_bbs[picked];
1365
382
            bb->succ[0]             = goto_bbs[picked];
1366
382
        }
1367
382
1368
382
        MVM_free(in_handlers);
1369
382
        MVM_free(goto_bbs);
1370
382
    }
1371
394
1372
394
    MVM_free(handlers_found);
1373
394
}
1374
1375
559k
static void analyze_phi(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
1376
559k
    MVMuint32 operand;
1377
559k
    MVMint32 common_flags;
1378
559k
    MVMObject *common_type;
1379
559k
    MVMObject *common_decont_type;
1380
559k
    MVMuint32 needs_merged_with_log_guard = 0;
1381
559k
    MVMSpeshFacts *target_facts = get_facts_direct(tc, g, ins->operands[0]);
1382
559k
1383
559k
    common_flags       = get_facts_direct(tc, g, ins->operands[1])->flags;
1384
559k
    common_type        = get_facts_direct(tc, g, ins->operands[1])->type;
1385
559k
    common_decont_type = get_facts_direct(tc, g, ins->operands[1])->decont_type;
1386
559k
1387
559k
    needs_merged_with_log_guard = common_flags & MVM_SPESH_FACT_FROM_LOG_GUARD;
1388
559k
1389
1.50M
    for(operand = 2; operand < ins->info->num_operands; operand++) {
1390
947k
        common_flags = common_flags & get_facts_direct(tc, g, ins->operands[operand])->flags;
1391
832k
        common_type = common_type == get_facts_direct(tc, g, ins->operands[operand])->type && common_type ? common_type : NULL;
1392
947k
        common_decont_type = common_decont_type == get_facts_direct(tc, g, ins->operands[operand])->decont_type && common_decont_type ? common_decont_type : NULL;
1393
947k
1394
947k
        /* We have to be a bit more careful if one or more of the facts we're
1395
947k
         * merging came from a log guard, as that means we'll have to propagate
1396
947k
         * the information what guards have been relied upon back "outwards"
1397
947k
         * through the PHI node we've merged stuff with. */
1398
947k
        if (get_facts_direct(tc, g, ins->operands[operand])->flags & MVM_SPESH_FACT_FROM_LOG_GUARD)
1399
18.8k
            needs_merged_with_log_guard = 1;
1400
947k
    }
1401
559k
1402
559k
    if (common_flags) {
1403
35.4k
        /*fprintf(stderr, "at a PHI node of %d operands: ", ins->info->num_operands);*/
1404
35.4k
        if (common_flags & MVM_SPESH_FACT_KNOWN_TYPE) {
1405
15.6k
            /*fprintf(stderr, "type ");*/
1406
15.6k
            if (common_type) {
1407
6.02k
                /*fprintf(stderr, "(same type) ");*/
1408
6.02k
                target_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
1409
6.02k
                target_facts->type = common_type;
1410
6.02k
            }
1411
15.6k
            /*else fprintf(stderr, "(diverging type) ");*/
1412
15.6k
        }
1413
35.4k
        /*if (common_flags & MVM_SPESH_FACT_KNOWN_VALUE) fprintf(stderr, "value ");*/
1414
35.4k
        if (common_flags & MVM_SPESH_FACT_DECONTED) {
1415
26.4k
            /*fprintf(stderr, "deconted ");*/
1416
26.4k
            target_facts->flags |= MVM_SPESH_FACT_DECONTED;
1417
26.4k
        }
1418
35.4k
        if (common_flags & MVM_SPESH_FACT_CONCRETE) {
1419
5.33k
            /*fprintf(stderr, "concrete ");*/
1420
5.33k
            target_facts->flags |= MVM_SPESH_FACT_CONCRETE;
1421
5.33k
        }
1422
35.4k
        if (common_flags & MVM_SPESH_FACT_TYPEOBJ) {
1423
1.95k
            /*fprintf(stderr, "type_object ");*/
1424
1.95k
        }
1425
35.4k
        if (common_flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
1426
0
            /*fprintf(stderr, "decont_type ");*/
1427
0
            if (common_decont_type) {
1428
0
                /*fprintf(stderr, "(same type) ");*/
1429
0
                target_facts->flags |= MVM_SPESH_FACT_KNOWN_DECONT_TYPE;
1430
0
                target_facts->decont_type = common_decont_type;
1431
0
            }
1432
0
            /*else fprintf(stderr, "(diverging type) ");*/
1433
0
        }
1434
35.4k
        if (common_flags & MVM_SPESH_FACT_DECONT_CONCRETE) {
1435
0
            /*fprintf(stderr, "decont_concrete ");*/
1436
0
            target_facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE;
1437
0
        }
1438
35.4k
        if (common_flags & MVM_SPESH_FACT_DECONT_TYPEOBJ) {
1439
0
            /*fprintf(stderr, "decont_typeobj ");*/
1440
0
            target_facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ;
1441
0
        }
1442
35.4k
        if (common_flags & MVM_SPESH_FACT_RW_CONT) {
1443
0
            /*fprintf(stderr, "rw_cont ");*/
1444
0
            target_facts->flags |= MVM_SPESH_FACT_RW_CONT;
1445
0
        }
1446
35.4k
        /*if (common_flags & MVM_SPESH_FACT_FROM_LOG_GUARD) fprintf(stderr, "from_log_guard ");*/
1447
35.4k
        /*if (common_flags & MVM_SPESH_FACT_HASH_ITER) fprintf(stderr, "hash_iter ");*/
1448
35.4k
        /*if (common_flags & MVM_SPESH_FACT_ARRAY_ITER) fprintf(stderr, "array_iter ");*/
1449
35.4k
        /*if (common_flags & MVM_SPESH_FACT_KNOWN_BOX_SRC) fprintf(stderr, "box_source ");*/
1450
35.4k
        /*fprintf(stderr, "\n");*/
1451
35.4k
1452
35.4k
        if (needs_merged_with_log_guard) {
1453
6.31k
            target_facts->flags |= MVM_SPESH_FACT_MERGED_WITH_LOG_GUARD;
1454
6.31k
        }
1455
523k
    } else {
1456
523k
        /*fprintf(stderr, "a PHI node of %d operands had no intersecting flags\n", ins->info->num_operands);*/
1457
523k
        return;
1458
523k
    }
1459
559k
}
1460
/* Visits the blocks in dominator tree order, recursively. */
1461
263k
static void optimize_bb(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) {
1462
263k
    MVMSpeshCallInfo arg_info;
1463
263k
    MVMint32 i;
1464
263k
1465
263k
    /* Look for instructions that are interesting to optimize. */
1466
263k
    MVMSpeshIns *ins = bb->first_ins;
1467
2.05M
    while (ins) {
1468
1.79M
        switch (ins->info->opcode) {
1469
559k
        case MVM_SSA_PHI:
1470
559k
            analyze_phi(tc, g, ins);
1471
559k
            break;
1472
120k
        case MVM_OP_set:
1473
120k
            copy_facts(tc, g, ins->operands[0], ins->operands[1]);
1474
120k
            break;
1475
861
        case MVM_OP_istrue:
1476
861
        case MVM_OP_isfalse:
1477
861
            optimize_istrue_isfalse(tc, g, bb, ins);
1478
861
            break;
1479
55.9k
        case MVM_OP_if_i:
1480
55.9k
        case MVM_OP_unless_i:
1481
55.9k
        case MVM_OP_if_n:
1482
55.9k
        case MVM_OP_unless_n:
1483
55.9k
        case MVM_OP_if_o:
1484
55.9k
        case MVM_OP_unless_o:
1485
55.9k
            optimize_iffy(tc, g, ins, bb);
1486
55.9k
            break;
1487
32.1k
        case MVM_OP_prepargs:
1488
32.1k
            arg_info.cs = g->sf->body.cu->body.callsites[ins->operands[0].callsite_idx];
1489
32.1k
            arg_info.prepargs_ins = ins;
1490
32.1k
            arg_info.prepargs_bb  = bb;
1491
32.1k
            break;
1492
65.7k
        case MVM_OP_arg_i:
1493
65.7k
        case MVM_OP_arg_n:
1494
65.7k
        case MVM_OP_arg_s:
1495
65.7k
        case MVM_OP_arg_o: {
1496
65.7k
            MVMint16 idx = ins->operands[0].lit_i16;
1497
65.7k
            if (idx < MAX_ARGS_FOR_OPT) {
1498
63.6k
                arg_info.arg_is_const[idx] = 0;
1499
63.6k
                arg_info.arg_facts[idx]    = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1500
63.6k
                arg_info.arg_ins[idx]      = ins;
1501
63.6k
            }
1502
65.7k
            break;
1503
65.7k
        }
1504
3.75k
        case MVM_OP_argconst_i:
1505
3.75k
        case MVM_OP_argconst_n:
1506
3.75k
        case MVM_OP_argconst_s: {
1507
3.75k
            MVMint16 idx = ins->operands[0].lit_i16;
1508
3.75k
            if (idx < MAX_ARGS_FOR_OPT) {
1509
3.49k
                arg_info.arg_is_const[idx] = 1;
1510
3.49k
                arg_info.arg_ins[idx]      = ins;
1511
3.49k
            }
1512
3.75k
            break;
1513
3.75k
        }
1514
6.58k
        case MVM_OP_coerce_in:
1515
6.58k
            optimize_coerce(tc, g, bb, ins);
1516
6.58k
            break;
1517
19.8k
        case MVM_OP_smrt_numify:
1518
19.8k
        case MVM_OP_smrt_strify:
1519
19.8k
            optimize_smart_coerce(tc, g, bb, ins);
1520
19.8k
            break;
1521
696
        case MVM_OP_invoke_v:
1522
696
            optimize_call(tc, g, bb, ins, 0, &arg_info);
1523
696
            break;
1524
31.4k
        case MVM_OP_invoke_i:
1525
31.4k
        case MVM_OP_invoke_n:
1526
31.4k
        case MVM_OP_invoke_s:
1527
31.4k
        case MVM_OP_invoke_o:
1528
31.4k
            optimize_call(tc, g, bb, ins, 1, &arg_info);
1529
31.4k
            break;
1530
394
        case MVM_OP_throwcatdyn:
1531
394
        case MVM_OP_throwcatlex:
1532
394
        case MVM_OP_throwcatlexotic:
1533
394
            optimize_throwcat(tc, g, bb, ins);
1534
394
            break;
1535
1.62k
        case MVM_OP_islist:
1536
1.62k
        case MVM_OP_ishash:
1537
1.62k
        case MVM_OP_isint:
1538
1.62k
        case MVM_OP_isnum:
1539
1.62k
        case MVM_OP_isstr:
1540
1.62k
            optimize_is_reprid(tc, g, ins);
1541
1.62k
            break;
1542
2.76k
        case MVM_OP_findmeth_s:
1543
2.76k
            optimize_findmeth_s_perhaps_constant(tc, g, ins);
1544
2.76k
            if (ins->info->opcode == MVM_OP_findmeth_s)
1545
450
                break;
1546
27.9k
        case MVM_OP_findmeth:
1547
27.9k
            optimize_method_lookup(tc, g, ins);
1548
27.9k
            break;
1549
475
        case MVM_OP_can:
1550
475
        case MVM_OP_can_s:
1551
475
            optimize_can_op(tc, g, bb, ins);
1552
475
            break;
1553
882
        case MVM_OP_gethow:
1554
882
            optimize_gethow(tc, g, ins);
1555
882
            break;
1556
5.25k
        case MVM_OP_isconcrete:
1557
5.25k
            optimize_isconcrete(tc, g, ins);
1558
5.25k
            break;
1559
1.53k
        case MVM_OP_istype:
1560
1.53k
            optimize_istype(tc, g, ins);
1561
1.53k
            break;
1562
24
        case MVM_OP_objprimspec:
1563
24
            optimize_objprimspec(tc, g, ins);
1564
24
            break;
1565
0
        case MVM_OP_unipropcode:
1566
0
        case MVM_OP_unipvalcode:
1567
0
            optimize_uniprop_ops(tc, g, bb, ins);
1568
0
            break;
1569
27.6k
        case MVM_OP_unshift_i:
1570
27.6k
        case MVM_OP_unshift_n:
1571
27.6k
        case MVM_OP_unshift_s:
1572
27.6k
        case MVM_OP_unshift_o:
1573
27.6k
        case MVM_OP_bindkey_i:
1574
27.6k
        case MVM_OP_bindkey_n:
1575
27.6k
        case MVM_OP_bindkey_s:
1576
27.6k
        case MVM_OP_bindkey_o:
1577
27.6k
        case MVM_OP_bindpos_i:
1578
27.6k
        case MVM_OP_bindpos_n:
1579
27.6k
        case MVM_OP_bindpos_s:
1580
27.6k
        case MVM_OP_bindpos_o:
1581
27.6k
        case MVM_OP_pop_i:
1582
27.6k
        case MVM_OP_pop_n:
1583
27.6k
        case MVM_OP_pop_s:
1584
27.6k
        case MVM_OP_pop_o:
1585
27.6k
        case MVM_OP_deletekey:
1586
27.6k
        case MVM_OP_setelemspos:
1587
27.6k
        case MVM_OP_splice:
1588
27.6k
        case MVM_OP_bindattr_i:
1589
27.6k
        case MVM_OP_bindattr_n:
1590
27.6k
        case MVM_OP_bindattr_s:
1591
27.6k
        case MVM_OP_bindattr_o:
1592
27.6k
        case MVM_OP_bindattrs_i:
1593
27.6k
        case MVM_OP_bindattrs_n:
1594
27.6k
        case MVM_OP_bindattrs_s:
1595
27.6k
        case MVM_OP_bindattrs_o:
1596
27.6k
        case MVM_OP_assign_i:
1597
27.6k
        case MVM_OP_assign_n:
1598
27.6k
            optimize_repr_op(tc, g, bb, ins, 0);
1599
27.6k
            break;
1600
88.9k
        case MVM_OP_atpos_i:
1601
88.9k
        case MVM_OP_atpos_n:
1602
88.9k
        case MVM_OP_atpos_s:
1603
88.9k
        case MVM_OP_atpos_o:
1604
88.9k
        case MVM_OP_atkey_i:
1605
88.9k
        case MVM_OP_atkey_n:
1606
88.9k
        case MVM_OP_atkey_s:
1607
88.9k
        case MVM_OP_atkey_o:
1608
88.9k
        case MVM_OP_elems:
1609
88.9k
        case MVM_OP_shift_i:
1610
88.9k
        case MVM_OP_shift_n:
1611
88.9k
        case MVM_OP_shift_s:
1612
88.9k
        case MVM_OP_shift_o:
1613
88.9k
        case MVM_OP_push_i:
1614
88.9k
        case MVM_OP_push_n:
1615
88.9k
        case MVM_OP_push_s:
1616
88.9k
        case MVM_OP_push_o:
1617
88.9k
        case MVM_OP_existskey:
1618
88.9k
        case MVM_OP_existspos:
1619
88.9k
        case MVM_OP_getattr_i:
1620
88.9k
        case MVM_OP_getattr_n:
1621
88.9k
        case MVM_OP_getattr_s:
1622
88.9k
        case MVM_OP_getattr_o:
1623
88.9k
        case MVM_OP_getattrs_i:
1624
88.9k
        case MVM_OP_getattrs_n:
1625
88.9k
        case MVM_OP_getattrs_s:
1626
88.9k
        case MVM_OP_getattrs_o:
1627
88.9k
        case MVM_OP_decont_i:
1628
88.9k
        case MVM_OP_decont_n:
1629
88.9k
        case MVM_OP_decont_s:
1630
88.9k
        case MVM_OP_decont_u:
1631
88.9k
        case MVM_OP_create:
1632
88.9k
            optimize_repr_op(tc, g, bb, ins, 1);
1633
88.9k
            break;
1634
10.6k
        case MVM_OP_box_i:
1635
10.6k
        case MVM_OP_box_n:
1636
10.6k
        case MVM_OP_box_s:
1637
10.6k
            optimize_repr_op(tc, g, bb, ins, 2);
1638
10.6k
            break;
1639
0
        case MVM_OP_newexception:
1640
0
        case MVM_OP_bindexmessage:
1641
0
        case MVM_OP_bindexpayload:
1642
0
        case MVM_OP_getexmessage:
1643
0
        case MVM_OP_getexpayload:
1644
0
            optimize_exception_ops(tc, g, bb, ins);
1645
0
            break;
1646
0
        case MVM_OP_hllize:
1647
0
            optimize_hllize(tc, g, ins);
1648
0
            break;
1649
123k
        case MVM_OP_decont:
1650
123k
            optimize_decont(tc, g, bb, ins);
1651
123k
            break;
1652
0
        case MVM_OP_assertparamcheck:
1653
0
            optimize_assertparamcheck(tc, g, bb, ins);
1654
0
            break;
1655
1.91k
        case MVM_OP_getlexstatic_o:
1656
1.91k
            optimize_getlex_known(tc, g, bb, ins);
1657
1.91k
            break;
1658
15.6k
        case MVM_OP_getlexperinvtype_o:
1659
15.6k
            if (specialized_on_invocant(tc, g))
1660
15.6k
                optimize_getlex_known(tc, g, bb, ins);
1661
15.6k
            break;
1662
0
        case MVM_OP_isrwcont:
1663
0
            optimize_container_check(tc, g, bb, ins);
1664
0
            break;
1665
37.1k
        case MVM_OP_sp_log:
1666
37.1k
        case MVM_OP_sp_osrfinalize:
1667
37.1k
            /* Left-over log instruction that didn't become a guard, or OSR
1668
37.1k
             * finalize instruction; just delete it. */
1669
37.1k
            MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1670
37.1k
            break;
1671
0
        case MVM_OP_prof_enter:
1672
0
            /* Profiling entered from spesh should indicate so. */
1673
0
            ins->info = MVM_op_get_op(MVM_OP_prof_enterspesh);
1674
0
            break;
1675
0
        case MVM_OP_coverage_log:
1676
0
            /* A coverage_log op that has already fired can be thrown out. */
1677
0
            optimize_coverage_log(tc, g, bb, ins);
1678
552k
        default:
1679
552k
            if (ins->info->opcode == (MVMuint16)-1)
1680
0
                optimize_extop(tc, g, bb, ins);
1681
1.79M
        }
1682
1.79M
1683
1.79M
1684
1.79M
        ins = ins->next;
1685
1.79M
    }
1686
263k
1687
263k
    /* Visit children. */
1688
513k
    for (i = 0; i < bb->num_children; i++)
1689
249k
        optimize_bb(tc, g, bb->children[i]);
1690
263k
}
1691
1692
/* Eliminates any unused instructions. */
1693
13.8k
static void eliminate_dead_ins(MVMThreadContext *tc, MVMSpeshGraph *g) {
1694
13.8k
    /* Keep eliminating to a fixed point. */
1695
13.8k
    MVMint8 death = 1;
1696
56.3k
    while (death) {
1697
42.4k
        MVMSpeshBB *bb = g->entry;
1698
42.4k
        death = 0;
1699
1.19M
        while (bb && !bb->inlined) {
1700
1.15M
            MVMSpeshIns *ins = bb->last_ins;
1701
7.72M
            while (ins) {
1702
6.57M
                MVMSpeshIns *prev = ins->prev;
1703
6.57M
                if (ins->info->opcode == MVM_SSA_PHI) {
1704
1.88M
                    MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[0]);
1705
1.88M
                    if (facts->usages == 0) {
1706
234k
                        /* Propagate non-usage. */
1707
234k
                        MVMint32 i;
1708
872k
                        for (i = 1; i < ins->info->num_operands; i++)
1709
637k
                            get_facts_direct(tc, g, ins->operands[i])->usages--;
1710
234k
1711
234k
                        /* Remove this phi. */
1712
234k
                        MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1713
234k
                        death = 1;
1714
234k
                    }
1715
1.88M
                }
1716
4.68M
                else if (ins->info->pure) {
1717
2.84M
                    /* Sanity check to make sure it's a write reg as first operand. */
1718
2.84M
                    if ((ins->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg) {
1719
2.84M
                        MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[0]);
1720
2.84M
                        if (facts->usages == 0) {
1721
150k
                            /* Propagate non-usage. */
1722
150k
                            MVMint32 i;
1723
226k
                            for (i = 1; i < ins->info->num_operands; i++)
1724
75.8k
                                if ((ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg)
1725
23.3k
                                    get_facts_direct(tc, g, ins->operands[i])->usages--;
1726
150k
1727
150k
                            /* Remove this instruction. */
1728
150k
                            MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1729
150k
                            death = 1;
1730
150k
                        }
1731
2.84M
                    }
1732
2.84M
                }
1733
6.57M
                ins = prev;
1734
6.57M
            }
1735
1.15M
            bb = bb->linear_next;
1736
1.15M
        }
1737
42.4k
    }
1738
13.8k
}
1739
1740
263k
static void second_pass(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) {
1741
263k
    MVMint32 i;
1742
263k
1743
263k
    /* Look for instructions that are interesting to optimize. */
1744
263k
    MVMSpeshIns *ins = bb->first_ins;
1745
1.63M
    while (ins) {
1746
1.36M
        if (ins->prev && ins->info->opcode == MVM_OP_set) {
1747
128k
            /* We may have turned some complex instruction into a simple set
1748
128k
             * in the big switch/case up there, but we wouldn't have called
1749
128k
             * "copy_facts" on the registers yet, so we have to do it here
1750
128k
             * unless we want to lose some important facts */
1751
128k
            copy_facts(tc, g, ins->operands[0], ins->operands[1]);
1752
128k
1753
128k
            /* Due to shoddy code-gen followed by spesh discarding lots of ops,
1754
128k
             * we get quite a few redundant set instructions.
1755
128k
             * They are not costly, but we can easily kick them out. */
1756
128k
            if (ins->operands[0].reg.orig == ins->operands[1].reg.orig) {
1757
5.16k
                MVMSpeshIns *previous = ins->prev;
1758
5.16k
                MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1759
5.16k
                ins = previous;
1760
123k
            } else if (ins->prev->info->opcode == MVM_OP_set) {
1761
5.50k
                if (ins->operands[0].reg.i == ins->prev->operands[1].reg.i + 1 &&
1762
1.93k
                        ins->operands[0].reg.orig == ins->prev->operands[1].reg.orig &&
1763
1.53k
                        ins->operands[1].reg.i == ins->prev->operands[0].reg.i &&
1764
1.25k
                        ins->operands[1].reg.orig == ins->prev->operands[0].reg.orig) {
1765
499
                    MVMSpeshIns *previous = ins->prev;
1766
499
                    MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1767
499
                    ins = previous;
1768
499
                }
1769
117k
            } else if ((ins->prev->info->operands[0] & MVM_operand_rw_mask) == MVM_operand_write_reg &&
1770
86.3k
                       ins->prev->operands[0].reg.orig == ins->operands[1].reg.orig &&
1771
69.8k
                       ins->prev->operands[0].reg.i == ins->operands[1].reg.i) {
1772
69.8k
                /* If a regular operation is immediately followed by a set,
1773
69.8k
                 * we have to look at the usages of the intermediate register
1774
69.8k
                 * and make sure it's only ever read by the set, and not, for
1775
69.8k
                 * example, required by a deopt barrier to have a copy of the
1776
69.8k
                 * value. */
1777
69.8k
                MVMSpeshFacts *facts = get_facts_direct(tc, g, ins->operands[1]);
1778
69.8k
                if (facts->usages <= 1) {
1779
38.0k
                    /* Cool, we can move the register into the original ins
1780
38.0k
                     * and throw out the set instruction. */
1781
38.0k
                    MVMSpeshIns *previous = ins->prev;
1782
38.0k
                    ins->prev->operands[0].reg = ins->operands[0].reg;
1783
38.0k
1784
38.0k
                    MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
1785
38.0k
                    ins = previous;
1786
38.0k
                    facts->usages--;
1787
38.0k
                }
1788
69.8k
            }
1789
1.24M
        } else if (ins->prev && ins->info->opcode == MVM_OP_sp_getspeshslot && ins->prev->info->opcode == ins->info->opcode) {
1790
14.7k
            /* Sometimes we emit two getspeshslots in a row that write into the
1791
14.7k
             * exact same register. that's clearly wasteful and we can save a
1792
14.7k
             * tiny shred of code size here.
1793
14.7k
             * However, since spesh slots are also involved in GC, we can also
1794
14.7k
             * null the spesh slot that we throw out. */
1795
14.7k
            if (ins->operands[0].reg.orig == ins->prev->operands[0].reg.orig) {
1796
14.7k
               g->spesh_slots[ins->prev->operands[1].lit_i16] = NULL;
1797
14.7k
1798
14.7k
               MVM_spesh_manipulate_delete_ins(tc, g, bb, ins->prev);
1799
14.7k
            }
1800
1.22M
        } else if (ins->info->opcode == MVM_OP_prof_allocated) {
1801
0
            optimize_prof_allocated(tc, g, bb, ins);
1802
0
        }
1803
1.36M
1804
1.36M
        ins = ins->next;
1805
1.36M
    }
1806
263k
    /* Visit children. */
1807
513k
    for (i = 0; i < bb->num_children; i++)
1808
249k
        second_pass(tc, g, bb->children[i]);
1809
263k
}
1810
1811
/* Eliminates any unreachable basic blocks (that is, dead code). Not having
1812
 * to consider them any further simplifies all that follows. */
1813
5.34k
static MVMint64 has_handler_anns(MVMThreadContext *tc, MVMSpeshBB *bb) {
1814
5.34k
    MVMSpeshIns *ins = bb->first_ins;
1815
31.8k
    while (ins) {
1816
26.5k
        MVMSpeshAnn *ann = ins->annotations;
1817
31.5k
        while (ann) {
1818
5.02k
            switch (ann->type) {
1819
0
            case MVM_SPESH_ANN_FH_START:
1820
0
            case MVM_SPESH_ANN_FH_END:
1821
0
            case MVM_SPESH_ANN_FH_GOTO:
1822
0
                return 1;
1823
5.02k
            }
1824
5.02k
            ann = ann->next;
1825
5.02k
        }
1826
26.5k
        ins = ins->next;
1827
26.5k
    }
1828
5.34k
    return 0;
1829
5.34k
}
1830
13.8k
static void eliminate_dead_bbs(MVMThreadContext *tc, MVMSpeshGraph *g) {
1831
13.8k
    /* Iterate to fixed point. */
1832
13.8k
    MVMint8  *seen     = MVM_malloc(g->num_bbs);
1833
13.8k
    MVMint32  orig_bbs = g->num_bbs;
1834
13.8k
    MVMint8   death    = 1;
1835
32.2k
    while (death) {
1836
18.4k
        /* First pass: mark every basic block that is the entry point or the
1837
18.4k
         * successor of some other block. */
1838
18.4k
        MVMSpeshBB *cur_bb = g->entry;
1839
18.4k
        memset(seen, 0, g->num_bbs);
1840
18.4k
        seen[0] = 1;
1841
381k
        while (cur_bb) {
1842
362k
            MVMuint16 i;
1843
820k
            for (i = 0; i < cur_bb->num_succ; i++)
1844
457k
                seen[cur_bb->succ[i]->idx] = 1;
1845
362k
            cur_bb = cur_bb->linear_next;
1846
362k
        }
1847
18.4k
1848
18.4k
        /* Second pass: eliminate dead BBs from consideration. Do not get
1849
18.4k
         * rid of any that are from inlines or that contain handler related
1850
18.4k
         * annotations. */
1851
18.4k
        death = 0;
1852
18.4k
        cur_bb = g->entry;
1853
357k
        while (cur_bb->linear_next) {
1854
339k
            MVMSpeshBB *death_cand = cur_bb->linear_next;
1855
339k
            if (!seen[death_cand->idx]) {
1856
5.36k
                if (!death_cand->inlined && !has_handler_anns(tc, death_cand)) {
1857
5.34k
                    cur_bb->linear_next = cur_bb->linear_next->linear_next;
1858
5.34k
                    g->num_bbs--;
1859
5.34k
                    death = 1;
1860
5.34k
                }
1861
5.36k
            }
1862
339k
            cur_bb = cur_bb->linear_next;
1863
339k
        }
1864
18.4k
    }
1865
13.8k
    MVM_free(seen);
1866
13.8k
1867
13.8k
    if (g->num_bbs != orig_bbs) {
1868
2.66k
        MVMint32    new_idx  = 0;
1869
2.66k
        MVMSpeshBB *cur_bb   = g->entry;
1870
52.8k
        while (cur_bb) {
1871
50.2k
            cur_bb->idx = new_idx;
1872
50.2k
            new_idx++;
1873
50.2k
            cur_bb = cur_bb->linear_next;
1874
50.2k
        }
1875
2.66k
    }
1876
13.8k
}
1877
1878
/* Goes through the various log-based guard instructions and removes any that
1879
 * are not being made use of. */
1880
13.8k
static void eliminate_unused_log_guards(MVMThreadContext *tc, MVMSpeshGraph *g) {
1881
13.8k
    MVMint32 i;
1882
44.9k
    for (i = 0; i < g->num_log_guards; i++)
1883
31.1k
        if (!g->log_guards[i].used)
1884
13.7k
            MVM_spesh_manipulate_delete_ins(tc, g, g->log_guards[i].bb,
1885
13.7k
                g->log_guards[i].ins);
1886
13.8k
}
1887
1888
/* Drives the overall optimization work taking place on a spesh graph. */
1889
13.8k
void MVM_spesh_optimize(MVMThreadContext *tc, MVMSpeshGraph *g) {
1890
13.8k
    optimize_bb(tc, g, g->entry);
1891
13.8k
    eliminate_dead_ins(tc, g);
1892
13.8k
    eliminate_dead_bbs(tc, g);
1893
13.8k
    eliminate_unused_log_guards(tc, g);
1894
13.8k
    second_pass(tc, g, g->entry);
1895
13.8k
}