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

lambda-lifting for (lambda () ...) as consumer of call-with-values

* libguile/vm-engine.c (vm_run): Add new error case,
  vm_error_not_enough_values.

* libguile/vm-i-system.c (goto/nargs, call/nargs): So, in these cases, if
  we get too many values, we don't truncate the values like we do in the
  single-value continuation case, or in the mvbind case. What to do? I
  guess we either truncate them here, or only allow the correct number of
  values. Dunno. Mark the code as a fixme.
  (truncate-values): New instruction, for mv-bind: checks that the number
  of values on the stack is compatible with the number of bindings we
  have arranged for them, truncating if necessary.

* module/language/scheme/translate.scm (custom-transformer-table):
  Compile receive as a primary form -- not so much because it is a
  primary form, but more to test the mv-bind machinery. Also it's more
  efficient, I think.

* module/system/il/compile.scm (lift-variables!): New helper, factored
  out of `optimize'.
  (optimize): Add a few more cases. Adapt `lambda' optimization, which
  isn't much. I'm not happy with ghil as a mungeable language.
  Add a case for call-with-values with the second argument is
  a lambda: lift the lambda. Untested.
  (codegen): Refactor the push-bindings! code. Compile mv-bind.

* module/system/il/ghil.scm (<ghil-mv-bind>): Add mv-bind construct,
  along with its procedures.

* module/system/il/glil.scm (<glil-mv-bind>): Add mv-bind construct,
  different from the high-level one. It makes sense in the source, I
  think.

* module/system/vm/assemble.scm (codegen): Assemble glil-mv-bind by
  pushing onto the bindings list, and actually push some code to truncate
  the values.
This commit is contained in:
Andy Wingo 2008-09-18 22:49:55 +02:00
parent 87e7228ff6
commit d51406fe87
7 changed files with 110 additions and 14 deletions

View file

@ -172,6 +172,11 @@ vm_run (SCM vm, SCM program, SCM args)
err_args = SCM_EOL; err_args = SCM_EOL;
goto vm_error; goto vm_error;
vm_error_not_enough_values:
err_msg = scm_from_locale_string ("VM: Not enough values for mv-bind");
err_args = SCM_EOL;
goto vm_error;
#if VM_CHECK_IP #if VM_CHECK_IP
vm_error_invalid_address: vm_error_invalid_address:
err_msg = scm_from_locale_string ("VM: Invalid program address"); err_msg = scm_from_locale_string ("VM: Invalid program address");

View file

@ -713,6 +713,7 @@ VM_DEFINE_INSTRUCTION (goto_nargs, "goto/nargs", 0, 0, 1)
SCM x; SCM x;
POP (x); POP (x);
nargs = scm_to_int (x); nargs = scm_to_int (x);
/* FIXME: should truncate values? */
goto vm_goto_args; goto vm_goto_args;
} }
@ -721,6 +722,7 @@ VM_DEFINE_INSTRUCTION (call_nargs, "call/nargs", 0, 0, 1)
SCM x; SCM x;
POP (x); POP (x);
nargs = scm_to_int (x); nargs = scm_to_int (x);
/* FIXME: should truncate values? */
goto vm_call; goto vm_call;
} }
@ -963,6 +965,29 @@ VM_DEFINE_INSTRUCTION (return_values_star, "return/values*", 1, -1, -1)
goto vm_return_values; goto vm_return_values;
} }
VM_DEFINE_INSTRUCTION (truncate_values, "truncate-values", 2, -1, -1)
{
SCM x;
int nbinds, rest;
POP (x);
nvalues = scm_to_int (x);
nbinds = FETCH ();
rest = FETCH ();
if (rest)
nbinds--;
if (nvalues < nbinds)
goto vm_error_not_enough_values;
if (rest)
POP_LIST (nvalues - nbinds);
else
DROPN (nvalues - nbinds);
NEXT;
}
/* /*
Local Variables: Local Variables:
c-file-style: "gnu" c-file-style: "gnu"

View file

@ -25,7 +25,6 @@
#:use-module (system il ghil) #:use-module (system il ghil)
#:use-module (system il inline) #:use-module (system il inline)
#:use-module (ice-9 receive) #:use-module (ice-9 receive)
#:use-module (srfi srfi-39)
#:use-module ((system base compile) #:select (syntax-error)) #:use-module ((system base compile) #:select (syntax-error))
#:export (translate)) #:export (translate))
@ -332,6 +331,17 @@
((,producer ,consumer) ((,producer ,consumer)
(make-ghil-mv-call e l (retrans producer) (retrans consumer)))) (make-ghil-mv-call e l (retrans producer) (retrans consumer))))
(receive
((,formals ,producer-exp . ,body)
;; Lovely, self-referential usage. Not strictly necessary, the
;; macro would do the trick; but it's good to test the mv-bind
;; code.
(receive (syms rest) (parse-formals formals)
(call-with-ghil-bindings e syms
(lambda (vars)
(make-ghil-mv-bind e l (retrans `(lambda () ,producer-exp))
vars rest (trans-body e l body)))))))
(values (values
((,x) (retrans x)) ((,x) (retrans x))
(,args (make-ghil-values e l (map retrans args)))))) (,args (make-ghil-values e l (map retrans args))))))

View file

@ -35,14 +35,32 @@
;;; Stage 2: Optimization ;;; Stage 2: Optimization
;;; ;;;
(define (lift-variables! env)
(let ((parent-env (ghil-env-parent env)))
(for-each (lambda (v)
(case (ghil-var-kind v)
((argument) (set! (ghil-var-kind v) 'local)))
(set! (ghil-var-env v) parent-env)
(ghil-env-add! parent-env v))
(ghil-env-variables env))))
(define (optimize x) (define (optimize x)
(record-case x (record-case x
((<ghil-set> env loc var val) ((<ghil-set> env loc var val)
(make-ghil-set env var (optimize val))) (make-ghil-set env var (optimize val)))
((<ghil-define> env loc var val)
(make-ghil-define env var (optimize val)))
((<ghil-if> env loc test then else) ((<ghil-if> env loc test then else)
(make-ghil-if env loc (optimize test) (optimize then) (optimize else))) (make-ghil-if env loc (optimize test) (optimize then) (optimize else)))
((<ghil-and> env loc exps)
(make-ghil-and env loc (map optimize exps)))
((<ghil-or> env loc exps)
(make-ghil-or env loc (map optimize exps)))
((<ghil-begin> env loc exps) ((<ghil-begin> env loc exps)
(make-ghil-begin env loc (map optimize exps))) (make-ghil-begin env loc (map optimize exps)))
@ -63,17 +81,23 @@
((<ghil-lambda> env loc vars rest meta body) ((<ghil-lambda> env loc vars rest meta body)
(cond (cond
((not rest) ((not rest)
(for-each (lambda (v) (lift-variables! env)
(case (ghil-var-kind v) (make-ghil-bind parent-env loc (map optimize args)))
((argument) (set! (ghil-var-kind v) 'local)))
(set! (ghil-var-env v) parent-env)
(ghil-env-add! parent-env v))
(ghil-env-variables env)))
(else (else
(make-ghil-call parent-env loc (optimize proc) (map optimize args))))) (make-ghil-call parent-env loc (optimize proc) (map optimize args)))))
(else (else
(make-ghil-call parent-env loc (optimize proc) (map optimize args)))))) (make-ghil-call parent-env loc (optimize proc) (map optimize args))))))
((<ghil-mv-call> env loc producer consumer)
(record-case consumer
;; (mv-call PRODUCER (lambda ARGS BODY...)) =>
;; (mv-let PRODUCER ARGS BODY...)
((<ghil-lambda> env loc vars rest meta body)
(lift-variables! env)
(make-ghil-mv-bind producer vars rest body))
(else
(make-ghil-mv-call env loc (optimize producer) (optimize consumer)))))
(else x))) (else x)))
@ -116,15 +140,11 @@
(define (push-code! loc code) (define (push-code! loc code)
(set! stack (cons code stack)) (set! stack (cons code stack))
(if loc (set! stack (cons (make-glil-source loc) stack)))) (if loc (set! stack (cons (make-glil-source loc) stack))))
(define (var->binding var)
(list (ghil-var-name var) (ghil-var-kind var) (ghil-var-index var)))
(define (push-bindings! loc vars) (define (push-bindings! loc vars)
(if (not (null? vars)) (if (not (null? vars))
(push-code! (push-code! loc (make-glil-bind (map var->binding vars)))))
loc
(make-glil-bind
(map list
(map ghil-var-name vars)
(map ghil-var-kind vars)
(map ghil-var-index vars))))))
(define (comp tree tail drop) (define (comp tree tail drop)
(define (push-label! label) (define (push-label! label)
(push-code! #f (make-glil-label label))) (push-code! #f (make-glil-label label)))
@ -290,6 +310,21 @@
(comp-tail body) (comp-tail body)
(push-code! #f (make-glil-unbind))) (push-code! #f (make-glil-unbind)))
((<ghil-mv-bind> env loc producer vars rest body)
;; VALS...
;; (set VARS)...
;; BODY
(let ((MV (make-label)))
(comp-push producer)
(push-code! loc (make-glil-mv-call 0 MV))
(push-code! #f (make-glil-const #:obj 1))
(push-label! MV)
(push-code! #f (make-glil-mv-bind (map var->binding vars) rest))
(for-each (lambda (var) (push-code! #f (make-glil-var 'set env var)))
(reverse vars)))
(comp-tail body)
(push-code! #f (make-glil-unbind)))
((<ghil-lambda> env loc vars rest meta body) ((<ghil-lambda> env loc vars rest meta body)
(return-code! loc (codegen tree))) (return-code! loc (codegen tree)))

View file

@ -62,6 +62,9 @@
<ghil-bind> make-ghil-bind ghil-bind? <ghil-bind> make-ghil-bind ghil-bind?
ghil-bind-env ghil-bind-loc ghil-bind-vars ghil-bind-vals ghil-bind-body ghil-bind-env ghil-bind-loc ghil-bind-vars ghil-bind-vals ghil-bind-body
<ghil-mv-bind> make-ghil-mv-bind ghil-mv-bind?
ghil-mv-bind-env ghil-mv-bind-loc ghil-mv-bind-producer ghil-mv-bind-vars ghil-mv-bind-rest ghil-mv-bind-body
<ghil-lambda> make-ghil-lambda ghil-lambda? <ghil-lambda> make-ghil-lambda ghil-lambda?
ghil-lambda-env ghil-lambda-loc ghil-lambda-vars ghil-lambda-rest ghil-lambda-env ghil-lambda-loc ghil-lambda-vars ghil-lambda-rest
ghil-lambda-meta ghil-lambda-body ghil-lambda-meta ghil-lambda-body
@ -117,6 +120,7 @@
(<ghil-or> env loc exps) (<ghil-or> env loc exps)
(<ghil-begin> env loc exps) (<ghil-begin> env loc exps)
(<ghil-bind> env loc vars vals body) (<ghil-bind> env loc vars vals body)
(<ghil-mv-bind> env loc producer vars rest body)
(<ghil-lambda> env loc vars rest meta body) (<ghil-lambda> env loc vars rest meta body)
(<ghil-call> env loc proc args) (<ghil-call> env loc proc args)
(<ghil-mv-call> env loc producer consumer) (<ghil-mv-call> env loc producer consumer)

View file

@ -32,6 +32,9 @@
<glil-bind> make-glil-bind glil-bind? <glil-bind> make-glil-bind glil-bind?
glil-bind-vars glil-bind-vars
<glil-mv-bind> make-glil-mv-bind glil-mv-bind?
glil-mv-bind-vars glil-mv-bind-rest
<glil-unbind> make-glil-unbind glil-unbind? <glil-unbind> make-glil-unbind glil-unbind?
<glil-source> make-glil-source glil-source? <glil-source> make-glil-source glil-source?
@ -76,6 +79,7 @@
;; Meta operations ;; Meta operations
(<glil-asm> vars meta body) (<glil-asm> vars meta body)
(<glil-bind> vars) (<glil-bind> vars)
(<glil-mv-bind> vars rest)
(<glil-unbind>) (<glil-unbind>)
(<glil-source> loc) (<glil-source> loc)
;; Objects ;; Objects

View file

@ -139,6 +139,19 @@
(set! binding-alist (set! binding-alist
(acons (current-address) bindings binding-alist)))) (acons (current-address) bindings binding-alist))))
((<glil-mv-bind> (binds vars) rest)
(let ((bindings
(map (lambda (v)
(let ((name (car v)) (type (cadr v)) (i (caddr v)))
(case type
((argument) (make-binding name #f i))
((local) (make-binding name #f (+ (glil-vars-nargs vars) i)))
((external) (make-binding name #t i)))))
binds)))
(set! binding-alist
(acons (current-address) bindings binding-alist))
(push-code! `(truncate-values ,(length binds) ,(if rest 1 0)))))
((<glil-unbind>) ((<glil-unbind>)
(set! binding-alist (acons (current-address) #f binding-alist))) (set! binding-alist (acons (current-address) #f binding-alist)))