allow `apply' on %nil-terminated lists
[bpt/guile.git] / libguile / vm.c
1 /* Copyright (C) 2001 Free Software Foundation, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
40 * If you do not wish that, delete this exception notice. */
41
42 #if HAVE_CONFIG_H
43 # include <config.h>
44 #endif
45
46 #include <string.h>
47 #include "vm-bootstrap.h"
48 #include "frames.h"
49 #include "instructions.h"
50 #include "objcodes.h"
51 #include "programs.h"
52 #include "lang.h" /* NULL_OR_NIL_P */
53 #include "vm.h"
54
55 /* I sometimes use this for debugging. */
56 #define vm_puts(OBJ) \
57 { \
58 scm_display (OBJ, scm_current_error_port ()); \
59 scm_newline (scm_current_error_port ()); \
60 }
61
62 /* The VM has a number of internal assertions that shouldn't normally be
63 necessary, but might be if you think you found a bug in the VM. */
64 #define VM_ENABLE_ASSERTIONS
65
66 /* We can add a mode that ensures that all stack items above the stack pointer
67 are NULL. This is useful for checking the internal consistency of the VM's
68 assumptions and its operators, but isn't necessary for normal operation. It
69 will ensure that assertions are enabled. */
70 #define VM_ENABLE_STACK_NULLING
71
72 #if defined (VM_ENABLE_STACK_NULLING) && !defined (VM_ENABLE_ASSERTIONS)
73 #define VM_ENABLE_ASSERTIONS
74 #endif
75
76 \f
77 /*
78 * VM Continuation
79 */
80
81 scm_t_bits scm_tc16_vm_cont;
82
83 static void
84 vm_mark_stack (SCM *base, scm_t_ptrdiff size, SCM *fp, scm_t_ptrdiff reloc)
85 {
86 SCM *sp, *upper, *lower;
87 sp = base + size - 1;
88
89 while (sp > base && fp)
90 {
91 upper = SCM_FRAME_UPPER_ADDRESS (fp);
92 lower = SCM_FRAME_LOWER_ADDRESS (fp);
93
94 for (; sp >= upper; sp--)
95 if (SCM_NIMP (*sp))
96 {
97 if (scm_in_heap_p (*sp))
98 scm_gc_mark (*sp);
99 else
100 fprintf (stderr, "BADNESS: crap on the stack: %p\n", *sp);
101 }
102
103
104 /* skip ra, mvra */
105 sp -= 2;
106
107 /* update fp from the dynamic link */
108 fp = (SCM*)*sp-- + reloc;
109
110 /* mark from the el down to the lower address */
111 for (; sp >= lower; sp--)
112 if (*sp && SCM_NIMP (*sp))
113 scm_gc_mark (*sp);
114 }
115 }
116
117 static SCM
118 vm_cont_mark (SCM obj)
119 {
120 struct scm_vm_cont *p = SCM_VM_CONT_DATA (obj);
121
122 vm_mark_stack (p->stack_base, p->stack_size, p->stack_base + p->fp, p->reloc);
123
124 return SCM_BOOL_F;
125 }
126
127 static scm_sizet
128 vm_cont_free (SCM obj)
129 {
130 struct scm_vm_cont *p = SCM_VM_CONT_DATA (obj);
131
132 scm_gc_free (p->stack_base, p->stack_size * sizeof (SCM), "stack-base");
133 scm_gc_free (p, sizeof (struct scm_vm), "vm");
134
135 return 0;
136 }
137
138 static SCM
139 capture_vm_cont (struct scm_vm *vp)
140 {
141 struct scm_vm_cont *p = scm_gc_malloc (sizeof (*p), "capture_vm_cont");
142 p->stack_size = vp->sp - vp->stack_base + 1;
143 p->stack_base = scm_gc_malloc (p->stack_size * sizeof (SCM),
144 "capture_vm_cont");
145 #ifdef VM_ENABLE_STACK_NULLING
146 if (vp->sp >= vp->stack_base)
147 if (!vp->sp[0] || vp->sp[1])
148 abort ();
149 memset (p->stack_base, 0, p->stack_size * sizeof (SCM));
150 #endif
151 p->ip = vp->ip;
152 p->sp = vp->sp - vp->stack_base;
153 p->fp = vp->fp - vp->stack_base;
154 memcpy (p->stack_base, vp->stack_base, p->stack_size * sizeof (SCM));
155 p->reloc = p->stack_base - vp->stack_base;
156 SCM_RETURN_NEWSMOB (scm_tc16_vm_cont, p);
157 }
158
159 static void
160 reinstate_vm_cont (struct scm_vm *vp, SCM cont)
161 {
162 struct scm_vm_cont *p = SCM_VM_CONT_DATA (cont);
163 if (vp->stack_size < p->stack_size)
164 {
165 /* puts ("FIXME: Need to expand"); */
166 abort ();
167 }
168 #ifdef VM_ENABLE_STACK_NULLING
169 {
170 scm_t_ptrdiff nzero = (vp->sp - vp->stack_base) - p->sp;
171 if (nzero > 0)
172 memset (vp->stack_base + p->stack_size, 0, nzero * sizeof (SCM));
173 /* actually nzero should always be negative, because vm_reset_stack will
174 unwind the stack to some point *below* this continuation */
175 }
176 #endif
177 vp->ip = p->ip;
178 vp->sp = vp->stack_base + p->sp;
179 vp->fp = vp->stack_base + p->fp;
180 memcpy (vp->stack_base, p->stack_base, p->stack_size * sizeof (SCM));
181 }
182
183 /* In theory, a number of vm instances can be active in the call trace, and we
184 only want to reify the continuations of those in the current continuation
185 root. I don't see a nice way to do this -- ideally it would involve dynwinds,
186 and previous values of the *the-vm* fluid within the current continuation
187 root. But we don't have access to continuation roots in the dynwind stack.
188 So, just punt for now -- take the current value of *the-vm*.
189
190 While I'm on the topic, ideally we could avoid copying the C stack if the
191 continuation root is inside VM code, and call/cc was invoked within that same
192 call to vm_run; but that's currently not implemented.
193 */
194 SCM
195 scm_vm_capture_continuations (void)
196 {
197 SCM vm = scm_the_vm ();
198 return scm_acons (vm, capture_vm_cont (SCM_VM_DATA (vm)), SCM_EOL);
199 }
200
201 void
202 scm_vm_reinstate_continuations (SCM conts)
203 {
204 for (; conts != SCM_EOL; conts = SCM_CDR (conts))
205 reinstate_vm_cont (SCM_VM_DATA (SCM_CAAR (conts)), SCM_CDAR (conts));
206 }
207
208 struct vm_unwind_data
209 {
210 struct scm_vm *vp;
211 SCM *sp;
212 SCM *fp;
213 };
214
215 static void
216 vm_reset_stack (void *data)
217 {
218 struct vm_unwind_data *w = data;
219 struct scm_vm *vp = w->vp;
220
221 vp->sp = w->sp;
222 vp->fp = w->fp;
223 #ifdef VM_ENABLE_STACK_NULLING
224 memset (vp->sp + 1, 0, (vp->stack_size - (vp->sp + 1 - vp->stack_base)) * sizeof(SCM));
225 #endif
226 }
227
228 static void enfalsen_frame (void *p)
229 {
230 struct scm_vm *vp = p;
231 vp->trace_frame = SCM_BOOL_F;
232 }
233
234 static void
235 vm_dispatch_hook (SCM vm, SCM hook, SCM hook_args)
236 {
237 struct scm_vm *vp = SCM_VM_DATA (vm);
238
239 if (!SCM_FALSEP (vp->trace_frame))
240 return;
241
242 scm_dynwind_begin (0);
243 vp->trace_frame = scm_c_make_vm_frame (vm, vp->fp, vp->sp, vp->ip, 0);
244 scm_dynwind_unwind_handler (enfalsen_frame, vp, SCM_F_WIND_EXPLICITLY);
245
246 scm_c_run_hook (hook, hook_args);
247
248 scm_dynwind_end ();
249 }
250
251 \f
252 /*
253 * VM Internal functions
254 */
255
256 static SCM sym_vm_run;
257 static SCM sym_vm_error;
258 static SCM sym_debug;
259
260 static scm_byte_t *
261 vm_fetch_length (scm_byte_t *ip, size_t *lenp)
262 {
263 /* NOTE: format defined in system/vm/conv.scm */
264 *lenp = *ip++;
265 if (*lenp < 254)
266 return ip;
267 else if (*lenp == 254)
268 {
269 int b1 = *ip++;
270 int b2 = *ip++;
271 *lenp = (b1 << 8) + b2;
272 }
273 else
274 {
275 int b1 = *ip++;
276 int b2 = *ip++;
277 int b3 = *ip++;
278 int b4 = *ip++;
279 *lenp = (b1 << 24) + (b2 << 16) + (b3 << 8) + b4;
280 }
281 return ip;
282 }
283
284 \f
285 /*
286 * VM
287 */
288
289 #define VM_DEFAULT_STACK_SIZE (16 * 1024)
290
291 #define VM_REGULAR_ENGINE 0
292 #define VM_DEBUG_ENGINE 1
293
294 #if 0
295 #define VM_NAME vm_regular_engine
296 #define VM_ENGINE VM_REGULAR_ENGINE
297 #include "vm-engine.c"
298 #undef VM_NAME
299 #undef VM_ENGINE
300 #endif
301
302 #define VM_NAME vm_debug_engine
303 #define VM_ENGINE VM_DEBUG_ENGINE
304 #include "vm-engine.c"
305 #undef VM_NAME
306 #undef VM_ENGINE
307
308 scm_t_bits scm_tc16_vm;
309
310 static SCM
311 make_vm (void)
312 #define FUNC_NAME "make_vm"
313 {
314 int i;
315 struct scm_vm *vp = scm_gc_malloc (sizeof (struct scm_vm), "vm");
316
317 vp->stack_size = VM_DEFAULT_STACK_SIZE;
318 vp->stack_base = scm_gc_malloc (vp->stack_size * sizeof (SCM),
319 "stack-base");
320 #ifdef VM_ENABLE_STACK_NULLING
321 memset (vp->stack_base, 0, vp->stack_size * sizeof (SCM));
322 #endif
323 vp->stack_limit = vp->stack_base + vp->stack_size - 3;
324 vp->ip = NULL;
325 vp->sp = vp->stack_base - 1;
326 vp->fp = NULL;
327 vp->time = 0;
328 vp->clock = 0;
329 vp->options = SCM_EOL;
330 for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
331 vp->hooks[i] = SCM_BOOL_F;
332 vp->trace_frame = SCM_BOOL_F;
333 SCM_RETURN_NEWSMOB (scm_tc16_vm, vp);
334 }
335 #undef FUNC_NAME
336
337 static SCM
338 vm_mark (SCM obj)
339 {
340 int i;
341 struct scm_vm *vp = SCM_VM_DATA (obj);
342
343 #ifdef VM_ENABLE_STACK_NULLING
344 if (vp->sp >= vp->stack_base)
345 if (!vp->sp[0] || vp->sp[1])
346 abort ();
347 #endif
348
349 /* mark the stack, precisely */
350 vm_mark_stack (vp->stack_base, vp->sp + 1 - vp->stack_base, vp->fp, 0);
351
352 /* mark other objects */
353 for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
354 scm_gc_mark (vp->hooks[i]);
355
356 scm_gc_mark (vp->trace_frame);
357
358 return vp->options;
359 }
360
361 static scm_sizet
362 vm_free (SCM obj)
363 {
364 struct scm_vm *vp = SCM_VM_DATA (obj);
365
366 scm_gc_free (vp->stack_base, vp->stack_size * sizeof (SCM),
367 "stack-base");
368 scm_gc_free (vp, sizeof (struct scm_vm), "vm");
369
370 return 0;
371 }
372
373 SCM
374 scm_vm_apply (SCM vm, SCM program, SCM args)
375 #define FUNC_NAME "scm_vm_apply"
376 {
377 SCM_VALIDATE_PROGRAM (1, program);
378 return vm_run (vm, program, args);
379 }
380 #undef FUNC_NAME
381
382 /* Scheme interface */
383
384 SCM_DEFINE (scm_vm_version, "vm-version", 0, 0, 0,
385 (void),
386 "")
387 #define FUNC_NAME s_scm_vm_version
388 {
389 return scm_from_locale_string (PACKAGE_VERSION);
390 }
391 #undef FUNC_NAME
392
393 SCM_DEFINE (scm_the_vm, "the-vm", 0, 0, 0,
394 (void),
395 "")
396 #define FUNC_NAME s_scm_the_vm
397 {
398 scm_i_thread *t = SCM_I_CURRENT_THREAD;
399
400 if (SCM_UNLIKELY (SCM_FALSEP ((t->vm))))
401 t->vm = make_vm ();
402
403 return t->vm;
404 }
405 #undef FUNC_NAME
406
407
408 SCM_DEFINE (scm_vm_p, "vm?", 1, 0, 0,
409 (SCM obj),
410 "")
411 #define FUNC_NAME s_scm_vm_p
412 {
413 return SCM_BOOL (SCM_VM_P (obj));
414 }
415 #undef FUNC_NAME
416
417 SCM_DEFINE (scm_make_vm, "make-vm", 0, 0, 0,
418 (void),
419 "")
420 #define FUNC_NAME s_scm_make_vm,
421 {
422 return make_vm ();
423 }
424 #undef FUNC_NAME
425
426 SCM_DEFINE (scm_vm_ip, "vm:ip", 1, 0, 0,
427 (SCM vm),
428 "")
429 #define FUNC_NAME s_scm_vm_ip
430 {
431 SCM_VALIDATE_VM (1, vm);
432 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->ip);
433 }
434 #undef FUNC_NAME
435
436 SCM_DEFINE (scm_vm_sp, "vm:sp", 1, 0, 0,
437 (SCM vm),
438 "")
439 #define FUNC_NAME s_scm_vm_sp
440 {
441 SCM_VALIDATE_VM (1, vm);
442 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->sp);
443 }
444 #undef FUNC_NAME
445
446 SCM_DEFINE (scm_vm_fp, "vm:fp", 1, 0, 0,
447 (SCM vm),
448 "")
449 #define FUNC_NAME s_scm_vm_fp
450 {
451 SCM_VALIDATE_VM (1, vm);
452 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->fp);
453 }
454 #undef FUNC_NAME
455
456 #define VM_DEFINE_HOOK(n) \
457 { \
458 struct scm_vm *vp; \
459 SCM_VALIDATE_VM (1, vm); \
460 vp = SCM_VM_DATA (vm); \
461 if (SCM_FALSEP (vp->hooks[n])) \
462 vp->hooks[n] = scm_make_hook (SCM_I_MAKINUM (1)); \
463 return vp->hooks[n]; \
464 }
465
466 SCM_DEFINE (scm_vm_boot_hook, "vm-boot-hook", 1, 0, 0,
467 (SCM vm),
468 "")
469 #define FUNC_NAME s_scm_vm_boot_hook
470 {
471 VM_DEFINE_HOOK (SCM_VM_BOOT_HOOK);
472 }
473 #undef FUNC_NAME
474
475 SCM_DEFINE (scm_vm_halt_hook, "vm-halt-hook", 1, 0, 0,
476 (SCM vm),
477 "")
478 #define FUNC_NAME s_scm_vm_halt_hook
479 {
480 VM_DEFINE_HOOK (SCM_VM_HALT_HOOK);
481 }
482 #undef FUNC_NAME
483
484 SCM_DEFINE (scm_vm_next_hook, "vm-next-hook", 1, 0, 0,
485 (SCM vm),
486 "")
487 #define FUNC_NAME s_scm_vm_next_hook
488 {
489 VM_DEFINE_HOOK (SCM_VM_NEXT_HOOK);
490 }
491 #undef FUNC_NAME
492
493 SCM_DEFINE (scm_vm_break_hook, "vm-break-hook", 1, 0, 0,
494 (SCM vm),
495 "")
496 #define FUNC_NAME s_scm_vm_break_hook
497 {
498 VM_DEFINE_HOOK (SCM_VM_BREAK_HOOK);
499 }
500 #undef FUNC_NAME
501
502 SCM_DEFINE (scm_vm_enter_hook, "vm-enter-hook", 1, 0, 0,
503 (SCM vm),
504 "")
505 #define FUNC_NAME s_scm_vm_enter_hook
506 {
507 VM_DEFINE_HOOK (SCM_VM_ENTER_HOOK);
508 }
509 #undef FUNC_NAME
510
511 SCM_DEFINE (scm_vm_apply_hook, "vm-apply-hook", 1, 0, 0,
512 (SCM vm),
513 "")
514 #define FUNC_NAME s_scm_vm_apply_hook
515 {
516 VM_DEFINE_HOOK (SCM_VM_APPLY_HOOK);
517 }
518 #undef FUNC_NAME
519
520 SCM_DEFINE (scm_vm_exit_hook, "vm-exit-hook", 1, 0, 0,
521 (SCM vm),
522 "")
523 #define FUNC_NAME s_scm_vm_exit_hook
524 {
525 VM_DEFINE_HOOK (SCM_VM_EXIT_HOOK);
526 }
527 #undef FUNC_NAME
528
529 SCM_DEFINE (scm_vm_return_hook, "vm-return-hook", 1, 0, 0,
530 (SCM vm),
531 "")
532 #define FUNC_NAME s_scm_vm_return_hook
533 {
534 VM_DEFINE_HOOK (SCM_VM_RETURN_HOOK);
535 }
536 #undef FUNC_NAME
537
538 SCM_DEFINE (scm_vm_option, "vm-option", 2, 0, 0,
539 (SCM vm, SCM key),
540 "")
541 #define FUNC_NAME s_scm_vm_option
542 {
543 SCM_VALIDATE_VM (1, vm);
544 return scm_assq_ref (SCM_VM_DATA (vm)->options, key);
545 }
546 #undef FUNC_NAME
547
548 SCM_DEFINE (scm_set_vm_option_x, "set-vm-option!", 3, 0, 0,
549 (SCM vm, SCM key, SCM val),
550 "")
551 #define FUNC_NAME s_scm_set_vm_option_x
552 {
553 SCM_VALIDATE_VM (1, vm);
554 SCM_VM_DATA (vm)->options
555 = scm_assq_set_x (SCM_VM_DATA (vm)->options, key, val);
556 return SCM_UNSPECIFIED;
557 }
558 #undef FUNC_NAME
559
560 SCM_DEFINE (scm_vm_stats, "vm-stats", 1, 0, 0,
561 (SCM vm),
562 "")
563 #define FUNC_NAME s_scm_vm_stats
564 {
565 SCM stats;
566
567 SCM_VALIDATE_VM (1, vm);
568
569 stats = scm_make_vector (SCM_I_MAKINUM (2), SCM_UNSPECIFIED);
570 scm_vector_set_x (stats, SCM_I_MAKINUM (0),
571 scm_from_ulong (SCM_VM_DATA (vm)->time));
572 scm_vector_set_x (stats, SCM_I_MAKINUM (1),
573 scm_from_ulong (SCM_VM_DATA (vm)->clock));
574
575 return stats;
576 }
577 #undef FUNC_NAME
578
579 SCM_DEFINE (scm_vm_trace_frame, "vm-trace-frame", 1, 0, 0,
580 (SCM vm),
581 "")
582 #define FUNC_NAME s_scm_vm_trace_frame
583 {
584 SCM_VALIDATE_VM (1, vm);
585 return SCM_VM_DATA (vm)->trace_frame;
586 }
587 #undef FUNC_NAME
588
589 \f
590 /*
591 * Initialize
592 */
593
594 SCM scm_load_compiled_with_vm (SCM file)
595 {
596 SCM program = scm_objcode_to_program (scm_load_objcode (file), SCM_EOL);
597
598 return vm_run (scm_the_vm (), program, SCM_EOL);
599 }
600
601 void
602 scm_bootstrap_vm (void)
603 {
604 static int strappage = 0;
605
606 if (strappage)
607 return;
608
609 scm_bootstrap_frames ();
610 scm_bootstrap_instructions ();
611 scm_bootstrap_objcodes ();
612 scm_bootstrap_programs ();
613
614 scm_tc16_vm_cont = scm_make_smob_type ("vm-cont", 0);
615 scm_set_smob_mark (scm_tc16_vm_cont, vm_cont_mark);
616 scm_set_smob_free (scm_tc16_vm_cont, vm_cont_free);
617
618 scm_tc16_vm = scm_make_smob_type ("vm", 0);
619 scm_set_smob_mark (scm_tc16_vm, vm_mark);
620 scm_set_smob_free (scm_tc16_vm, vm_free);
621 scm_set_smob_apply (scm_tc16_vm, scm_vm_apply, 1, 0, 1);
622
623 scm_c_define ("load-compiled",
624 scm_c_make_gsubr ("load-compiled/vm", 1, 0, 0,
625 scm_load_compiled_with_vm));
626
627 sym_vm_run = scm_permanent_object (scm_from_locale_symbol ("vm-run"));
628 sym_vm_error = scm_permanent_object (scm_from_locale_symbol ("vm-error"));
629 sym_debug = scm_permanent_object (scm_from_locale_symbol ("debug"));
630
631 strappage = 1;
632 }
633
634 void
635 scm_init_vm (void)
636 {
637 scm_bootstrap_vm ();
638
639 #ifndef SCM_MAGIC_SNARFER
640 #include "vm.x"
641 #endif
642 }
643
644 /*
645 Local Variables:
646 c-file-style: "gnu"
647 End:
648 */