Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/spesh/stats.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Gets the statistics for a static frame, creating them if needed. */
4
3.38M
MVMSpeshStats * stats_for(MVMThreadContext *tc, MVMStaticFrame *sf) {
5
3.38M
    MVMStaticFrameSpesh *spesh = sf->body.spesh;
6
3.38M
    if (!spesh->body.spesh_stats)
7
80.1k
        spesh->body.spesh_stats = MVM_calloc(1, sizeof(MVMSpeshStats));
8
3.38M
    return spesh->body.spesh_stats;
9
3.38M
}
10
11
/* Gets the stats by callsite, adding it if it's missing. */
12
3.38M
MVMuint32 by_callsite_idx(MVMThreadContext *tc, MVMSpeshStats *ss, MVMCallsite *cs) {
13
3.38M
    /* See if we already have it. */
14
3.38M
    MVMuint32 found;
15
3.38M
    MVMuint32 n = ss->num_by_callsite;
16
4.04M
    for (found = 0; found < n; found++)
17
3.95M
        if (ss->by_callsite[found].cs == cs)
18
3.29M
            return found;
19
3.38M
20
3.38M
    /* If not, we need a new record. */
21
91.8k
    found = ss->num_by_callsite;
22
91.8k
    ss->num_by_callsite++;
23
91.8k
    ss->by_callsite = MVM_realloc(ss->by_callsite,
24
91.8k
        ss->num_by_callsite * sizeof(MVMSpeshStatsByCallsite));
25
91.8k
    memset(&(ss->by_callsite[found]), 0, sizeof(MVMSpeshStatsByCallsite));
26
91.8k
    ss->by_callsite[found].cs = cs;
27
91.8k
    return found;
28
3.38M
}
29
30
/* Checks if a type tuple is incomplete (no types logged for some passed
31
 * objects, or no decont type logged for a container type). */
32
MVMint32 incomplete_type_tuple(MVMThreadContext *tc, MVMCallsite *cs,
33
3.28M
                               MVMSpeshStatsType *arg_types) {
34
3.28M
    MVMuint32 i;
35
9.04M
    for (i = 0; i < cs->flag_count; i++) {
36
5.90M
        if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) {
37
4.98M
            MVMObject *type = arg_types[i].type;
38
4.98M
            if (!type)
39
149k
                return 1;
40
4.83M
            if (arg_types[i].type_concrete && type->st->container_spec)
41
0
                if (!arg_types[i].decont_type && REPR(type)->ID != MVM_REPR_ID_NativeRef)
42
0
                    return 1;
43
4.83M
        }
44
5.90M
    }
45
3.13M
    return 0;
46
3.28M
}
47
48
/* Returns true if the callsite has no object arguments, false otherwise. */
49
3.31M
MVMint32 cs_without_object_args(MVMThreadContext *tc, MVMCallsite *cs) {
50
3.31M
    MVMuint32 i;
51
3.32M
    for (i = 0; i < cs->flag_count; i++)
52
3.30M
        if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ)
53
3.28M
            return 0;
54
25.2k
    return 1;
55
3.31M
}
56
57
/* Gets the stats by type, adding it if it's missing. Frees arg_types. Returns
58
 * the index in the by type array, or -1 if unresolved. */
