1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Frame <binding> objects capture frame, can ref value directly

* module/system/repl/debug.scm (print-locals): Adapt to
  frame-binding-ref change.

* module/system/vm/frame.scm (<binding>): Add `frame' field.
  (available-bindings): Capture the frame.
  (binding-ref, binding-set!): New functions, accessing a local variable
  value directly from a frame.
  (frame-binding-ref, frame-binding-set!): Remove.  As these are very
  low-level debugging interfaces introduced in 2.0, never documented,
  and quite tied to the VM, we feel comfortable making this change.
  (frame-call-representation): Adapt to available-bindings change.
  (frame-environment, frame-object-binding): Adapt to binding-ref
  interface change.

* doc/ref/vm.texi (Stack Layout): Mention that slots can be re-used.
  Update disassembly in example.

* doc/ref/api-debug.texi (Frames): Remove documentation for
  frame-local-ref, frame-local-set!, and frame-num-locals.  Replace with
  documentation for frame-bindings, binding accessors, and binding-ref /
  binding-set!.
This commit is contained in:
Andy Wingo 2016-01-31 11:15:58 +01:00
parent 67e8aa85e8
commit cd0b61a04e
4 changed files with 76 additions and 52 deletions

View file

@ -201,16 +201,32 @@ respectively. @xref{VM Concepts}, for more information.
@deffnx {Scheme Procedure} frame-mv-return-address frame
Accessors for the three saved VM registers in a frame: the previous
frame pointer, the single-value return address, and the multiple-value
return address. @xref{Stack Layout}, for more information.
return address. @xref{Stack Layout}, for more information.
@end deffn
@deffn {Scheme Procedure} frame-num-locals frame
@deffnx {Scheme Procedure} frame-local-ref frame i
@deffnx {Scheme Procedure} frame-local-set! frame i val
Accessors for the temporary values corresponding to @var{frame}'s
procedure application. The first local is the first argument given to
the procedure. After the arguments, there are the local variables, and
after that temporary values. @xref{Stack Layout}, for more information.
@deffn {Scheme Procedure} frame-bindings frame
Return a list of binding records indicating the local variables that are
live in a frame.
@end deffn
@deffn {Scheme Procedure} frame-lookup-binding frame var
Fetch the bindings in @var{frame}, and return the first one whose name
is @var{var}, or @code{#f} otherwise.
@end deffn
@deffn {Scheme Procedure} binding-index binding
@deffnx {Scheme Procedure} binding-name binding
@deffnx {Scheme Procedure} binding-slot binding
@deffnx {Scheme Procedure} binding-representation binding
Accessors for the various fields in a binding. The implicit ``callee''
argument is index 0, the first argument is index 1, and so on to the end
of the arguments. After that are temporary variables. Note that if a
variable is dead, it might not be available.
@end deffn
@deffn {Scheme Procedure} binding-ref binding
@deffnx {Scheme Procedure} binding-set! binding val
Accessors for the values of local variables in a frame.
@end deffn
@deffn {Scheme Procedure} display-application frame [port [indent]]

View file

@ -160,10 +160,18 @@ The structure of the top stack frame is as follows:
\------------------/ <- sp
@end example
In the above drawing, the stack grows downward. Usually the procedure
being applied is in local 0, followed by the arguments from local 1.
After that are enough slots to store the various lexically-bound and
temporary values that are needed in the function's application.
In the above drawing, the stack grows downward. At the beginning of a
function call, the procedure being applied is in local 0, followed by
the arguments from local 1. After the procedure checks that it is being
passed a compatible set of arguments, the procedure allocates some
additional space in the frame to hold variables local to the function.
Note that once a value in a local variable slot is no longer needed,
Guile is free to re-use that slot. This applies to the slots that were
initially used for the callee and arguments, too. For this reason,
backtraces in Guile aren't always able to show all of the arguments: it
could be that the slot corresponding to that argument was re-used by
some other variable.
The @dfn{return address} is the @code{ip} that was in effect before this
program was applied. When we return from this activation frame, we will
@ -274,25 +282,26 @@ We can see how these concepts tie together by disassembling the
@smallexample
scheme@@(guile-user)> (define (foo a) (lambda (b) (list foo a b)))
scheme@@(guile-user)> ,x foo
Disassembly of #<procedure foo (a)> at #xddb824:
Disassembly of #<procedure foo (a)> at #xea4ce4:
0 (assert-nargs-ee/locals 2 0) ;; 2 slots (1 arg) at (unknown file):1:0
1 (make-closure 1 6 1) ;; anonymous procedure at #xddb840 (1 free var)
0 (assert-nargs-ee/locals 2 0) ;; 2 slots (1 arg) at (unknown file):1:0
1 (make-closure 1 7 1) ;; anonymous procedure at #xea4d04 (1 free var)
4 (free-set! 1 0 0) ;; free var 0
6 (return 1)
6 (mov 0 1)
7 (return-values 2) ;; 1 value
----------------------------------------
Disassembly of anonymous procedure at #xddb840:
Disassembly of anonymous procedure at #xea4d04:
0 (assert-nargs-ee/locals 2 2) ;; 4 slots (1 arg) at (unknown file):1:16
1 (toplevel-box 1 73 57 67 #t) ;; `foo'
0 (assert-nargs-ee/locals 2 2) ;; 4 slots (1 arg) at (unknown file):1:16
1 (toplevel-box 1 74 58 68 #t) ;; `foo'
6 (box-ref 1 1)
7 (make-short-immediate 0 772) ;; () at (unknown file):1:28
8 (cons 2 2 0)
9 (free-ref 3 3 0) ;; free var 0
11 (cons 3 3 2)
12 (cons 3 1 3)
13 (return 3)
12 (cons 2 1 3)
13 (return-values 2) ;; 1 value
@end smallexample
First there's some prelude, where @code{foo} checks that it was called

View file

@ -115,7 +115,7 @@
(format port "~aLocal variables:~%" per-line-prefix)
(for-each
(lambda (binding)
(let ((v (frame-binding-ref frame binding)))
(let ((v (binding-ref binding)))
(display per-line-prefix port)
(run-hook before-print-hook v)
(format port "~a = ~v:@y\n" (binding-name binding) width v)))

View file

@ -33,10 +33,11 @@
binding-slot
binding-representation
frame-instruction-pointer-or-primitive-procedure-name
frame-bindings
frame-lookup-binding
frame-binding-ref frame-binding-set!
binding-ref binding-set!
frame-instruction-pointer-or-primitive-procedure-name
frame-call-representation
frame-environment
frame-object-binding frame-object-name))
@ -46,8 +47,9 @@
"scm_init_frames_builtins"))
(define-record-type <binding>
(make-binding idx name slot representation)
(make-binding frame idx name slot representation)
binding?
(frame binding-frame)
(idx binding-index)
(name binding-name)
(slot binding-slot)
@ -206,7 +208,7 @@
(lp (1+ n) (+ pos (vector-ref parsed n)))))
killv))
(define (available-bindings arity ip top-frame?)
(define (available-bindings frame arity ip top-frame?)
(let* ((defs (list->vector (arity-definitions arity)))
(code (arity-code arity))
(parsed (parse-code code))
@ -282,7 +284,7 @@
(if n
(match (vector-ref defs n)
(#(name def-offset slot representation)
(cons (make-binding n name slot representation)
(cons (make-binding frame n name slot representation)
(lp (1+ n)))))
'()))))
(lp (1+ n) (- offset (vector-ref parsed n)))))))
@ -292,7 +294,7 @@
(cond
((find-program-arity ip)
=> (lambda (arity)
(available-bindings arity ip top-frame?)))
(available-bindings frame arity ip top-frame?)))
(else '()))))
(define (frame-lookup-binding frame var)
@ -304,22 +306,18 @@
(else
(lp (cdr bindings))))))
(define (frame-binding-set! frame var val)
(let ((binding (if (binding? var)
var
(or (frame-lookup-binding frame var)
(error "variable not bound in frame" var frame)))))
(frame-local-set! frame (binding-slot binding) val
(binding-representation binding))))
(define (frame-binding-ref frame var)
(let ((binding (if (binding? var)
var
(or (frame-lookup-binding frame var)
(error "variable not bound in frame" var frame)))))
(frame-local-ref frame (binding-slot binding)
(binding-representation binding))))
(define (binding-ref binding)
(frame-local-ref (or (binding-frame binding)
(error "binding has no frame" binding))
(binding-slot binding)
(binding-representation binding)))
(define (binding-set! binding val)
(frame-local-set! (or (binding-frame binding)
(error "binding has no frame" binding))
(binding-slot binding)
val
(binding-representation binding)))
(define* (frame-procedure-name frame #:key
(info (find-program-debug-info
@ -443,12 +441,13 @@
=> (lambda (arity)
(if (and top-frame? (eqv? ip (arity-low-pc arity)))
(application-arguments)
(reconstruct-arguments (available-bindings arity ip top-frame?)
(arity-nreq arity)
(arity-nopt arity)
(arity-keyword-args arity)
(arity-has-rest? arity)
1))))
(reconstruct-arguments
(available-bindings frame arity ip top-frame?)
(arity-nreq arity)
(arity-nopt arity)
(arity-keyword-args arity)
(arity-has-rest? arity)
1))))
((and (primitive-code? ip)
(program-arguments-alist (frame-local-ref frame 0 'scm) ip))
=> (lambda (args)
@ -470,12 +469,12 @@
(define (frame-environment frame)
(map (lambda (binding)
(cons (binding-name binding) (frame-binding-ref frame binding)))
(cons (binding-name binding) (binding-ref binding)))
(frame-bindings frame)))
(define (frame-object-binding frame obj)
(do ((bs (frame-bindings frame) (cdr bs)))
((or (null? bs) (eq? obj (frame-binding-ref frame (car bs))))
((or (null? bs) (eq? obj (binding-ref (car bs))))
(and (pair? bs) (car bs)))))
(define (frame-object-name frame obj)