/home/travis/build/MoarVM/MoarVM/src/core/continuation.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | 27 | static void clear_tag(MVMThreadContext *tc, void *sr_data) { |
4 | 27 | MVMContinuationTag **update = &tc->cur_frame->extra->continuation_tags; |
5 | 27 | while (*update) { |
6 | 27 | if (*update == sr_data) { |
7 | 27 | *update = (*update)->next; |
8 | 27 | MVM_free(sr_data); |
9 | 27 | return; |
10 | 27 | } |
11 | 0 | update = &((*update)->next); |
12 | 0 | } |
13 | 0 | MVM_exception_throw_adhoc(tc, "Internal error: failed to clear continuation tag"); |
14 | 0 | } |
15 | | void MVM_continuation_reset(MVMThreadContext *tc, MVMObject *tag, |
16 | 29 | MVMObject *code, MVMRegister *res_reg) { |
17 | 29 | /* Save the tag. */ |
18 | 29 | MVMFrameExtra *e = MVM_frame_extra(tc, tc->cur_frame); |
19 | 29 | MVMContinuationTag *tag_record = MVM_malloc(sizeof(MVMContinuationTag)); |
20 | 29 | tag_record->tag = tag; |
21 | 29 | tag_record->active_handlers = tc->active_handlers; |
22 | 29 | tag_record->next = e->continuation_tags; |
23 | 29 | e->continuation_tags = tag_record; |
24 | 29 | |
25 | 29 | /* Were we passed code or a continuation? */ |
26 | 29 | if (REPR(code)->ID == MVM_REPR_ID_MVMContinuation) { |
27 | 3 | /* Continuation; invoke it. */ |
28 | 3 | MVM_continuation_invoke(tc, (MVMContinuation *)code, NULL, res_reg); |
29 | 3 | } |
30 | 26 | else { |
31 | 26 | /* Run the passed code. */ |
32 | 26 | MVMCallsite *null_args_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS); |
33 | 26 | code = MVM_frame_find_invokee(tc, code, NULL); |
34 | 26 | MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_OBJ, null_args_callsite); |
35 | 26 | MVM_frame_special_return(tc, tc->cur_frame, clear_tag, NULL, tag_record, NULL); |
36 | 26 | STABLE(code)->invoke(tc, code, null_args_callsite, tc->cur_frame->args); |
37 | 26 | } |
38 | 29 | |
39 | 29 | MVM_CHECK_CALLER_CHAIN(tc, tc->cur_frame); |
40 | 29 | } |
41 | | |
42 | | void MVM_continuation_control(MVMThreadContext *tc, MVMint64 protect, |
43 | | MVMObject *tag, MVMObject *code, |
44 | 24 | MVMRegister *res_reg) { |
45 | 24 | MVMObject *cont; |
46 | 24 | MVMCallsite *inv_arg_callsite; |
47 | 24 | |
48 | 24 | /* Hunt the tag on the stack. Also toss any dynamic variable cache |
49 | 24 | * entries, as they may be invalid once the continuation is invoked (the |
50 | 24 | * Perl 6 $*THREAD is a good example of a problematic one). */ |
51 | 24 | MVMFrame *root_frame = NULL; |
52 | 24 | MVMContinuationTag *tag_record = NULL; |
53 | 24 | MVMFrame *jump_frame; |
54 | 24 | |
55 | 24 | |
56 | 24 | MVM_jit_code_trampoline(tc); |
57 | 24 | |
58 | 24 | MVMROOT2(tc, tag, code, { |
59 | 24 | jump_frame = MVM_frame_force_to_heap(tc, tc->cur_frame); |
60 | 24 | }); |
61 | 155 | while (jump_frame) { |
62 | 155 | MVMFrameExtra *e = jump_frame->extra; |
63 | 155 | if (e) { |
64 | 29 | e->dynlex_cache_name = NULL; |
65 | 29 | tag_record = e->continuation_tags; |
66 | 31 | while (tag_record) { |
67 | 26 | if (MVM_is_null(tc, tag) || tag_record->tag == tag) |
68 | 24 | break; |
69 | 2 | tag_record = tag_record->next; |
70 | 2 | } |
71 | 29 | if (tag_record) |
72 | 24 | break; |
73 | 29 | } |
74 | 131 | root_frame = jump_frame; |
75 | 131 | jump_frame = jump_frame->caller; |
76 | 131 | } |
77 | 24 | if (!tag_record) |
78 | 0 | MVM_exception_throw_adhoc(tc, "No matching continuation reset found"); |
79 | 24 | if (!root_frame) |
80 | 0 | MVM_exception_throw_adhoc(tc, "No continuation root frame found"); |
81 | 24 | |
82 | 24 | /* Create continuation. */ |
83 | 24 | MVMROOT3(tc, code, jump_frame, root_frame, { |
84 | 24 | cont = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTContinuation); |
85 | 24 | MVM_ASSIGN_REF(tc, &(cont->header), ((MVMContinuation *)cont)->body.top, |
86 | 24 | tc->cur_frame); |
87 | 24 | MVM_ASSIGN_REF(tc, &(cont->header), ((MVMContinuation *)cont)->body.root, |
88 | 24 | root_frame); |
89 | 24 | ((MVMContinuation *)cont)->body.addr = *tc->interp_cur_op; |
90 | 24 | ((MVMContinuation *)cont)->body.res_reg = res_reg; |
91 | 24 | if (tc->instance->profiling) |
92 | 24 | ((MVMContinuation *)cont)->body.prof_cont = |
93 | 24 | MVM_profile_log_continuation_control(tc, root_frame); |
94 | 24 | }); |
95 | 24 | |
96 | 24 | /* Save and clear any active exception handler(s) added since reset. */ |
97 | 24 | if (tc->active_handlers != tag_record->active_handlers) { |
98 | 3 | MVMActiveHandler *ah = tc->active_handlers; |
99 | 3 | while (ah) { |
100 | 3 | if (ah->next_handler == tag_record->active_handlers) { |
101 | 3 | /* Found the handler at the point of reset. Slice off the more |
102 | 3 | * recent ones. */ |
103 | 3 | ((MVMContinuation *)cont)->body.active_handlers = tc->active_handlers; |
104 | 3 | tc->active_handlers = ah->next_handler; |
105 | 3 | ah->next_handler = NULL; |
106 | 3 | break; |
107 | 3 | } |
108 | 0 | ah = ah->next_handler; |
109 | 0 | } |
110 | 3 | } |
111 | 24 | |
112 | 24 | /* Move back to the frame with the reset in it. */ |
113 | 24 | tc->cur_frame = jump_frame; |
114 | 24 | tc->current_frame_nr = jump_frame->sequence_nr; |
115 | 24 | |
116 | 24 | *(tc->interp_cur_op) = tc->cur_frame->return_address; |
117 | 24 | *(tc->interp_bytecode_start) = MVM_frame_effective_bytecode(tc->cur_frame); |
118 | 24 | *(tc->interp_reg_base) = tc->cur_frame->work; |
119 | 24 | *(tc->interp_cu) = tc->cur_frame->static_info->body.cu; |
120 | 24 | |
121 | 24 | /* Clear special return handler, given we didn't just fall out of the |
122 | 24 | * reset. */ |
123 | 24 | MVM_frame_clear_special_return(tc, tc->cur_frame); |
124 | 24 | |
125 | 24 | /* If we're not protecting the follow-up call, remove the tag record. */ |
126 | 24 | if (!protect) |
127 | 24 | clear_tag(tc, tag_record); |
128 | 24 | |
129 | 24 | /* Invoke specified code, passing the continuation. We return to |
130 | 24 | * interpreter to run this, which then returns control to the |
131 | 24 | * original reset or invoke. */ |
132 | 24 | code = MVM_frame_find_invokee(tc, code, NULL); |
133 | 24 | inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); |
134 | 24 | MVM_args_setup_thunk(tc, tc->cur_frame->return_value, tc->cur_frame->return_type, inv_arg_callsite); |
135 | 24 | tc->cur_frame->args[0].o = cont; |
136 | 24 | STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); |
137 | 24 | |
138 | 24 | MVM_CHECK_CALLER_CHAIN(tc, tc->cur_frame); |
139 | 24 | } |
140 | | |
141 | | void MVM_continuation_invoke(MVMThreadContext *tc, MVMContinuation *cont, |
142 | 17 | MVMObject *code, MVMRegister *res_reg) { |
143 | 17 | /* First of all do a repr id check */ |
144 | 17 | if (REPR(cont)->ID != MVM_REPR_ID_MVMContinuation) |
145 | 0 | MVM_exception_throw_adhoc(tc, "continuationinvoke expects an MVMContinuation"); |
146 | 17 | |
147 | 17 | /* Ensure we are the only invoker of the continuation. */ |
148 | 17 | if (!MVM_trycas(&(cont->body.invoked), 0, 1)) |
149 | 0 | MVM_exception_throw_adhoc(tc, "This continuation has already been invoked"); |
150 | 17 | |
151 | 17 | /* Switch caller of the root to current invoker. */ |
152 | 17 | MVMROOT2(tc, cont, code, { |
153 | 17 | MVM_frame_force_to_heap(tc, tc->cur_frame); |
154 | 17 | }); |
155 | 17 | MVM_ASSIGN_REF(tc, &(cont->body.root->header), cont->body.root->caller, tc->cur_frame); |
156 | 17 | |
157 | 17 | /* Set up current frame to receive result. */ |
158 | 17 | tc->cur_frame->return_value = res_reg; |
159 | 17 | tc->cur_frame->return_type = MVM_RETURN_OBJ; |
160 | 17 | tc->cur_frame->return_address = *(tc->interp_cur_op); |
161 | 17 | |
162 | 17 | MVM_jit_code_trampoline(tc); |
163 | 17 | |
164 | 17 | /* Switch to the target frame. */ |
165 | 17 | tc->cur_frame = cont->body.top; |
166 | 17 | tc->current_frame_nr = cont->body.top->sequence_nr; |
167 | 17 | |
168 | 17 | *(tc->interp_cur_op) = cont->body.addr; |
169 | 17 | *(tc->interp_bytecode_start) = MVM_frame_effective_bytecode(tc->cur_frame); |
170 | 17 | *(tc->interp_reg_base) = tc->cur_frame->work; |
171 | 17 | *(tc->interp_cu) = tc->cur_frame->static_info->body.cu; |
172 | 17 | |
173 | 17 | /* Put saved active handlers list in place. */ |
174 | 17 | /* TODO: if we really need to support double-shot, this needs a re-visit. |
175 | 17 | * As it is, Rakudo's gather/take only needs single-invoke continuations, |
176 | 17 | * so we'll punt on the issue for now. */ |
177 | 17 | if (cont->body.active_handlers) { |
178 | 3 | MVMActiveHandler *ah = cont->body.active_handlers; |
179 | 3 | while (ah->next_handler) |
180 | 0 | ah = ah->next_handler; |
181 | 3 | ah->next_handler = tc->active_handlers; |
182 | 3 | tc->active_handlers = cont->body.active_handlers; |
183 | 3 | cont->body.active_handlers = NULL; |
184 | 3 | } |
185 | 17 | |
186 | 17 | /* If we're profiling, get it back in sync. */ |
187 | 17 | if (cont->body.prof_cont && tc->instance->profiling) |
188 | 0 | MVM_profile_log_continuation_invoke(tc, cont->body.prof_cont); |
189 | 17 | |
190 | 17 | /* Provided we have it, invoke the specified code, putting its result in |
191 | 17 | * the specified result register. Otherwise, put a NULL there. */ |
192 | 17 | if (MVM_is_null(tc, code)) { |
193 | 3 | cont->body.res_reg->o = tc->instance->VMNull; |
194 | 3 | } |
195 | 14 | else { |
196 | 14 | MVMCallsite *null_args_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS); |
197 | 14 | code = MVM_frame_find_invokee(tc, code, NULL); |
198 | 14 | MVM_args_setup_thunk(tc, cont->body.res_reg, MVM_RETURN_OBJ, null_args_callsite); |
199 | 14 | STABLE(code)->invoke(tc, code, null_args_callsite, tc->cur_frame->args); |
200 | 14 | } |
201 | 17 | |
202 | 17 | MVM_CHECK_CALLER_CHAIN(tc, tc->cur_frame); |
203 | 17 | } |
204 | | |
205 | 1 | void MVM_continuation_free_tags(MVMThreadContext *tc, MVMFrame *f) { |
206 | 1 | MVMContinuationTag *tag = f->extra->continuation_tags; |
207 | 2 | while (tag) { |
208 | 1 | MVMContinuationTag *next = tag->next; |
209 | 1 | MVM_free(tag); |
210 | 1 | tag = next; |
211 | 1 | } |
212 | 1 | f->extra->continuation_tags = NULL; |
213 | 1 | } |