59
MVMint32 by_type(MVMThreadContext *tc, MVMSpeshStats *ss, MVMuint32 callsite_idx,
60
3.31M
                  MVMSpeshStatsType *arg_types) {
61
3.31M
    /* Resolve type by callsite level info. If this is the no-callsite
62
3.31M
     * specialization there is nothing further to do. */
63
3.31M
    MVMSpeshStatsByCallsite *css = &(ss->by_callsite[callsite_idx]);
64
3.31M
    MVMCallsite *cs = css->cs;
65
3.31M
    if (!cs) {
66
0
        MVM_free(arg_types);
67
0
        return -1;
68
0
    }
69
3.31M
70
3.31M
    /* Otherwise if there's no object args, then we'll use a single "by type"
71
3.31M
     * specialization, so we can have data tracked by offset at least. */
72
3.31M
    else if (cs_without_object_args(tc, cs)) {
73
25.2k
        if (css->num_by_type == 0) {
74
3.82k
            css->num_by_type++;
75
3.82k
            css->by_type = MVM_calloc(1, sizeof(MVMSpeshStatsByType));
76
3.82k
            css->by_type[0].arg_types = arg_types;
77
3.82k
        }
78
21.4k
        else {
79
21.4k
            MVM_free(arg_types);
80
21.4k
        }
81
25.2k
        return 0;
82
25.2k
    }
83
3.31M
84
3.31M
    /* Maybe the type tuple is incomplete, maybe because the log buffer ended
85
3.31M
     * prior to having all the type information. Discard. */
86
3.28M
    else if (incomplete_type_tuple(tc, cs, arg_types)) {
87
149k
        MVM_free(arg_types);
88
149k
        return -1;
89
149k
    }
90
3.28M
91
3.28M
    /* Can produce by-type stats. */
92
3.13M
    else {
93
3.13M
        /* See if we already have it. */
94
3.13M
        size_t args_length = cs->flag_count * sizeof(MVMSpeshStatsType);
95
3.13M
        MVMuint32 found;
96
3.13M
        MVMuint32 n = css->num_by_type;
97
12.5M
        for (found = 0; found < n; found++) {
98
12.4M
            if (memcmp(css->by_type[found].arg_types, arg_types, args_length) == 0) {
99
2.99M
                MVM_free(arg_types);
100
2.99M
                return found;
101
2.99M
            }
102
12.4M
        }
103
3.13M
104
3.13M
        /* If not, we need a new record. */
105
142k
        found = css->num_by_type;
106
142k
        css->num_by_type++;
107
142k
        css->by_type = MVM_realloc(css->by_type,
108
142k
            css->num_by_type * sizeof(MVMSpeshStatsByType));
109
142k
        memset(&(css->by_type[found]), 0, sizeof(MVMSpeshStatsByType));
110
142k
        css->by_type[found].arg_types = arg_types;
111
142k
        return found;
112
3.13M
    }
113
3.31M
}
114
115
/* Get the stats by offset entry, adding it if it's missing. */
116
MVMSpeshStatsByOffset * by_offset(MVMThreadContext *tc, MVMSpeshStatsByType *tss,
117
17.8M
                                  MVMuint32 bytecode_offset) {
118
17.8M
    /* See if we already have it. */
119
17.8M
    MVMuint32 found;
120
17.8M
    MVMuint32 n = tss->num_by_offset;
121
113M
    for (found = 0; found < n; found++)
122
112M
        if (tss->by_offset[found].bytecode_offset == bytecode_offset)
123
17.3M
            return &(tss->by_offset[found]);
124
17.8M
125
17.8M
    /* If not, we need a new record. */
126
446k
    found = tss->num_by_offset;
127
446k
    tss->num_by_offset++;
128
446k
    tss->by_offset = MVM_realloc(tss->by_offset,
129
446k
        tss->num_by_offset * sizeof(MVMSpeshStatsByOffset));
130
446k
    memset(&(tss->by_offset[found]), 0, sizeof(MVMSpeshStatsByOffset));
131
446k
    tss->by_offset[found].bytecode_offset = bytecode_offset;
132
446k
    return &(tss->by_offset[found]);
133
17.8M
}
134
135
/* Adds/increments the count of a certain type seen at the given offset. */
136
void add_type_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss,
137
10.1M
                        MVMStaticFrame *sf, MVMObject *type, MVMuint8 concrete) {
138
10.1M
    /* If we have it already, increment the count. */
139
10.1M
    MVMuint32 found;
140
10.1M
    MVMuint32 n = oss->num_types;
141
10.7M
    for (found = 0; found < n; found++) {
142
10.3M
        if (oss->types[found].type == type && oss->types[found].type_concrete == concrete) {
143
9.72M
            oss->types[found].count++;
144
9.72M
            return;
145
9.72M
        }
146
10.3M
    }
147
10.1M
148
10.1M
    /* Otherwise, add it to the list. */
149
416k
    found = oss->num_types;
150
416k
    oss->num_types++;
151
416k
    oss->types = MVM_realloc(oss->types, oss->num_types * sizeof(MVMSpeshStatsTypeCount));
152
416k
    MVM_ASSIGN_REF(tc, &(sf->body.spesh->common.header), oss->types[found].type, type);
153
416k
    oss->types[found].type_concrete = concrete;
154
416k
    oss->types[found].count = 1;
155
416k
}
156
157
/* Adds/increments the count of a certain invocation target seen at the given
158
 * offset. */
159
void add_invoke_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss,
160
                          MVMStaticFrame *sf, MVMStaticFrame *target_sf,
161
4.98M
                          MVMint16 caller_is_outer, MVMint16 was_multi) {
162
4.98M
    /* If we have it already, increment the count. */
163
4.98M
    MVMuint32 found;
164
4.98M
    MVMuint32 n = oss->num_invokes;
165
5.27M
    for (found = 0; found < n; found++) {
166
5.02M
        if (oss->invokes[found].sf == target_sf) {
167
4.73M
            oss->invokes[found].count++;
168
4.73M
            if (caller_is_outer)
169
47.6k
                oss->invokes[found].caller_is_outer_count++;
170
4.73M
            if (was_multi)
171
40.4k
                oss->invokes[found].was_multi_count++;
172
4.73M
            return;
173
4.73M
        }
174
5.02M
    }
175
4.98M
176
4.98M
    /* Otherwise, add it to the list. */
177
251k
    found = oss->num_invokes;
178
251k
    oss->num_invokes++;
179
251k
    oss->invokes = MVM_realloc(oss->invokes,
180
251k
        oss->num_invokes * sizeof(MVMSpeshStatsInvokeCount));
181
251k
    MVM_ASSIGN_REF(tc, &(sf->body.spesh->common.header), oss->invokes[found].sf, target_sf);
182
251k
    oss->invokes[found].count = 1;
183
248k
    oss->invokes[found].caller_is_outer_count = caller_is_outer ? 1 : 0;
184
246k
    oss->invokes[found].was_multi_count = was_multi ? 1 : 0;
185
251k
}
186
187
/* Adds/increments the count of a plugin guard index seen at the given offset. */
188
void add_plugin_guard_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss,
189
5.35k
                                MVMuint32 guard_index) {
190
5.35k
    /* If we have it already, increment the count. */
191
5.35k
    MVMuint32 found;
192
5.35k
    MVMuint32 n = oss->num_plugin_guards;
193
5.36k
    for (found = 0; found < n; found++) {
194
5.33k
        if (oss->plugin_guards[found].guard_index == guard_index) {
195
5.33k
            oss->plugin_guards[found].count++;
196
5.33k
            return;
197
5.33k
        }
198
5.33k
    }
199
5.35k
200
5.35k
    /* Otherwise, add it to the list. */
201
22
    found = oss->num_plugin_guards;
202
22
    oss->num_plugin_guards++;
203
22
    oss->plugin_guards = MVM_realloc(oss->plugin_guards,
204
22
            oss->num_plugin_guards * sizeof(MVMSpeshStatsPluginGuardCount));
205
22
    oss->plugin_guards[found].guard_index = guard_index;
206
22
    oss->plugin_guards[found].count = 1;
207
22
}
208
209
/* Adds/increments the count of a type tuple seen at the given offset. */
210
void add_type_tuple_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss,
211
2.67M
                              MVMStaticFrame *sf, MVMSpeshSimCallType *info) {
212
2.67M
    /* Compute type tuple size. */
213
2.67M
    size_t tt_size = info->cs->flag_count * sizeof(MVMSpeshStatsType);
214
2.67M
215
2.67M
    /* If we have it already, increment the count. */
216
2.67M
    MVMuint32 found, i;
217
2.67M
    MVMuint32 n = oss->num_type_tuples;
218
10.0M
    for (found = 0; found < n; found++) {
219
9.80M
        if (oss->type_tuples[found].cs == info->cs) {
220
9.80M
            if (memcmp(oss->type_tuples[found].arg_types, info->arg_types, tt_size) == 0) {
221
2.39M
                oss->type_tuples[found].count++;
222
2.39M
                return;
223
2.39M
            }
224
9.80M
        }
225
9.80M
    }
226
2.67M
227
2.67M
    /* Otherwise, add it to the list; copy type tuple to ease memory
228
2.67M
     * management, but also need to write barrier any types. */
229
272k
    found = oss->num_type_tuples;
230
272k
    oss->num_type_tuples++;
231
272k
    oss->type_tuples = MVM_realloc(oss->type_tuples,
232
272k
        oss->num_type_tuples * sizeof(MVMSpeshStatsTypeTupleCount));
233
272k
    oss->type_tuples[found].cs = info->cs;
234
272k
    oss->type_tuples[found].arg_types = MVM_malloc(tt_size);
235
272k
    memcpy(oss->type_tuples[found].arg_types, info->arg_types, tt_size);
236
786k
    for (i = 0; i < info->cs->flag_count; i++) {
237
514k
        if (info->arg_types[i].type)
238
447k
            MVM_gc_write_barrier(tc, &(sf->body.spesh->common.header),
239
447k
                &(info->arg_types[i].type->header));
240
514k
        if (info->arg_types[i].decont_type)
241
0
            MVM_gc_write_barrier(tc, &(sf->body.spesh->common.header),
242
0
                &(info->arg_types[i].decont_type->header));
243
514k
    }
244
272k
    oss->type_tuples[found].count = 1;
245
272k
}
246
247
/* Initializes the stack simulation. */
248
145
void sim_stack_init(MVMThreadContext *tc, MVMSpeshSimStack *sims) {
249
145
    sims->used = 0;
250
145
    sims->limit = 32;
251
145
    sims->frames = MVM_malloc(sims->limit * sizeof(MVMSpeshSimStackFrame));
252
145
    sims->depth = 0;
253
145
}
254
255
/* Pushes an entry onto the stack frame model. */
256
void sim_stack_push(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMStaticFrame *sf,
257
3.38M
                    MVMSpeshStats *ss, MVMuint32 cid, MVMuint32 callsite_idx) {
258
3.38M
    MVMSpeshSimStackFrame *frame;
259
3.38M
    MVMCallsite *cs;
260
3.38M
    if (sims->used == sims->limit) {
261
235
        sims->limit *= 2;
262
235
        sims->frames = MVM_realloc(sims->frames, sims->limit * sizeof(MVMSpeshSimStackFrame));
263
235
    }
264
3.38M
    frame = &(sims->frames[sims->used++]);
265
3.38M
    frame->sf = sf;
266
3.38M
    frame->ss = ss;
267
3.38M
    frame->cid = cid;
268
3.38M
    frame->callsite_idx = callsite_idx;
269
3.38M
    frame->type_idx = -1;
270
3.38M
    frame->arg_types = (cs = ss->by_callsite[callsite_idx].cs)
271
3.31M
        ? MVM_calloc(cs->flag_count, sizeof(MVMSpeshStatsType))
272
3.38M
        : NULL;
273
3.38M
    frame->offset_logs = NULL;
274
3.38M
    frame->offset_logs_used = frame->offset_logs_limit = 0;
275
3.38M
    frame->osr_hits = 0;
276
3.38M
    frame->call_type_info = NULL;
277
3.38M
    frame->call_type_info_used = frame->call_type_info_limit = 0;
278
3.38M
    frame->last_invoke_offset = 0;
279
3.38M
    frame->last_invoke_sf = NULL;
280
3.38M
    sims->depth++;
281
3.38M
}
282
283
/* Adds an entry to a sim frame's callsite type info list, for later
284
 * inclusion in the callsite stats. */
