diff options
author | Andy Wingo <wingo@pobox.com> | 2008-10-03 16:00:30 +0200 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2008-10-03 16:00:30 +0200 |
commit | 11ea1aba9eb99756e72b6447a93da8d901558299 (patch) | |
tree | 882323910c897b6b727288a683f29f25bd61a73e /libguile/vm-i-system.c | |
parent | edb1d1d78da289ffe9170ffe21e120cb31e463b5 (diff) | |
download | guile-11ea1aba9eb99756e72b6447a93da8d901558299.tar.gz |
precise stack marking, fix some missed references, still imperfect
* libguile/vm-engine.h (CHECK_STACK_LEAK, NULLSTACK): Add a new mode,
VM_ENABLE_STACK_NULLING, that tries to ensure that all stack data past
the top of the stack is NULL. This helps to verify the VM's
consistency. If VM_ENABLE_STACK_NULLING is not defined, there is no
overhead.
(DROP, DROPN): Hook into NULLSTACK.
(POP_LIST): Hoo, fix a good bug: if CONS triggered a GC, the elements
of the list that had not yet been consed would not be marked, because
the sp was already below them.
(NEXT): Hook into CHECK_STACK_LEAK.
(INIT_ARGS): Add a note that consing the rest arg can cause GC.
(NEW_FRAME): Cons up the external data after initializing the frame, so
that if GC is triggered, the precise marker sees a well-formed frame.
* libguile/vm-i-loader.c (load-program): In the four-integers case, use
the POP macro so that we can hook into NULLSTACK (if necessary).
* libguile/vm-i-scheme.c (ARGS2, ARGS3): Hook into NULLSTACK.
* libguile/vm-i-system.c (halt): Null the nvalues. Rework some asserts
into using ASSERT, and null the stack when we free the frame.
(variable-set): Use DROPN instead of sp -= 2.
(BR): Hook into NULLSTACK.
(goto/args): Hook into NULLSTACK. In the non-self case, delay updating
the frame until after INIT_ARGS so that GC sees a well-formed frame.
Delay consing the externals until after the frame is set up, as in
NEW_FRAME.
(call/cc): Add some asserts.
(return): Rework some asserts into ASSERT, and hook into NULLSTACK.
(return/values): Hook into NULLSTACK, and use ASSERT.
(return/values*) Use ASSERT.
* libguile/vm.c (VM_ENABLE_ASSERTIONS, VM_ENABLE_STACK_NULLING): These
are the variables that control assertions and nulling. Perhaps we can
do these per-engine when we start compiling the debug engine separate
from a speedy engine.
(vm_mark_stack): Add a precise stack marker. Yay!
(vm_cont_mark): Mark the continuation stack precisely.
(capture_vm_cont): Record the difference from the vp's stack_base too,
so that we can translate the dynamic links when marking the
continuation stack. Memset the stack to NULL if we are doing nulling.
(reinstate_vm_cont): If we are nulling, null out the relevant part
of the stack.
(vm_reset_stack): When resetting sp due to a nonlocal exit, null out
the stack too.
(vm_mark): If we are nulling, assert that there are no extra values on
the stack. Mark the stack precisely.
Diffstat (limited to 'libguile/vm-i-system.c')
-rw-r--r-- | libguile/vm-i-system.c | 73 |
1 files changed, 45 insertions, 28 deletions
diff --git a/libguile/vm-i-system.c b/libguile/vm-i-system.c index 9a0e028a5..e336e4215 100644 --- a/libguile/vm-i-system.c +++ b/libguile/vm-i-system.c @@ -58,6 +58,7 @@ VM_DEFINE_INSTRUCTION (halt, "halt", 0, 0, 0) vp->time += scm_c_get_internal_run_time () - start_time; HALT_HOOK (); nvalues = SCM_I_INUM (*sp--); + NULLSTACK (1); if (nvalues == 1) POP (ret); else @@ -69,17 +70,14 @@ VM_DEFINE_INSTRUCTION (halt, "halt", 0, 0, 0) } { -#ifdef THE_GOVERNMENT_IS_AFTER_ME - if (sp != stack_base) - abort (); - if (stack_base != SCM_FRAME_UPPER_ADDRESS (fp) - 1) - abort (); -#endif + ASSERT (sp == stack_base); + ASSERT (stack_base == SCM_FRAME_UPPER_ADDRESS (fp) - 1); /* Restore registers */ sp = SCM_FRAME_LOWER_ADDRESS (fp) - 1; ip = NULL; fp = SCM_FRAME_DYNAMIC_LINK (fp); + NULLSTACK (stack_base - sp); } SYNC_ALL (); scm_dynwind_end (); @@ -366,7 +364,7 @@ VM_DEFINE_INSTRUCTION (external_set, "external-set", 1, 1, 0) VM_DEFINE_INSTRUCTION (variable_set, "variable-set", 0, 1, 0) { VARIABLE_SET (sp[0], sp[-1]); - sp -= 2; + DROPN (2); NEXT; } @@ -435,6 +433,7 @@ VM_DEFINE_INSTRUCTION (late_variable_set, "late-variable-set", 1, 1, 0) FETCH_OFFSET (offset); \ if (p) \ ip += offset; \ + NULLSTACK (1); \ DROP (); \ NEXT; \ } @@ -621,6 +620,7 @@ VM_DEFINE_INSTRUCTION (goto_args, "goto/args", 1, -1, 1) /* Drop the first argument and the program itself. */ sp -= 2; + NULLSTACK (bp->nargs + 1) /* Call itself */ ip = bp->base; @@ -636,6 +636,9 @@ VM_DEFINE_INSTRUCTION (goto_args, "goto/args", 1, -1, 1) SCM *data, *tail_args, *dl; int i; scm_byte_t *ra, *mvra; +#ifdef VM_ENABLE_STACK_NULLING + SCM *old_sp; +#endif EXIT_HOOK (); @@ -646,11 +649,19 @@ VM_DEFINE_INSTRUCTION (goto_args, "goto/args", 1, -1, 1) dl = SCM_FRAME_DYNAMIC_LINK (fp); /* switch programs */ - fp[-1] = program = x; + program = x; CACHE_PROGRAM (); INIT_ARGS (); + /* delay updating the frame so that if INIT_ARGS has to cons up a rest + arg, going into GC, the stack still makes sense */ + fp[-1] = program; nargs = bp->nargs; +#ifdef VM_ENABLE_STACK_NULLING + old_sp = sp; + CHECK_STACK_LEAK (); +#endif + /* new registers -- logically this would be better later, but let's make sure we have space for the locals now */ data = SCM_FRAME_DATA_ADDRESS (fp); @@ -663,21 +674,26 @@ VM_DEFINE_INSTRUCTION (goto_args, "goto/args", 1, -1, 1) for (i = 0; i < nargs; i++) fp[i] = tail_args[i]; + NULLSTACK (old_sp - sp); + /* init locals */ for (i = bp->nlocs; i; i--) data[-i] = SCM_UNDEFINED; - /* and the external variables */ - external = bp->external; - for (i = 0; i < bp->nexts; i++) - CONS (external, SCM_UNDEFINED, external); - /* Set frame data */ data[4] = (SCM)ra; data[3] = (SCM)mvra; data[2] = (SCM)dl; data[1] = SCM_BOOL_F; + + /* Postpone initializing external vars, because if the CONS causes a GC, + we want the stack marker to see the data array formatted as expected. */ + data[0] = SCM_UNDEFINED; + external = bp->external; + for (i = 0; i < bp->nexts; i++) + CONS (external, SCM_UNDEFINED, external); data[0] = external; + ENTER_HOOK (); APPLY_HOOK (); NEXT; @@ -887,6 +903,9 @@ VM_DEFINE_INSTRUCTION (call_cc, "call/cc", 0, 1, 1) nargs = 1; goto vm_call; } + ASSERT (sp == vp->sp); + ASSERT (fp == vp->fp); + ASSERT (ip == vp->ip); else if (SCM_VALUESP (cont)) { /* multiple values returned to continuation */ @@ -946,18 +965,20 @@ VM_DEFINE_INSTRUCTION (return, "return", 0, 0, 1) data = SCM_FRAME_DATA_ADDRESS (fp); POP (ret); -#ifdef THE_GOVERNMENT_IS_AFTER_ME - if (sp != stack_base) - abort (); - if (stack_base != data + 4) - abort (); -#endif + ASSERT (sp == stack_base); + ASSERT (stack_base == data + 4); /* Restore registers */ sp = SCM_FRAME_LOWER_ADDRESS (fp); ip = SCM_FRAME_BYTE_CAST (data[4]); fp = SCM_FRAME_STACK_CAST (data[2]); - stack_base = SCM_FRAME_UPPER_ADDRESS (fp) - 1; + { +#ifdef VM_ENABLE_STACK_NULLING + int nullcount = stack_base - sp; +#endif + stack_base = SCM_FRAME_UPPER_ADDRESS (fp) - 1; + NULLSTACK (nullcount); + } /* Set return value (sp is already pushed) */ *sp = ret; @@ -983,10 +1004,7 @@ VM_DEFINE_INSTRUCTION (return_values, "return/values", 1, -1, -1) RETURN_HOOK (); data = SCM_FRAME_DATA_ADDRESS (fp); -#ifdef THE_GOVERNMENT_IS_AFTER_ME - if (stack_base != data + 4) - abort (); -#endif + ASSERT (stack_base == data + 4); /* data[3] is the mv return address */ if (nvalues != 1 && data[3]) @@ -1003,6 +1021,7 @@ VM_DEFINE_INSTRUCTION (return_values, "return/values", 1, -1, -1) *++sp = SCM_I_MAKINUM (nvalues); /* Finally set new stack_base */ + NULLSTACK (stack_base - sp + nvalues + 1); stack_base = SCM_FRAME_UPPER_ADDRESS (fp) - 1; } else if (nvalues >= 1) @@ -1020,6 +1039,7 @@ VM_DEFINE_INSTRUCTION (return_values, "return/values", 1, -1, -1) *++sp = stack_base[1]; /* Finally set new stack_base */ + NULLSTACK (stack_base - sp); stack_base = SCM_FRAME_UPPER_ADDRESS (fp) - 1; } else @@ -1038,10 +1058,7 @@ VM_DEFINE_INSTRUCTION (return_values_star, "return/values*", 1, -1, -1) SCM l; nvalues = FETCH (); -#ifdef THE_GOVERNMENT_IS_AFTER_ME - if (nvalues < 1) - abort (); -#endif + ASSERT (nvalues >= 1); nvalues--; POP (l); |