scm_i_make_continuation takes vm and vm_cont args explicitly
[bpt/guile.git] / libguile / continuations.c
CommitLineData
1d1cae0e 1/* Copyright (C) 1995,1996,1998,2000,2001,2004, 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
0f2d19dd 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
0f2d19dd 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
1bbd0b84 18
1bbd0b84 19
0f2d19dd 20\f
dbb605f5
LC
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
0f2d19dd 24
bbd43f03
RB
25#include "libguile/_scm.h"
26
13070bd3 27#include <string.h>
8b7f0bb3 28#include <stdio.h>
13070bd3 29
8b7f0bb3 30#include "libguile/async.h"
d0f6ceb8 31#include "libguile/debug.h"
a0599745
MD
32#include "libguile/root.h"
33#include "libguile/stackchk.h"
5f144b10
GH
34#include "libguile/smob.h"
35#include "libguile/ports.h"
36#include "libguile/dynwind.h"
ce212434 37#include "libguile/values.h"
9de87eea 38#include "libguile/eval.h"
bfffd258 39#include "libguile/vm.h"
1d1cae0e 40#include "libguile/instructions.h"
5f144b10 41
db4b4ca6 42#include "libguile/validate.h"
a0599745 43#include "libguile/continuations.h"
01c8a3dd 44
0f2d19dd
JB
45\f
46
1d1cae0e
AW
47static scm_t_bits tc16_continuation;
48#define SCM_CONTREGSP(x) SCM_TYP16_PREDICATE (tc16_continuation, x)
49
50#define SCM_CONTREGS(x) ((scm_t_contregs *) SCM_SMOB_DATA_1 (x))
51
52#define SCM_CONTINUATION_LENGTH(x) (SCM_CONTREGS (x)->num_stack_items)
53#define SCM_SET_CONTINUATION_LENGTH(x, n)\
54 (SCM_CONTREGS (x)->num_stack_items = (n))
55#define SCM_JMPBUF(x) ((SCM_CONTREGS (x))->jmpbuf)
56#define SCM_DYNENV(x) ((SCM_CONTREGS (x))->dynenv)
57#define SCM_THROW_VALUE(x) ((SCM_CONTREGS (x))->throw_value)
58#define SCM_CONTINUATION_ROOT(x) ((SCM_CONTREGS (x))->root)
59#define SCM_DFRAME(x) ((SCM_CONTREGS (x))->dframe)
60
61\f
62
997659f8 63/* scm_i_make_continuation will return a procedure whose objcode contains an
1d1cae0e
AW
64 instruction to reinstate the continuation. Here, as in gsubr.c and smob.c, we
65 define the form of that trampoline function.
0f2d19dd
JB
66 */
67
1d1cae0e 68#ifdef WORDS_BIGENDIAN
babfc7b2
AW
69#define OBJCODE_HEADER(main,meta) 0, 0, 0, main, 0, 0, 0, meta+8
70#define META_HEADER(meta) 0, 0, 0, meta, 0, 0, 0, 0
1d1cae0e 71#else
babfc7b2
AW
72#define OBJCODE_HEADER(main,meta) main, 0, 0, 0, meta+8, 0, 0, 0
73#define META_HEADER(meta) meta, 0, 0, 0, 0, 0, 0, 0
1d1cae0e
AW
74#endif
75
76#define ROUND_UP(len,align) (((len-1)|(align-1))+1)
77#define ALIGN_PTR(type,p,align) (type*)(ROUND_UP (((scm_t_bits)p), align))
78
79#ifdef SCM_ALIGNED
80#define SCM_DECLARE_STATIC_ALIGNED_ARRAY(type, sym)\
81static const type sym[]
82#define SCM_STATIC_ALIGNED_ARRAY(alignment, type, sym)\
83static SCM_ALIGNED (alignment) const type sym[]
84#else
85#define SCM_DECLARE_STATIC_ALIGNED_ARRAY(type, sym)\
86static type *sym
87#define SCM_STATIC_ALIGNED_ARRAY(alignment, type, sym) \
88SCM_SNARF_INIT(sym = scm_malloc (sizeof(sym##__unaligned) + alignment - 1); \
89 sym = ALIGN_PTR (type, sym, alignment); \
90 memcpy (sym, sym##__unaligned, sizeof(sym##__unaligned));) \
91static type *sym = NULL; \
92static const type sym##__unaligned[]
93#endif
94
95#define STATIC_OBJCODE_TAG \
96 SCM_PACK (scm_tc7_objcode | (SCM_F_OBJCODE_IS_STATIC << 8))
97
98#define SCM_STATIC_OBJCODE(sym) \
99 SCM_DECLARE_STATIC_ALIGNED_ARRAY (scm_t_uint8, sym##__bytecode); \
100 SCM_STATIC_ALIGNED_ARRAY (8, scm_t_cell, sym##__cells) = { \
101 { STATIC_OBJCODE_TAG, SCM_PACK (sym##__bytecode) }, \
102 { SCM_BOOL_F, SCM_PACK (0) } \
103 }; \
104 static const SCM sym = SCM_PACK (sym##__cells); \
105 SCM_STATIC_ALIGNED_ARRAY (8, scm_t_uint8, sym##__bytecode)
106
107
108SCM_STATIC_OBJCODE (cont_objcode) = {
109 /* This code is the same as in gsubr.c, except we use smob_call instead of
110 struct_call. */
babfc7b2 111 OBJCODE_HEADER (8, 19),
1d1cae0e
AW
112 /* leave args on the stack */
113 /* 0 */ scm_op_object_ref, 0, /* push scm_t_contregs smob */
114 /* 2 */ scm_op_continuation_call, /* and longjmp (whee) */
115 /* 3 */ scm_op_nop, /* pad to 8 bytes */
116 /* 4 */ scm_op_nop, scm_op_nop, scm_op_nop, scm_op_nop,
117 /* 8 */
118
119 /* We could put some meta-info to say that this proc is a continuation. Not sure
120 how to do that, though. */
babfc7b2 121 META_HEADER (19),
1d1cae0e
AW
122 /* 0 */ scm_op_make_eol, /* bindings */
123 /* 1 */ scm_op_make_eol, /* sources */
124 /* 2 */ scm_op_make_int8, 0, scm_op_make_int8, 3, /* arity: from ip 0 to ip 3 */
125 /* 6 */ scm_op_make_int8_0, /* the arity is 0 required args */
126 /* 7 */ scm_op_make_int8_0, /* 0 optionals */
127 /* 8 */ scm_op_make_true, /* and a rest arg */
128 /* 9 */ scm_op_list, 0, 5, /* make a list of those 5 vals */
129 /* 12 */ scm_op_list, 0, 1, /* and the arities will be a list of that one list */
130 /* 15 */ scm_op_list, 0, 3, /* pack bindings, sources, and arities into list */
131 /* 18 */ scm_op_return /* and return */
132 /* 19 */
133};
134
135
babfc7b2
AW
136SCM_STATIC_OBJCODE (call_cc_objcode) = {
137 /* Before Scheme's call/cc is compiled, eval.c will use this hand-coded
138 call/cc. */
139 OBJCODE_HEADER (8, 17),
140 /* 0 */ scm_op_assert_nargs_ee, 0, 1, /* assert that nargs==1 */
141 /* 3 */ scm_op_local_ref, 0, /* push the proc */
142 /* 5 */ scm_op_tail_call_cc, /* and call/cc */
143 /* 6 */ scm_op_nop, scm_op_nop, /* pad to 8 bytes */
144 /* 8 */
145
146 META_HEADER (17),
147 /* 0 */ scm_op_make_eol, /* bindings */
148 /* 1 */ scm_op_make_eol, /* sources */
149 /* 2 */ scm_op_make_int8, 3, scm_op_make_int8, 6, /* arity: from ip 0 to ip 6 */
150 /* 6 */ scm_op_make_int8_1, /* the arity is 0 required args */
151 /* 7 */ scm_op_list, 0, 3, /* make a list of those 5 vals */
152 /* 10 */ scm_op_list, 0, 1, /* and the arities will be a list of that one list */
153 /* 13 */ scm_op_list, 0, 3, /* pack bindings, sources, and arities into list */
154 /* 16 */ scm_op_return /* and return */
155 /* 17 */
156};
157
158
1d1cae0e
AW
159static SCM
160make_continuation_trampoline (SCM contregs)
161{
162 SCM ret = scm_make_program (cont_objcode,
163 scm_c_make_vector (1, contregs),
164 SCM_BOOL_F);
165 SCM_SET_CELL_WORD_0 (ret,
166 SCM_CELL_WORD_0 (ret) | SCM_F_PROGRAM_IS_CONTINUATION);
167
168 return ret;
169}
170
171
172/* {Continuations}
173 */
0f2d19dd 174
01c8a3dd 175
e841c3e0 176static int
e81d98ec 177continuation_print (SCM obj, SCM port, scm_print_state *state SCM_UNUSED)
5f144b10 178{
92c2555f 179 scm_t_contregs *continuation = SCM_CONTREGS (obj);
5f144b10
GH
180
181 scm_puts ("#<continuation ", port);
182 scm_intprint (continuation->num_stack_items, 10, port);
183 scm_puts (" @ ", port);
0236bc68 184 scm_uintprint (SCM_SMOB_DATA_1 (obj), 16, port);
5f144b10
GH
185 scm_putc ('>', port);
186 return 1;
187}
1cc91f1b 188
5f144b10
GH
189/* this may return more than once: the first time with the escape
190 procedure, then subsequently with the value to be passed to the
191 continuation. */
997659f8 192#define FUNC_NAME "scm_i_make_continuation"
0f2d19dd 193SCM
269479e3 194scm_i_make_continuation (int *first, SCM vm, SCM vm_cont)
0f2d19dd 195{
9de87eea
MV
196 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
197 SCM cont;
92c2555f 198 scm_t_contregs *continuation;
c014a02e 199 long stack_size;
01c8a3dd 200 SCM_STACKITEM * src;
0f2d19dd 201
0f2d19dd 202 SCM_FLUSH_REGISTER_WINDOWS;
9de87eea 203 stack_size = scm_stack_size (thread->continuation_base);
4c9419ac
MV
204 continuation = scm_gc_malloc (sizeof (scm_t_contregs)
205 + (stack_size - 1) * sizeof (SCM_STACKITEM),
206 "continuation");
5f144b10 207 continuation->num_stack_items = stack_size;
9de87eea 208 continuation->dynenv = scm_i_dynwinds ();
5f144b10 209 continuation->throw_value = SCM_EOL;
9de87eea 210 continuation->root = thread->continuation_root;
9de87eea 211 src = thread->continuation_base;
4ccb2cd2 212#if ! SCM_STACK_GROWS_UP
5f144b10
GH
213 src -= stack_size;
214#endif
5c5c27dc 215 continuation->offset = continuation->stack - src;
5f144b10 216 memcpy (continuation->stack, src, sizeof (SCM_STACKITEM) * stack_size);
269479e3
AW
217 continuation->vm = vm;
218 continuation->vm_cont = vm_cont;
5f144b10 219
1d1cae0e 220 SCM_NEWSMOB (cont, tc16_continuation, continuation);
79824460 221
a4dbe1ac 222 *first = !SCM_I_SETJMP (continuation->jmpbuf);
346e4402 223 if (*first)
193297d8 224 {
346e4402 225#ifdef __ia64__
9a5fa6e9 226 continuation->backing_store_size =
346e4402 227 (char *) scm_ia64_ar_bsp(&continuation->jmpbuf.ctx)
9a5fa6e9 228 -
346e4402 229 (char *) thread->register_backing_store_base;
193297d8
RB
230 continuation->backing_store = NULL;
231 continuation->backing_store =
4c9419ac
MV
232 scm_gc_malloc (continuation->backing_store_size,
233 "continuation backing store");
193297d8 234 memcpy (continuation->backing_store,
346e4402 235 (void *) thread->register_backing_store_base,
193297d8 236 continuation->backing_store_size);
346e4402 237#endif /* __ia64__ */
1d1cae0e 238 return make_continuation_trampoline (cont);
193297d8
RB
239 }
240 else
241 {
3c468478 242 SCM ret = continuation->throw_value;
3c468478
MV
243 continuation->throw_value = SCM_BOOL_F;
244 return ret;
5f144b10 245 }
0f2d19dd 246}
5f144b10 247#undef FUNC_NAME
0f2d19dd 248
babfc7b2
AW
249SCM
250scm_i_call_with_current_continuation (SCM proc)
251{
252 static SCM call_cc = SCM_BOOL_F;
253
254 if (scm_is_false (call_cc))
255 call_cc = scm_make_program (call_cc_objcode, SCM_BOOL_F, SCM_BOOL_F);
256
257 return scm_call_1 (call_cc, proc);
258}
259
1d1cae0e
AW
260SCM
261scm_i_continuation_to_frame (SCM continuation)
262{
263 SCM contregs;
264 scm_t_contregs *cont;
265
266 contregs = scm_c_vector_ref (scm_program_objects (continuation), 0);
267 cont = SCM_CONTREGS (contregs);
268
269479e3 269 if (scm_is_true (cont->vm_cont))
1d1cae0e 270 {
269479e3
AW
271 struct scm_vm_cont *data = SCM_VM_CONT_DATA (cont->vm_cont);
272 return scm_c_make_frame (cont->vm_cont,
1d1cae0e
AW
273 data->fp + data->reloc,
274 data->sp + data->reloc,
275 data->ip,
276 data->reloc);
277 }
278 else
279 return SCM_BOOL_F;
280}
281
282
283/* {Apply}
284 */
d3c6aef9
MV
285
286/* Invoking a continuation proceeds as follows:
287 *
288 * - the stack is made large enough for the called continuation
289 * - the old windchain is unwound down to the branching point
290 * - the continuation stack is copied into place
291 * - the windchain is rewound up to the continuation's context
292 * - the continuation is invoked via longjmp (or setcontext)
293 *
294 * This order is important so that unwind and rewind handlers are run
295 * with their correct stack.
296 */
297
5f144b10 298static void scm_dynthrow (SCM, SCM);
01c8a3dd
DH
299
300/* Grow the stack by a fixed amount to provide space to copy in the
301 * continuation. Possibly this function has to be called several times
302 * recursively before enough space is available. Make sure the compiler does
303 * not optimize the growth array away by storing it's address into a global
304 * variable.
305 */
306
92c2555f 307scm_t_bits scm_i_dummy;
1cc91f1b 308
0f2d19dd 309static void
01c8a3dd
DH
310grow_stack (SCM cont, SCM val)
311{
92c2555f 312 scm_t_bits growth[100];
01c8a3dd 313
92c2555f 314 scm_i_dummy = (scm_t_bits) growth;
01c8a3dd 315 scm_dynthrow (cont, val);
0f2d19dd 316}
0f2d19dd 317
1cc91f1b 318
01c8a3dd
DH
319/* Copy the continuation stack into the current stack. Calling functions from
320 * within this function is safe, since only stack frames below this function's
321 * own frame are overwritten. Thus, memcpy can be used for best performance.
322 */
d3c6aef9
MV
323
324typedef struct {
325 scm_t_contregs *continuation;
326 SCM_STACKITEM *dst;
327} copy_stack_data;
328
01c8a3dd 329static void
d3c6aef9
MV
330copy_stack (void *data)
331{
332 copy_stack_data *d = (copy_stack_data *)data;
333 memcpy (d->dst, d->continuation->stack,
334 sizeof (SCM_STACKITEM) * d->continuation->num_stack_items);
269479e3
AW
335 scm_i_vm_reinstate_continuation (d->continuation->vm,
336 d->continuation->vm_cont);
346e4402
NJ
337#ifdef __ia64__
338 SCM_I_CURRENT_THREAD->pending_rbs_continuation = d->continuation;
339#endif
d3c6aef9
MV
340}
341
342static void
343copy_stack_and_call (scm_t_contregs *continuation, SCM val,
5f144b10 344 SCM_STACKITEM * dst)
0f2d19dd 345{
d3c6aef9
MV
346 long delta;
347 copy_stack_data data;
348
9de87eea 349 delta = scm_ilength (scm_i_dynwinds ()) - scm_ilength (continuation->dynenv);
d3c6aef9
MV
350 data.continuation = continuation;
351 data.dst = dst;
14578fa4 352 scm_i_dowinds (continuation->dynenv, delta, copy_stack, &data);
01c8a3dd 353
5f144b10 354 continuation->throw_value = val;
a4dbe1ac 355 SCM_I_LONGJMP (continuation->jmpbuf, 1);
01c8a3dd
DH
356}
357
346e4402
NJ
358#ifdef __ia64__
359void
a4dbe1ac 360scm_ia64_longjmp (scm_i_jmp_buf *JB, int VAL)
346e4402
NJ
361{
362 scm_i_thread *t = SCM_I_CURRENT_THREAD;
363
364 if (t->pending_rbs_continuation)
365 {
366 memcpy (t->register_backing_store_base,
367 t->pending_rbs_continuation->backing_store,
368 t->pending_rbs_continuation->backing_store_size);
369 t->pending_rbs_continuation = NULL;
370 }
371 setcontext (&JB->ctx);
372}
373#endif
374
01c8a3dd
DH
375/* Call grow_stack until the stack space is large enough, then, as the current
376 * stack frame might get overwritten, let copy_stack_and_call perform the
377 * actual copying and continuation calling.
378 */
379static void
380scm_dynthrow (SCM cont, SCM val)
381{
9de87eea 382 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
92c2555f 383 scm_t_contregs *continuation = SCM_CONTREGS (cont);
9de87eea 384 SCM_STACKITEM *dst = thread->continuation_base;
01c8a3dd
DH
385 SCM_STACKITEM stack_top_element;
386
87f30eda 387 if (thread->critical_section_level)
8b7f0bb3
MV
388 {
389 fprintf (stderr, "continuation invoked from within critical section.\n");
390 abort ();
391 }
392
4ccb2cd2 393#if SCM_STACK_GROWS_UP
5afcf08b 394 if (dst + continuation->num_stack_items >= &stack_top_element)
01c8a3dd 395 grow_stack (cont, val);
0f2d19dd 396#else
5f144b10 397 dst -= continuation->num_stack_items;
c8a1bdc4 398 if (dst <= &stack_top_element)
01c8a3dd 399 grow_stack (cont, val);
0f2d19dd 400#endif /* def SCM_STACK_GROWS_UP */
01c8a3dd 401
5f144b10
GH
402 SCM_FLUSH_REGISTER_WINDOWS;
403 copy_stack_and_call (continuation, val, dst);
0f2d19dd
JB
404}
405
db4b4ca6 406
1d1cae0e
AW
407void
408scm_i_continuation_call (SCM cont, size_t n, SCM *argv)
0f2d19dd 409{
9de87eea 410 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
92c2555f 411 scm_t_contregs *continuation = SCM_CONTREGS (cont);
1d1cae0e
AW
412 SCM args = SCM_EOL;
413
414 /* FIXME: shuffle args on VM stack instead of heap-allocating */
415 while (n--)
416 args = scm_cons (argv[n], args);
5f144b10 417
9de87eea 418 if (continuation->root != thread->continuation_root)
1d1cae0e
AW
419 scm_misc_error
420 ("%continuation-call",
421 "invoking continuation would cross continuation barrier: ~A",
422 scm_list_1 (cont));
0f2d19dd 423
ce212434 424 scm_dynthrow (cont, scm_values (args));
0f2d19dd 425}
0f2d19dd 426
9de87eea
MV
427SCM
428scm_i_with_continuation_barrier (scm_t_catch_body body,
429 void *body_data,
430 scm_t_catch_handler handler,
43e01b1e
NJ
431 void *handler_data,
432 scm_t_catch_handler pre_unwind_handler,
433 void *pre_unwind_handler_data)
9de87eea
MV
434{
435 SCM_STACKITEM stack_item;
436 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
437 SCM old_controot;
438 SCM_STACKITEM *old_contbase;
9de87eea
MV
439 SCM result;
440
441 /* Establish a fresh continuation root.
442 */
443 old_controot = thread->continuation_root;
444 old_contbase = thread->continuation_base;
9de87eea
MV
445 thread->continuation_root = scm_cons (thread->handle, old_controot);
446 thread->continuation_base = &stack_item;
9de87eea
MV
447
448 /* Call FUNC inside a catch all. This is now guaranteed to return
449 directly and exactly once.
450 */
43e01b1e
NJ
451 result = scm_c_catch (SCM_BOOL_T,
452 body, body_data,
453 handler, handler_data,
454 pre_unwind_handler, pre_unwind_handler_data);
9de87eea
MV
455
456 /* Return to old continuation root.
457 */
9de87eea
MV
458 thread->continuation_base = old_contbase;
459 thread->continuation_root = old_controot;
460
461 return result;
462}
463
464struct c_data {
465 void *(*func) (void *);
466 void *data;
467 void *result;
468};
469
470static SCM
471c_body (void *d)
472{
473 struct c_data *data = (struct c_data *)d;
474 data->result = data->func (data->data);
475 return SCM_UNSPECIFIED;
476}
477
478static SCM
479c_handler (void *d, SCM tag, SCM args)
480{
481 struct c_data *data = (struct c_data *)d;
9de87eea
MV
482 data->result = NULL;
483 return SCM_UNSPECIFIED;
484}
485
486void *
487scm_c_with_continuation_barrier (void *(*func) (void *), void *data)
488{
489 struct c_data c_data;
490 c_data.func = func;
491 c_data.data = data;
492 scm_i_with_continuation_barrier (c_body, &c_data,
43e01b1e
NJ
493 c_handler, &c_data,
494 scm_handle_by_message_noexit, NULL);
9de87eea
MV
495 return c_data.result;
496}
497
498struct scm_data {
499 SCM proc;
500};
501
502static SCM
503scm_body (void *d)
504{
505 struct scm_data *data = (struct scm_data *)d;
506 return scm_call_0 (data->proc);
507}
508
509static SCM
510scm_handler (void *d, SCM tag, SCM args)
511{
9de87eea
MV
512 return SCM_BOOL_F;
513}
514
515SCM_DEFINE (scm_with_continuation_barrier, "with-continuation-barrier", 1,0,0,
516 (SCM proc),
69d2000d
MV
517"Call @var{proc} and return its result. Do not allow the invocation of\n"
518"continuations that would leave or enter the dynamic extent of the call\n"
519"to @code{with-continuation-barrier}. Such an attempt causes an error\n"
520"to be signaled.\n"
521"\n"
522"Throws (such as errors) that are not caught from within @var{proc} are\n"
523"caught by @code{with-continuation-barrier}. In that case, a short\n"
524"message is printed to the current error port and @code{#f} is returned.\n"
525"\n"
526"Thus, @code{with-continuation-barrier} returns exactly once.\n")
9de87eea
MV
527#define FUNC_NAME s_scm_with_continuation_barrier
528{
529 struct scm_data scm_data;
530 scm_data.proc = proc;
531 return scm_i_with_continuation_barrier (scm_body, &scm_data,
43e01b1e
NJ
532 scm_handler, &scm_data,
533 scm_handle_by_message_noexit, NULL);
9de87eea
MV
534}
535#undef FUNC_NAME
db4b4ca6 536
0f2d19dd
JB
537void
538scm_init_continuations ()
0f2d19dd 539{
1d1cae0e
AW
540 tc16_continuation = scm_make_smob_type ("continuation", 0);
541 scm_set_smob_print (tc16_continuation, continuation_print);
a0599745 542#include "libguile/continuations.x"
0f2d19dd
JB
543}
544
89e00824
ML
545/*
546 Local Variables:
547 c-file-style: "gnu"
548 End:
549*/