;;;; 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)
(when (null (leaf-refs fun))
(let ((kind (functional-kind fun)))
(unless (or (eq kind :top-level)
- (and *byte-compiling* (eq kind :optional)))
+ (functional-has-external-references-p fun))
(aver (member kind '(:optional :cleanup :escape)))
(setf (functional-kind fun) nil)
(delete-functional fun)))))
(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))