Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/spesh/candidate.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Calculates the work and env sizes based on the number of locals and
4
 * lexicals. */
5
static void calculate_work_env_sizes(MVMThreadContext *tc, MVMStaticFrame *sf,
6
31.0k
                                     MVMSpeshCandidate *c) {
7
31.0k
    MVMuint32 max_callsite_size;
8
31.0k
    MVMint32 i;
9
31.0k
10
31.0k
    max_callsite_size = sf->body.cu->body.max_callsite_size;
11
31.0k
12
35.3k
    for (i = 0; i < c->num_inlines; i++) {
13
4.25k
        MVMuint32 cs = c->inlines[i].code->body.sf->body.cu->body.max_callsite_size;
14
4.25k
        if (cs > max_callsite_size)
15
444
            max_callsite_size = cs;
16
4.25k
    }
17
31.0k
18
31.0k
    c->work_size = (c->num_locals + max_callsite_size) * sizeof(MVMRegister);
19
31.0k
    c->env_size = c->num_lexicals * sizeof(MVMRegister);
20
31.0k
}
21
22
/* Tries to set up a specialization of the bytecode for a given arg tuple.
23
 * Doesn't do the actual optimizations, just works out the guards and does
24
 * any simple argument transformations, and then inserts logging to record
25
 * what we see. Produces bytecode with those transformations and the logging
26
 * instructions. */
