Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/spesh/arg_guard.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Calculates the maxium number of new nodes that might be needed to add a
4
 * guard for the specified callsite and types. (It may be less in reality
5
 * due to head sharing.) */
6
43.2k
static size_t max_new_nodes(MVMCallsite *cs, MVMSpeshStatsType *types) {
7
43.2k
    size_t needed = 2; /* One for callsite, one for result */
8
43.2k
    if (types) {
9
39.1k
        MVMuint32 i;
10
116k
        for (i = 0; i < cs->flag_count; i++) {
11
76.8k
            if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) {
12
60.5k
                if (types[i].type)
13
60.5k
                    needed += 2; /* One to read arg, one to check */
14
60.5k
                if (types[i].rw_cont)
15
0
                    needed++;
16
60.5k
                if (types[i].decont_type)
17
0
                    needed += 2; /* One to decont, one to check */
18
60.5k
            }
19
76.8k
        }
20
39.1k
    }
21
43.2k
    return needed + 1;
22
43.2k
}
23
24
/* Allocates a new set of spesh argument guards extended by the extras amount
25
 * of nodes specified. Copies the original argument guards into it. */
26
static MVMSpeshArgGuard * copy_and_extend(MVMThreadContext *tc, MVMSpeshArgGuard *orig,
27
43.2k
                                          size_t extra) {
28
24.7k
    size_t orig_nodes = orig ? orig->used_nodes : 0;
29
43.2k
    size_t total_nodes = orig_nodes + extra;
30
43.2k
    size_t node_size = total_nodes * sizeof(MVMSpeshArgGuardNode);
31
43.2k
    size_t size = sizeof(MVMSpeshArgGuard) + node_size;
32
43.2k
    MVMSpeshArgGuard *copy = MVM_fixed_size_alloc(tc, tc->instance->fsa, size);
33
43.2k
    copy->nodes = (MVMSpeshArgGuardNode *)((char *)copy + sizeof(MVMSpeshArgGuard));
34
43.2k
    copy->used_nodes = orig_nodes;
35
43.2k
    copy->num_nodes = total_nodes;
36
43.2k
    if (orig_nodes)
37
24.7k
        memcpy(copy->nodes, orig->nodes, orig_nodes * sizeof(MVMSpeshArgGuardNode));
38
43.2k
    return copy;
39
43.2k
}
40
41
/* Locates an existing node that matches a particular callsite. If there is
42
 * no such node, adds it. */
43
43.2k
static MVMuint32 get_callsite_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMCallsite *cs) {
44
43.2k
    MVMuint32 have_fixup_node = 0;
45
43.2k
    MVMuint32 fixup_node;
46
43.2k
    if (ag->used_nodes) {
47
24.7k
        MVMuint32 current_node = 0;
48
27.0k
        do {
49
27.0k
            MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]);
50
27.0k
            if (agn->op == MVM_SPESH_GUARD_OP_CALLSITE) {
51
27.0k
                /* If it matches, we've found it. */
52
27.0k
                if (agn->cs == cs)
53
23.4k
                    return current_node;
54
27.0k
55
27.0k
                /* Otherwise, treat this as the working fixup node, and take
56
27.0k
                 * the no branch. */
57
3.67k
                fixup_node = current_node;
58
3.67k
                have_fixup_node = 1;
59
3.67k
                current_node = agn->no;
60
3.67k
            }
61
0
            else {
62
0
                /* We only expect callsite nodes at the top level. */
63
0
                MVM_panic(1, "Spesh arg guard: unexpected callsite structure in tree");
64
0
            }
65
3.67k
        } while (current_node != 0);
66
24.7k
    }
67
43.2k
68
43.2k
    /* If we get here, we need to add a node for this callsite. */
69
19.7k
    ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_CALLSITE;
70
19.7k
    ag->nodes[ag->used_nodes].cs = cs;
71
19.7k
    ag->nodes[ag->used_nodes].yes = 0;
72
19.7k
    ag->nodes[ag->used_nodes].no = 0;
73
19.7k
    if (have_fixup_node)
74
1.29k
        ag->nodes[fixup_node].no = ag->used_nodes;
75
19.7k
    return ag->used_nodes++;
76
43.2k
}
77
78
/* Resolves or inserts the argument load node. This is a little complex, in
79
 * that we may (though it should be quite unusual) have multiple starting
80
 * points in the argument list to consider. For example, there may be for
81
 * ($obj, $obj) specializations of (Foo, <no guard>) and (<no guard>, Foo).
82
 * In that case, we tweak the previous tree(s) of other starting points so
83
 * any "no result" points to instead try the added subtree. */
84
static MVMuint32 get_load_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMuint32 base_node,
85
60.5k
                               MVMuint16 arg_idx) {
86
60.5k
    MVMuint16 new_no = 0;
87
60.5k
    if (ag->nodes[base_node].yes) {
88
33.1k
        MVMuint32 check_node = ag->nodes[base_node].yes;
89
33.1k
        MVMSpeshArgGuardOp op = ag->nodes[check_node].op;
90
33.1k
        if (op == MVM_SPESH_GUARD_OP_LOAD_ARG) {
91
33.1k
            if (ag->nodes[check_node].arg_index == arg_idx)
92
33.1k
                return check_node;
93
0
            MVM_panic(1, "Spesh arg guard: unimplemented sparse guard case");
94
0
        }
95
0
        else if (op == MVM_SPESH_GUARD_OP_RESULT) {
96
0
            new_no = check_node;
97
0
        }
98
0
        else {
99
0
            MVM_panic(1, "Spesh arg guard: unexpected op %d in get_load_node", op);
100
0
        }
101
33.1k
    }
102
60.5k
103
60.5k
    /* If we get here, need to add a new load node. */
104
27.3k
    ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_LOAD_ARG;
105
27.3k
    ag->nodes[ag->used_nodes].arg_index = arg_idx;
106
27.3k
    ag->nodes[ag->used_nodes].yes = 0;
107
27.3k
    ag->nodes[ag->used_nodes].no = new_no;
108
27.3k
    ag->nodes[base_node].yes = ag->used_nodes;
109
27.3k
    return ag->used_nodes++;
110
60.5k
}
111
112
/* Resolves or inserts a node for testing the curernt type loaded into the
113
 * test buffer. If it needs to insert a new node, it chains it on to the
114
 * end of the existing set of type tests. */
115
static MVMuint32 get_type_check_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
116
60.5k
                                     MVMuint32 base_node, MVMObject *type, MVMuint8 concrete) {
117
60.5k
    MVMuint32 current_node = ag->nodes[base_node].yes;
118
60.5k
    MVMuint32 have_fixup_node = 0;
119
60.5k
    MVMuint32 fixup_node;
120
66.0k
    while (current_node != 0) {
121
36.4k
        MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]);
122
36.4k
        if (agn->op == MVM_SPESH_GUARD_OP_STABLE_CONC) {
123
31.7k
            /* If it matches, we've found it. */
124
31.7k
            if (concrete && agn->st == type->st)
125
26.9k
                return current_node;
126
31.7k
127
31.7k
             /* Otherwise, treat this as the working fixup node, and take
128
31.7k
             * the no branch. */
129
4.72k
            fixup_node = current_node;
130
4.72k
            have_fixup_node = 1;
131
4.72k
            current_node = agn->no;
132
4.72k
        }
133
4.72k
        else if (agn->op == MVM_SPESH_GUARD_OP_STABLE_TYPE) {
134
4.72k
            /* If it matches, we've found it. */
135
4.72k
            if (!concrete && agn->st == type->st)
136
3.99k
                return current_node;
137
4.72k
138
4.72k
             /* Otherwise, treat this as the working fixup node, and take
139
4.72k
             * the no branch. */
140
734
            fixup_node = current_node;
141
734
            have_fixup_node = 1;
142
734
            current_node = agn->no;
143
734
        }
144
0
        else {
145
0
            /* We only expect type matching nodes at the top level. */
146
0
            MVM_panic(1, "Spesh arg guard: unexpected type structure in tree");
147
0
        }
148
36.4k
    }
149
60.5k
150
60.5k
    /* If we get here, we need to add a node for this callsite. */
151
29.5k
    ag->nodes[ag->used_nodes].op = concrete
152
26.4k
        ? MVM_SPESH_GUARD_OP_STABLE_CONC
153
3.16k
        : MVM_SPESH_GUARD_OP_STABLE_TYPE;
154
29.5k
    ag->nodes[ag->used_nodes].st = type->st;
155
29.5k
    ag->nodes[ag->used_nodes].yes = 0;
156
29.5k
    ag->nodes[ag->used_nodes].no = 0;
157
29.5k
    if (have_fixup_node)
158
2.19k
        ag->nodes[fixup_node].no = ag->used_nodes;
159
29.5k
    else
160
27.3k
        ag->nodes[base_node].yes = ag->used_nodes;
161
29.5k
    return ag->used_nodes++;
162
60.5k
}
163
164
/* Resolves or inserts a guard for "is this an rw container" hanging off the
165
 * specified base node. We will always have the rw-or-not check right after
166
 * the container type check, so if there's already a "yes" branch off the base
167
 * node that is not an rw container check, we'll add the rw container check in
168
 * its place and attach the "no" branch to where it used to point. This means
169
 * we can know if there is such a node for this container type by just looking
170
 * at the "yes" branch of the base node we are passed. */
171
static MVMuint32 get_rw_cont_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
172
0
                                  MVMuint32 base_node) {
173
0
    MVMuint32 yes_node = ag->nodes[base_node].yes;
174
0
    if (yes_node && ag->nodes[yes_node].op == MVM_SPESH_GUARD_OP_DEREF_RW)
175
0
        return yes_node;
176
0
    ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_DEREF_RW;
177
0
    ag->nodes[ag->used_nodes].offset = 0; /* TODO populate this properly */
178
0
    ag->nodes[ag->used_nodes].yes = 0;
179
0
    ag->nodes[ag->used_nodes].no = yes_node;
180
0
    ag->nodes[base_node].yes = ag->used_nodes;
181
0
    return ag->used_nodes++;
182
0
}
183
184
/* Resolves or inserts a guard op that decontainerizes the current test
185
 * register content. We only do this once before a possible chain of nodes
186
 * that test the decontainerized type. Therefore, we can expect that such a
187
 * node is already in the tree at this point, *or* that there is an RW
188
 * guard node and *then* the one we're looking for. */
189
static MVMuint32 get_decont_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
190
0
                                 MVMuint32 base_node) {
191
0
    MVMuint32 check_node = ag->nodes[base_node].yes;
192
0
    MVMuint32 update_no_node = 0;
193
0
    if (check_node) {
194
0
        if (ag->nodes[check_node].op == MVM_SPESH_GUARD_OP_DEREF_VALUE) {
195
0
            return check_node;
196
0
        }
197
0
        else if (ag->nodes[check_node].op == MVM_SPESH_GUARD_OP_DEREF_RW) {
198
0
            MVMuint32 no_node = ag->nodes[check_node].no;
199
0
            if (no_node) {
200
0
                if (ag->nodes[no_node].op == MVM_SPESH_GUARD_OP_DEREF_VALUE)
201
0
                    return no_node;
202
0
            }
203
0
            else {
204
0
                update_no_node = check_node;
205
0
            }
206
0
        }
207
0
        if (!update_no_node)
208
0
            MVM_panic(1, "Spesh arg guard: unexpected tree structure adding deref value");
209
0
    }
210
0
    ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_DEREF_VALUE;
211
0
    ag->nodes[ag->used_nodes].offset = 0; /* TODO populate this properly */
212
0
    ag->nodes[ag->used_nodes].yes = 0;
213
0
    ag->nodes[ag->used_nodes].no = 0;
214
0
    if (update_no_node)
215
0
        ag->nodes[update_no_node].no = ag->used_nodes;
216
0
    else
217
0
        ag->nodes[base_node].yes = ag->used_nodes;
218
0
    return ag->used_nodes++;
219
0
}
220
221
/* Resolves or inserts guards for the specified type information, rooted off
222
 * the given node. */
