/home/travis/build/MoarVM/MoarVM/src/core/frame.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* This allows the dynlex cache to be disabled when bug hunting, if needed. */ |
4 | | #define MVM_DYNLEX_CACHE_ENABLED 1 |
5 | | |
6 | | /* Computes the initial work area for a frame or a specialization of a frame. */ |
7 | | MVMRegister * MVM_frame_initial_work(MVMThreadContext *tc, MVMuint16 *local_types, |
8 | 102k | MVMuint16 num_locals) { |
9 | 102k | MVMuint16 i; |
10 | 102k | MVMRegister *work_initial = MVM_calloc(num_locals, sizeof(MVMRegister)); |
11 | 1.75M | for (i = 0; i < num_locals; i++) |
12 | 1.65M | if (local_types[i] == MVM_reg_obj) |
13 | 1.19M | work_initial[i].o = tc->instance->VMNull; |
14 | 102k | return work_initial; |
15 | 102k | } |
16 | | |
17 | | /* Takes a static frame and does various one-off calculations about what |
18 | | * space it shall need. Also triggers bytecode verification of the frame's |
19 | | * bytecode. */ |
20 | 102k | static void prepare_and_verify_static_frame(MVMThreadContext *tc, MVMStaticFrame *static_frame) { |
21 | 102k | MVMStaticFrameBody *static_frame_body = &static_frame->body; |
22 | 102k | MVMCompUnit *cu = static_frame_body->cu; |
23 | 102k | |
24 | 102k | /* Ensure the frame is fully deserialized. */ |
25 | 102k | if (!static_frame_body->fully_deserialized) |
26 | 102k | MVM_bytecode_finish_frame(tc, cu, static_frame, 0); |
27 | 102k | |
28 | 102k | /* Take compilation unit lock, to make sure we don't race to do the |
29 | 102k | * frame preparation/verification work. */ |
30 | 102k | MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)cu->body.deserialize_frame_mutex); |
31 | 102k | if (static_frame->body.instrumentation_level == 0) { |
32 | 102k | /* Work size is number of locals/registers plus size of the maximum |
33 | 102k | * call site argument list. */ |
34 | 102k | static_frame_body->work_size = sizeof(MVMRegister) * |
35 | 102k | (static_frame_body->num_locals + static_frame_body->cu->body.max_callsite_size); |
36 | 102k | |
37 | 102k | /* Validate the bytecode. */ |
38 | 102k | MVM_validate_static_frame(tc, static_frame); |
39 | 102k | |
40 | 102k | /* Obtain an index to each threadcontext's lexotic pool table */ |
41 | 102k | static_frame_body->pool_index = MVM_incr(&tc->instance->num_frames_run); |
42 | 102k | |
43 | 102k | /* Compute work area initial state that we can memcpy into place each |
44 | 102k | * time. */ |
45 | 102k | if (static_frame_body->num_locals) |
46 | 102k | static_frame_body->work_initial = MVM_frame_initial_work(tc, |
47 | 102k | static_frame_body->local_types, |
48 | 102k | static_frame_body->num_locals); |
49 | 102k | |
50 | 102k | /* Check if we have any state var lexicals. */ |
51 | 102k | if (static_frame_body->static_env_flags) { |
52 | 102k | MVMuint8 *flags = static_frame_body->static_env_flags; |
53 | 102k | MVMint64 numlex = static_frame_body->num_lexicals; |
54 | 102k | MVMint64 i; |
55 | 214k | for (i = 0; i < numlex; i++) |
56 | 112k | if (flags[i] == 2) { |
57 | 2 | static_frame_body->has_state_vars = 1; |
58 | 2 | break; |
59 | 2 | } |
60 | 102k | } |
61 | 102k | |
62 | 102k | /* Set its spesh threshold. */ |
63 | 102k | static_frame_body->spesh_threshold = MVM_spesh_threshold(tc, static_frame); |
64 | 102k | } |
65 | 102k | |
66 | 102k | /* Unlock, now we're finished. */ |
67 | 102k | MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)cu->body.deserialize_frame_mutex); |
68 | 102k | } |
69 | | |
70 | | /* When we don't match the current instrumentation level, we hit this. It may |
71 | | * simply be that we never invoked the frame, in which case we prepare and |
72 | | * verify it. It may also be because we need to instrument the code for |
73 | | * profiling. */ |
74 | 102k | static void instrumentation_level_barrier(MVMThreadContext *tc, MVMStaticFrame *static_frame) { |
75 | 102k | /* Prepare and verify if needed. */ |
76 | 102k | if (static_frame->body.instrumentation_level == 0) |
77 | 102k | prepare_and_verify_static_frame(tc, static_frame); |
78 | 102k | |
79 | 102k | /* Mark frame as being at the current instrumentation level. */ |
80 | 102k | static_frame->body.instrumentation_level = tc->instance->instrumentation_level; |
81 | 102k | |
82 | 102k | /* Add profiling instrumentation if needed. */ |
83 | 102k | if (tc->instance->profiling) |
84 | 0 | MVM_profile_instrument(tc, static_frame); |
85 | 102k | else if (tc->instance->cross_thread_write_logging) |
86 | 0 | MVM_cross_thread_write_instrument(tc, static_frame); |
87 | 102k | else if (tc->instance->coverage_logging) |
88 | 0 | MVM_line_coverage_instrument(tc, static_frame); |
89 | 102k | else |
90 | 102k | MVM_profile_ensure_uninstrumented(tc, static_frame); |
91 | 102k | } |
92 | | |
93 | | /* Called when the GC destroys a frame. Since the frame may have been alive as |
94 | | * part of a continuation that was taken but never invoked, we should check |
95 | | * things normally cleaned up on return don't need cleaning up also. */ |
96 | 552k | void MVM_frame_destroy(MVMThreadContext *tc, MVMFrame *frame) { |
97 | 552k | if (frame->work) { |
98 | 0 | MVM_args_proc_cleanup(tc, &frame->params); |
99 | 0 | MVM_fixed_size_free(tc, tc->instance->fsa, frame->allocd_work, |
100 | 0 | frame->work); |
101 | 0 | } |
102 | 552k | if (frame->env) |
103 | 240k | MVM_fixed_size_free(tc, tc->instance->fsa, frame->allocd_env, frame->env); |
104 | 552k | if (frame->continuation_tags) |
105 | 0 | MVM_continuation_free_tags(tc, frame); |
106 | 552k | } |
107 | | |
108 | | /* Creates a frame for usage as a context only, possibly forcing all of the |
109 | | * static lexicals to be deserialized if it's used for auto-close purposes. */ |
110 | | static MVMFrame * create_context_only(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
111 | 1.31k | MVMObject *code_ref, MVMint32 autoclose) { |
112 | 1.31k | MVMFrame *frame; |
113 | 1.31k | |
114 | 1.31k | MVMROOT(tc, static_frame, { |
115 | 1.31k | MVMROOT(tc, code_ref, { |
116 | 1.31k | /* If the frame was never invoked before, need initial calculations |
117 | 1.31k | * and verification. */ |
118 | 1.31k | if (static_frame->body.instrumentation_level == 0) |
119 | 1.31k | instrumentation_level_barrier(tc, static_frame); |
120 | 1.31k | |
121 | 1.31k | frame = MVM_gc_allocate_frame(tc); |
122 | 1.31k | }); |
123 | 1.31k | }); |
124 | 1.31k | |
125 | 1.31k | /* Set static frame, code ref, and handlers. */ |
126 | 1.31k | MVM_ASSIGN_REF(tc, &(frame->header), frame->static_info, static_frame); |
127 | 1.31k | MVM_ASSIGN_REF(tc, &(frame->header), frame->code_ref, code_ref); |
128 | 1.31k | frame->effective_handlers = static_frame->body.handlers; |
129 | 1.31k | |
130 | 1.31k | /* Allocate space for lexicals, copying the default lexical environment |
131 | 1.31k | * into place and, if we're auto-closing, making sure anything we'd clone |
132 | 1.31k | * is vivified to prevent the clone (which is what creates the correct |
133 | 1.31k | * BEGIN/INIT semantics). */ |
134 | 1.31k | if (static_frame->body.env_size) { |
135 | 1.31k | frame->env = MVM_fixed_size_alloc(tc, tc->instance->fsa, static_frame->body.env_size); |
136 | 1.31k | frame->allocd_env = static_frame->body.env_size; |
137 | 1.31k | if (autoclose) { |
138 | 5 | MVMuint16 i, num_lexicals = static_frame->body.num_lexicals; |
139 | 5 | |
140 | 5 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&static_frame); |
141 | 5 | |
142 | 102 | for (i = 0; i < num_lexicals; i++) { |
143 | 97 | if (!static_frame->body.static_env[i].o && static_frame->body.static_env_flags[i] == 1) { |
144 | 0 | MVMint32 scid, objid; |
145 | 0 | if (MVM_bytecode_find_static_lexical_scref(tc, static_frame->body.cu, |
146 | 0 | static_frame, i, &scid, &objid)) { |
147 | 0 | MVMObject *resolved; |
148 | 0 | MVMSerializationContext *sc = MVM_sc_get_sc(tc, static_frame->body.cu, scid); |
149 | 0 |
|
150 | 0 | if (sc == NULL) |
151 | 0 | MVM_exception_throw_adhoc(tc, |
152 | 0 | "SC not yet resolved; lookup failed"); |
153 | 0 |
|
154 | 0 | resolved = MVM_sc_get_object(tc, sc, objid); |
155 | 0 |
|
156 | 0 | MVM_ASSIGN_REF(tc, &(static_frame->common.header), |
157 | 0 | static_frame->body.static_env[i].o, |
158 | 0 | resolved); |
159 | 0 | } |
160 | 0 | } |
161 | 97 | } |
162 | 5 | MVM_gc_root_temp_pop(tc); |
163 | 5 | } |
164 | 1.31k | memcpy(frame->env, static_frame->body.static_env, static_frame->body.env_size); |
165 | 1.31k | } |
166 | 1.31k | |
167 | 1.31k | return frame; |
168 | 1.31k | } |
169 | | |
170 | | /* Creates a frame that is suitable for deserializing a context into. Starts |
171 | | * with a ref count of 1 due to being held by an SC. */ |
172 | | MVMFrame * MVM_frame_create_context_only(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
173 | 1.31k | MVMObject *code_ref) { |
174 | 1.31k | return create_context_only(tc, static_frame, code_ref, 0); |
175 | 1.31k | } |
176 | | |
177 | | /* Provides auto-close functionality, for the handful of cases where we have |
178 | | * not ever been in the outer frame of something we're invoking. In this case, |
179 | | * we fake up a frame based on the static lexical environment. */ |
180 | 26 | static MVMFrame * autoclose(MVMThreadContext *tc, MVMStaticFrame *needed) { |
181 | 26 | MVMFrame *result; |
182 | 26 | |
183 | 26 | /* First, see if we can find one on the call stack; return it if so. */ |
184 | 26 | MVMFrame *candidate = tc->cur_frame; |
185 | 86 | while (candidate) { |
186 | 81 | if (candidate->static_info->body.bytecode == needed->body.bytecode) |
187 | 21 | return candidate; |
188 | 60 | candidate = candidate->caller; |
189 | 60 | } |
190 | 26 | |
191 | 26 | /* If not, fake up a frame See if it also needs an outer. */ |
192 | 5 | MVMROOT(tc, needed, { |
193 | 5 | result = create_context_only(tc, needed, (MVMObject *)needed->body.static_code, 1); |
194 | 5 | }); |
195 | 5 | if (needed->body.outer) { |
196 | 4 | /* See if the static code object has an outer. */ |
197 | 4 | MVMCode *outer_code = needed->body.outer->body.static_code; |
198 | 4 | if (outer_code->body.outer && |
199 | 3 | outer_code->body.outer->static_info->body.bytecode == needed->body.bytecode) { |
200 | 0 | /* Yes, just take it. */ |
201 | 0 | MVM_ASSIGN_REF(tc, &(result->header), result->outer, outer_code->body.outer); |
202 | 0 | } |
203 | 4 | else { |
204 | 4 | /* Otherwise, recursively auto-close. */ |
205 | 4 | MVMROOT(tc, result, { |
206 | 4 | MVMFrame *ac = autoclose(tc, needed->body.outer); |
207 | 4 | MVM_ASSIGN_REF(tc, &(result->header), result->outer, ac); |
208 | 4 | }); |
209 | 4 | } |
210 | 4 | } |
211 | 5 | return result; |
212 | 26 | } |
213 | | |
214 | | /* Obtains memory for a frame on the thread-local call stack. */ |
215 | | static MVMFrame * allocate_frame(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
216 | 12.4M | MVMSpeshCandidate *spesh_cand) { |
217 | 12.4M | MVMFrame *frame; |
218 | 12.4M | MVMint32 env_size, work_size; |
219 | 12.4M | MVMStaticFrameBody *static_frame_body; |
220 | 12.4M | |
221 | 12.4M | /* Allocate the frame. */ |
222 | 12.4M | MVMCallStackRegion *stack = tc->stack_current; |
223 | 12.4M | if (stack->alloc + sizeof(MVMFrame) >= stack->alloc_limit) |
224 | 0 | stack = MVM_callstack_region_next(tc); |
225 | 12.4M | frame = (MVMFrame *)stack->alloc; |
226 | 12.4M | stack->alloc += sizeof(MVMFrame); |
227 | 12.4M | memset(frame, 0, sizeof(MVMFrame)); |
228 | 12.4M | |
229 | 12.4M | /* Allocate space for lexicals and work area. */ |
230 | 12.4M | static_frame_body = &(static_frame->body); |
231 | 8.62M | env_size = spesh_cand ? spesh_cand->env_size : static_frame_body->env_size; |
232 | 12.4M | if (env_size) { |
233 | 953k | frame->env = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, env_size); |
234 | 953k | frame->allocd_env = env_size; |
235 | 953k | } |
236 | 8.62M | work_size = spesh_cand ? spesh_cand->work_size : static_frame_body->work_size; |
237 | 12.4M | if (work_size) { |
238 | 12.4M | if (spesh_cand) { |
239 | 8.62M | /* Allocate zeroed memory. Spesh makes sure we have VMNull setup in |
240 | 8.62M | * the places we need it. */ |
241 | 8.62M | frame->work = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, work_size); |
242 | 8.62M | } |
243 | 3.84M | else { |
244 | 3.84M | /* Copy frame template with VMNulls in to place. */ |
245 | 3.84M | frame->work = MVM_fixed_size_alloc(tc, tc->instance->fsa, work_size); |
246 | 3.84M | memcpy(frame->work, static_frame_body->work_initial, |
247 | 3.84M | sizeof(MVMRegister) * static_frame_body->num_locals); |
248 | 3.84M | } |
249 | 12.4M | frame->allocd_work = work_size; |
250 | 12.4M | |
251 | 12.4M | /* Calculate args buffer position. */ |
252 | 12.4M | frame->args = frame->work + (spesh_cand |
253 | 8.62M | ? spesh_cand->num_locals |
254 | 3.84M | : static_frame_body->num_locals); |
255 | 12.4M | } |
256 | 12.4M | |
257 | 12.4M | /* Assign a sequence nr */ |
258 | 12.4M | frame->sequence_nr = tc->next_frame_nr++; |
259 | 12.4M | |
260 | 12.4M | return frame; |
261 | 12.4M | } |
262 | | |
263 | | /* Obtains memory for a frame on the heap. */ |
264 | | static MVMFrame * allocate_heap_frame(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
265 | 1.66k | MVMSpeshCandidate *spesh_cand) { |
266 | 1.66k | MVMFrame *frame; |
267 | 1.66k | MVMint32 env_size, work_size; |
268 | 1.66k | MVMStaticFrameBody *static_frame_body; |
269 | 1.66k | |
270 | 1.66k | /* Allocate the frame. */ |
271 | 1.66k | MVMROOT(tc, static_frame, { |
272 | 1.66k | frame = MVM_gc_allocate_frame(tc); |
273 | 1.66k | }); |
274 | 1.66k | |
275 | 1.66k | /* Allocate space for lexicals and work area. */ |
276 | 1.66k | static_frame_body = &(static_frame->body); |
277 | 1.66k | env_size = spesh_cand ? spesh_cand->env_size : static_frame_body->env_size; |
278 | 1.66k | if (env_size) { |
279 | 0 | frame->env = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, env_size); |
280 | 0 | frame->allocd_env = env_size; |
281 | 0 | } |
282 | 1.66k | work_size = spesh_cand ? spesh_cand->work_size : static_frame_body->work_size; |
283 | 1.66k | if (work_size) { |
284 | 1.66k | /* Fill up all object registers with a pointer to our VMNull object */ |
285 | 1.66k | if (spesh_cand && spesh_cand->local_types) { |
286 | 0 | MVMuint32 num_locals = spesh_cand->num_locals; |
287 | 0 | MVMuint16 *local_types = spesh_cand->local_types; |
288 | 0 | MVMuint32 i; |
289 | 0 | frame->work = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, work_size); |
290 | 0 | for (i = 0; i < num_locals; i++) |
291 | 0 | if (local_types[i] == MVM_reg_obj) |
292 | 0 | frame->work[i].o = tc->instance->VMNull; |
293 | 0 | } |
294 | 1.66k | else { |
295 | 1.66k | frame->work = MVM_fixed_size_alloc(tc, tc->instance->fsa, work_size); |
296 | 1.66k | memcpy(frame->work, static_frame_body->work_initial, |
297 | 1.66k | sizeof(MVMRegister) * static_frame_body->num_locals); |
298 | 1.66k | } |
299 | 1.66k | frame->allocd_work = work_size; |
300 | 1.66k | |
301 | 1.66k | /* Calculate args buffer position. */ |
302 | 1.66k | frame->args = frame->work + (spesh_cand |
303 | 0 | ? spesh_cand->num_locals |
304 | 1.66k | : static_frame_body->num_locals); |
305 | 1.66k | } |
306 | 1.66k | |
307 | 1.66k | return frame; |
308 | 1.66k | } |
309 | | |
310 | | /* This exists to reduce the amount of pointer-fiddling that has to be |
311 | | * done by the JIT */ |
312 | | void MVM_frame_invoke_code(MVMThreadContext *tc, MVMCode *code, |
313 | 983k | MVMCallsite *callsite, MVMint32 spesh_cand) { |
314 | 983k | MVM_frame_invoke(tc, code->body.sf, callsite, tc->cur_frame->args, |
315 | 983k | code->body.outer, (MVMObject*)code, spesh_cand); |
316 | 983k | } |
317 | | |
318 | | /* Takes a static frame and a thread context. Invokes the static frame. */ |
319 | | void MVM_frame_invoke(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
320 | | MVMCallsite *callsite, MVMRegister *args, |
321 | 12.4M | MVMFrame *outer, MVMObject *code_ref, MVMint32 spesh_cand) { |
322 | 12.4M | MVMFrame *frame; |
323 | 12.4M | MVMuint32 found_spesh; |
324 | 12.4M | |
325 | 12.4M | /* If the frame was never invoked before, or never before at the current |
326 | 12.4M | * instrumentation level, we need to trigger the instrumentation level |
327 | 12.4M | * barrier. */ |
328 | 12.4M | if (static_frame->body.instrumentation_level != tc->instance->instrumentation_level) { |
329 | 102k | MVMROOT(tc, static_frame, { |
330 | 102k | MVMROOT(tc, code_ref, { |
331 | 102k | MVMROOT(tc, outer, { |
332 | 102k | instrumentation_level_barrier(tc, static_frame); |
333 | 102k | }); |
334 | 102k | }); |
335 | 102k | }); |
336 | 102k | } |
337 | 12.4M | |
338 | 12.4M | /* Ensure we have an outer if needed. This is done ahead of allocating the |
339 | 12.4M | * new frame, since an autoclose will force the callstack on to the heap. */ |
340 | 12.4M | if (outer) { |
341 | 12.4M | /* We were provided with an outer frame and it will already have had |
342 | 12.4M | * its reference count incremented; just ensure that it is based on the |
343 | 12.4M | * correct static frame (compare on bytecode address to cope with |
344 | 12.4M | * nqp::freshcoderef). */ |
345 | 12.4M | if (outer->static_info->body.orig_bytecode != static_frame->body.outer->body.orig_bytecode) { |
346 | 0 | char *frame_cuuid = MVM_string_utf8_encode_C_string(tc, static_frame->body.cuuid); |
347 | 0 | char *frame_name; |
348 | 0 | char *outer_cuuid = MVM_string_utf8_encode_C_string(tc, outer->static_info->body.cuuid); |
349 | 0 | char *outer_name; |
350 | 0 | char *frame_outer_cuuid = MVM_string_utf8_encode_C_string(tc, static_frame->body.outer->body.cuuid); |
351 | 0 | char *frame_outer_name; |
352 | 0 |
|
353 | 0 | char *waste[7] = { frame_cuuid, outer_cuuid, frame_outer_cuuid, NULL, NULL, NULL, NULL }; |
354 | 0 | int waste_counter = 3; |
355 | 0 |
|
356 | 0 | if (static_frame->body.name) { |
357 | 0 | frame_name = MVM_string_utf8_encode_C_string(tc, static_frame->body.name); |
358 | 0 | waste[waste_counter++] = frame_name; |
359 | 0 | } |
360 | 0 | else { |
361 | 0 | frame_name = "<anonymous static frame>"; |
362 | 0 | } |
363 | 0 |
|
364 | 0 | if (outer->static_info->body.name) { |
365 | 0 | outer_name = MVM_string_utf8_encode_C_string(tc, outer->static_info->body.name); |
366 | 0 | waste[waste_counter++] = outer_name; |
367 | 0 | } |
368 | 0 | else { |
369 | 0 | outer_name = "<anonymous static frame>"; |
370 | 0 | } |
371 | 0 |
|
372 | 0 | if (static_frame->body.outer->body.name) { |
373 | 0 | frame_outer_name = MVM_string_utf8_encode_C_string(tc, static_frame->body.outer->body.name); |
374 | 0 | waste[waste_counter++] = frame_outer_name; |
375 | 0 | } |
376 | 0 | else { |
377 | 0 | frame_outer_name = "<anonymous static frame>"; |
378 | 0 | } |
379 | 0 |
|
380 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
381 | 0 | "When invoking %s '%s', provided outer frame %p (%s '%s') does not match expected static frame %p (%s '%s')", |
382 | 0 | frame_cuuid, |
383 | 0 | frame_name, |
384 | 0 | outer->static_info, |
385 | 0 | outer_cuuid, |
386 | 0 | outer_name, |
387 | 0 | static_frame->body.outer, |
388 | 0 | frame_outer_cuuid, |
389 | 0 | frame_outer_name); |
390 | 0 | } |
391 | 12.4M | } |
392 | 5.40k | else if (static_frame->body.static_code) { |
393 | 5.40k | MVMCode *static_code = static_frame->body.static_code; |
394 | 5.40k | if (static_code->body.outer) { |
395 | 1 | /* We're lacking an outer, but our static code object may have one. |
396 | 1 | * This comes up in the case of cloned protoregexes, for example. */ |
397 | 1 | outer = static_code->body.outer; |
398 | 1 | } |
399 | 5.40k | else if (static_frame->body.outer) { |
400 | 22 | /* Auto-close, and cache it in the static frame. */ |
401 | 22 | MVMROOT(tc, static_frame, { |
402 | 22 | MVMROOT(tc, code_ref, { |
403 | 22 | MVM_frame_force_to_heap(tc, tc->cur_frame); |
404 | 22 | outer = autoclose(tc, static_frame->body.outer); |
405 | 22 | MVM_ASSIGN_REF(tc, &(static_code->common.header), |
406 | 22 | static_code->body.outer, outer); |
407 | 22 | }); |
408 | 22 | }); |
409 | 22 | } |
410 | 5.40k | } |
411 | 12.4M | |
412 | 12.4M | /* See if any specializations apply. */ |
413 | 12.4M | found_spesh = 0; |
414 | 12.4M | if (spesh_cand >= 0 && spesh_cand < static_frame->body.num_spesh_candidates) { |
415 | 1.44M | MVMSpeshCandidate *chosen_cand = &static_frame->body.spesh_candidates[spesh_cand]; |
416 | 1.44M | if (!chosen_cand->sg) { |
417 | 1.44M | frame = allocate_frame(tc, static_frame, chosen_cand); |
418 | 1.44M | frame->effective_bytecode = chosen_cand->bytecode; |
419 | 1.44M | frame->effective_handlers = chosen_cand->handlers; |
420 | 1.44M | frame->effective_spesh_slots = chosen_cand->spesh_slots; |
421 | 1.44M | frame->spesh_cand = chosen_cand; |
422 | 1.44M | frame->spesh_log_idx = -1; |
423 | 1.44M | found_spesh = 1; |
424 | 1.44M | } |
425 | 1.44M | } |
426 | 12.4M | if (!found_spesh && ++static_frame->body.invocations >= static_frame->body.spesh_threshold && callsite->is_interned) { |
427 | 7.85M | /* Look for specialized bytecode. */ |
428 | 7.85M | MVMint32 num_spesh = static_frame->body.num_spesh_candidates; |
429 | 7.85M | MVMSpeshCandidate *chosen_cand = NULL; |
430 | 7.85M | MVMint32 i, j; |
431 | 12.6M | for (i = 0; i < num_spesh; i++) { |
432 | 11.9M | MVMSpeshCandidate *cand = &static_frame->body.spesh_candidates[i]; |
433 | 11.9M | if (cand->cs == callsite) { |
434 | 10.1M | MVMint32 match = 1; |
435 | 22.8M | for (j = 0; j < cand->num_guards; j++) { |
436 | 15.6M | MVMint32 pos = cand->guards[j].slot; |
437 | 15.6M | MVMSTable *st = (MVMSTable *)cand->guards[j].match; |
438 | 15.6M | MVMObject *arg = args[pos].o; |
439 | 15.6M | if (!arg) { |
440 | 0 | match = 0; |
441 | 0 | break; |
442 | 0 | } |
443 | 15.6M | switch (cand->guards[j].kind) { |
444 | 13.6M | case MVM_SPESH_GUARD_CONC: |
445 | 13.6M | if (!IS_CONCRETE(arg) || STABLE(arg) != st) |
446 | 2.37M | match = 0; |
447 | 13.6M | break; |
448 | 2.05M | case MVM_SPESH_GUARD_TYPE: |
449 | 2.05M | if (IS_CONCRETE(arg) || STABLE(arg) != st) |
450 | 649k | match = 0; |
451 | 2.05M | break; |
452 | 0 | case MVM_SPESH_GUARD_DC_CONC: { |
453 | 0 | MVMRegister dc; |
454 | 0 | STABLE(arg)->container_spec->fetch(tc, arg, &dc); |
455 | 0 | if (!dc.o || !IS_CONCRETE(dc.o) || STABLE(dc.o) != st) |
456 | 0 | match = 0; |
457 | 0 | break; |
458 | 13.6M | } |
459 | 0 | case MVM_SPESH_GUARD_DC_TYPE: { |
460 | 0 | MVMRegister dc; |
461 | 0 | STABLE(arg)->container_spec->fetch(tc, arg, &dc); |
462 | 0 | if (!dc.o || IS_CONCRETE(dc.o) || STABLE(dc.o) != st) |
463 | 0 | match = 0; |
464 | 0 | break; |
465 | 13.6M | } |
466 | 0 | case MVM_SPESH_GUARD_DC_CONC_RW: { |
467 | 0 | if (STABLE(arg)->container_spec->can_store(tc, arg)) { |
468 | 0 | MVMRegister dc; |
469 | 0 | STABLE(arg)->container_spec->fetch(tc, arg, &dc); |
470 | 0 | if (!dc.o || !IS_CONCRETE(dc.o) || STABLE(dc.o) != st) |
471 | 0 | match = 0; |
472 | 0 | } |
473 | 0 | else { |
474 | 0 | match = 0; |
475 | 0 | } |
476 | 0 | break; |
477 | 13.6M | } |
478 | 0 | case MVM_SPESH_GUARD_DC_TYPE_RW: { |
479 | 0 | if (STABLE(arg)->container_spec->can_store(tc, arg)) { |
480 | 0 | MVMRegister dc; |
481 | 0 | STABLE(arg)->container_spec->fetch(tc, arg, &dc); |
482 | 0 | if (!dc.o || IS_CONCRETE(dc.o) || STABLE(dc.o) != st) |
483 | 0 | match = 0; |
484 | 0 | } |
485 | 0 | else { |
486 | 0 | match = 0; |
487 | 0 | } |
488 | 0 | break; |
489 | 13.6M | } |
490 | 15.6M | } |
491 | 15.6M | if (!match) |
492 | 3.02M | break; |
493 | 15.6M | } |
494 | 10.1M | if (match) { |
495 | 7.15M | chosen_cand = cand; |
496 | 7.15M | break; |
497 | 7.15M | } |
498 | 10.1M | } |
499 | 11.9M | } |
500 | 7.85M | |
501 | 7.85M | /* If we didn't find any, and we're below the limit, can set up a |
502 | 7.85M | * specialization. */ |
503 | 7.85M | if (!chosen_cand && num_spesh < MVM_SPESH_LIMIT && tc->instance->spesh_enabled) |
504 | 17.0k | chosen_cand = MVM_spesh_candidate_setup(tc, static_frame, |
505 | 17.0k | callsite, args, 0); |
506 | 7.85M | |
507 | 7.85M | /* Now try to use specialized bytecode. We may need to compete to |
508 | 7.85M | * be a logging run of it. */ |
509 | 7.85M | if (chosen_cand) { |
510 | 7.17M | if (chosen_cand->sg) { |
511 | 123k | /* In the logging phase. Try to get a logging index; ensure it |
512 | 123k | * is not being used as an OSR logging candidate elsewhere. */ |
513 | 123k | AO_t cur_idx = MVM_load(&(chosen_cand->log_enter_idx)); |
514 | 123k | if (!chosen_cand->osr_logging && cur_idx < MVM_SPESH_LOG_RUNS) { |
515 | 120k | if (MVM_cas(&(chosen_cand->log_enter_idx), cur_idx, cur_idx + 1) == cur_idx) { |
516 | 120k | /* We get to log. */ |
517 | 120k | frame = allocate_frame(tc, static_frame, chosen_cand); |
518 | 120k | frame->effective_bytecode = chosen_cand->bytecode; |
519 | 120k | frame->effective_handlers = chosen_cand->handlers; |
520 | 120k | frame->effective_spesh_slots = chosen_cand->spesh_slots; |
521 | 120k | frame->spesh_log_slots = chosen_cand->log_slots; |
522 | 120k | frame->spesh_cand = chosen_cand; |
523 | 120k | frame->spesh_log_idx = (MVMint8)cur_idx; |
524 | 120k | found_spesh = 1; |
525 | 120k | } |
526 | 120k | } |
527 | 123k | } |
528 | 7.05M | else { |
529 | 7.05M | /* In the post-specialize phase; can safely used the code. */ |
530 | 7.05M | frame = allocate_frame(tc, static_frame, chosen_cand); |
531 | 7.05M | if (chosen_cand->jitcode) { |
532 | 6.65M | frame->effective_bytecode = chosen_cand->jitcode->bytecode; |
533 | 6.65M | frame->jit_entry_label = chosen_cand->jitcode->labels[0]; |
534 | 6.65M | } |
535 | 394k | else { |
536 | 394k | frame->effective_bytecode = chosen_cand->bytecode; |
537 | 394k | } |
538 | 7.05M | frame->effective_handlers = chosen_cand->handlers; |
539 | 7.05M | frame->effective_spesh_slots = chosen_cand->spesh_slots; |
540 | 7.05M | frame->spesh_cand = chosen_cand; |
541 | 7.05M | frame->spesh_log_idx = -1; |
542 | 7.05M | found_spesh = 1; |
543 | 7.05M | } |
544 | 7.17M | } |
545 | 7.85M | } |
546 | 12.4M | if (!found_spesh) { |
547 | 3.84M | frame = allocate_frame(tc, static_frame, NULL); |
548 | 3.84M | frame->effective_bytecode = static_frame->body.bytecode; |
549 | 3.84M | frame->effective_handlers = static_frame->body.handlers; |
550 | 3.84M | } |
551 | 12.4M | |
552 | 12.4M | /* Set static frame. */ |
553 | 12.4M | frame->static_info = static_frame; |
554 | 12.4M | |
555 | 12.4M | /* Store the code ref (NULL at the top-level). */ |
556 | 12.4M | frame->code_ref = code_ref; |
557 | 12.4M | |
558 | 12.4M | /* Outer. */ |
559 | 12.4M | frame->outer = outer; |
560 | 12.4M | |
561 | 12.4M | /* Caller is current frame in the thread context. */ |
562 | 12.4M | frame->caller = tc->cur_frame; |
563 | 12.4M | |
564 | 12.4M | /* Initialize argument processing. */ |
565 | 12.4M | MVM_args_proc_init(tc, &frame->params, callsite, args); |
566 | 12.4M | |
567 | 12.4M | /* Update interpreter and thread context, so next execution will use this |
568 | 12.4M | * frame. */ |
569 | 12.4M | tc->cur_frame = frame; |
570 | 12.4M | tc->current_frame_nr = frame->sequence_nr; |
571 | 12.4M | |
572 | 12.4M | *(tc->interp_cur_op) = frame->effective_bytecode; |
573 | 12.4M | *(tc->interp_bytecode_start) = frame->effective_bytecode; |
574 | 12.4M | *(tc->interp_reg_base) = frame->work; |
575 | 12.4M | *(tc->interp_cu) = static_frame->body.cu; |
576 | 12.4M | |
577 | 12.4M | |
578 | 12.4M | |
579 | 12.4M | /* If we need to do so, make clones of things in the lexical environment |
580 | 12.4M | * that need it. Note that we do this after tc->cur_frame became the |
581 | 12.4M | * current frame, to make sure these new objects will certainly get |
582 | 12.4M | * marked if GC is triggered along the way. */ |
583 | 12.4M | if (static_frame->body.has_state_vars) { |
584 | 18 | /* Drag everything out of static_frame_body before we start, |
585 | 18 | * as GC action may invalidate it. */ |
586 | 18 | MVMRegister *env = static_frame->body.static_env; |
587 | 18 | MVMuint8 *flags = static_frame->body.static_env_flags; |
588 | 18 | MVMint64 numlex = static_frame->body.num_lexicals; |
589 | 18 | MVMRegister *state = NULL; |
590 | 18 | MVMint64 state_act = 0; /* 0 = none so far, 1 = first time, 2 = later */ |
591 | 18 | MVMint64 i; |
592 | 18 | MVMROOT(tc, frame, { |
593 | 18 | for (i = 0; i < numlex; i++) { |
594 | 18 | if (flags[i] == 2) { |
595 | 18 | redo_state: |
596 | 18 | switch (state_act) { |
597 | 18 | case 0: |
598 | 18 | if (!frame->code_ref) |
599 | 18 | MVM_exception_throw_adhoc(tc, |
600 | 18 | "Frame must have code-ref to have state variables"); |
601 | 18 | state = ((MVMCode *)frame->code_ref)->body.state_vars; |
602 | 18 | if (state) { |
603 | 18 | /* Already have state vars; pull them from this. */ |
604 | 18 | state_act = 2; |
605 | 18 | } |
606 | 18 | else { |
607 | 18 | /* Allocate storage for state vars. */ |
608 | 18 | state = (MVMRegister *)MVM_calloc(1, frame->static_info->body.env_size); |
609 | 18 | ((MVMCode *)frame->code_ref)->body.state_vars = state; |
610 | 18 | state_act = 1; |
611 | 18 | |
612 | 18 | /* Note that this frame should run state init code. */ |
613 | 18 | frame->flags |= MVM_FRAME_FLAG_STATE_INIT; |
614 | 18 | } |
615 | 18 | goto redo_state; |
616 | 18 | case 1: { |
617 | 18 | MVMObject *cloned = MVM_repr_clone(tc, env[i].o); |
618 | 18 | frame->env[i].o = cloned; |
619 | 18 | MVM_ASSIGN_REF(tc, &(frame->code_ref->header), state[i].o, cloned); |
620 | 18 | break; |
621 | 18 | } |
622 | 18 | case 2: |
623 | 18 | frame->env[i].o = state[i].o; |
624 | 18 | break; |
625 | 18 | } |
626 | 18 | } |
627 | 18 | } |
628 | 18 | }); |
629 | 18 | } |
630 | 12.4M | } |
631 | | |
632 | | /* Forces the specified frame from the stack and on to the heap, if it's not |
633 | | * already there. */ |
634 | 605k | MVMFrame * MVM_frame_force_to_heap(MVMThreadContext *tc, MVMFrame *frame) { |
635 | 605k | if (MVM_FRAME_IS_ON_CALLSTACK(tc, frame)) { |
636 | 347k | /* To keep things simple, we'll promote the entire stack. */ |
637 | 347k | MVMFrame *cur_to_promote = tc->cur_frame; |
638 | 347k | MVMFrame *new_cur_frame = NULL; |
639 | 347k | MVMFrame *update_caller = NULL; |
640 | 347k | MVMFrame *result = NULL; |
641 | 347k | MVMROOT(tc, new_cur_frame, { |
642 | 347k | MVMROOT(tc, update_caller, { |
643 | 347k | MVMROOT(tc, result, { |
644 | 347k | while (cur_to_promote) { |
645 | 347k | /* Allocate a heap frame. */ |
646 | 347k | MVMFrame *promoted = MVM_gc_allocate_frame(tc); |
647 | 347k | |
648 | 347k | /* Copy current frame's body to it. */ |
649 | 347k | memcpy( |
650 | 347k | (char *)promoted + sizeof(MVMCollectable), |
651 | 347k | (char *)cur_to_promote + sizeof(MVMCollectable), |
652 | 347k | sizeof(MVMFrame) - sizeof(MVMCollectable)); |
653 | 347k | |
654 | 347k | /* Update caller of previously promtoed frame, if any. This is the |
655 | 347k | * only reference that might point to a non-heap frame. */ |
656 | 347k | if (update_caller) { |
657 | 347k | MVM_ASSIGN_REF(tc, &(update_caller->header), |
658 | 347k | update_caller->caller, promoted); |
659 | 347k | } |
660 | 347k | |
661 | 347k | /* If we're the first time through the lopo, then we're instead |
662 | 347k | * replacing the current stack top. Note we do it at the end, |
663 | 347k | * so that the GC can still walk unpromoted frames if it runs |
664 | 347k | * in this loop. */ |
665 | 347k | else { |
666 | 347k | new_cur_frame = promoted; |
667 | 347k | } |
668 | 347k | |
669 | 347k | /* If the frame we're promoting was in the active handlers list, |
670 | 347k | * update the address there. */ |
671 | 347k | if (tc->active_handlers) { |
672 | 347k | MVMActiveHandler *ah = tc->active_handlers; |
673 | 347k | while (ah) { |
674 | 347k | if (ah->frame == cur_to_promote) |
675 | 347k | ah->frame = promoted; |
676 | 347k | ah = ah->next_handler; |
677 | 347k | } |
678 | 347k | } |
679 | 347k | |
680 | 347k | /* If we're replacing the frame we were asked to promote, that will |
681 | 347k | * become our result. */ |
682 | 347k | if (cur_to_promote == frame) |
683 | 347k | result = promoted; |
684 | 347k | |
685 | 347k | /* Check if there's a caller, or if we reached the end of the |
686 | 347k | * chain. */ |
687 | 347k | if (cur_to_promote->caller) { |
688 | 347k | /* If the caller is on the stack then it needs promotion too. |
689 | 347k | * If not, we're done. */ |
690 | 347k | if (MVM_FRAME_IS_ON_CALLSTACK(tc, cur_to_promote->caller)) { |
691 | 347k | /* Clear caller in promoted frame, to avoid a heap -> stack |
692 | 347k | * reference if we GC during this loop. */ |
693 | 347k | promoted->caller = NULL; |
694 | 347k | update_caller = promoted; |
695 | 347k | cur_to_promote = cur_to_promote->caller; |
696 | 347k | } |
697 | 347k | else { |
698 | 347k | if (cur_to_promote == tc->thread_entry_frame) |
699 | 347k | tc->thread_entry_frame = promoted; |
700 | 347k | cur_to_promote = NULL; |
701 | 347k | } |
702 | 347k | } |
703 | 347k | else { |
704 | 347k | /* End of caller chain; check if we promoted the entry |
705 | 347k | * frame */ |
706 | 347k | if (cur_to_promote == tc->thread_entry_frame) |
707 | 347k | tc->thread_entry_frame = promoted; |
708 | 347k | cur_to_promote = NULL; |
709 | 347k | } |
710 | 347k | } |
711 | 347k | }); |
712 | 347k | }); |
713 | 347k | }); |
714 | 347k | |
715 | 347k | /* All is promoted. Update thread's current frame and reset the thread |
716 | 347k | * local callstack. */ |
717 | 347k | tc->cur_frame = new_cur_frame; |
718 | 347k | MVM_callstack_reset(tc); |
719 | 347k | |
720 | 347k | /* Hand back new location of promoted frame. */ |
721 | 347k | if (!result) |
722 | 0 | MVM_panic(1, "Failed to find frame to promote on call stack"); |
723 | 347k | return result; |
724 | 347k | } |
725 | 257k | else { |
726 | 257k | return frame; |
727 | 257k | } |
728 | 605k | } |
729 | | |
730 | | /* Creates a frame for de-optimization purposes. */ |
731 | | MVMFrame * MVM_frame_create_for_deopt(MVMThreadContext *tc, MVMStaticFrame *static_frame, |
732 | 1.66k | MVMCode *code_ref) { |
733 | 1.66k | MVMFrame *frame; |
734 | 1.66k | MVMROOT(tc, static_frame, { |
735 | 1.66k | MVMROOT(tc, code_ref, { |
736 | 1.66k | frame = allocate_heap_frame(tc, static_frame, NULL); |
737 | 1.66k | }); |
738 | 1.66k | }); |
739 | 1.66k | frame->effective_bytecode = static_frame->body.bytecode; |
740 | 1.66k | frame->effective_handlers = static_frame->body.handlers; |
741 | 1.66k | MVM_ASSIGN_REF(tc, &(frame->header), frame->static_info, static_frame); |
742 | 1.66k | MVM_ASSIGN_REF(tc, &(frame->header), frame->code_ref, code_ref); |
743 | 1.66k | MVM_ASSIGN_REF(tc, &(frame->header), frame->outer, code_ref->body.outer); |
744 | 1.66k | return frame; |
745 | 1.66k | } |
746 | | |
747 | | /* Removes a single frame, as part of a return or unwind. Done after any exit |
748 | | * handler has already been run. */ |
749 | 12.4M | static MVMuint64 remove_one_frame(MVMThreadContext *tc, MVMuint8 unwind) { |
750 | 12.4M | MVMFrame *returner = tc->cur_frame; |
751 | 12.4M | MVMFrame *caller = returner->caller; |
752 | 12.4M | |
753 | 12.4M | /* See if we were in a logging spesh frame, and need to complete the |
754 | 12.4M | * specialization. */ |
755 | 12.4M | if (returner->spesh_cand && returner->spesh_log_idx >= 0) { |
756 | 120k | if (returner->spesh_cand->osr_logging) { |
757 | 1 | /* Didn't achieve enough log entries to complete the OSR, but |
758 | 1 | * clearly hot, so specialize anyway. This also avoids races |
759 | 1 | * when the candidate is called again later and still has |
760 | 1 | * sp_osrfinalize instructions in it. */ |
761 | 1 | returner->spesh_cand->osr_logging = 0; |
762 | 1 | MVM_spesh_candidate_specialize(tc, returner->static_info, |
763 | 1 | returner->spesh_cand); |
764 | 1 | } |
765 | 120k | else if (MVM_decr(&(returner->spesh_cand->log_exits_remaining)) == 1) { |
766 | 13.7k | MVM_spesh_candidate_specialize(tc, returner->static_info, |
767 | 13.7k | returner->spesh_cand); |
768 | 13.7k | } |
769 | 120k | } |
770 | 12.4M | |
771 | 12.4M | /* Clear up any continuation tags. */ |
772 | 12.4M | if (returner->continuation_tags) |
773 | 0 | MVM_continuation_free_tags(tc, returner); |
774 | 12.4M | |
775 | 12.4M | /* Clean up frame working space. */ |
776 | 12.4M | if (returner->work) { |
777 | 12.4M | MVM_args_proc_cleanup(tc, &returner->params); |
778 | 12.4M | MVM_fixed_size_free(tc, tc->instance->fsa, returner->allocd_work, |
779 | 12.4M | returner->work); |
780 | 12.4M | } |
781 | 12.4M | |
782 | 12.4M | /* If it's a call stack frame, remove it from the stack. */ |
783 | 12.4M | if (MVM_FRAME_IS_ON_CALLSTACK(tc, returner)) { |
784 | 11.4M | MVMCallStackRegion *stack = tc->stack_current; |
785 | 11.4M | stack->alloc = (char *)returner; |
786 | 11.4M | if ((char *)stack->alloc - sizeof(MVMCallStackRegion) == (char *)stack) |
787 | 2.55M | MVM_callstack_region_prev(tc); |
788 | 11.4M | if (returner->env) |
789 | 500k | MVM_fixed_size_free(tc, tc->instance->fsa, returner->allocd_env, returner->env); |
790 | 11.4M | } |
791 | 12.4M | |
792 | 12.4M | /* Otherwise, NULL out ->work, to indicate the frame is no longer in |
793 | 12.4M | * dynamic scope. This is used by the GC to avoid marking stuff (this is |
794 | 12.4M | * needed for safety as otherwise we'd read freed memory), as well as by |
795 | 12.4M | * exceptions to ensure the target of an exception throw is indeed still |
796 | 12.4M | * in dynamic scope. */ |
797 | 982k | else { |
798 | 982k | returner->work = NULL; |
799 | 982k | } |
800 | 12.4M | |
801 | 12.4M | /* Switch back to the caller frame if there is one. */ |
802 | 12.4M | if (caller && returner != tc->thread_entry_frame) { |
803 | 12.4M | tc->cur_frame = caller; |
804 | 12.4M | tc->current_frame_nr = caller->sequence_nr; |
805 | 12.4M | |
806 | 12.4M | *(tc->interp_cur_op) = caller->return_address; |
807 | 12.4M | *(tc->interp_bytecode_start) = caller->effective_bytecode; |
808 | 12.4M | *(tc->interp_reg_base) = caller->work; |
809 | 12.4M | *(tc->interp_cu) = caller->static_info->body.cu; |
810 | 12.4M | |
811 | 12.4M | |
812 | 12.4M | /* Handle any special return hooks. */ |
813 | 12.4M | if (caller->special_return || caller->special_unwind) { |
814 | 75.6k | MVMSpecialReturn sr = caller->special_return; |
815 | 75.6k | MVMSpecialReturn su = caller->special_unwind; |
816 | 75.6k | void *srd = caller->special_return_data; |
817 | 75.6k | caller->special_return = NULL; |
818 | 75.6k | caller->special_unwind = NULL; |
819 | 75.6k | caller->special_return_data = NULL; |
820 | 75.6k | caller->mark_special_return_data = NULL; |
821 | 75.6k | if (unwind && su) |
822 | 3 | su(tc, srd); |
823 | 75.6k | else if (!unwind && sr) |
824 | 75.6k | sr(tc, srd); |
825 | 75.6k | } |
826 | 12.4M | |
827 | 12.4M | return 1; |
828 | 12.4M | } |
829 | 260 | else { |
830 | 260 | tc->cur_frame = NULL; |
831 | 260 | return 0; |
832 | 260 | } |
833 | 12.4M | } |
834 | | |
835 | | /* Attempt to return from the current frame. Returns non-zero if we can, |
836 | | * and zero if there is nowhere to return to (which would signal the exit |
837 | | * of the interpreter). */ |
838 | 0 | static void remove_after_handler(MVMThreadContext *tc, void *sr_data) { |
839 | 0 | remove_one_frame(tc, 0); |
840 | 0 | } |
841 | 12.4M | MVMuint64 MVM_frame_try_return(MVMThreadContext *tc) { |
842 | 12.4M | MVMFrame *cur_frame = tc->cur_frame; |
843 | 12.4M | |
844 | 12.4M | if (cur_frame->static_info->body.has_exit_handler && |
845 | 0 | !(cur_frame->flags & MVM_FRAME_FLAG_EXIT_HAND_RUN)) { |
846 | 0 | /* Set us up to run exit handler, and make it so we'll really exit the |
847 | 0 | * frame when that has been done. */ |
848 | 0 | MVMFrame *caller = cur_frame->caller; |
849 | 0 | MVMHLLConfig *hll = MVM_hll_current(tc); |
850 | 0 | MVMObject *handler; |
851 | 0 | MVMObject *result; |
852 | 0 | MVMCallsite *two_args_callsite; |
853 | 0 |
|
854 | 0 | if (!caller) |
855 | 0 | MVM_exception_throw_adhoc(tc, "Entry point frame cannot have an exit handler"); |
856 | 0 | if (tc->cur_frame == tc->thread_entry_frame) |
857 | 0 | MVM_exception_throw_adhoc(tc, "Thread entry point frame cannot have an exit handler"); |
858 | 0 |
|
859 | 0 | if (caller->return_type == MVM_RETURN_OBJ) { |
860 | 0 | result = caller->return_value->o; |
861 | 0 | } |
862 | 0 | else { |
863 | 0 | MVMROOT(tc, cur_frame, { |
864 | 0 | switch (caller->return_type) { |
865 | 0 | case MVM_RETURN_INT: |
866 | 0 | result = MVM_repr_box_int(tc, hll->int_box_type, caller->return_value->i64); |
867 | 0 | break; |
868 | 0 | case MVM_RETURN_NUM: |
869 | 0 | result = MVM_repr_box_num(tc, hll->num_box_type, caller->return_value->n64); |
870 | 0 | break; |
871 | 0 | case MVM_RETURN_STR: |
872 | 0 | result = MVM_repr_box_str(tc, hll->str_box_type, caller->return_value->s); |
873 | 0 | break; |
874 | 0 | default: |
875 | 0 | result = NULL; |
876 | 0 | } |
877 | 0 | }); |
878 | 0 | } |
879 | 0 |
|
880 | 0 | handler = MVM_frame_find_invokee(tc, hll->exit_handler, NULL); |
881 | 0 | two_args_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); |
882 | 0 | MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, two_args_callsite); |
883 | 0 | cur_frame->args[0].o = cur_frame->code_ref; |
884 | 0 | cur_frame->args[1].o = result; |
885 | 0 | cur_frame->special_return = remove_after_handler; |
886 | 0 | cur_frame->flags |= MVM_FRAME_FLAG_EXIT_HAND_RUN; |
887 | 0 | STABLE(handler)->invoke(tc, handler, two_args_callsite, cur_frame->args); |
888 | 0 | return 1; |
889 | 0 | } |
890 | 12.4M | else { |
891 | 12.4M | /* No exit handler, so a straight return. */ |
892 | 12.4M | return remove_one_frame(tc, 0); |
893 | 12.4M | } |
894 | 12.4M | } |
895 | | |
896 | | /* Try a return from the current frame; skip running any exit handlers. */ |
897 | 0 | MVMuint64 MVM_frame_try_return_no_exit_handlers(MVMThreadContext *tc) { |
898 | 0 | return remove_one_frame(tc, 0); |
899 | 0 | } |
900 | | |
901 | | /* Unwinds execution state to the specified frame, placing control flow at either |
902 | | * an absolute or relative (to start of target frame) address and optionally |
903 | | * setting a returned result. */ |
904 | | typedef struct { |
905 | | MVMFrame *frame; |
906 | | MVMuint8 *abs_addr; |
907 | | MVMuint32 rel_addr; |
908 | | } MVMUnwindData; |
909 | 0 | static void mark_unwind_data(MVMThreadContext *tc, MVMFrame *frame, MVMGCWorklist *worklist) { |
910 | 0 | MVMUnwindData *ud = (MVMUnwindData *)frame->special_return_data; |
911 | 0 | MVM_gc_worklist_add(tc, worklist, &(ud->frame)); |
912 | 0 | } |
913 | 0 | static void continue_unwind(MVMThreadContext *tc, void *sr_data) { |
914 | 0 | MVMUnwindData *ud = (MVMUnwindData *)sr_data; |
915 | 0 | MVMFrame *frame = ud->frame; |
916 | 0 | MVMuint8 *abs_addr = ud->abs_addr; |
917 | 0 | MVMuint32 rel_addr = ud->rel_addr; |
918 | 0 | MVM_free(sr_data); |
919 | 0 | MVM_frame_unwind_to(tc, frame, abs_addr, rel_addr, NULL); |
920 | 0 | } |
921 | | void MVM_frame_unwind_to(MVMThreadContext *tc, MVMFrame *frame, MVMuint8 *abs_addr, |
922 | 415k | MVMuint32 rel_addr, MVMObject *return_value) { |
923 | 419k | while (tc->cur_frame != frame) { |
924 | 3.40k | MVMFrame *cur_frame = tc->cur_frame; |
925 | 3.40k | if (cur_frame->static_info->body.has_exit_handler && |
926 | 0 | !(cur_frame->flags & MVM_FRAME_FLAG_EXIT_HAND_RUN)) { |
927 | 0 | /* We're unwinding a frame with an exit handler. Thus we need to |
928 | 0 | * pause the unwind, run the exit handler, and keep enough info |
929 | 0 | * around in order to finish up the unwind afterwards. */ |
930 | 0 | MVMHLLConfig *hll = MVM_hll_current(tc); |
931 | 0 | MVMFrame *caller; |
932 | 0 | MVMObject *handler; |
933 | 0 | MVMCallsite *two_args_callsite; |
934 | 0 |
|
935 | 0 | /* Force the frame onto the heap, since we'll reference it from the |
936 | 0 | * unwind data. */ |
937 | 0 | MVMROOT(tc, frame, { |
938 | 0 | MVMROOT(tc, cur_frame, { |
939 | 0 | MVMROOT(tc, return_value, { |
940 | 0 | frame = MVM_frame_force_to_heap(tc, frame); |
941 | 0 | cur_frame = tc->cur_frame; |
942 | 0 | }); |
943 | 0 | }); |
944 | 0 | }); |
945 | 0 |
|
946 | 0 | caller = cur_frame->caller; |
947 | 0 | if (!caller) |
948 | 0 | MVM_exception_throw_adhoc(tc, "Entry point frame cannot have an exit handler"); |
949 | 0 | if (cur_frame == tc->thread_entry_frame) |
950 | 0 | MVM_exception_throw_adhoc(tc, "Thread entry point frame cannot have an exit handler"); |
951 | 0 |
|
952 | 0 | handler = MVM_frame_find_invokee(tc, hll->exit_handler, NULL); |
953 | 0 | two_args_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); |
954 | 0 | MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, two_args_callsite); |
955 | 0 | cur_frame->args[0].o = cur_frame->code_ref; |
956 | 0 | cur_frame->args[1].o = NULL; |
957 | 0 | cur_frame->special_return = continue_unwind; |
958 | 0 | cur_frame->mark_special_return_data = mark_unwind_data; |
959 | 0 | { |
960 | 0 | MVMUnwindData *ud = MVM_malloc(sizeof(MVMUnwindData)); |
961 | 0 | ud->frame = frame; |
962 | 0 | ud->abs_addr = abs_addr; |
963 | 0 | ud->rel_addr = rel_addr; |
964 | 0 | if (return_value) |
965 | 0 | MVM_exception_throw_adhoc(tc, "return_value + exit_handler case NYI"); |
966 | 0 | cur_frame->special_return_data = ud; |
967 | 0 | } |
968 | 0 | cur_frame->flags |= MVM_FRAME_FLAG_EXIT_HAND_RUN; |
969 | 0 | STABLE(handler)->invoke(tc, handler, two_args_callsite, cur_frame->args); |
970 | 0 | return; |
971 | 0 | } |
972 | 3.40k | else { |
973 | 3.40k | /* If we're profiling, log an exit. */ |
974 | 3.40k | if (tc->instance->profiling) |
975 | 0 | MVM_profile_log_unwind(tc); |
976 | 3.40k | |
977 | 3.40k | /* No exit handler, so just remove the frame. */ |
978 | 3.40k | if (!remove_one_frame(tc, 1)) |
979 | 0 | MVM_panic(1, "Internal error: Unwound entire stack and missed handler"); |
980 | 3.40k | } |
981 | 3.40k | } |
982 | 415k | if (abs_addr) |
983 | 9 | *tc->interp_cur_op = abs_addr; |
984 | 415k | else if (rel_addr) |
985 | 415k | *tc->interp_cur_op = *tc->interp_bytecode_start + rel_addr; |
986 | 415k | if (return_value) |
987 | 0 | MVM_args_set_result_obj(tc, return_value, 1); |
988 | 415k | } |
989 | | |
990 | | /* Gets a code object for a frame, lazily deserializing it if needed. */ |
991 | 742k | MVMObject * MVM_frame_get_code_object(MVMThreadContext *tc, MVMCode *code) { |
992 | 742k | if (REPR(code)->ID != MVM_REPR_ID_MVMCode) |
993 | 0 | MVM_exception_throw_adhoc(tc, "getcodeobj needs a code ref"); |
994 | 742k | |
995 | 742k | if (!code->body.code_object) { |
996 | 35.4k | MVMStaticFrame *sf = code->body.sf; |
997 | 35.4k | if (sf->body.code_obj_sc_dep_idx > 0) { |
998 | 35.4k | MVMObject *resolved; |
999 | 35.4k | MVMSerializationContext *sc = MVM_sc_get_sc(tc, sf->body.cu, |
1000 | 35.4k | sf->body.code_obj_sc_dep_idx - 1); |
1001 | 35.4k | if (sc == NULL) |
1002 | 0 | MVM_exception_throw_adhoc(tc, |
1003 | 0 | "SC not yet resolved; lookup failed"); |
1004 | 35.4k | |
1005 | 35.4k | MVMROOT(tc, code, { |
1006 | 35.4k | resolved = MVM_sc_get_object(tc, sc, sf->body.code_obj_sc_idx); |
1007 | 35.4k | }); |
1008 | 35.4k | |
1009 | 35.4k | MVM_ASSIGN_REF(tc, &(code->common.header), code->body.code_object, |
1010 | 35.4k | resolved); |
1011 | 35.4k | } |
1012 | 35.4k | } |
1013 | 742k | return code->body.code_object; |
1014 | 742k | } |
1015 | | |
1016 | | /* Given the specified code object, sets its outer to the current scope. */ |
1017 | 191k | void MVM_frame_capturelex(MVMThreadContext *tc, MVMObject *code) { |
1018 | 191k | MVMCode *code_obj = (MVMCode *)code; |
1019 | 191k | MVMFrame *captured; |
1020 | 191k | if (REPR(code)->ID != MVM_REPR_ID_MVMCode) |
1021 | 0 | MVM_exception_throw_adhoc(tc, |
1022 | 0 | "Can only perform capturelex on object with representation MVMCode"); |
1023 | 191k | MVMROOT(tc, code, { |
1024 | 191k | captured = MVM_frame_force_to_heap(tc, tc->cur_frame); |
1025 | 191k | }); |
1026 | 191k | MVM_ASSIGN_REF(tc, &(code->header), code_obj->body.outer, captured); |
1027 | 191k | } |
1028 | | |
1029 | | /* This is used for situations in Perl 6 like: |
1030 | | * supply { |
1031 | | * my $x = something(); |
1032 | | * whenever $supply { |
1033 | | * QUIT { $x.foo() } |
1034 | | * } |
1035 | | * } |
1036 | | * Here, the QUIT may be called without an invocation of the whenever ever |
1037 | | * having taken place. At the point we closure-clone the whenever block, we |
1038 | | * will capture_inner the QUIT phaser. This creates a fake outer for the |
1039 | | * QUIT, but makes *its* outer point to the nearest instance of the relevant |
1040 | | * static frame on the call stack, so that the QUIT will disocver the correct |
1041 | | * $x. |
1042 | | */ |
1043 | 0 | void MVM_frame_capture_inner(MVMThreadContext *tc, MVMObject *code) { |
1044 | 0 | MVMCode *code_obj = (MVMCode *)code; |
1045 | 0 | MVMFrame *outer; |
1046 | 0 | MVMROOT(tc, code, { |
1047 | 0 | MVMStaticFrame *sf_outer = code_obj->body.sf->body.outer; |
1048 | 0 | MVMROOT(tc, sf_outer, { |
1049 | 0 | outer = create_context_only(tc, sf_outer, (MVMObject *)sf_outer->body.static_code, 1); |
1050 | 0 | }); |
1051 | 0 | MVMROOT(tc, outer, { |
1052 | 0 | MVMFrame *outer_outer = autoclose(tc, sf_outer->body.outer); |
1053 | 0 | MVM_ASSIGN_REF(tc, &(outer->header), outer->outer, outer_outer); |
1054 | 0 | }); |
1055 | 0 | }); |
1056 | 0 | MVM_ASSIGN_REF(tc, &(code->header), code_obj->body.outer, outer); |
1057 | 0 | } |
1058 | | |
1059 | | /* Given the specified code object, copies it and returns a copy which |
1060 | | * captures a closure over the current scope. */ |
1061 | 352k | MVMObject * MVM_frame_takeclosure(MVMThreadContext *tc, MVMObject *code) { |
1062 | 352k | MVMCode *closure; |
1063 | 352k | MVMFrame *captured; |
1064 | 352k | |
1065 | 352k | if (REPR(code)->ID != MVM_REPR_ID_MVMCode) |
1066 | 0 | MVM_exception_throw_adhoc(tc, |
1067 | 0 | "Can only perform takeclosure on object with representation MVMCode"); |
1068 | 352k | |
1069 | 352k | MVMROOT(tc, code, { |
1070 | 352k | closure = (MVMCode *)REPR(code)->allocate(tc, STABLE(code)); |
1071 | 352k | MVMROOT(tc, closure, { |
1072 | 352k | captured = MVM_frame_force_to_heap(tc, tc->cur_frame); |
1073 | 352k | }); |
1074 | 352k | }); |
1075 | 352k | |
1076 | 352k | MVM_ASSIGN_REF(tc, &(closure->common.header), closure->body.sf, ((MVMCode *)code)->body.sf); |
1077 | 352k | MVM_ASSIGN_REF(tc, &(closure->common.header), closure->body.name, ((MVMCode *)code)->body.name); |
1078 | 352k | MVM_ASSIGN_REF(tc, &(closure->common.header), closure->body.outer, captured); |
1079 | 352k | |
1080 | 352k | MVM_ASSIGN_REF(tc, &(closure->common.header), closure->body.code_object, |
1081 | 352k | ((MVMCode *)code)->body.code_object); |
1082 | 352k | |
1083 | 352k | return (MVMObject *)closure; |
1084 | 352k | } |
1085 | | |
1086 | | /* Vivifies a lexical in a frame. */ |
1087 | 5.88k | MVMObject * MVM_frame_vivify_lexical(MVMThreadContext *tc, MVMFrame *f, MVMuint16 idx) { |
1088 | 5.88k | MVMuint8 *flags; |
1089 | 5.88k | MVMint16 flag; |
1090 | 5.88k | MVMRegister *static_env; |
1091 | 5.88k | MVMuint16 effective_idx = 0; |
1092 | 5.88k | MVMStaticFrame *effective_sf; |
1093 | 5.88k | if (idx < f->static_info->body.num_lexicals) { |
1094 | 5.88k | flags = f->static_info->body.static_env_flags; |
1095 | 5.88k | static_env = f->static_info->body.static_env; |
1096 | 5.88k | effective_idx = idx; |
1097 | 5.88k | effective_sf = f->static_info; |
1098 | 5.88k | } |
1099 | 0 | else if (f->spesh_cand) { |
1100 | 0 | MVMint32 i; |
1101 | 0 | flags = NULL; |
1102 | 0 | for (i = 0; i < f->spesh_cand->num_inlines; i++) { |
1103 | 0 | MVMStaticFrame *isf = f->spesh_cand->inlines[i].code->body.sf; |
1104 | 0 | effective_idx = idx - f->spesh_cand->inlines[i].lexicals_start; |
1105 | 0 | if (effective_idx < isf->body.num_lexicals) { |
1106 | 0 | flags = isf->body.static_env_flags; |
1107 | 0 | static_env = isf->body.static_env; |
1108 | 0 | effective_sf = isf; |
1109 | 0 | break; |
1110 | 0 | } |
1111 | 0 | } |
1112 | 0 | } |
1113 | 0 | else { |
1114 | 0 | flags = NULL; |
1115 | 0 | } |
1116 | 5.88k | flag = flags ? flags[effective_idx] : -1; |
1117 | 5.88k | if (flag != -1 && static_env[effective_idx].o == NULL) { |
1118 | 5.88k | MVMint32 scid, objid; |
1119 | 5.88k | if (MVM_bytecode_find_static_lexical_scref(tc, effective_sf->body.cu, |
1120 | 5.75k | effective_sf, effective_idx, &scid, &objid)) { |
1121 | 5.75k | MVMSerializationContext *sc = MVM_sc_get_sc(tc, effective_sf->body.cu, scid); |
1122 | 5.75k | MVMObject *resolved; |
1123 | 5.75k | if (sc == NULL) |
1124 | 0 | MVM_exception_throw_adhoc(tc, |
1125 | 0 | "SC not yet resolved; lookup failed"); |
1126 | 5.75k | MVMROOT(tc, f, { |
1127 | 5.75k | resolved = MVM_sc_get_object(tc, sc, objid); |
1128 | 5.75k | }); |
1129 | 5.75k | MVM_ASSIGN_REF(tc, &(f->static_info->common.header), |
1130 | 5.75k | f->static_info->body.static_env[effective_idx].o, |
1131 | 5.75k | resolved); |
1132 | 5.75k | } |
1133 | 5.88k | } |
1134 | 5.88k | if (flag == 0) { |
1135 | 5.88k | MVMObject *viv = static_env[effective_idx].o; |
1136 | 5.88k | if (!viv) |
1137 | 130 | viv = tc->instance->VMNull; |
1138 | 5.88k | MVM_ASSIGN_REF(tc, &(f->header), f->env[idx].o, viv); |
1139 | 5.88k | return viv; |
1140 | 5.88k | } |
1141 | 4 | else if (flag == 1) { |
1142 | 4 | MVMObject *viv; |
1143 | 4 | MVMROOT(tc, f, { |
1144 | 4 | viv = MVM_repr_clone(tc, static_env[effective_idx].o); |
1145 | 4 | MVM_ASSIGN_REF(tc, &(f->header), f->env[idx].o, viv); |
1146 | 4 | }); |
1147 | 4 | return viv; |
1148 | 4 | } |
1149 | 0 | else { |
1150 | 0 | return tc->instance->VMNull; |
1151 | 0 | } |
1152 | 5.88k | } |
1153 | | |
1154 | | /* Looks up the address of the lexical with the specified name and the |
1155 | | * specified type. Non-existing object lexicals produce NULL, expected |
1156 | | * (for better or worse) by various things. Otherwise, an error is thrown |
1157 | | * if it does not exist. Incorrect type always throws. */ |
1158 | 3.32M | MVMRegister * MVM_frame_find_lexical_by_name(MVMThreadContext *tc, MVMString *name, MVMuint16 type) { |
1159 | 3.32M | MVMFrame *cur_frame = tc->cur_frame; |
1160 | 6.73M | while (cur_frame != NULL) { |
1161 | 6.73M | MVMLexicalRegistry *lexical_names = cur_frame->static_info->body.lexical_names; |
1162 | 6.73M | if (lexical_names) { |
1163 | 3.52M | /* Indexes were formerly stored off-by-one to avoid semi-predicate issue. */ |
1164 | 3.52M | MVMLexicalRegistry *entry; |
1165 | 3.52M | MVM_HASH_GET(tc, lexical_names, name, entry) |
1166 | 3.52M | if (entry) { |
1167 | 3.32M | if (cur_frame->static_info->body.lexical_types[entry->value] == type) { |
1168 | 3.32M | MVMRegister *result = &cur_frame->env[entry->value]; |
1169 | 3.32M | if (type == MVM_reg_obj && !result->o) |
1170 | 131 | MVM_frame_vivify_lexical(tc, cur_frame, entry->value); |
1171 | 3.32M | return result; |
1172 | 3.32M | } |
1173 | 0 | else { |
1174 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1175 | 0 | char *waste[] = { c_name, NULL }; |
1176 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
1177 | 0 | "Lexical with name '%s' has wrong type", |
1178 | 0 | c_name); |
1179 | 0 | } |
1180 | 3.32M | } |
1181 | 3.52M | } |
1182 | 3.41M | cur_frame = cur_frame->outer; |
1183 | 3.41M | } |
1184 | 0 | if (type != MVM_reg_obj) { |
1185 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1186 | 0 | char *waste[] = { c_name, NULL }; |
1187 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "No lexical found with name '%s'", |
1188 | 0 | c_name); |
1189 | 0 | } |
1190 | 0 | return NULL; |
1191 | 3.32M | } |
1192 | | |
1193 | | /* Binds the specified value to the given lexical, finding it along the static |
1194 | | * chain. */ |
1195 | 0 | MVM_PUBLIC void MVM_frame_bind_lexical_by_name(MVMThreadContext *tc, MVMString *name, MVMuint16 type, MVMRegister *value) { |
1196 | 0 | MVMFrame *cur_frame = tc->cur_frame; |
1197 | 0 | while (cur_frame != NULL) { |
1198 | 0 | MVMLexicalRegistry *lexical_names = cur_frame->static_info->body.lexical_names; |
1199 | 0 | if (lexical_names) { |
1200 | 0 | MVMLexicalRegistry *entry; |
1201 | 0 | MVM_HASH_GET(tc, lexical_names, name, entry) |
1202 | 0 | if (entry) { |
1203 | 0 | if (cur_frame->static_info->body.lexical_types[entry->value] == type) { |
1204 | 0 | if (type == MVM_reg_obj || type == MVM_reg_str) { |
1205 | 0 | MVM_ASSIGN_REF(tc, &(cur_frame->header), |
1206 | 0 | cur_frame->env[entry->value].o, value->o); |
1207 | 0 | } |
1208 | 0 | else { |
1209 | 0 | cur_frame->env[entry->value] = *value; |
1210 | 0 | } |
1211 | 0 | return; |
1212 | 0 | } |
1213 | 0 | else { |
1214 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1215 | 0 | char *waste[] = { c_name, NULL }; |
1216 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
1217 | 0 | "Lexical with name '%s' has wrong type", |
1218 | 0 | c_name); |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 | } |
1222 | 0 | cur_frame = cur_frame->outer; |
1223 | 0 | } |
1224 | 0 | { |
1225 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1226 | 0 | char *waste[] = { c_name, NULL }; |
1227 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "No lexical found with name '%s'", |
1228 | 0 | c_name); |
1229 | 0 | } |
1230 | 0 | } |
1231 | | |
1232 | | /* Finds a lexical in the outer frame, throwing if it's not there. */ |
1233 | 4 | MVMObject * MVM_frame_find_lexical_by_name_outer(MVMThreadContext *tc, MVMString *name) { |
1234 | 4 | MVMRegister *r = MVM_frame_find_lexical_by_name_rel(tc, name, tc->cur_frame->outer); |
1235 | 4 | if (r) |
1236 | 4 | return r->o; |
1237 | 0 | else { |
1238 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1239 | 0 | char *waste[] = { c_name, NULL }; |
1240 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "No lexical found with name '%s'", |
1241 | 0 | c_name); |
1242 | 0 | } |
1243 | 4 | } |
1244 | | |
1245 | | /* Looks up the address of the lexical with the specified name, starting with |
1246 | | * the specified frame. Only works if it's an object lexical. */ |
1247 | 6 | MVMRegister * MVM_frame_find_lexical_by_name_rel(MVMThreadContext *tc, MVMString *name, MVMFrame *cur_frame) { |
1248 | 11 | while (cur_frame != NULL) { |
1249 | 10 | MVMLexicalRegistry *lexical_names = cur_frame->static_info->body.lexical_names; |
1250 | 10 | if (lexical_names) { |
1251 | 10 | /* Indexes were formerly stored off-by-one to avoid semi-predicate issue. */ |
1252 | 10 | MVMLexicalRegistry *entry; |
1253 | 10 | MVM_HASH_GET(tc, lexical_names, name, entry) |
1254 | 10 | if (entry) { |
1255 | 5 | if (cur_frame->static_info->body.lexical_types[entry->value] == MVM_reg_obj) { |
1256 | 5 | MVMRegister *result = &cur_frame->env[entry->value]; |
1257 | 5 | if (!result->o) |
1258 | 0 | MVM_frame_vivify_lexical(tc, cur_frame, entry->value); |
1259 | 5 | return result; |
1260 | 5 | } |
1261 | 0 | else { |
1262 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1263 | 0 | char *waste[] = { c_name, NULL }; |
1264 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
1265 | 0 | "Lexical with name '%s' has wrong type", |
1266 | 0 | c_name); |
1267 | 0 | } |
1268 | 5 | } |
1269 | 10 | } |
1270 | 5 | cur_frame = cur_frame->outer; |
1271 | 5 | } |
1272 | 1 | return NULL; |
1273 | 6 | } |
1274 | | |
1275 | | /* Looks up the address of the lexical with the specified name, starting with |
1276 | | * the specified frame. It checks all outer frames of the caller frame chain. */ |
1277 | 10 | MVMRegister * MVM_frame_find_lexical_by_name_rel_caller(MVMThreadContext *tc, MVMString *name, MVMFrame *cur_caller_frame) { |
1278 | 37 | while (cur_caller_frame != NULL) { |
1279 | 35 | MVMFrame *cur_frame = cur_caller_frame; |
1280 | 124 | while (cur_frame != NULL) { |
1281 | 97 | MVMLexicalRegistry *lexical_names = cur_frame->static_info->body.lexical_names; |
1282 | 97 | if (lexical_names) { |
1283 | 84 | /* Indexes were formerly stored off-by-one to avoid semi-predicate issue. */ |
1284 | 84 | MVMLexicalRegistry *entry; |
1285 | 84 | MVM_HASH_GET(tc, lexical_names, name, entry) |
1286 | 84 | if (entry) { |
1287 | 8 | if (cur_frame->static_info->body.lexical_types[entry->value] == MVM_reg_obj) { |
1288 | 8 | MVMRegister *result = &cur_frame->env[entry->value]; |
1289 | 8 | if (!result->o) |
1290 | 0 | MVM_frame_vivify_lexical(tc, cur_frame, entry->value); |
1291 | 8 | return result; |
1292 | 8 | } |
1293 | 0 | else { |
1294 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1295 | 0 | char *waste[] = { c_name, NULL }; |
1296 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
1297 | 0 | "Lexical with name '%s' has wrong type", |
1298 | 0 | c_name); |
1299 | 0 | } |
1300 | 8 | } |
1301 | 84 | } |
1302 | 89 | cur_frame = cur_frame->outer; |
1303 | 89 | } |
1304 | 27 | cur_caller_frame = cur_caller_frame->caller; |
1305 | 27 | } |
1306 | 2 | return NULL; |
1307 | 10 | } |
1308 | | |
1309 | | /* Looks up the address of the lexical with the specified name and the |
1310 | | * specified type. Returns null if it does not exist. */ |
1311 | 237k | static void try_cache_dynlex(MVMThreadContext *tc, MVMFrame *from, MVMFrame *to, MVMString *name, MVMRegister *reg, MVMuint16 type, MVMuint32 fcost, MVMuint32 icost) { |
1312 | 237k | #if MVM_DYNLEX_CACHE_ENABLED |
1313 | 237k | MVMint32 next = 0; |
1314 | 237k | MVMint32 frames = 0; |
1315 | 237k | MVMuint32 desperation = 0; |
1316 | 237k | |
1317 | 237k | if (fcost+icost > 20) |
1318 | 39.5k | desperation = 1; |
1319 | 237k | |
1320 | 955k | while (from && from != to) { |
1321 | 854k | frames++; |
1322 | 854k | if (frames >= next) { |
1323 | 527k | if (!from->dynlex_cache_name || (desperation && frames > 1)) { |
1324 | 390k | MVM_ASSIGN_REF(tc, &(from->header), from->dynlex_cache_name, name); |
1325 | 390k | from->dynlex_cache_reg = reg; |
1326 | 390k | from->dynlex_cache_type = type; |
1327 | 390k | if (desperation && next == 3) { |
1328 | 34.8k | next = fcost / 2; |
1329 | 34.8k | } |
1330 | 355k | else { |
1331 | 355k | if (next) |
1332 | 136k | return; |
1333 | 218k | next = 3; |
1334 | 218k | } |
1335 | 390k | } |
1336 | 527k | } |
1337 | 717k | from = from->caller; |
1338 | 717k | } |
1339 | 237k | #endif |
1340 | 237k | } |
1341 | 690k | MVMRegister * MVM_frame_find_contextual_by_name(MVMThreadContext *tc, MVMString *name, MVMuint16 *type, MVMFrame *cur_frame, MVMint32 vivify, MVMFrame **found_frame) { |
1342 | 690k | FILE *dlog = tc->instance->dynvar_log_fh; |
1343 | 690k | MVMuint32 fcost = 0; /* frames traversed */ |
1344 | 690k | MVMuint32 icost = 0; /* inlines traversed */ |
1345 | 690k | MVMuint32 ecost = 0; /* frames traversed with empty cache */ |
1346 | 690k | MVMuint32 xcost = 0; /* frames traversed with wrong name */ |
1347 | 690k | char *c_name; |
1348 | 690k | MVMuint64 start_time; |
1349 | 690k | MVMuint64 last_time; |
1350 | 690k | |
1351 | 690k | MVMFrame *initial_frame = cur_frame; |
1352 | 690k | if (!name) |
1353 | 0 | MVM_exception_throw_adhoc(tc, "Contextual name cannot be null"); |
1354 | 690k | if (dlog) { |
1355 | 0 | c_name = MVM_string_utf8_encode_C_string(tc, name); |
1356 | 0 | start_time = uv_hrtime(); |
1357 | 0 | last_time = tc->instance->dynvar_log_lasttime; |
1358 | 0 | } |
1359 | 690k | |
1360 | 3.38M | while (cur_frame != NULL) { |
1361 | 3.35M | MVMLexicalRegistry *lexical_names; |
1362 | 3.35M | MVMSpeshCandidate *cand = cur_frame->spesh_cand; |
1363 | 3.35M | /* See if we are inside an inline. Note that this isn't actually |
1364 | 3.35M | * correct for a leaf frame, but those aren't inlined and don't |
1365 | 3.35M | * use getdynlex for their own lexicals since the compiler already |
1366 | 3.35M | * knows where to find them */ |
1367 | 3.35M | if (cand && cand->num_inlines) { |
1368 | 521k | if (cand->jitcode && cur_frame->effective_bytecode == cand->jitcode->bytecode) { |
1369 | 375k | void **labels = cand->jitcode->labels; |
1370 | 375k | void *return_label = cur_frame->jit_entry_label; |
1371 | 375k | MVMJitInline *inls = cand->jitcode->inlines; |
1372 | 375k | MVMint32 i; |
1373 | 375k | if (return_label == NULL) |
1374 | 0 | MVM_oops(tc, "Return label is NULL!\n"); |
1375 | 1.40M | for (i = 0; i < cand->jitcode->num_inlines; i++) { |
1376 | 1.03M | icost++; |
1377 | 1.03M | if (return_label >= labels[inls[i].start_label] && return_label <= labels[inls[i].end_label]) { |
1378 | 88.5k | MVMStaticFrame *isf = cand->inlines[i].code->body.sf; |
1379 | 88.5k | if ((lexical_names = isf->body.lexical_names)) { |
1380 | 262 | MVMLexicalRegistry *entry; |
1381 | 262 | MVM_HASH_GET(tc, lexical_names, name, entry); |
1382 | 262 | if (entry) { |
1383 | 0 | MVMuint16 lexidx = cand->inlines[i].lexicals_start + entry->value; |
1384 | 0 | MVMRegister *result = &cur_frame->env[lexidx]; |
1385 | 0 | *type = cand->lexical_types[lexidx]; |
1386 | 0 | if (vivify && *type == MVM_reg_obj && !result->o) { |
1387 | 0 | MVMROOT(tc, cur_frame, { |
1388 | 0 | MVMROOT(tc, initial_frame, { |
1389 | 0 | MVMROOT(tc, name, { |
1390 | 0 | MVM_frame_vivify_lexical(tc, cur_frame, lexidx); |
1391 | 0 | }); |
1392 | 0 | }); |
1393 | 0 | }); |
1394 | 0 | } |
1395 | 0 | if (fcost+icost > 1) |
1396 | 0 | try_cache_dynlex(tc, initial_frame, cur_frame, name, result, *type, fcost, icost); |
1397 | 0 | if (dlog) { |
1398 | 0 | fprintf(dlog, "I %s %d %d %d %d %"PRIu64" %"PRIu64" %"PRIu64"\n", c_name, fcost, icost, ecost, xcost, last_time, start_time, uv_hrtime()); |
1399 | 0 | fflush(dlog); |
1400 | 0 | MVM_free(c_name); |
1401 | 0 | tc->instance->dynvar_log_lasttime = uv_hrtime(); |
1402 | 0 | } |
1403 | 0 | *found_frame = cur_frame; |
1404 | 0 | return result; |
1405 | 0 | } |
1406 | 262 | } |
1407 | 88.5k | } |
1408 | 1.03M | } |
1409 | 145k | } else { |
1410 | 145k | MVMint32 ret_offset = cur_frame->return_address - cur_frame->effective_bytecode; |
1411 | 145k | MVMint32 i; |
1412 | 578k | for (i = 0; i < cand->num_inlines; i++) { |
1413 | 433k | icost++; |
1414 | 433k | if (ret_offset >= cand->inlines[i].start && ret_offset < cand->inlines[i].end) { |
1415 | 9.60k | MVMStaticFrame *isf = cand->inlines[i].code->body.sf; |
1416 | 9.60k | if ((lexical_names = isf->body.lexical_names)) { |
1417 | 0 | MVMLexicalRegistry *entry; |
1418 | 0 | MVM_HASH_GET(tc, lexical_names, name, entry); |
1419 | 0 | if (entry) { |
1420 | 0 | MVMuint16 lexidx = cand->inlines[i].lexicals_start + entry->value; |
1421 | 0 | MVMRegister *result = &cur_frame->env[lexidx]; |
1422 | 0 | *type = cand->lexical_types[lexidx]; |
1423 | 0 | if (vivify && *type == MVM_reg_obj && !result->o) { |
1424 | 0 | MVMROOT(tc, cur_frame, { |
1425 | 0 | MVMROOT(tc, initial_frame, { |
1426 | 0 | MVMROOT(tc, name, { |
1427 | 0 | MVM_frame_vivify_lexical(tc, cur_frame, lexidx); |
1428 | 0 | }); |
1429 | 0 | }); |
1430 | 0 | }); |
1431 | 0 | } |
1432 | 0 | if (fcost+icost > 1) |
1433 | 0 | try_cache_dynlex(tc, initial_frame, cur_frame, name, result, *type, fcost, icost); |
1434 | 0 | if (dlog) { |
1435 | 0 | fprintf(dlog, "I %s %d %d %d %d %"PRIu64" %"PRIu64" %"PRIu64"\n", c_name, fcost, icost, ecost, xcost, last_time, start_time, uv_hrtime()); |
1436 | 0 | fflush(dlog); |
1437 | 0 | MVM_free(c_name); |
1438 | 0 | tc->instance->dynvar_log_lasttime = uv_hrtime(); |
1439 | 0 | } |
1440 | 0 | *found_frame = cur_frame; |
1441 | 0 | return result; |
1442 | 0 | } |
1443 | 0 | } |
1444 | 9.60k | } |
1445 | 433k | } |
1446 | 145k | } |
1447 | 521k | } |
1448 | 3.35M | |
1449 | 3.35M | /* See if we've got it cached at this level. */ |
1450 | 3.35M | if (cur_frame->dynlex_cache_name) { |
1451 | 1.29M | if (MVM_string_equal(tc, name, cur_frame->dynlex_cache_name)) { |
1452 | 478k | MVMRegister *result = cur_frame->dynlex_cache_reg; |
1453 | 478k | *type = cur_frame->dynlex_cache_type; |
1454 | 478k | if (fcost+icost > 5) |
1455 | 94.9k | try_cache_dynlex(tc, initial_frame, cur_frame, name, result, *type, fcost, icost); |
1456 | 478k | if (dlog) { |
1457 | 0 | fprintf(dlog, "C %s %d %d %d %d %"PRIu64" %"PRIu64" %"PRIu64"\n", c_name, fcost, icost, ecost, xcost, last_time, start_time, uv_hrtime()); |
1458 | 0 | fflush(dlog); |
1459 | 0 | MVM_free(c_name); |
1460 | 0 | tc->instance->dynvar_log_lasttime = uv_hrtime(); |
1461 | 0 | } |
1462 | 478k | *found_frame = cur_frame; |
1463 | 478k | return result; |
1464 | 478k | } |
1465 | 1.29M | else |
1466 | 815k | xcost++; |
1467 | 1.29M | } |
1468 | 3.35M | else |
1469 | 2.06M | ecost++; |
1470 | 3.35M | |
1471 | 3.35M | /* Now look in the frame itself. */ |
1472 | 2.87M | if ((lexical_names = cur_frame->static_info->body.lexical_names)) { |
1473 | 1.50M | MVMLexicalRegistry *entry; |
1474 | 1.50M | MVM_HASH_GET(tc, lexical_names, name, entry) |
1475 | 1.50M | if (entry) { |
1476 | 184k | MVMRegister *result = &cur_frame->env[entry->value]; |
1477 | 184k | *type = cur_frame->static_info->body.lexical_types[entry->value]; |
1478 | 184k | if (vivify && *type == MVM_reg_obj && !result->o) { |
1479 | 0 | MVMROOT(tc, cur_frame, { |
1480 | 0 | MVMROOT(tc, initial_frame, { |
1481 | 0 | MVMROOT(tc, name, { |
1482 | 0 | MVM_frame_vivify_lexical(tc, cur_frame, entry->value); |
1483 | 0 | }); |
1484 | 0 | }); |
1485 | 0 | }); |
1486 | 0 | } |
1487 | 184k | if (dlog) { |
1488 | 0 | fprintf(dlog, "F %s %d %d %d %d %"PRIu64" %"PRIu64" %"PRIu64"\n", c_name, fcost, icost, ecost, xcost, last_time, start_time, uv_hrtime()); |
1489 | 0 | fflush(dlog); |
1490 | 0 | MVM_free(c_name); |
1491 | 0 | tc->instance->dynvar_log_lasttime = uv_hrtime(); |
1492 | 0 | } |
1493 | 184k | if (fcost+icost > 1) |
1494 | 142k | try_cache_dynlex(tc, initial_frame, cur_frame, name, result, *type, fcost, icost); |
1495 | 184k | *found_frame = cur_frame; |
1496 | 184k | return result; |
1497 | 184k | } |
1498 | 1.50M | } |
1499 | 2.69M | fcost++; |
1500 | 2.69M | cur_frame = cur_frame->caller; |
1501 | 2.69M | } |
1502 | 27.5k | if (dlog) { |
1503 | 0 | fprintf(dlog, "N %s %d %d %d %d %"PRIu64" %"PRIu64" %"PRIu64"\n", c_name, fcost, icost, ecost, xcost, last_time, start_time, uv_hrtime()); |
1504 | 0 | fflush(dlog); |
1505 | 0 | MVM_free(c_name); |
1506 | 0 | tc->instance->dynvar_log_lasttime = uv_hrtime(); |
1507 | 0 | } |
1508 | 27.5k | *found_frame = NULL; |
1509 | 27.5k | return NULL; |
1510 | 690k | } |
1511 | | |
1512 | 681k | MVMObject * MVM_frame_getdynlex(MVMThreadContext *tc, MVMString *name, MVMFrame *cur_frame) { |
1513 | 681k | MVMuint16 type; |
1514 | 681k | MVMFrame *found_frame; |
1515 | 681k | MVMRegister *lex_reg = MVM_frame_find_contextual_by_name(tc, name, &type, cur_frame, 1, &found_frame); |
1516 | 681k | MVMObject *result = NULL, *result_type = NULL; |
1517 | 681k | if (lex_reg) { |
1518 | 654k | switch (type) { |
1519 | 0 | case MVM_reg_int64: |
1520 | 0 | result_type = (*tc->interp_cu)->body.hll_config->int_box_type; |
1521 | 0 | if (!result_type) |
1522 | 0 | MVM_exception_throw_adhoc(tc, "missing int box type"); |
1523 | 0 | result = REPR(result_type)->allocate(tc, STABLE(result_type)); |
1524 | 0 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&result); |
1525 | 0 | if (REPR(result)->initialize) |
1526 | 0 | REPR(result)->initialize(tc, STABLE(result), result, OBJECT_BODY(result)); |
1527 | 0 | REPR(result)->box_funcs.set_int(tc, STABLE(result), result, |
1528 | 0 | OBJECT_BODY(result), lex_reg->i64); |
1529 | 0 | MVM_gc_root_temp_pop(tc); |
1530 | 0 | break; |
1531 | 0 | case MVM_reg_num64: |
1532 | 0 | result_type = (*tc->interp_cu)->body.hll_config->num_box_type; |
1533 | 0 | if (!result_type) |
1534 | 0 | MVM_exception_throw_adhoc(tc, "missing num box type"); |
1535 | 0 | result = REPR(result_type)->allocate(tc, STABLE(result_type)); |
1536 | 0 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&result); |
1537 | 0 | if (REPR(result)->initialize) |
1538 | 0 | REPR(result)->initialize(tc, STABLE(result), result, OBJECT_BODY(result)); |
1539 | 0 | REPR(result)->box_funcs.set_num(tc, STABLE(result), result, |
1540 | 0 | OBJECT_BODY(result), lex_reg->n64); |
1541 | 0 | MVM_gc_root_temp_pop(tc); |
1542 | 0 | break; |
1543 | 0 | case MVM_reg_str: |
1544 | 0 | result_type = (*tc->interp_cu)->body.hll_config->str_box_type; |
1545 | 0 | if (!result_type) |
1546 | 0 | MVM_exception_throw_adhoc(tc, "missing str box type"); |
1547 | 0 | result = REPR(result_type)->allocate(tc, STABLE(result_type)); |
1548 | 0 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&result); |
1549 | 0 | if (REPR(result)->initialize) |
1550 | 0 | REPR(result)->initialize(tc, STABLE(result), result, OBJECT_BODY(result)); |
1551 | 0 | REPR(result)->box_funcs.set_str(tc, STABLE(result), result, |
1552 | 0 | OBJECT_BODY(result), lex_reg->s); |
1553 | 0 | MVM_gc_root_temp_pop(tc); |
1554 | 0 | break; |
1555 | 654k | case MVM_reg_obj: |
1556 | 654k | result = lex_reg->o; |
1557 | 654k | break; |
1558 | 0 | default: |
1559 | 0 | MVM_exception_throw_adhoc(tc, "invalid register type in getdynlex: %d", type); |
1560 | 654k | } |
1561 | 654k | } |
1562 | 681k | return result; |
1563 | 681k | } |
1564 | | |
1565 | 9.12k | void MVM_frame_binddynlex(MVMThreadContext *tc, MVMString *name, MVMObject *value, MVMFrame *cur_frame) { |
1566 | 9.12k | MVMuint16 type; |
1567 | 9.12k | MVMFrame *found_frame; |
1568 | 9.12k | MVMRegister *lex_reg = MVM_frame_find_contextual_by_name(tc, name, &type, cur_frame, 0, &found_frame); |
1569 | 9.12k | if (!lex_reg) { |
1570 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1571 | 0 | char *waste[] = { c_name, NULL }; |
1572 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "No contextual found with name '%s'", |
1573 | 0 | c_name); |
1574 | 0 | } |
1575 | 9.12k | switch (type) { |
1576 | 0 | case MVM_reg_int64: |
1577 | 0 | lex_reg->i64 = REPR(value)->box_funcs.get_int(tc, |
1578 | 0 | STABLE(value), value, OBJECT_BODY(value)); |
1579 | 0 | break; |
1580 | 0 | case MVM_reg_num64: |
1581 | 0 | lex_reg->n64 = REPR(value)->box_funcs.get_num(tc, |
1582 | 0 | STABLE(value), value, OBJECT_BODY(value)); |
1583 | 0 | break; |
1584 | 0 | case MVM_reg_str: |
1585 | 0 | MVM_ASSIGN_REF(tc, &(found_frame->header), lex_reg->s, |
1586 | 0 | REPR(value)->box_funcs.get_str(tc, STABLE(value), value, OBJECT_BODY(value))); |
1587 | 0 | break; |
1588 | 9.12k | case MVM_reg_obj: |
1589 | 9.12k | MVM_ASSIGN_REF(tc, &(found_frame->header), lex_reg->o, value); |
1590 | 9.12k | break; |
1591 | 0 | default: |
1592 | 0 | MVM_exception_throw_adhoc(tc, "invalid register type in binddynlex"); |
1593 | 9.12k | } |
1594 | 9.12k | } |
1595 | | |
1596 | | /* Returns the storage unit for the lexical in the specified frame. Does not |
1597 | | * try to vivify anything - gets exactly what is there. */ |
1598 | 4.58k | MVMRegister * MVM_frame_lexical(MVMThreadContext *tc, MVMFrame *f, MVMString *name) { |
1599 | 4.58k | MVMLexicalRegistry *lexical_names = f->static_info->body.lexical_names; |
1600 | 4.58k | if (lexical_names) { |
1601 | 4.58k | MVMLexicalRegistry *entry; |
1602 | 4.58k | MVM_HASH_GET(tc, lexical_names, name, entry) |
1603 | 4.58k | if (entry) |
1604 | 4.58k | return &f->env[entry->value]; |
1605 | 4.58k | } |
1606 | 4.58k | |
1607 | 0 | { |
1608 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1609 | 0 | char *waste[] = { c_name, NULL }; |
1610 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "Frame has no lexical with name '%s'", |
1611 | 0 | c_name); |
1612 | 0 | } |
1613 | 0 | } |
1614 | | |
1615 | | /* Returns the storage unit for the lexical in the specified frame. */ |
1616 | 0 | MVMRegister * MVM_frame_try_get_lexical(MVMThreadContext *tc, MVMFrame *f, MVMString *name, MVMuint16 type) { |
1617 | 0 | MVMLexicalRegistry *lexical_names = f->static_info->body.lexical_names; |
1618 | 0 | if (lexical_names) { |
1619 | 0 | MVMLexicalRegistry *entry; |
1620 | 0 | MVM_HASH_GET(tc, lexical_names, name, entry) |
1621 | 0 | if (entry && f->static_info->body.lexical_types[entry->value] == type) { |
1622 | 0 | MVMRegister *result = &f->env[entry->value]; |
1623 | 0 | if (type == MVM_reg_obj && !result->o) |
1624 | 0 | MVM_frame_vivify_lexical(tc, f, entry->value); |
1625 | 0 | return result; |
1626 | 0 | } |
1627 | 0 | } |
1628 | 0 | return NULL; |
1629 | 0 | } |
1630 | | |
1631 | | /* Returns the primitive type specification for a lexical. */ |
1632 | 10.3k | MVMuint16 MVM_frame_lexical_primspec(MVMThreadContext *tc, MVMFrame *f, MVMString *name) { |
1633 | 10.3k | MVMLexicalRegistry *lexical_names = f->static_info->body.lexical_names; |
1634 | 10.3k | if (lexical_names) { |
1635 | 10.3k | MVMLexicalRegistry *entry; |
1636 | 10.3k | MVM_HASH_GET(tc, lexical_names, name, entry) |
1637 | 10.3k | if (entry) { |
1638 | 10.3k | switch (f->static_info->body.lexical_types[entry->value]) { |
1639 | 0 | case MVM_reg_int64: |
1640 | 0 | return MVM_STORAGE_SPEC_BP_INT; |
1641 | 0 | case MVM_reg_num64: |
1642 | 0 | return MVM_STORAGE_SPEC_BP_NUM; |
1643 | 0 | case MVM_reg_str: |
1644 | 0 | return MVM_STORAGE_SPEC_BP_STR; |
1645 | 10.3k | case MVM_reg_obj: |
1646 | 10.3k | return MVM_STORAGE_SPEC_BP_NONE; |
1647 | 0 | default: |
1648 | 0 | { |
1649 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1650 | 0 | char *waste[] = { c_name, NULL }; |
1651 | 0 | MVM_exception_throw_adhoc_free(tc, waste, |
1652 | 0 | "Unhandled lexical type in lexprimspec for '%s'", |
1653 | 0 | c_name); |
1654 | 0 | } |
1655 | 10.3k | } |
1656 | 10.3k | } |
1657 | 10.3k | } |
1658 | 0 | { |
1659 | 0 | char *c_name = MVM_string_utf8_encode_C_string(tc, name); |
1660 | 0 | char *waste[] = { c_name, NULL }; |
1661 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "Frame has no lexical with name '%s'", |
1662 | 0 | c_name); |
1663 | 0 | } |
1664 | 0 | } |
1665 | | |
1666 | 8.11M | static MVMObject * find_invokee_internal(MVMThreadContext *tc, MVMObject *code, MVMCallsite **tweak_cs, MVMInvocationSpec *is) { |
1667 | 8.11M | if (!MVM_is_null(tc, is->class_handle)) { |
1668 | 8.11M | MVMRegister dest; |
1669 | 8.11M | if (!IS_CONCRETE(code)) |
1670 | 0 | MVM_exception_throw_adhoc(tc, "Can not invoke a code type object"); |
1671 | 8.11M | REPR(code)->attr_funcs.get_attribute(tc, |
1672 | 8.11M | STABLE(code), code, OBJECT_BODY(code), |
1673 | 8.11M | is->class_handle, is->attr_name, |
1674 | 8.11M | is->hint, &dest, MVM_reg_obj); |
1675 | 8.11M | code = dest.o; |
1676 | 8.11M | } |
1677 | 3 | else { |
1678 | 3 | /* Need to tweak the callsite and args to include the code object |
1679 | 3 | * being invoked. */ |
1680 | 3 | if (tweak_cs) { |
1681 | 3 | MVMCallsite *orig = *tweak_cs; |
1682 | 3 | if (orig->with_invocant) { |
1683 | 0 | *tweak_cs = orig->with_invocant; |
1684 | 0 | } |
1685 | 3 | else { |
1686 | 3 | MVMCallsite *new = MVM_calloc(1, sizeof(MVMCallsite)); |
1687 | 3 | MVMint32 fsize = orig->flag_count; |
1688 | 3 | new->flag_count = fsize + 1; |
1689 | 3 | new->arg_flags = MVM_malloc(new->flag_count * sizeof(MVMCallsiteEntry)); |
1690 | 3 | new->arg_flags[0] = MVM_CALLSITE_ARG_OBJ; |
1691 | 3 | memcpy(new->arg_flags + 1, orig->arg_flags, fsize); |
1692 | 3 | new->arg_count = orig->arg_count + 1; |
1693 | 3 | new->num_pos = orig->num_pos + 1; |
1694 | 3 | new->has_flattening = orig->has_flattening; |
1695 | 3 | new->is_interned = 0; |
1696 | 3 | new->with_invocant = NULL; |
1697 | 3 | *tweak_cs = orig->with_invocant = new; |
1698 | 3 | } |
1699 | 3 | memmove(tc->cur_frame->args + 1, tc->cur_frame->args, |
1700 | 3 | orig->arg_count * sizeof(MVMRegister)); |
1701 | 3 | tc->cur_frame->args[0].o = code; |
1702 | 3 | tc->cur_frame->cur_args_callsite = *tweak_cs; /* Keep in sync. */ |
1703 | 3 | } |
1704 | 0 | else { |
1705 | 0 | MVM_exception_throw_adhoc(tc, |
1706 | 0 | "Cannot invoke object with invocation handler in this context"); |
1707 | 0 | } |
1708 | 3 | code = is->invocation_handler; |
1709 | 3 | } |
1710 | 8.11M | return code; |
1711 | 8.11M | } |
1712 | | |
1713 | 347k | MVMObject * MVM_frame_find_invokee(MVMThreadContext *tc, MVMObject *code, MVMCallsite **tweak_cs) { |
1714 | 347k | if (MVM_is_null(tc, code)) |
1715 | 0 | MVM_exception_throw_adhoc(tc, "Cannot invoke null object"); |
1716 | 347k | if (STABLE(code)->invoke == MVM_6model_invoke_default) { |
1717 | 346k | MVMInvocationSpec *is = STABLE(code)->invocation_spec; |
1718 | 346k | if (!is) { |
1719 | 0 | MVM_exception_throw_adhoc(tc, "Cannot invoke this object (REPR: %s; %s)", |
1720 | 0 | REPR(code)->name, STABLE(code)->debug_name); |
1721 | 0 | } |
1722 | 346k | code = find_invokee_internal(tc, code, tweak_cs, is); |
1723 | 346k | } |
1724 | 347k | return code; |
1725 | 347k | } |
1726 | | |
1727 | | MVM_USED_BY_JIT |
1728 | 10.8M | MVMObject * MVM_frame_find_invokee_multi_ok(MVMThreadContext *tc, MVMObject *code, MVMCallsite **tweak_cs, MVMRegister *args) { |
1729 | 10.8M | if (!code) |
1730 | 0 | MVM_exception_throw_adhoc(tc, "Cannot invoke null object"); |
1731 | 10.8M | if (STABLE(code)->invoke == MVM_6model_invoke_default) { |
1732 | 7.90M | MVMInvocationSpec *is = STABLE(code)->invocation_spec; |
1733 | 7.90M | if (!is) { |
1734 | 0 | MVM_exception_throw_adhoc(tc, "Cannot invoke this object (REPR: %s; %s)", REPR(code)->name, STABLE(code)->debug_name); |
1735 | 0 | } |
1736 | 7.90M | if (!MVM_is_null(tc, is->md_class_handle)) { |
1737 | 7.26M | /* We might be able to dig straight into the multi cache and not |
1738 | 7.26M | * have to invoke the proto. */ |
1739 | 7.26M | MVMRegister dest; |
1740 | 7.26M | if (!IS_CONCRETE(code)) |
1741 | 0 | MVM_exception_throw_adhoc(tc, "Can not invoke a code type object"); |
1742 | 7.26M | REPR(code)->attr_funcs.get_attribute(tc, |
1743 | 7.26M | STABLE(code), code, OBJECT_BODY(code), |
1744 | 7.26M | is->md_class_handle, is->md_valid_attr_name, |
1745 | 7.26M | is->md_valid_hint, &dest, MVM_reg_int64); |
1746 | 7.26M | if (dest.i64) { |
1747 | 134k | REPR(code)->attr_funcs.get_attribute(tc, |
1748 | 134k | STABLE(code), code, OBJECT_BODY(code), |
1749 | 134k | is->md_class_handle, is->md_cache_attr_name, |
1750 | 134k | is->md_cache_hint, &dest, MVM_reg_obj); |
1751 | 134k | if (!MVM_is_null(tc, dest.o)) { |
1752 | 134k | MVMObject *result = MVM_multi_cache_find_callsite_args(tc, |
1753 | 134k | dest.o, *tweak_cs, args); |
1754 | 134k | if (result) |
1755 | 130k | return MVM_frame_find_invokee(tc, result, tweak_cs); |
1756 | 134k | } |
1757 | 134k | } |
1758 | 7.26M | } |
1759 | 7.76M | code = find_invokee_internal(tc, code, tweak_cs, is); |
1760 | 7.76M | } |
1761 | 10.6M | return code; |
1762 | 10.8M | } |
1763 | | |
1764 | | /* Creates a MVMContent wrapper object around an MVMFrame. */ |
1765 | 2.61k | MVMObject * MVM_frame_context_wrapper(MVMThreadContext *tc, MVMFrame *f) { |
1766 | 2.61k | MVMObject *ctx; |
1767 | 2.61k | f = MVM_frame_force_to_heap(tc, f); |
1768 | 2.61k | MVMROOT(tc, f, { |
1769 | 2.61k | ctx = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTContext); |
1770 | 2.61k | MVM_ASSIGN_REF(tc, &(ctx->header), ((MVMContext *)ctx)->body.context, f); |
1771 | 2.61k | }); |
1772 | 2.61k | return ctx; |
1773 | 2.61k | } |