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