/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 | } |