27
MVMSpeshCandidate * MVM_spesh_candidate_setup(MVMThreadContext *tc,
28
        MVMStaticFrame *static_frame, MVMCallsite *callsite, MVMRegister *args,
29
17.1k
        MVMint32 osr) {
30
17.1k
    MVMSpeshCandidate *result;
31
17.1k
    MVMSpeshGuard *guards;
32
17.1k
    MVMSpeshCode *sc;
33
17.1k
    MVMint32 num_spesh_slots, num_log_slots, num_guards, *deopts, num_deopts;
34
17.1k
    MVMuint16 num_locals, num_lexicals, used;
35
17.1k
    MVMCollectable **spesh_slots, **log_slots;
36
17.1k
    char *before = 0;
37
17.1k
    char *after = 0;
38
17.1k
    MVMSpeshGraph *sg;
39
17.1k
40
17.1k
    /* If we've reached our specialization limit, don't continue. */
41
17.1k
    if (tc->instance->spesh_limit)
42
0
        if (++tc->instance->spesh_produced > tc->instance->spesh_limit)
43
0
            return NULL;
44
17.1k
45
17.1k
    /* If we're profiling, log we're starting spesh work. Also track entry
46
17.1k
     * to spesh when GC debugging. */
47
17.1k
    if (tc->instance->profiling)
48
0
        MVM_profiler_log_spesh_start(tc);
49
17.1k
#if MVM_GC_DEBUG
50
    tc->in_spesh = 1;
51
#endif
52
17.1k
53
17.1k
    /* Do initial generation of the specialization, working out the argument
54
17.1k
     * guards and adding logging. */
55
17.1k
    sg = MVM_spesh_graph_create(tc, static_frame, 0, 1);
56
17.1k
    if (tc->instance->spesh_log_fh)
57
0
        before = MVM_spesh_dump(tc, sg);
58
17.1k
    MVM_spesh_args(tc, sg, callsite, args);
59
17.1k
    MVM_spesh_log_add_logging(tc, sg, osr);
60
17.1k
    if (tc->instance->spesh_log_fh)
61
0
        after = MVM_spesh_dump(tc, sg);
62
17.1k
    sc              = MVM_spesh_codegen(tc, sg);
63
17.1k
    num_guards      = sg->num_arg_guards;
64
17.1k
    guards          = sg->arg_guards;
65
17.1k
    num_deopts      = sg->num_deopt_addrs;
66
17.1k
    deopts          = sg->deopt_addrs;
67
17.1k
    num_spesh_slots = sg->num_spesh_slots;
68
17.1k
    spesh_slots     = sg->spesh_slots;
69
17.1k
    num_log_slots   = sg->num_log_slots;
70
17.1k
    log_slots       = sg->log_slots;
71
17.1k
    num_locals      = sg->num_locals;
72
17.1k
    num_lexicals    = sg->num_lexicals;
73
17.1k
74
17.1k
    /* Now try to add it. Note there's a slim chance another thread beat us
75
17.1k
     * to doing so. Also other threads can read the specializations without
76
17.1k
     * lock, so make absolutely sure we increment the count of them after we
77
17.1k
     * add the new one. */
78
17.1k
    result    = NULL;
79
17.1k
    used      = 0;
80
17.1k
    uv_mutex_lock(&tc->instance->mutex_spesh_install);
81
17.1k
    if (static_frame->body.num_spesh_candidates < MVM_SPESH_LIMIT) {
82
17.1k
        MVMint32 num_spesh = static_frame->body.num_spesh_candidates;
83
17.1k
        MVMint32 i;
84
30.8k
        for (i = 0; i < num_spesh; i++) {
85
13.6k
            MVMSpeshCandidate *compare = &static_frame->body.spesh_candidates[i];
86
13.6k
            if (compare->cs == callsite && compare->num_guards == num_guards &&
87
8.17k
                memcmp(compare->guards, guards, num_guards * sizeof(MVMSpeshGuard)) == 0) {
88
0
                /* Beaten! */
89
0
                result = osr ? NULL : &static_frame->body.spesh_candidates[i];
90
0
                break;
91
0
            }
92
13.6k
        }
93
17.1k
        if (!result) {
94
17.1k
            if (!static_frame->body.spesh_candidates)
95
9.31k
                static_frame->body.spesh_candidates = MVM_calloc(
96
9.31k
                    MVM_SPESH_LIMIT, sizeof(MVMSpeshCandidate));
97
17.1k
            result                      = &static_frame->body.spesh_candidates[num_spesh];
98
17.1k
            result->cs                  = callsite;
99
17.1k
            result->num_guards          = num_guards;
100
17.1k
            result->guards              = guards;
101
17.1k
            result->bytecode            = sc->bytecode;
102
17.1k
            result->bytecode_size       = sc->bytecode_size;
103
17.1k
            result->handlers            = sc->handlers;
104
17.1k
            result->num_handlers        = sg->num_handlers;
105
17.1k
            result->num_spesh_slots     = num_spesh_slots;
106
17.1k
            result->spesh_slots         = spesh_slots;
107
17.1k
            result->num_deopts          = num_deopts;
108
17.1k
            result->deopts              = deopts;
109
17.1k
            result->num_log_slots       = num_log_slots;
110
17.1k
            result->log_slots           = log_slots;
111
17.1k
            result->num_locals          = num_locals;
112
17.1k
            result->num_lexicals        = num_lexicals;
113
17.1k
            result->local_types         = sg->local_types;
114
17.1k
            result->lexical_types       = sg->lexical_types;
115
17.1k
            result->sg                  = sg;
116
17.1k
            result->log_enter_idx       = 0;
117
17.1k
            result->log_exits_remaining = MVM_SPESH_LOG_RUNS;
118
17.1k
            calculate_work_env_sizes(tc, static_frame, result);
119
17.1k
            if (osr)
120
149
                result->osr_logging = 1;
121
17.1k
            MVM_barrier();
122
17.1k
            static_frame->body.num_spesh_candidates++;
123
17.1k
            if (static_frame->common.header.flags & MVM_CF_SECOND_GEN)
124
17.1k
                MVM_gc_write_barrier_hit(tc, (MVMCollectable *)static_frame);
125
17.1k
            if (tc->instance->spesh_log_fh) {
126
0
                char *c_name = MVM_string_utf8_encode_C_string(tc, static_frame->body.name);
127
0
                char *c_cuid = MVM_string_utf8_encode_C_string(tc, static_frame->body.cuuid);
128
0
                fprintf(tc->instance->spesh_log_fh,
129
0
                    "Inserting logging for specialization of '%s' (cuid: %s)\n\n", c_name, c_cuid);
130
0
                fprintf(tc->instance->spesh_log_fh,
131
0
                    "Before:\n%s\nAfter:\n%s\n\n========\n\n", before, after);
132
0
                fflush(tc->instance->spesh_log_fh);
133
0
                MVM_free(c_name);
134
0
                MVM_free(c_cuid);
135
0
            }
136
17.1k
            used = 1;
137
17.1k
        }
138
17.1k
    }
139
17.1k
    MVM_free(after);
140
17.1k
    MVM_free(before);
141
17.1k
    if (result && !used) {
142
0
        MVM_free(sc->bytecode);
143
0
        if (sc->handlers)
144
0
            MVM_free(sc->handlers);
145
0
        MVM_spesh_graph_destroy(tc, sg);
146
0
    }
147
17.1k
    uv_mutex_unlock(&tc->instance->mutex_spesh_install);
148
17.1k
149
17.1k
    /* If we're profiling or GC debugging, log we've finished spesh work. */
150
17.1k
    if (tc->instance->profiling)
151
0
        MVM_profiler_log_spesh_end(tc);
152
17.1k
#if MVM_GC_DEBUG
153
    tc->in_spesh = 0;
154
#endif
155
17.1k
156
17.1k
    MVM_free(sc);
157
17.1k
    return result;
158
17.1k
}
159
160
/* Called at the point we have the finished logging for a specialization and
161
 * so are ready to do the specialization work for it. We can be sure this
162
 * will only be called once, and when nothing is running the logging version
163
 * of the code. */
