mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-17 01:00:20 +02:00
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.
This commit is contained in:
parent
edb1d1d78d
commit
11ea1aba9e
5 changed files with 157 additions and 61 deletions
|
@ -127,9 +127,7 @@
|
|||
* Cache/Sync
|
||||
*/
|
||||
|
||||
#define ENABLE_ASSERTIONS
|
||||
|
||||
#ifdef ENABLE_ASSERTIONS
|
||||
#ifdef VM_ENABLE_ASSERTIONS
|
||||
# define ASSERT(condition) if (SCM_UNLIKELY (!(condition))) abort()
|
||||
#else
|
||||
# define ASSERT(condition)
|
||||
|
@ -151,7 +149,7 @@
|
|||
vp->fp = fp; \
|
||||
}
|
||||
|
||||
#ifdef IP_PARANOIA
|
||||
#ifdef VM_ENABLE_PARANOID_ASSERTIONS
|
||||
#define CHECK_IP() \
|
||||
do { if (ip < bp->base || ip - bp->base > bp->size) abort (); } while (0)
|
||||
#else
|
||||
|
@ -245,6 +243,16 @@
|
|||
* Stack operation
|
||||
*/
|
||||
|
||||
#ifdef VM_ENABLE_STACK_NULLING
|
||||
# define CHECK_STACK_LEAKN(_n) ASSERT (!sp[_n]);
|
||||
# define CHECK_STACK_LEAK() CHECK_STACK_LEAKN(1)
|
||||
# define NULLSTACK(_n) { int __x = _n; CHECK_STACK_LEAKN (_n+1); while (__x > 0) sp[__x--] = NULL; }
|
||||
#else
|
||||
# define CHECK_STACK_LEAKN(_n)
|
||||
# define CHECK_STACK_LEAK()
|
||||
# define NULLSTACK(_n)
|
||||
#endif
|
||||
|
||||
#define CHECK_OVERFLOW() \
|
||||
if (sp > stack_limit) \
|
||||
goto vm_error_stack_overflow
|
||||
|
@ -254,8 +262,8 @@
|
|||
goto vm_error_stack_underflow;
|
||||
|
||||
#define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0)
|
||||
#define DROP() do { sp--; CHECK_UNDERFLOW (); } while (0)
|
||||
#define DROPN(_n) do { sp -= (_n); CHECK_UNDERFLOW (); } while (0)
|
||||
#define DROP() do { sp--; CHECK_UNDERFLOW (); NULLSTACK (1); } while (0)
|
||||
#define DROPN(_n) do { sp -= (_n); CHECK_UNDERFLOW (); NULLSTACK (_n); } while (0)
|
||||
#define POP(x) do { x = *sp; DROP (); } while (0)
|
||||
|
||||
/* A fast CONS. This has to be fast since its used, for instance, by
|
||||
|
@ -275,10 +283,12 @@
|
|||
do \
|
||||
{ \
|
||||
int i; \
|
||||
SCM l = SCM_EOL; \
|
||||
sp -= n; \
|
||||
for (i = n; i; i--) \
|
||||
CONS (l, sp[i], l); \
|
||||
SCM l = SCM_EOL, x; \
|
||||
for (i = n; i; i--) \
|
||||
{ \
|
||||
POP (x); \
|
||||
CONS (l, x, l); \
|
||||
} \
|
||||
PUSH (l); \
|
||||
} while (0)
|
||||
|
||||
|
@ -404,6 +414,7 @@ do { \
|
|||
{ \
|
||||
CLOCK (1); \
|
||||
NEXT_HOOK (); \
|
||||
CHECK_STACK_LEAK (); \
|
||||
NEXT_JUMP (); \
|
||||
}
|
||||
|
||||
|
@ -419,6 +430,8 @@ do { \
|
|||
int n = nargs - (bp->nargs - 1); \
|
||||
if (n < 0) \
|
||||
goto vm_error_wrong_num_args; \
|
||||
/* NB, can cause GC while setting up the \
|
||||
stack frame */ \
|
||||
POP_LIST (n); \
|
||||
} \
|
||||
else \
|
||||
|
@ -453,17 +466,21 @@ do { \
|
|||
for (i=bp->nlocs; i; i--) \
|
||||
data[-i] = SCM_UNDEFINED; \
|
||||
\
|
||||
/* Create 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] = 0x0; \
|
||||
data[2] = (SCM)dl; \
|
||||
data[1] = SCM_BOOL_F; \
|
||||
data[0] = external; \
|
||||
\
|
||||
/* 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; \
|
||||
}
|
||||
|
||||
#define CACHE_EXTERNAL() external = fp[bp->nargs + bp->nlocs]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue