(setf (component-new-funs component) ())
(dolist (fun (component-lambdas component))
(reinit-lambda-physenv fun))
- (dolist (fun (component-lambdas component))
- (compute-closure fun)
- (dolist (let (lambda-lets fun))
- (compute-closure let)))
+ (mapc #'compute-closure (component-lambdas component))
(find-non-local-exits component)
(find-cleanup-points component)
(let ((found-it nil))
(dolist (lambda (component-lambdas component))
(when (compute-closure lambda)
- (setq found-it t))
- (dolist (let (lambda-lets lambda))
- (when (compute-closure let)
- (setq found-it t))))
+ (setq found-it t)))
found-it))
;;; This is like old CMU CL PRE-ENVIRONMENT-ANALYZE-TOPLEVEL, except
;;; post-pass will use the existing structure, rather than
;;; allocating a new one. We return true if we discover any
;;; possible closure vars.
-;;; But that doesn't seem to explain why it's important. I do observe
-;;; that when it's not done, compiler assertions occasionally fail. My
-;;; tentative hypothesis is that other environment analysis expects to
+;;; But that doesn't seem to explain either why it's important to do
+;;; this for top level lambdas, or why it's important to do it only
+;;; for top level lambdas instead of just doing it indiscriminately
+;;; for all lambdas. I do observe that when it's not done, compiler
+;;; assertions occasionally fail. My tentative hypothesis for why it's
+;;; important to do it is that other environment analysis expects to
;;; bottom out on the outermost enclosing thing, and (insert
;;; mysterious reason here) it's important to set up bottomed-out-here
-;;; environments before anything else. -- WHN 2001-09-30
+;;; environments before anything else. I haven't been able to guess
+;;; why it's important to do it selectively instead of
+;;; indiscriminately. -- WHN 2001-11-10
(defun preallocate-physenvs-for-toplevelish-lambdas (component)
- (/show "entering PREALLOCATE-PHYSENVS-FOR-TOPLEVELISH-LAMDBAS" component)
(dolist (clambda (component-lambdas component))
- (/show clambda (lambda-vars clambda) (lambda-toplevelish-p clambda))
(when (lambda-toplevelish-p clambda)
(compute-closure clambda)))
- (/show "leaving PREALLOCATE-PHYSENVS-FOR-TOPLEVELISH-LAMDBAS" component)
(values))
-;;; If CLAMBDA has a PHYSENV , return it, otherwise assign an empty one.
+;;; If CLAMBDA has a PHYSENV, return it, otherwise assign an empty one
+;;; and return that.
(defun get-lambda-physenv (clambda)
(declare (type clambda clambda))
(let ((homefun (lambda-home clambda)))
(or (lambda-physenv homefun)
- (let ((res (make-physenv :function homefun)))
+ (let ((res (make-physenv :lambda homefun)))
(setf (lambda-physenv homefun) res)
+ ;; All the LETLAMBDAs belong to HOMEFUN, and share the same
+ ;; PHYSENV. Thus, (1) since HOMEFUN's PHYSENV was NIL,
+ ;; theirs should be NIL too, and (2) since we're modifying
+ ;; HOMEFUN's PHYSENV, we should modify theirs, too.
(dolist (letlambda (lambda-lets homefun))
- ;; This assertion is to make explicit an
- ;; apparently-otherwise-undocumented property of existing
- ;; code: We never overwrite an old LAMBDA-PHYSENV.
- ;; -- WHN 2001-09-30
- (aver (null (lambda-physenv letlambda)))
- ;; I *think* this is true regardless of LAMBDA-KIND.
- ;; -- WHN 2001-09-30
(aver (eql (lambda-home letlambda) homefun))
+ (aver (null (lambda-physenv letlambda)))
(setf (lambda-physenv letlambda) res))
res))))
(declare (type node node))
(get-lambda-physenv (node-home-lambda node)))
-;;; Find any variables in FUN with references outside of the home
-;;; environment and close over them. If a closed over variable is set,
-;;; then we set the INDIRECT flag so that we will know the closed over
-;;; value is really a pointer to the value cell. We also warn about
-;;; unreferenced variables here, just because it's a convenient place
-;;; to do it. We return true if we close over anything.
-(defun compute-closure (fun)
- (declare (type clambda fun))
- (let ((env (get-lambda-physenv fun))
- (did-something nil))
- (note-unreferenced-vars fun)
- (dolist (var (lambda-vars fun))
- (dolist (ref (leaf-refs var))
- (let ((ref-env (get-node-physenv ref)))
- (unless (eq ref-env env)
- (when (lambda-var-sets var)
- (setf (lambda-var-indirect var) t))
- (setq did-something t)
- (close-over var ref-env env))))
- (dolist (set (basic-var-sets var))
- (let ((set-env (get-node-physenv set)))
- (unless (eq set-env env)
- (setq did-something t)
- (setf (lambda-var-indirect var) t)
- (close-over var set-env env)))))
- did-something))
+;;; Find any variables in CLAMBDA -- either directly in LAMBDA-VARS or
+;;; in the LAMBDA-VARS of elements of LAMBDA-LETS -- with references
+;;; outside of the home environment and close over them. If a
+;;; closed-over variable is set, then we set the INDIRECT flag so that
+;;; we will know the closed over value is really a pointer to the
+;;; value cell. We also warn about unreferenced variables here, just
+;;; because it's a convenient place to do it. We return true if we
+;;; close over anything.
+(defun compute-closure (clambda)
+ (declare (type clambda clambda))
+ (flet (;; This is the old CMU CL COMPUTE-CLOSURE, which only works
+ ;; on LAMBDA-VARS directly, not on the LAMBDA-VARS of
+ ;; LAMBDA-LETS. It seems never to be valid to use this
+ ;; operation alone, so in SBCL, it's private, and the public
+ ;; interface always runs over all the variables, both the
+ ;; LAMBDA-VARS of CLAMBDA itself and the LAMBDA-VARS of
+ ;; CLAMBDA's LAMBDA-LETS.
+ ;;
+ ;; Note that we don't need to make a distinction between the
+ ;; outer CLAMBDA argument and the inner one, or refer to the
+ ;; outer CLAMBDA argument at all, because the LET-conversion
+ ;; process carefully modifies all the necessary CLAMBDA slots
+ ;; (e.g. LAMBDA-PHYSENV) of a LET-converted CLAMBDA to refer
+ ;; to the new home.
+ (%compute-closure (clambda)
+ (let ((physenv (get-lambda-physenv clambda))
+ (did-something nil))
+ (note-unreferenced-vars clambda)
+ (dolist (var (lambda-vars clambda))
+ (dolist (ref (leaf-refs var))
+ (let ((ref-physenv (get-node-physenv ref)))
+ (unless (eq ref-physenv physenv)
+ (when (lambda-var-sets var)
+ (setf (lambda-var-indirect var) t))
+ (setq did-something t)
+ (close-over var ref-physenv physenv))))
+ (dolist (set (basic-var-sets var))
+ (let ((set-physenv (get-node-physenv set)))
+ (unless (eq set-physenv physenv)
+ (setq did-something t)
+ (setf (lambda-var-indirect var) t)
+ (close-over var set-physenv physenv)))))
+ did-something)))
+ (let ((did-something nil))
+ (when (%compute-closure clambda)
+ (setf did-something t))
+ (dolist (lambda-let (lambda-lets clambda))
+ ;; There's no need to recurse through full COMPUTE-CLOSURE
+ ;; here, since LETS only go one layer deep.
+ (aver (null (lambda-lets lambda-let)))
+ (when (%compute-closure lambda-let)
+ (setf did-something t)))
+ did-something)))
-;;; Make sure that THING is closed over in REF-ENV and in all
-;;; environments for the functions that reference REF-ENV's function
-;;; (not just calls.) HOME-ENV is THING's home environment. When we
+;;; Make sure that THING is closed over in REF-PHYSENV and in all
+;;; PHYSENVs for the functions that reference REF-PHYSENV's function
+;;; (not just calls). HOME-PHYSENV is THING's home environment. When we
;;; reach the home environment, we stop propagating the closure.
-(defun close-over (thing ref-env home-env)
- (declare (type physenv ref-env home-env))
- (cond ((eq ref-env home-env))
- ((member thing (physenv-closure ref-env)))
+(defun close-over (thing ref-physenv home-physenv)
+ (declare (type physenv ref-physenv home-physenv))
+ (cond ((eq ref-physenv home-physenv))
+ ((member thing (physenv-closure ref-physenv)))
(t
- (push thing (physenv-closure ref-env))
- (dolist (call (leaf-refs (physenv-function ref-env)))
- (close-over thing (get-node-physenv call) home-env))))
+ (push thing (physenv-closure ref-physenv))
+ (dolist (call (leaf-refs (physenv-lambda ref-physenv)))
+ (close-over thing (get-node-physenv call) home-physenv))))
(values))
\f
;;;; non-local exit