164
void MVM_spesh_candidate_specialize(MVMThreadContext *tc, MVMStaticFrame *static_frame,
165
13.8k
        MVMSpeshCandidate *candidate) {
166
13.8k
    MVMSpeshCode  *sc;
167
13.8k
    MVMSpeshGraph *sg;
168
13.8k
    MVMJitGraph   *jg = NULL;
169
13.8k
170
13.8k
    /* If we're profiling or GC debugging, log we're starting spesh work. */
171
13.8k
    if (tc->instance->profiling)
172
0
        MVM_profiler_log_spesh_start(tc);
173
13.8k
#if MVM_GC_DEBUG
174
    tc->in_spesh = 1;
175
#endif
176
13.8k
177
13.8k
    /* Obtain the graph, add facts, and do optimization work. */
178
13.8k
    sg = candidate->sg;
179
13.8k
    MVM_spesh_facts_discover(tc, sg);
180
13.8k
    MVM_spesh_optimize(tc, sg);
181
13.8k
182
13.8k
    /* Dump updated graph if needed. */
183
13.8k
    if (tc->instance->spesh_log_fh) {
184
0
        char *c_name = MVM_string_utf8_encode_C_string(tc, static_frame->body.name);
185
0
        char *c_cuid = MVM_string_utf8_encode_C_string(tc, static_frame->body.cuuid);
186
0
        char *dump   = MVM_spesh_dump(tc, sg);
187
0
        fprintf(tc->instance->spesh_log_fh,
188
0
            "Finished specialization of '%s' (cuid: %s)\n\n", c_name, c_cuid);
189
0
        fprintf(tc->instance->spesh_log_fh,
190
0
            "%s\n\n========\n\n", dump);
191
0
        fflush(tc->instance->spesh_log_fh);
192
0
        MVM_free(dump);
193
0
        MVM_free(c_name);
194
0
        MVM_free(c_cuid);
195
0
    }
196
13.8k
197
13.8k
    /* Generate code, and replace that in the candidate. */
198
13.8k
    sc = MVM_spesh_codegen(tc, sg);
199
13.8k
    MVM_free(candidate->bytecode);
200
13.8k
    if (candidate->handlers)
201
1.63k
        MVM_free(candidate->handlers);
202
13.8k
    candidate->bytecode      = sc->bytecode;
203
13.8k
    candidate->bytecode_size = sc->bytecode_size;
204
13.8k
    candidate->handlers      = sc->handlers;
205
13.8k
    candidate->num_handlers  = sg->num_handlers;
206
13.8k
    candidate->num_deopts    = sg->num_deopt_addrs;
207
13.8k
    candidate->deopts        = sg->deopt_addrs;
208
13.8k
    candidate->num_locals    = sg->num_locals;
209
13.8k
    candidate->num_lexicals  = sg->num_lexicals;
210
13.8k
    candidate->num_inlines   = sg->num_inlines;
211
13.8k
    candidate->inlines       = sg->inlines;
212
13.8k
    candidate->local_types   = sg->local_types;
213
13.8k
    candidate->lexical_types = sg->lexical_types;
214
13.8k
    calculate_work_env_sizes(tc, static_frame, candidate);
215
13.8k
    MVM_free(sc);
216
13.8k
217
13.8k
    /* Try to JIT compile the optimised graph. The JIT graph hangs from
218
13.8k
     * the spesh graph and can safely be deleted with it. */
219
13.8k
    if (tc->instance->jit_enabled) {
220
13.8k
        jg = MVM_jit_try_make_graph(tc, sg);
221
13.8k
        if (jg != NULL)
222
12.3k
            candidate->jitcode = MVM_jit_compile_graph(tc, jg);
223
13.8k
    }
224
13.8k
225
13.8k
    /* No longer need log slots. */
226
13.8k
    MVM_free(candidate->log_slots);
227
13.8k
    candidate->log_slots = NULL;
228
13.8k
229
13.8k
    /* Update spesh slots. */
230
13.8k
    candidate->num_spesh_slots = sg->num_spesh_slots;
231
13.8k
    candidate->spesh_slots     = sg->spesh_slots;
232
13.8k
233
13.8k
    /* May now be referencing nursery objects, so barrier just in case. */
234
13.8k
    if (static_frame->common.header.flags & MVM_CF_SECOND_GEN)
235
13.8k
        MVM_gc_write_barrier_hit(tc, (MVMCollectable *)static_frame);
236
13.8k
237
13.8k
    /* Destroy spesh graph, and finally clear point to it in the candidate,
238
13.8k
     * which unblocks use of the specialization. */
239
13.8k
    if (candidate->num_inlines) {
240
2.49k
        MVMint32 i;
241
6.75k
        for (i = 0; i < candidate->num_inlines; i++)
242
4.25k
            if (candidate->inlines[i].g) {
243
3.88k
                MVM_spesh_graph_destroy(tc, candidate->inlines[i].g);
244
3.88k
                candidate->inlines[i].g = NULL;
245
3.88k
            }
246
2.49k
    }
247
13.8k
    MVM_spesh_graph_destroy(tc, sg);
248
13.8k
    MVM_barrier();
249
13.8k
    candidate->sg = NULL;
250
13.8k
251
13.8k
    /* If we're profiling or GC debugging, log we've finished spesh work. */
252
13.8k
    if (tc->instance->profiling)
253
0
        MVM_profiler_log_spesh_end(tc);
254
13.8k
#if MVM_GC_DEBUG
255
    tc->in_spesh = 0;
256
#endif
257
13.8k
}
258
259
260
0
void MVM_spesh_candidate_destroy(MVMThreadContext *tc, MVMSpeshCandidate *candidate) {
261
0
    if (candidate->sg)
262
0
        MVM_spesh_graph_destroy(tc, candidate->sg);
263
0
    MVM_free(candidate->guards);
264
0
    MVM_free(candidate->bytecode);
265
0
    MVM_free(candidate->handlers);
266
0
    MVM_free(candidate->spesh_slots);
267
0
    MVM_free(candidate->deopts);
268
0
    MVM_free(candidate->log_slots);
269
0
    MVM_free(candidate->inlines);
270
0
    MVM_free(candidate->local_types);
271
0
    MVM_free(candidate->lexical_types);
272
0
    if (candidate->jitcode)
273
0
        MVM_jit_destroy_code(tc, candidate->jitcode);
274
0
}