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