summaryrefslogtreecommitdiff
path: root/libguile/vm-i-system.c
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2008-10-03 16:00:30 +0200
committerAndy Wingo <wingo@pobox.com>2008-10-03 16:00:30 +0200
commit11ea1aba9eb99756e72b6447a93da8d901558299 (patch)
tree882323910c897b6b727288a683f29f25bd61a73e /libguile/vm-i-system.c
parentedb1d1d78da289ffe9170ffe21e120cb31e463b5 (diff)
downloadguile-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.c73
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);