mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Fixed a stack leak. Now observing actual performance.
* src/*.[ch]: Replaced `scm_mem2symbol' by `scm_from_locale_symboln' and `scm_ulong2num' by `scm_from_ulong'. * src/vm_system.c (tail-call): Fixed stack leak (SP lacked decrement by one more Scheme object in the tail-recursive case). * benchmark/measure.scm (measure): Make sure we are using the compiled procedure (i.e. a program object) when measuring. This yields better results than before. :-) * doc/guile-vm.texi: Augmented the instruction set documentation with branch instructions, `call' and `tail-call'. git-archimport-id: lcourtes@laas.fr--2004-libre/guile-vm--revival--0.6--patch-7
This commit is contained in:
parent
2d80426a3e
commit
f41cb00ce2
10 changed files with 149 additions and 38 deletions
3
README
3
README
|
@ -16,6 +16,9 @@ Status of the last release, 0.5
|
||||||
The very first release, 0.0
|
The very first release, 0.0
|
||||||
http://sources.redhat.com/ml/guile/2000-07/msg00418.html
|
http://sources.redhat.com/ml/guile/2000-07/msg00418.html
|
||||||
|
|
||||||
|
Simple benchmark
|
||||||
|
http://sources.redhat.com/ml/guile/2000-07/msg00425.html
|
||||||
|
|
||||||
Performance, portability, GNU Lightning
|
Performance, portability, GNU Lightning
|
||||||
http://lists.gnu.org/archive/html/guile-devel/2001-03/msg00132.html
|
http://lists.gnu.org/archive/html/guile-devel/2001-03/msg00132.html
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
(g-c-d x (- y x))
|
(g-c-d x (- y x))
|
||||||
(g-c-d (- x y) y))))
|
(g-c-d (- x y) y))))
|
||||||
|
|
||||||
(define (loop how-long)
|
(define (loop n)
|
||||||
;; This one shows that procedure calls are no faster than within the
|
;; This one shows that procedure calls are no faster than within the
|
||||||
;; interpreter: the VM yields no performance improvement.
|
;; interpreter: the VM yields no performance improvement.
|
||||||
(if (= 0 how-long)
|
(if (= 0 n)
|
||||||
0
|
0
|
||||||
(loop (1- how-long))))
|
(loop (1- n))))
|
||||||
|
|
||||||
;; Disassembly of `loop'
|
;; Disassembly of `loop'
|
||||||
;;
|
;;
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
; 11 (link "1-")
|
; 11 (link "1-")
|
||||||
; 15 (vector 3)
|
; 15 (vector 3)
|
||||||
; 17 (make-int8:0) ;; 0
|
; 17 (make-int8:0) ;; 0
|
||||||
; 18 (load-symbol "how-long") ;; how-long
|
; 18 (load-symbol "n") ;; n
|
||||||
; 28 (make-false) ;; #f
|
; 28 (make-false) ;; #f
|
||||||
; 29 (make-int8:0) ;; 0
|
; 29 (make-int8:0) ;; 0
|
||||||
; 30 (list 3)
|
; 30 (list 3)
|
||||||
|
@ -92,25 +92,27 @@
|
||||||
; 23 (tail-call 1)
|
; 23 (tail-call 1)
|
||||||
|
|
||||||
|
|
||||||
(define (loopi how-long)
|
(define (loopi n)
|
||||||
;; Same as `loop'.
|
;; Same as `loop'.
|
||||||
(let loopi ((how-long how-long))
|
(let loopi ((n n))
|
||||||
(if (= 0 how-long)
|
(if (= 0 n)
|
||||||
0
|
0
|
||||||
(loopi (1- how-long)))))
|
(loopi (1- n)))))
|
||||||
|
|
||||||
|
|
||||||
(define (do-cons x)
|
(define (do-cons x)
|
||||||
;; This one shows that the built-in `cons' instruction yields a significant
|
;; This one shows that the built-in `cons' instruction yields a significant
|
||||||
;; improvement (speedup: 1.4).
|
;; improvement (speedup: 1.5).
|
||||||
(let loop ((x x)
|
(let loop ((x x)
|
||||||
(result '()))
|
(result '()))
|
||||||
(if (<= x 0)
|
(if (<= x 0)
|
||||||
result
|
result
|
||||||
(loop (1- x) (cons x result)))))
|
(loop (1- x) (cons x result)))))
|
||||||
|
|
||||||
|
(define big-list (iota 500000))
|
||||||
|
|
||||||
(define (copy-list lst)
|
(define (copy-list lst)
|
||||||
;; Speedup: 1.3.
|
;; Speedup: 5.9.
|
||||||
(let loop ((lst lst)
|
(let loop ((lst lst)
|
||||||
(result '()))
|
(result '()))
|
||||||
(if (null? lst)
|
(if (null? lst)
|
||||||
|
|
|
@ -10,18 +10,21 @@ exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@"
|
||||||
(define-module (measure)
|
(define-module (measure)
|
||||||
:export (measure)
|
:export (measure)
|
||||||
:use-module (system vm core)
|
:use-module (system vm core)
|
||||||
|
:use-module (system vm disasm)
|
||||||
:use-module (system base compile)
|
:use-module (system base compile)
|
||||||
:use-module (system base language))
|
:use-module (system base language))
|
||||||
|
|
||||||
|
|
||||||
(define (time-for-eval sexp eval)
|
(define (time-for-eval sexp eval)
|
||||||
(let ((before (tms:utime (times))))
|
(let ((before (tms:utime (times))))
|
||||||
(eval sexp (current-module))
|
(eval sexp)
|
||||||
(let ((elapsed (- (tms:utime (times)) before)))
|
(let ((elapsed (- (tms:utime (times)) before)))
|
||||||
(format #t "elapsed time: ~a~%" elapsed)
|
(format #t "elapsed time: ~a~%" elapsed)
|
||||||
elapsed)))
|
elapsed)))
|
||||||
|
|
||||||
(define *scheme* (lookup-language 'scheme))
|
(define *scheme* (lookup-language 'scheme))
|
||||||
|
|
||||||
|
|
||||||
(define (measure . args)
|
(define (measure . args)
|
||||||
(if (< (length args) 2)
|
(if (< (length args) 2)
|
||||||
(begin
|
(begin
|
||||||
|
@ -33,13 +36,29 @@ exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@"
|
||||||
(let* ((sexp (with-input-from-string (car args)
|
(let* ((sexp (with-input-from-string (car args)
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(read))))
|
(read))))
|
||||||
(time-interpreted (time-for-eval sexp eval))
|
(eval-here (lambda (sexp) (eval sexp (current-module))))
|
||||||
(objcode (compile-in sexp (current-module) *scheme*))
|
(proc-name (car sexp))
|
||||||
(time-compiled (time-for-eval objcode
|
(proc-source (procedure-source (eval proc-name (current-module))))
|
||||||
(let ((vm (the-vm))
|
(% (format #t "proc: ~a~%source: ~a~%" proc-name proc-source))
|
||||||
(prog (objcode->program objcode)))
|
(time-interpreted (time-for-eval sexp eval-here))
|
||||||
(lambda (o e)
|
(& (if (defined? proc-name)
|
||||||
(vm prog))))))
|
(eval `(set! ,proc-name #f) (current-module))
|
||||||
|
(format #t "unbound~%")))
|
||||||
|
(objcode (compile-in proc-source
|
||||||
|
(current-module) *scheme*))
|
||||||
|
(the-program (vm-load (the-vm) objcode))
|
||||||
|
|
||||||
|
; (%%% (disassemble-objcode objcode))
|
||||||
|
(time-compiled (time-for-eval `(,proc-name ,@(cdr sexp))
|
||||||
|
(lambda (sexp)
|
||||||
|
(eval `(begin
|
||||||
|
(define ,proc-name
|
||||||
|
,the-program)
|
||||||
|
,sexp)
|
||||||
|
(current-module))))))
|
||||||
|
|
||||||
|
(format #t "proc: ~a => ~a~%"
|
||||||
|
proc-name (eval proc-name (current-module)))
|
||||||
(format #t "interpreted: ~a~%" time-interpreted)
|
(format #t "interpreted: ~a~%" time-interpreted)
|
||||||
(format #t "compiled: ~a~%" time-compiled)
|
(format #t "compiled: ~a~%" time-compiled)
|
||||||
(format #t "speedup: ~a~%"
|
(format #t "speedup: ~a~%"
|
||||||
|
|
|
@ -92,6 +92,18 @@ However, be warned that important parts still correspond to version
|
||||||
* Variable Management::
|
* Variable Management::
|
||||||
* Program Execution::
|
* Program Execution::
|
||||||
* Instruction Set::
|
* Instruction Set::
|
||||||
|
|
||||||
|
@detailmenu
|
||||||
|
--- The Detailed Node Listing ---
|
||||||
|
|
||||||
|
Instruction Set
|
||||||
|
|
||||||
|
* Environment Control Instructions::
|
||||||
|
* Branch Instructions::
|
||||||
|
* Subprogram Control Instructions::
|
||||||
|
* Data Control Instructions::
|
||||||
|
|
||||||
|
@end detailmenu
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@c *********************************************************************
|
@c *********************************************************************
|
||||||
|
@ -470,11 +482,12 @@ useful calculations.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Environment Control Instructions::
|
* Environment Control Instructions::
|
||||||
|
* Branch Instructions::
|
||||||
* Subprogram Control Instructions::
|
* Subprogram Control Instructions::
|
||||||
* Data Control Instructions::
|
* Data Control Instructions::
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node Environment Control Instructions, Subprogram Control Instructions, Instruction Set, Instruction Set
|
@node Environment Control Instructions, Branch Instructions, Instruction Set, Instruction Set
|
||||||
@section Environment Control Instructions
|
@section Environment Control Instructions
|
||||||
|
|
||||||
@deffn @insn{} link binding-name
|
@deffn @insn{} link binding-name
|
||||||
|
@ -517,7 +530,61 @@ This call yields the following sequence of instructions:
|
||||||
@item %unbind
|
@item %unbind
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
@node Subprogram Control Instructions, Data Control Instructions, Environment Control Instructions, Instruction Set
|
|
||||||
|
@node Branch Instructions, Subprogram Control Instructions, Environment Control Instructions, Instruction Set
|
||||||
|
@section Branch Instructions
|
||||||
|
|
||||||
|
All the conditional branch instructions described below work in the
|
||||||
|
same way:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item They take the Scheme object located on the stack and use it as
|
||||||
|
the branch condition;
|
||||||
|
@item If the condition if false, then program execution continues with
|
||||||
|
the next instruction;
|
||||||
|
@item If the condition is true, then the instruction pointer is
|
||||||
|
increased by the offset passed as an argument to the branch
|
||||||
|
instruction;
|
||||||
|
@item Finally, when the instruction finished, the condition object is
|
||||||
|
removed from the stack.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Note that the offset passed to the instruction is encoded on two 8-bit
|
||||||
|
integers which are then combined by the VM as one 16-bit integer.
|
||||||
|
|
||||||
|
@deffn @insn{} br offset
|
||||||
|
Jump to @var{offset}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if offset
|
||||||
|
Jump to @var{offset} if the condition on the stack is not false.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if-not offset
|
||||||
|
Jump to @var{offset} if the condition on the stack is false.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if-eq offset
|
||||||
|
Jump to @var{offset} if the two objects located on the stack are
|
||||||
|
equal in the sense of @var{eq?}. Note that, for this instruction, the
|
||||||
|
stack pointer is decremented by two Scheme objects instead of only
|
||||||
|
one.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if-not-eq offset
|
||||||
|
Same as @var{br-if-eq} for non-equal objects.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if-null offset
|
||||||
|
Jump to @var{offset} if the object on the stack is @code{'()}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} br-if-not-null offset
|
||||||
|
Jump to @var{offset} if the object on the stack is not @code{'()}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
@node Subprogram Control Instructions, Data Control Instructions, Branch Instructions, Instruction Set
|
||||||
@section Subprogram Control Instructions
|
@section Subprogram Control Instructions
|
||||||
|
|
||||||
Programs (read: ``compiled procedure'') may refer to external
|
Programs (read: ``compiled procedure'') may refer to external
|
||||||
|
@ -582,10 +649,10 @@ This clearly shows that there is little difference between references
|
||||||
to local variables and references to externally bound variables.
|
to local variables and references to externally bound variables.
|
||||||
|
|
||||||
@deffn @insn{} load-program bytecode
|
@deffn @insn{} load-program bytecode
|
||||||
Load the program whose bytecode is @var{bytecode} (a u8vector) and pop
|
Load the program whose bytecode is @var{bytecode} (a u8vector), pop
|
||||||
its meta-information from the stack. The program's meta-information
|
its meta-information from the stack, and push a corresponding program
|
||||||
may consist of (in the order in which it should be pushed onto the
|
object onto the stack. The program's meta-information may consist of
|
||||||
stack):
|
(in the order in which it should be pushed onto the stack):
|
||||||
|
|
||||||
@itemize
|
@itemize
|
||||||
@item optionally, a pair representing meta-data (see the
|
@item optionally, a pair representing meta-data (see the
|
||||||
|
@ -601,8 +668,6 @@ the number of external variables (@var{nexts}) (see the example
|
||||||
above).
|
above).
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
In the end, push a program object onto the stack.
|
|
||||||
|
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn @insn{} object-ref offset
|
@deffn @insn{} object-ref offset
|
||||||
|
@ -614,6 +679,19 @@ Push the variable object for the external variable located at
|
||||||
Free the program's frame.
|
Free the program's frame.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} call nargs
|
||||||
|
Call the procedure, continuation or program located at
|
||||||
|
@code{sp[-nargs]} with the @var{nargs} arguments located from
|
||||||
|
@code{sp[0]} to @code{sp[-nargs + 1]}. The
|
||||||
|
procedure/continuation/program and its arguments are dropped from the
|
||||||
|
stack and the result is pushed.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn @insn{} tail-call nargs
|
||||||
|
Same as @code{call} except that, for tail-recursive calls to a
|
||||||
|
program, the current stack frame is re-used, as required by RnRS.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
@node Data Control Instructions, , Subprogram Control Instructions, Instruction Set
|
@node Data Control Instructions, , Subprogram Control Instructions, Instruction Set
|
||||||
@section Data Control Instructions
|
@section Data Control Instructions
|
||||||
|
|
|
@ -140,7 +140,7 @@ SCM_DEFINE (scm_frame_return_address, "frame-return-address", 1, 0, 0,
|
||||||
#define FUNC_NAME s_scm_frame_return_address
|
#define FUNC_NAME s_scm_frame_return_address
|
||||||
{
|
{
|
||||||
SCM_VALIDATE_HEAP_FRAME (1, frame);
|
SCM_VALIDATE_HEAP_FRAME (1, frame);
|
||||||
return scm_ulong2num ((unsigned long) (SCM_FRAME_RETURN_ADDRESS
|
return scm_from_ulong ((unsigned long) (SCM_FRAME_RETURN_ADDRESS
|
||||||
(SCM_HEAP_FRAME_POINTER (frame))));
|
(SCM_HEAP_FRAME_POINTER (frame))));
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
|
@ -135,7 +135,7 @@ SCM_DEFINE (scm_program_base, "program-base", 1, 0, 0,
|
||||||
{
|
{
|
||||||
SCM_VALIDATE_PROGRAM (1, program);
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
return scm_ulong2num ((unsigned long) SCM_PROGRAM_DATA (program)->base);
|
return scm_from_ulong ((unsigned long) SCM_PROGRAM_DATA (program)->base);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
6
src/vm.c
6
src/vm.c
|
@ -339,7 +339,7 @@ SCM_DEFINE (scm_vm_ip, "vm:ip", 1, 0, 0,
|
||||||
#define FUNC_NAME s_scm_vm_ip
|
#define FUNC_NAME s_scm_vm_ip
|
||||||
{
|
{
|
||||||
SCM_VALIDATE_VM (1, vm);
|
SCM_VALIDATE_VM (1, vm);
|
||||||
return scm_ulong2num ((unsigned long) SCM_VM_DATA (vm)->ip);
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->ip);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ SCM_DEFINE (scm_vm_sp, "vm:sp", 1, 0, 0,
|
||||||
#define FUNC_NAME s_scm_vm_sp
|
#define FUNC_NAME s_scm_vm_sp
|
||||||
{
|
{
|
||||||
SCM_VALIDATE_VM (1, vm);
|
SCM_VALIDATE_VM (1, vm);
|
||||||
return scm_ulong2num ((unsigned long) SCM_VM_DATA (vm)->sp);
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->sp);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ SCM_DEFINE (scm_vm_fp, "vm:fp", 1, 0, 0,
|
||||||
#define FUNC_NAME s_scm_vm_fp
|
#define FUNC_NAME s_scm_vm_fp
|
||||||
{
|
{
|
||||||
SCM_VALIDATE_VM (1, vm);
|
SCM_VALIDATE_VM (1, vm);
|
||||||
return scm_ulong2num ((unsigned long) SCM_VM_DATA (vm)->fp);
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->fp);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,7 @@
|
||||||
|
|
||||||
#define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0)
|
#define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0)
|
||||||
#define DROP() do { CHECK_UNDERFLOW (); sp--; } while (0)
|
#define DROP() do { CHECK_UNDERFLOW (); sp--; } while (0)
|
||||||
|
#define DROPN(_n) do { CHECK_UNDERFLOW (); sp -= (_n); } while (0)
|
||||||
#define POP(x) do { x = *sp; DROP (); } 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
|
/* A fast CONS. This has to be fast since its used, for instance, by
|
||||||
|
@ -227,8 +228,11 @@
|
||||||
x = scm_cell (SCM_UNPACK (y), SCM_UNPACK (z)); \
|
x = scm_cell (SCM_UNPACK (y), SCM_UNPACK (z)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pop the N objects on top of the stack and push a list that contains
|
||||||
|
them. */
|
||||||
#define POP_LIST(n) \
|
#define POP_LIST(n) \
|
||||||
do { \
|
do \
|
||||||
|
{ \
|
||||||
int i; \
|
int i; \
|
||||||
SCM l = SCM_EOL; \
|
SCM l = SCM_EOL; \
|
||||||
sp -= n; \
|
sp -= n; \
|
||||||
|
|
|
@ -51,7 +51,7 @@ VM_DEFINE_LOADER (load_integer, "load-integer")
|
||||||
long val = 0;
|
long val = 0;
|
||||||
while (len-- > 0)
|
while (len-- > 0)
|
||||||
val = (val << 8) + FETCH ();
|
val = (val << 8) + FETCH ();
|
||||||
PUSH (scm_long2num (val));
|
PUSH (scm_from_ulong (val));
|
||||||
NEXT;
|
NEXT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -84,7 +84,7 @@ VM_DEFINE_LOADER (load_symbol, "load-symbol")
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
FETCH_LENGTH (len);
|
FETCH_LENGTH (len);
|
||||||
PUSH (scm_mem2symbol (ip, len));
|
PUSH (scm_from_locale_symboln (ip, len));
|
||||||
ip += len;
|
ip += len;
|
||||||
NEXT;
|
NEXT;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ VM_DEFINE_LOADER (load_keyword, "load-keyword")
|
||||||
SCM sym;
|
SCM sym;
|
||||||
size_t len;
|
size_t len;
|
||||||
FETCH_LENGTH (len);
|
FETCH_LENGTH (len);
|
||||||
sym = scm_mem2symbol (ip, len);
|
sym = scm_from_locale_symboln (ip, len);
|
||||||
PUSH (scm_make_keyword_from_dash_symbol (sym));
|
PUSH (scm_make_keyword_from_dash_symbol (sym));
|
||||||
ip += len;
|
ip += len;
|
||||||
NEXT;
|
NEXT;
|
||||||
|
@ -104,7 +104,7 @@ VM_DEFINE_LOADER (load_module, "load-module")
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
FETCH_LENGTH (len);
|
FETCH_LENGTH (len);
|
||||||
PUSH (scm_c_lookup_env (scm_mem2symbol (ip, len)));
|
PUSH (scm_c_lookup_env (scm_from_locale_symboln (ip, len)));
|
||||||
ip += len;
|
ip += len;
|
||||||
NEXT;
|
NEXT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,7 +376,10 @@ VM_DEFINE_INSTRUCTION (call, "call", 1, -1, 1)
|
||||||
*/
|
*/
|
||||||
if (!SCM_FALSEP (scm_procedure_p (x)))
|
if (!SCM_FALSEP (scm_procedure_p (x)))
|
||||||
{
|
{
|
||||||
|
/* At this point, the stack contains the procedure and each one of its
|
||||||
|
arguments. */
|
||||||
SCM args;
|
SCM args;
|
||||||
|
|
||||||
POP_LIST (nargs);
|
POP_LIST (nargs);
|
||||||
POP (args);
|
POP (args);
|
||||||
*sp = scm_apply (x, args, SCM_EOL);
|
*sp = scm_apply (x, args, SCM_EOL);
|
||||||
|
@ -407,7 +410,7 @@ VM_DEFINE_INSTRUCTION (call, "call", 1, -1, 1)
|
||||||
|
|
||||||
VM_DEFINE_INSTRUCTION (tail_call, "tail-call", 1, -1, 1)
|
VM_DEFINE_INSTRUCTION (tail_call, "tail-call", 1, -1, 1)
|
||||||
{
|
{
|
||||||
SCM x;
|
register SCM x;
|
||||||
nargs = FETCH ();
|
nargs = FETCH ();
|
||||||
x = sp[-nargs];
|
x = sp[-nargs];
|
||||||
|
|
||||||
|
@ -425,7 +428,9 @@ VM_DEFINE_INSTRUCTION (tail_call, "tail-call", 1, -1, 1)
|
||||||
sp -= bp->nargs - 1;
|
sp -= bp->nargs - 1;
|
||||||
for (i = 0; i < bp->nargs; i++)
|
for (i = 0; i < bp->nargs; i++)
|
||||||
LOCAL_SET (i, sp[i]);
|
LOCAL_SET (i, sp[i]);
|
||||||
sp--;
|
|
||||||
|
/* Drop the first argument and the program itself. */
|
||||||
|
sp -= 2;
|
||||||
|
|
||||||
/* Call itself */
|
/* Call itself */
|
||||||
ip = bp->base;
|
ip = bp->base;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue