Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/spesh/facts.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* The code in this file walks the spesh graph, recording facts we discover
4
 * about each version of each local variable, and propagating the info as it
5
 * can. */
6
7
/* Copies facts from one var to another. */
8
static void copy_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 to_orig,
9
202k
                       MVMuint16 to_i, MVMuint16 from_orig, MVMuint16 from_i) {
10
202k
    MVMSpeshFacts *tfacts = &g->facts[to_orig][to_i];
11
202k
    MVMSpeshFacts *ffacts = &g->facts[from_orig][from_i];
12
202k
    tfacts->flags         = ffacts->flags;
13
202k
    tfacts->type          = ffacts->type;
14
202k
    tfacts->decont_type   = ffacts->decont_type;
15
202k
    tfacts->value         = ffacts->value;
16
202k
    tfacts->log_guard     = ffacts->log_guard;
17
202k
}
18
19
/* Called when one set of facts depend on another, allowing any log guard
20
 * that is to thank to be marked used as needed later on. */
21
void MVM_spesh_facts_depend(MVMThreadContext *tc, MVMSpeshGraph *g,
22
20.3k
                            MVMSpeshFacts *target, MVMSpeshFacts *source) {
23
20.3k
    if (source->flags & MVM_SPESH_FACT_FROM_LOG_GUARD) {
24
1.79k
        target->flags     |= MVM_SPESH_FACT_FROM_LOG_GUARD;
25
1.79k
        target->log_guard  = source->log_guard;
26
1.79k
    }
27
20.3k
}
28
29
/* Handles object-creating instructions. */
30
static void create_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 obj_orig,
31
17.1k
                         MVMuint16 obj_i, MVMuint16 type_orig, MVMuint16 type_i) {
32
17.1k
    MVMSpeshFacts *type_facts = &(g->facts[type_orig][type_i]);
33
17.1k
    MVMSpeshFacts *obj_facts  = &(g->facts[obj_orig][obj_i]);
34
17.1k
35
17.1k
    /* The type is carried. */
36
17.1k
    if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
37
17.0k
        obj_facts->type   = type_facts->type;
38
17.0k
        obj_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
39
17.0k
        MVM_spesh_facts_depend(tc, g, obj_facts, type_facts);
40
17.0k
    }
41
17.1k
42
17.1k
    /* We know it's a concrete object. */
43
17.1k
    obj_facts->flags |= MVM_SPESH_FACT_CONCRETE;
44
17.1k
45
17.1k
    /* If we know the type object, then we can check to see if
46
17.1k
     * it's a container type. */
47
17.1k
    if (type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
48
17.0k
        MVMObject *type = type_facts->type;
49
17.0k
        if (type && !STABLE(type)->container_spec)
50
17.0k
            obj_facts->flags |= MVM_SPESH_FACT_DECONTED;
51
17.0k
    }
52
17.1k
}
53
54
static void create_facts_with_type(MVMThreadContext *tc, MVMSpeshGraph *g,
55
                                   MVMuint16 obj_orig, MVMuint16 obj_i,
56
0
                                   MVMObject *type) {
57
0
    MVMSpeshFacts *obj_facts  = &(g->facts[obj_orig][obj_i]);
58
0
59
0
    /* The type is carried. */
60
0
    obj_facts->type   = type;
61
0
    obj_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
62
0
63
0
    /* We know it's a concrete object. */
64
0
    obj_facts->flags |= MVM_SPESH_FACT_CONCRETE;
65
0
66
0
    /* If we know the type object, then we can check to see if
67
0
     * it's a container type. */
68
0
    if (type && !STABLE(type)->container_spec)
69
0
        obj_facts->flags |= MVM_SPESH_FACT_DECONTED;
70
0
}
71
72
/* Adds facts from knowing the exact value being put into an object local. */
73
static void object_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 tgt_orig,
74
68.2k
                         MVMuint16 tgt_i, MVMObject *obj) {
75
68.2k
    /* Ensure it's non-null. */
76
68.2k
    if (!obj)
77
3.15k
        return;
78
68.2k
79
68.2k
    /* Set the value itself. */
80
65.0k
    g->facts[tgt_orig][tgt_i].value.o  = obj;
81
65.0k
    g->facts[tgt_orig][tgt_i].flags   |= MVM_SPESH_FACT_KNOWN_VALUE;
82
65.0k
83
65.0k
    /* We also know the type. */
84
65.0k
    g->facts[tgt_orig][tgt_i].type   = STABLE(obj)->WHAT;
85
65.0k
    g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_KNOWN_TYPE;
86
65.0k
87
65.0k
    /* Set concreteness and decontainerized flags. */
88
65.0k
    if (IS_CONCRETE(obj)) {
89
202
        g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_CONCRETE;
90
202
        if (!STABLE(obj)->container_spec)
91
202
            g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_DECONTED;
92
202
    }
93
64.8k
    else {
94
64.8k
        g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_TYPEOBJ | MVM_SPESH_FACT_DECONTED;
95
64.8k
    }
96
65.0k
}
97
98
/* Checks if there's a possible aliasing operation that could cause the
99
 * facts about the contents of a container to be invalid by the instruction
100
 * under consideration. Assumes the instruction is a decont with argument 1
101
 * being the thing to decontainerize. */