285
void add_sim_call_type_info(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf,
286
                            MVMuint32 bytecode_offset, MVMCallsite *cs,
287
2.77M
                            MVMSpeshStatsType *arg_types) {
288
2.77M
    MVMSpeshSimCallType *info;
289
2.77M
    if (simf->call_type_info_used == simf->call_type_info_limit) {
290
1.19M
        simf->call_type_info_limit += 32;
291
1.19M
        simf->call_type_info = MVM_realloc(simf->call_type_info,
292
1.19M
            simf->call_type_info_limit * sizeof(MVMSpeshSimCallType));
293
1.19M
    }
294
2.77M
    info = &(simf->call_type_info[simf->call_type_info_used++]);
295
2.77M
    info->bytecode_offset = bytecode_offset;
296
2.77M
    info->cs = cs;
297
2.77M
    info->arg_types = arg_types;
298
2.77M
}
299
300
/* Incorporate information collected into a sim stack frame. */
301
void incorporate_stats(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf,
302
                       MVMuint32 frame_depth, MVMSpeshSimStackFrame *caller,
303
3.43M
                       MVMObject *sf_updated) {
304
3.43M
    MVMSpeshStatsByType *tss;
305
3.43M
    MVMint32 first_type_hit = 0;
306
3.43M
307
3.43M
    /* Bump version if needed. */
308
3.43M
    if (simf->ss->last_update != tc->instance->spesh_stats_version) {
309
36.4k
        simf->ss->last_update = tc->instance->spesh_stats_version;
310
36.4k
        MVM_repr_push_o(tc, sf_updated, (MVMObject *)simf->sf);
311
36.4k
    }
312
3.43M
313
3.43M
    /* Add OSR hits at callsite level and update depth. */
314
3.43M
    if (simf->osr_hits) {
315
197k
        simf->ss->osr_hits += simf->osr_hits;
316
197k
        simf->ss->by_callsite[simf->callsite_idx].osr_hits += simf->osr_hits;
317
197k
    }
318
3.43M
    if (frame_depth > simf->ss->by_callsite[simf->callsite_idx].max_depth)
319
237k
        simf->ss->by_callsite[simf->callsite_idx].max_depth = frame_depth;
320
3.43M
321
3.43M
    /* See if there's a type tuple to attach type-based stats to. */
322
3.43M
    if (simf->type_idx < 0 && simf->arg_types) {
323
3.31M
        simf->type_idx = by_type(tc, simf->ss, simf->callsite_idx, simf->arg_types);
324
3.31M
        simf->arg_types = NULL;
325
3.31M
        first_type_hit = 1;
326
3.31M
    }
327
3.43M
    tss = simf->type_idx >= 0
328
3.20M
        ? &(simf->ss->by_callsite[simf->callsite_idx].by_type[simf->type_idx])
329
3.43M
        : NULL;
330
3.43M
    if (tss) {
331
3.20M
        /* Incorporate data logged at offsets. */
332
3.20M
        MVMuint32 i;
333
18.3M
        for (i = 0; i < simf->offset_logs_used; i++) {
334
15.1M
            MVMSpeshLogEntry *e = simf->offset_logs[i];
335
15.1M
            switch (e->kind) {
336
10.1M
                case MVM_SPESH_LOG_TYPE:
337
10.1M
                case MVM_SPESH_LOG_RETURN: {
338
10.1M
                    MVMSpeshStatsByOffset *oss = by_offset(tc, tss,
339
10.1M
                        e->type.bytecode_offset);
340
10.1M
                    add_type_at_offset(tc, oss, simf->sf, e->type.type,
341
10.1M
                        e->type.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE);
342
10.1M
                    break;
343
10.1M
                }
344
4.98M
                case MVM_SPESH_LOG_INVOKE: {
345
4.98M
                    MVMSpeshStatsByOffset *oss = by_offset(tc, tss,
346
4.98M
                        e->invoke.bytecode_offset);
347
4.98M
                    add_invoke_at_offset(tc, oss, simf->sf, e->invoke.sf,
348
4.98M
                        e->invoke.caller_is_outer, e->invoke.was_multi);
349
4.98M
                    break;
350
10.1M
                }
351
5.35k
                case MVM_SPESH_LOG_PLUGIN_RESOLUTION: {
352
5.35k
                    MVMSpeshStatsByOffset *oss = by_offset(tc, tss,
353
5.35k
                        e->plugin.bytecode_offset);
354
5.35k
                    add_plugin_guard_at_offset(tc, oss, e->plugin.guard_index);
355
5.35k
                    break;
356
10.1M
                }
357
15.1M
            }
358
15.1M
        }
359
3.20M
360
3.20M
        /* Incorporate callsite type stats (what type tuples did we make a
361
3.20M
         * call with). */
362
5.87M
        for (i = 0; i < simf->call_type_info_used; i++) {
363
2.67M
            MVMSpeshSimCallType *info = &(simf->call_type_info[i]);
364
2.67M
            MVMSpeshStatsByOffset *oss = by_offset(tc, tss, info->bytecode_offset);
365
2.67M
            add_type_tuple_at_offset(tc, oss, simf->sf, info);
366
2.67M
        }
367
3.20M
368
3.20M
        /* Incorporate OSR hits and bump max depth. */
369
3.20M
        if (first_type_hit)
370
3.16M
            tss->hits++;
371
3.20M
        tss->osr_hits += simf->osr_hits;
372
3.20M
        if (frame_depth > tss->max_depth)
373
299k
            tss->max_depth = frame_depth;
374
3.20M
375
3.20M
        /* If the callee's last incovation matches the frame just invoked,
376
3.20M
         * then log the type tuple against the callsite. */
377
3.20M
        if (caller && caller->last_invoke_sf == simf->sf)
378
2.77M
            add_sim_call_type_info(tc, caller, caller->last_invoke_offset,
379
2.77M
                simf->ss->by_callsite[simf->callsite_idx].cs,
380
2.77M
                tss->arg_types);
381
3.20M
    }
382
3.43M
383
3.43M
    /* Clear up offset logs and call type info; they're either incorproated or
384
3.43M
     * to be tossed. Also zero OSR hits, so we don't over-inflate them if this
385
3.43M
     * frame entry survives. */
386
3.43M
    MVM_free(simf->offset_logs);
387
3.43M
    simf->offset_logs = NULL;
388
3.43M
    simf->offset_logs_used = simf->offset_logs_limit = 0;
389
3.43M
    MVM_free(simf->call_type_info);
390
3.43M
    simf->call_type_info = NULL;
391
3.43M
    simf->call_type_info_used = simf->call_type_info_limit = 0;
392
3.43M
    simf->osr_hits = 0;
393
3.43M
}
394
395
/* Pops the top frame from the sim stack. */
396
3.37M
void sim_stack_pop(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMObject *sf_updated) {
397
3.37M
    MVMSpeshSimStackFrame *simf;
398
3.37M
    MVMuint32 frame_depth;
399
3.37M
400
3.37M
    /* Pop off the simulated frame and incorporate logged data into the spesh
401
3.37M
     * stats model. */
402
3.37M
    if (sims->used == 0)
403
0
        MVM_panic(1, "Spesh stats: cannot pop an empty simulation stack");
404
3.37M
    sims->used--;
405
3.37M
    simf = &(sims->frames[sims->used]);
406
3.37M
    frame_depth = sims->depth--;
407
3.37M
408
3.37M
    /* Incorporate logged data into the statistics model. */
409
3.37M
    incorporate_stats(tc, simf, frame_depth,
410
3.37M
        sims->used > 0 ? &(sims->frames[sims->used - 1]) : NULL,
411
3.37M
        sf_updated);
412
3.37M
}
413
414
/* Gets the simulation stack frame for the specified correlation ID. If it is
415
 * not on the top, searches to see if it's further down. If it is, then pops
416
 * off the top to reach it. If it's not found at all, returns NULL and does
417
 * nothing to the simulation stack. */
418
MVMSpeshSimStackFrame * sim_stack_find(MVMThreadContext *tc, MVMSpeshSimStack *sims,
419
22.5M
                                       MVMuint32 cid, MVMObject *sf_updated) {
420
22.5M
    MVMuint32 found_at = sims->used;
421
22.5M
    while (found_at != 0) {
422
22.5M
        found_at--;
423
22.5M
        if (sims->frames[found_at].cid == cid) {
424
22.5M
            MVMint32 pop = (sims->used - found_at) - 1;
425
22.5M
            MVMint32 i;
426
22.5M
            for (i = 0; i < pop; i++)
427
15.2k
                sim_stack_pop(tc, sims, sf_updated);
428
22.5M
            return &(sims->frames[found_at]);
429
22.5M
        }
430
22.5M
    }
431
0
    return NULL;
432
22.5M
}
433
434
/* Pops all the frames in the stack simulation and frees the frames storage. */
435
0
void sim_stack_teardown(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMObject *sf_updated) {
436
0
    while (sims->used)
437
0
        sim_stack_pop(tc, sims, sf_updated);
438
0
    MVM_free(sims->frames);
439
0
}
440
441
/* Gets the parameter type slot from a simulation frame. */
442
5.23M
MVMSpeshStatsType * param_type(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf, MVMSpeshLogEntry *e) {
443
5.23M
    if (simf->arg_types) {
444
5.10M
        MVMuint16 idx = e->param.arg_idx;
445
5.10M
        MVMCallsite *cs = simf->ss->by_callsite[simf->callsite_idx].cs;
446
5.10M
        if (cs) {
447
5.10M
            MVMint32 flag_idx = idx < cs->num_pos
448
4.91M
                ? idx
449
182k
                : cs->num_pos + (((idx - 1) - cs->num_pos) / 2);
450
5.10M
            if (flag_idx >= cs->flag_count)
451
0
                MVM_panic(1, "Spesh stats: argument flag index out of bounds");
452
5.10M
            if (cs->arg_flags[flag_idx] & MVM_CALLSITE_ARG_OBJ)
453
4.84M
                return &(simf->arg_types[flag_idx]);
454
5.10M
        }
455
5.10M
    }
456
394k
    return NULL;
457
5.23M
}
458
459
/* Records a static value for a frame, unless it's already in the log. */
460
void add_static_value(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf, MVMint32 bytecode_offset,
461
48.3k
                      MVMObject *value) {
462
48.3k
    MVMSpeshStats *ss = simf->ss;
463
48.3k
    MVMuint32 i, id;
464
133k
    for (i = 0; i < ss->num_static_values; i++)
465
128k
        if (ss->static_values[i].bytecode_offset == bytecode_offset)
466
43.9k
            return;
467
4.41k
    id = ss->num_static_values++;
468
4.41k
    ss->static_values = MVM_realloc(ss->static_values,
469
4.41k
        ss->num_static_values * sizeof(MVMSpeshStatsStatic));
470
4.41k
    ss->static_values[id].bytecode_offset = bytecode_offset;
471
4.41k
    MVM_ASSIGN_REF(tc, &(simf->sf->body.spesh->common.header), ss->static_values[id].value, value);
472
4.41k
}
473
474
/* Decides whether to save or free the simulation stack. */
475
static void save_or_free_sim_stack(MVMThreadContext *tc, MVMSpeshSimStack *sims,
476
1.58k
                                   MVMThreadContext *save_on_tc, MVMObject *sf_updated) {
477
1.58k
    MVMint32 first_survivor = -1;
478
1.58k
    MVMint32 i;
479
1.58k
    if (save_on_tc) {
480
1.58k
        for (i = 0; i < sims->used; i++) {
481
1.58k
            MVMSpeshSimStackFrame *simf = &(sims->frames[i]);
482
1.58k
            MVMuint32 age = tc->instance->spesh_stats_version - simf->ss->last_update;
483
1.58k
            if (age < MVM_SPESH_STATS_MAX_AGE - 1) {
484
1.58k
                first_survivor = i;
485
1.58k
                break;
486
1.58k
            }
487
1.58k
        }
488
1.58k
    }
489
1.58k
    if (first_survivor >= 0) {
490
1.58k
        /* Move survivors to the start. */
491
1.58k
        if (first_survivor > 0) {
492
0
            sims->used -= first_survivor;
493
0
            memmove(sims->frames, sims->frames + first_survivor,
494
0
                sims->used * sizeof(MVMSpeshSimStackFrame));
495
0
        }
496
1.58k
497
1.58k
        /* Incorporate data from the rest into the stats model, clearing it
498
1.58k
         * away. */
499
1.58k
        i = sims->used - 1;
500
60.8k
        while (i >= 0) {
501
59.2k
            incorporate_stats(tc, &(sims->frames[i]), first_survivor + i,
502
57.6k
                i > 0 ? &(sims->frames[i - 1]) : NULL,
503
59.2k
                sf_updated);
504
59.2k
            i--;
505
59.2k
        }
506
1.58k
507
1.58k
        /* Save frames for next time. */
508
1.58k
        save_on_tc->spesh_sim_stack = sims;
509
1.58k
    }
510
0
    else {
511
0
        /* Everything on the simulated stack is too old; throw it away. */
512
0
        sim_stack_teardown(tc, sims, sf_updated);
513
0
        MVM_free(sims);
514
0
    }
515
1.58k
}
516
517
/* Receives a spesh log and updates static frame statistics. Each static frame
518
 * that is updated is pushed once into sf_updated. */
