Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/spesh/plugin.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* The spesh plugin mechanism allows for extending spesh to be able to reason
4
 * about high-level language semantics that would be challenging to implement
5
 * inside of the specializer, perhaps because we don't communicate sufficient
6
 * information for it to do what is required. */
7
8
/* Registers a new spesh plugin. */
9
void MVM_spesh_plugin_register(MVMThreadContext *tc, MVMString *language,
10
12
        MVMString *name, MVMObject *plugin) {
11
12
    MVMHLLConfig *hll = MVM_hll_get_config_for(tc, language);
12
12
    uv_mutex_lock(&tc->instance->mutex_hllconfigs);
13
12
    if (!hll->spesh_plugins)
14
1
        hll->spesh_plugins = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
15
12
    MVM_repr_bind_key_o(tc, hll->spesh_plugins, name, plugin);
16
12
    uv_mutex_unlock(&tc->instance->mutex_hllconfigs);
17
12
}
18
19
/* Gets the spesh plugin state for the specified frame. */
20
95.7k
static MVMSpeshPluginState * get_plugin_state(MVMThreadContext *tc, MVMStaticFrame *sf) {
21
95.7k
    MVMStaticFrameSpesh *sfs = sf->body.spesh;
22
95.7k
    if (!sfs)
23
0
        MVM_panic(1, "Unexpectedly missing specialization state for static frame");
24
95.7k
    return sfs->body.plugin_state;
25
95.7k
}
26
27
/* Gets the guard set for a particular position. */
28
static MVMSpeshPluginGuardSet * guard_set_for_position(MVMThreadContext *tc,
29
95.7k
        MVMuint32 cur_position, MVMSpeshPluginState *ps) {
30
95.7k
    if (ps) {
31
95.6k
        MVMint32 l = 0;
32
95.6k
        MVMint32 r = ps->num_positions - 1;
33
95.6k
        while (l <= r) {
34
95.6k
            MVMint32 m = l + (r - l) / 2;
35
95.6k
            MVMuint32 test = ps->positions[m].bytecode_position;
36
95.6k
            if (test == cur_position) {
37
95.6k
                return ps->positions[m].guard_set;
38
95.6k
            }
39
10
            if (test < cur_position)
40
8
                l = m + 1;
41
10
            else
42
2
                r = m - 1;
43
10
        }
44
95.6k
    }
45
32
    return NULL;
46
95.7k
}
47
48
/* Looks through a guard set and returns any result that matches. */
49
static MVMObject * evaluate_guards(MVMThreadContext *tc, MVMSpeshPluginGuardSet *gs,
50
95.6k
        MVMCallsite *callsite, MVMuint16 *guard_offset) {
51
95.6k
    MVMuint32 pos = 0;
52
95.6k
    MVMuint32 end = gs->num_guards;
53
95.6k
    MVMRegister *args = tc->cur_frame->args;
54
95.6k
    MVMuint32 arg_end = callsite->flag_count;
55
95.6k
    MVMObject *collected_objects = tc->instance->VMNull;
56
197k
    while (pos < end) {
57
197k
        MVMuint16 kind = gs->guards[pos].kind;
58
197k
        if (kind == MVM_SPESH_PLUGIN_GUARD_RESULT) {
59
95.6k
            *guard_offset = pos;
60
95.6k
            return gs->guards[pos].u.result;
61
95.6k
        }
62
101k
        else {
63
101k
            MVMuint16 test_idx = gs->guards[pos].test_idx;
64
101k
            MVMObject *test = test_idx < arg_end
65
98.6k
                    ? args[test_idx].o
66
2.96k
                    : MVM_repr_at_pos_o(tc, collected_objects, test_idx - arg_end);
67
101k
            MVMuint32 outcome;
68
101k
            switch (kind) {
69
83.2k
                case MVM_SPESH_PLUGIN_GUARD_OBJ:
70
83.2k
                    outcome = test == gs->guards[pos].u.object;
71
83.2k
                    break;
72
713
                case MVM_SPESH_PLUGIN_GUARD_NOTOBJ:
73
713
                    outcome = test != gs->guards[pos].u.object;
74
713
                    break;
75
5.16k
                case MVM_SPESH_PLUGIN_GUARD_TYPE:
76
5.16k
                    outcome = STABLE(test) == gs->guards[pos].u.type;
77
5.16k
                    break;
78
5.48k
                case MVM_SPESH_PLUGIN_GUARD_CONC:
79
5.48k
                    outcome = IS_CONCRETE(test);
80
5.48k
                    break;
81
5.54k
                case MVM_SPESH_PLUGIN_GUARD_TYPEOBJ:
82
5.54k
                    outcome = !IS_CONCRETE(test);
83
5.54k
                    break;
84
1.48k
                case MVM_SPESH_PLUGIN_GUARD_GETATTR:
85
1.48k
                    if (MVM_is_null(tc, collected_objects))
86
1.47k
                        collected_objects = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
87
1.48k
                    MVMROOT(tc, collected_objects, {
88
1.48k
                        MVMObject *attr = MVM_repr_get_attr_o(tc, test,
89
1.48k
                                gs->guards[pos].u.attr.class_handle, gs->guards[pos].u.attr.name, MVM_NO_HINT);
90
1.48k
                        MVM_repr_push_o(tc, collected_objects, attr);
91
1.48k
                    });
92
1.48k
                    outcome = 1;
93
1.48k
                    break;
94
0
                default:
95
0
                    MVM_panic(1, "Guard kind unrecognized in spesh plugin guard set");
96
101k
            }
97
101k
            if (outcome) {
98
101k
                pos += 1;
99
101k
            }
100
49
            else {
101
49
                pos += gs->guards[pos].skip_on_fail;
102
49
                if (!MVM_is_null(tc, collected_objects))
103
19
                    MVM_repr_pos_set_elems(tc, collected_objects, 0);
104
49
            }
105
101k
        }
106
197k
    }
107
15
    return NULL;
108
95.6k
}
109
110
/* Tries to resolve a plugin by looking at the guards for the position. */
111
static MVMObject * resolve_using_guards(MVMThreadContext *tc, MVMuint32 cur_position,
112
95.6k
        MVMCallsite *callsite, MVMuint16 *guard_offset, MVMStaticFrame *sf) {
113
95.6k
    MVMSpeshPluginState *ps = get_plugin_state(tc, sf);
114
95.6k
    MVMSpeshPluginGuardSet *gs = guard_set_for_position(tc, cur_position, ps);
115
95.6k
    return gs ? evaluate_guards(tc, gs, callsite, guard_offset) : NULL;
116
95.6k
}
117
118
/* Produces an updated guard set with the given resolution result. Returns
119
 * the base guard set if we already having a matching guard (which means two
120
 * threads raced to do this resolution). */
121
13
static MVMint32 already_have_guard(MVMThreadContext *tc, MVMSpeshPluginGuardSet *base_guards) {
122
13
    return 0;
123
13
}
124
static MVMSpeshPluginGuardSet * append_guard(MVMThreadContext *tc,
125
        MVMSpeshPluginGuardSet *base_guards, MVMObject *resolved,
126
29
        MVMStaticFrameSpesh *barrier) {
127
29
    MVMSpeshPluginGuardSet *result;
128
29
    if (base_guards && already_have_guard(tc, base_guards)) {
129
0
        result = base_guards;
130
0
    }
131
29
    else {
132
29
        MVMuint32 insert_pos, i;
133
29
        result = MVM_fixed_size_alloc(tc, tc->instance->fsa, sizeof(MVMSpeshPluginGuardSet));
134
16
        result->num_guards = (base_guards ? base_guards->num_guards : 0) +
135
29
            tc->num_plugin_guards + 1; /* + 1 for result node */
136
29
        result->guards = MVM_fixed_size_alloc(tc, tc->instance->fsa,
137
29
                sizeof(MVMSpeshPluginGuard) * result->num_guards);
138
29
        if (base_guards) {
139
13
            memcpy(result->guards, base_guards->guards,
140
13
                    base_guards->num_guards * sizeof(MVMSpeshPluginGuard));
141
13
            insert_pos = base_guards->num_guards;
142
13
        }
143
16
        else {
144
16
            insert_pos = 0;
145
16
        }
146
84
        for (i = 0; i < tc->num_plugin_guards; i++) {
147
55
            result->guards[insert_pos].kind = tc->plugin_guards[i].kind;
148
55
            result->guards[insert_pos].test_idx = tc->plugin_guards[i].test_idx;
149
55
            result->guards[insert_pos].skip_on_fail = 1 + tc->num_plugin_guards - i;
150
55
            switch (tc->plugin_guards[i].kind) {
151
13
                case MVM_SPESH_PLUGIN_GUARD_OBJ:
152
13
                case MVM_SPESH_PLUGIN_GUARD_NOTOBJ:
153
13
                    MVM_ASSIGN_REF(tc, &(barrier->common.header),
154
13
                            result->guards[insert_pos].u.object,
155
13
                            tc->plugin_guards[i].u.object);
156
13
                    break;
157
18
                case MVM_SPESH_PLUGIN_GUARD_TYPE:
158
18
                    MVM_ASSIGN_REF(tc, &(barrier->common.header),
159
18
                            result->guards[insert_pos].u.type,
160
18
                            tc->plugin_guards[i].u.type);
161
18
                    break;
162
18
                case MVM_SPESH_PLUGIN_GUARD_CONC:
163
18
                case MVM_SPESH_PLUGIN_GUARD_TYPEOBJ:
164
18
                    /* These carry no extra argument. */
165
18
                    break;
166
6
                case MVM_SPESH_PLUGIN_GUARD_GETATTR:
167
6
                    MVM_ASSIGN_REF(tc, &(barrier->common.header),
168
6
                            result->guards[insert_pos].u.attr.class_handle,
169
6
                            tc->plugin_guards[i].u.attr.class_handle);
170
6
                    MVM_ASSIGN_REF(tc, &(barrier->common.header),
171
6
                            result->guards[insert_pos].u.attr.name,
172
6
                            tc->plugin_guards[i].u.attr.name);
173
6
                    break;
174
0
                default:
175
0
                    MVM_panic(1, "Unexpected spesh plugin guard type");
176
55
            }
177
55
            insert_pos++;
178
55
        }
179
29
        result->guards[insert_pos].kind = MVM_SPESH_PLUGIN_GUARD_RESULT;
180
29
        MVM_ASSIGN_REF(tc, &(barrier->common.header),
181
29
                result->guards[insert_pos].u.result, resolved);
182
29
    }
183
29
    return result;
184
29
}
185
186
/* Produces an updated spesh plugin state by adding the updated guards at
187
 * the specified position. */
188
MVMSpeshPluginState * updated_state(MVMThreadContext *tc, MVMSpeshPluginState *base_state,
189
        MVMuint32 position, MVMSpeshPluginGuardSet *base_guards,
190
29
        MVMSpeshPluginGuardSet *new_guards) {
191
29
    MVMSpeshPluginState *result = MVM_fixed_size_alloc(tc, tc->instance->fsa,
192
29
            sizeof(MVMSpeshPluginState));
193
15
    result->num_positions = (base_state ? base_state->num_positions : 0) +
194
16
        (base_guards == NULL ? 1 : 0);
195
29
    result->positions = MVM_fixed_size_alloc(tc, tc->instance->fsa,
196
29
            sizeof(MVMSpeshPluginPosition) * result->num_positions);
197
29
    if (base_state) {
198
15
        MVMuint32 copy_from = 0;
199
15
        MVMuint32 insert_at = 0;
200
15
        MVMuint32 inserted = 0;
201
31
        while (!inserted && copy_from < base_state->num_positions) {
202
16
            MVMuint32 bytecode_position = base_state->positions[copy_from].bytecode_position;
203
16
            if (bytecode_position < position) {
204
3
                result->positions[insert_at] = base_state->positions[copy_from];
205
3
                copy_from++;
206
3
                insert_at++;
207
3
            }
208
13
            else if (bytecode_position == position) {
209
13
                /* Update of existing state; copy those after this one. */
210
13
                result->positions[insert_at].bytecode_position = position;
211
13
                result->positions[insert_at].guard_set = new_guards;
212
13
                copy_from++;
213
13
                insert_at++;
214
13
                inserted = 1;
215
13
            }
216
0
            else {
217
0
                /* Insert a new position in order. */
218
0
                result->positions[insert_at].bytecode_position = position;
219
0
                result->positions[insert_at].guard_set = new_guards;
220
0
                insert_at++;
221
0
                inserted = 1;
222
0
            }
223
16
            if (inserted && copy_from < base_state->num_positions)
224
0
                memcpy(result->positions + insert_at, base_state->positions + copy_from,
225
0
                        (base_state->num_positions - copy_from) * sizeof(MVMSpeshPluginPosition));
226
16
        }
227
15
        if (!inserted) {
228
2
            result->positions[insert_at].bytecode_position = position;
229
2
            result->positions[insert_at].guard_set = new_guards;
230
2
        }
231
15
    }
232
14
    else {
233
14
        result->positions[0].bytecode_position = position;
234
14
        result->positions[0].guard_set = new_guards;
235
14
    }
236
29
    return result;
237
29
}
238
239
/* Schedules replaced guards to be freed. */
240
29
void free_dead_guards(MVMThreadContext *tc, MVMSpeshPluginGuardSet *gs) {
241
29
    if (gs) {
242
13
        MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa,
243
13
                gs->num_guards * sizeof(MVMSpeshPluginGuard), gs->guards);
244
13
        MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa,
245
13
                sizeof(MVMSpeshPluginGuardSet), gs);
246
13
    }
247
29
}
248
249
/* Schedules replaced state to be freed. */
250
29
void free_dead_state(MVMThreadContext *tc, MVMSpeshPluginState *ps) {
251
29
    if (ps) {
252
15
        MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa,
253
15
                ps->num_positions * sizeof(MVMSpeshPluginPosition), ps->positions);
254
15
        MVM_fixed_size_free_at_safepoint(tc, tc->instance->fsa,
255
15
                sizeof(MVMSpeshPluginState), ps);
256
15
    }
257
29
}
258
259
/* Data for use with special return mechanism to store the result of a
260
 * resolution. */
261
typedef struct {
262
    /* Current speshresolve we're handling. */
263
    MVMRegister *result;
264
    MVMStaticFrame *sf;
265
    MVMuint32 position;
266
267
    /* The speshresolve that was in dynamic scope when we started this one,
268
     * if any. */
269
    MVMSpeshPluginGuard *prev_plugin_guards;
270
    MVMObject *prev_plugin_guard_args;
271
    MVMuint32 prev_num_plugin_guards;
272
} MVMSpeshPluginSpecialReturnData;
273
274
/* Callback to mark the special return data. */
275
0
static void mark_plugin_sr_data(MVMThreadContext *tc, MVMFrame *frame, MVMGCWorklist *worklist) {
276
0
    MVMSpeshPluginSpecialReturnData *srd = (MVMSpeshPluginSpecialReturnData *)
277
0
        frame->extra->special_return_data;
278
0
    MVM_gc_worklist_add(tc, worklist, &(srd->sf));
279
0
    MVM_gc_worklist_add(tc, worklist, &(srd->prev_plugin_guard_args));
280
0
}
281
282
/* Restore the spesh plugin resolve state that was in dynamic scope when we
283
 * started this spesh resolve. */
284
31
static void restore_prev_spesh_plugin_state(MVMThreadContext *tc, MVMSpeshPluginSpecialReturnData *srd) {
285
31
    tc->plugin_guards = srd->prev_plugin_guards;
286
31
    tc->plugin_guard_args = srd->prev_plugin_guard_args;
287
31
    tc->num_plugin_guards = srd->prev_num_plugin_guards;
288
31
}
289
290
/* Callback after a resolver to update the guard structure. */
291
29
static void add_resolution_to_guard_set(MVMThreadContext *tc, void *sr_data) {
292
29
    MVMSpeshPluginSpecialReturnData *srd = (MVMSpeshPluginSpecialReturnData *)sr_data;
293
29
    MVMSpeshPluginState *base_state = get_plugin_state(tc, srd->sf);
294
29
    MVMSpeshPluginGuardSet *base_guards = guard_set_for_position(tc, srd->position, base_state);
295
29
    MVMSpeshPluginGuardSet *new_guards = append_guard(tc, base_guards, srd->result->o,
296
29
            srd->sf->body.spesh);
297
29
    if (new_guards != base_guards) {
298
29
        MVMSpeshPluginState *new_state = updated_state(tc, base_state, srd->position,
299
29
                base_guards, new_guards);
300
29
        MVMuint32 committed = MVM_trycas(
301
29
                &srd->sf->body.spesh->body.plugin_state,
302
29
                base_state, new_state);
303
29
        if (committed) {
304
29
            free_dead_guards(tc, base_guards);
305
29
            free_dead_state(tc, base_state);
306
29
        }
307
0
        else {
308
0
            /* Lost update race. Discard; a retry will happen naturally. */
309
0
            free_dead_guards(tc, new_guards);
310
0
            free_dead_state(tc, new_state);
311
0
        }
312
29
    }
313
29
314
29
    /* Clear up recording state. */
315
29
    MVM_fixed_size_free(tc, tc->instance->fsa,
316
29
            MVM_SPESH_PLUGIN_GUARD_LIMIT * sizeof(MVMSpeshPluginGuard),
317
29
            tc->plugin_guards);
318
29
    restore_prev_spesh_plugin_state(tc, srd);
319
29
320
29
    MVM_free(srd);
321
29
}
322
323
/* When we throw an exception when evaluating a resolver, then clean up any
324
 * recorded guards so as not to leak memory. */
325
2
static void cleanup_recorded_guards(MVMThreadContext *tc, void *sr_data) {
326
2
    MVMSpeshPluginSpecialReturnData *srd = (MVMSpeshPluginSpecialReturnData *)sr_data;
327
2
    MVM_fixed_size_free(tc, tc->instance->fsa,
328
2
            MVM_SPESH_PLUGIN_GUARD_LIMIT * sizeof(MVMSpeshPluginGuard),
329
2
            tc->plugin_guards);
330
2
    restore_prev_spesh_plugin_state(tc, srd);
331
2
    MVM_free(srd);
332
2
}
333
334
/* Sets up state in the current thread context for recording guards. */
335
31
static void setup_for_guard_recording(MVMThreadContext *tc, MVMCallsite *callsite) {
336
31
    /* Validate the callsite meets restrictions. */
337
31
    MVMuint32 i = 0;
338
31
    if (callsite->num_pos != callsite->flag_count)
339
0
        MVM_exception_throw_adhoc(tc, "A spesh plugin must have only positional args");
340
31
    if (callsite->has_flattening)
341
0
        MVM_exception_throw_adhoc(tc, "A spesh plugin must not have flattening args");
342
61
    for (i = 0; i < callsite->flag_count; i++)
343
30
        if (callsite->arg_flags[i] != MVM_CALLSITE_ARG_OBJ)
344
0
            MVM_exception_throw_adhoc(tc, "A spesh plugin must only be passed object args");
345
31
346
31
    /* Set up guard recording space and arguments array. */
347
31
    tc->plugin_guards = MVM_fixed_size_alloc(tc, tc->instance->fsa,
348
31
            MVM_SPESH_PLUGIN_GUARD_LIMIT * sizeof(MVMSpeshPluginGuard));
349
31
    tc->num_plugin_guards = 0;
350
31
    tc->plugin_guard_args = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
351
61
    for (i = 0; i < callsite->flag_count; i++)
352
30
        MVM_repr_push_o(tc, tc->plugin_guard_args, tc->cur_frame->args[i].o);
353
31
}
354
355
/* Resolves a spesh plugin for the current HLL from the slow-path interpreter. */
356
static void call_resolver(MVMThreadContext *tc, MVMString *name, MVMRegister *result,
357
                          MVMuint32 position, MVMStaticFrame *sf, MVMuint8 *next_addr,
358
31
                          MVMCallsite *callsite) {
359
31
    /* No pre-resolved value, so we need to run the plugin. Capture state
360
31
     * of any ongoing spesh plugin resolve. */
361
31
    MVMSpeshPluginGuard *prev_plugin_guards = tc->plugin_guards;
362
31
    MVMObject *prev_plugin_guard_args = tc->plugin_guard_args;
363
31
    MVMuint32 prev_num_plugin_guards = tc->num_plugin_guards;
364
31
365
31
    /* Find the plugin. */
366
31
    MVMSpeshPluginSpecialReturnData *srd;
367
31
    MVMObject *plugin = NULL;
368
31
    MVMHLLConfig *hll = MVM_hll_current(tc);
369
31
    uv_mutex_lock(&tc->instance->mutex_hllconfigs);
370
31
    if (hll->spesh_plugins)
371
31
        plugin = MVM_repr_at_key_o(tc, hll->spesh_plugins, name);
372
31
    uv_mutex_unlock(&tc->instance->mutex_hllconfigs);
373
31
    if (MVM_is_null(tc, plugin)) {
374
0
        char *c_name = MVM_string_utf8_encode_C_string(tc, name);
375
0
        char *waste[] = { c_name, NULL };
376
0
        MVM_exception_throw_adhoc_free(tc, waste,
377
0
            "No such spesh plugin '%s' for current language",
378
0
            c_name);
379
0
    }
380
31
381
31
    /* Set up the guard state to record into. */
382
31
    MVMROOT2(tc, plugin, prev_plugin_guard_args, {
383
31
        setup_for_guard_recording(tc, callsite);
384
31
    });
385
31
386
31
    /* Run it, registering handlers to save or discard guards and result. */
387
31
    tc->cur_frame->return_value = result;
388
31
    tc->cur_frame->return_type = MVM_RETURN_OBJ;
389
31
    if (next_addr)
390
31
        tc->cur_frame->return_address = next_addr; /* JIT sets this otherwise */
391
31
    srd = MVM_malloc(sizeof(MVMSpeshPluginSpecialReturnData));
392
31
    srd->result = result;
393
31
    srd->position = position;
394
31
    srd->sf = tc->cur_frame->static_info;
395
31
    srd->prev_plugin_guards = prev_plugin_guards;
396
31
    srd->prev_plugin_guard_args = prev_plugin_guard_args;
397
31
    srd->prev_num_plugin_guards = prev_num_plugin_guards;
398
31
    MVM_frame_special_return(tc, tc->cur_frame, add_resolution_to_guard_set,
399
31
            cleanup_recorded_guards, srd, mark_plugin_sr_data);
400
31
    STABLE(plugin)->invoke(tc, plugin, callsite, tc->cur_frame->args);
401
31
}
402
void MVM_spesh_plugin_resolve(MVMThreadContext *tc, MVMString *name,
403
                              MVMRegister *result, MVMuint8 *op_addr,
404
95.6k
                              MVMuint8 *next_addr, MVMCallsite *callsite) {
405
95.6k
    MVMuint32 position = (MVMuint32)(op_addr - *tc->interp_bytecode_start);
406
95.6k
    MVMObject *resolved;
407
95.6k
    MVMuint16 guard_offset;
408
95.6k
    MVMROOT(tc, name, {
409
95.6k
        resolved = resolve_using_guards(tc, position, callsite, &guard_offset,
410
95.6k
                tc->cur_frame->static_info);
411
95.6k
    });
412
95.6k
    if (resolved) {
413
95.6k
        /* Resolution through guard tree successful, so no invoke needed. */
414
95.6k
        result->o = resolved;
415
95.6k
        *tc->interp_cur_op = next_addr;
416
95.6k
        if (MVM_spesh_log_is_logging(tc))
417
5.73k
            MVM_spesh_log_plugin_resolution(tc, position, guard_offset);
418
95.6k
    }
419
31
    else {
420
31
        call_resolver(tc, name, result, position, tc->cur_frame->static_info,
421
31
                next_addr, callsite);
422
31
    }
423
95.6k
}
424
425
/* Resolves a spesh plugin for the current HLL from quickened bytecode. */
426
void MVM_spesh_plugin_resolve_spesh(MVMThreadContext *tc, MVMString *name,
427
                                    MVMRegister *result, MVMuint32 position,
428
                                    MVMStaticFrame *sf, MVMuint8 *next_addr,
429
0
                                    MVMCallsite *callsite) {
430
0
    MVMObject *resolved;
431
0
    MVMuint16 guard_offset;
432
0
    MVMROOT2(tc, name, sf, {
433
0
        resolved = resolve_using_guards(tc, position, callsite, &guard_offset, sf);
434
0
    });
435
0
    if (resolved) {
436
0
        /* Resolution through guard tree successful, so no invoke needed. */
437
0
        result->o = resolved;
438
0
        *tc->interp_cur_op = next_addr;
439
0
    }
440
0
    else {
441
0
        call_resolver(tc, name, result, position, sf, next_addr, callsite);
442
0
    }
443
0
}
444
445
/* Resolves a spesh plugin for the current HLL from the JIT. */
446
void MVM_spesh_plugin_resolve_jit(MVMThreadContext *tc, MVMString *name,
447
                                  MVMRegister *result, MVMuint32 position,
448
0
                                  MVMStaticFrame *sf, MVMCallsite *callsite) {
449
0
    MVMObject *resolved;
450
0
    MVMuint16 guard_offset;
451
0
    MVMROOT2(tc, name, sf, {
452
0
        resolved = resolve_using_guards(tc, position, callsite, &guard_offset, sf);
453
0
    });
454
0
    if (resolved) {
455
0
        /* Resolution through guard tree successful, so no invoke needed. */
456
0
        result->o = resolved;
457
0
    }
458
0
    else {
459
0
        call_resolver(tc, name, result, position, sf, NULL, callsite);
460
0
    }
461
0
}
462
463
/* Returns a pointer into the current recording guard set for the guard we
464
 * should write into. */
465
55
MVMSpeshPluginGuard * get_guard_to_record_into(MVMThreadContext *tc) {
466
55
    if (tc->plugin_guards) {
467
55
        if (tc->num_plugin_guards < MVM_SPESH_PLUGIN_GUARD_LIMIT) {
468
55
            return &(tc->plugin_guards[tc->num_plugin_guards++]);
469
55
        }
470
0
        else {
471
0
            MVM_exception_throw_adhoc(tc, "Too many guards recorded by spesh plugin");
472
0
        }
473
55
    }
474
0
    else {
475
0
        MVM_exception_throw_adhoc(tc, "Not in a spesh plugin, so cannot record a guard");
476
0
    }
477
55
}
478
479
/* Gets the index of the guarded args to record a guard against. */
480
55
MVMuint16 get_guard_arg_index(MVMThreadContext *tc, MVMObject *find) {
481
55
    MVMint64 n = MVM_repr_elems(tc, tc->plugin_guard_args);
482
55
    MVMint64 i;
483
67
    for (i = 0; i < n; i++)
484
67
        if (MVM_repr_at_pos_o(tc, tc->plugin_guard_args, i) == find)
485
55
            return (MVMuint16)i;
486
0
    MVM_exception_throw_adhoc(tc, "Object not in set of those to guard against");
487
0
}
488
489
/* Adds a guard that the guardee must have exactly the specified type. Will
490
 * throw if we are not currently inside of a spesh plugin. */
491
18
void MVM_spesh_plugin_addguard_type(MVMThreadContext *tc, MVMObject *guardee, MVMObject *type) {
492
18
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
493
18
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
494
18
    guard->kind = MVM_SPESH_PLUGIN_GUARD_TYPE;
495
18
    guard->test_idx = idx;
496
18
    guard->u.type = STABLE(type);
497
18
}
498
499
/* Adds a guard that the guardee must be concrete. Will throw if we are not
500
 * currently inside of a spesh plugin. */
501
11
void MVM_spesh_plugin_addguard_concrete(MVMThreadContext *tc, MVMObject *guardee) {
502
11
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
503
11
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
504
11
    guard->kind = MVM_SPESH_PLUGIN_GUARD_CONC;
505
11
    guard->test_idx = idx;
506
11
}
507
508
/* Adds a guard that the guardee must not be concrete. Will throw if we are
509
 * not currently inside of a spesh plugin. */
510
7
void MVM_spesh_plugin_addguard_typeobj(MVMThreadContext *tc, MVMObject *guardee) {
511
7
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
512
7
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
513
7
    guard->kind = MVM_SPESH_PLUGIN_GUARD_TYPEOBJ;
514
7
    guard->test_idx = idx;
515
7
}
516
517
/* Adds a guard that the guardee must exactly match the provided object
518
 * literal. Will throw if we are not currently inside of a spesh plugin. */
519
11
void MVM_spesh_plugin_addguard_obj(MVMThreadContext *tc, MVMObject *guardee) {
520
11
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
521
11
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
522
11
    guard->kind = MVM_SPESH_PLUGIN_GUARD_OBJ;
523
11
    guard->test_idx = idx;
524
11
    guard->u.object = guardee;
525
11
}
526
527
/* Adds a guard that the guardee must NOT exactly match the provided object
528
 * literal. Will throw if we are not currently inside of a spesh plugin. */
529
2
void MVM_spesh_plugin_addguard_notobj(MVMThreadContext *tc, MVMObject *guardee, MVMObject *not) {
530
2
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
531
2
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
532
2
    guard->kind = MVM_SPESH_PLUGIN_GUARD_NOTOBJ;
533
2
    guard->test_idx = idx;
534
2
    guard->u.object = not;
535
2
}
536
537
/* Gets an attribute and adds that object to the set of objects that we may
538
 * guard against. Will throw if we are not currently inside of a spesh
539
 * plugin. */
540
MVMObject * MVM_spesh_plugin_addguard_getattr(MVMThreadContext *tc, MVMObject *guardee,
541
6
            MVMObject *class_handle, MVMString *name) {
542
6
    MVMObject *attr;
543
6
    MVMuint16 idx = get_guard_arg_index(tc, guardee);
544
6
    MVMSpeshPluginGuard *guard = get_guard_to_record_into(tc);
545
6
    guard->kind = MVM_SPESH_PLUGIN_GUARD_GETATTR;
546
6
    guard->test_idx = idx;
547
6
    guard->u.attr.class_handle = class_handle;
548
6
    guard->u.attr.name = name;
549
6
    attr = MVM_repr_get_attr_o(tc, guardee, class_handle, name, MVM_NO_HINT);
550
6
    MVM_repr_push_o(tc, tc->plugin_guard_args, attr);
551
6
    return attr;
552
6
}
553
554
/* Rewrites a spesh plugin resolve to instead resolve to a spesh slot with the
555
 * result of the resolution, inserting guards as needed. */
556
static MVMSpeshOperand *arg_ins_to_reg_list(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
557
6
                                            MVMSpeshIns *ins, MVMuint32 *reg_list_length) {
558
6
    /* Search back to prepargs to find the maximum argument index. */
559
6
    MVMSpeshIns *cursor = ins->prev;
560
6
    MVMSpeshIns *cur_arg;
561
6
    MVMint32 max_arg_idx = -1;
562
12
    while (cursor->info->opcode != MVM_OP_prepargs) {
563
6
        if (cursor->info->opcode == MVM_OP_arg_o) {
564
6
            MVMuint16 idx = cursor->operands[0].lit_ui16;
565
6
            if (idx > max_arg_idx)
566
6
                max_arg_idx = idx;
567
6
        }
568
0
        else {
569
0
            MVM_oops(tc, "Malformed spesh resolve argument sequence");
570
0
        }
571
6
        cursor = cursor->prev;
572
6
    }
573
6
    cur_arg = cursor->next;
574
6
575
6
    /* Delete the prepargs instruction. */
576
6
    MVM_spesh_manipulate_delete_ins(tc, g, bb, cursor);
577
6
578
6
    /* If there are any args, collect registers and delete them. */
579
6
    if (max_arg_idx >= 0) {
580
6
        MVMSpeshOperand *result = MVM_malloc((max_arg_idx + 1) * sizeof(MVMSpeshOperand));
581
12
        while (cur_arg->info->opcode == MVM_OP_arg_o) {
582
6
            MVMSpeshIns *next_arg = cur_arg->next;
583
6
            result[cur_arg->operands[0].lit_ui16] = cur_arg->operands[1];
584
6
            MVM_spesh_manipulate_delete_ins(tc, g, bb, cur_arg);
585
6
            cur_arg = next_arg;
586
6
        }
587
6
        *reg_list_length = max_arg_idx + 1;
588
6
        return result;
589
6
    }
590
0
    else {
591
0
        *reg_list_length = 0;
592
0
        return NULL;
593
0
    }
594
6
}
595
6
MVMSpeshAnn * steal_prepargs_deopt(MVMThreadContext *tc, MVMSpeshIns *ins) {
596
6
    MVMSpeshIns *cur = ins->prev;
597
12
    while (cur) {
598
12
        if (cur->info->opcode == MVM_OP_prepargs) {
599
6
            MVMSpeshAnn *ann = cur->annotations;
600
6
            MVMSpeshAnn *prev_ann = NULL;
601
7
            while (ann) {
602
7
                if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS) {
603
6
                    if (prev_ann)
604
1
                        prev_ann->next = ann->next;
605
6
                    else
606
5
                        cur->annotations = ann->next;
607
6
                    ann->next = NULL;
608
6
                    return ann;
609
6
                }
610
1
                prev_ann = ann;
611
1
                ann = ann->next;
612
1
            }
613
0
            MVM_oops(tc, "Could not find deopt annotation on prepargs before speshresolve");
614
0
        }
615
6
        cur = cur->prev;
616
6
    }
617
0
    MVM_oops(tc, "Could not find prepargs before speshresolve");
618
0
}
619
3
MVMSpeshAnn * clone_deopt_ann(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshAnn *in) {
620
3
    MVMSpeshAnn *cloned = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
621
3
    MVMuint32 deopt_idx = g->num_deopt_addrs;
622
3
    cloned->type = in->type;
623
3
    cloned->data.deopt_idx = deopt_idx;
624
3
    MVM_spesh_graph_grow_deopt_table(tc, g);
625
3
    g->deopt_addrs[deopt_idx * 2] = g->deopt_addrs[in->data.deopt_idx * 2];
626
3
    g->num_deopt_addrs++;
627
3
    return cloned;
628
3
}
629
void MVM_spesh_plugin_rewrite_resolve(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
630
                                      MVMSpeshIns *ins, MVMuint32 bytecode_offset,
631
6
                                      MVMint32 guard_index) {
632
6
    /* Resolve guard set (should never be missing, but fail softly if so). */
633
6
    MVMSpeshPluginState *ps = get_plugin_state(tc, g->sf);
634
6
    MVMSpeshPluginGuardSet *gs = guard_set_for_position(tc, bytecode_offset, ps);
635
6
    if (gs) {
636
6
        /* Steal the deopt annotation from the prepargs, and calculate the
637
6
         * deopt position. */
638
6
        MVMSpeshAnn *stolen_deopt_ann = steal_prepargs_deopt(tc, ins);
639
6
        MVMuint32 stolen_deopt_ann_used = 0;
640
6
        MVMuint32 deopt_to = g->deopt_addrs[2 * stolen_deopt_ann->data.deopt_idx];
641
6
642
6
        /* Collect registers that go with each argument, and delete the arg
643
6
         * and prepargs instructions. */
644
6
        MVMuint32 initial_arg_regs_length, arg_regs_length, i;
645
6
        MVMSpeshOperand *arg_regs = arg_ins_to_reg_list(tc, g, bb, ins, &arg_regs_length);
646
6
647
6
        /* Find result and add it to a spesh slot. */
648
6
        MVMObject *resolvee = gs->guards[guard_index].u.result;
649
6
        MVMuint16 resolvee_slot = MVM_spesh_add_spesh_slot_try_reuse(tc, g, (MVMCollectable *)resolvee);
650
6
651
6
        /* Find guard range. */
652
6
        MVMint32 guards_end = guard_index - 1;
653
6
        MVMint32 guards_start = guards_end;
654
16
        while (guards_start >= 0 && gs->guards[guards_start].kind != MVM_SPESH_PLUGIN_GUARD_RESULT)
655
10
            guards_start--;
656
6
657
6
        /* Rewrite resolve instruction into a spesh slot lookup. */
658
6
        ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
659
6
        ins->operands[1].lit_i16 = resolvee_slot;
660
6
        MVM_spesh_facts_object_facts(tc, g, ins->operands[0], resolvee);
661
6
662
6
        /* Prepend guards. */
663
6
        initial_arg_regs_length = arg_regs_length;
664
16
        while (++guards_start <= guards_end) {
665
10
            MVMSpeshPluginGuard *guard = &(gs->guards[guards_start]);
666
10
            MVMSpeshFacts *guarded_facts = MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx]);
667
10
            if (guard->kind != MVM_SPESH_PLUGIN_GUARD_GETATTR) {
668
9
                if (stolen_deopt_ann_used)
669
3
                    stolen_deopt_ann = clone_deopt_ann(tc, g, stolen_deopt_ann);
670
9
                else
671
6
                    stolen_deopt_ann_used = 1;
672
9
            }
673
10
            switch (guard->kind) {
674
1
                case MVM_SPESH_PLUGIN_GUARD_OBJ: {
675
1
                    if ((guarded_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) == 0
676
1
                            || guarded_facts->value.o != (MVMObject *)guard->u.object) {
677
1
                        MVMSpeshIns *guard_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
678
1
                        guard_ins->info = MVM_op_get_op(MVM_OP_sp_guardobj);
679
1
                        guard_ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
680
1
                        guard_ins->operands[0] = arg_regs[guard->test_idx];
681
1
                        MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
682
1
                        guard_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
683
1
                                (MVMCollectable *)guard->u.object);
684
1
                        guard_ins->operands[2].lit_ui32 = deopt_to;
685
1
                        guard_ins->annotations = stolen_deopt_ann;
686
1
                        MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, guard_ins);
687
1
                        if (guard->test_idx >= initial_arg_regs_length) {
688
0
                            guarded_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
689
0
                            guarded_facts->value.o = (MVMObject *)guard->u.object;
690
0
                        }
691
1
                    }
692
0
                    else {
693
0
                        MVM_spesh_get_and_use_facts(tc, g, arg_regs[guard->test_idx]);
694
0
                    }
695
1
                    break;
696
1
                }
697
1
                case MVM_SPESH_PLUGIN_GUARD_NOTOBJ: {
698
1
                    MVMuint32 may_match = 1;
699
1
                    if ((guarded_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) &&
700
0
                            guarded_facts->value.o != guard->u.object)
701
0
                        may_match = 0;
702
1
                    else if ((guarded_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) &&
703
1
                            STABLE(guarded_facts->type) != STABLE(guard->u.object))
704
0
                        may_match = 0;
705
1
                    else if ((guarded_facts->flags & MVM_SPESH_FACT_CONCRETE) &&
706
1
                            !IS_CONCRETE(guard->u.object))
707
0
                        may_match = 0;
708
1
                    else if ((guarded_facts->flags & MVM_SPESH_FACT_TYPEOBJ) &&
709
0
                            IS_CONCRETE(guard->u.object))
710
0
                        may_match = 0;
711
1
                    if (may_match) {
712
1
                        MVMSpeshIns *guard_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
713
1
                        guard_ins->info = MVM_op_get_op(MVM_OP_sp_guardnotobj);
714
1
                        guard_ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
715
1
                        guard_ins->operands[0] = arg_regs[guard->test_idx];
716
1
                        MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
717
1
                        guard_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
718
1
                                (MVMCollectable *)guard->u.object);
719
1
                        guard_ins->operands[2].lit_ui32 = deopt_to;
720
1
                        guard_ins->annotations = stolen_deopt_ann;
721
1
                        MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, guard_ins);
722
1
                    }
723
0
                    else {
724
0
                        MVM_spesh_get_and_use_facts(tc, g, arg_regs[guard->test_idx]);
725
0
                    }
726
1
                    break;
727
1
                }
728
3
                case MVM_SPESH_PLUGIN_GUARD_TYPE: {
729
3
                    if ((guarded_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) == 0
730
2
                            || STABLE(guarded_facts->type) != guard->u.type) {
731
2
                        MVMSpeshIns *guard_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
732
2
                        guard_ins->info = MVM_op_get_op(MVM_OP_sp_guard);
733
2
                        guard_ins->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
734
2
                        guard_ins->operands[0] = arg_regs[guard->test_idx];
735
2
                        MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
736
2
                        guard_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
737
2
                                (MVMCollectable *)guard->u.type);
738
2
                        guard_ins->operands[2].lit_ui32 = deopt_to;
739
2
                        guard_ins->annotations = stolen_deopt_ann;
740
2
                        MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, guard_ins);
741
2
                        if (guard->test_idx >= initial_arg_regs_length) {
742
1
                            guarded_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
743
1
                            guarded_facts->type = guard->u.type->WHAT;
744
1
                        }
745
2
                    }
746
1
                    else {
747
1
                        MVM_spesh_get_and_use_facts(tc, g, arg_regs[guard->test_idx]);
748
1
                    }
749
3
                    break;
750
1
                }
751
2
                case MVM_SPESH_PLUGIN_GUARD_CONC: {
752
2
                    if ((guarded_facts->flags & MVM_SPESH_FACT_CONCRETE) == 0) {
753
1
                        MVMSpeshIns *guard_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
754
1
                        guard_ins->info = MVM_op_get_op(MVM_OP_sp_guardjustconc);
755
1
                        guard_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
756
1
                        guard_ins->operands[0] = arg_regs[guard->test_idx];
757
1
                        MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
758
1
                        guard_ins->operands[1].lit_ui32 = deopt_to;
759
1
                        guard_ins->annotations = stolen_deopt_ann;
760
1
                        MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, guard_ins);
761
1
                        if (guard->test_idx >= initial_arg_regs_length)
762
0
                            guarded_facts->flags |= MVM_SPESH_FACT_CONCRETE;
763
1
                    }
764
1
                    else {
765
1
                        MVM_spesh_get_and_use_facts(tc, g, arg_regs[guard->test_idx]);
766
1
                    }
767
2
                    break;
768
1
                }
769
2
                case MVM_SPESH_PLUGIN_GUARD_TYPEOBJ: {
770
2
                    if ((guarded_facts->flags & MVM_SPESH_FACT_TYPEOBJ) == 0) {
771
2
                        MVMSpeshIns *guard_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
772
2
                        guard_ins->info = MVM_op_get_op(MVM_OP_sp_guardjusttype);
773
2
                        guard_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
774
2
                        guard_ins->operands[0] = arg_regs[guard->test_idx];
775
2
                        MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
776
2
                        guard_ins->operands[1].lit_ui32 = deopt_to;
777
2
                        guard_ins->annotations = stolen_deopt_ann;
778
2
                        MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, guard_ins);
779
2
                        if (guard->test_idx >= initial_arg_regs_length)
780
1
                            guarded_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
781
2
                    }
782
0
                    else {
783
0
                        MVM_spesh_get_and_use_facts(tc, g, arg_regs[guard->test_idx]);
784
0
                    }
785
2
                    break;
786
1
                }
787
1
                case MVM_SPESH_PLUGIN_GUARD_GETATTR: {
788
1
                    MVMSpeshFacts *facts;
789
1
790
1
                    /* This isn't really a guard, but rather a request to insert
791
1
                     * a getattr instruction. We also need a target register for
792
1
                     * it, and will allocate a temporary one for it. */
793
1
                    MVMSpeshOperand target = MVM_spesh_manipulate_get_temp_reg(tc,
794
1
                        g, MVM_reg_obj);
795
1
796
1
                    /* Further to that, we also need registers holding both the
797
1
                     * class handle and attribute name, which will be used by
798
1
                     * the getattr instructions. */
799
1
                    MVMSpeshOperand ch_temp = MVM_spesh_manipulate_get_temp_reg(tc,
800
1
                        g, MVM_reg_obj);
801
1
                    MVMSpeshOperand name_temp = MVM_spesh_manipulate_get_temp_reg(tc,
802
1
                        g, MVM_reg_str);
803
1
804
1
                    /* Emit spesh slot lookup instruction for the class handle. */
805
1
                    MVMSpeshIns *spesh_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
806
1
                    spesh_ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
807
1
                    spesh_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
808
1
                    spesh_ins->operands[0] = ch_temp;
809
1
                    spesh_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
810
1
                        (MVMCollectable *)guard->u.attr.class_handle);