102
159k
MVMint32 MVM_spesh_facts_decont_blocked_by_alias(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
103
159k
    /* No facts or no writer means we can't know it's safe; block. */
104
159k
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
105
159k
    if (!facts || !facts->writer)
106
0
        return 1;
107
159k
108
159k
    /* Walk backwards over instructions. */
109
513k
    while (ins->prev) {
110
440k
        ins = ins->prev;
111
440k
112
440k
        /* If we found the writer without anything blocking, we're good. */
113
440k
        if (ins == facts->writer)
114
79.7k
            return 0;
115
440k
116
440k
        /* If there's an operation that may alias, blocked. */
117
360k
        switch (ins->info->opcode) {
118
6.06k
            case MVM_OP_bindattr_o:
119
6.06k
            case MVM_OP_bindattrs_o:
120
6.06k
            case MVM_OP_assign:
121
6.06k
            case MVM_OP_assignunchecked:
122
6.06k
            case MVM_OP_assign_i:
123
6.06k
            case MVM_OP_assign_n:
124
6.06k
            case MVM_OP_assign_s:
125
6.06k
            case MVM_OP_sp_bind_o:
126
6.06k
            case MVM_OP_sp_p6obind_o:
127
6.06k
                return 1;
128
360k
        }
129
360k
    }
130
159k
131
159k
    /* We didn't find the writer in this basic block, meaning an invocation
132
159k
     * may have made it unsafe. Block. */
133
73.8k
    return 1;
134
159k
}
135
136
/* Propagates information relating to decontainerization. */
137
static void decont_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins,
138
                         MVMuint16 out_orig, MVMuint16 out_i, MVMuint16 in_orig,
139
123k
                         MVMuint16 in_i) {
140
123k
    MVMSpeshFacts *out_facts = &(g->facts[out_orig][out_i]);
141
123k
    MVMSpeshFacts *in_facts  = &(g->facts[in_orig][in_i]);
142
123k
143
123k
    /* If we know the original is decontainerized already, just copy its
144
123k
     * info. */
145
123k
    MVMint32 in_flags = in_facts->flags;
146
123k
    if (in_flags & MVM_SPESH_FACT_DECONTED)
147
82.1k
        copy_facts(tc, g, out_orig, out_i, in_orig, in_i);
148
123k
149
123k
    /* We know the result is decontainerized. */
150
123k
    out_facts->flags |= MVM_SPESH_FACT_DECONTED;
151
123k
152
123k
    /* We may also know the original was containerized, and have some facts
153
123k
     * about its contents. */
154
123k
    if (!MVM_spesh_facts_decont_blocked_by_alias(tc, g, ins)) {
155
58.6k
        if (in_flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
156
0
            out_facts->type = in_facts->decont_type;
157
0
            out_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
158
0
        }
159
58.6k
        if (in_flags & MVM_SPESH_FACT_DECONT_CONCRETE)
160
0
            out_facts->flags |= MVM_SPESH_FACT_CONCRETE;
161
58.6k
        else if (in_flags & MVM_SPESH_FACT_DECONT_TYPEOBJ)
162
0
            out_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
163
58.6k
        if (in_flags & (MVM_SPESH_FACT_KNOWN_DECONT_TYPE |
164
58.6k
                        MVM_SPESH_FACT_DECONT_CONCRETE |
165
58.6k
                        MVM_SPESH_FACT_DECONT_TYPEOBJ))
166
0
            MVM_spesh_facts_depend(tc, g, out_facts, in_facts);
167
58.6k
    }
168
123k
}
169
170
/* Looks up a wval and adds information based on it. */
171
static void wval_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 tgt_orig,
172
52.8k
                       MVMuint16 tgt_i, MVMuint16 dep, MVMint64 idx) {
173
52.8k
    MVMCompUnit *cu = g->sf->body.cu;
174
52.8k
    if (dep < cu->body.num_scs) {
175
52.8k
        MVMSerializationContext *sc = MVM_sc_get_sc(tc, cu, dep);
176
52.8k
        if (sc)
177
52.8k
            object_facts(tc, g, tgt_orig, tgt_i, MVM_sc_try_get_object(tc, sc, idx));
178
52.8k
    }
179
52.8k
}
180
181
/* Let's figure out what exact type of iter we'll get from an iter op */
182
static void iter_facts(MVMThreadContext *tc, MVMSpeshGraph *g,
183
                       MVMuint16 out_orig, MVMuint16 out_i,
184
1.28k
                       MVMuint16 in_orig, MVMuint16 in_i) {
185
1.28k
    MVMSpeshFacts *out_facts = &(g->facts[out_orig][out_i]);
186
1.28k
    MVMSpeshFacts *in_facts  = &(g->facts[in_orig][in_i]);
187
1.28k
188
1.28k
    if (in_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
189
528
        switch (REPR(in_facts->type)->ID) {
190
134
            case MVM_REPR_ID_VMArray:
191
134
                out_facts->type = g->sf->body.cu->body.hll_config->array_iterator_type;
192
134
                out_facts->flags |= MVM_SPESH_FACT_ARRAY_ITER;
193
134
                break;
194
125
            case MVM_REPR_ID_MVMHash:
195
125
            case MVM_REPR_ID_MVMContext:
196
125
                out_facts->type = g->sf->body.cu->body.hll_config->hash_iterator_type;
197
125
                out_facts->flags |= MVM_SPESH_FACT_HASH_ITER;
198
125
                break;
199
269
            default:
200
269
                return;
201
528
        }
202
259
        out_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE;
203
259
    }
204
1.28k
205
1.28k
}
206
207
/* constant ops on literals give us a specialize-time-known value */
208
78.8k
static void literal_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
209
78.8k
    MVMSpeshFacts *tgt_facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i];
210
78.8k
    switch (ins->info->opcode) {
211
0
        case MVM_OP_const_i64:
212
0
            tgt_facts->value.i = ins->operands[1].lit_i64;
213
0
            break;
214
0
        case MVM_OP_const_i32:
215
0
            tgt_facts->value.i = ins->operands[1].lit_i32;
216
0
            break;
217
0
        case MVM_OP_const_i16:
218
0
            tgt_facts->value.i = ins->operands[1].lit_i16;
219
0
            break;
220
0
        case MVM_OP_const_i8:
221
0
            tgt_facts->value.i = ins->operands[1].lit_i8;
222
0
            break;
223
0
        case MVM_OP_const_n32:
224
0
            tgt_facts->value.n = ins->operands[1].lit_n32;
225
0
            break;
226
0
        case MVM_OP_const_n64:
227
0
            tgt_facts->value.n = ins->operands[1].lit_n64;
228
0
            break;
229
10
        case MVM_OP_const_i64_32:
230
10
            tgt_facts->value.i = ins->operands[1].lit_i32;
231
10
            break;
232
27.3k
        case MVM_OP_const_i64_16:
233
27.3k
            tgt_facts->value.i = ins->operands[1].lit_i16;
234
27.3k
            break;
235
51.5k
        case MVM_OP_const_s:
236
51.5k
            tgt_facts->value.s = MVM_cu_string(tc, g->sf->body.cu,
237
51.5k
                ins->operands[1].lit_str_idx);
238
51.5k
            break;
239
0
        default:
240
0
            return;
241
78.8k
    }
242
78.8k
    tgt_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
243
78.8k
}
244
245
/* Discover facts from extops. */
246
0
static void discover_extop(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
247
0
    MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
248
0
    MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
249
0
    MVMuint16       i;
250
0
    for (i = 0; i < num_extops; i++) {
251
0
        if (extops[i].info == ins->info) {
252
0
            /* Found op; call its discovery function, if any. */
253
0
            if (extops[i].discover)
254
0
                extops[i].discover(tc, g, ins);
255
0
            return;
256
0
        }
257
0
    }
258
0
}
259
/* Allocates space for keeping track of guards inserted from logging, and
260
 * their usage. */
261
13.8k
static void allocate_log_guard_table(MVMThreadContext *tc, MVMSpeshGraph *g) {
262
13.8k
    g->log_guards = MVM_spesh_alloc(tc, g, g->num_log_slots * sizeof(MVMSpeshLogGuard));
263
13.8k
}
264
265
/* Check for stability of what was logged, and if it looks sane then add facts
266
 * and turn the log instruction into a  */
267
61.0k
static void log_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
268
61.0k
    MVMObject     *stable_value = NULL;
269
61.0k
    MVMObject     *stable_cont  = NULL;
270
61.0k
    MVMSpeshFacts *facts;
271
61.0k
272
61.0k
    /* See if all the recorded facts match up; a NULL means there was a code
273
61.0k
     * path that never reached making a log entry. */
274
61.0k
    MVMuint16 log_start = ins->operands[1].lit_i16 * MVM_SPESH_LOG_RUNS;
275
61.0k
    MVMuint16 i;
276
538k
    for (i = log_start; i < log_start + MVM_SPESH_LOG_RUNS; i++) {
277
479k
        MVMObject *consider = (MVMObject *)g->log_slots[i];
278
479k
        if (consider) {
279
233k
            if (!stable_value) {
280
33.1k
                stable_value = consider;
281
33.1k
            }
282
200k
            else if (STABLE(stable_value) != STABLE(consider)
283
198k
                    || IS_CONCRETE(stable_value) != IS_CONCRETE(consider)) {
284
2.07k
                stable_value = NULL;
285
2.07k
                break;
286
2.07k
            }
287
233k
        }
288
479k
    }
289
61.0k
    if (!stable_value)
290
29.9k
        return;
291
61.0k
292
61.0k
    /* If the value is a container type, need to look inside of it. */
293
31.1k
    if (STABLE(stable_value)->container_spec && IS_CONCRETE(stable_value)) {
294
0
        MVMContainerSpec const *contspec = STABLE(stable_value)->container_spec;
295
0
        if (!contspec->fetch_never_invokes)
296
0
            return;
297
0
        stable_cont  = stable_value;
298
0
        stable_value = NULL;
299
0
        for (i = log_start; i < log_start + MVM_SPESH_LOG_RUNS; i++) {
300
0
            MVMRegister r;
301
0
            contspec->fetch(tc, stable_cont, &r);
302
0
            if (r.o) {
303
0
                if (!stable_value) {
304
0
                    stable_value = r.o;
305
0
                }
306
0
                else if (STABLE(stable_value) != STABLE(r.o)
307
0
                        || IS_CONCRETE(stable_value) != IS_CONCRETE(r.o)) {
308
0
                    stable_value = NULL;
309
0
                    break;
310
0
                }
311
0
            }
312
0
        }
313
0
        if (!stable_value)
314
0
            return;
315
0
    }
316
31.1k
317
31.1k
    /* Produce a guard op and set facts. */
318
31.1k
    if (stable_cont) {
319
0
        MVMSpeshOperand reg  = ins->operands[0];
320
0
        MVMContainerSpec *cs = (MVMContainerSpec *) STABLE(stable_cont)->container_spec;
321
0
        facts                = &g->facts[reg.reg.orig][reg.reg.i];
322
0
        facts->type          = STABLE(stable_cont)->WHAT;
323
0
        facts->flags        |= (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE |
324
0
                               MVM_SPESH_FACT_KNOWN_DECONT_TYPE);
325
0
        facts->decont_type   = STABLE(stable_value)->WHAT;
326
0
327
0
        /* If this is a native container, we get away with testing
328
0
         * against the STABLE only, as the NativeRef REPR has all
329
0
         * interesting values in its REPRData. */
330
0
        if (cs->can_store(tc, stable_cont) &&
331
0
                (MVM_6model_container_iscont_i(tc, stable_cont) ||
332
0
                MVM_6model_container_iscont_n(tc, stable_cont) ||
333
0
                MVM_6model_container_iscont_s(tc, stable_cont))) {
334
0
            facts         = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i];
335
0
            /*facts->type   = STABLE(stable_value)->WHAT;*/
336
0
            facts->flags |= MVM_SPESH_FACT_RW_CONT;
337
0
338
0
            ins->info = MVM_op_get_op(MVM_OP_sp_guardconc);
339
0
340
0
            ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
341
0
            ins->operands[0] = reg;
342
0
            ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_cont));
343
0
        } else {
344
0
            if (cs->can_store(tc, stable_cont)) {
345
0
                /* We could do stability testing on rw-ness too, but it's quite
346
0
                 * unlikely we'll have codepaths with a mix of readable and
347
0
                 * writable containers. */
348
0
                facts->flags |= MVM_SPESH_FACT_RW_CONT;
349
0
                if (IS_CONCRETE(stable_value)) {
350
0
                    facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE;
351
0
                    ins->info = MVM_op_get_op(MVM_OP_sp_guardrwconc);
352
0
                }
353
0
                else {
354
0
                    facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ;
355
0
                    ins->info = MVM_op_get_op(MVM_OP_sp_guardrwtype);
356
0
                }
357
0
            }
358
0
            else {
359
0
                if (IS_CONCRETE(stable_value)) {
360
0
                    facts->flags |= MVM_SPESH_FACT_DECONT_CONCRETE;
361
0
                    ins->info = MVM_op_get_op(MVM_OP_sp_guardcontconc);
362
0
                }
363
0
                else {
364
0
                    facts->flags |= MVM_SPESH_FACT_DECONT_TYPEOBJ;
365
0
                    ins->info = MVM_op_get_op(MVM_OP_sp_guardconttype);
366
0
                }
367
0
            }
368
0
            ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
369
0
            ins->operands[0] = reg;
370
0
            ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_cont));
371
0
            ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_value));
372
0
        }
373
0
    }
374
31.1k
    else {
375
31.1k
        facts         = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i];
376
31.1k
        facts->type   = STABLE(stable_value)->WHAT;
377
31.1k
        facts->flags |= (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_DECONTED);
378
31.1k
        if (IS_CONCRETE(stable_value)) {
379
27.9k
            facts->flags |= MVM_SPESH_FACT_CONCRETE;
380
27.9k
            ins->info = MVM_op_get_op(MVM_OP_sp_guardconc);
381
27.9k
        }
382
3.12k
        else {
383
3.12k
            facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
384
3.12k
            ins->info = MVM_op_get_op(MVM_OP_sp_guardtype);
385
3.12k
        }
386
31.1k
        ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)STABLE(stable_value));
387
31.1k
    }
388
31.1k
389
31.1k
    /* Add entry in log guards table, and mark facts as depending on it. */
390
31.1k
    g->log_guards[g->num_log_guards].ins = ins;
391
31.1k
    g->log_guards[g->num_log_guards].bb  = bb;
392
31.1k
    facts->flags     |= MVM_SPESH_FACT_FROM_LOG_GUARD;
393
31.1k
    facts->log_guard  = g->num_log_guards;
394
31.1k
    g->num_log_guards++;
395
31.1k
}
396
397
/* Visits the blocks in dominator tree order, recursively. */
398
static void add_bb_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
399
263k
                         MVMint32 cur_deopt_idx) {
400
263k
    MVMint32 i, is_phi;
401
263k
402
263k
    /* Look for instructions that provide or propagate facts. */
403
263k
    MVMSpeshIns *ins = bb->first_ins;
404
2.06M
    while (ins) {
405
1.80M
        /* See if there's a deopt annotation, and sync cur_deopt_idx. */
406
1.80M
        MVMSpeshAnn *ann = ins->annotations;
407
1.82M
        while (ann) {
408
120k
            if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS ||
409
104k
                    ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS) {
410
104k
                cur_deopt_idx = ann->data.deopt_idx;
411
104k
                break;
412
104k
            }
413
16.6k
            ann = ann->next;
414
16.6k
        }
415
1.80M
416
1.80M
        /* Look through operands for reads and writes. */
417
1.80M
        is_phi = ins->info->opcode == MVM_SSA_PHI;
418
6.40M
        for (i = 0; i < ins->info->num_operands; i++) {
419
4.59M
            /* Reads need usage tracking; if the read is after a deopt point
420
4.59M
             * relative to the write then give it an extra usage bump. */
421
4.59M
            if ((is_phi && i > 0)
422
3.09M
                || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg)) {
423
2.50M
                MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]);
424
1.43M
                facts->usages += facts->deopt_idx == cur_deopt_idx ? 1 : 2;
425
2.50M
            }
426
4.59M
427
4.59M
            /* Writes need the current deopt index and the writing instruction
428
4.59M
             * to be specified. */
429
4.59M
            if ((is_phi && i == 0)
430
4.03M
                || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg)) {
431
1.41M
                MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]);
432
1.41M
                facts->deopt_idx = cur_deopt_idx;
433
1.41M
                facts->writer    = ins;
434
1.41M
            }
435
4.59M
        }
436
1.80M
437
1.80M
        /* Look for ops that are fact-interesting. */
438
1.80M
        switch (ins->info->opcode) {
439
6.97k
        case MVM_OP_inc_i:
440
6.97k
        case MVM_OP_inc_u:
441
6.97k
        case MVM_OP_dec_i:
442
6.97k
        case MVM_OP_dec_u:
443
6.97k
            /* These all read as well as write a value, so bump usages. */
444
6.97k
            g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i - 1].usages++;
445
6.97k
            break;
446
120k
        case MVM_OP_set:
447
120k
            copy_facts(tc, g,
448
120k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
449
120k
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
450
120k
            break;
451
6.43k
        case MVM_OP_create:
452
6.43k
            create_facts(tc, g,
453
6.43k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
454
6.43k
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
455
6.43k
            break;
456
10.6k
        case MVM_OP_box_s:
457
10.6k
        case MVM_OP_box_i:
458
10.6k
        case MVM_OP_box_n: {
459
10.6k
                MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]);
460
10.6k
                create_facts(tc, g,
461
10.6k
                    ins->operands[0].reg.orig, ins->operands[0].reg.i,
462
10.6k
                    ins->operands[2].reg.orig, ins->operands[2].reg.i);
463
10.6k
                target_facts->flags |= MVM_SPESH_FACT_KNOWN_BOX_SRC;
464
10.6k
                break;
465
10.6k
            }
466
0
        case MVM_OP_add_I:
467
0
        case MVM_OP_sub_I:
468
0
        case MVM_OP_mul_I:
469
0
        case MVM_OP_div_I:
470
0
        case MVM_OP_mod_I:
471
0
            create_facts(tc, g,
472
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
473
0
                ins->operands[3].reg.orig, ins->operands[3].reg.i);
474
0
            break;
475
0
        case MVM_OP_neg_I:
476
0
        case MVM_OP_abs_I:
477
0
            create_facts(tc, g,
478
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
479
0
                ins->operands[2].reg.orig, ins->operands[2].reg.i);
480
0
            break;
481
0
        case MVM_OP_bootint:
482
0
            object_facts(tc, g,
483
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
484
0
                tc->instance->boot_types.BOOTInt);
485
0
            break;
486
0
        case MVM_OP_bootnum:
487
0
            object_facts(tc, g,
488
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
489
0
                tc->instance->boot_types.BOOTNum);
490
0
            break;
491
0
        case MVM_OP_bootstr:
492
0
            object_facts(tc, g,
493
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
494
0
                tc->instance->boot_types.BOOTStr);
495
0
            break;
496
0
        case MVM_OP_bootarray:
497
0
            object_facts(tc, g,
498
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
499
0
                tc->instance->boot_types.BOOTArray);
500
0
            break;
501
429
        case MVM_OP_bootintarray:
502
429
            object_facts(tc, g,
503
429
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
504
429
                tc->instance->boot_types.BOOTIntArray);
505
429
            break;
506
0
        case MVM_OP_bootnumarray:
507
0
            object_facts(tc, g,
508
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
509
0
                tc->instance->boot_types.BOOTNumArray);
510
0
            break;
511
21
        case MVM_OP_bootstrarray:
512
21
            object_facts(tc, g,
513
21
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
514
21
                tc->instance->boot_types.BOOTStrArray);
515
21
            break;
516
0
        case MVM_OP_boothash:
517
0
            object_facts(tc, g,
518
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
519
0
                tc->instance->boot_types.BOOTHash);
520
0
            break;
521
8.65k
        case MVM_OP_hllboxtype_i:
522
8.65k
            object_facts(tc, g,
523
8.65k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
524
8.65k
                g->sf->body.cu->body.hll_config->int_box_type);
525
8.65k
            break;
526
263
        case MVM_OP_hllboxtype_n:
527
263
            object_facts(tc, g,
528
263
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
529
263
                g->sf->body.cu->body.hll_config->num_box_type);
530
263
            break;
531
1.77k
        case MVM_OP_hllboxtype_s:
532
1.77k
            object_facts(tc, g,
533
1.77k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
534
1.77k
                g->sf->body.cu->body.hll_config->str_box_type);
535
1.77k
            break;
536
3.31k
        case MVM_OP_hlllist:
537
3.31k
            object_facts(tc, g,
538
3.31k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
539
3.31k
                g->sf->body.cu->body.hll_config->slurpy_array_type);
540
3.31k
            break;
541
914
        case MVM_OP_hllhash:
542
914
            object_facts(tc, g,
543
914
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
544
914
                g->sf->body.cu->body.hll_config->slurpy_hash_type);
545
914
            break;
546
123k
        case MVM_OP_decont:
547
123k
            decont_facts(tc, g, ins,
548
123k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
549
123k
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
550
123k
            break;
551
52.8k
        case MVM_OP_wval:
552
52.8k
            wval_facts(tc, g,
553
52.8k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
554
52.8k
                ins->operands[1].lit_i16, ins->operands[2].lit_i16);
555
52.8k
            break;
556
0
        case MVM_OP_wval_wide:
557
0
            wval_facts(tc, g,
558
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
559
0
                ins->operands[1].lit_i16, ins->operands[2].lit_i64);
560
0
            break;
561
1.28k
        case MVM_OP_iter:
562
1.28k
            iter_facts(tc, g,
563
1.28k
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
564
1.28k
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
565
1.28k
            break;
566
0
        case MVM_OP_newexception:
567
0
            create_facts_with_type(tc, g,
568
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
569
0
                tc->instance->boot_types.BOOTException);
570
0
            break;
571
0
        case MVM_OP_getlexref_i:
572
0
        case MVM_OP_getlexref_i32:
573
0
        case MVM_OP_getlexref_i16:
574
0
        case MVM_OP_getlexref_i8:
575
0
        case MVM_OP_getlexref_u32:
576
0
        case MVM_OP_getlexref_u16:
577
0
        case MVM_OP_getlexref_u8:
578
0
            create_facts_with_type(tc, g,
579
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
580
0
                g->sf->body.cu->body.hll_config->int_lex_ref);
581
0
            break;
582
0
        case MVM_OP_getlexref_n:
583
0
        case MVM_OP_getlexref_n32:
584
0
            create_facts_with_type(tc, g,
585
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
586
0
                g->sf->body.cu->body.hll_config->num_lex_ref);
587
0
            break;
588
0
        case MVM_OP_getlexref_s:
589
0
            create_facts_with_type(tc, g,
590
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
591
0
                g->sf->body.cu->body.hll_config->str_lex_ref);
592
0
            break;
593
0
        case MVM_OP_getattrref_i:
594
0
        case MVM_OP_getattrsref_i:
595
0
            create_facts_with_type(tc, g,
596
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
597
0
                g->sf->body.cu->body.hll_config->int_attr_ref);
598
0
            break;
599
0
        case MVM_OP_getattrref_n:
600
0
        case MVM_OP_getattrsref_n:
601
0
            create_facts_with_type(tc, g,
602
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
603
0
                g->sf->body.cu->body.hll_config->num_attr_ref);
604
0
            break;
605
0
        case MVM_OP_getattrref_s:
606
0
        case MVM_OP_getattrsref_s:
607
0
            create_facts_with_type(tc, g,
608
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
609
0
                g->sf->body.cu->body.hll_config->str_attr_ref);
610
0
            break;
611
0
        case MVM_OP_atposref_i:
612
0
            create_facts_with_type(tc, g,
613
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
614
0
                g->sf->body.cu->body.hll_config->int_pos_ref);
615
0
            break;
616
0
        case MVM_OP_atposref_n:
617
0
            create_facts_with_type(tc, g,
618
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
619
0
                g->sf->body.cu->body.hll_config->num_pos_ref);
620
0
            break;
621
0
        case MVM_OP_atposref_s:
622
0
            create_facts_with_type(tc, g,
623
0
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
624
0
                g->sf->body.cu->body.hll_config->str_pos_ref);
625
0
            break;
626
0
627
78.8k
        case MVM_OP_const_i64:
628
78.8k
        case MVM_OP_const_i32:
629
78.8k
        case MVM_OP_const_i16:
630
78.8k
        case MVM_OP_const_i8:
631
78.8k
        case MVM_OP_const_n64:
632
78.8k
        case MVM_OP_const_n32:
633
78.8k
        case MVM_OP_const_i64_32:
634
78.8k
        case MVM_OP_const_i64_16:
635
78.8k
        case MVM_OP_const_s:
636
78.8k
            literal_facts(tc, g, ins);
637
78.8k
            break;
638
78.5k
        case MVM_OP_sp_log: {
639
78.5k
            MVMuint16 po = ins->prev
640
47.1k
                ? ins->prev->info->opcode
641
31.3k
                : bb->pred[0]->last_ins->info->opcode;
642
78.5k
            if (po != MVM_OP_getlexstatic_o && po != MVM_OP_getlexperinvtype_o)
643
61.0k
                log_facts(tc, g, bb, ins);
644
78.5k
            break;
645
78.8k
        }
646
1.30M
        default:
647
1.30M
            if (ins->info->opcode == (MVMuint16)-1)
648
0
                discover_extop(tc, g, ins);
649
1.80M
        }
650
1.80M
        ins = ins->next;
651
1.80M
    }
652
263k
653
263k
    /* Visit children. */
654
513k
    for (i = 0; i < bb->num_children; i++)
655
249k
        add_bb_facts(tc, g, bb->children[i], cur_deopt_idx);
656
263k
}
657
658
/* Exception handlers that use a block to store the handler must not have the
659
 * instructions that install the block eliminated. This tweaks the usage of
660
 * them. */
661
13.8k
static void tweak_block_handler_usage(MVMThreadContext *tc, MVMSpeshGraph *g) {
662
13.8k
    MVMint32 i;
663
18.2k
    for (i = 0; i < g->sf->body.num_handlers; i++) {
664
4.35k
        if (g->sf->body.handlers[i].action == MVM_EX_ACTION_INVOKE)
665
7
            g->facts[g->sf->body.handlers[i].block_reg][1].usages++;
666
4.35k
    }
667
13.8k
}
668
669
/* Kicks off fact discovery from the top of the (dominator) tree. */
670
13.8k
void MVM_spesh_facts_discover(MVMThreadContext *tc, MVMSpeshGraph *g) {
671
13.8k
    allocate_log_guard_table(tc, g);
672
13.8k
    add_bb_facts(tc, g, g->entry, -1);
673
13.8k
    tweak_block_handler_usage(tc, g);
674
13.8k
}