519
1.60k
void MVM_spesh_stats_update(MVMThreadContext *tc, MVMSpeshLog *sl, MVMObject *sf_updated) {
520
1.60k
    MVMuint32 i;
521
1.60k
    MVMuint32 n = sl->body.used;
522
1.60k
    MVMSpeshSimStack *sims;
523
1.60k
    MVMThreadContext *log_from_tc = sl->body.thread->body.tc;
524
1.60k
#if MVM_GC_DEBUG
525
    tc->in_spesh = 1;
526
#endif
527
1.60k
    /* See if we have a simulation stack left over from before; create a new
528
1.60k
     * one if not. */
529
1.60k
    if (log_from_tc && log_from_tc->spesh_sim_stack) {
530
1.45k
        /* Filter out those whose stats pointer is outdated. */
531
1.45k
        MVMuint32 insert_pos = 0;
532
1.45k
        sims = log_from_tc->spesh_sim_stack;
533
55.7k
        for (i = 0; i < sims->used; i++) {
534
54.3k
            MVMSpeshStats *cur_stats = sims->frames[i].sf->body.spesh->body.spesh_stats;
535
54.3k
            if (cur_stats == sims->frames[i].ss) {
536
54.3k
                if (i != insert_pos)
537
0
                    sims->frames[insert_pos] = sims->frames[i];
538
54.3k
                insert_pos++;
539
54.3k
            }
540
54.3k
        }
541
1.45k
        sims->used = insert_pos;
542
1.45k
        log_from_tc->spesh_sim_stack = NULL;
543
1.45k
    }
544
145
    else {
545
145
        sims = MVM_malloc(sizeof(MVMSpeshSimStack));
546
145
        sim_stack_init(tc, sims);
547
145
    }
548
1.60k
549
1.60k
    /* Process the log entries. */
550
25.9M
    for (i = 0; i < n; i++) {
551
25.9M
        MVMSpeshLogEntry *e = &(sl->body.entries[i]);
552
25.9M
        switch (e->kind) {
553
3.38M
            case MVM_SPESH_LOG_ENTRY: {
554
3.38M
                MVMSpeshStats *ss = stats_for(tc, e->entry.sf);
555
3.38M
                MVMuint32 callsite_idx;
556
3.38M
                if (ss->last_update != tc->instance->spesh_stats_version) {
557
264k
                    ss->last_update = tc->instance->spesh_stats_version;
558
264k
                    MVM_repr_push_o(tc, sf_updated, (MVMObject *)e->entry.sf);
559
264k
                }
560
3.38M
                ss->hits++;
561
3.38M
                callsite_idx = by_callsite_idx(tc, ss, e->entry.cs);
562
3.38M
                ss->by_callsite[callsite_idx].hits++;
563
3.38M
                sim_stack_push(tc, sims, e->entry.sf, ss, e->id, callsite_idx);
564
3.38M
                break;
565
3.38M
            }
566
5.23M
            case MVM_SPESH_LOG_PARAMETER: {
567
5.23M
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
568
5.23M
                if (simf) {
569
5.23M
                    MVMSpeshStatsType *type_slot = param_type(tc, simf, e);
570
5.23M
                    if (type_slot) {
571
4.84M
                        MVM_ASSIGN_REF(tc, &(simf->sf->body.spesh->common.header),
572
4.84M
                            type_slot->type, e->param.type);
573
4.84M
                        type_slot->type_concrete =
574
4.84M
                            e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE ? 1 : 0;
575
4.84M
                        type_slot->rw_cont =
576
4.84M
                            e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_RW_CONT ? 1 : 0;
577
4.84M
                    }
578
5.23M
                }
579
5.23M
                break;
580
3.38M
            }
581
0
            case MVM_SPESH_LOG_PARAMETER_DECONT: {
582
0
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
583
0
                if (simf) {
584
0
                    MVMSpeshStatsType *type_slot = param_type(tc, simf, e);
585
0
                    if (type_slot) {
586
0
                        MVM_ASSIGN_REF(tc, &(simf->sf->body.spesh->common.header),
587
0
                            type_slot->decont_type, e->param.type);
588
0
                        type_slot->decont_type_concrete =
589
0
                            e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE;
590
0
                    }
591
0
                }
592
0
                break;
593
3.38M
            }
594
13.2M
            case MVM_SPESH_LOG_TYPE:
595
13.2M
            case MVM_SPESH_LOG_INVOKE:
596
13.2M
            case MVM_SPESH_LOG_PLUGIN_RESOLUTION: {
597
13.2M
                /* We only incorporate these into the model later, and only
598
13.2M
                 * then if we need to. For now, just keep references to
599
13.2M
                 * them. */
600
13.2M
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
601
13.2M
                if (simf) {
602
13.2M
                    if (simf->offset_logs_used == simf->offset_logs_limit) {
603
2.84M
                        simf->offset_logs_limit += 32;
604
2.84M
                        simf->offset_logs = MVM_realloc(simf->offset_logs,
605
2.84M
                            simf->offset_logs_limit * sizeof(MVMSpeshLogEntry *));
606
2.84M
                    }
607
13.2M
                    simf->offset_logs[simf->offset_logs_used++] = e;
608
13.2M
                    if (e->kind == MVM_SPESH_LOG_INVOKE) {
609
5.19M
                        simf->last_invoke_offset = e->invoke.bytecode_offset;
610
5.19M
                        simf->last_invoke_sf = e->invoke.sf;
611
5.19M
                    }
612
13.2M
                }
613
13.2M
                break;
614
13.2M
            }
615
594k
            case MVM_SPESH_LOG_OSR: {
616
594k
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
617
594k
                if (simf)
618
594k
                    simf->osr_hits++;
619
594k
                break;
620
13.2M
            }
621
48.3k
            case MVM_SPESH_LOG_STATIC: {
622
48.3k
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
623
48.3k
                if (simf)
624
48.3k
                    add_static_value(tc, simf, e->value.bytecode_offset, e->value.value);
625
48.3k
                break;
626
13.2M
            }
627
3.36M
            case MVM_SPESH_LOG_RETURN: {
628
3.36M
                MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated);
629
3.36M
                if (simf) {
630
3.36M
                    MVMStaticFrame *called_sf = simf->sf;
631
3.36M
                    sim_stack_pop(tc, sims, sf_updated);
632
3.36M
                    if (e->type.type && sims->used) {
633
2.80M
                        MVMSpeshSimStackFrame *caller = &(sims->frames[sims->used - 1]);
634
2.80M
                        if (called_sf == caller->last_invoke_sf) {
635
2.44M
                            if (caller->offset_logs_used == caller->offset_logs_limit) {
636
24.6k
                                caller->offset_logs_limit += 32;
637
24.6k
                                caller->offset_logs = MVM_realloc(caller->offset_logs,
638
24.6k
                                    caller->offset_logs_limit * sizeof(MVMSpeshLogEntry *));
639
24.6k
                            }
640
2.44M
                            e->type.bytecode_offset = caller->last_invoke_offset;
641
2.44M
                            caller->offset_logs[caller->offset_logs_used++] = e;
642
2.44M
                        }
643
2.80M
                    }
644
3.36M
                }
645
3.36M
                break;
646
13.2M
            }
647
25.9M
        }
648
25.9M
    }
