Coverage Report

Created: 2018-07-03 15:31

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