0.pre7.38:
[sbcl.git] / src / compiler / envanal.lisp
index 11b86c1..5592641 100644 (file)
@@ -1,6 +1,6 @@
 ;;;; This file implements the environment analysis phase for the
 ;;;; compiler. This phase annotates IR1 with a hierarchy environment
-;;;; structures, determining the environment that each Lambda
+;;;; structures, determining the environment that each LAMBDA 
 ;;;; allocates its variables and finding what values are closed over
 ;;;; by each environment.
 
 
 (in-package "SB!C")
 
-;;; Do environment analysis on the code in Component. This involves
+;;; Do environment analysis on the code in COMPONENT. This involves
 ;;; various things:
-;;;  1. Make an Environment structure for each non-let lambda, assigning 
-;;;     the lambda-environment for all lambdas.
+;;;  1. Make an ENVIRONMENT structure for each non-LET LAMBDA, assigning 
+;;;     the LAMBDA-ENVIRONMENT for all LAMBDAs.
 ;;;  2. Find all values that need to be closed over by each environment.
 ;;;  3. Scan the blocks in the component closing over non-local-exit
 ;;;     continuations.
 ;;;  4. Delete all non-top-level functions with no references. This
 ;;;     should only get functions with non-NULL kinds, since normal
 ;;;     functions are deleted when their references go to zero. If
-;;;     *byte-compiling*, then don't delete optional entries with no
+;;;     *BYTE-COMPILING*, then don't delete optional entries with no
 ;;;     references, since the byte interpreter wants to call entries
 ;;;     that the XEP doesn't.
 (defun environment-analyze (component)
@@ -49,6 +49,7 @@
     (when (null (leaf-refs fun))
       (let ((kind (functional-kind fun)))
        (unless (or (eq kind :top-level)
+                   (functional-has-external-references-p fun)
                    (and *byte-compiling* (eq kind :optional)))
          (aver (member kind '(:optional :cleanup :escape)))
          (setf (functional-kind fun) nil)
 
   (values))
 
-;;; Called on component with top-level lambdas before the compilation of the
-;;; associated non-top-level code to detect closed over top-level variables.
-;;; We just do COMPUTE-CLOSURE on all the lambdas. This will pre-allocate
-;;; environments for all the functions with closed-over top-level variables.
-;;; The post-pass will use the existing structure, rather than allocating a new
-;;; one. We return true if we discover any possible closure vars.
+;;; This is to be called on a COMPONENT with top-level LAMBDAs before
+;;; the compilation of the associated non-top-level code to detect
+;;; closed over top-level variables. We just do COMPUTE-CLOSURE on all
+;;; the lambdas. This will pre-allocate environments for all the
+;;; functions with closed-over top-level variables. The post-pass will
+;;; use the existing structure, rather than allocating a new one. We
+;;; return true if we discover any possible closure vars.
 (defun pre-environment-analyze-top-level (component)
   (declare (type component component))
   (let ((found-it nil))
          (setq found-it t))))
     found-it))
 
-;;; If Fun has an environment, return it, otherwise assign one.
+;;; This is like old CMU CL PRE-ENVIRONMENT-ANALYZE-TOP-LEVEL, except
+;;;   (1) It's been brought into the post-0.7.0 world where the property
+;;;       HAS-EXTERNAL-REFERENCES-P is orthogonal to the property of
+;;;       being specialized/optimized for locall at top level.
+;;;   (2) There's no return value, since we don't care whether we
+;;;       find any possible closure variables.
+;;;
+;;; I wish I could find an explanation of why
+;;; PRE-ENVIRONMENT-ANALYZE-TOP-LEVEL is important. The old CMU CL
+;;; comments said
+;;;     Called on component with top-level lambdas before the
+;;;     compilation of the associated non-top-level code to detect
+;;;     closed over top-level variables. We just do COMPUTE-CLOSURE on
+;;;     all the lambdas. This will pre-allocate environments for all
+;;;     the functions with closed-over top-level variables. The
+;;;     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
+;;; 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
+(defun preallocate-environments-for-top-levelish-lambdas (component)
+  (dolist (clambda (component-lambdas component))
+    (when (lambda-top-levelish-p clambda)
+      (compute-closure clambda)))
+  (values))
+
+;;; If FUN has an environment, return it, otherwise assign an empty one.
 (defun get-lambda-environment (fun)
   (declare (type clambda fun))
   (let* ((fun (lambda-home fun))
     (or env
        (let ((res (make-environment :function fun)))
          (setf (lambda-environment fun) res)
-         (dolist (lambda (lambda-lets fun))
-           (setf (lambda-environment lambda) res))
+         (dolist (letlambda (lambda-lets fun))
+           ;; This assertion is to make explicit an
+           ;; apparently-otherwise-undocumented property of existing
+           ;; code: We never overwrite an old LAMBDA-ENVIRONMENT.
+           ;; -- WHN 2001-09-30
+           (aver (null (lambda-environment letlambda)))
+           ;; I *think* this is true regardless of LAMBDA-KIND.
+           ;; -- WHN 2001-09-30
+           (aver (eql (lambda-home letlambda) fun))
+           (setf (lambda-environment letlambda) res))
          res))))
 
-;;; If Fun has no environment, assign one, otherwise clean up variables that
-;;; have no sets or refs. If a var has no references, we remove it from the
-;;; closure. If it has no sets, we clear the INDIRECT flag. This is
-;;; necessary because pre-analysis is done before optimization.
+;;; If FUN has no physical environment, assign one, otherwise clean up
+;;; the old physical environment, removing/flagging variables that
+;;; have no sets or refs. If a var has no references, we remove it
+;;; from the closure. If it has no sets, we clear the INDIRECT flag.
+;;; This is necessary because pre-analysis is done before
+;;; optimization.
 (defun reinit-lambda-environment (fun)
   (let ((old (lambda-environment (lambda-home fun))))
     (cond (old
           (get-lambda-environment fun))))
   (values))
 
-;;; Get node's environment, assigning one if necessary.
+;;; Get NODE's environment, assigning one if necessary.
 (defun get-node-environment (node)
   (declare (type node node))
   (get-lambda-environment (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.
+;;; 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-environment fun))
            (close-over var set-env env)))))
     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 reach the home environment,
-;;; we stop propagating the closure.
+;;; 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
+;;; reach the home environment, we stop propagating the closure.
 (defun close-over (thing ref-env home-env)
   (declare (type environment ref-env home-env))
   (cond ((eq ref-env home-env))
 \f
 ;;;; non-local exit
 
-;;; Insert the entry stub before the original exit target, and add a new
-;;; entry to the Environment-Nlx-Info. The %NLX-Entry call in the stub is
-;;; passed the NLX-Info as an argument so that the back end knows what entry is
-;;; being done.
+;;; Insert the entry stub before the original exit target, and add a
+;;; new entry to the ENVIRONMENT-NLX-INFO. The %NLX-ENTRY call in the
+;;; stub is passed the NLX-INFO as an argument so that the back end
+;;; knows what entry is being done.
 ;;;
-;;; The link from the Exit block to the entry stub is changed to be a link to
-;;; the component head. Similarly, the Exit block is linked to the component
-;;; tail. This leaves the entry stub reachable, but makes the flow graph less
-;;; confusing to flow analysis.
+;;; The link from the EXIT block to the entry stub is changed to be a
+;;; link to the component head. Similarly, the EXIT block is linked to
+;;; the component tail. This leaves the entry stub reachable, but
+;;; makes the flow graph less confusing to flow analysis.
 ;;;
-;;; If a catch or an unwind-protect, then we set the Lexenv for the last node
-;;; in the cleanup code to be the enclosing environment, to represent the fact
-;;; that the binding was undone as a side-effect of the exit. This will cause
-;;; a lexical exit to be broken up if we are actually exiting the scope (i.e.
-;;; a BLOCK), and will also do any other cleanups that may have to be done on
-;;; the way.
+;;; If a CATCH or an UNWIND-protect, then we set the LEXENV for the
+;;; last node in the cleanup code to be the enclosing environment, to
+;;; represent the fact that the binding was undone as a side-effect of
+;;; the exit. This will cause a lexical exit to be broken up if we are
+;;; actually exiting the scope (i.e. a BLOCK), and will also do any
+;;; other cleanups that may have to be done on the way.
 (defun insert-nlx-entry-stub (exit env)
   (declare (type environment env) (type exit exit))
   (let* ((exit-block (node-block exit))
 
   (values))
 
-;;; Do stuff necessary to represent a non-local exit from the node Exit into
-;;; Env. This is called for each non-local exit node, of which there may be
-;;; several per exit continuation. This is what we do:
-;;; -- If there isn't any NLX-Info entry in the environment, make an entry
-;;;    stub, otherwise just move the exit block link to the component tail.
+;;; Do stuff necessary to represent a non-local exit from the node
+;;; EXIT into ENV. This is called for each non-local exit node, of
+;;; which there may be several per exit continuation. This is what we
+;;; do:
+;;; -- If there isn't any NLX-Info entry in the environment, make
+;;;    an entry stub, otherwise just move the exit block link to
+;;;    the component tail.
 ;;; -- Close over the NLX-Info in the exit environment.
-;;; -- If the exit is from an :Escape function, then substitute a constant
-;;;    reference to NLX-Info structure for the escape function reference. This
-;;;    will cause the escape function to be deleted (although not removed from
-;;;    the DFO.)  The escape function is no longer needed, and we don't want to
-;;;    emit code for it. We then also change the %NLX-ENTRY call to use
-;;;    the NLX continuation so that there will be a use to represent the NLX
-;;;    use.
+;;; -- If the exit is from an :Escape function, then substitute a
+;;;    constant reference to NLX-Info structure for the escape
+;;;    function reference. This will cause the escape function to
+;;;    be deleted (although not removed from the DFO.)  The escape
+;;;    function is no longer needed, and we don't want to emit code
+;;;    for it. We then also change the %NLX-ENTRY call to use the
+;;;    NLX continuation so that there will be a use to represent
+;;;    the NLX use.
 (defun note-non-local-exit (env exit)
   (declare (type environment env) (type exit exit))
   (let ((entry (exit-entry exit))
 
   (values))
 
-;;; Iterate over the Exits in Component, calling Note-Non-Local-Exit when we
-;;; find a block that ends in a non-local Exit node. We also ensure that all
-;;; Exit nodes are either non-local or degenerate by calling IR1-Optimize-Exit
-;;; on local exits. This makes life simpler for later phases.
+;;; Iterate over the EXITs in COMPONENT, calling NOTE-NON-LOCAL-EXIT
+;;; when we find a block that ends in a non-local EXIT node. We also
+;;; ensure that all EXIT nodes are either non-local or degenerate by
+;;; calling IR1-OPTIMIZE-EXIT on local exits. This makes life simpler
+;;; for later phases.
 (defun find-non-local-exits (component)
   (declare (type component component))
   (dolist (lambda (component-lambdas component))
 \f
 ;;;; cleanup emission
 
-;;; Zoom up the cleanup nesting until we hit Cleanup1, accumulating cleanup
-;;; code as we go. When we are done, convert the cleanup code in an implicit
-;;; MV-Prog1. We have to force local call analysis of new references to
-;;; Unwind-Protect cleanup functions. If we don't actually have to do
-;;; anything, then we don't insert any cleanup code.
+;;; Zoom up the cleanup nesting until we hit CLEANUP1, accumulating
+;;; cleanup code as we go. When we are done, convert the cleanup code
+;;; in an implicit MV-PROG1. We have to force local call analysis of
+;;; new references to UNWIND-PROTECT cleanup functions. If we don't
+;;; actually have to do anything, then we don't insert any cleanup
+;;; code.
 ;;;
-;;; If we do insert cleanup code, we check that Block1 doesn't end in a "tail"
-;;; local call.
+;;; If we do insert cleanup code, we check that BLOCK1 doesn't end in
+;;; a "tail" local call.
 ;;;
-;;; We don't need to adjust the ending cleanup of the cleanup block, since
-;;; the cleanup blocks are inserted at the start of the DFO, and are thus never
-;;; scanned.
+;;; We don't need to adjust the ending cleanup of the cleanup block,
+;;; since the cleanup blocks are inserted at the start of the DFO, and
+;;; are thus never scanned.
 (defun emit-cleanups (block1 block2)
   (declare (type cblock block1 block2))
   (collect ((code)
 
   (values))
 
-;;; Loop over the blocks in component, calling Emit-Cleanups when we see a
-;;; successor in the same environment with a different cleanup. We ignore the
-;;; cleanup transition if it is to a cleanup enclosed by the current cleanup,
-;;; since in that case we are just messing up the environment, hence this is
-;;; not the place to clean it.
+;;; Loop over the blocks in COMPONENT, calling EMIT-CLEANUPS when we
+;;; see a successor in the same environment with a different cleanup.
+;;; We ignore the cleanup transition if it is to a cleanup enclosed by
+;;; the current cleanup, since in that case we are just messing up the
+;;; environment, hence this is not the place to clean it.
 (defun find-cleanup-points (component)
   (declare (type component component))
   (do-blocks (block1 component)
              (emit-cleanups block1 block2)))))))
   (values))
 
-;;; Mark all tail-recursive uses of function result continuations with the
-;;; corresponding tail-set. Nodes whose type is NIL (i.e. don't return) such
-;;; as calls to ERROR are never annotated as tail in order to preserve
-;;; debugging information.
+;;; Mark all tail-recursive uses of function result continuations with
+;;; the corresponding TAIL-SET. Nodes whose type is NIL (i.e. don't
+;;; return) such as calls to ERROR are never annotated as tail in
+;;; order to preserve debugging information.
 (defun tail-annotate (component)
   (declare (type component component))
   (dolist (fun (component-lambdas component))