1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-17 17:20:29 +02:00

abort always dispatches to VM bytecode, to detect same-invocation aborts

* libguile/control.h:
* libguile/control.c (scm_c_make_prompt): Take an extra arg, a cookie.
  Continuations will be rewindable only if the abort has the same cookie
  as the prompt.
  (scm_at_abort): Redefine from scm_abort, and instead of taking rest
  args, take the abort values as a list directly. Also, don't allow
  rewinding, because we won't support rewinding the C stack with
  delimited continuations.

* libguile/eval.c (eval): Adapt to scm_c_make_prompt change.

* libguile/vm-engine.c (vm_engine): Use vp->cookie to get a unique value
  corresponding to this VM invocation.
* libguile/vm-i-system.c (prompt): Pass the cookie to scm_c_make_prompt.
  (abort): Take an additional tail arg.
* libguile/vm.c (vm_abort): Parse out the abort tail arg. This is for
  the @abort case, or the (apply abort ...) case.
  (make_vm): Initialize the cookie to 0.
* libguile/vm.h (struct scm_vm): Add cookie.

* module/ice-9/boot-9.scm (abort): Define here as a trampoline to
  @abort. Needed to make sure that a call to abort dispatches to a VM
  opcode, so the cookie will be the same.

* module/language/tree-il.scm (<tree-il>): Add a "tail" field to
  <abort>, for the (apply abort ...) case, or (@abort tag args). Should
  be #<const ()> in the normal case. Add support throughout.
* module/language/tree-il/analyze.scm (analyze-lexicals): Add abort-tail
  support here too.

* module/language/tree-il/compile-glil.scm (flatten): Compile the tail
  argument appropriately.
* module/language/tree-il/primitives.scm (*primitive-expand-table*): Fix
  @abort and abort cases to pass the tail arg to make-abort.
This commit is contained in:
Andy Wingo 2010-02-22 21:53:24 +01:00
parent f828ab4f30
commit 2d026f04cc
12 changed files with 71 additions and 50 deletions

View file

@ -27,7 +27,8 @@
SCM SCM
scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p) scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p,
scm_t_int64 vm_cookie)
{ {
scm_t_bits tag; scm_t_bits tag;
SCM ret; SCM ret;
@ -42,6 +43,7 @@ scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p)
regs->fp = SCM_VM_DATA (vm)->fp; regs->fp = SCM_VM_DATA (vm)->fp;
regs->sp = SCM_VM_DATA (vm)->sp; regs->sp = SCM_VM_DATA (vm)->sp;
regs->ip = SCM_VM_DATA (vm)->ip; regs->ip = SCM_VM_DATA (vm)->ip;
regs->cookie = vm_cookie;
SCM_SET_CELL_OBJECT (ret, 1, k); SCM_SET_CELL_OBJECT (ret, 1, k);
SCM_SET_CELL_WORD (ret, 2, (scm_t_bits)regs); SCM_SET_CELL_WORD (ret, 2, (scm_t_bits)regs);
@ -109,9 +111,9 @@ scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv)
abort (); abort ();
} }
SCM_DEFINE (scm_abort, "abort", 1, 0, 1, (SCM tag, SCM args), SCM_DEFINE (scm_at_abort, "@abort", 2, 0, 0, (SCM tag, SCM args),
"Abort to the nearest prompt with tag @var{tag}.") "Abort to the nearest prompt with tag @var{tag}.")
#define FUNC_NAME s_scm_abort #define FUNC_NAME s_scm_at_abort
{ {
SCM *argv; SCM *argv;
size_t i, n; size_t i, n;
@ -123,23 +125,11 @@ SCM_DEFINE (scm_abort, "abort", 1, 0, 1, (SCM tag, SCM args),
scm_c_abort (scm_the_vm (), tag, n, argv); scm_c_abort (scm_the_vm (), tag, n, argv);
/* Oh, what, you're still here? The abort must have been reinstated. OK, pull /* Oh, what, you're still here? The abort must have been reinstated. Actually,
args back from the stack, and keep going... */ that's quite impossible, given that we're already in C-land here, so...
abort! */
{ abort ();
SCM vals = SCM_EOL;
struct scm_vm *vp = SCM_VM_DATA (scm_the_vm ());
n = scm_to_size_t (vp->sp[0]);
for (i = 0; i < n; i++)
vals = scm_cons (vp->sp[-(i + 1)], vals);
/* The continuation call did reset the VM's registers, but then these values
were pushed on; so we need to pop them ourselves. */
vp->sp -= n + 1;
/* FIXME NULLSTACK */
return (scm_is_pair (vals) && scm_is_null (scm_cdr (vals)))
? scm_car (vals) : scm_values (vals);
}
} }
#undef FUNC_NAME #undef FUNC_NAME

View file

@ -37,12 +37,15 @@ struct scm_prompt_registers
scm_t_uint8 *ip; scm_t_uint8 *ip;
SCM *sp; SCM *sp;
SCM *fp; SCM *fp;
scm_t_int64 cookie;
scm_i_jmp_buf regs; scm_i_jmp_buf regs;
}; };
SCM_INTERNAL SCM scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p); SCM_INTERNAL SCM scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p,
scm_t_int64 cookie);
SCM_INTERNAL SCM scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv) SCM_NORETURN; SCM_INTERNAL SCM scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv) SCM_NORETURN;
SCM_INTERNAL SCM scm_at_abort (SCM tag, SCM args) SCM_NORETURN;
SCM_INTERNAL void scm_init_control (void); SCM_INTERNAL void scm_init_control (void);

View file

@ -429,7 +429,7 @@ eval (SCM x, SCM env)
{ {
SCM prompt, handler, res; SCM prompt, handler, res;
prompt = scm_c_make_prompt (scm_the_vm (), eval (CAR (mx), env), 0); prompt = scm_c_make_prompt (scm_the_vm (), eval (CAR (mx), env), 0, -1);
handler = eval (CDDR (mx), env); handler = eval (CDDR (mx), env);
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ())); scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));

View file

@ -47,7 +47,9 @@ VM_NAME (SCM vm, SCM program, SCM *argv, int nargs)
SCM *objects = NULL; /* constant objects */ SCM *objects = NULL; /* constant objects */
size_t object_count = 0; /* length of OBJECTS */ size_t object_count = 0; /* length of OBJECTS */
SCM *stack_limit = vp->stack_limit; /* stack limit address */ SCM *stack_limit = vp->stack_limit; /* stack limit address */
SCM dynstate = SCM_I_CURRENT_THREAD->dynamic_state; SCM dynstate = SCM_I_CURRENT_THREAD->dynamic_state;
scm_t_int64 vm_cookie = vp->cookie++;
/* Internal variables */ /* Internal variables */
int nvalues = 0; int nvalues = 0;

View file

@ -1464,7 +1464,7 @@ VM_DEFINE_INSTRUCTION (83, prompt, "prompt", 4, 2, 0)
/* Push the prompt onto the dynamic stack. The setjmp itself has to be local /* Push the prompt onto the dynamic stack. The setjmp itself has to be local
to this procedure. */ to this procedure. */
/* FIXME: do more error checking */ /* FIXME: do more error checking */
prompt = scm_c_make_prompt (vm, k, escape_only_p); prompt = scm_c_make_prompt (vm, k, escape_only_p, vm_cookie);
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ())); scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));
if (SCM_PROMPT_SETJMP (prompt)) if (SCM_PROMPT_SETJMP (prompt))
{ {
@ -1509,7 +1509,7 @@ VM_DEFINE_INSTRUCTION (86, abort, "abort", 1, -1, -1)
{ {
unsigned n = FETCH (); unsigned n = FETCH ();
SYNC_REGISTER (); SYNC_REGISTER ();
if (sp - n - 1 <= SCM_FRAME_UPPER_ADDRESS (fp)) if (sp - n - 2 <= SCM_FRAME_UPPER_ADDRESS (fp))
goto vm_error_stack_underflow; goto vm_error_stack_underflow;
vm_abort (vm, n); vm_abort (vm, n);
/* vm_abort should not return */ /* vm_abort should not return */

View file

@ -206,15 +206,25 @@ static void
vm_abort (SCM vm, size_t n) vm_abort (SCM vm, size_t n)
{ {
size_t i; size_t i;
SCM tag, *argv; ssize_t tail_len;
SCM tag, tail, *argv;
/* FIXME: VM_ENABLE_STACK_NULLING */
tail = *(SCM_VM_DATA (vm)->sp--);
/* NULLSTACK (1) */
tail_len = scm_ilength (tail);
if (tail_len < 0)
abort ();
tag = SCM_VM_DATA (vm)->sp[-n]; tag = SCM_VM_DATA (vm)->sp[-n];
argv = alloca (n * sizeof (SCM)); argv = alloca ((n + tail_len) * sizeof (SCM));
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
argv[i] = SCM_VM_DATA (vm)->sp[-(n-1-i)]; argv[i] = SCM_VM_DATA (vm)->sp[-(n-1-i)];
for (; i < n + tail_len; i++, tail = scm_cdr (tail))
argv[i] = scm_car (tail);
/* NULLSTACK (n + 1) */
SCM_VM_DATA (vm)->sp -= n + 1; SCM_VM_DATA (vm)->sp -= n + 1;
scm_c_abort (vm, tag, n, argv); scm_c_abort (vm, tag, n + tail_len, argv);
} }
@ -386,6 +396,7 @@ make_vm (void)
vp->trace_level = 0; vp->trace_level = 0;
for (i = 0; i < SCM_VM_NUM_HOOKS; i++) for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
vp->hooks[i] = SCM_BOOL_F; vp->hooks[i] = SCM_BOOL_F;
vp->cookie = 0;
return scm_cell (scm_tc7_vm, (scm_t_bits)vp); return scm_cell (scm_tc7_vm, (scm_t_bits)vp);
} }
#undef FUNC_NAME #undef FUNC_NAME

View file

@ -51,6 +51,7 @@ struct scm_vm {
SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */ SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */
SCM options; /* options */ SCM options; /* options */
int trace_level; /* traces enabled if trace_level > 0 */ int trace_level; /* traces enabled if trace_level > 0 */
scm_t_int64 cookie; /* used to detect unrewindable continuations */
}; };
SCM_API SCM scm_the_vm_fluid; SCM_API SCM scm_the_vm_fluid;

View file

@ -404,6 +404,8 @@
;;; Delimited continuations ;;; Delimited continuations
(define (prompt tag thunk handler) (define (prompt tag thunk handler)
(@prompt tag (thunk) handler)) (@prompt tag (thunk) handler))
(define (abort tag . args)
(@abort tag args))
;;; apply-to-args is functionally redundant with apply and, worse, ;;; apply-to-args is functionally redundant with apply and, worse,
;;; is less general than apply since it only takes two arguments. ;;; is less general than apply since it only takes two arguments.

View file

@ -50,7 +50,7 @@
<dynref> dynref? make-dynref dynref-src dynref-fluid <dynref> dynref? make-dynref dynref-src dynref-fluid
<dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp <dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp
<prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler <prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler
<abort> abort? make-abort abort-src abort-tag abort-args <abort> abort? make-abort abort-src abort-tag abort-args abort-tail
parse-tree-il parse-tree-il
unparse-tree-il unparse-tree-il
@ -86,7 +86,7 @@
(<dynref> fluid) (<dynref> fluid)
(<dynset> fluid exp) (<dynset> fluid exp)
(<prompt> tag body handler) (<prompt> tag body handler)
(<abort> tag args)) (<abort> tag args tail))
@ -192,8 +192,8 @@
((prompt ,tag ,body ,handler) ((prompt ,tag ,body ,handler)
(make-prompt loc (retrans tag) (retrans body) (retrans handler))) (make-prompt loc (retrans tag) (retrans body) (retrans handler)))
((abort ,tag ,type ,args) ((abort ,tag ,args ,tail)
(make-abort loc (retrans tag) type (map retrans args))) (make-abort loc (retrans tag) (map retrans args) (retrans tail)))
(else (else
(error "unrecognized tree-il" exp))))) (error "unrecognized tree-il" exp)))))
@ -276,8 +276,9 @@
((<prompt> tag body handler) ((<prompt> tag body handler)
`(prompt ,tag ,(unparse-tree-il body) ,(unparse-tree-il handler))) `(prompt ,tag ,(unparse-tree-il body) ,(unparse-tree-il handler)))
((<abort> tag args) ((<abort> tag args tail)
`(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args))))) `(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)
,(unparse-tree-il tail)))))
(define (tree-il->scheme e) (define (tree-il->scheme e)
(record-case e (record-case e
@ -374,8 +375,9 @@
,(tree-il->scheme handler))) ,(tree-il->scheme handler)))
((<abort> tag args) ((<abort> tag args tail)
`(@abort ,(tree-il->scheme tag) ,@(map tree-il->scheme args))))) `(apply abort ,(tree-il->scheme tag) ,@(map tree-il->scheme args)
,(tree-il->scheme tail)))))
(define (tree-il-fold leaf down up seed tree) (define (tree-il-fold leaf down up seed tree)
@ -444,8 +446,8 @@ This is an implementation of `foldts' as described by Andy Wingo in
(up tree (up tree
(loop tag (loop body (loop handler (loop tag (loop body (loop handler
(down tree result)))))) (down tree result))))))
((<abort> tag args) ((<abort> tag args tail)
(up tree (loop tag (loop args (down tree result))))) (up tree (loop tail (loop args (loop tag (down tree result))))))
(else (else
(leaf tree result)))))) (leaf tree result))))))
@ -518,9 +520,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
(let*-values (((seed ...) (foldts tag seed ...)) (let*-values (((seed ...) (foldts tag seed ...))
((seed ...) (foldts body seed ...))) ((seed ...) (foldts body seed ...)))
(foldts handler seed ...))) (foldts handler seed ...)))
((<abort> tag args) ((<abort> tag args tail)
(let*-values (((seed ...) (foldts tag seed ...))) (let*-values (((seed ...) (foldts tag seed ...))
(fold-values foldts args seed ...))) ((seed ...) (fold-values foldts args seed ...)))
(foldts tail seed ...)))
(else (else
(values seed ...))))) (values seed ...)))))
(up tree seed ...))))))) (up tree seed ...)))))))
@ -599,9 +602,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
(set! (prompt-body x) (lp body)) (set! (prompt-body x) (lp body))
(set! (prompt-handler x) (lp handler))) (set! (prompt-handler x) (lp handler)))
((<abort> tag args) ((<abort> tag args tail)
(set! (abort-tag x) (lp tag)) (set! (abort-tag x) (lp tag))
(set! (abort-args x) (map lp args))) (set! (abort-args x) (map lp args))
(set! (abort-tail x) (lp tail)))
(else #f)) (else #f))
@ -681,9 +685,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
(set! (prompt-body x) (lp body)) (set! (prompt-body x) (lp body))
(set! (prompt-handler x) (lp handler))) (set! (prompt-handler x) (lp handler)))
((<abort> tag args) ((<abort> tag args tail)
(set! (abort-tag x) (lp tag)) (set! (abort-tag x) (lp tag))
(set! (abort-args x) (map lp args))) (set! (abort-args x) (map lp args))
(set! (abort-tail x) (lp tail)))
(else #f)) (else #f))
x))) x)))

View file

@ -351,8 +351,8 @@
((<prompt> tag body handler) ((<prompt> tag body handler)
(lset-union eq? (step tag) (step body) (step handler))) (lset-union eq? (step tag) (step body) (step handler)))
((<abort> tag args) ((<abort> tag args tail)
(apply lset-union eq? (step tag) (map step args))) (apply lset-union eq? (step tag) (step tail) (map step args)))
(else '()))) (else '())))
@ -525,8 +525,8 @@
(and cont-var (zero? (hashq-ref refcounts cont-var 0)))) (and cont-var (zero? (hashq-ref refcounts cont-var 0))))
(max (recur tag) (recur body) (recur handler)))) (max (recur tag) (recur body) (recur handler))))
((<abort> tag args) ((<abort> tag args tail)
(apply max (recur tag) (map recur args))) (apply max (recur tag) (recur tail) (map recur args)))
(else n))) (else n)))

View file

@ -1111,9 +1111,10 @@
(and (eq? context 'drop) (not RA))) (and (eq? context 'drop) (not RA)))
(emit-label POST)))) (emit-label POST))))
((<abort> src tag args) ((<abort> src tag args tail)
(comp-push tag) (comp-push tag)
(for-each comp-push args) (for-each comp-push args)
(comp-push tail)
(emit-code src (make-glil-call 'abort (length args))) (emit-code src (make-glil-call 'abort (length args)))
;; so, the abort can actually return. if it does, the values will be on ;; so, the abort can actually return. if it does, the values will be on
;; the stack, then the MV marker, just as in an MV context. ;; the stack, then the MV marker, just as in an MV context.

View file

@ -63,7 +63,7 @@
fluid-ref fluid-set! fluid-ref fluid-set!
@prompt prompt abort @prompt prompt @abort abort
struct? struct-vtable make-struct struct-ref struct-set! struct? struct-vtable make-struct struct-ref struct-set!
@ -475,9 +475,15 @@
(else #f))) (else #f)))
(else #f))) (else #f)))
(hashq-set! *primitive-expand-table*
'@abort
(case-lambda
((src tag tail-args)
(make-abort src tag '() tail-args))
(else #f)))
(hashq-set! *primitive-expand-table* (hashq-set! *primitive-expand-table*
'abort 'abort
(case-lambda (case-lambda
((src tag . args) ((src tag . args)
(make-abort src tag args)) (make-abort src tag args (make-const #f '())))
(else #f))) (else #f)))