223
static MVMuint32 get_type_node(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMuint32 base_node,
224
60.5k
                               MVMSpeshStatsType *type, MVMuint16 arg_idx) {
225
60.5k
    MVMuint32 current_node = get_load_node(tc, ag, base_node, arg_idx);
226
60.5k
    current_node = get_type_check_node(tc, ag, current_node, type->type, type->type_concrete);
227
60.5k
    if (type->rw_cont)
228
0
        current_node = get_rw_cont_node(tc, ag, current_node);
229
60.5k
    if (type->decont_type) {
230
0
        current_node = get_decont_node(tc, ag, current_node);
231
0
        current_node = get_type_check_node(tc, ag, current_node, type->decont_type,
232
0
            type->decont_type_concrete);
233
0
    }
234
60.5k
    return current_node;
235
60.5k
}
236
237
/* Inserts a guard for the specified types into the tree. */
238
static MVMint32 try_add_guard(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMCallsite *cs,
239
43.2k
                              MVMSpeshStatsType *types, MVMuint32 candidate) {
240
43.2k
    MVMuint32 current_node = get_callsite_node(tc, ag, cs);
241
43.2k
    if (types) {
242
39.1k
        /* We're adding a type-based result, and thus for a speculative
243
39.1k
         * specialization. Certain specializations come ahead of those, and
244
39.1k
         * hang off the callsite node; skip over any such node. */
245
39.1k
        MVMuint16 arg_idx = 0;
246
39.1k
        MVMuint16 i;
247
39.1k
        if (ag->nodes[ag->nodes[current_node].yes].op == MVM_SPESH_GUARD_OP_CERTAIN_RESULT)
248
732
            current_node = ag->nodes[current_node].yes;
249
116k
        for (i = 0; i < cs->flag_count; i++) {
250
76.8k
            if (cs->arg_flags[i] & MVM_CALLSITE_ARG_NAMED)
251
2.85k
                arg_idx++; /* Skip over name */
252
76.8k
            if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) {
253
60.5k
                MVMSpeshStatsType *type = &(types[i]);
254
60.5k
                if (type->type)
255
60.5k
                    current_node = get_type_node(tc, ag, current_node, type, arg_idx);
256
60.5k
            }
257
76.8k
            arg_idx++;
258
76.8k
        }
259
39.1k
        if (ag->nodes[current_node].yes)
260
18.3k
            return 0;
261
20.7k
        ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_RESULT;
262
20.7k
        ag->nodes[ag->used_nodes].yes = 0;
263
20.7k
        ag->nodes[ag->used_nodes].no = 0;
264
20.7k
    }
265
4.02k
    else {
266
4.02k
        /* We're adding a certain result. If there already is such a node, we
267
4.02k
         * already have that specialization. Otherwise, we need to insert it
268
4.02k
         * and redirect the current_node's .yes to point to it, and it to
269
4.02k
         * point to whatever current_node's .yes used to point to (so it goes
270
4.02k
         * in ahead of type guards etc.). */
271
4.02k
        if (ag->nodes[ag->nodes[current_node].yes].op == MVM_SPESH_GUARD_OP_CERTAIN_RESULT)
272
2.29k
            return 0;
273
1.72k
        ag->nodes[ag->used_nodes].op = MVM_SPESH_GUARD_OP_CERTAIN_RESULT;
274
1.72k
        ag->nodes[ag->used_nodes].yes = ag->nodes[current_node].yes;
275
1.72k
        ag->nodes[ag->used_nodes].no = 0;
276
1.72k
    }
277
22.5k
    ag->nodes[ag->used_nodes].result = candidate;
278
22.5k
    ag->nodes[current_node].yes = ag->used_nodes++;
279
22.5k
    return 1;
280
43.2k
}
281
282
/* Takes a pointer to a guard set. Replaces it with a guard set that also
283
 * includes a guard for the specified type tuple (passed with callsite to
284
 * know how many types are involved), and resolving to the specified spesh
285
 * candidate index. Any previous guard set will be scheduled for freeing at
286
 * the next safepoint. */
287
void MVM_spesh_arg_guard_add(MVMThreadContext *tc, MVMSpeshArgGuard **orig,
288
                             MVMCallsite *cs, MVMSpeshStatsType *types,
289
11.0k
                             MVMuint32 candidate) {
290
11.0k
    MVMSpeshArgGuard *new_guard = copy_and_extend(tc, *orig, max_new_nodes(cs, types));
291
11.0k
    if (!try_add_guard(tc, new_guard, cs, types, candidate))
292
0
        MVM_panic(1, "Spesh arg guard: trying to add duplicate result for same guard");
293
11.0k
    if (*orig) {
294
2.29k
        MVMSpeshArgGuard *prev = *orig;
295
2.29k
        *orig = new_guard;
296
2.29k
        MVM_spesh_arg_guard_destroy(tc, prev, 1);
297
2.29k
    }
298
8.73k
    else {
299
8.73k
        *orig = new_guard;
300
8.73k
    }
301
11.0k
}
302
303
/* Checks if we already have a guard that precisely matches the specified
304
 * pair of callsite and type tuple. This is a more exact check that "would
305
 * the guard match", since a less precise specialization would match if we
306
 * just ran the guard tree against the arguments. This answers the question of
307
 * "if I added this, would it collide with an existing entry" instead. */
308
MVMint32 MVM_spesh_arg_guard_exists(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
309
32.1k
                                    MVMCallsite *cs, MVMSpeshStatsType *types) {
310
32.1k
    MVMSpeshArgGuard *try_guard = copy_and_extend(tc, ag, max_new_nodes(cs, types));
311
32.1k
    MVMint32 exists = !try_add_guard(tc, try_guard, cs, types, 0);
312
32.1k
    MVM_spesh_arg_guard_destroy(tc, try_guard, 0);
313
32.1k
    return exists;
314
32.1k
}
315
316
/* Runs the guard against a type tuple, which is used primarily for detecting
317
 * if an existing specialization already exists. Returns the index of that
318
 * specialization, or -1 if there is no match. */
319
MVMint32 MVM_spesh_arg_guard_run_types(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
320
14.8k
                                        MVMCallsite *cs, MVMSpeshStatsType *types) {
321
14.8k
    MVMuint32 current_node = 0;
322
14.8k
    MVMSpeshStatsType *test = NULL;
323
14.8k
    MVMuint32 use_decont_type = 0;
324
14.8k
    MVMint32 current_result = -1;
325
14.8k
    if (!ag)
326
4.07k
        return -1;
327
49.8k
    do {
328
49.8k
        MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]);
329
49.8k
        switch (agn->op) {
330
11.3k
            case MVM_SPESH_GUARD_OP_CALLSITE:
331
10.5k
                current_node = agn->cs == cs ? agn->yes : agn->no;
332
11.3k
                break;
333
13.5k
            case MVM_SPESH_GUARD_OP_LOAD_ARG: {
334
13.5k
                test = &(types[agn->arg_index < cs->num_pos
335
13.4k
                    ? agn->arg_index
336
150
                    : cs->num_pos + (((agn->arg_index - 1) - cs->num_pos) / 2)]);
337
13.5k
                use_decont_type = 0;
338
13.5k
                current_node = agn->yes;
339
13.5k
                break;
340
13.6k
            case MVM_SPESH_GUARD_OP_STABLE_CONC:
341
13.6k
                if (use_decont_type)
342
0
                    current_node = test->decont_type_concrete && test->decont_type &&
343
0
                            test->decont_type->st == agn->st
344
0
                        ? agn->yes
345
0
                        : agn->no;
346
13.6k
                else
347
13.6k
                    current_node = test->type_concrete && test->type &&
348
13.6k
                            test->type->st == agn->st
349
12.5k
                        ? agn->yes
350
1.10k
                        : agn->no;
351
13.6k
                break;
352
693
            case MVM_SPESH_GUARD_OP_STABLE_TYPE:
353
693
                if (use_decont_type)
354
0
                    current_node = !test->decont_type_concrete && test->decont_type &&
355
0
                            test->decont_type->st == agn->st
356
0
                        ? agn->yes
357
0
                        : agn->no;
358
693
                else
359
693
                    current_node = !test->type_concrete && test->type &&
360
690
                            test->type->st == agn->st
361
661
                        ? agn->yes
362
32
                        : agn->no;
363
693
                break;
364
0
            case MVM_SPESH_GUARD_OP_DEREF_VALUE:
365
0
                if (test->decont_type) {
366
0
                    use_decont_type = 1;
367
0
                    current_node = agn->yes;
368
0
                }
369
0
                else {
370
0
                    current_node = agn->no;
371
0
                }
372
0
                break;
373
11.3k
            }
374
0
            case MVM_SPESH_GUARD_OP_DEREF_RW:
375
0
                current_node = test->rw_cont
376
0
                    ? agn->yes
377
0
                    : agn->no;
378
0
                break;
379
346
            case MVM_SPESH_GUARD_OP_CERTAIN_RESULT:
380
346
                current_result = agn->result;
381
346
                current_node = agn->yes;
382
346
                break;
383
10.2k
            case MVM_SPESH_GUARD_OP_RESULT:
384
10.2k
                return agn->result;
385
49.8k
        }
386
39.6k
    } while (current_node != 0);
387
607
    return current_result;
388
10.8k
}
389
390
/* Evaluates the argument guards. Returns >= 0 if there is a matching spesh
391
 * candidate, or -1 if there is not. */
392
MVMint32 MVM_spesh_arg_guard_run(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
393
                                 MVMCallsite *cs, MVMRegister *args,
394
15.4M
                                 MVMint32 *certain) {
395
15.4M
    MVMuint32 current_node = 0;
396
15.4M
    MVMObject *test = NULL;
397
15.4M
    MVMint32 current_result = -1;
398
15.4M
    if (!ag)
399
5.81M
        return -1;
400
43.7M
    do {
401
43.7M
        MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]);
402
43.7M
        switch (agn->op) {
403
11.0M
            case MVM_SPESH_GUARD_OP_CALLSITE:
404
7.44M
                current_node = agn->cs == cs ? agn->yes : agn->no;
405
11.0M
                break;
406
10.6M
            case MVM_SPESH_GUARD_OP_LOAD_ARG:
407
10.6M
                test = args[agn->arg_index].o;
408
10.6M
                current_node = agn->yes;
409
10.6M
                break;
410
10.6M
            case MVM_SPESH_GUARD_OP_STABLE_CONC:
411
10.6M
                current_node = IS_CONCRETE(test) && test->st == agn->st
412
8.60M
                    ? agn->yes
413
2.09M
                    : agn->no;
414
10.6M
                break;
415
3.86M
            case MVM_SPESH_GUARD_OP_STABLE_TYPE:
416
3.86M
                current_node = !IS_CONCRETE(test) && test->st == agn->st
417
1.78M
                    ? agn->yes
418
2.08M
                    : agn->no;
419
3.86M
                break;
420
0
            case MVM_SPESH_GUARD_OP_DEREF_VALUE: {
421
0
                /* TODO Use offset approach later to avoid these calls. */
422
0
                MVMRegister dc;
423
0
                test->st->container_spec->fetch(tc, test, &dc);
424
0
                test = dc.o;
425
0
                current_node = test ? agn->yes : agn->no;
426
0
                break;
427
11.0M
            }
428
0
            case MVM_SPESH_GUARD_OP_DEREF_RW:
429
0
                /* TODO Use offset approach later to avoid these calls. */
430
0
                current_node = STABLE(test)->container_spec->can_store(tc, test)
431
0
                    ? agn->yes
432
0
                    : agn->no;
433
0
                break;
434
399k
            case MVM_SPESH_GUARD_OP_CERTAIN_RESULT:
435
399k
                current_result = agn->result;
436
399k
                if (certain)
437
0
                    *certain = agn->result;
438
399k
                current_node = agn->yes;
439
399k
                break;
440
7.01M
            case MVM_SPESH_GUARD_OP_RESULT:
441
7.01M
                return agn->result;
442
43.7M
        }
443
36.6M
    } while (current_node != 0);
444
2.66M
    return current_result;
445
9.67M
}
446
447
/* Runs the guards using call information gathered by the optimizer. This is
448
 * used for finding existing candidates to emit fast calls to or inline. */
449
MVMint32 MVM_spesh_arg_guard_run_callinfo(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
450
7.74k
                                          MVMSpeshCallInfo *arg_info) {
451
7.74k
    MVMuint32 current_node = 0;
452
7.74k
    MVMSpeshFacts *facts = NULL;
453
7.74k
    MVMuint8 use_decont_facts = 0;
454
7.74k
    MVMint32 current_result = -1;
455
7.74k
    if (!ag)
456
2.06k
        return -1;
457
22.3k
    do {
458
22.3k
        MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]);
459
22.3k
        switch (agn->op) {
460
6.68k
            case MVM_SPESH_GUARD_OP_CALLSITE:
461
4.98k
                current_node = agn->cs == arg_info->cs ? agn->yes : agn->no;
462
6.68k
                break;
463
5.73k
            case MVM_SPESH_GUARD_OP_LOAD_ARG:
464
5.73k
                if (agn->arg_index >= MAX_ARGS_FOR_OPT)
465
0
                    return -1;
466
5.73k
                facts = arg_info->arg_facts[agn->arg_index];
467
5.73k
                use_decont_facts = 0;
468
5.73k
                current_node = agn->yes;
469
5.73k
                break;
470
6.06k
            case MVM_SPESH_GUARD_OP_STABLE_CONC:
471
6.06k
                if (use_decont_facts) {
472
0
                    current_node = facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE &&
473
0
                            facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE &&
474
0
                            facts->decont_type->st == agn->st
475
0
                        ? agn->yes
476
0
                        : agn->no;
477
0
                }
478
6.06k
                else {
479
6.06k
                    current_node = facts->flags & MVM_SPESH_FACT_CONCRETE &&
480
4.32k
                            facts->flags & MVM_SPESH_FACT_KNOWN_TYPE &&
481
4.30k
                            facts->type->st == agn->st
482
3.52k
                        ? agn->yes
483
2.53k
                        : agn->no;
484
6.06k
                }
485
6.06k
                break;
486
466
            case MVM_SPESH_GUARD_OP_STABLE_TYPE:
487
466
                if (use_decont_facts) {
488
0
                    current_node = facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ &&
489
0
                            facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE &&
490
0
                            facts->decont_type->st == agn->st
491
0
                        ? agn->yes
492
0
                        : agn->no;
493
0
                }
494
466
                else {
495
466
                    current_node = facts->flags & MVM_SPESH_FACT_TYPEOBJ &&
496
284
                            facts->flags & MVM_SPESH_FACT_KNOWN_TYPE &&
497
284
                            facts->type->st == agn->st
498
284
                        ? agn->yes
499
182
                        : agn->no;
500
466
                }
501
466
                break;
502
0
            case MVM_SPESH_GUARD_OP_DEREF_VALUE: {
503
0
                if (facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
504
0
                    use_decont_facts = 1;
505
0
                    current_node = agn->yes;
506
0
                }
507
0
                else {
508
0
                    current_node = agn->no;
509
0
                }
510
0
                break;
511
5.73k
            }
512
0
            case MVM_SPESH_GUARD_OP_DEREF_RW:
513
0
                current_node = facts->flags & MVM_SPESH_FACT_RW_CONT
514
0
                    ? agn->yes
515
0
                    : agn->no;
516
0
                break;
517
1.24k
            case MVM_SPESH_GUARD_OP_CERTAIN_RESULT:
518
1.24k
                current_result = agn->result;
519
1.24k
                current_node = agn->yes;
520
1.24k
                break;
521
2.21k
            case MVM_SPESH_GUARD_OP_RESULT:
522
2.21k
                return agn->result;
523
22.3k
        }