649
1.58k
    save_or_free_sim_stack(tc, sims, log_from_tc, sf_updated);
650
1.58k
#if MVM_GC_DEBUG
651
    tc->in_spesh = 0;
652
#endif
653
1.58k
}
654
655
/* Takes an array of frames we recently updated the stats in. If they weren't
656
 * updated in a while, clears them out. */
657
1.47k
void MVM_spesh_stats_cleanup(MVMThreadContext *tc, MVMObject *check_frames) {
658
1.47k
    MVMint64 elems = MVM_repr_elems(tc, check_frames);
659
1.47k
    MVMint64 insert_pos = 0;
660
1.47k
    MVMint64 i;
661
1.80M
    for (i = 0; i < elems; i++) {
662
1.80M
        MVMStaticFrame *sf = (MVMStaticFrame *)MVM_repr_at_pos_o(tc, check_frames, i);
663
1.80M
        MVMStaticFrameSpesh *spesh = sf->body.spesh;
664
1.80M
        MVMSpeshStats *ss = spesh->body.spesh_stats;
665
1.80M
        if (!ss) {
666
51.3k
            /* No stats; already destroyed, don't keep this frame under
667
51.3k
             * consideration. */
668
51.3k
        }
669
1.75M
        else if (tc->instance->spesh_stats_version - ss->last_update > MVM_SPESH_STATS_MAX_AGE) {
670
20.4k
            MVM_spesh_stats_destroy(tc, ss);
671
20.4k
            MVM_free(spesh->body.spesh_stats);
672
20.4k
            spesh->body.spesh_stats = NULL;
673
20.4k
        }
674
1.73M
        else {
675
1.73M
            MVM_repr_bind_pos_o(tc, check_frames, insert_pos++, (MVMObject *)sf);
676
1.73M
        }
677
1.80M
    }
678
1.47k
    MVM_repr_pos_set_elems(tc, check_frames, insert_pos);
679
1.47k
}
680
681
11.1k
void MVM_spesh_stats_gc_mark(MVMThreadContext *tc, MVMSpeshStats *ss, MVMGCWorklist *worklist) {
682
11.1k
    if (ss) {
683
9.57k
        MVMuint32 i, j, k, l, m;
684
21.8k
        for (i = 0; i < ss->num_by_callsite; i++) {
685
12.2k
            MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]);
686
45.4k
            for (j = 0; j < by_cs->num_by_type; j++) {
687
33.1k
                MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]);
688
33.1k
                MVMuint32 num_types = by_cs->cs->flag_count;
689
105k
                for (k = 0; k < num_types; k++) {
690
72.5k
                    MVM_gc_worklist_add(tc, worklist, &(by_type->arg_types[k].type));
691
72.5k
                    MVM_gc_worklist_add(tc, worklist, &(by_type->arg_types[k].decont_type));
692
72.5k
                }
693
141k
                for (k = 0; k < by_type->num_by_offset; k++) {
694
107k
                    MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]);
695
214k
                    for (l = 0; l < by_offset->num_types; l++)
696
106k
                        MVM_gc_worklist_add(tc, worklist, &(by_offset->types[l].type));
697
165k
                    for (l = 0; l < by_offset->num_invokes; l++)
698
57.0k
                        MVM_gc_worklist_add(tc, worklist, &(by_offset->invokes[l].sf));
699
174k
                    for (l = 0; l < by_offset->num_type_tuples; l++) {
700
66.2k
                        MVMSpeshStatsType *off_types = by_offset->type_tuples[l].arg_types;
701
66.2k
                        MVMuint32 num_off_types = by_offset->type_tuples[l].cs->flag_count;
702
198k
                        for (m = 0; m < num_off_types; m++) {
703
132k
                            MVM_gc_worklist_add(tc, worklist, &(off_types[m].type));
704
132k
                            MVM_gc_worklist_add(tc, worklist, &(off_types[m].decont_type));
705
132k
                        }
706
66.2k
                    }
707
107k
                }
708
33.1k
            }
709
12.2k
        }
710
10.9k
        for (i = 0; i < ss->num_static_values; i++)
711
1.37k
            MVM_gc_worklist_add(tc, worklist, &(ss->static_values[i].value));
712
9.57k
    }
713
11.1k
}
714
715
0
void MVM_spesh_stats_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *snapshot, MVMSpeshStats *ss) {
716
0
    if (ss) {
717
0
        MVMuint32 i, j, k, l, m;
718
0
        for (i = 0; i < ss->num_by_callsite; i++) {
719
0
            MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]);
720
0
            for (j = 0; j < by_cs->num_by_type; j++) {
721
0
                MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]);
722
0
                MVMuint32 num_types = by_cs->cs->flag_count;
723
0
                for (k = 0; k < num_types; k++) {
724
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
725
0
                        (MVMCollectable*)(by_type->arg_types[k].type), "type");
726
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
727
0
                        (MVMCollectable*)(by_type->arg_types[k].decont_type), "decont type");
728
0
                }
729
0
                for (k = 0; k < by_type->num_by_offset; k++) {
730
0
                    MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]);
731
0
                    for (l = 0; l < by_offset->num_types; l++)
732
0
                        MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
733
0
                            (MVMCollectable*)(by_offset->types[l].type), "type at offset");
734
0
                    for (l = 0; l < by_offset->num_invokes; l++)
735
0
                        MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
736
0
                            (MVMCollectable*)(by_offset->invokes[l].sf), "invoke");
737
0
                    for (l = 0; l < by_offset->num_type_tuples; l++) {
738
0
                        MVMSpeshStatsType *off_types = by_offset->type_tuples[l].arg_types;
739
0
                        MVMuint32 num_off_types = by_offset->type_tuples[l].cs->flag_count;
740
0
                        for (m = 0; m < num_off_types; m++) {
741
0
                            MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
742
0
                                (MVMCollectable*)(off_types[m].type), "type tuple type");
743
0
                            MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
744
0
                                (MVMCollectable*)(off_types[m].decont_type), "type tuple deconted type");
745
0
                        }
746
0
                    }
747
0
                }
748
0
            }
749
0
        }
750
0
        for (i = 0; i < ss->num_static_values; i++)
751
0
            MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
752
0
                (MVMCollectable*)(ss->static_values[i].value), "static value");
753
0
    }
754
0
}
755
756
20.4k
void MVM_spesh_stats_destroy(MVMThreadContext *tc, MVMSpeshStats *ss) {
757
20.4k
    if (ss) {
758
20.4k
        MVMuint32 i, j, k, l;
759
42.5k
        for (i = 0; i < ss->num_by_callsite; i++) {
760
22.0k
            MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]);
761
50.9k
            for (j = 0; j < by_cs->num_by_type; j++) {
762
28.9k
                MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]);
763
129k
                for (k = 0; k < by_type->num_by_offset; k++) {
764
100k
                    MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]);
765
100k
                    MVM_free(by_offset->types);
766
100k
                    MVM_free(by_offset->invokes);
767
147k
                    for (l = 0; l < by_offset->num_type_tuples; l++)
768
46.1k
                        MVM_free(by_offset->type_tuples[l].arg_types);
769
100k
                    MVM_free(by_offset->type_tuples);
770
100k
                    MVM_free(by_offset->plugin_guards);
771
100k
                }
772
28.9k
                MVM_free(by_type->by_offset);
773
28.9k
                MVM_free(by_type->arg_types);
774
28.9k
            }
775
22.0k
            MVM_free(by_cs->by_type);
776
22.0k
        }
777
20.4k
        MVM_free(ss->by_callsite);
778
20.4k
        MVM_free(ss->static_values);
779
20.4k
    }
780
20.4k
}
781
782
void MVM_spesh_sim_stack_gc_mark(MVMThreadContext *tc, MVMSpeshSimStack *sims,
783
750
                                 MVMGCWorklist *worklist) {
784
401
    MVMuint32 n = sims ? sims->used : 0;
785
750
    MVMuint32 i;
786
11.5k
    for (i = 0; i < n; i++) {
787
10.7k
        MVMSpeshSimStackFrame *simf = &(sims->frames[i]);
788
10.7k
        MVM_gc_worklist_add(tc, worklist, &(simf->sf));
789
10.7k
        MVM_gc_worklist_add(tc, worklist, &(simf->last_invoke_sf));
790
10.7k
    }
791
750
}
792
793
0
void MVM_spesh_sim_stack_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMSpeshSimStack *sims) {
794
0
    MVMuint32 n = sims ? sims->used : 0;
795
0
    MVMuint32 i;
796
0
    for (i = 0; i < n; i++) {
797
0
        MVMSpeshSimStackFrame *simf = &(sims->frames[i]);
798
0
        MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
799
0
            (MVMCollectable*)(simf->sf), "staticframe");
800
0
        MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
801
0
            (MVMCollectable*)(simf->last_invoke_sf), "last invoked staticframe");
802
0
    }
803
0
}
804
805
23
void MVM_spesh_sim_stack_destroy(MVMThreadContext *tc, MVMSpeshSimStack *sims) {
806
23
    if (sims) {
807
2
        MVM_free(sims->frames);
808
2
        MVM_free(sims);
809
2
    }
810
23
}