811
1
                    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, spesh_ins);
812
1
                    facts = MVM_spesh_get_facts(tc, g, ch_temp);
813
1
                    facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
814
1
                    facts->type = guard->u.attr.class_handle;
815
1
816
1
                    /* Emit spesh slot lookup instruction for the attr name. */
817
1
                    spesh_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
818
1
                    spesh_ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot);
819
1
                    spesh_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
820
1
                    spesh_ins->operands[0] = name_temp;
821
1
                    spesh_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
822
1
                        (MVMCollectable *)guard->u.attr.name);
823
1
                    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, spesh_ins);
824
1
                    facts = MVM_spesh_get_facts(tc, g, name_temp);
825
1
                    facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE;
826
1
                    facts->value.s = guard->u.attr.name;
827
1
828
1
                    /* Emit the getattr instruction. */
829
1
                    spesh_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
830
1
                    spesh_ins->info = MVM_op_get_op(MVM_OP_getattrs_o);
831
1
                    spesh_ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
832
1
                    spesh_ins->operands[0] = target;
833
1
                    spesh_ins->operands[1] = arg_regs[guard->test_idx];
834
1
                    MVM_spesh_get_facts(tc, g, arg_regs[guard->test_idx])->usages++;
835
1
                    spesh_ins->operands[2] = ch_temp;
836
1
                    MVM_spesh_get_facts(tc, g, ch_temp)->usages++;
837
1
                    spesh_ins->operands[3] = name_temp;
838
1
                    MVM_spesh_get_facts(tc, g, name_temp)->usages++;
839
1
                    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, spesh_ins);
840
1
841
1
                    /* Release the temporaries for the class handle and name. */
842
1
                    MVM_spesh_manipulate_release_temp_reg(tc, g, ch_temp);
843
1
                    MVM_spesh_manipulate_release_temp_reg(tc, g, name_temp);
844
1
845
1
                    /* Add the target into the guard reg set. */
846
1
                    arg_regs = MVM_realloc(arg_regs,
847
1
                        (arg_regs_length + 1) * sizeof(MVMSpeshOperand));
848
1
                    arg_regs[arg_regs_length++] = target;
849
1
850
1
                    break;
851
1
                }
852
0
                default:
853
0
                    MVM_panic(1, "Unknown spesh plugin guard kind %d to insert during specialization",
854
0
                            guard->kind);
855
10
            }
856
10
        }
857
6
858
6
        /* Free up any temporary registers we created. */
859
7
        for (i = initial_arg_regs_length; i < arg_regs_length; i++)
860
1
            MVM_spesh_manipulate_release_temp_reg(tc, g, arg_regs[i]);
861
6
        MVM_free(arg_regs);
862
6
    }
863
6
}
864
865
/* Called to mark a guard list. */
866
void MVM_spesh_plugin_guard_list_mark(MVMThreadContext *tc, MVMSpeshPluginGuard *guards,
867
790
                                      MVMuint32 num_guards, MVMGCWorklist *worklist) {
868
790
    MVMuint32 i;
869
790
    if (guards) {
870
118
        for (i = 0; i < num_guards; i++) {
871
86
            switch (guards[i].kind) {
872
41
                case MVM_SPESH_PLUGIN_GUARD_RESULT:
873
41
                    MVM_gc_worklist_add(tc, worklist, &guards[i].u.result);
874
41
                    break;
875
33
                case MVM_SPESH_PLUGIN_GUARD_OBJ:
876
33
                case MVM_SPESH_PLUGIN_GUARD_NOTOBJ:
877
33
                    MVM_gc_worklist_add(tc, worklist, &guards[i].u.object);
878
33
                    break;
879
3
                case MVM_SPESH_PLUGIN_GUARD_TYPE:
880
3
                    MVM_gc_worklist_add(tc, worklist, &guards[i].u.type);
881
3
                    break;
882
1
                case MVM_SPESH_PLUGIN_GUARD_GETATTR:
883
1
                    MVM_gc_worklist_add(tc, worklist, &guards[i].u.attr.class_handle);
884
1
                    MVM_gc_worklist_add(tc, worklist, &guards[i].u.attr.name);
885
1
                    break;
886
86
            }
887
86
        }
888
32
    }
889
790
}
890
891
/* Called from the GC to mark the spesh plugin state. */
892
void MVM_spesh_plugin_state_mark(MVMThreadContext *tc, MVMSpeshPluginState *ps,
893
11.1k
                                 MVMGCWorklist *worklist) {
894
11.1k
    if (ps) {
895
26
        MVMuint32 i;
896
58
        for (i = 0; i < ps->num_positions; i++) {
897
32
            MVMSpeshPluginGuardSet *gs = ps->positions[i].guard_set;
898
32
            MVM_spesh_plugin_guard_list_mark(tc, gs->guards, gs->num_guards, worklist);
899
32
        }
900
26
    }
901
11.1k
}
902
903
/* Called from the GC when the spesh plugin state should be freed. This means
904
 * that it is no longer reachable, meaning the static frame and its bytecode
905
 * also went away. Thus a simple free will suffice. */
906
0
void MVM_spesh_plugin_state_free(MVMThreadContext *tc, MVMSpeshPluginState *ps) {
907
0
    if (ps) {
908
0
        MVMuint32 i;
909
0
        for (i = 0; i < ps->num_positions; i++) {
910
0
            MVM_fixed_size_free(tc, tc->instance->fsa,
911
0
                    ps->positions[i].guard_set->num_guards * sizeof(MVMSpeshPluginGuard),
912
0
                    ps->positions[i].guard_set->guards);
913
0
            MVM_fixed_size_free(tc, tc->instance->fsa, sizeof(MVMSpeshPluginGuardSet),
914
0
                    ps->positions[i].guard_set);
915
0
        }
916
0
        MVM_fixed_size_free(tc, tc->instance->fsa,
917
0
                ps->num_positions * sizeof(MVMSpeshPluginPosition),
918
0
                ps->positions);
919
0
        MVM_fixed_size_free(tc, tc->instance->fsa, sizeof(MVMSpeshPluginState), ps);
920
0
    }
921
0
}