1 /* Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 #include "libguile/bdw-gc.h"
28 #include <gc/gc_mark.h>
33 #include "instructions.h"
36 #include "lang.h" /* NULL_OR_NIL_P */
39 /* I sometimes use this for debugging. */
40 #define vm_puts(OBJ) \
42 scm_display (OBJ, scm_current_error_port ()); \
43 scm_newline (scm_current_error_port ()); \
46 /* The VM has a number of internal assertions that shouldn't normally be
47 necessary, but might be if you think you found a bug in the VM. */
48 #define VM_ENABLE_ASSERTIONS
50 /* We can add a mode that ensures that all stack items above the stack pointer
51 are NULL. This is useful for checking the internal consistency of the VM's
52 assumptions and its operators, but isn't necessary for normal operation. It
53 will ensure that assertions are enabled. Slows down the VM by about 30%. */
54 /* NB! If you enable this, search for NULLING in throw.c */
55 /* #define VM_ENABLE_STACK_NULLING */
57 /* #define VM_ENABLE_PARANOID_ASSERTIONS */
59 #if defined (VM_ENABLE_STACK_NULLING) && !defined (VM_ENABLE_ASSERTIONS)
60 #define VM_ENABLE_ASSERTIONS
63 /* When defined, arrange so that the GC doesn't scan the VM stack beyond its
64 current SP. This should help avoid excess data retention. See
65 http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/3001
67 #define VM_ENABLE_PRECISE_STACK_GC_SCAN
76 scm_i_vm_cont_print (SCM x
, SCM port
, scm_print_state
*pstate
)
78 scm_puts ("#<vm-continuation ", port
);
79 scm_uintprint (SCM_UNPACK (x
), 16, port
);
83 /* In theory, a number of vm instances can be active in the call trace, and we
84 only want to reify the continuations of those in the current continuation
85 root. I don't see a nice way to do this -- ideally it would involve dynwinds,
86 and previous values of the *the-vm* fluid within the current continuation
87 root. But we don't have access to continuation roots in the dynwind stack.
88 So, just punt for now, we just capture the continuation for the current VM.
90 While I'm on the topic, ideally we could avoid copying the C stack if the
91 continuation root is inside VM code, and call/cc was invoked within that same
92 call to vm_run; but that's currently not implemented.
95 scm_i_vm_capture_stack (SCM
*stack_base
, SCM
*fp
, SCM
*sp
, scm_t_uint8
*ra
,
96 scm_t_uint8
*mvra
, scm_t_uint32 flags
)
98 struct scm_vm_cont
*p
;
100 p
= scm_gc_malloc (sizeof (*p
), "capture_vm_cont");
101 p
->stack_size
= sp
- stack_base
+ 1;
102 p
->stack_base
= scm_gc_malloc (p
->stack_size
* sizeof (SCM
),
104 #if defined(VM_ENABLE_STACK_NULLING) && 0
105 /* Tail continuations leave their frame on the stack for subsequent
106 application, but don't capture the frame -- so there are some elements on
107 the stack then, and this check doesn't work, so disable it for now. */
108 if (sp
>= vp
->stack_base
)
109 if (!vp
->sp
[0] || vp
->sp
[1])
111 memset (p
->stack_base
, 0, p
->stack_size
* sizeof (SCM
));
117 memcpy (p
->stack_base
, stack_base
, (sp
+ 1 - stack_base
) * sizeof (SCM
));
118 p
->reloc
= p
->stack_base
- stack_base
;
120 return scm_cell (scm_tc7_vm_cont
, (scm_t_bits
)p
);
124 vm_return_to_continuation (SCM vm
, SCM cont
, size_t n
, SCM
*argv
)
127 struct scm_vm_cont
*cp
;
130 argv_copy
= alloca (n
* sizeof(SCM
));
131 memcpy (argv_copy
, argv
, n
* sizeof(SCM
));
133 vp
= SCM_VM_DATA (vm
);
134 cp
= SCM_VM_CONT_DATA (cont
);
136 if (n
== 0 && !cp
->mvra
)
137 scm_misc_error (NULL
, "Too few values returned to continuation",
140 if (vp
->stack_size
< cp
->stack_size
+ n
+ 1)
142 /* puts ("FIXME: Need to expand"); */
145 #ifdef VM_ENABLE_STACK_NULLING
147 scm_t_ptrdiff nzero
= (vp
->sp
- cp
->sp
);
149 memset (vp
->stack_base
+ cp
->stack_size
, 0, nzero
* sizeof (SCM
));
150 /* actually nzero should always be negative, because vm_reset_stack will
151 unwind the stack to some point *below* this continuation */
156 memcpy (vp
->stack_base
, cp
->stack_base
, cp
->stack_size
* sizeof (SCM
));
158 if (n
== 1 || !cp
->mvra
)
162 *vp
->sp
= argv_copy
[0];
167 for (i
= 0; i
< n
; i
++)
170 *vp
->sp
= argv_copy
[i
];
173 *vp
->sp
= scm_from_size_t (n
);
179 scm_i_vm_capture_continuation (SCM vm
)
181 struct scm_vm
*vp
= SCM_VM_DATA (vm
);
182 return scm_i_vm_capture_stack (vp
->stack_base
, vp
->fp
, vp
->sp
, vp
->ip
, NULL
, 0);
186 vm_dispatch_hook (SCM vm
, int hook_num
)
192 vp
= SCM_VM_DATA (vm
);
193 hook
= vp
->hooks
[hook_num
];
195 if (SCM_LIKELY (scm_is_false (hook
))
196 || scm_is_null (SCM_HOOK_PROCEDURES (hook
)))
200 frame
= scm_c_make_frame (vm
, vp
->fp
, vp
->sp
, vp
->ip
, 0);
201 scm_c_run_hookn (hook
, &frame
, 1);
205 static void vm_abort (SCM vm
, size_t n
, scm_t_int64 cookie
) SCM_NORETURN
;
207 vm_abort (SCM vm
, size_t n
, scm_t_int64 vm_cookie
)
211 SCM tag
, tail
, *argv
;
213 /* FIXME: VM_ENABLE_STACK_NULLING */
214 tail
= *(SCM_VM_DATA (vm
)->sp
--);
216 tail_len
= scm_ilength (tail
);
219 tag
= SCM_VM_DATA (vm
)->sp
[-n
];
220 argv
= alloca ((n
+ tail_len
) * sizeof (SCM
));
221 for (i
= 0; i
< n
; i
++)
222 argv
[i
] = SCM_VM_DATA (vm
)->sp
[-(n
-1-i
)];
223 for (; i
< n
+ tail_len
; i
++, tail
= scm_cdr (tail
))
224 argv
[i
] = scm_car (tail
);
225 /* NULLSTACK (n + 1) */
226 SCM_VM_DATA (vm
)->sp
-= n
+ 1;
228 scm_c_abort (vm
, tag
, n
+ tail_len
, argv
, vm_cookie
);
232 vm_reinstate_partial_continuation (SCM vm
, SCM cont
, SCM intwinds
,
233 SCM extwinds
, size_t n
, SCM
*argv
)
236 struct scm_vm_cont
*cp
;
237 SCM
*argv_copy
, *base
;
240 argv_copy
= alloca (n
* sizeof(SCM
));
241 memcpy (argv_copy
, argv
, n
* sizeof(SCM
));
243 vp
= SCM_VM_DATA (vm
);
244 cp
= SCM_VM_CONT_DATA (cont
);
245 base
= SCM_FRAME_UPPER_ADDRESS (vp
->fp
) + 1;
247 #define RELOC(scm_p) (scm_p + cp->reloc + (base - cp->stack_base))
249 if ((base
- vp
->stack_base
) + cp
->stack_size
+ n
+ 1 > vp
->stack_size
)
251 /* puts ("FIXME: Need to expand"); */
255 memcpy (base
, cp
->stack_base
, cp
->stack_size
* sizeof (SCM
));
257 /* now relocate frame pointers */
260 for (fp
= RELOC (cp
->fp
);
261 SCM_FRAME_LOWER_ADDRESS (fp
) > base
;
262 fp
= SCM_FRAME_DYNAMIC_LINK (fp
))
263 SCM_FRAME_SET_DYNAMIC_LINK (fp
, RELOC (SCM_FRAME_DYNAMIC_LINK (fp
)));
266 vp
->sp
= base
- 1 + cp
->stack_size
;
267 vp
->fp
= RELOC (cp
->fp
);
272 /* now push args. ip is in a MV context. */
273 for (i
= 0; i
< n
; i
++)
276 *vp
->sp
= argv_copy
[i
];
279 *vp
->sp
= scm_from_size_t (n
);
284 * VM Internal functions
287 SCM_SYMBOL (sym_vm_run
, "vm-run");
288 SCM_SYMBOL (sym_vm_error
, "vm-error");
289 SCM_SYMBOL (sym_keyword_argument_error
, "keyword-argument-error");
290 SCM_SYMBOL (sym_debug
, "debug");
293 scm_i_vm_print (SCM x
, SCM port
, scm_print_state
*pstate
)
295 scm_puts ("#<vm ", port
);
296 scm_uintprint (SCM_UNPACK (x
), 16, port
);
297 scm_puts (">", port
);
301 really_make_boot_program (long nargs
)
304 scm_t_uint8 text
[] = { scm_op_mv_call
, 0, 0, 0, 1,
305 scm_op_make_int8_1
, scm_op_halt
};
306 struct scm_objcode
*bp
;
309 if (SCM_UNLIKELY (nargs
> 255 || nargs
< 0))
311 text
[1] = (scm_t_uint8
)nargs
;
313 bp
= scm_malloc (sizeof (struct scm_objcode
) + sizeof (text
));
314 memcpy (SCM_C_OBJCODE_BASE (bp
), text
, sizeof (text
));
315 bp
->len
= sizeof(text
);
318 u8vec
= scm_c_take_bytevector ((scm_t_int8
*)bp
,
319 sizeof (struct scm_objcode
) + sizeof (text
));
320 ret
= scm_make_program (scm_bytecode_to_objcode (u8vec
),
321 SCM_BOOL_F
, SCM_BOOL_F
);
322 SCM_SET_CELL_WORD_0 (ret
, SCM_CELL_WORD_0 (ret
) | SCM_F_PROGRAM_IS_BOOT
);
326 #define NUM_BOOT_PROGS 8
328 vm_make_boot_program (long nargs
)
330 static SCM programs
[NUM_BOOT_PROGS
] = { 0, };
332 if (SCM_UNLIKELY (!programs
[0]))
335 for (i
= 0; i
< NUM_BOOT_PROGS
; i
++)
336 programs
[i
] = really_make_boot_program (i
);
339 if (SCM_LIKELY (nargs
< NUM_BOOT_PROGS
))
340 return programs
[nargs
];
342 return really_make_boot_program (nargs
);
351 resolve_variable (SCM what
, SCM program_module
)
353 if (SCM_LIKELY (scm_is_symbol (what
)))
355 if (SCM_LIKELY (scm_module_system_booted_p
356 && scm_is_true (program_module
)))
358 return scm_module_lookup (program_module
, what
);
361 SCM v
= scm_sym2var (what
, SCM_BOOL_F
, SCM_BOOL_F
);
362 if (scm_is_false (v
))
363 scm_misc_error (NULL
, "unbound variable: ~S", scm_list_1 (what
));
371 /* compilation of @ or @@
372 `what' is a three-element list: (MODNAME SYM INTERFACE?)
373 INTERFACE? is #t if we compiled @ or #f if we compiled @@
375 mod
= scm_resolve_module (SCM_CAR (what
));
376 if (scm_is_true (SCM_CADDR (what
)))
377 mod
= scm_module_public_interface (mod
);
378 if (scm_is_false (mod
))
379 scm_misc_error (NULL
, "no such module: ~S",
380 scm_list_1 (SCM_CAR (what
)));
382 return scm_module_lookup (mod
, SCM_CADR (what
));
386 #define VM_DEFAULT_STACK_SIZE (64 * 1024)
388 #define VM_NAME vm_regular_engine
389 #define FUNC_NAME "vm-regular-engine"
390 #define VM_ENGINE SCM_VM_REGULAR_ENGINE
391 #include "vm-engine.c"
396 #define VM_NAME vm_debug_engine
397 #define FUNC_NAME "vm-debug-engine"
398 #define VM_ENGINE SCM_VM_DEBUG_ENGINE
399 #include "vm-engine.c"
404 static const scm_t_vm_engine vm_engines
[] =
405 { vm_regular_engine
, vm_debug_engine
};
407 #ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
409 /* The GC "kind" for the VM stack. */
410 static int vm_stack_gc_kind
;
416 #define FUNC_NAME "make_vm"
421 vp
= scm_gc_malloc (sizeof (struct scm_vm
), "vm");
423 vp
->stack_size
= VM_DEFAULT_STACK_SIZE
;
425 #ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
426 vp
->stack_base
= (SCM
*)
427 GC_generic_malloc (vp
->stack_size
* sizeof (SCM
), vm_stack_gc_kind
);
429 /* Keep a pointer to VP so that `vm_stack_mark ()' can know what the stack
431 *vp
->stack_base
= PTR2SCM (vp
);
435 vp
->stack_base
= scm_gc_malloc (vp
->stack_size
* sizeof (SCM
),
439 #ifdef VM_ENABLE_STACK_NULLING
440 memset (vp
->stack_base
, 0, vp
->stack_size
* sizeof (SCM
));
442 vp
->stack_limit
= vp
->stack_base
+ vp
->stack_size
;
444 vp
->sp
= vp
->stack_base
- 1;
446 vp
->engine
= SCM_VM_DEBUG_ENGINE
;
447 vp
->options
= SCM_EOL
;
449 for (i
= 0; i
< SCM_VM_NUM_HOOKS
; i
++)
450 vp
->hooks
[i
] = SCM_BOOL_F
;
452 return scm_cell (scm_tc7_vm
, (scm_t_bits
)vp
);
456 #ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
458 /* Mark the VM stack region between its base and its current top. */
459 static struct GC_ms_entry
*
460 vm_stack_mark (GC_word
*addr
, struct GC_ms_entry
*mark_stack_ptr
,
461 struct GC_ms_entry
*mark_stack_limit
, GC_word env
)
464 const struct scm_vm
*vm
;
466 /* The first word of the VM stack should contain a pointer to the
468 vm
= * ((struct scm_vm
**) addr
);
471 || (SCM
*) addr
!= vm
->stack_base
- 1
472 || vm
->stack_limit
- vm
->stack_base
!= vm
->stack_size
)
473 /* ADDR must be a pointer to a free-list element, which we must ignore
474 (see warning in <gc/gc_mark.h>). */
475 return mark_stack_ptr
;
477 for (word
= (GC_word
*) vm
->stack_base
; word
<= (GC_word
*) vm
->sp
; word
++)
478 mark_stack_ptr
= GC_MARK_AND_PUSH ((* (GC_word
**) word
),
479 mark_stack_ptr
, mark_stack_limit
,
482 return mark_stack_ptr
;
485 #endif /* VM_ENABLE_PRECISE_STACK_GC_SCAN */
489 scm_c_vm_run (SCM vm
, SCM program
, SCM
*argv
, int nargs
)
491 struct scm_vm
*vp
= SCM_VM_DATA (vm
);
492 return vm_engines
[vp
->engine
](vm
, program
, argv
, nargs
);
495 SCM_DEFINE (scm_vm_apply
, "vm-apply", 3, 0, 0,
496 (SCM vm
, SCM program
, SCM args
),
498 #define FUNC_NAME s_scm_vm_apply
503 SCM_VALIDATE_VM (1, vm
);
504 SCM_VALIDATE_PROC (2, program
);
506 nargs
= scm_ilength (args
);
507 if (SCM_UNLIKELY (nargs
< 0))
508 scm_wrong_type_arg_msg (FUNC_NAME
, 3, args
, "list");
510 argv
= alloca(nargs
* sizeof(SCM
));
511 for (i
= 0; i
< nargs
; i
++)
513 argv
[i
] = SCM_CAR (args
);
514 args
= SCM_CDR (args
);
517 return scm_c_vm_run (vm
, program
, argv
, nargs
);
522 scm_vm_call_with_new_stack (SCM vm
, SCM thunk
, SCM id
)
524 return scm_c_vm_run (vm
, thunk
, NULL
, 0);
527 /* Scheme interface */
529 SCM_DEFINE (scm_vm_version
, "vm-version", 0, 0, 0,
532 #define FUNC_NAME s_scm_vm_version
534 return scm_from_locale_string (PACKAGE_VERSION
);
538 SCM_DEFINE (scm_the_vm
, "the-vm", 0, 0, 0,
541 #define FUNC_NAME s_scm_the_vm
543 scm_i_thread
*t
= SCM_I_CURRENT_THREAD
;
545 if (SCM_UNLIKELY (scm_is_false ((t
->vm
))))
553 SCM_DEFINE (scm_vm_p
, "vm?", 1, 0, 0,
556 #define FUNC_NAME s_scm_vm_p
558 return scm_from_bool (SCM_VM_P (obj
));
562 SCM_DEFINE (scm_make_vm
, "make-vm", 0, 0, 0,
565 #define FUNC_NAME s_scm_make_vm,
571 SCM_DEFINE (scm_vm_ip
, "vm:ip", 1, 0, 0,
574 #define FUNC_NAME s_scm_vm_ip
576 SCM_VALIDATE_VM (1, vm
);
577 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm
)->ip
);
581 SCM_DEFINE (scm_vm_sp
, "vm:sp", 1, 0, 0,
584 #define FUNC_NAME s_scm_vm_sp
586 SCM_VALIDATE_VM (1, vm
);
587 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm
)->sp
);
591 SCM_DEFINE (scm_vm_fp
, "vm:fp", 1, 0, 0,
594 #define FUNC_NAME s_scm_vm_fp
596 SCM_VALIDATE_VM (1, vm
);
597 return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm
)->fp
);
601 #define VM_DEFINE_HOOK(n) \
604 SCM_VALIDATE_VM (1, vm); \
605 vp = SCM_VM_DATA (vm); \
606 if (scm_is_false (vp->hooks[n])) \
607 vp->hooks[n] = scm_make_hook (SCM_I_MAKINUM (1)); \
608 return vp->hooks[n]; \
611 SCM_DEFINE (scm_vm_boot_hook
, "vm-boot-hook", 1, 0, 0,
614 #define FUNC_NAME s_scm_vm_boot_hook
616 VM_DEFINE_HOOK (SCM_VM_BOOT_HOOK
);
620 SCM_DEFINE (scm_vm_halt_hook
, "vm-halt-hook", 1, 0, 0,
623 #define FUNC_NAME s_scm_vm_halt_hook
625 VM_DEFINE_HOOK (SCM_VM_HALT_HOOK
);
629 SCM_DEFINE (scm_vm_next_hook
, "vm-next-hook", 1, 0, 0,
632 #define FUNC_NAME s_scm_vm_next_hook
634 VM_DEFINE_HOOK (SCM_VM_NEXT_HOOK
);
638 SCM_DEFINE (scm_vm_break_hook
, "vm-break-hook", 1, 0, 0,
641 #define FUNC_NAME s_scm_vm_break_hook
643 VM_DEFINE_HOOK (SCM_VM_BREAK_HOOK
);
647 SCM_DEFINE (scm_vm_enter_hook
, "vm-enter-hook", 1, 0, 0,
650 #define FUNC_NAME s_scm_vm_enter_hook
652 VM_DEFINE_HOOK (SCM_VM_ENTER_HOOK
);
656 SCM_DEFINE (scm_vm_apply_hook
, "vm-apply-hook", 1, 0, 0,
659 #define FUNC_NAME s_scm_vm_apply_hook
661 VM_DEFINE_HOOK (SCM_VM_APPLY_HOOK
);
665 SCM_DEFINE (scm_vm_exit_hook
, "vm-exit-hook", 1, 0, 0,
668 #define FUNC_NAME s_scm_vm_exit_hook
670 VM_DEFINE_HOOK (SCM_VM_EXIT_HOOK
);
674 SCM_DEFINE (scm_vm_return_hook
, "vm-return-hook", 1, 0, 0,
677 #define FUNC_NAME s_scm_vm_return_hook
679 VM_DEFINE_HOOK (SCM_VM_RETURN_HOOK
);
683 SCM_DEFINE (scm_vm_option
, "vm-option", 2, 0, 0,
686 #define FUNC_NAME s_scm_vm_option
688 SCM_VALIDATE_VM (1, vm
);
689 return scm_assq_ref (SCM_VM_DATA (vm
)->options
, key
);
693 SCM_DEFINE (scm_set_vm_option_x
, "set-vm-option!", 3, 0, 0,
694 (SCM vm
, SCM key
, SCM val
),
696 #define FUNC_NAME s_scm_set_vm_option_x
698 SCM_VALIDATE_VM (1, vm
);
699 SCM_VM_DATA (vm
)->options
700 = scm_assq_set_x (SCM_VM_DATA (vm
)->options
, key
, val
);
701 return SCM_UNSPECIFIED
;
705 SCM_DEFINE (scm_vm_trace_level
, "vm-trace-level", 1, 0, 0,
708 #define FUNC_NAME s_scm_vm_trace_level
710 SCM_VALIDATE_VM (1, vm
);
711 return scm_from_int (SCM_VM_DATA (vm
)->trace_level
);
715 SCM_DEFINE (scm_set_vm_trace_level_x
, "set-vm-trace-level!", 2, 0, 0,
718 #define FUNC_NAME s_scm_set_vm_trace_level_x
720 SCM_VALIDATE_VM (1, vm
);
721 SCM_VM_DATA (vm
)->trace_level
= scm_to_int (level
);
722 return SCM_UNSPECIFIED
;
731 SCM
scm_load_compiled_with_vm (SCM file
)
733 SCM program
= scm_make_program (scm_load_objcode (file
),
734 SCM_BOOL_F
, SCM_BOOL_F
);
736 return scm_c_vm_run (scm_the_vm (), program
, NULL
, 0);
740 scm_bootstrap_vm (void)
742 scm_c_register_extension ("libguile", "scm_init_vm",
743 (scm_t_extension_init_func
)scm_init_vm
, NULL
);
745 #ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
747 GC_new_kind (GC_new_free_list (),
748 GC_MAKE_PROC (GC_new_proc (vm_stack_mark
), 0),
757 #ifndef SCM_MAGIC_SNARFER
758 #include "libguile/vm.x"