1 /*
2 * vm.c - virtual machine
3 *
4 * Copyright (c) 2000-2020 Shiro Kawai <shiro@acm.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the authors nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #define LIBGAUCHE_BODY
35 #include "gauche.h"
36 #include "gauche/class.h"
37 #include "gauche/exception.h"
38 #include "gauche/priv/builtin-syms.h"
39 #include "gauche/priv/vmP.h"
40 #include "gauche/priv/identifierP.h"
41 #include "gauche/priv/parameterP.h"
42 #include "gauche/code.h"
43 #include "gauche/vminsn.h"
44 #include "gauche/prof.h"
45
46
47 /* Experimental code to use custom mark procedure for stack gc.
48 Currently it doens't show any improvement, so we disable it
49 by default. */
50 #ifdef USE_CUSTOM_STACK_MARKER
51 #include "gc_mark.h"
52
53 static void **vm_stack_free_list;
54 static int vm_stack_kind;
55 static int vm_stack_mark_proc;
56 #endif /*USE_CUSTOM_STACK_MARKER*/
57
58 #ifdef HAVE_SCHED_H
59 #include <sched.h>
60 #endif
61
62 #ifndef EX_SOFTWARE
63 /* SRFI-22 requires this. */
64 #define EX_SOFTWARE 70
65 #endif
66
67 /* An object to mark the boundary frame. */
68 static ScmWord boundaryFrameMark = SCM_VM_INSN(SCM_VM_NOP);
69
70 /* return true if cont is a boundary continuation frame */
71 #define BOUNDARY_FRAME_P(cont) ((cont)->pc == &boundaryFrameMark)
72
73 /* return true if cont has the end marker of partial continuation */
74 #define MARKER_FRAME_P(cont) ((cont)->marker == 1)
75
76 /* A stub VM code to make VM return immediately */
77 static ScmWord return_code[] = { SCM_VM_INSN(SCM_VM_RET) };
78 #define PC_TO_RETURN return_code
79
80 /* A dummy env frame to indicate C Continuation */
81 static ScmEnvFrame ccEnvMark = {
82 NULL, /* up */
83 SCM_FALSE, /* info */
84 0 /* size */
85 };
86
87 #define C_CONTINUATION_P(cont) ((cont)->env == &ccEnvMark)
88
89 /* A dummy compiled code structure used as 'fill-in', when Scm_Apply
90 is called without any VM code running. See Scm_Apply below. */
91 static ScmCompiledCode internal_apply_compiled_code =
92 SCM_COMPILED_CODE_CONST_INITIALIZER(NULL, 0, 0, 0, 0,
93 SCM_SYM_INTERNAL_APPLY,
94 SCM_NIL, SCM_FALSE,
95 SCM_FALSE, SCM_FALSE);
96
97 /* This saves offset of each instruction handler, initialized by
98 the first call to run_loop. The info can be used for detailed
99 profiling. */
100 static unsigned long vminsn_offsets[SCM_VM_NUM_INSNS] = { 0, };
101
102 /*
103 * The VM.
104 *
105 * VM encapsulates the dynamic status of the current execution.
106 * In Gauche, there's always one active virtual machine per thread,
107 * referred by Scm_VM(). From Scheme, VM is seen as a <thread> object.
108 *
109 * From Scheme, VM is viewed as <thread> object. The class definition
110 * is in thrlib.stub.
111 */
112
113 static ScmVM *rootVM = NULL; /* VM for primodial thread */
114 static ScmHashCore vm_table; /* VMs other than primordial one is
115 registered to this hashtable, in order
116 to avoid being GC-ed. */
117 static ScmInternalMutex vm_table_mutex;
118 static void vm_register(ScmVM *vm);
119 static void vm_unregister(ScmVM *vm);
120
121 static u_long vm_numeric_id = 0; /* used for Scm_VM->vmid */
122 static ScmInternalMutex vm_id_mutex;
123
124 #define CALL_TRACE_SIZE_MIN 512
125 #define CALL_TRACE_SIZE_MAX 65535
126 static u_long vm_call_trace_size = 0; /* global default */
127
128 #ifdef GAUCHE_USE_PTHREADS
129 static pthread_key_t vm_key;
130 #define theVM ((ScmVM*)pthread_getspecific(vm_key))
131 #elif GAUCHE_USE_WTHREADS
132 static DWORD vm_key;
133 #define theVM ((ScmVM*)TlsGetValue(vm_key))
134 #else /* !GAUCHE_USE_PTHREADS && !GAUCHE_USE_WTHREADS */
135 static ScmVM *theVM;
136 #endif /* !GAUCHE_USE_PTHREADS */
137
138 static void save_stack(ScmVM *vm);
139
140 static ScmSubr default_exception_handler_rec;
141 #define DEFAULT_EXCEPTION_HANDLER SCM_OBJ(&default_exception_handler_rec)
142 static ScmObj throw_cont_calculate_handlers(ScmObj target, ScmObj current);
143 static void call_dynamic_handlers(ScmObj target, ScmObj current);
144 static ScmObj throw_cont_body(ScmObj, ScmEscapePoint*, ScmObj);
145 static void process_queued_requests(ScmVM *vm);
146 static void vm_finalize(ScmObj vm, void *data);
147 static int check_arglist_tail_for_apply(ScmVM *vm, ScmObj restargs, int max_count);
148
149 static ScmEnvFrame *get_env(ScmVM *vm);
150
151 static void call_error_reporter(ScmObj e);
152
153 /*#define COUNT_INSN_FREQUENCY*/
154 #ifdef COUNT_INSN_FREQUENCY
155 #include "vmstat.c"
156 #endif /*COUNT_INSN_FREQUENCY*/
157
158 /*
159 * Constructor
160 *
161 * PROTO argument is treated as a prototype for the new VM, i.e.
162 * some of default values are 'inherited' from PROTO.
163 *
164 * VM should be 'attached' to the running OS thread before being
165 * used. The root thread is always attached to the primordial thread
166 * at the initialization stage (see Scm__InitVM()). For other threads,
167 * it depends on whether the thread is created from Gauche side or not.
168 *
169 * If the thread is created from Gauche side (i.e. by Scm_MakeThread()
170 * C API or make-thread Scheme API), attaching is handled automatically
171 * by Gauche.
172 *
173 * If the thread is created by other means, the VM should be attached
174 * to the thread by Scm_AttachVM() API. The VMs attached by this are
175 * somewhat different than the ones attached by Gauche; such VM can't
176 * be passed to thread-join, for example. This type of VM is for
177 * the applications that want to evaluate Gauche program in their own
178 * thread.
179 * NOTE: the thread should still be created by Boehm-GC's pthread_create,
180 * for it is the only way for GC to see the thread's stack.
181 */
182
Scm_NewVM(ScmVM * proto,ScmObj name)183 ScmVM *Scm_NewVM(ScmVM *proto, ScmObj name)
184 {
185 ScmVM *v = SCM_NEW(ScmVM);
186
187 SCM_SET_CLASS(v, SCM_CLASS_VM);
188 v->state = SCM_VM_NEW;
189 (void)SCM_INTERNAL_MUTEX_INIT(v->vmlock);
190 (void)SCM_INTERNAL_COND_INIT(v->cond);
191 v->canceller = NULL;
192 v->inspector = NULL;
193 v->name = name;
194 v->specific = SCM_FALSE;
195 v->thunk = NULL;
196 v->result = SCM_UNDEFINED;
197 v->resultException = SCM_UNDEFINED;
198 v->module = proto ? proto->module : Scm_SchemeModule();
199 v->cstack = NULL;
200
201 v->curin = proto? proto->curin : SCM_PORT(Scm_Stdin());
202 v->curout = proto? proto->curout : SCM_PORT(Scm_Stdout());
203 v->curerr = proto? proto->curerr : SCM_PORT(Scm_Stderr());
204
205 v->parameters = Scm__MakeVMParameterTable(proto);
206
207 v->compilerFlags = proto? proto->compilerFlags : 0;
208 v->runtimeFlags = proto? proto->runtimeFlags : 0;
209 v->attentionRequest = 0;
210 v->signalPending = 0;
211 v->finalizerPending = 0;
212 v->stopRequest = 0;
213
214 #ifdef USE_CUSTOM_STACK_MARKER
215 v->stack = (ScmObj*)GC_generic_malloc((SCM_VM_STACK_SIZE+1)*sizeof(ScmObj),
216 vm_stack_kind);
217 *v->stack++ = SCM_OBJ(v);
218 #else /*!USE_CUSTOM_STACK_MARKER*/
219 v->stack = SCM_NEW_ARRAY(ScmObj, SCM_VM_STACK_SIZE);
220 #endif /*!USE_CUSTOM_STACK_MARKER*/
221 v->sp = v->stack;
222 v->stackBase = v->stack;
223 v->stackEnd = v->stack + SCM_VM_STACK_SIZE;
224 #if GAUCHE_FFX
225 v->fpstack = SCM_NEW_ATOMIC_ARRAY(ScmFlonum, SCM_VM_STACK_SIZE);
226 v->fpstackEnd = v->fpstack + SCM_VM_STACK_SIZE;
227 v->fpsp = v->fpstack;
228 #endif /* GAUCHE_FFX */
229
230 v->env = NULL;
231 v->argp = v->stack;
232 v->cont = NULL;
233 v->pc = PC_TO_RETURN;
234 v->base = NULL;
235 v->val0 = SCM_UNDEFINED;
236 for (int i=0; i<SCM_VM_MAX_VALUES; i++) v->vals[i] = SCM_UNDEFINED;
237 v->numVals = 1;
238
239 v->handlers = SCM_NIL;
240
241 v->exceptionHandler = DEFAULT_EXCEPTION_HANDLER;
242 v->escapePoint = v->escapePointFloating = NULL;
243 v->escapeReason = SCM_VM_ESCAPE_NONE;
244 v->escapeData[0] = NULL;
245 v->escapeData[1] = NULL;
246 v->customErrorReporter = (proto? proto->customErrorReporter : SCM_FALSE);
247 #if GAUCHE_SPLIT_STACK
248 v->lastErrorCont = NULL;
249 #endif /*GAUCHE_SPLIT_STACK*/
250
251 v->evalSituation = SCM_VM_EXECUTING;
252
253 sigemptyset(&v->sigMask);
254 Scm_SignalQueueInit(&v->sigq);
255
256 /* stats */
257 v->stat.sovCount = 0;
258 v->stat.sovTime = 0;
259 v->stat.loadStat = SCM_NIL;
260 v->profilerRunning = FALSE;
261 v->prof = NULL;
262
263 (void)SCM_INTERNAL_THREAD_INIT(v->thread);
264
265 #if defined(GAUCHE_USE_WTHREADS)
266 v->winCleanup = NULL;
267 #endif /*defined(GAUCHE_USE_WTHREADS)*/
268
269 (void)SCM_INTERNAL_MUTEX_LOCK(vm_id_mutex);
270 v->vmid = vm_numeric_id++;
271 (void)SCM_INTERNAL_MUTEX_UNLOCK(vm_id_mutex);
272
273 v->callTrace = (vm_call_trace_size
274 ? Scm__MakeCallTraceQueue(vm_call_trace_size)
275 : NULL);
276
277 v->resetChain = SCM_NIL;
278
279 Scm_RegisterFinalizer(SCM_OBJ(v), vm_finalize, NULL);
280 return v;
281 }
282
283 /* Attach the thread to the current thread.
284 See the notes of Scm_NewVM above.
285 Returns TRUE on success, FALSE on failure. */
Scm_AttachVM(ScmVM * vm)286 int Scm_AttachVM(ScmVM *vm)
287 {
288 #ifdef GAUCHE_HAS_THREADS
289 if (theVM != NULL) {
290 /* The current thread already has another VM attached. */
291 return FALSE;
292 }
293 /* NB: We want to check if this VM has already attached to another
294 thread or not. */
295
296 if (!SCM_INTERNAL_THREAD_SETSPECIFIC(Scm_VMKey(), vm)) return FALSE;
297
298 if (!SCM_INTERNAL_THREAD_INITIALIZED_P(vm->thread)) {
299 #ifdef GAUCHE_USE_WTHREADS
300 /* GetCurrentThread() on Windows returns a pseudo handle
301 indicating 'myself', which can't be usable from other thread.
302 We need a special care. The resulting HANDLE should be closed,
303 which is done in the finalizer. */
304 HANDLE t;
305 BOOL r = DuplicateHandle(GetCurrentProcess(), /* source process */
306 GetCurrentThread(), /* source handle */
307 GetCurrentProcess(), /* target process */
308 &t,
309 0, FALSE, DUPLICATE_SAME_ACCESS);
310 if (!r) {
311 Scm_SysError("Getting current thread handle failed");
312 }
313 vm->thread = t;
314 #else /* GAUCHE_USE_PTHREADS */
315 vm->thread = SCM_INTERNAL_THREAD_GETCURRENT();
316 #endif /* GAUCHE_USE_PTHREADS */
317 }
318 vm->state = SCM_VM_RUNNABLE;
319 vm_register(vm);
320 return TRUE;
321 #else /* no threads */
322 return FALSE;
323 #endif /* no threads */
324 }
325
326 /* If the current VM is attached by Scm_AttachVM() rather than Scheme
327 creating a thread, this needs to be called once you've done with the
328 VM (typically just before the thread terminates).
329 */
Scm_DetachVM(ScmVM * vm)330 void Scm_DetachVM(ScmVM *vm)
331 {
332 #ifdef GAUCHE_HAS_THREADS
333 if (vm != NULL) {
334 (void)SCM_INTERNAL_THREAD_SETSPECIFIC(Scm_VMKey(), NULL);
335 vm_unregister(vm);
336 }
337 #endif /* GAUCHE_HAS_THREADS */
338 }
339
Scm_VMGetNumResults(ScmVM * vm)340 int Scm_VMGetNumResults(ScmVM *vm)
341 {
342 return vm->numVals;
343 }
344
Scm_VMGetResult(ScmVM * vm)345 ScmObj Scm_VMGetResult(ScmVM *vm)
346 {
347 ScmObj head = SCM_NIL, tail = SCM_NIL;
348 if (vm->numVals == 0) return SCM_NIL;
349 SCM_APPEND1(head, tail, vm->val0);
350 for (int i=1; i<vm->numVals; i++) {
351 SCM_APPEND1(head, tail, vm->vals[i-1]);
352 }
353 return head;
354 }
355
Scm_VMSetResult(ScmObj obj)356 void Scm_VMSetResult(ScmObj obj)
357 {
358 ScmVM *vm = theVM;
359 vm->val0 = obj;
360 vm->numVals = 1;
361 }
362
363 /*
364 * Current VM.
365 */
Scm_VM(void)366 ScmVM *Scm_VM(void)
367 {
368 return theVM;
369 }
370
371 /* Some macros inserts Scm_VM() in its output. If such macros are expanded
372 below, we can safely replace Scm_VM() to theVM. */
373 #define Scm_VM() theVM
374
375 /*
376 * Get VM key
377 */
378 #if defined(GAUCHE_USE_PTHREADS)
Scm_VMKey(void)379 pthread_key_t Scm_VMKey(void)
380 {
381 return vm_key;
382 }
383 #elif defined(GAUCHE_USE_WTHREADS)
Scm_VMKey(void)384 DWORD Scm_VMKey(void)
385 {
386 return vm_key;
387 }
388 #endif /*GAUCHE_USE_WTHREADS*/
389
390 /* Warn if VM is terminated by uncaught exception, and GC-ed without
391 joining. It is cleary an unexpected case and worth reporting. */
vm_finalize(ScmObj obj,void * data SCM_UNUSED)392 static void vm_finalize(ScmObj obj, void *data SCM_UNUSED)
393 {
394 ScmVM *vm = SCM_VM(obj);
395 ScmObj re = vm->resultException;
396
397 if (SCM_UNCAUGHT_EXCEPTION_P(re)) {
398 Scm_Warn("A thread %S (%lu) died a lonely death with uncaught exception %S.",
399 vm->name, vm->vmid, SCM_THREAD_EXCEPTION(re)->data);
400 }
401 #ifdef GAUCHE_USE_WTHREADS
402 if (vm->thread != INVALID_HANDLE_VALUE) {
403 CloseHandle(vm->thread);
404 vm->thread = INVALID_HANDLE_VALUE;
405 }
406 #endif /*GAUCHE_USE_WTHREADS*/
407 }
408
409 /* Thread specific storage may not be scanned by GC. We keep pointer
410 to the live VM in the global hashtable. */
vm_register(ScmVM * vm)411 static void vm_register(ScmVM *vm)
412 {
413 SCM_INTERNAL_MUTEX_LOCK(vm_table_mutex);
414 ScmDictEntry *e = Scm_HashCoreSearch(&vm_table, (intptr_t)vm,
415 SCM_DICT_CREATE);
416 (void)SCM_DICT_SET_VALUE(e, SCM_TRUE);
417 SCM_INTERNAL_MUTEX_UNLOCK(vm_table_mutex);
418 }
419
vm_unregister(ScmVM * vm)420 static void vm_unregister(ScmVM *vm)
421 {
422 SCM_INTERNAL_MUTEX_LOCK(vm_table_mutex);
423 (void)Scm_HashCoreSearch(&vm_table, (intptr_t)vm, SCM_DICT_DELETE);
424 SCM_INTERNAL_MUTEX_UNLOCK(vm_table_mutex);
425 }
426
427 /*====================================================================
428 * VM interpreter
429 *
430 * Interprets intermediate code CODE on VM.
431 */
432
433 /*
434 * Micro-operations
435 */
436
437 /* fetching */
438 #define INCR_PC (PC++)
439 #define FETCH_LOCATION(var) ((var) = (ScmWord*)*PC)
440 #define FETCH_OPERAND(var) ((var) = SCM_OBJ(*PC))
441 #define FETCH_OPERAND_PUSH (*SP++ = SCM_OBJ(*PC))
442
443 #ifndef COUNT_INSN_FREQUENCY
444 #define FETCH_INSN(var) ((var) = *PC++)
445 #else
446 #define FETCH_INSN(var) ((var) = fetch_insn_counting(vm, var))
447 #endif
448
449 /* For sanity check in debugging mode */
450 #ifdef PARANOIA
451 #define CHECK_STACK_PARANOIA(n) CHECK_STACK(n)
452 #else
453 #define CHECK_STACK_PARANOIA(n) (void)(n) /*dummy - avoid unused var warning*/
454 #endif
455
456 /* Hint for gcc -- at this moment, using __builtin_expect doesn't
457 do any good (except for SCM_PROF_COUNT_CALL). I'll try this
458 later on. */
459 #if 1
460 #define MOSTLY_FALSE(expr) __builtin_expect(expr, 0)
461 #else
462 #define MOSTLY_FALSE(expr) expr
463 #endif
464
465 /* Find the stack bottom next to the continuation frame.
466 This macro should be applied only if CONT is in stack. */
467 #define CONT_FRAME_END(cont) ((ScmObj*)(cont) + CONT_FRAME_SIZE)
468
469 /* check if *pc is an return instruction. if so, some
470 shortcuts are taken. */
471 #define TAIL_POS() (*PC == SCM_VM_INSN(SCM_VM_RET))
472
473 /* push OBJ to the top of the stack
474 The evaluation of OBJ may require to refer SP, so we can't
475 just say *SP++ = (obj). */
476 #define PUSH_ARG(obj) (*SP = (obj), SP++)
477
478 /* pop the top object of the stack and store it to VAR */
479 #define POP_ARG(var) ((var) = *--SP)
480
481 #define SHIFT_FRAME(from, to, size) \
482 do { \
483 ScmObj *f = (from), *t = (to); \
484 int c; \
485 for (c=0; c<(size); c++, f++, t++) *t = *f; \
486 } while (0)
487
488 /* VM registers. We've benchmarked if keeping some of those registers
489 local variables makes VM loop run faster; however, it turned out
490 that more local variables tended to make them spill from machine
491 registers and didn't improve performance. Having only vm, a pointer
492 to the current VM, on register is enough. */
493 #define PC (vm->pc)
494 #define SP (vm->sp)
495 #define VAL0 (vm->val0)
496 #define ENV (vm->env)
497 #define CONT (vm->cont)
498 #define ARGP (vm->argp)
499 #define BASE (vm->base)
500
501 /* IN_STACK_P(ptr) returns true if ptr points into the active stack area.
502 IN_FULL_STACK_P(ptr) returns true if ptr points into any part of the stack.
503 */
504
505 #if GAUCHE_SPLIT_STACK
506 #define IN_STACK_P(ptr) \
507 ((ptr) >= vm->stackBase && (ptr) < vm->stackEnd)
508 #define IN_FULL_STACK_P(ptr) \
509 ((ptr) >= vm->stack && (ptr) < vm->stackEnd)
510 #else /*!GAUCHE_SPLIT_STACK*/
511 #define IN_STACK_P(ptr) \
512 ((unsigned long)((ptr) - vm->stack) < SCM_VM_STACK_SIZE)
513 #define IN_FULL_STACK_P(ptr) IN_STACK_P(ptr)
514 #endif /*!GAUCHE_SPLIT_STACK*/
515
516 /* Check if stack has room at least size bytes. */
517 #define CHECK_STACK(size) \
518 do { \
519 if (MOSTLY_FALSE(SP >= vm->stackEnd - (size))) { \
520 save_stack(vm); \
521 } \
522 } while (0)
523
524 /* Push a continuation frame. next_pc is the PC from where execution
525 will be resumed. */
526 #define PUSH_CONT(next_pc) \
527 do { \
528 ScmContFrame *newcont = (ScmContFrame*)SP; \
529 newcont->prev = CONT; \
530 newcont->env = ENV; \
531 newcont->size = (int)(SP - ARGP); \
532 newcont->marker = 0; \
533 newcont->cpc = PC; \
534 newcont->pc = next_pc; \
535 newcont->base = BASE; \
536 CONT = newcont; \
537 SP += CONT_FRAME_SIZE; \
538 ARGP = SP; \
539 } while (0)
540
541 /* pop a continuation frame, i.e. return from a procedure. */
542 #define POP_CONT() \
543 do { \
544 if (C_CONTINUATION_P(CONT)) { \
545 void *data__[SCM_CCONT_DATA_SIZE]; \
546 ScmObj v__ = VAL0; \
547 ScmCContinuationProc *after__; \
548 void **d__ = data__; \
549 void **s__ = (void**)CONT - CONT->size; \
550 while (s__ < (void**)CONT) { \
551 *d__++ = *s__++; \
552 } \
553 after__ = (ScmCContinuationProc*)CONT->pc; \
554 if (IN_STACK_P((ScmObj*)CONT)) { \
555 SP = (ScmObj*)CONT - CONT->size; \
556 } \
557 ENV = NULL; \
558 ARGP = SP; \
559 PC = PC_TO_RETURN; \
560 BASE = CONT->base; \
561 CONT = CONT->prev; \
562 SCM_FLONUM_ENSURE_MEM(v__); \
563 VAL0 = after__(v__, data__); \
564 } else if (IN_STACK_P((ScmObj*)CONT)) { \
565 SP = (ScmObj*)CONT; \
566 ENV = CONT->env; \
567 ARGP = SP - CONT->size; \
568 PC = CONT->pc; \
569 BASE = CONT->base; \
570 CONT = CONT->prev; \
571 } else { \
572 int size__ = CONT->size; \
573 ARGP = SP = vm->stackBase; \
574 ENV = CONT->env; \
575 PC = CONT->pc; \
576 BASE = CONT->base; \
577 if (size__) { \
578 ScmObj *s__ = (ScmObj*)CONT - size__; \
579 ScmObj *d__ = SP; \
580 while (s__ < (ScmObj*)CONT) { \
581 *d__++ = *s__++; \
582 } \
583 SP = d__; \
584 } \
585 CONT = CONT->prev; \
586 } \
587 } while (0)
588
589 /* return operation. */
590 #define RETURN_OP() \
591 do { \
592 if (CONT == NULL || BOUNDARY_FRAME_P(CONT)) { \
593 return; /* no more continuations */ \
594 } else if (MARKER_FRAME_P(CONT)) { \
595 POP_CONT(); \
596 /* the end of partial continuation */ \
597 vm->cont = NULL; \
598 } else { \
599 POP_CONT(); \
600 } \
601 } while (0)
602
603 /* push environment header to finish the environment frame.
604 env, sp, argp is updated. */
605 #define FINISH_ENV(info_, up_) \
606 do { \
607 ScmEnvFrame *e__ = (ScmEnvFrame*)SP; \
608 e__->up = up_; \
609 e__->info = info_; \
610 e__->size = SP - ARGP; \
611 SP += ENV_HDR_SIZE; \
612 ARGP = SP; \
613 ENV = e__; \
614 } while (0)
615
616 /* extend the current environment by SIZE words. used for LET. */
617 #define PUSH_LOCAL_ENV(size_, info_) \
618 do { \
619 int i__; \
620 for (i__=0; i__<size_; i__++) { \
621 *SP++ = SCM_UNDEFINED; \
622 } \
623 FINISH_ENV(info_, ENV); \
624 } while (0)
625
626 /* used for the inlined instruction which is supposed to be called at
627 tail position (e.g. SLOT-REF). This checks whether we're at the tail
628 position or not, and if not, push a cont frame to make the operation
629 a tail call. */
630 #define TAIL_CALL_INSTRUCTION() \
631 do { \
632 if (!TAIL_POS()) { \
633 CHECK_STACK(CONT_FRAME_SIZE); \
634 PUSH_CONT(PC); \
635 PC = PC_TO_RETURN; \
636 } \
637 } while (0)
638
639 /* global reference. this piece of code is used for a few GREF-something
640 combined instruction. */
641 #define GLOBAL_REF(v) \
642 do { \
643 ScmGloc *gloc; \
644 FETCH_OPERAND(v); \
645 if (!SCM_GLOCP(v)) { \
646 VM_ASSERT(SCM_IDENTIFIERP(v)); \
647 gloc = Scm_IdentifierGlobalBinding(SCM_IDENTIFIER(v)); \
648 if (gloc == NULL) { \
649 VM_ERR(("unbound variable: %S", \
650 SCM_IDENTIFIER(v)->name)); \
651 } \
652 /* memorize gloc */ \
653 *PC = SCM_WORD(gloc); \
654 } else { \
655 gloc = SCM_GLOC(v); \
656 } \
657 v = SCM_GLOC_GET(gloc); \
658 if (SCM_AUTOLOADP(v)) { \
659 v = Scm_ResolveAutoload(SCM_AUTOLOAD(v), 0); \
660 } \
661 if (SCM_UNBOUNDP(v)) { \
662 VM_ERR(("unbound variable: %S", SCM_OBJ(gloc->name))); \
663 } \
664 if (SCM_UNINITIALIZEDP(v)) { \
665 VM_ERR(("uninitialized variable: %S", SCM_OBJ(gloc->name)));\
666 } \
667 INCR_PC; \
668 } while (0)
669
670 /* for debug */
671 #define VM_DUMP(delimiter) \
672 fprintf(stderr, delimiter); \
673 Scm_VMDump(vm)
674
675 #define VM_ASSERT(expr) \
676 do { \
677 if (!(expr)) { \
678 fprintf(stderr, "\"%s\", line %d: Assertion failed: %s\n", \
679 __FILE__, __LINE__, #expr); \
680 Scm_VMDump(theVM); \
681 Scm_Panic("exiting...\n"); \
682 } \
683 } while (0)
684
685 #define VM_ERR(errargs) \
686 do { \
687 Scm_Error errargs; \
688 } while (0)
689
690 /* Discard the current procedure's local frame before performing a tail call.
691 Just before the tail call, the typical stack position is like this:
692
693 SP >| |
694 | argN |
695 | : |
696 ARGP>| arg0 |
697 | env |
698 ENV >| env |
699 |localM|
700 | : |
701 |local0|
702 CONT>| cont |
703
704 Arg0...argN is the arguments for the call, and local0...localM is the
705 environment of the current procedure, which is no longer needed.
706 We shift arg0...argN to just above the continuation frame.
707
708 If the continuation frame has been saved (i.e. CONT is not pointing
709 the stack area), then we know for sure that there's no valid data
710 from the stack bottom to ARGP. So we shift arg0...argN to the
711 beginning of the stack. We set ENV = NULL afterwards to prevent
712 the debugging process from being confused---at the end of the procedure
713 calling sequence, ENV is set to point to the newly formed environment
714 frame out of arg0...argN.
715
716 MEMO: this shifting used to be done after folding &rest arguments.
717 Benchmark showed shifting first is slightly faster.
718 */
719 #define DISCARD_ENV() \
720 do { \
721 ScmObj *to; \
722 int argc = (int)(SP - ARGP); \
723 if (IN_STACK_P((ScmObj*)CONT)) { \
724 to = CONT_FRAME_END(CONT); \
725 } else { \
726 to = vm->stackBase; \
727 } \
728 if (argc) { \
729 ScmObj *t = to, *a = ARGP; \
730 int c; \
731 for (c=0; c<argc; c++) *t++ = *a++; \
732 } \
733 ARGP = to; \
734 SP = to + argc; \
735 ENV = NULL; \
736 } while (0)
737
738
739 /* inline expansion of number comparison. */
740 #define NUM_CMP(op, r) \
741 do { \
742 ScmObj x_, y_ = VAL0; \
743 POP_ARG(x_); \
744 if (SCM_INTP(y_) && SCM_INTP(x_)) { \
745 r = ((signed long)(intptr_t)x_ op (signed long)(intptr_t)y_); \
746 } else if (SCM_FLONUMP(y_) && SCM_FLONUMP(x_)) { \
747 r = (SCM_FLONUM_VALUE(x_) op SCM_FLONUM_VALUE(y_)); \
748 } else { \
749 r = (Scm_NumCmp(x_, y_) op 0); \
750 } \
751 } while (0)
752
753 #define NUM_CCMP(op, r) \
754 do { \
755 ScmObj x_, y_ = VAL0; \
756 FETCH_OPERAND(x_); \
757 r = (SCM_FLONUM_VALUE(x_) op Scm_GetDouble(y_)); \
758 } while (0)
759
760 /* We take advantage of GCC's `computed goto' feature
761 (see gcc.info, "Labels as Values").
762 'NEXT' or 'NEXT_PUSHCHECK' is placed at the end of most
763 vm insn handlers to dispatch to the next instruction.
764 We don't simply jump to the beginning of the dispatch
765 table (hence the dispatching is handled by SWITCH macro),
766 since the former performs worse in branch prediction;
767 at least, if we have dispatches at the end of every handler,
768 we can hope the branch predictor detect frequently used
769 vm insn sequence. 'NEXT_PUSHCHECK' further reduces branch
770 predictor failure - quite a few insns that yield a value
771 in VAL0 is followed by PUSH instruction, so we detect it
772 specially. We still use fused insns (e.g. LREF0-PUSH) when
773 the combination is very frequent - but for the less frequent
774 instructions, NEXT_PUSHCHECK proved effective without introducing
775 new fused vm insns.
776 */
777 #ifdef __GNUC__
778 #define SWITCH(val) goto *dispatch_table[val];
779 #define CASE(insn) SCM_CPP_CAT(LABEL_, insn) :
780 #define DEFAULT LABEL_DEFAULT :
781 #define DISPATCH /*empty*/
782 #define NEXT \
783 do { \
784 FETCH_INSN(code); \
785 goto *dispatch_table[SCM_VM_INSN_CODE(code)]; \
786 } while (0)
787 #define NEXT_PUSHCHECK \
788 do { \
789 FETCH_INSN(code); \
790 if (code == SCM_VM_PUSH) { \
791 PUSH_ARG(VAL0); \
792 FETCH_INSN(code); \
793 } \
794 goto *dispatch_table[SCM_VM_INSN_CODE(code)]; \
795 } while (0)
796 #else /* !__GNUC__ */
797 #define SWITCH(val) switch (val)
798 #define CASE(insn) case insn :
799 #define DISPATCH dispatch:
800 #define NEXT goto dispatch
801 #define NEXT_PUSHCHECK goto dispatch
802 #endif
803
804 /* Check VM interrupt request. */
805 #define CHECK_INTR \
806 do { if (vm->attentionRequest) goto process_queue; } while (0)
807
808 /* WNA - "Wrong Number of Arguments" handler. The actual call is in vmcall.c.
809 We handle the autocurrying magic here.
810
811 PROC is the procedure object (guaranteed).
812 NGIVEN is # of actual args on the VM stack. The last several args may
813 be folded in a list in APPLY_CALL context. FOLDLEN holds the number of
814 folded args. In normal call context, FOLDLEN is -1.
815
816 If the proc is curried, the VM stack state is ready to execute next op.
817 Otherwise thie procedure won't return.
818 */
819
wna(ScmVM * vm SCM_UNUSED,ScmObj proc,int ngiven,int foldlen SCM_UNUSED)820 static void wna(ScmVM *vm SCM_UNUSED,
821 ScmObj proc,
822 int ngiven,
823 int foldlen SCM_UNUSED)
824 {
825 int reqargs = SCM_PROCEDURE_REQUIRED(proc);
826 #if 0
827 /* Disabled for now. See proc.c (Scm_CurryProcedure) for the details. */
828 if (SCM_PROCEDURE_CURRYING(proc) && ngiven < reqargs && ngiven > 0) {
829 VAL0 = Scm_CurryProcedure(proc, ARGP, ngiven, foldlen);
830 /*TODO: how should we count this path for profiling? */
831 } else {
832 Scm_Error("wrong number of arguments for %S (required %d, got %d)",
833
834 proc, reqargs, ngiven);
835 /*NOTREACHED*/
836 }
837 #else
838 Scm_Error("wrong number of arguments for %S (required %d, got %d)",
839 proc, reqargs, ngiven);
840 #endif
841 }
842
843 /* local_env_shift
844 Called from LOCAL-ENV-SHIFT and LOCAL-ENV-JUMP insns (see vminsn.scm),
845 and adjusts env frames for optimized local function call.
846 This routine does two things
847 - Creates a new local env frame from the values in the current stack.
848 The size of frame can be determined by SP-ARGP.
849 - Discard DEPTH env frames.
850 */
local_env_shift(ScmVM * vm,int env_depth)851 static void local_env_shift(ScmVM *vm, int env_depth)
852 {
853 int nargs = (int)(SP - ARGP);
854 ScmEnvFrame *tenv = ENV;
855 /* We can discard env_depth environment frames.
856 There are several cases:
857 - if the target env frame (TENV) is in stack:
858 -- if the current cont frame is over TENV
859 => shift argframe on top of the current cont frame
860 -- otherwise => shift argframe on top of TENV
861 - if TENV is in heap:
862 -- if the current cont frame is in stack
863 => shift argframe on top of the current cont frame
864 -- otherwise => shift argframe at the stack base
865 */
866 while (env_depth-- > 0) {
867 SCM_ASSERT(tenv);
868 tenv = tenv->up;
869 }
870
871 ScmObj *to;
872 if (IN_STACK_P((ScmObj*)tenv)) {
873 if (IN_STACK_P((ScmObj*)CONT) && (((ScmObj*)CONT) > ((ScmObj*)tenv))) {
874 to = CONT_FRAME_END(CONT);
875 } else {
876 to = ((ScmObj*)tenv) + ENV_HDR_SIZE;
877 }
878 } else {
879 if (IN_STACK_P((ScmObj*)CONT)) {
880 to = CONT_FRAME_END(CONT);
881 } else {
882 to = vm->stackBase; /* continuation has already been saved */
883 }
884 }
885 if (nargs > 0 && to != ARGP) {
886 ScmObj *t = to;
887 ScmObj *a = ARGP;
888 for (int c = 0; c < nargs; c++) {
889 *t++ = *a++;
890 }
891 }
892 ARGP = to;
893 SP = to + nargs;
894 if (nargs > 0) { FINISH_ENV(SCM_FALSE, tenv); }
895 else { ENV = tenv; }
896 }
897
898
899 /*===================================================================
900 * Main loop of VM
901 */
run_loop()902 static void run_loop()
903 {
904 ScmVM *vm = theVM;
905 ScmWord code = 0;
906
907 #ifdef __GNUC__
908 static void *dispatch_table[256] = {
909 #define DEFINSN(insn, name, nargs, type, flags) && SCM_CPP_CAT(LABEL_, insn),
910 #include "vminsn.c"
911 #undef DEFINSN
912 };
913 #endif /* __GNUC__ */
914
915 /* Records the offset of each instruction handler from run_loop entry
916 address. They can be retrieved by gauche.internal#%vm-get-insn-offsets.
917 Useful for tuning if used with machine instruction-level profiler. */
918 if (vminsn_offsets[0] == 0) {
919 /* No need to lock, for this is only executed when run_loop runs for
920 the first time, which is in Scm_Init(). */
921 for (int i=0; i<SCM_VM_NUM_INSNS; i++) {
922 vminsn_offsets[i] =
923 (unsigned long)((char*)dispatch_table[i] - (char*)run_loop);
924 }
925 }
926
927 for (;;) {
928 DISPATCH;
929 /*VM_DUMP("");*/
930 if (vm->attentionRequest) goto process_queue;
931 FETCH_INSN(code);
932 SWITCH(SCM_VM_INSN_CODE(code)) {
933 #define VMLOOP
934 #include "vminsn.c"
935 #undef VMLOOP
936 #ifndef __GNUC__
937 default:
938 Scm_Panic("Illegal vm instruction: %08x",
939 SCM_VM_INSN_CODE(code));
940 #endif
941 }
942 process_queue:
943 CHECK_STACK(CONT_FRAME_SIZE);
944 PUSH_CONT(PC);
945 process_queued_requests(vm);
946 POP_CONT();
947 NEXT;
948 }
949 }
950 /* End of run_loop */
951
952 /*==================================================================
953 * Stack management
954 */
955
956 /* We have 'fowarding pointer' for env and cont frames being moved.
957 Forwarding pointers are resolved within these internal routines
958 and should never leak out.
959
960 Forwarded pointer is marked by the 'size' field be set -1.
961 Env->up or Cont->prev field holds the relocated frame.
962
963 Invariance: forwarded pointer only appear in stack. We skip some
964 IN_STACK_P check because of it. */
965
966 #define FORWARDED_ENV_P(e) ((e)&&((e)->size == -1))
967 #define FORWARDED_ENV(e) ((e)->up)
968
969 #define FORWARDED_CONT_P(c) ((c)&&((c)->size == -1))
970 #define FORWARDED_CONT(c) ((c)->prev)
971
972 /* Performance note: As of 0.8.4_pre1, each get_env call spends about
973 1us to 4us on P4 2GHz machine with several benchmark suites. The
974 average env frames to be saved is less than 3. The ratio of the pass1
975 (env frame save) and the pass 2 (cont pointer adjustment) is somewhere
976 around 2:1 to 1:2. Inlining SCM_NEW call didn't help.
977
978 This is a considerable amount of time, since save_env may be called
979 the order of 10^6 times. I'm not sure I can optimize this routine
980 further without a radical change in stack management code.
981
982 Better strategy is to put an effort in the compiler to avoid closure
983 creation as much as possible. */
984
985 /* Move the chain of env frames from the stack to the heap,
986 replacing the in-stack frames for forwarding env frames.
987
988 This routine just moves the env frames, but leaves pointers that
989 point to moved frames intact (such pointers are found only in
990 the in-stack contniuation frames, chained from vm->cont).
991 It's the caller's responsibility to update those pointers.
992
993 The env frames below the stackBase is also moved, to keep the invariance
994 that heap would never contain a pointer into the stack.
995 */
save_env(ScmVM * vm,ScmEnvFrame * env_begin)996 static inline ScmEnvFrame *save_env(ScmVM *vm, ScmEnvFrame *env_begin)
997 {
998 ScmEnvFrame *e = env_begin, *prev = NULL, *next, *head = NULL, *saved;
999
1000 if (!IN_FULL_STACK_P((ScmObj*)e)) return e;
1001
1002 do {
1003 long esize = (long)e->size;
1004 if (esize < 0) {
1005 /* forwaded frame */
1006 if (prev) prev->up = FORWARDED_ENV(e);
1007 return head;
1008 }
1009
1010 ScmObj *d = SCM_NEW2(ScmObj*, ENV_SIZE(esize) * sizeof(ScmObj));
1011 ScmObj *s = (ScmObj*)e - esize;
1012 for (long i=esize; i>0; i--) {
1013 SCM_FLONUM_ENSURE_MEM(*s);
1014 *d++ = *s++;
1015 }
1016 *(ScmEnvFrame*)d = *e; /* copy env header */
1017 saved = (ScmEnvFrame*)d;
1018 if (prev) prev->up = saved;
1019 if (head == NULL) head = saved;
1020 next = e->up;
1021 e->up = prev = saved; /* forwarding pointer */
1022 e->size = -1; /* indicates forwarded */
1023 e->info = SCM_FALSE;
1024 e = next;
1025 } while (IN_FULL_STACK_P((ScmObj*)e));
1026 return head;
1027 }
1028
save_cont_1(ScmVM * vm,ScmContFrame * c)1029 static void save_cont_1(ScmVM *vm, ScmContFrame *c)
1030 {
1031 if (!IN_FULL_STACK_P((ScmObj*)c)) return;
1032
1033 ScmContFrame *prev = NULL;
1034
1035 /* First pass */
1036 do {
1037 int size = (CONT_FRAME_SIZE + c->size) * sizeof(ScmObj);
1038 ScmObj *heap = SCM_NEW2(ScmObj*, size);
1039 ScmContFrame *csave = (ScmContFrame*)(heap + c->size);
1040
1041 /* update env ptr if necessary */
1042 if (FORWARDED_ENV_P(c->env)) {
1043 c->env = FORWARDED_ENV(c->env);
1044 } else if (IN_FULL_STACK_P((ScmObj*)c->env)) {
1045 c->env = save_env(vm, c->env);
1046 }
1047
1048 /* copy cont frame */
1049 if (!C_CONTINUATION_P(c)) {
1050 ScmObj *s = (ScmObj*)c - c->size;
1051 ScmObj *d = heap;
1052 if (c->size) {
1053 for (int i=c->size; i>0; i--) {
1054 SCM_FLONUM_ENSURE_MEM(*s);
1055 *d++ = *s++;
1056 }
1057 }
1058 *(ScmContFrame*)d = *c; /* copy the frame */
1059 } else {
1060 /* C continuation */
1061 ScmObj *s = (ScmObj*)c - c->size;
1062 ScmObj *d = heap;
1063 for (int i=CONT_FRAME_SIZE + c->size; i>0; i--) {
1064 /* NB: C continuation frame contains opaque pointer,
1065 so we shouldn't ENSURE_MEM. */
1066 *d++ = *s++;
1067 }
1068 }
1069
1070 /* make the orig frame forwarded */
1071 if (prev) prev->prev = csave;
1072 prev = csave;
1073
1074 ScmContFrame *tmp = c->prev;
1075 c->prev = csave;
1076 c->size = -1;
1077 c = tmp;
1078 } while (IN_FULL_STACK_P((ScmObj*)c));
1079 }
1080
1081
1082 /* Copy the continuation frames to the heap.
1083 We run two passes, first replacing cont frames with the forwarding
1084 cont frames, then updates the pointers to them.
1085 After save_cont, the only thing possibly left in the stack is the argument
1086 frame pointed by vm->argp.
1087 */
save_cont(ScmVM * vm)1088 static void save_cont(ScmVM *vm)
1089 {
1090 /* Save the environment chain first. */
1091 vm->env = save_env(vm, vm->env);
1092
1093 /* First pass */
1094 save_cont_1(vm, vm->cont);
1095 #if GAUCHE_SPLIT_STACK
1096 save_cont_1(vm, vm->lastErrorCont);
1097 #endif /*GAUCHE_SPLIT_STACK*/
1098
1099 /* Second pass */
1100 if (FORWARDED_CONT_P(vm->cont)) {
1101 vm->cont = FORWARDED_CONT(vm->cont);
1102 }
1103 #if GAUCHE_SPLIT_STACK
1104 if (FORWARDED_CONT_P(vm->lastErrorCont)) {
1105 vm->lastErrorCont = FORWARDED_CONT(vm->lastErrorCont);
1106 }
1107 #endif /*GAUCHE_SPLIT_STACK*/
1108 for (ScmCStack *cstk = vm->cstack; cstk; cstk = cstk->prev) {
1109 if (FORWARDED_CONT_P(cstk->cont)) {
1110 cstk->cont = FORWARDED_CONT(cstk->cont);
1111 }
1112 }
1113 for (ScmEscapePoint *ep = vm->escapePoint; ep; ep = ep->prev) {
1114 if (FORWARDED_CONT_P(ep->cont)) {
1115 ep->cont = FORWARDED_CONT(ep->cont);
1116 }
1117 }
1118 for (ScmEscapePoint *ep = SCM_VM_FLOATING_EP(vm); ep; ep = ep->floating) {
1119 if (FORWARDED_CONT_P(ep->cont)) {
1120 ep->cont = FORWARDED_CONT(ep->cont);
1121 }
1122 }
1123 vm->stackBase = vm->stack;
1124 }
1125
save_stack(ScmVM * vm)1126 static void save_stack(ScmVM *vm)
1127 {
1128 #if HAVE_GETTIMEOFDAY
1129 int stats = SCM_VM_RUNTIME_FLAG_IS_SET(vm, SCM_COLLECT_VM_STATS);
1130 struct timeval t0, t1;
1131
1132 if (stats) {
1133 gettimeofday(&t0, NULL);
1134 }
1135 #endif
1136
1137 save_cont(vm);
1138 memmove(vm->stackBase, vm->argp,
1139 (vm->sp - (ScmObj*)vm->argp) * sizeof(ScmObj*));
1140 vm->sp -= (ScmObj*)vm->argp - vm->stackBase;
1141 vm->argp = vm->stackBase;
1142 /* Clear the stack. This removes bogus pointers and accelerates GC */
1143 for (ScmObj *p = vm->sp; p < vm->stackEnd; p++) *p = NULL;
1144
1145 #if HAVE_GETTIMEOFDAY
1146 if (stats) {
1147 gettimeofday(&t1, NULL);
1148 vm->stat.sovCount++;
1149 vm->stat.sovTime +=
1150 (t1.tv_sec - t0.tv_sec)*1000000+(t1.tv_usec - t0.tv_usec);
1151 }
1152 #endif
1153 }
1154
get_env(ScmVM * vm)1155 static ScmEnvFrame *get_env(ScmVM *vm)
1156 {
1157 ScmEnvFrame *e = save_env(vm, vm->env);
1158 if (e != vm->env) {
1159 vm->env = e;
1160 for (ScmContFrame *c = vm->cont; IN_FULL_STACK_P((ScmObj*)c); c = c->prev) {
1161 if (FORWARDED_ENV_P(c->env)) {
1162 c->env = FORWARDED_ENV(c->env);
1163 }
1164 }
1165 }
1166 return e;
1167 }
1168
1169 /* When VM stack has incomplete stack frame (that is, SP != ARGP or
1170 * *PC != SCM_VM_RET), and we need to run something on VM, we should
1171 * preserve this incomplete frame. Pushing an extra continuation
1172 * frame does the job. We set the PC to point to RET instruction,
1173 * so the next time the control returns to the calling VM loop,
1174 * the first thing it would do is to pop this extra continuation
1175 * frame (unless other thigs are pushed onto the VM stack by VMPushCC).
1176 * Returns TRUE if a new extra frame is pushed, FALSE if not.
1177 *
1178 * Caveat: This function changes the next instruction to be executed.
1179 * It is a problem if this is called during VM insturctions such as
1180 * CAR-PUSH, which expects the next instruction to make use of the
1181 * pushed value---if we make RET to be executed instead of the original
1182 * instruction, the pushed value will be lost. So, the caller of this
1183 * function needs to see if the frame is actually pushed, and call
1184 * Scm__VMUnprotectStack below to recover the original instruction.
1185 */
Scm__VMProtectStack(ScmVM * vm)1186 int Scm__VMProtectStack(ScmVM *vm)
1187 {
1188 if (vm->sp != vm->argp || *vm->pc != SCM_VM_INSN(SCM_VM_RET)) {
1189 CHECK_STACK(CONT_FRAME_SIZE);
1190 PUSH_CONT(PC);
1191 vm->pc = PC_TO_RETURN;
1192 return TRUE;
1193 } else {
1194 return FALSE;
1195 }
1196 }
1197
1198 /* The inverse of Scm__VMProtectStack. This is required if you called
1199 * Scm__VMProtectStack _in_the_middle_of_VM_instruction_execution_.
1200 * The VM instruction may push things after that, counting on the fact
1201 * that subsequent instructoins use the pushed item. However,
1202 * Scm__VMProtectStack makes the next instruction to be executed
1203 * to RET. This makes the thing pushed by the current instruction be
1204 * discarded immediately, before the original subsequent instructions
1205 * seeing it. Calling Scm__VMUnprotectStack restores the original next
1206 * instruction (assuming it's properly paired up with Scm__VMProtectStack).
1207 */
Scm__VMUnprotectStack(ScmVM * vm)1208 void Scm__VMUnprotectStack(ScmVM *vm)
1209 {
1210 SCM_ASSERT(vm->pc == PC_TO_RETURN);
1211 POP_CONT();
1212 }
1213
1214 #if GAUCHE_FFX
1215 /* Move all the FLONUM_REGs to heap and clear the fpstack.
1216 We cache small number of visited env frames to avoid duplicate scanning
1217 (if there are more env frames, linear search in the cache gets even
1218 more costly than duplicate scanning).
1219 */
1220
1221 #define ENV_CACHE_SIZE 32
1222
1223 #undef COUNT_FLUSH_FPSTACK
1224
1225 #ifdef COUNT_FLUSH_FPSTACK
1226 static int flush_fpstack_count = 0;
1227 static u_long flush_fpstack_time = 0;
print_flush_fpstack_count(void * z)1228 static void print_flush_fpstack_count(void*z)
1229 {
1230 fprintf(stderr, "fpstack count = %d time = %ldus (avg %fus)\n",
1231 flush_fpstack_count, flush_fpstack_time,
1232 flush_fpstack_time/(double)flush_fpstack_count);
1233 }
1234 #endif
1235
Scm_VMFlushFPStack(ScmVM * vm)1236 void Scm_VMFlushFPStack(ScmVM *vm)
1237 {
1238 ScmEnvFrame *visited[ENV_CACHE_SIZE];
1239 int visited_index = 0;
1240 #ifdef COUNT_FLUSH_FPSTACK
1241 struct timeval t0, t1;
1242 gettimeofday(&t0, NULL);
1243 #endif
1244
1245 /* first, scan value registers and incomplete frames */
1246 SCM_FLONUM_ENSURE_MEM(VAL0);
1247 for (int i=0; i<SCM_VM_MAX_VALUES; i++) {
1248 SCM_FLONUM_ENSURE_MEM(vm->vals[i]);
1249 }
1250 if (IN_FULL_STACK_P(ARGP)) {
1251 for (ScmObj *p = ARGP; p < SP; p++) SCM_FLONUM_ENSURE_MEM(*p);
1252 }
1253
1254 /* scan the main environment chain */
1255 ScmEnvFrame *e = ENV;
1256 while (IN_FULL_STACK_P((ScmObj*)e)) {
1257 for (int i = 0; i < visited_index; i++) {
1258 if (visited[i] == e) goto next;
1259 }
1260 if (visited_index < ENV_CACHE_SIZE) {
1261 visited[visited_index++] = e;
1262 }
1263
1264 for (int i = 0; i < e->size; i++) {
1265 ScmObj *p = &ENV_DATA(e, i);
1266 SCM_FLONUM_ENSURE_MEM(*p);
1267 }
1268 next:
1269 e = e->up;
1270 }
1271
1272 /* scan the env chains grabbed by cont chain */
1273 ScmContFrame *c = CONT;
1274 while (IN_FULL_STACK_P((ScmObj*)c)) {
1275 e = c->env;
1276 while (IN_FULL_STACK_P((ScmObj*)e)) {
1277 for (int i = 0; i < visited_index; i++) {
1278 if (visited[i] == e) goto next2;
1279 }
1280 if (visited_index < ENV_CACHE_SIZE) {
1281 visited[visited_index++] = e;
1282 }
1283 for (int i = 0; i < e->size; i++) {
1284 ScmObj *p = &ENV_DATA(e, i);
1285 SCM_FLONUM_ENSURE_MEM(*p);
1286 }
1287 next2:
1288 e = e->up;
1289 }
1290 if (IN_FULL_STACK_P((ScmObj*)c) && c->size > 0) {
1291 ScmObj *p = (ScmObj*)c - c->size;
1292 for (int i=0; i<c->size; i++, p++) SCM_FLONUM_ENSURE_MEM(*p);
1293 }
1294 c = c->prev;
1295 }
1296 #if GAUCHE_SPLIT_STACK
1297 if ((c = vm->lastErrorCont) != NULL) {
1298 while (IN_FULL_STACK_P((ScmObj*)c)) {
1299 e = c->env;
1300 while (IN_FULL_STACK_P((ScmObj*)e)) {
1301 for (int i = 0; i < visited_index; i++) {
1302 if (visited[i] == e) goto next3;
1303 }
1304 if (visited_index < ENV_CACHE_SIZE) {
1305 visited[visited_index++] = e;
1306 }
1307 for (int i = 0; i < e->size; i++) {
1308 ScmObj *p = &ENV_DATA(e, i);
1309 SCM_FLONUM_ENSURE_MEM(*p);
1310 }
1311 next3:
1312 e = e->up;
1313 }
1314 if (IN_FULL_STACK_P((ScmObj*)c) && c->size > 0) {
1315 ScmObj *p = (ScmObj*)c - c->size;
1316 for (int i=0; i<c->size; i++, p++) SCM_FLONUM_ENSURE_MEM(*p);
1317 }
1318 c = c->prev;
1319 }
1320 }
1321 #endif
1322
1323 vm->fpsp = vm->fpstack;
1324
1325 #ifdef COUNT_FLUSH_FPSTACK
1326 flush_fpstack_count++;
1327 gettimeofday(&t1, NULL);
1328 flush_fpstack_time +=
1329 (t1.tv_sec - t0.tv_sec)*1000000+(t1.tv_usec - t0.tv_usec);
1330 #endif
1331 }
1332 #undef ENV_CACHE_SIZE
1333
1334 #endif /*GAUCHE_FFX*/
1335
1336
1337 /*==================================================================
1338 * Function application from C
1339 */
1340
1341 /* The Scm_VMApply family is supposed to be called in SUBR. It doesn't really
1342 applies the function in it. Instead, it modifies the VM state so that
1343 the specified function will be called immediately after this SUBR
1344 returns to the VM. The return value of Scm_VMApply is just a PROC,
1345 but it should be returned as the return value of SUBR, which will be
1346 used by the VM.
1347 NB: we don't check proc is a procedure or not. It can be a non-procedure
1348 object, because of the object-apply hook. */
1349
1350 /* Static VM instruction arrays.
1351 Scm_VMApplyN modifies VM's pc to point it. */
1352
1353 static ScmWord apply_calls[][2] = {
1354 { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 0),
1355 SCM_VM_INSN(SCM_VM_RET) },
1356 { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 1),
1357 SCM_VM_INSN(SCM_VM_RET) },
1358 { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 2),
1359 SCM_VM_INSN(SCM_VM_RET) },
1360 { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 3),
1361 SCM_VM_INSN(SCM_VM_RET) },
1362 { SCM_VM_INSN1(SCM_VM_TAIL_CALL, 4),
1363 SCM_VM_INSN(SCM_VM_RET) },
1364 };
1365
1366 static ScmWord apply_callN[2] = {
1367 SCM_VM_INSN1(SCM_VM_TAIL_APPLY, 2),
1368 SCM_VM_INSN(SCM_VM_RET)
1369 };
1370
Scm_VMApply(ScmObj proc,ScmObj args)1371 ScmObj Scm_VMApply(ScmObj proc, ScmObj args)
1372 {
1373 int numargs = Scm_Length(args);
1374 int reqstack;
1375 ScmVM *vm = theVM;
1376
1377 if (numargs < 0) Scm_Error("improper list not allowed: %S", args);
1378 SCM_ASSERT(TAIL_POS());
1379 SCM_ASSERT(ARGP == SP);
1380 #if 0
1381 reqstack = ENV_SIZE(numargs) + 1;
1382 if (reqstack >= SCM_VM_STACK_SIZE) {
1383 /* there's no way we can accept that many arguments */
1384 Scm_Error("too many arguments (%d) to apply", numargs);
1385 }
1386 CHECK_STACK(reqstack);
1387
1388 ScmObj cp;
1389 SCM_FOR_EACH(cp, args) {
1390 PUSH_ARG(SCM_CAR(cp));
1391 }
1392 if (numargs <= 4) {
1393 PC = apply_calls[numargs];
1394 } else {
1395 PC = SCM_NEW_ARRAY(ScmWord, 2);
1396 PC[0] = SCM_VM_INSN1(SCM_VM_TAIL_CALL, numargs);
1397 PC[1] = SCM_VM_INSN(SCM_VM_RET);
1398 }
1399 return proc;
1400 #else
1401 reqstack = ENV_SIZE(1) + 1;
1402 CHECK_STACK(reqstack);
1403 PUSH_ARG(proc);
1404 PC = apply_callN;
1405 return Scm_CopyList(args);
1406 #endif
1407 }
1408
1409 /* shortcuts for common cases */
Scm_VMApply0(ScmObj proc)1410 ScmObj Scm_VMApply0(ScmObj proc)
1411 {
1412 theVM->pc = apply_calls[0];
1413 return proc;
1414 }
1415
Scm_VMApply1(ScmObj proc,ScmObj arg)1416 ScmObj Scm_VMApply1(ScmObj proc, ScmObj arg)
1417 {
1418 ScmVM *vm = theVM;
1419 CHECK_STACK(1);
1420 PUSH_ARG(arg);
1421 PC = apply_calls[1];
1422 return proc;
1423 }
1424
Scm_VMApply2(ScmObj proc,ScmObj arg1,ScmObj arg2)1425 ScmObj Scm_VMApply2(ScmObj proc, ScmObj arg1, ScmObj arg2)
1426 {
1427 ScmVM *vm = theVM;
1428 CHECK_STACK(2);
1429 PUSH_ARG(arg1);
1430 PUSH_ARG(arg2);
1431 PC = apply_calls[2];
1432 return proc;
1433 }
1434
Scm_VMApply3(ScmObj proc,ScmObj arg1,ScmObj arg2,ScmObj arg3)1435 ScmObj Scm_VMApply3(ScmObj proc, ScmObj arg1, ScmObj arg2, ScmObj arg3)
1436 {
1437 ScmVM *vm = theVM;
1438 CHECK_STACK(3);
1439 PUSH_ARG(arg1);
1440 PUSH_ARG(arg2);
1441 PUSH_ARG(arg3);
1442 PC = apply_calls[3];
1443 return proc;
1444 }
1445
Scm_VMApply4(ScmObj proc,ScmObj arg1,ScmObj arg2,ScmObj arg3,ScmObj arg4)1446 ScmObj Scm_VMApply4(ScmObj proc, ScmObj arg1, ScmObj arg2, ScmObj arg3, ScmObj arg4)
1447 {
1448 ScmVM *vm = theVM;
1449 CHECK_STACK(4);
1450 PUSH_ARG(arg1);
1451 PUSH_ARG(arg2);
1452 PUSH_ARG(arg3);
1453 PUSH_ARG(arg4);
1454 PC = apply_calls[4];
1455 return proc;
1456 }
1457
eval_restore_env(ScmObj * args SCM_UNUSED,int argc SCM_UNUSED,void * data)1458 static ScmObj eval_restore_env(ScmObj *args SCM_UNUSED,
1459 int argc SCM_UNUSED,
1460 void *data)
1461 {
1462 theVM->module = SCM_MODULE(data);
1463 return SCM_UNDEFINED;
1464 }
1465
1466 /* For now, we only supports a module as the evaluation environment */
Scm_VMEval(ScmObj expr,ScmObj e)1467 ScmObj Scm_VMEval(ScmObj expr, ScmObj e)
1468 {
1469 int restore_module = SCM_MODULEP(e);
1470 ScmVM *vm = theVM;
1471
1472 ScmObj v = Scm_Compile(expr, e);
1473 if (SCM_VM_COMPILER_FLAG_IS_SET(theVM, SCM_COMPILE_SHOWRESULT)) {
1474 Scm_CompiledCodeDump(SCM_COMPILED_CODE(v));
1475 }
1476
1477 vm->numVals = 1;
1478 if (restore_module) {
1479 /* if we swap the module, we need to make sure it is recovered
1480 after eval */
1481 ScmObj body = Scm_MakeClosure(v, NULL);
1482 ScmObj before = Scm_MakeSubr(eval_restore_env, SCM_MODULE(e),
1483 0, 0, SCM_SYM_EVAL_BEFORE);
1484 ScmObj after = Scm_MakeSubr(eval_restore_env, (void*)vm->module,
1485 0, 0, SCM_SYM_EVAL_AFTER);
1486 return Scm_VMDynamicWind(before, body, after);
1487 } else {
1488 /* shortcut */
1489 SCM_ASSERT(SCM_COMPILED_CODE_P(v));
1490 vm->base = SCM_COMPILED_CODE(v);
1491 vm->pc = SCM_COMPILED_CODE(v)->code;
1492 SCM_PROF_COUNT_CALL(vm, v);
1493 return SCM_UNDEFINED;
1494 }
1495 }
1496
1497 /* Arrange C function AFTER to be called after the procedure returns.
1498 * Usually followed by Scm_VMApply* function.
1499 */
Scm_VMPushCC(ScmCContinuationProc * after,void ** data,int datasize)1500 void Scm_VMPushCC(ScmCContinuationProc *after,
1501 void **data, int datasize)
1502 {
1503 ScmVM *vm = theVM;
1504
1505 CHECK_STACK(CONT_FRAME_SIZE+datasize);
1506 ScmObj *s = SP;
1507 for (int i=0; i<datasize; i++) {
1508 *s++ = SCM_OBJ(data[i]);
1509 }
1510 ScmContFrame *cc = (ScmContFrame*)s;
1511 s += CONT_FRAME_SIZE;
1512 cc->prev = CONT;
1513 cc->env = &ccEnvMark;
1514 cc->size = datasize;
1515 cc->marker = 0;
1516 cc->cpc = NULL;
1517 cc->pc = (ScmWord*)after;
1518 cc->base = BASE;
1519 CONT = cc;
1520 ARGP = SP = s;
1521 }
1522
1523 /*-------------------------------------------------------------
1524 * User level eval and apply.
1525 * When the C routine wants the Scheme code to return to it,
1526 * instead of using C-continuation, the continuation
1527 * "cross the border" of C-stack and Scheme-stack. This
1528 * border has peculiar characteristics. Once the Scheme
1529 * returns, continuations saved during the execution of the
1530 * Scheme code becomes invalid.
1531 *
1532 * At the implementation level, this boundary is kept in a
1533 * structure ScmCStack.
1534 */
1535
1536 /* Border gate. All the C->Scheme calls should go through here.
1537 *
1538 * The current C stack information is saved in cstack. The
1539 * current VM stack information is saved (as a continuation
1540 * frame pointer) in cstack.cont.
1541 */
1542
user_eval_inner(ScmObj program,ScmWord * codevec)1543 static ScmObj user_eval_inner(ScmObj program, ScmWord *codevec)
1544 {
1545 ScmCStack cstack;
1546 ScmVM * volatile vm = theVM;
1547 /* Save prev_pc, for the boundary continuation uses pc slot
1548 to mark the boundary. */
1549 ScmWord * volatile prev_pc = PC;
1550 ScmObj vmhandlers = vm->handlers;
1551 ScmObj vmresetChain = vm->resetChain;
1552
1553 /* Push extra continuation. This continuation frame is a 'boundary
1554 frame' and marked by pc == &boundaryFrameMark. VM loop knows
1555 it should return to C frame when it sees a boundary frame.
1556 A boundary frame also keeps the unfinished argument frame at
1557 the point when Scm_Eval or Scm_Apply is called. */
1558 CHECK_STACK(CONT_FRAME_SIZE);
1559 PUSH_CONT(&boundaryFrameMark);
1560 SCM_ASSERT(SCM_COMPILED_CODE_P(program));
1561 vm->base = SCM_COMPILED_CODE(program);
1562 if (codevec != NULL) {
1563 PC = codevec;
1564 } else {
1565 PC = vm->base->code;
1566 CHECK_STACK(vm->base->maxstack);
1567 }
1568 SCM_PROF_COUNT_CALL(vm, program);
1569
1570 cstack.prev = vm->cstack;
1571 cstack.cont = vm->cont;
1572 vm->cstack = &cstack;
1573
1574 restart:
1575 vm->escapeReason = SCM_VM_ESCAPE_NONE;
1576 if (sigsetjmp(cstack.jbuf, FALSE) == 0) {
1577 run_loop(); /* VM loop */
1578 if (vm->cont == cstack.cont) {
1579 POP_CONT();
1580 PC = prev_pc;
1581 } else if (vm->cont == NULL) {
1582 /* we're finished with executing partial continuation. */
1583
1584 /* restore reset-chain for reset/shift */
1585 vm->resetChain = vmresetChain;
1586
1587 /* save return values */
1588 ScmObj val0 = vm->val0;
1589 int nvals = vm->numVals;
1590 ScmObj *vals = NULL;
1591 if (nvals > 1) {
1592 vals = SCM_NEW_ARRAY(ScmObj, nvals-1);
1593 memcpy(vals, vm->vals, sizeof(ScmObj)*(nvals-1));
1594 }
1595
1596 /* call dynamic handlers for returning to the caller */
1597 call_dynamic_handlers(vmhandlers, vm->handlers);
1598
1599 /* restore return values */
1600 vm->val0 = val0;
1601 vm->numVals = nvals;
1602 if (vals != NULL) {
1603 memcpy(vm->vals, vals, sizeof(ScmObj)*(nvals-1));
1604 }
1605
1606 vm->cont = cstack.cont;
1607 POP_CONT();
1608 PC = prev_pc;
1609 } else {
1610 /* If we come here, we've been executing a ghost continuation.
1611 The C world the ghost should return no longer exists, so we
1612 raise an error. */
1613 Scm_Error("attempt to return from a ghost continuation.");
1614 }
1615 } else {
1616 /* An escape situation happened. */
1617 if (vm->escapeReason == SCM_VM_ESCAPE_CONT) {
1618 ScmEscapePoint *ep = (ScmEscapePoint*)vm->escapeData[0];
1619 if (ep->cstack == vm->cstack) {
1620 ScmObj handlers = throw_cont_calculate_handlers(ep->handlers,
1621 vm->handlers);
1622 /* force popping continuation when restarted */
1623 vm->pc = PC_TO_RETURN;
1624 vm->val0 = throw_cont_body(handlers, ep, vm->escapeData[1]);
1625 goto restart;
1626 } else {
1627 SCM_ASSERT(vm->cstack && vm->cstack->prev);
1628 vm->cont = cstack.cont;
1629 POP_CONT();
1630 vm->cstack = vm->cstack->prev;
1631 siglongjmp(vm->cstack->jbuf, 1);
1632 }
1633 } else if (vm->escapeReason == SCM_VM_ESCAPE_ERROR) {
1634 ScmEscapePoint *ep = (ScmEscapePoint*)vm->escapeData[0];
1635 if (ep && ep->cstack == vm->cstack) {
1636 vm->cont = ep->cont;
1637 vm->pc = PC_TO_RETURN;
1638 /* restore reset-chain for reset/shift */
1639 if (ep->cstack) vm->resetChain = ep->resetChain;
1640 goto restart;
1641 } else if (vm->cstack->prev == NULL) {
1642 /* This loop is the outermost C stack, and nobody will
1643 capture the error. This happens when the base C code
1644 calls Scm_EvalRec/ApplyRec, instead of Scm_Eval/Apply.
1645 We can't return, since there's no way to pass the
1646 error info. We can only just exit.
1647 */
1648 Scm_Exit(EX_SOFTWARE);
1649 } else {
1650 /* Jump again until C stack is recovered. We could pop
1651 the extra continuation frame so that the VM stack
1652 is consistent. */
1653 vm->cont = cstack.cont;
1654 POP_CONT();
1655 vm->cstack = vm->cstack->prev;
1656 siglongjmp(vm->cstack->jbuf, 1);
1657 }
1658 } else {
1659 Scm_Panic("invalid longjmp");
1660 }
1661 /* NOTREACHED */
1662 }
1663 vm->cstack = vm->cstack->prev;
1664 return vm->val0;
1665 }
1666
1667 /* API for recursive call to VM. Exceptions are not captured.
1668 Returns the primary result. To retrieve the rest of results,
1669 you have to use Scm_VMGetResult etc. */
1670
Scm_EvalRec(ScmObj expr,ScmObj e)1671 ScmObj Scm_EvalRec(ScmObj expr, ScmObj e)
1672 {
1673 ScmObj v = Scm_Compile(expr, e);
1674 SCM_COMPILED_CODE(v)->name = SCM_SYM_INTERNAL_EVAL;
1675 if (SCM_VM_COMPILER_FLAG_IS_SET(theVM, SCM_COMPILE_SHOWRESULT)) {
1676 Scm_CompiledCodeDump(SCM_COMPILED_CODE(v));
1677 }
1678 return user_eval_inner(v, NULL);
1679 }
1680
1681 /* NB: The ApplyRec family can be called in an inner loop (e.g. the display
1682 callback from GLUT.) So we don't want to allocate at all. We put
1683 a temporary code vector on C stack. It is OK, since once
1684 user_eval_inner returns it would never be reused. However, tools
1685 that want to keep a pointer to a code vector would need to be aware
1686 of this case. */
apply_rec(ScmVM * vm,ScmObj proc,int nargs)1687 static ScmObj apply_rec(ScmVM *vm, ScmObj proc, int nargs)
1688 {
1689 ScmWord code[2];
1690 code[0] = SCM_WORD(SCM_VM_INSN1(SCM_VM_VALUES_APPLY, nargs));
1691 code[1] = SCM_WORD(SCM_VM_INSN(SCM_VM_RET));
1692
1693 vm->val0 = proc;
1694 ScmObj program = vm->base?
1695 SCM_OBJ(vm->base) : SCM_OBJ(&internal_apply_compiled_code);
1696 return user_eval_inner(program, code);
1697 }
1698
Scm_ApplyRec(ScmObj proc,ScmObj args)1699 ScmObj Scm_ApplyRec(ScmObj proc, ScmObj args)
1700 {
1701 int nargs = Scm_Length(args);
1702 ScmVM *vm = theVM;
1703
1704 if (nargs < 0) {
1705 Scm_Error("improper list not allowed: %S", args);
1706 }
1707
1708 for (int i=0; i<nargs; i++) {
1709 if (i == SCM_VM_MAX_VALUES-1) {
1710 vm->vals[i] = args;
1711 break;
1712 }
1713 vm->vals[i] = SCM_CAR(args);
1714 args = SCM_CDR(args);
1715 }
1716 return apply_rec(vm, proc, nargs);
1717 }
1718
Scm_ApplyRec0(ScmObj proc)1719 ScmObj Scm_ApplyRec0(ScmObj proc)
1720 {
1721 return apply_rec(theVM, proc, 0);
1722 }
1723
Scm_ApplyRec1(ScmObj proc,ScmObj arg0)1724 ScmObj Scm_ApplyRec1(ScmObj proc, ScmObj arg0)
1725 {
1726 ScmVM *vm = theVM;
1727 vm->vals[0] = arg0;
1728 return apply_rec(vm, proc, 1);
1729 }
1730
Scm_ApplyRec2(ScmObj proc,ScmObj arg0,ScmObj arg1)1731 ScmObj Scm_ApplyRec2(ScmObj proc, ScmObj arg0, ScmObj arg1)
1732 {
1733 ScmVM *vm = theVM;
1734 vm->vals[0] = arg0;
1735 vm->vals[1] = arg1;
1736 return apply_rec(vm, proc, 2);
1737 }
1738
Scm_ApplyRec3(ScmObj proc,ScmObj arg0,ScmObj arg1,ScmObj arg2)1739 ScmObj Scm_ApplyRec3(ScmObj proc, ScmObj arg0, ScmObj arg1, ScmObj arg2)
1740 {
1741 ScmVM *vm = theVM;
1742 vm->vals[0] = arg0;
1743 vm->vals[1] = arg1;
1744 vm->vals[2] = arg2;
1745 return apply_rec(vm, proc, 3);
1746 }
1747
Scm_ApplyRec4(ScmObj proc,ScmObj arg0,ScmObj arg1,ScmObj arg2,ScmObj arg3)1748 ScmObj Scm_ApplyRec4(ScmObj proc, ScmObj arg0, ScmObj arg1, ScmObj arg2,
1749 ScmObj arg3)
1750 {
1751 ScmVM *vm = theVM;
1752 vm->vals[0] = arg0;
1753 vm->vals[1] = arg1;
1754 vm->vals[2] = arg2;
1755 vm->vals[3] = arg3;
1756 return apply_rec(vm, proc, 4);
1757 }
1758
Scm_ApplyRec5(ScmObj proc,ScmObj arg0,ScmObj arg1,ScmObj arg2,ScmObj arg3,ScmObj arg4)1759 ScmObj Scm_ApplyRec5(ScmObj proc, ScmObj arg0, ScmObj arg1, ScmObj arg2,
1760 ScmObj arg3, ScmObj arg4)
1761 {
1762 ScmVM *vm = theVM;
1763 vm->vals[0] = arg0;
1764 vm->vals[1] = arg1;
1765 vm->vals[2] = arg2;
1766 vm->vals[3] = arg3;
1767 vm->vals[4] = arg4;
1768 return apply_rec(vm, proc, 5);
1769 }
1770
1771 /*
1772 * Safe version of user-level Eval, Apply and Load.
1773 * Exceptions are caught and stored in ScmEvalPacket.
1774 */
1775
1776 enum {
1777 SAFE_EVAL,
1778 SAFE_EVAL_CSTRING,
1779 SAFE_APPLY
1780 };
1781
1782 struct eval_packet_rec {
1783 ScmObj env;
1784 int kind;
1785 ScmObj arg0; /* form (EVAL), proc (APPLY) */
1786 ScmObj args; /* args (APPLY) */
1787 const char *cstr; /* cstring (EVAL_CSTRING) */
1788 ScmObj exception;
1789 };
1790
safe_eval_handler(ScmObj * args,int nargs,void * data)1791 static ScmObj safe_eval_handler(ScmObj *args,
1792 int nargs, void *data)
1793 {
1794 SCM_ASSERT(nargs == 1);
1795 ((struct eval_packet_rec *)data)->exception = args[0];
1796 return SCM_UNDEFINED;
1797 }
1798
safe_eval_thunk(ScmObj * args SCM_UNUSED,int nargs SCM_UNUSED,void * data)1799 static ScmObj safe_eval_thunk(ScmObj *args SCM_UNUSED,
1800 int nargs SCM_UNUSED,
1801 void *data)
1802 {
1803 struct eval_packet_rec *epak = (struct eval_packet_rec*)data;
1804 ScmObj r;
1805
1806 switch (epak->kind) {
1807 case SAFE_EVAL_CSTRING:
1808 r = Scm_VMEval(Scm_ReadFromCString(epak->cstr), epak->env);
1809 break;
1810 case SAFE_EVAL:
1811 r = Scm_VMEval(epak->arg0, epak->env);
1812 break;
1813 case SAFE_APPLY:
1814 r = Scm_VMApply(epak->arg0, epak->args);
1815 break;
1816 default:
1817 Scm_Panic("safe_eval_subr: bad kind");
1818 return SCM_UNBOUND; /* dummy */
1819 }
1820 /* If expressino was select-module, the current module may be changed. */
1821 epak->env = SCM_OBJ(SCM_CURRENT_MODULE());
1822 return r;
1823 }
1824
safe_eval_int(ScmObj * args SCM_UNUSED,int nargs SCM_UNUSED,void * data)1825 static ScmObj safe_eval_int(ScmObj *args SCM_UNUSED,
1826 int nargs SCM_UNUSED,
1827 void *data)
1828 {
1829 ScmObj thunk = Scm_MakeSubr(safe_eval_thunk, data, 0, 0, SCM_FALSE);
1830 ScmObj handler = Scm_MakeSubr(safe_eval_handler, data, 1, 0, SCM_FALSE);
1831 return Scm_VMWithErrorHandler(handler, thunk);
1832 }
1833
safe_eval_wrap(int kind,ScmObj arg0,ScmObj args,const char * cstr,ScmObj env,ScmEvalPacket * result)1834 static int safe_eval_wrap(int kind, ScmObj arg0, ScmObj args,
1835 const char *cstr, ScmObj env,
1836 ScmEvalPacket *result)
1837 {
1838 ScmVM *vm = theVM;
1839
1840 struct eval_packet_rec epak;
1841 epak.env = env;
1842 epak.kind = kind;
1843 epak.arg0 = arg0;
1844 epak.args = args;
1845 epak.cstr = cstr;
1846 epak.exception = SCM_UNBOUND;
1847
1848 ScmObj proc = Scm_MakeSubr(safe_eval_int, &epak, 0, 0, SCM_FALSE);
1849 ScmObj r = Scm_ApplyRec(proc, SCM_NIL);
1850
1851 if (SCM_UNBOUNDP(epak.exception)) {
1852 /* normal termination */
1853 if (result) {
1854 result->numResults = vm->numVals;
1855 result->results[0] = r;
1856 for (int i=1; i<vm->numVals; i++) {
1857 result->results[i] = vm->vals[i-1];
1858 }
1859 result->exception = SCM_FALSE;
1860 if (SCM_MODULEP(epak.env)) {
1861 result->module = SCM_MODULE(epak.env);
1862 }
1863 }
1864 return vm->numVals;
1865 } else {
1866 /* abnormal termination */
1867 if (result) {
1868 result->numResults = 0;
1869 result->exception = epak.exception;
1870 }
1871 return -1;
1872 }
1873 }
1874
Scm_Eval(ScmObj form,ScmObj env,ScmEvalPacket * packet)1875 int Scm_Eval(ScmObj form, ScmObj env, ScmEvalPacket *packet)
1876 {
1877 return safe_eval_wrap(SAFE_EVAL, form, SCM_FALSE, NULL, env, packet);
1878 }
1879
Scm_EvalCString(const char * expr,ScmObj env,ScmEvalPacket * packet)1880 int Scm_EvalCString(const char *expr, ScmObj env, ScmEvalPacket *packet)
1881 {
1882 return safe_eval_wrap(SAFE_EVAL_CSTRING, SCM_FALSE, SCM_FALSE,
1883 expr, env, packet);
1884 }
1885
Scm_Apply(ScmObj proc,ScmObj args,ScmEvalPacket * packet)1886 int Scm_Apply(ScmObj proc, ScmObj args, ScmEvalPacket *packet)
1887 {
1888 return safe_eval_wrap(SAFE_APPLY, proc, args, NULL, SCM_FALSE, packet);
1889 }
1890
1891 /*
1892 * A subroutine to be called while executing apply instruction.
1893 * Apply needs to check the argument tail is a valid list. However,
1894 * naively using ordinary procedures can trigger forcing of lazy
1895 * pair, which breaks VM state.
1896 *
1897 * When called, we know vm->sp[-1] contains the tail list of arguments.
1898 * It may be a lazy pair. If so, we should force it in the safe environment.
1899 * Returns the length of the tail list.
1900 *
1901 * Currently, we force the lazy argument tail in very inefficient way,
1902 * assuming that such case is very rare.
1903 *
1904 * When max_limit is non-negative, we only check up that number of
1905 * arguments and bail. For the true length, the caller should call
1906 * again with max_limit == -1.
1907 */
1908
check_arglist_tail_for_apply(ScmVM * vm SCM_UNUSED,ScmObj z,int max_limit)1909 int check_arglist_tail_for_apply(ScmVM *vm SCM_UNUSED, ScmObj z, int max_limit)
1910 {
1911 int count = 0;
1912 static ScmObj length_proc = SCM_UNDEFINED;
1913 ScmObj tortoise = z;
1914
1915 for (;;) {
1916 if (SCM_NULLP(z)) return count;
1917 if (SCM_LAZY_PAIR_P(z)) goto do_lazy_pair;
1918 if (!SCM_PAIRP(z)) goto bad_list;
1919
1920 z = SCM_CDR(z);
1921 count++;
1922
1923 if (SCM_NULLP(z)) return count;
1924 if (SCM_LAZY_PAIR_P(z)) goto do_lazy_pair;
1925 if (!SCM_PAIRP(z)) goto bad_list;
1926
1927 z = SCM_CDR(z);
1928 tortoise = SCM_CDR(tortoise);
1929 if (z == tortoise) goto bad_list; /* circular */
1930 count++;
1931
1932 if (max_limit >= 0 && count >= max_limit) return count;
1933 }
1934
1935 do_lazy_pair:
1936 {
1937 ScmEvalPacket result;
1938 SCM_BIND_PROC(length_proc, "length", Scm_GaucheModule());
1939 int nres = Scm_Apply(length_proc, SCM_LIST1(z), &result);
1940 if (nres == -1) Scm_Raise(result.exception, 0);
1941 SCM_ASSERT(nres == 1);
1942 SCM_ASSERT(SCM_INTP(result.results[0]));
1943 count += SCM_INT_VALUE(result.results[0]);
1944 return count;
1945 }
1946 bad_list:
1947 Scm_Error("improper list not allowed: %S", tortoise);
1948 }
1949
1950 /*=================================================================
1951 * Dynamic handlers
1952 */
1953
1954 static ScmCContinuationProc dynwind_before_cc;
1955 static ScmCContinuationProc dynwind_body_cc;
1956 static ScmCContinuationProc dynwind_after_cc;
1957
Scm_VMDynamicWind(ScmObj before,ScmObj body,ScmObj after)1958 ScmObj Scm_VMDynamicWind(ScmObj before, ScmObj body, ScmObj after)
1959 {
1960 void *data[3];
1961
1962 /* NB: we don't check types of arguments, since we allow object-apply
1963 hooks can be used for them. */
1964 data[0] = (void*)before;
1965 data[1] = (void*)body;
1966 data[2] = (void*)after;
1967
1968 Scm_VMPushCC(dynwind_before_cc, data, 3);
1969 return Scm_VMApply0(before);
1970 }
1971
dynwind_before_cc(ScmObj result SCM_UNUSED,void ** data)1972 static ScmObj dynwind_before_cc(ScmObj result SCM_UNUSED, void **data)
1973 {
1974 ScmObj before = SCM_OBJ(data[0]);
1975 ScmObj body = SCM_OBJ(data[1]);
1976 ScmObj after = SCM_OBJ(data[2]);
1977 void *d[1];
1978 ScmVM *vm = theVM;
1979
1980 d[0] = (void*)after;
1981 vm->handlers = Scm_Cons(Scm_Cons(before, after), vm->handlers);
1982 Scm_VMPushCC(dynwind_body_cc, d, 1);
1983 return Scm_VMApply0(body);
1984 }
1985
dynwind_body_cc(ScmObj result,void ** data)1986 static ScmObj dynwind_body_cc(ScmObj result, void **data)
1987 {
1988 ScmObj after = SCM_OBJ(data[0]);
1989 void *d[3];
1990 ScmVM *vm = theVM;
1991
1992 SCM_ASSERT(SCM_PAIRP(vm->handlers));
1993 vm->handlers = SCM_CDR(vm->handlers);
1994
1995 /* Save return values.
1996 We could avoid malloc when numVals is small (we can push
1997 them directly onto the stack). But our benchmark showed doing so
1998 actually gets slightly slower. More branches may have a negative
1999 effect. So we keep it simple here.
2000 */
2001 int nvals = vm->numVals;
2002 d[0] = (void*)result;
2003 d[1] = (void*)(intptr_t)nvals;
2004 if (nvals > 1) {
2005 ScmObj *vals = SCM_NEW_ARRAY(ScmObj, nvals-1);
2006 memcpy(vals, vm->vals, sizeof(ScmObj)*(nvals-1));
2007 d[2] = (void*)vals;
2008 Scm_VMPushCC(dynwind_after_cc, d, 3);
2009 } else {
2010 Scm_VMPushCC(dynwind_after_cc, d, 2);
2011 }
2012 return Scm_VMApply0(after);
2013 }
2014
dynwind_after_cc(ScmObj result SCM_UNUSED,void ** data)2015 static ScmObj dynwind_after_cc(ScmObj result SCM_UNUSED, void **data)
2016 {
2017 ScmVM *vm = theVM;
2018
2019 /* Restore return values. */
2020 ScmObj val0 = SCM_OBJ(data[0]);
2021 int nvals = (int)(intptr_t)data[1];
2022 vm->numVals = nvals;
2023 if (nvals > 1) {
2024 ScmObj *vals = (ScmObj*)data[2];
2025 SCM_ASSERT(nvals <= SCM_VM_MAX_VALUES);
2026 memcpy(vm->vals, vals, sizeof(ScmObj)*(nvals-1));
2027 }
2028 return val0;
2029 }
2030
2031 /* C-friendly wrapper */
Scm_VMDynamicWindC(ScmSubrProc * before,ScmSubrProc * body,ScmSubrProc * after,void * data)2032 ScmObj Scm_VMDynamicWindC(ScmSubrProc *before,
2033 ScmSubrProc *body,
2034 ScmSubrProc *after,
2035 void *data)
2036 {
2037 ScmObj beforeproc =
2038 before ? Scm_MakeSubr(before, data, 0, 0, SCM_FALSE) : Scm_NullProc();
2039 ScmObj afterproc =
2040 after ? Scm_MakeSubr(after, data, 0, 0, SCM_FALSE) : Scm_NullProc();
2041 ScmObj bodyproc =
2042 body ? Scm_MakeSubr(body, data, 0, 0, SCM_FALSE) : Scm_NullProc();
2043
2044 return Scm_VMDynamicWind(beforeproc, bodyproc, afterproc);
2045 }
2046
2047
2048 /*=================================================================
2049 * Exception handling
2050 */
2051
2052 /* Conceptually, exception handling is nothing more than a particular
2053 * combination of dynamic-wind and call/cc. Gauche implements a parts
2054 * of it in C so that it will be efficient and safer to use.
2055 *
2056 * The most basic layer consists of these two functions:
2057 *
2058 * with-exception-handler
2059 * raise
2060 *
2061 * There is a slight problem, though. These two functions are defined
2062 * both in srfi-18 (multithreads) and srfi-34 (exception handling), and
2063 * two disagrees in the semantics of raise.
2064 *
2065 * Srfi-18 requires an exception handler to be called with the same dynamic
2066 * environment as the one of the primitive that raises the exception.
2067 * That means when an exception handler is running, the current
2068 * exception handler is the running handler itself. Naturally, calling
2069 * raise unconditionally within the exception handler causes infinite loop.
2070 *
2071 * Srfi-34 says that an exception handler is called with the same dynamic
2072 * environment where the exception is raised, _except_ that the current
2073 * exception handler is "popped", i.e. when an exception handler is running,
2074 * the current exception handler is the "outer" or "old" one. Calling
2075 * raise within an exception handler passes the control to the outer
2076 * exception handler.
2077 *
2078 * At this point I haven't decided which model Gauche should support natively.
2079 * The current implementation predates srfi-34 and roughly follows srfi-18.
2080 * It appears that srfi-18's mechanism is more "primitive" or "lightweight"
2081 * than srfi-34's, so it's likely that Gauche will continue to support
2082 * srfi-18 model natively, and maybe provides srfi-34's interface by an
2083 * additional module.
2084 *
2085 * The following is a model of the current implementation, sans the messy
2086 * part of handling C stacks.
2087 * Suppose a system variable %xh keeps the list of exception handlers.
2088 *
2089 * (define (current-exception-handler) (car %xh))
2090 *
2091 * (define (raise exn)
2092 * (receive r ((car %xh) exn)
2093 * (when (uncontinuable-exception? exn)
2094 * (set! %xh (cdr %xh))
2095 * (error "returned from uncontinuable exception"))
2096 * (apply values r)))
2097 *
2098 * (define (with-exception-handler handler thunk)
2099 * (let ((prev %xh))
2100 * (dynamic-wind
2101 * (lambda () (set! %xh (cons handler)))
2102 * thunk
2103 * (lambda () (set! %xh prev)))))
2104 *
2105 * In C level, the chain of the handlers are represented in the chain
2106 * of ScmEscapePoints.
2107 *
2108 * Note that this model assumes an exception handler returns unless it
2109 * explicitly invokes continuation captured elsewhere. In reality,
2110 * "error" exceptions are not supposed to return (hence it is checked
2111 * in raise). Gauche provides another useful exception handling
2112 * constructs that automates such continuation capturing. It can be
2113 * explained by the following code.
2114 *
2115 * (define (with-error-handler handler thunk)
2116 * (call/cc
2117 * (lambda (cont)
2118 * (let ((prev-handler (current-exception-handler)))
2119 * (with-exception-handler
2120 * (lambda (exn)
2121 * (if (error? exn)
2122 * (call-with-values (handler exn) cont)
2123 * (prev-handler exn)))
2124 * thunk)))))
2125 *
2126 * In the actual implementation,
2127 *
2128 * - No "real" continuation procedure is created, but a lightweight
2129 * mechanism is used. The lightweight mechanism is similar to
2130 * "one-shot" callback (call/1cc in Chez Scheme).
2131 * - The error handler chain is kept in vm->escapePoint
2132 * - There are messy lonjmp/setjmp stuff involved to keep C stack sane.
2133 */
2134
2135 /*
2136 * Default exception handler
2137 * This is what we have as the system default, and also
2138 * what with-error-handler installs as an exception handler.
2139 */
2140
Scm_VMDefaultExceptionHandler(ScmObj e)2141 ScmObj Scm_VMDefaultExceptionHandler(ScmObj e)
2142 {
2143 ScmVM *vm = theVM;
2144 ScmEscapePoint *ep = vm->escapePoint;
2145
2146 if (ep) {
2147 /* There's an escape point defined by with-error-handler. */
2148 ScmObj vmhandlers = vm->handlers;
2149 ScmObj result = SCM_FALSE, rvals[SCM_VM_MAX_VALUES];
2150 int numVals = 0;
2151
2152 #if GAUCHE_SPLIT_STACK
2153 vm->lastErrorCont = vm->cont;
2154 vm->stackBase = vm->sp;
2155 #endif
2156
2157 /* To conform SRFI-34, the error handler (clauses in 'guard' form)
2158 should be executed with the same continuation and dynamic
2159 environment of the guard form itself. That means the dynamic
2160 handlers should be rewound before we invoke the guard clause.
2161
2162 If an error is raised within the dynamic handlers, it will be
2163 captured by the same error handler. */
2164 if (ep->rewindBefore) {
2165 call_dynamic_handlers(ep->handlers, vm->handlers);
2166 }
2167
2168 /* Call the error handler and save the results.
2169 NB: before calling the error handler, we need to pop
2170 vm->escapePoint, so that the error occurred during
2171 the error handler should be dealt with the upstream error
2172 handler. We keep ep in vm->escapePoint->floating, so that
2173 ep->cont can be updated when stack overflow occurs during the
2174 error handler. See also the description of ScmEscapePoint in
2175 gauche/vm.h. */
2176 vm->escapePoint = ep->prev;
2177 SCM_VM_FLOATING_EP_SET(vm, ep);
2178
2179 SCM_UNWIND_PROTECT {
2180 result = Scm_ApplyRec(ep->ehandler, SCM_LIST1(e));
2181
2182 /* save return values */
2183 /* NB: for loop is slightly faster than memcpy */
2184 numVals = vm->numVals;
2185 if (numVals > 1) {
2186 for (int i=0; i<numVals-1; i++) rvals[i] = vm->vals[i];
2187 }
2188
2189 /* call dynamic handlers to rewind */
2190 if (!ep->rewindBefore) {
2191 call_dynamic_handlers(ep->handlers, vm->handlers);
2192 }
2193 }
2194 SCM_WHEN_ERROR {
2195 /* make sure the floating pointer is reset when an error is
2196 signalled during handlers */
2197 SCM_VM_FLOATING_EP_SET(vm, ep->floating);
2198 SCM_NEXT_HANDLER;
2199 }
2200 SCM_END_PROTECT;
2201
2202 /* If exception is reraised, the exception handler can return
2203 to the caller. */
2204 if (ep->reraised) {
2205 ep->reraised = FALSE;
2206
2207 /* recover escape point */
2208 vm->escapePoint = ep;
2209 SCM_VM_FLOATING_EP_SET(vm, ep->floating);
2210
2211 /* call dynamic handlers to reenter dynamic-winds */
2212 call_dynamic_handlers(vmhandlers, ep->handlers);
2213
2214 /* reraise and return */
2215 vm->exceptionHandler = ep->xhandler;
2216 vm->escapePoint = ep->prev;
2217 SCM_VM_FLOATING_EP_SET(vm, ep);
2218 result = Scm_VMThrowException(vm, e, 0);
2219 vm->exceptionHandler = DEFAULT_EXCEPTION_HANDLER;
2220 vm->escapePoint = ep;
2221 SCM_VM_FLOATING_EP_SET(vm, ep->floating);
2222 return result;
2223 }
2224
2225 /* restore return values */
2226 vm->val0 = result;
2227 vm->numVals = numVals;
2228 if (numVals > 1) {
2229 for (int i=0; i<numVals-1; i++) vm->vals[i] = rvals[i];
2230 }
2231
2232 /* Install the continuation */
2233 vm->cont = ep->cont;
2234 SCM_VM_FLOATING_EP_SET(vm, ep->floating);
2235 if (ep->errorReporting) {
2236 SCM_VM_RUNTIME_FLAG_SET(vm, SCM_ERROR_BEING_REPORTED);
2237 }
2238 /* restore reset-chain for reset/shift */
2239 if (ep->cstack) vm->resetChain = ep->resetChain;
2240 } else {
2241 /* We don't have an active error handler, so this is the fallback
2242 behavior. Reports the error and rewind dynamic handlers and
2243 C stacks. */
2244 call_error_reporter(e);
2245 /* unwind the dynamic handlers */
2246 ScmObj hp;
2247 SCM_FOR_EACH(hp, vm->handlers) {
2248 ScmObj handler = SCM_CDAR(hp);
2249 vm->handlers = SCM_CDR(hp);
2250 Scm_ApplyRec(handler, SCM_NIL);
2251 }
2252 }
2253
2254 SCM_ASSERT(vm->cstack);
2255 vm->escapeReason = SCM_VM_ESCAPE_ERROR;
2256 vm->escapeData[0] = ep;
2257 vm->escapeData[1] = e;
2258 siglongjmp(vm->cstack->jbuf, 1);
2259 }
2260
2261 /* Call error reporter - either the custome one, or the default
2262 Scm_ReportError. We set SCM_ERROR_BEING_REPORTED flag during it
2263 to prevent infinite loop. */
call_error_reporter(ScmObj e)2264 static void call_error_reporter(ScmObj e)
2265 {
2266 ScmVM *vm = Scm_VM();
2267
2268 if (SCM_VM_RUNTIME_FLAG_IS_SET(vm, SCM_ERROR_BEING_REPORTED)) {
2269 /* An _uncaptured_ error occurred during reporting an error.
2270 We can't proceed, for it will cause infinite loop.
2271 Note that it is OK for an error to occur inside the error
2272 reporter, as far as the error is handled by user-installed
2273 handler. The user-installed handler can even invoke a
2274 continuation that is captured outside; the flag is reset
2275 in such case.
2276 Be careful that it is possible that stderr is no longer
2277 available here (since it may be the very cause of the
2278 recursive error). All we can do is to abort. */
2279 Scm_Abort("Unhandled error occurred during reporting an error. Process aborted.\n");
2280 }
2281
2282 SCM_VM_RUNTIME_FLAG_SET(vm, SCM_ERROR_BEING_REPORTED);
2283 SCM_UNWIND_PROTECT {
2284 if (SCM_PROCEDUREP(vm->customErrorReporter)) {
2285 Scm_ApplyRec(vm->customErrorReporter, SCM_LIST1(e));
2286 } else {
2287 Scm_ReportError(e, SCM_OBJ(SCM_CURERR));
2288 }
2289 }
2290 SCM_WHEN_ERROR {
2291 /* NB: this is called when a continuation captured outside is
2292 invoked inside the error reporter. It may be invoked by
2293 the user's error handler. */
2294 SCM_VM_RUNTIME_FLAG_CLEAR(vm, SCM_ERROR_BEING_REPORTED);
2295 }
2296 SCM_END_PROTECT;
2297 SCM_VM_RUNTIME_FLAG_CLEAR(vm, SCM_ERROR_BEING_REPORTED);
2298 }
2299
default_exception_handler_body(ScmObj * argv,int argc,void * data SCM_UNUSED)2300 static ScmObj default_exception_handler_body(ScmObj *argv,
2301 int argc,
2302 void *data SCM_UNUSED)
2303 {
2304 SCM_ASSERT(argc == 1);
2305 return Scm_VMDefaultExceptionHandler(argv[0]);
2306 }
2307
2308 static SCM_DEFINE_STRING_CONST(default_exception_handler_name,
2309 "default-exception-handler",
2310 25, 25); /* strlen("default-exception-handler") */
2311 static SCM_DEFINE_SUBR(default_exception_handler_rec, 1, 0,
2312 SCM_OBJ(&default_exception_handler_name),
2313 default_exception_handler_body, NULL, NULL);
2314
2315 /*
2316 * Entry point of throwing exception.
2317 *
2318 * This function can be called from Scheme function raise,
2319 * or C-function Scm_Error families and signal handler.
2320 * So there may be a raw C code in the continuation of this C call.
2321 * Thus we can't use Scm_VMApply to call the user-defined exception
2322 * handler.
2323 * Note that this function may return.
2324 */
Scm_VMThrowException(ScmVM * vm,ScmObj exception,u_long raise_flags)2325 ScmObj Scm_VMThrowException(ScmVM *vm, ScmObj exception, u_long raise_flags)
2326 {
2327 SCM_VM_RUNTIME_FLAG_CLEAR(vm, SCM_ERROR_BEING_HANDLED);
2328
2329 if (vm->exceptionHandler != DEFAULT_EXCEPTION_HANDLER) {
2330 vm->val0 = Scm_ApplyRec(vm->exceptionHandler, SCM_LIST1(exception));
2331 if (SCM_SERIOUS_CONDITION_P(exception)
2332 || raise_flags&SCM_RAISE_NON_CONTINUABLE) {
2333 /* the user-installed exception handler returned while it
2334 shouldn't. In order to prevent infinite loop, we should
2335 pop the erroneous handler. For now, we just reset
2336 the current exception handler. */
2337 vm->exceptionHandler = DEFAULT_EXCEPTION_HANDLER;
2338 Scm_Error("user-defined exception handler returned on non-continuable exception %S", exception);
2339 }
2340 return vm->val0;
2341 }
2342 return Scm_VMDefaultExceptionHandler(exception);
2343 }
2344
2345 /*
2346 * with-error-handler
2347 */
install_ehandler(ScmObj * args SCM_UNUSED,int nargs SCM_UNUSED,void * data)2348 static ScmObj install_ehandler(ScmObj *args SCM_UNUSED,
2349 int nargs SCM_UNUSED,
2350 void *data)
2351 {
2352 ScmEscapePoint *ep = (ScmEscapePoint*)data;
2353 ScmVM *vm = theVM;
2354 vm->exceptionHandler = DEFAULT_EXCEPTION_HANDLER;
2355 vm->escapePoint = ep;
2356 SCM_VM_RUNTIME_FLAG_CLEAR(vm, SCM_ERROR_BEING_REPORTED);
2357 return SCM_UNDEFINED;
2358 }
2359
discard_ehandler(ScmObj * args SCM_UNUSED,int nargs SCM_UNUSED,void * data)2360 static ScmObj discard_ehandler(ScmObj *args SCM_UNUSED,
2361 int nargs SCM_UNUSED,
2362 void *data)
2363 {
2364 ScmEscapePoint *ep = (ScmEscapePoint *)data;
2365 ScmVM *vm = theVM;
2366 vm->escapePoint = ep->prev;
2367 vm->exceptionHandler = ep->xhandler;
2368 if (ep->errorReporting) {
2369 SCM_VM_RUNTIME_FLAG_SET(vm, SCM_ERROR_BEING_REPORTED);
2370 }
2371 return SCM_UNDEFINED;
2372 }
2373
with_error_handler(ScmVM * vm,ScmObj handler,ScmObj thunk,int rewindBefore)2374 static ScmObj with_error_handler(ScmVM *vm, ScmObj handler,
2375 ScmObj thunk, int rewindBefore)
2376 {
2377 ScmEscapePoint *ep = SCM_NEW(ScmEscapePoint);
2378
2379 /* NB: we can save pointer to the stack area (vm->cont) to ep->cont,
2380 * since such ep is always accessible via vm->escapePoint chain and
2381 * ep->cont is redirected whenever the continuation is captured while
2382 * ep is valid.
2383 */
2384 ep->prev = vm->escapePoint;
2385 ep->floating = SCM_VM_FLOATING_EP(vm);
2386 ep->ehandler = handler;
2387 ep->cont = vm->cont;
2388 ep->handlers = vm->handlers;
2389 ep->cstack = vm->cstack;
2390 ep->xhandler = vm->exceptionHandler;
2391 ep->resetChain = vm->resetChain;
2392 ep->partHandlers = SCM_NIL;
2393 ep->errorReporting =
2394 SCM_VM_RUNTIME_FLAG_IS_SET(vm, SCM_ERROR_BEING_REPORTED);
2395 ep->rewindBefore = rewindBefore;
2396 ep->reraised = FALSE;
2397
2398 vm->escapePoint = ep; /* This will be done in install_ehandler, but
2399 make sure ep is visible from save_cont
2400 to redirect ep->cont */
2401 ScmObj before = Scm_MakeSubr(install_ehandler, ep, 0, 0, SCM_FALSE);
2402 ScmObj after = Scm_MakeSubr(discard_ehandler, ep, 0, 0, SCM_FALSE);
2403 return Scm_VMDynamicWind(before, thunk, after);
2404 }
2405
Scm_VMWithErrorHandler(ScmObj handler,ScmObj thunk)2406 ScmObj Scm_VMWithErrorHandler(ScmObj handler, ScmObj thunk)
2407 {
2408 return with_error_handler(theVM, handler, thunk, FALSE);
2409 }
2410
Scm_VMWithGuardHandler(ScmObj handler,ScmObj thunk)2411 ScmObj Scm_VMWithGuardHandler(ScmObj handler, ScmObj thunk)
2412 {
2413 return with_error_handler(theVM, handler, thunk, TRUE);
2414 }
2415
Scm_VMReraise()2416 ScmObj Scm_VMReraise()
2417 {
2418 ScmEscapePoint *ep = SCM_VM_FLOATING_EP(theVM);
2419 if (ep) ep->reraised = TRUE;
2420 return SCM_UNDEFINED;
2421 }
2422
2423 /*
2424 * with-exception-handler
2425 *
2426 * This primitive gives the programmer whole responsibility of
2427 * dealing with exceptions.
2428 */
2429
install_xhandler(ScmObj * args SCM_UNUSED,int nargs SCM_UNUSED,void * data)2430 static ScmObj install_xhandler(ScmObj *args SCM_UNUSED,
2431 int nargs SCM_UNUSED,
2432 void *data)
2433 {
2434 theVM->exceptionHandler = SCM_OBJ(data);
2435 return SCM_UNDEFINED;
2436 }
2437
Scm_VMWithExceptionHandler(ScmObj handler,ScmObj thunk)2438 ScmObj Scm_VMWithExceptionHandler(ScmObj handler, ScmObj thunk)
2439 {
2440 ScmObj current = theVM->exceptionHandler;
2441 ScmObj before = Scm_MakeSubr(install_xhandler, handler, 0, 0, SCM_FALSE);
2442 ScmObj after = Scm_MakeSubr(install_xhandler, current, 0, 0, SCM_FALSE);
2443 return Scm_VMDynamicWind(before, thunk, after);
2444 }
2445
2446 /*==============================================================
2447 * Call With Current Continuation
2448 */
2449
2450 /* Figure out which before and after thunk should be called.
2451
2452 In general, handler chains consist a tree. For example,
2453 we capture a continuatoin at the chain F, then we the chain
2454 was popped to C, and we create other chains. Now we want to
2455 invoke the captured continuation. We have a handler tree like this:
2456
2457
2458 current -> I -- H -- G
2459 \
2460 C -- B -- A
2461 /
2462 target -> F -- E -- D
2463
2464
2465 Here, target is the captured continuation's chain, and current is
2466 the current head of the chain.
2467
2468 We have to call the following handlers in this order:
2469
2470 I's after handler
2471 H's after handler
2472 G's after handler
2473 D's before handler
2474 E's before handler
2475 F's before handler
2476
2477 Returns a list of (before-flag <handler> . <handler-chain>).
2478 The before-flag is used to determine when handler chain is updated.
2479 (for 'before' handler, handler chain should be updated after calling it.
2480 for 'after' handler, handler chain should be updated before calling it.)
2481 The <handler-chain> is the state of handlers on which <handler> should
2482 be executed. */
throw_cont_calculate_handlers(ScmObj target,ScmObj current)2483 static ScmObj throw_cont_calculate_handlers(ScmObj target, ScmObj current)
2484 {
2485 ScmObj h = SCM_NIL, t = SCM_NIL, p;
2486 ScmObj h2 = SCM_NIL;
2487
2488 /* shortcut */
2489 if (target == current) return SCM_NIL;
2490
2491 SCM_FOR_EACH(p, current) {
2492 SCM_ASSERT(SCM_PAIRP(SCM_CAR(p)));
2493 if (!SCM_FALSEP(Scm_Memq(SCM_CAR(p), target))) break;
2494 /* push 'after' handlers to be called */
2495 SCM_APPEND1(h, t, Scm_Cons(SCM_FALSE, Scm_Cons(SCM_CDAR(p), SCM_CDR(p))));
2496 }
2497 SCM_FOR_EACH(p, target) {
2498 SCM_ASSERT(SCM_PAIRP(SCM_CAR(p)));
2499 if (!SCM_FALSEP(Scm_Memq(SCM_CAR(p), current))) break;
2500 /* push 'before' handlers to be called */
2501 h2 = Scm_Cons(Scm_Cons(SCM_TRUE, Scm_Cons(SCM_CAAR(p), p)), h2);
2502 }
2503 SCM_APPEND(h, t, h2);
2504 return h;
2505 }
2506
call_dynamic_handlers(ScmObj target,ScmObj current)2507 static void call_dynamic_handlers(ScmObj target, ScmObj current)
2508 {
2509 ScmVM *vm = theVM;
2510 ScmObj handlers_to_call = throw_cont_calculate_handlers(target, current);
2511 ScmObj p;
2512 SCM_FOR_EACH(p, handlers_to_call) {
2513 ScmObj before_flag = SCM_CAAR(p);
2514 ScmObj handler = SCM_CADR(SCM_CAR(p));
2515 ScmObj chain = SCM_CDDR(SCM_CAR(p));
2516 if (SCM_FALSEP(before_flag)) vm->handlers = chain;
2517 Scm_ApplyRec(handler, SCM_NIL);
2518 if (!SCM_FALSEP(before_flag)) vm->handlers = chain;
2519 }
2520 }
2521
2522 static ScmObj throw_cont_cc(ScmObj, void **);
2523
throw_cont_body(ScmObj handlers,ScmEscapePoint * ep,ScmObj args)2524 static ScmObj throw_cont_body(ScmObj handlers, /* after/before thunks
2525 to be called */
2526 ScmEscapePoint *ep, /* target continuation */
2527 ScmObj args) /* args to pass to the
2528 target continuation */
2529 {
2530 void *data[4];
2531 int nargs;
2532 ScmObj ap;
2533 ScmVM *vm = theVM;
2534
2535 /*
2536 * first, check to see if we need to evaluate dynamic handlers.
2537 */
2538 if (SCM_PAIRP(handlers)) {
2539 SCM_ASSERT(SCM_PAIRP(SCM_CAR(handlers)));
2540 ScmObj before_flag = SCM_CAAR(handlers);
2541 ScmObj handler = SCM_CADR(SCM_CAR(handlers));
2542 ScmObj chain = SCM_CDDR(SCM_CAR(handlers));
2543
2544 data[0] = (void*)SCM_CDR(handlers);
2545 data[1] = (void*)ep;
2546 data[2] = (void*)args;
2547 data[3] = (void*)(SCM_FALSEP(before_flag)? NULL : chain);
2548 Scm_VMPushCC(throw_cont_cc, data, 4);
2549 if (SCM_FALSEP(before_flag)) vm->handlers = chain;
2550 return Scm_VMApply0(handler);
2551 }
2552
2553 /*
2554 * If the target continuation is a full continuation, we can abandon
2555 * the current continuation. However, if the target continuation is
2556 * partial, we must return to the current continuation after executing
2557 * the partial continuation. The returning part is handled by
2558 * user_level_inner, but we have to make sure that our current continuation
2559 * won't be overwritten by execution of the partial continuation.
2560 *
2561 * NB: As an exception case, if we'll jump into reset,
2562 * we might reach to the end of partial continuation even though
2563 * the target continuation is a full continuation.
2564 */
2565 if (ep->cstack == NULL || SCM_PAIRP(ep->resetChain)) {
2566 save_cont(vm);
2567 }
2568
2569 /*
2570 * now, install the target continuation
2571 */
2572 vm->pc = PC_TO_RETURN;
2573 vm->cont = ep->cont;
2574 /* restore reset-chain for reset/shift */
2575 if (ep->cstack) vm->resetChain = ep->resetChain;
2576
2577 nargs = Scm_Length(args);
2578 if (nargs == 1) {
2579 return SCM_CAR(args);
2580 } else if (nargs < 1) {
2581 vm->numVals = 0;
2582 return SCM_UNDEFINED;
2583 } else if (nargs >= SCM_VM_MAX_VALUES) {
2584 Scm_Error("too many values passed to the continuation");
2585 }
2586
2587 ap = SCM_CDR(args);
2588 for (int i=0; SCM_PAIRP(ap); i++, ap=SCM_CDR(ap)) {
2589 vm->vals[i] = SCM_CAR(ap);
2590 }
2591 vm->numVals = nargs;
2592 return SCM_CAR(args);
2593 }
2594
throw_cont_cc(ScmObj result SCM_UNUSED,void ** data)2595 static ScmObj throw_cont_cc(ScmObj result SCM_UNUSED, void **data)
2596 {
2597 ScmVM *vm = theVM;
2598
2599 ScmObj handlers = SCM_OBJ(data[0]);
2600 ScmEscapePoint *ep = (ScmEscapePoint *)data[1];
2601 ScmObj args = SCM_OBJ(data[2]);
2602 if (data[3]) {
2603 ScmObj chain = SCM_OBJ(data[3]);
2604 vm->handlers = chain;
2605 }
2606 return throw_cont_body(handlers, ep, args);
2607 }
2608
2609 /* Body of the continuation SUBR */
throw_continuation(ScmObj * argframe,int nargs SCM_UNUSED,void * data)2610 static ScmObj throw_continuation(ScmObj *argframe,
2611 int nargs SCM_UNUSED, void *data)
2612 {
2613 ScmEscapePoint *ep = (ScmEscapePoint*)data;
2614 ScmObj args = argframe[0];
2615 ScmVM *vm = theVM;
2616
2617 /* First, check to see if we need to rewind C stack.
2618 NB: If we are invoking a partial continuation (ep->cstack == NULL),
2619 we execute it on the current cstack. */
2620 if (ep->cstack && vm->cstack != ep->cstack) {
2621 ScmCStack *cs;
2622 for (cs = vm->cstack; cs; cs = cs->prev) {
2623 if (ep->cstack == cs) break;
2624 }
2625
2626 /* If the continuation captured below the current C stack, we rewind
2627 to the captured stack first. */
2628 if (cs != NULL) {
2629 vm->escapeReason = SCM_VM_ESCAPE_CONT;
2630 vm->escapeData[0] = ep;
2631 vm->escapeData[1] = args;
2632 siglongjmp(vm->cstack->jbuf, 1);
2633 }
2634 /* If we're here, the continuation is 'ghost'---it was captured on
2635 a C stack that no longer exists, or that was in another thread.
2636 We'll execute the Scheme part of such a ghost continuation
2637 on the current C stack. User_eval_inner will catch if we
2638 ever try to return to the stale C frame.
2639
2640 Note that current vm->cstack chain may point to a continuation
2641 frame in vm stack, so we need to save the continuation chain
2642 first, since vm->cstack may be popped when other continuation
2643 is invoked during the execution of the target one. (We may be
2644 able to save some memory access by checking vm->cstack chain to see
2645 if we really have such a frame, but just calling save_cont is
2646 easier and always safe.)
2647 */
2648 save_cont(vm);
2649 }
2650
2651 /* check reset-chain to avoid the wrong return from partial
2652 continuation */
2653 if (ep->cstack == NULL && !SCM_PAIRP(ep->resetChain)) {
2654 Scm_Error("reset missing.");
2655 }
2656
2657 ScmObj handlers_to_call;
2658 if (ep->cstack) {
2659 /* for full continuation */
2660 handlers_to_call = throw_cont_calculate_handlers(ep->handlers,
2661 vm->handlers);
2662 } else {
2663 /* for partial continuation */
2664 handlers_to_call
2665 = throw_cont_calculate_handlers(Scm_Append2(ep->partHandlers,
2666 vm->handlers),
2667 vm->handlers);
2668 }
2669 return throw_cont_body(handlers_to_call, ep, args);
2670 }
2671
Scm_VMCallCC(ScmObj proc)2672 ScmObj Scm_VMCallCC(ScmObj proc)
2673 {
2674 ScmVM *vm = theVM;
2675
2676 save_cont(vm);
2677 ScmEscapePoint *ep = SCM_NEW(ScmEscapePoint);
2678 ep->prev = NULL;
2679 ep->ehandler = SCM_FALSE;
2680 ep->cont = vm->cont;
2681 ep->handlers = vm->handlers;
2682 ep->cstack = vm->cstack;
2683 ep->resetChain = vm->resetChain;
2684 ep->partHandlers = SCM_NIL;
2685
2686 ScmObj contproc = Scm_MakeSubr(throw_continuation, ep, 0, 1,
2687 SCM_MAKE_STR("continuation"));
2688 return Scm_VMApply1(proc, contproc);
2689 }
2690
2691 /* call with partial continuation. this corresponds to the 'shift' operator
2692 in shift/reset controls (Gasbichler&Sperber, "Final Shift for Call/cc",
2693 ICFP02.) Note that we treat the boundary frame as the bottom of
2694 partial continuation. */
Scm_VMCallPC(ScmObj proc)2695 ScmObj Scm_VMCallPC(ScmObj proc)
2696 {
2697 ScmVM *vm = theVM;
2698
2699 /* save the continuation. we only need to save the portion above the
2700 latest boundary frame (+environmentns pointed from them), but for now,
2701 we save everything to make things easier. If we want to squeeze
2702 performance we'll optimize it later. */
2703 save_cont(vm);
2704
2705 /* find the latest boundary frame */
2706 ScmContFrame *c, *cp;
2707 for (c = vm->cont, cp = NULL;
2708 c && !BOUNDARY_FRAME_P(c);
2709 cp = c, c = c->prev)
2710 /*empty*/;
2711
2712 /* set the end marker of partial continuation */
2713 if (cp && !MARKER_FRAME_P(cp)) {
2714 cp->marker = 1;
2715 /* also set the delimited flag in reset information */
2716 if (SCM_PAIRP(vm->resetChain)) {
2717 SCM_SET_CAR_UNCHECKED(SCM_CAR(vm->resetChain), SCM_TRUE);
2718 }
2719 }
2720
2721 ScmEscapePoint *ep = SCM_NEW(ScmEscapePoint);
2722 ep->prev = NULL;
2723 ep->ehandler = SCM_FALSE;
2724 ep->cont = (cp? vm->cont : NULL);
2725 ep->handlers = SCM_NIL; /* don't use for partial continuation */
2726 ep->cstack = NULL; /* so that the partial continuation can be run
2727 on any cstack state. */
2728 ep->resetChain = (SCM_PAIRP(vm->resetChain)?
2729 Scm_Cons(Scm_Cons(SCM_FALSE, SCM_NIL), SCM_NIL) :
2730 SCM_NIL); /* used only to detect reset missing */
2731 ep->partHandlers = SCM_NIL;
2732
2733 /* get the dynamic handlers chain saved on reset */
2734 ScmObj reset_handlers = (SCM_PAIRP(vm->resetChain)?
2735 SCM_CDAR(vm->resetChain) : SCM_NIL);
2736
2737 /* cut the dynamic handlers chain from current to reset */
2738 ScmObj h = SCM_NIL, t = SCM_NIL, p;
2739 SCM_FOR_EACH(p, vm->handlers) {
2740 if (p == reset_handlers) break;
2741 SCM_APPEND1(h, t, SCM_CAR(p));
2742 }
2743 ep->partHandlers = h;
2744
2745 /* call dynamic handlers for exiting reset */
2746 call_dynamic_handlers(reset_handlers, vm->handlers);
2747
2748 ScmObj contproc = Scm_MakeSubr(throw_continuation, ep, 0, 1,
2749 SCM_MAKE_STR("continuation"));
2750 /* Remove the saved continuation chain.
2751 NB: vm->cont can be NULL if we've been executing a partial continuation.
2752 It's ok, for a continuation pointed by cstack will be restored
2753 in user_eval_inner.
2754 NB: If the delimited flag in reset information is not set,
2755 we can consider we've been executing a partial continuation. */
2756 if (cp && (SCM_PAIRP(vm->resetChain) &&
2757 SCM_FALSEP(SCM_CAAR(vm->resetChain)))) {
2758 vm->cont = NULL;
2759 } else {
2760 vm->cont = c;
2761 }
2762
2763 return Scm_VMApply1(proc, contproc);
2764 }
2765
Scm_VMReset(ScmObj proc)2766 ScmObj Scm_VMReset(ScmObj proc)
2767 {
2768 ScmVM *vm = theVM;
2769
2770 /* push/pop reset-chain for reset/shift */
2771 vm->resetChain = Scm_Cons(Scm_Cons(SCM_FALSE, vm->handlers),
2772 vm->resetChain);
2773 ScmObj ret = Scm_ApplyRec(proc, SCM_NIL);
2774 SCM_ASSERT(SCM_PAIRP(vm->resetChain));
2775 vm->resetChain = SCM_CDR(vm->resetChain);
2776 return ret;
2777 }
2778
2779 /*==============================================================
2780 * Unwind protect API
2781 */
2782
Scm_VMUnwindProtect(ScmVM * vm,ScmCStack * cstack)2783 long Scm_VMUnwindProtect(ScmVM *vm, ScmCStack *cstack)
2784 {
2785 cstack->prev = vm->cstack;
2786 cstack->cont = NULL;
2787 vm->cstack = cstack;
2788 return sigsetjmp(cstack->jbuf, FALSE);
2789 }
2790
Scm_VMNextHandler(ScmVM * vm)2791 void Scm_VMNextHandler(ScmVM *vm)
2792 {
2793 if (vm->cstack->prev) {
2794 vm->cstack = vm->cstack->prev;
2795 siglongjmp(vm->cstack->jbuf, 1);
2796 } else {
2797 Scm_Exit(1);
2798 }
2799 }
2800
Scm_VMRewindProtect(ScmVM * vm)2801 void Scm_VMRewindProtect(ScmVM *vm)
2802 {
2803 SCM_ASSERT(vm->cstack);
2804 vm->cstack = vm->cstack->prev;
2805 }
2806
2807 /*==============================================================
2808 * Values
2809 */
2810
Scm_VMValues(ScmVM * vm,ScmObj args)2811 ScmObj Scm_VMValues(ScmVM *vm, ScmObj args)
2812 {
2813 if (!SCM_PAIRP(args)) {
2814 vm->numVals = 0;
2815 return SCM_UNDEFINED;
2816 }
2817 int nvals = 1;
2818 ScmObj cp;
2819 SCM_FOR_EACH(cp, SCM_CDR(args)) {
2820 vm->vals[nvals-1] = SCM_CAR(cp);
2821 if (nvals++ >= SCM_VM_MAX_VALUES) {
2822 Scm_Error("too many values: %S", args);
2823 }
2824 }
2825 vm->numVals = nvals;
2826 return SCM_CAR(args);
2827 }
2828
Scm_Values(ScmObj args)2829 ScmObj Scm_Values(ScmObj args)
2830 {
2831 return Scm_VMValues(theVM, args);
2832 }
2833
Scm_VMValues2(ScmVM * vm,ScmObj val0,ScmObj val1)2834 ScmObj Scm_VMValues2(ScmVM *vm, ScmObj val0, ScmObj val1)
2835 {
2836 vm->numVals = 2;
2837 vm->vals[0] = val1;
2838 return val0;
2839 }
2840
Scm_Values2(ScmObj val0,ScmObj val1)2841 ScmObj Scm_Values2(ScmObj val0, ScmObj val1)
2842 {
2843 return Scm_VMValues2(theVM, val0, val1);
2844 }
2845
Scm_VMValues3(ScmVM * vm,ScmObj val0,ScmObj val1,ScmObj val2)2846 ScmObj Scm_VMValues3(ScmVM *vm, ScmObj val0, ScmObj val1, ScmObj val2)
2847 {
2848 vm->numVals = 3;
2849 vm->vals[0] = val1;
2850 vm->vals[1] = val2;
2851 return val0;
2852 }
2853
Scm_Values3(ScmObj val0,ScmObj val1,ScmObj val2)2854 ScmObj Scm_Values3(ScmObj val0, ScmObj val1, ScmObj val2)
2855 {
2856 return Scm_VMValues3(theVM, val0, val1, val2);
2857 }
2858
2859
Scm_VMValues4(ScmVM * vm,ScmObj val0,ScmObj val1,ScmObj val2,ScmObj val3)2860 ScmObj Scm_VMValues4(ScmVM *vm, ScmObj val0, ScmObj val1,
2861 ScmObj val2, ScmObj val3)
2862 {
2863 vm->numVals = 4;
2864 vm->vals[0] = val1;
2865 vm->vals[1] = val2;
2866 vm->vals[2] = val3;
2867 return val0;
2868 }
2869
Scm_Values4(ScmObj val0,ScmObj val1,ScmObj val2,ScmObj val3)2870 ScmObj Scm_Values4(ScmObj val0, ScmObj val1, ScmObj val2, ScmObj val3)
2871 {
2872 return Scm_VMValues4(theVM, val0, val1, val2, val3);
2873 }
2874
Scm_VMValues5(ScmVM * vm,ScmObj val0,ScmObj val1,ScmObj val2,ScmObj val3,ScmObj val4)2875 ScmObj Scm_VMValues5(ScmVM *vm, ScmObj val0, ScmObj val1,
2876 ScmObj val2, ScmObj val3, ScmObj val4)
2877 {
2878 vm->numVals = 5;
2879 vm->vals[0] = val1;
2880 vm->vals[1] = val2;
2881 vm->vals[2] = val3;
2882 vm->vals[3] = val4;
2883 return val0;
2884 }
2885
Scm_Values5(ScmObj val0,ScmObj val1,ScmObj val2,ScmObj val3,ScmObj val4)2886 ScmObj Scm_Values5(ScmObj val0, ScmObj val1,
2887 ScmObj val2, ScmObj val3, ScmObj val4)
2888 {
2889 return Scm_VMValues5(theVM, val0, val1, val2, val3, val4);
2890 }
2891
Scm_VMValuesFromArray(ScmVM * vm,ScmObj * argv,ScmSmallInt argc)2892 ScmObj Scm_VMValuesFromArray(ScmVM *vm, ScmObj *argv, ScmSmallInt argc)
2893 {
2894 if (argc == 0) {
2895 vm->numVals = 0;
2896 return SCM_UNDEFINED;
2897 }
2898 for (ScmSmallInt i=1; i<argc; i++) {
2899 if (i >= SCM_VM_MAX_VALUES) {
2900 Scm_Error("too many values (%d)", argc);
2901 }
2902 vm->vals[i-1] = argv[i];
2903 }
2904 vm->numVals = argc;
2905 return argv[0];
2906 }
2907
Scm_ValuesFromArray(ScmObj * argv,ScmSmallInt argc)2908 ScmObj Scm_ValuesFromArray(ScmObj *argv, ScmSmallInt argc)
2909 {
2910 return Scm_VMValuesFromArray(theVM, argv, argc);
2911 }
2912
2913 /*==================================================================
2914 * Queued handler processing.
2915 */
2916
2917 /* Signal handlers and finalizers are queued in VM when they
2918 * are requested, and processed when VM is in consistent state.
2919 * process_queued_requests() are called near the beginning of
2920 * VM loop, when the VM checks if there's any queued request.
2921 *
2922 * When this procedure is called, VM is in middle of any two
2923 * VM instructions. We need to make sure the handlers won't
2924 * disturb the VM state.
2925 *
2926 * Conceptually, this procedure inserts handler invocations before
2927 * the current continuation.
2928 */
2929
process_queued_requests_cc(ScmObj result SCM_UNUSED,void ** data)2930 static ScmObj process_queued_requests_cc(ScmObj result SCM_UNUSED, void **data)
2931 {
2932 /* restore the saved continuation of normal execution flow of VM */
2933 ScmVM *vm = theVM;
2934
2935 vm->numVals = (int)(intptr_t)data[0];
2936 vm->val0 = data[1];
2937 if (vm->numVals > 1) {
2938 ScmObj cp = SCM_OBJ(data[2]);
2939 for (int i=0; i<vm->numVals-1; i++) {
2940 vm->vals[i] = SCM_CAR(cp);
2941 cp = SCM_CDR(cp);
2942 }
2943 }
2944 return vm->val0;
2945 }
2946
process_queued_requests(ScmVM * vm)2947 static void process_queued_requests(ScmVM *vm)
2948 {
2949 void *data[3];
2950
2951 /* preserve the current continuation */
2952 data[0] = (void*)(intptr_t)vm->numVals;
2953 data[1] = vm->val0;
2954 if (vm->numVals > 1) {
2955 ScmObj h = SCM_NIL, t = SCM_NIL;
2956
2957 for (int i=0; i<vm->numVals-1; i++) {
2958 SCM_APPEND1(h, t, vm->vals[i]);
2959 }
2960 data[2] = h;
2961 } else {
2962 data[2] = NULL;
2963 }
2964 Scm_VMPushCC(process_queued_requests_cc, data, 3);
2965
2966 /* NB: it is safe to turn off attentionRequest here; if attentionRequest
2967 is turned on again after this and before SigCheck() or FinalizerRun(),
2968 the new request is processed within these procedures; we'll enter
2969 process_queued_requests() again without anything to process, but
2970 that's an acceptable overhead. */
2971 vm->attentionRequest = FALSE;
2972
2973 /* Process queued stuff. Currently they call VM recursively,
2974 but we'd better to arrange them to be processed in the same
2975 VM level. */
2976 if (vm->signalPending) Scm_SigCheck(vm);
2977 if (vm->finalizerPending) Scm_VMFinalizerRun(vm);
2978
2979 /* VM STOP is required from other thread.
2980 See Scm_ThreadStop() in ext/threads/threads.c */
2981 if (vm->stopRequest) {
2982 SCM_INTERNAL_MUTEX_SAFE_LOCK_BEGIN(vm->vmlock);
2983 switch (vm->stopRequest) {
2984 case 0:
2985 /* stopRequest is canceled between the last check and
2986 LOCK_BEGIN. We do nothing. */
2987 break;
2988 case SCM_VM_REQUEST_SUSPEND:
2989 vm->stopRequest = 0;
2990 vm->state = SCM_VM_STOPPED;
2991 (void)SCM_INTERNAL_COND_BROADCAST(vm->cond);
2992 while (vm->state == SCM_VM_STOPPED) {
2993 /* Here the inspector thread examines VM state */
2994 (void)SCM_INTERNAL_COND_WAIT(vm->cond, vm->vmlock);
2995 }
2996 break;
2997 case SCM_VM_REQUEST_TERMINATE:
2998 vm->state = SCM_VM_TERMINATED;
2999 break;
3000 default:
3001 Scm_Panic("Unknown value in vm->stopRequest (%d). Aborting.",
3002 vm->stopRequest);
3003 }
3004 SCM_INTERNAL_MUTEX_SAFE_LOCK_END();
3005 if (vm->state == SCM_VM_TERMINATED) {
3006 SCM_INTERNAL_THREAD_EXIT(); /* this'll notify vm->cond. */
3007 }
3008 }
3009 }
3010
3011 /*==============================================================
3012 * Debug features.
3013 */
3014
3015 /*
3016 * Stack trace.
3017 *
3018 * The "lite" version returns a list of source information of
3019 * continuation frames.
3020 *
3021 * The full stack trace is consisted by a list of pair of
3022 * source information and environment vector. Environment vector
3023 * is a copy of content of env frame, with the first element
3024 * be the environment info. Environment vector may be #f if
3025 * the continuation frame doesn't have associated env.
3026 */
3027
Scm_VMGetStackLite(ScmVM * vm)3028 ScmObj Scm_VMGetStackLite(ScmVM *vm)
3029 {
3030 ScmContFrame *c = vm->cont;
3031 ScmObj stack = SCM_NIL, tail = SCM_NIL;
3032 ScmObj info = Scm_VMGetSourceInfo(vm->base, vm->pc);
3033 if (!SCM_FALSEP(info)) SCM_APPEND1(stack, tail, info);
3034 while (c) {
3035 info = Scm_VMGetSourceInfo(c->base, c->cpc);
3036 if (!SCM_FALSEP(info)) SCM_APPEND1(stack, tail, info);
3037 c = c->prev;
3038 }
3039 return stack;
3040 }
3041
3042 /*
3043 * Call trace
3044 */
Scm_SetCallTraceSize(u_long size)3045 void Scm_SetCallTraceSize(u_long size)
3046 {
3047 vm_call_trace_size = size;
3048 }
3049
Scm__MakeCallTraceQueue(u_long size)3050 ScmCallTrace *Scm__MakeCallTraceQueue(u_long size)
3051 {
3052 if (size > CALL_TRACE_SIZE_MAX) size = CALL_TRACE_SIZE_MAX;
3053 else if (size < CALL_TRACE_SIZE_MIN) size = CALL_TRACE_SIZE_MIN;
3054 else {
3055 u_long n = 1;
3056 while (n < size) n <<= 1; /* never overflow as we check the size above */
3057 size = n;
3058 }
3059
3060 ScmCallTrace *ct = SCM_NEW2(ScmCallTrace*,
3061 sizeof(ScmCallTrace)
3062 + (size-1)*sizeof(ScmCallTraceEntry));
3063 ct->size = size;
3064 ct->top = 0;
3065 for (u_long i=0; i<size; i++) {
3066 ct->entries[i].base = NULL;
3067 ct->entries[i].pc = NULL;
3068 }
3069 return ct;
3070 }
3071
Scm_VMGetCallTraceLite(ScmVM * vm)3072 ScmObj Scm_VMGetCallTraceLite(ScmVM *vm)
3073 {
3074 ScmObj trace = SCM_NIL, tail = SCM_NIL;
3075 ScmCallTrace *ct = vm->callTrace;
3076
3077 if (ct) {
3078 ScmObj info = Scm_VMGetSourceInfo(vm->base, vm->pc);
3079 if (!SCM_FALSEP(info)) SCM_APPEND1(trace, tail, info);
3080 for (int i = ct->size - 1; i >= 0; i--) {
3081 int j = (ct->top + i) % ct->size;
3082 info = Scm_VMGetSourceInfo(ct->entries[j].base, ct->entries[j].pc);
3083 if (!SCM_FALSEP(info)) SCM_APPEND1(trace, tail, info);
3084 }
3085 }
3086 return trace;
3087 }
3088
3089 #define DEFAULT_ENV_TABLE_SIZE 64
3090
3091 struct EnvTab {
3092 struct EnvTabEntry {
3093 ScmEnvFrame *env;
3094 ScmObj vec;
3095 } entries[DEFAULT_ENV_TABLE_SIZE];
3096 int numEntries;
3097 };
3098
3099 #if 0 /* for now */
3100 static ScmObj env2vec(ScmEnvFrame *env, struct EnvTab *etab)
3101 {
3102 if (!env) return SCM_FALSE;
3103 for (int i=0; i<etab->numEntries; i++) {
3104 if (etab->entries[i].env == env) {
3105 return etab->entries[i].vec;
3106 }
3107 }
3108 ScmObj vec = Scm_MakeVector((int)env->size+2, SCM_FALSE);
3109 SCM_VECTOR_ELEMENT(vec, 0) = env2vec(env->up, etab);
3110 SCM_VECTOR_ELEMENT(vec, 1) = SCM_NIL; /*Scm_VMGetBindInfo(env->info);*/
3111 for (int i=0; i<env->size; i++) {
3112 SCM_VECTOR_ELEMENT(vec, i+2) = ENV_DATA(env, (env->size-i-1));
3113 }
3114 if (etab->numEntries < DEFAULT_ENV_TABLE_SIZE) {
3115 etab->entries[etab->numEntries].env = env;
3116 etab->entries[etab->numEntries].vec = vec;
3117 etab->numEntries++;
3118 }
3119 return vec;
3120 }
3121 #endif
3122
Scm_VMGetStack(ScmVM * vm SCM_UNUSED)3123 ScmObj Scm_VMGetStack(ScmVM *vm SCM_UNUSED /* temporary*/)
3124 {
3125 #if 0 /* for now */
3126 ScmContFrame *c = vm->cont;
3127 ScmObj stack = SCM_NIL, tail = SCM_NIL;
3128 ScmObj info, evec;
3129 struct EnvTab envTab;
3130
3131 envTab.numEntries = 0;
3132 if (SCM_PAIRP(vm->pc)) {
3133 info = Scm_VMGetSourceInfo(vm->pc);
3134 SCM_APPEND1(stack, tail, Scm_Cons(info, env2vec(vm->env, &envTab)));
3135 }
3136
3137 for (; c; c = c->prev) {
3138 if (!SCM_PAIRP(c->info)) continue;
3139 info = Scm_VMGetSourceInfo(c->info);
3140 evec = env2vec(c->env, &envTab);
3141 SCM_APPEND1(stack, tail, Scm_Cons(info, evec));
3142 }
3143 return stack;
3144 #endif
3145 return SCM_NIL;
3146 }
3147
3148 /*
3149 * Dump VM internal state.
3150 */
get_debug_info(ScmCompiledCode * base,SCM_PCTYPE pc)3151 static ScmObj get_debug_info(ScmCompiledCode *base, SCM_PCTYPE pc)
3152 {
3153 if (base == NULL
3154 || (pc < base->code || pc >= base->code + base->codeSize)) {
3155 return SCM_FALSE;
3156 }
3157 int off = (int)(pc - base->code);
3158 ScmObj ip;
3159 SCM_FOR_EACH(ip, base->debugInfo) {
3160 ScmObj p = SCM_CAR(ip);
3161 if (!SCM_PAIRP(p) || !SCM_INTP(SCM_CAR(p))) continue;
3162 /* PC points to the next instruction,
3163 search for info entry right before it. */
3164 if (SCM_INT_VALUE(SCM_CAR(p)) < off) {
3165 return SCM_CDR(p);
3166 }
3167 }
3168 return SCM_FALSE;
3169 }
3170
Scm_VMGetSourceInfo(ScmCompiledCode * base,SCM_PCTYPE pc)3171 ScmObj Scm_VMGetSourceInfo(ScmCompiledCode *base, SCM_PCTYPE pc)
3172 {
3173 ScmObj info = get_debug_info(base, pc);
3174 if (SCM_PAIRP(info)) {
3175 ScmObj p = Scm_Assq(SCM_SYM_SOURCE_INFO, info);
3176 if (SCM_PAIRP(p)) return SCM_CDR(p);
3177 }
3178 return SCM_FALSE;
3179 }
3180
Scm_VMGetBindInfo(ScmCompiledCode * base,SCM_PCTYPE pc)3181 ScmObj Scm_VMGetBindInfo(ScmCompiledCode *base, SCM_PCTYPE pc)
3182 {
3183 ScmObj info = get_debug_info(base, pc);
3184 if (SCM_PAIRP(info)) {
3185 ScmObj p = Scm_Assq(SCM_SYM_BIND_INFO, info);
3186 if (SCM_PAIRP(p)) return SCM_CDR(p);
3187 }
3188 return SCM_FALSE;
3189 }
3190
dump_env(ScmEnvFrame * env,ScmPort * out)3191 static void dump_env(ScmEnvFrame *env, ScmPort *out)
3192 {
3193 Scm_Printf(out, " %p %55.1S\n", env, env->info);
3194 Scm_Printf(out, " up=%p size=%d\n", env->up, env->size);
3195 Scm_Printf(out, " [");
3196 for (int i=0; i<env->size; i++) {
3197 Scm_Printf(out, " %S", ENV_DATA(env, i));
3198 }
3199 Scm_Printf(out, " ]\n");
3200 }
3201
3202 /* Show offset of given PC w.r.t to the beginning of the code of
3203 the current base. Note that PC may not point to the code of the
3204 base. */
dump_pc_offset(ScmWord * pc,ScmCompiledCode * base,ScmPort * out)3205 static void dump_pc_offset(ScmWord *pc, ScmCompiledCode *base, ScmPort *out)
3206 {
3207 if (base && base->code <= pc && pc < base->code + base->codeSize) {
3208 Scm_Printf(out, " [%5u(%p)]", (u_long)(pc - base->code), base->code);
3209 }
3210 }
3211
3212
Scm_VMDump(ScmVM * vm)3213 void Scm_VMDump(ScmVM *vm)
3214 {
3215 ScmPort *out = vm->curerr;
3216 ScmEnvFrame *env = vm->env;
3217 ScmContFrame *cont = vm->cont;
3218 ScmCStack *cstk = vm->cstack;
3219 ScmEscapePoint *ep = vm->escapePoint;
3220
3221 Scm_Printf(out, "VM %p -----------------------------------------------------------\n", vm);
3222 Scm_Printf(out, " pc: %p", vm->pc);
3223 dump_pc_offset(vm->pc, vm->base, out);
3224 Scm_Printf(out, " (%08x)\n", *vm->pc);
3225 Scm_Printf(out, " sp: %p [%p-%p-%p]\n", vm->sp,
3226 vm->stack, vm->stackBase, vm->stackEnd);
3227 Scm_Printf(out, " argp: %p\n", vm->argp);
3228 Scm_Printf(out, " val0: %#65.1S\n", vm->val0);
3229
3230 Scm_Printf(out, " envs:\n");
3231 while (env) {
3232 dump_env(env, out);
3233 env = env->up;
3234 }
3235
3236 Scm_Printf(out, "conts:\n");
3237 while (cont) {
3238 Scm_Printf(out, " %p\n", cont);
3239 Scm_Printf(out, " env = %p\n", cont->env);
3240 Scm_Printf(out, " size = %d\n", cont->size);
3241 if (!C_CONTINUATION_P(cont)) {
3242 Scm_Printf(out, " pc = %p", cont->pc);
3243 dump_pc_offset(cont->pc, cont->base, out);
3244 Scm_Printf(out, " (%08x)\n", *cont->pc);
3245 } else {
3246 Scm_Printf(out, " pc = {cproc %p}\n", cont->pc);
3247 }
3248 Scm_Printf(out, " base = %p\n", cont->base);
3249 cont = cont->prev;
3250 }
3251
3252 Scm_Printf(out, "C stacks:\n");
3253 while (cstk) {
3254 Scm_Printf(out, " %p: prev=%p, cont=%p\n",
3255 cstk, cstk->prev, cstk->cont);
3256 cstk = cstk->prev;
3257 }
3258 Scm_Printf(out, "Escape points:\n");
3259 while (ep) {
3260 Scm_Printf(out, " %p: cont=%p, handler=%#20.1S\n",
3261 ep, ep->cont, ep->ehandler);
3262 ep = ep->prev;
3263 }
3264 Scm_Printf(out, "dynenv: %S\n", vm->handlers);
3265 Scm_Printf(out, "reset-chain-length: %d\n", (int)Scm_Length(vm->resetChain));
3266 if (vm->base) {
3267 Scm_Printf(out, "Code:\n");
3268 Scm_CompiledCodeDump(vm->base);
3269 }
3270 }
3271
3272 #ifdef USE_CUSTOM_STACK_MARKER
vm_stack_mark(GC_word * addr,struct GC_ms_entry * mark_sp,struct GC_ms_entry * mark_sp_limit,GC_word env)3273 struct GC_ms_entry *vm_stack_mark(GC_word *addr,
3274 struct GC_ms_entry *mark_sp,
3275 struct GC_ms_entry *mark_sp_limit,
3276 GC_word env)
3277 {
3278 struct GC_ms_entry *e = mark_sp;
3279 ScmObj *vmsb = ((ScmObj*)addr)+1;
3280 ScmVM *vm = (ScmVM*)*addr;
3281 int limit = vm->sp - vm->stackBase + 5;
3282 void *spb = (void *)vm->stackBase;
3283 void *sbe = (void *)(vm->stackBase + SCM_VM_STACK_SIZE);
3284 void *hb = GC_least_plausible_heap_addr;
3285 void *he = GC_greatest_plausible_heap_addr;
3286
3287 for (int i=0; i<limit; i++, vmsb++) {
3288 ScmObj z = *vmsb;
3289 if ((hb < (void *)z && (void *)z < spb)
3290 || ((void *)z > sbe && (void *)z < he)) {
3291 e = GC_mark_and_push((void *)z, e, mark_sp_limit, (void *)addr);
3292 }
3293 }
3294 return e;
3295 }
3296 #endif /*USE_CUSTOM_STACK_MARKER*/
3297
Scm__VMInsnOffsets()3298 ScmObj Scm__VMInsnOffsets()
3299 {
3300 ScmObj v = Scm_MakeVector(SCM_VM_NUM_INSNS, SCM_FALSE);
3301 for (int i = 0; i < SCM_VM_NUM_INSNS; i++) {
3302 SCM_VECTOR_ELEMENT(v, i) = Scm_MakeIntegerU(vminsn_offsets[i]);
3303 }
3304 return v;
3305 }
3306
3307
3308 /*===============================================================
3309 * Initialization
3310 */
3311
Scm__InitVM(void)3312 void Scm__InitVM(void)
3313 {
3314 #ifdef USE_CUSTOM_STACK_MARKER
3315 vm_stack_free_list = GC_new_free_list();
3316 vm_stack_mark_proc = GC_new_proc(vm_stack_mark);
3317 vm_stack_kind = GC_new_kind(vm_stack_free_list,
3318 GC_MAKE_PROC(vm_stack_mark_proc, 0),
3319 0, 0);
3320 #endif /*USE_CUSTOM_STACK_MARKER*/
3321
3322 Scm_HashCoreInitSimple(&vm_table, SCM_HASH_EQ, 8, NULL);
3323 SCM_INTERNAL_MUTEX_INIT(vm_table_mutex);
3324 SCM_INTERNAL_MUTEX_INIT(vm_id_mutex);
3325
3326 /* Create root VM */
3327 rootVM = Scm_NewVM(NULL, SCM_MAKE_STR_IMMUTABLE("root"));
3328 rootVM->state = SCM_VM_RUNNABLE;
3329 #ifdef GAUCHE_USE_PTHREADS
3330 if (pthread_key_create(&vm_key, NULL) != 0) {
3331 Scm_Panic("pthread_key_create failed.");
3332 }
3333 if (pthread_setspecific(vm_key, rootVM) != 0) {
3334 Scm_Panic("pthread_setspecific failed.");
3335 }
3336 rootVM->thread = pthread_self();
3337 #elif GAUCHE_USE_WTHREADS
3338 vm_key = TlsAlloc();
3339 if (vm_key == TLS_OUT_OF_INDEXES) {
3340 Scm_Panic("TlsAlloc failed");
3341 }
3342 if (!TlsSetValue(vm_key, (LPVOID)rootVM)) {
3343 Scm_Panic("TlsSetValue failed");
3344 }
3345 rootVM->thread = GetCurrentThread();
3346 #else /* no threads */
3347 theVM = rootVM;
3348 #endif /* no threads */
3349
3350 #ifdef COUNT_INSN_FREQUENCY
3351 Scm_AddCleanupHandler(dump_insn_frequency, NULL);
3352 #endif /*COUNT_INSN_FREQUENCY*/
3353
3354 #ifdef COUNT_FLUSH_FPSTACK
3355 Scm_AddCleanupHandler(print_flush_fpstack_count, NULL);
3356 #endif
3357
3358 if (Scm_GetEnv("GAUCHE_CHECK_UNDEFINED_TEST") != NULL) {
3359 SCM_VM_RUNTIME_FLAG_SET(rootVM, SCM_CHECK_UNDEFINED_TEST);
3360 }
3361 if (Scm_GetEnv("GAUCHE_LEGACY_DEFINE") != NULL) {
3362 SCM_VM_COMPILER_FLAG_SET(rootVM, SCM_COMPILE_LEGACY_DEFINE);
3363 }
3364 if (Scm_GetEnv("GAUCHE_MUTABLE_LITERALS") != NULL) {
3365 SCM_VM_COMPILER_FLAG_SET(rootVM, SCM_COMPILE_MUTABLE_LITERALS);
3366 }
3367 /* NB: In 0.9.10, we warn srfi-N feature ID only when requested.
3368 We'll reverse the default in the later releases. */
3369 SCM_VM_COMPILER_FLAG_SET(rootVM, SCM_COMPILE_SRFI_FEATURE_ID);
3370 if (Scm_GetEnv("GAUCHE_WARN_SRFI_FEATURE_ID") != NULL) {
3371 SCM_VM_COMPILER_FLAG_CLEAR(rootVM, SCM_COMPILE_SRFI_FEATURE_ID);
3372 }
3373 else if (Scm_GetEnv("GAUCHE_ALLOW_SRFI_FEATURE_ID") != NULL) {
3374 SCM_VM_COMPILER_FLAG_SET(rootVM, SCM_COMPILE_SRFI_FEATURE_ID);
3375 }
3376 }
3377