Coverage Report

Created: 2017-04-15 07:07

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