524
20.1k
    } while (current_node != 0);
525
3.47k
    return current_result;
526
5.68k
}
527
528
/* Marks any objects held by an argument guard. */
529
void MVM_spesh_arg_guard_gc_mark(MVMThreadContext *tc, MVMSpeshArgGuard *ag,
530
11.1k
                                 MVMGCWorklist *worklist) {
531
11.1k
    if (ag) {
532
5.45k
        MVMuint32 i;
533
36.5k
        for (i = 0; i < ag->used_nodes; i++) {
534
31.1k
            switch (ag->nodes[i].op) {
535
9.47k
                case MVM_SPESH_GUARD_OP_STABLE_CONC:
536
9.47k
                case MVM_SPESH_GUARD_OP_STABLE_TYPE:
537
9.47k
                    MVM_gc_worklist_add(tc, worklist, &(ag->nodes[i].st));
538
9.47k
                    break;
539
31.1k
            }
540
31.1k
        }
541
5.45k
    }
542
11.1k
}
543
544
void MVM_spesh_arg_guard_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
545
0
                                     MVMSpeshArgGuard *ag) {
546
0
    if (ag) {
547
0
        MVMuint32 i;
548
0
        for (i = 0; i < ag->used_nodes; i++) {
549
0
            switch (ag->nodes[i].op) {
550
0
                case MVM_SPESH_GUARD_OP_STABLE_CONC:
551
0
                case MVM_SPESH_GUARD_OP_STABLE_TYPE:
552
0
                    MVM_profile_heap_add_collectable_rel_idx(tc, ss,
553
0
                        (MVMCollectable*)(ag->nodes[i].st), i);
554
0
                    break;
555
0
            }
556
0
        }
557
0
    }
558
0
}
559
560
/* Frees the memory associated with an argument guard. If `safe` is set to a
561
 * non-zero value then the memory is freed at the next safepoint. If it is set
562
 * to zero, the memory is freed immediately. */
563
34.4k
void MVM_spesh_arg_guard_destroy(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMuint32 safe) {
564
34.4k
    if (ag) {
565
34.4k
        size_t total_size = sizeof(MVMSpeshArgGuard) +
566
34.4k
            ag->num_nodes * sizeof(MVMSpeshArgGuardNode);
567
34.4k
        if (safe)
568
2.29k
            MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa, total_size, ag);
569
34.4k
        else
570
32.1k
            MVM_fixed_size_free(tc, tc->instance->fsa, total_size, ag);
571
34.4k
    }
572
34.4k
}
573
574
/* Discards an arg guard held on a static frame, if any, NULLing it out so the
575
 * candidates will no longer be reachable. */
576
0
void MVM_spesh_arg_guard_discard(MVMThreadContext *tc, MVMStaticFrame *sf) {
577
0
    MVMStaticFrameSpesh *spesh = sf->body.spesh;
578
0
    if (spesh && spesh->body.spesh_arg_guard) {
579
0
        MVM_spesh_arg_guard_destroy(tc, spesh->body.spesh_arg_guard, 1);
580
0
        spesh->body.spesh_arg_guard = NULL;
581
0
    }
582
0
}