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