;;;; files for more information.
(in-package "SB!C")
-
-(file-comment
- "$Header$")
\f
;;;; interface for obtaining results of constant folding
-;;; Return true if the sole use of Cont is a reference to a constant leaf.
-(declaim (ftype (function (continuation) boolean) constant-continuation-p))
-(defun constant-continuation-p (cont)
- (let ((use (continuation-use cont)))
- (and (ref-p use)
- (constant-p (ref-leaf use)))))
+;;; Return true for a CONTINUATION whose sole use is a reference to a
+;;; constant leaf.
+(defun constant-continuation-p (thing)
+ (and (continuation-p thing)
+ (let ((use (continuation-use thing)))
+ (and (ref-p use)
+ (constant-p (ref-leaf use))))))
;;; Return the constant value for a continuation whose only use is a
;;; constant node.
(declaim (ftype (function (continuation) t) continuation-value))
(defun continuation-value (cont)
- (assert (constant-continuation-p cont))
+ (aver (constant-continuation-p cont))
(constant-value (ref-leaf (continuation-use cont))))
\f
;;;; interface for obtaining results of type inference
(reoptimize-continuation (node-cont node))))))
(values))
-;;; Similar to Derive-Node-Type, but asserts that it is an error for
-;;; Cont's value not to be typep to Type. If we improve the assertion,
-;;; we set TYPE-CHECK and TYPE-ASSERTED to guarantee that the new
-;;; assertion will be checked.
+;;; This is similar to DERIVE-NODE-TYPE, but asserts that it is an
+;;; error for CONT's value not to be TYPEP to TYPE. If we improve the
+;;; assertion, we set TYPE-CHECK and TYPE-ASSERTED to guarantee that
+;;; the new assertion will be checked.
(defun assert-continuation-type (cont type)
(declare (type continuation cont) (type ctype type))
(let ((cont-type (continuation-asserted-type cont)))
(reoptimize-continuation cont)))))
(values))
-;;; Assert that Call is to a function of the specified Type. It is
+;;; Assert that CALL is to a function of the specified TYPE. It is
;;; assumed that the call is legal and has only constants in the
;;; keyword positions.
(defun assert-call-type (call type)
- (declare (type combination call) (type function-type type))
- (derive-node-type call (function-type-returns type))
+ (declare (type combination call) (type fun-type type))
+ (derive-node-type call (fun-type-returns type))
(let ((args (combination-args call)))
- (dolist (req (function-type-required type))
+ (dolist (req (fun-type-required type))
(when (null args) (return-from assert-call-type))
(let ((arg (pop args)))
(assert-continuation-type arg req)))
- (dolist (opt (function-type-optional type))
+ (dolist (opt (fun-type-optional type))
(when (null args) (return-from assert-call-type))
(let ((arg (pop args)))
(assert-continuation-type arg opt)))
- (let ((rest (function-type-rest type)))
+ (let ((rest (fun-type-rest type)))
(when rest
(dolist (arg args)
(assert-continuation-type arg rest))))
- (dolist (key (function-type-keywords type))
+ (dolist (key (fun-type-keywords type))
(let ((name (key-info-name key)))
(do ((arg args (cddr arg)))
((null arg))
\f
;;;; IR1-OPTIMIZE
-;;; Do one forward pass over Component, deleting unreachable blocks
+;;; Do one forward pass over COMPONENT, deleting unreachable blocks
;;; and doing IR1 optimizations. We can ignore all blocks that don't
-;;; have the Reoptimize flag set. If Component-Reoptimize is true when
+;;; have the REOPTIMIZE flag set. If COMPONENT-REOPTIMIZE is true when
;;; we are done, then another iteration would be beneficial.
;;;
;;; We delete blocks when there is either no predecessor or the block
(return)))
(when (and (block-reoptimize block) (block-component block))
- (assert (not (block-delete-p block)))
+ (aver (not (block-delete-p block)))
(ir1-optimize-block block))
(when (and (block-flush-p block) (block-component block))
- (assert (not (block-delete-p block)))
+ (aver (not (block-delete-p block)))
(flush-dead-code block)))))
(values))
;;; -- With a combination, we call Propagate-Function-Change whenever
;;; the function changes, and call IR1-Optimize-Combination if any
;;; argument changes.
-;;; -- With an Exit, we derive the node's type from the Value's type. We don't
-;;; propagate Cont's assertion to the Value, since if we did, this would
-;;; move the checking of Cont's assertion to the exit. This wouldn't work
-;;; with Catch and UWP, where the Exit node is just a placeholder for the
-;;; actual unknown exit.
+;;; -- With an Exit, we derive the node's type from the Value's type.
+;;; We don't propagate Cont's assertion to the Value, since if we
+;;; did, this would move the checking of Cont's assertion to the
+;;; exit. This wouldn't work with Catch and UWP, where the Exit
+;;; node is just a placeholder for the actual unknown exit.
;;;
-;;; Note that we clear the node & block reoptimize flags *before* doing the
-;;; optimization. This ensures that the node or block will be reoptimized if
-;;; necessary. We leave the NODE-OPTIMIZE flag set going into
-;;; IR1-OPTIMIZE-RETURN, since IR1-OPTIMIZE-RETURN wants to clear the flag
-;;; itself.
+;;; Note that we clear the node & block reoptimize flags *before*
+;;; doing the optimization. This ensures that the node or block will
+;;; be reoptimized if necessary. We leave the NODE-OPTIMIZE flag set
+;;; going into IR1-OPTIMIZE-RETURN, since IR1-OPTIMIZE-RETURN wants to
+;;; clear the flag itself.
(defun ir1-optimize-block (block)
(declare (type cblock block))
(setf (block-reoptimize block) nil)
;;; We cannot combine with a successor block if:
;;; 1. The successor has more than one predecessor.
-;;; 2. The last node's Cont is also used somewhere else.
+;;; 2. The last node's CONT is also used somewhere else.
;;; 3. The successor is the current block (infinite loop).
-;;; 4. The next block has a different cleanup, and thus we may want to insert
-;;; cleanup code between the two blocks at some point.
-;;; 5. The next block has a different home lambda, and thus the control
-;;; transfer is a non-local exit.
+;;; 4. The next block has a different cleanup, and thus we may want
+;;; to insert cleanup code between the two blocks at some point.
+;;; 5. The next block has a different home lambda, and thus the
+;;; control transfer is a non-local exit.
;;;
;;; If we succeed, we return true, otherwise false.
;;;
-;;; Joining is easy when the successor's Start continuation is the same from
-;;; our Last's Cont. If they differ, then we can still join when the last
-;;; continuation has no next and the next continuation has no uses. In this
-;;; case, we replace the next continuation with the last before joining the
-;;; blocks.
+;;; Joining is easy when the successor's Start continuation is the
+;;; same from our Last's Cont. If they differ, then we can still join
+;;; when the last continuation has no next and the next continuation
+;;; has no uses. In this case, we replace the next continuation with
+;;; the last before joining the blocks.
(defun join-successor-if-possible (block)
(declare (type cblock block))
(let ((next (first (block-succ block))))
((and (null (block-start-uses next))
(eq (continuation-kind last-cont) :inside-block))
(let ((next-node (continuation-next next-cont)))
- ;; If next-cont does have a dest, it must be unreachable,
- ;; since there are no uses. DELETE-CONTINUATION will mark the
- ;; dest block as delete-p [and also this block, unless it is
- ;; no longer backward reachable from the dest block.]
+ ;; If next-cont does have a dest, it must be
+ ;; unreachable, since there are no uses.
+ ;; DELETE-CONTINUATION will mark the dest block as
+ ;; delete-p [and also this block, unless it is no
+ ;; longer backward reachable from the dest block.]
(delete-continuation next-cont)
(setf (node-prev next-node) last-cont)
(setf (continuation-next last-cont) next-node)
nil))))))
;;; Join together two blocks which have the same ending/starting
-;;; continuation. The code in Block2 is moved into Block1 and Block2 is
-;;; deleted from the DFO. We combine the optimize flags for the two blocks so
-;;; that any indicated optimization gets done.
+;;; continuation. The code in Block2 is moved into Block1 and Block2
+;;; is deleted from the DFO. We combine the optimize flags for the two
+;;; blocks so that any indicated optimization gets done.
(defun join-blocks (block1 block2)
(declare (type cblock block1 block2))
(let* ((last (block-last block2))
(values))
-;;; Delete any nodes in Block whose value is unused and have no
+;;; Delete any nodes in BLOCK whose value is unused and have no
;;; side-effects. We can delete sets of lexical variables when the set
;;; variable has no references.
;;;
-;;; [### For now, don't delete potentially flushable calls when they have the
-;;; Call attribute. Someday we should look at the funcitonal args to determine
-;;; if they have any side-effects.]
+;;; [### For now, don't delete potentially flushable calls when they
+;;; have the CALL attribute. Someday we should look at the funcitonal
+;;; args to determine if they have any side-effects.]
(defun flush-dead-code (block)
(declare (type cblock block))
(do-nodes-backwards (node cont block)
\f
;;;; local call return type propagation
-;;; This function is called on RETURN nodes that have their REOPTIMIZE flag
-;;; set. It iterates over the uses of the RESULT, looking for interesting
-;;; stuff to update the TAIL-SET. If a use isn't a local call, then we union
-;;; its type together with the types of other such uses. We assign to the
-;;; RETURN-RESULT-TYPE the intersection of this type with the RESULT's asserted
-;;; type. We can make this intersection now (potentially before type checking)
-;;; because this assertion on the result will eventually be checked (if
+;;; This function is called on RETURN nodes that have their REOPTIMIZE
+;;; flag set. It iterates over the uses of the RESULT, looking for
+;;; interesting stuff to update the TAIL-SET. If a use isn't a local
+;;; call, then we union its type together with the types of other such
+;;; uses. We assign to the RETURN-RESULT-TYPE the intersection of this
+;;; type with the RESULT's asserted type. We can make this
+;;; intersection now (potentially before type checking) because this
+;;; assertion on the result will eventually be checked (if
;;; appropriate.)
;;;
-;;; We call MAYBE-CONVERT-TAIL-LOCAL-CALL on each local non-MV combination,
-;;; which may change the succesor of the call to be the called function, and if
-;;; so, checks if the call can become an assignment. If we convert to an
-;;; assignment, we abort, since the RETURN has been deleted.
+;;; We call MAYBE-CONVERT-TAIL-LOCAL-CALL on each local non-MV
+;;; combination, which may change the succesor of the call to be the
+;;; called function, and if so, checks if the call can become an
+;;; assignment. If we convert to an assignment, we abort, since the
+;;; RETURN has been deleted.
(defun find-result-type (node)
(declare (type creturn node))
(let ((result (return-result node)))
(do-uses (use result)
(cond ((and (basic-combination-p use)
(eq (basic-combination-kind use) :local))
- (assert (eq (lambda-tail-set (node-home-lambda use))
- (lambda-tail-set (combination-lambda use))))
+ (aver (eq (lambda-tail-set (node-home-lambda use))
+ (lambda-tail-set (combination-lambda use))))
(when (combination-p use)
(when (nth-value 1 (maybe-convert-tail-local-call use))
(return-from find-result-type (values)))))
(setf (return-result-type node) int))))
(values))
-;;; Do stuff to realize that something has changed about the value delivered
-;;; to a return node. Since we consider the return values of all functions in
-;;; the tail set to be equivalent, this amounts to bringing the entire tail set
-;;; up to date. We iterate over the returns for all the functions in the tail
-;;; set, reanalyzing them all (not treating Node specially.)
+;;; Do stuff to realize that something has changed about the value
+;;; delivered to a return node. Since we consider the return values of
+;;; all functions in the tail set to be equivalent, this amounts to
+;;; bringing the entire tail set up to date. We iterate over the
+;;; returns for all the functions in the tail set, reanalyzing them
+;;; all (not treating Node specially.)
;;;
-;;; When we are done, we check whether the new type is different from the old
-;;; TAIL-SET-TYPE. If so, we set the type and also reoptimize all the
-;;; continuations for references to functions in the tail set. This will cause
-;;; IR1-OPTIMIZE-COMBINATION to derive the new type as the results of the
-;;; calls.
+;;; When we are done, we check whether the new type is different from
+;;; the old TAIL-SET-TYPE. If so, we set the type and also reoptimize
+;;; all the continuations for references to functions in the tail set.
+;;; This will cause IR1-OPTIMIZE-COMBINATION to derive the new type as
+;;; the results of the calls.
(defun ir1-optimize-return (node)
(declare (type creturn node))
(let* ((tails (lambda-tail-set (return-lambda node)))
- (funs (tail-set-functions tails)))
+ (funs (tail-set-funs tails)))
(collect ((res *empty-type* values-type-union))
(dolist (fun funs)
(let ((return (lambda-return fun)))
(when (type/= (res) (tail-set-type tails))
(setf (tail-set-type tails) (res))
- (dolist (fun (tail-set-functions tails))
+ (dolist (fun (tail-set-funs tails))
(dolist (ref (leaf-refs fun))
(reoptimize-continuation (node-cont ref)))))))
(if (continuation-value test)
(if-alternative node)
(if-consequent node)))
- ((not (types-intersect type (specifier-type 'null)))
+ ((not (types-equal-or-intersect type (specifier-type 'null)))
(if-alternative node))
((type= type (specifier-type 'null))
(if-consequent node)))))
;;; This function attempts to delete an exit node, returning true if
;;; it deletes the block as a consequence:
-;;; -- If the exit is degenerate (has no Entry), then we don't do anything,
-;;; since there is nothing to be done.
-;;; -- If the exit node and its Entry have the same home lambda then we know
-;;; the exit is local, and can delete the exit. We change uses of the
-;;; Exit-Value to be uses of the original continuation, then unlink the
-;;; node. If the exit is to a TR context, then we must do MERGE-TAIL-SETS
-;;; on any local calls which delivered their value to this exit.
-;;; -- If there is no value (as in a GO), then we skip the value semantics.
+;;; -- If the exit is degenerate (has no Entry), then we don't do
+;;; anything, since there is nothing to be done.
+;;; -- If the exit node and its Entry have the same home lambda then
+;;; we know the exit is local, and can delete the exit. We change
+;;; uses of the Exit-Value to be uses of the original continuation,
+;;; then unlink the node. If the exit is to a TR context, then we
+;;; must do MERGE-TAIL-SETS on any local calls which delivered
+;;; their value to this exit.
+;;; -- If there is no value (as in a GO), then we skip the value
+;;; semantics.
;;;
;;; This function is also called by environment analysis, since it
;;; wants all exits to be optimized even if normal optimization was
#!+sb-show
(defvar *show-transforms-p* nil)
-;;; Do IR1 optimizations on a Combination node.
+;;; Do IR1 optimizations on a COMBINATION node.
(declaim (ftype (function (combination) (values)) ir1-optimize-combination))
(defun ir1-optimize-combination (node)
(when (continuation-reoptimize (basic-combination-fun node))
;; cross-compiler doesn't know how to evaluate it.
#+sb-xc-host
(let* ((ref (continuation-use (combination-fun node)))
- (fun (leaf-name (ref-leaf ref))))
- (fboundp fun)))
+ (fun-name (leaf-source-name (ref-leaf ref))))
+ (fboundp fun-name)))
(constant-fold-call node)
(return-from ir1-optimize-combination)))
#!+sb-show
(when *show-transforms-p*
(let* ((cont (basic-combination-fun node))
- (fname (continuation-function-name cont t)))
+ (fname (continuation-fun-name cont t)))
(/show "trying transform" x (transform-function x) "for" fname)))
(unless (ir1-transform node x)
#!+sb-show
(tail (component-tail (block-component block)))
(succ (first (block-succ block))))
(unless (or (and (eq call (block-last block)) (eq succ tail))
- (block-delete-p block)
- *converting-for-interpreter*)
+ (block-delete-p block))
(when (or (and (eq (continuation-asserted-type cont) *empty-type*)
(not (or ir1-p (eq (continuation-kind cont) :deleted))))
(eq (node-derived-type call) *empty-type*))
(delete-continuation-use call)
(cond
((block-last block)
- (assert (and (eq (block-last block) call)
- (eq (continuation-kind cont) :block-start))))
+ (aver (and (eq (block-last block) call)
+ (eq (continuation-kind cont) :block-start))))
(t
(setf (block-last block) call)
(link-blocks block (continuation-starts-block cont)))))
(unlink-blocks block (first (block-succ block)))
(setf (component-reanalyze (block-component block)) t)
- (assert (not (block-succ block)))
+ (aver (not (block-succ block)))
(link-blocks block tail)
(add-continuation-use call (make-continuation))
t))))
-;;; Called both by IR1 conversion and IR1 optimization when they have
-;;; verified the type signature for the call, and are wondering if
-;;; something should be done to special-case the call. If Call is a
-;;; call to a global function, then see whether it defined or known:
-;;; -- If a DEFINED-FUNCTION should be inline expanded, then convert the
-;;; expansion and change the call to call it. Expansion is enabled if
-;;; :INLINE or if space=0. If the FUNCTIONAL slot is true, we never expand,
-;;; since this function has already been converted. Local call analysis
-;;; will duplicate the definition if necessary. We claim that the parent
-;;; form is LABELS for context declarations, since we don't want it to be
-;;; considered a real global function.
-;;; -- In addition to a direct check for the function name in the table, we
-;;; also must check for slot accessors. If the function is a slot accessor,
-;;; then we set the combination kind to the function info of %Slot-Setter or
-;;; %Slot-Accessor, as appropriate.
-;;; -- If it is a known function, mark it as such by setting the Kind.
+;;; This is called both by IR1 conversion and IR1 optimization when
+;;; they have verified the type signature for the call, and are
+;;; wondering if something should be done to special-case the call. If
+;;; CALL is a call to a global function, then see whether it defined
+;;; or known:
+;;; -- If a DEFINED-FUN should be inline expanded, then convert
+;;; the expansion and change the call to call it. Expansion is
+;;; enabled if :INLINE or if SPACE=0. If the FUNCTIONAL slot is
+;;; true, we never expand, since this function has already been
+;;; converted. Local call analysis will duplicate the definition if
+;;; necessary. We claim that the parent form is LABELS for context
+;;; declarations, since we don't want it to be considered a real
+;;; global function.
+;;; -- In addition to a direct check for the function name in the
+;;; table, we also must check for slot accessors. If the function
+;;; is a slot accessor, then we set the combination kind to the
+;;; function info of %Slot-Setter or %Slot-Accessor, as
+;;; appropriate.
+;;; -- If it is a known function, mark it as such by setting the KIND.
;;;
;;; We return the leaf referenced (NIL if not a leaf) and the
-;;; function-info assigned.
+;;; FUNCTION-INFO assigned.
(defun recognize-known-call (call ir1-p)
(declare (type combination call))
(let* ((ref (continuation-use (basic-combination-fun call)))
(leaf (when (ref-p ref) (ref-leaf ref)))
- (inlinep (if (and (defined-function-p leaf)
- (not (byte-compiling)))
- (defined-function-inlinep leaf)
+ (inlinep (if (defined-fun-p leaf)
+ (defined-fun-inlinep leaf)
:no-chance)))
(cond
((eq inlinep :notinline) (values nil nil))
(:inline t)
(:no-chance nil)
((nil :maybe-inline) (policy call (zerop space))))
- (defined-function-inline-expansion leaf)
- (let ((fun (defined-function-functional leaf)))
+ ;; FIXME: In sbcl-0.pre7.87, it looks as though we'll
+ ;; get here when LEAF is a GLOBAL-VAR (not a DEFINED-FUN)
+ ;; whenever (ZEROP SPACE), in which case we'll die with
+ ;; a type error when we try to access LEAF as a DEFINED-FUN.
+ (defined-fun-inline-expansion leaf)
+ (let ((fun (defined-fun-functional leaf)))
(or (not fun)
(and (eq inlinep :inline) (functional-kind fun))))
(inline-expansion-ok call))
(flet ((frob ()
(let ((res (ir1-convert-lambda-for-defun
- (defined-function-inline-expansion leaf)
+ (defined-fun-inline-expansion leaf)
leaf t
#'ir1-convert-inline-lambda)))
- (setf (defined-function-functional leaf) res)
+ (setf (defined-fun-functional leaf) res)
(change-ref-leaf ref res))))
(if ir1-p
(frob)
(with-ir1-environment call
(frob)
- (local-call-analyze *current-component*))))
+ (locall-analyze-component *current-component*))))
(values (ref-leaf (continuation-use (basic-combination-fun call)))
nil))
(t
- (let* ((name (leaf-name leaf))
+ (let* ((name (leaf-source-name leaf))
(info (info :function :info
(if (slot-accessor-p leaf)
- (if (consp name)
- '%slot-setter
- '%slot-accessor)
- name))))
+ (if (consp source-name) ; i.e. if SETF function
+ '%slot-setter
+ '%slot-accessor)
+ name))))
(if info
(values leaf (setf (basic-combination-kind call) info))
(values leaf nil)))))))
;;; and that checking is done by local call analysis.
(defun validate-call-type (call type ir1-p)
(declare (type combination call) (type ctype type))
- (cond ((not (function-type-p type))
- (assert (multiple-value-bind (val win)
- (csubtypep type (specifier-type 'function))
- (or val (not win))))
+ (cond ((not (fun-type-p type))
+ (aver (multiple-value-bind (val win)
+ (csubtypep type (specifier-type 'function))
+ (or val (not win))))
(recognize-known-call call ir1-p))
((valid-function-use call type
:argument-test #'always-subtypep
:result-test #'always-subtypep
- :error-function #'compiler-warning
+ ;; KLUDGE: Common Lisp is such a dynamic
+ ;; language that all we can do here in
+ ;; general is issue a STYLE-WARNING. It
+ ;; would be nice to issue a full WARNING
+ ;; in the special case of of type
+ ;; mismatches within a compilation unit
+ ;; (as in section 3.2.2.3 of the spec)
+ ;; but at least as of sbcl-0.6.11, we
+ ;; don't keep track of whether the
+ ;; mismatched data came from the same
+ ;; compilation unit, so we can't do that.
+ ;; -- WHN 2001-02-11
+ ;;
+ ;; FIXME: Actually, I think we could
+ ;; issue a full WARNING if the call
+ ;; violates a DECLAIM FTYPE.
+ :error-function #'compiler-style-warning
:warning-function #'compiler-note)
(assert-call-type call type)
(maybe-terminate-block call ir1-p)
(values nil nil))))
;;; This is called by IR1-OPTIMIZE when the function for a call has
-;;; changed. If the call is local, we try to let-convert it, and
+;;; changed. If the call is local, we try to LET-convert it, and
;;; derive the result type. If it is a :FULL call, we validate it
;;; against the type, which recognizes known calls, does inline
;;; expansion, etc. If a call to a predicate in a non-conditional
(continuation-use (basic-combination-fun call))
call))
((not leaf))
- ((or (info :function :source-transform (leaf-name leaf))
+ ((or (info :function :source-transform (leaf-source-name leaf))
(and info
(ir1-attributep (function-info-attributes info)
predicate)
(let ((dest (continuation-dest (node-cont call))))
(and dest (not (if-p dest))))))
- (let ((name (leaf-name leaf)))
- (when (symbolp name)
- (let ((dums (make-gensym-list (length
- (combination-args call)))))
- (transform-call call
- `(lambda ,dums
- (,name ,@dums))))))))))))
+ (when (and (leaf-has-source-name-p leaf)
+ ;; FIXME: This SYMBOLP is part of a literal
+ ;; translation of a test in the old CMU CL
+ ;; source, and it's not quite clear what
+ ;; the old source meant. Did it mean "has a
+ ;; valid name"? Or did it mean "is an
+ ;; ordinary function name, not a SETF
+ ;; function"? Either way, the old CMU CL
+ ;; code probably didn't deal with SETF
+ ;; functions correctly, and neither does
+ ;; this new SBCL code, and that should be fixed.
+ (symbolp (leaf-source-name leaf)))
+ (let ((dummies (make-gensym-list (length
+ (combination-args call)))))
+ (transform-call call
+ `(lambda ,dummies
+ (,(leaf-source-name leaf)
+ ,@dummies)))))))))))
(values))
\f
;;;; known function optimization
-;;; Add a failed optimization note to FAILED-OPTIMZATIONS for Node,
-;;; Fun and Args. If there is already a note for Node and Transform,
+;;; Add a failed optimization note to FAILED-OPTIMZATIONS for NODE,
+;;; FUN and ARGS. If there is already a note for NODE and TRANSFORM,
;;; replace it, otherwise add a new one.
(defun record-optimization-failure (node transform args)
(declare (type combination node) (type transform transform)
- (type (or function-type list) args))
+ (type (or fun-type list) args))
(let* ((table (component-failed-optimizations *component-being-compiled*))
(found (assoc transform (gethash node table))))
(if found
(declare (type combination node) (type transform transform))
(let* ((type (transform-type transform))
(fun (transform-function transform))
- (constrained (function-type-p type))
+ (constrained (fun-type-p type))
(table (component-failed-optimizations *component-being-compiled*))
(flame (if (transform-important transform)
- (policy node (>= speed brevity))
- (policy node (> speed brevity))))
+ (policy node (>= speed inhibit-warnings))
+ (policy node (> speed inhibit-warnings))))
(*compiler-error-context* node))
(cond ((not (member (transform-when transform)
- (if *byte-compiling*
- '(:byte :both)
- '(:native :both))))
+ '(:native :both)))
;; FIXME: Make sure that there's a transform for
;; (MEMBER SYMBOL ..) into MEMQ.
;; FIXME: Note that when/if I make SHARE operation to shared
;; '(:BOTH) tail sublists.
(let ((when (transform-when transform)))
(not (or (eq when :both)
- (eq when (if *byte-compiling* :byte :native)))))
+ (eq when :native))))
t)
((or (not constrained)
(valid-function-use node type :strict-result t))
(record-optimization-failure node transform args))
(setf (gethash node table)
(remove transform (gethash node table) :key #'car)))
- t))))
+ t)
+ (:delayed
+ (remhash node table)
+ nil))))
((and flame
(valid-function-use node
type
- :argument-test #'types-intersect
- :result-test #'values-types-intersect))
+ :argument-test #'types-equal-or-intersect
+ :result-test
+ #'values-types-equal-or-intersect))
(record-optimization-failure node transform type)
t)
(t
t))))
-;;; Just throw the severity and args...
+;;; When we don't like an IR1 transform, we throw the severity/reason
+;;; and args.
+;;;
+;;; GIVE-UP-IR1-TRANSFORM is used to throw out of an IR1 transform,
+;;; aborting this attempt to transform the call, but admitting the
+;;; possibility that this or some other transform will later succeed.
+;;; If arguments are supplied, they are format arguments for an
+;;; efficiency note.
+;;;
+;;; ABORT-IR1-TRANSFORM is used to throw out of an IR1 transform and
+;;; force a normal call to the function at run time. No further
+;;; optimizations will be attempted.
+;;;
+;;; DELAY-IR1-TRANSFORM is used to throw out of an IR1 transform, and
+;;; delay the transform on the node until later. REASONS specifies
+;;; when the transform will be later retried. The :OPTIMIZE reason
+;;; causes the transform to be delayed until after the current IR1
+;;; optimization pass. The :CONSTRAINT reason causes the transform to
+;;; be delayed until after constraint propagation.
+;;;
+;;; FIXME: Now (0.6.11.44) that there are 4 variants of this (GIVE-UP,
+;;; ABORT, DELAY/:OPTIMIZE, DELAY/:CONSTRAINT) and we're starting to
+;;; do CASE operations on the various REASON values, it might be a
+;;; good idea to go OO, representing the reasons by objects, using
+;;; CLOS methods on the objects instead of CASE, and (possibly) using
+;;; SIGNAL instead of THROW.
(declaim (ftype (function (&rest t) nil) give-up-ir1-transform))
(defun give-up-ir1-transform (&rest args)
- #!+sb-doc
- "This function is used to throw out of an IR1 transform, aborting this
- attempt to transform the call, but admitting the possibility that this or
- some other transform will later succeed. If arguments are supplied, they are
- format arguments for an efficiency note."
(throw 'give-up-ir1-transform (values :failure args)))
(defun abort-ir1-transform (&rest args)
- #!+sb-doc
- "This function is used to throw out of an IR1 transform and force a normal
- call to the function at run time. No further optimizations will be
- attempted."
(throw 'give-up-ir1-transform (values :aborted args)))
-
-;;; Take the lambda-expression Res, IR1 convert it in the proper
+(defun delay-ir1-transform (node &rest reasons)
+ (let ((assoc (assoc node *delayed-ir1-transforms*)))
+ (cond ((not assoc)
+ (setf *delayed-ir1-transforms*
+ (acons node reasons *delayed-ir1-transforms*))
+ (throw 'give-up-ir1-transform :delayed))
+ ((cdr assoc)
+ (dolist (reason reasons)
+ (pushnew reason (cdr assoc)))
+ (throw 'give-up-ir1-transform :delayed)))))
+
+;;; Clear any delayed transform with no reasons - these should have
+;;; been tried in the last pass. Then remove the reason from the
+;;; delayed transform reasons, and if any become empty then set
+;;; reoptimize flags for the node. Return true if any transforms are
+;;; to be retried.
+(defun retry-delayed-ir1-transforms (reason)
+ (setf *delayed-ir1-transforms*
+ (remove-if-not #'cdr *delayed-ir1-transforms*))
+ (let ((reoptimize nil))
+ (dolist (assoc *delayed-ir1-transforms*)
+ (let ((reasons (remove reason (cdr assoc))))
+ (setf (cdr assoc) reasons)
+ (unless reasons
+ (let ((node (car assoc)))
+ (unless (node-deleted node)
+ (setf reoptimize t)
+ (setf (node-reoptimize node) t)
+ (let ((block (node-block node)))
+ (setf (block-reoptimize block) t)
+ (setf (component-reoptimize (block-component block)) t)))))))
+ reoptimize))
+
+
+;;; Take the lambda-expression RES, IR1 convert it in the proper
;;; environment, and then install it as the function for the call
-;;; Node. We do local call analysis so that the new function is
+;;; NODE. We do local call analysis so that the new function is
;;; integrated into the control flow.
(defun transform-call (node res)
(declare (type combination node) (list res))
(with-ir1-environment node
- (let ((new-fun (ir1-convert-inline-lambda res))
+ (let ((new-fun (ir1-convert-inline-lambda
+ res
+ :debug-name "<something inlined in TRANSFORM-CALL>"))
(ref (continuation-use (combination-fun node))))
(change-ref-leaf ref new-fun)
(setf (combination-kind node) :full)
- (local-call-analyze *current-component*)))
+ (locall-analyze-component *current-component*)))
(values))
;;; Replace a call to a foldable function of constant arguments with
;;; the result of evaluating the form. We insert the resulting
;;; constant node after the call, stealing the call's continuation. We
-;;; give the call a continuation with no Dest, which should cause it
+;;; give the call a continuation with no DEST, which should cause it
;;; and its arguments to go away. If there is an error during the
;;; evaluation, we give a warning and leave the call alone, making the
;;; call a :ERROR call.
(declare (type combination call))
(let* ((args (mapcar #'continuation-value (combination-args call)))
(ref (continuation-use (combination-fun call)))
- (fun (leaf-name (ref-leaf ref))))
+ (fun-name (leaf-source-name (ref-leaf ref))))
(multiple-value-bind (values win)
- (careful-call fun args call "constant folding")
+ (careful-call fun-name args call "constant folding")
(if (not win)
(setf (combination-kind call) :error)
(let ((dummies (make-gensym-list (length args))))
\f
;;;; local call optimization
-;;; Propagate Type to Leaf and its Refs, marking things changed. If
+;;; Propagate TYPE to LEAF and its REFS, marking things changed. If
;;; the leaf type is a function type, then just leave it alone, since
;;; TYPE is never going to be more specific than that (and
;;; TYPE-INTERSECTION would choke.)
(defun propagate-to-refs (leaf type)
(declare (type leaf leaf) (type ctype type))
(let ((var-type (leaf-type leaf)))
- (unless (function-type-p var-type)
- (let ((int (type-intersection var-type type)))
+ (unless (fun-type-p var-type)
+ (let ((int (type-approx-intersection2 var-type type)))
(when (type/= int var-type)
(setf (leaf-type leaf) int)
(dolist (ref (leaf-refs leaf))
((or constant functional) t)
(lambda-var
(null (lambda-var-sets leaf)))
- (defined-function
- (not (eq (defined-function-inlinep leaf) :notinline)))
+ (defined-fun
+ (not (eq (defined-fun-inlinep leaf) :notinline)))
(global-var
(case (global-var-kind leaf)
- (:global-function t)
- (:constant t))))))
+ (:global-function t))))))
;;; If we have a non-set LET var with a single use, then (if possible)
;;; replace the variable reference's CONT with the arg continuation.
;;; would be NIL.
;;; -- the var's DEST has a different policy than the ARG's (think safety).
;;;
-;;; We change the Ref to be a reference to NIL with unused value, and
+;;; We change the REF to be a reference to NIL with unused value, and
;;; let it be flushed as dead code. A side-effect of this substitution
;;; is to delete the variable.
(defun substitute-single-use-continuation (arg var)
cont-atype
(continuation-asserted-type arg))
*empty-type*))
- (eq (lexenv-cookie (node-lexenv dest))
- (lexenv-cookie (node-lexenv (continuation-dest arg)))))
- (assert (member (continuation-kind arg)
- '(:block-start :deleted-block-start :inside-block)))
+ (eq (lexenv-policy (node-lexenv dest))
+ (lexenv-policy (node-lexenv (continuation-dest arg)))))
+ (aver (member (continuation-kind arg)
+ '(:block-start :deleted-block-start :inside-block)))
(assert-continuation-type arg cont-atype)
(setf (node-derived-type ref) *wild-type*)
(change-ref-leaf ref (find-constant nil))
;;; flush the FUN continuation.
(defun delete-let (fun)
(declare (type clambda fun))
- (assert (member (functional-kind fun) '(:let :mv-let)))
+ (aver (member (functional-kind fun) '(:let :mv-let)))
(note-unreferenced-vars fun)
(let ((call (let-combination fun)))
(flush-dest (basic-combination-fun call))
;;;
;;; Substitution of individual references is inhibited if the
;;; reference is in a different component from the home. This can only
-;;; happen with closures over top-level lambda vars. In such cases,
+;;; happen with closures over top level lambda vars. In such cases,
;;; the references may have already been compiled, and thus can't be
;;; retroactively modified.
;;;
this-comp)
t)
(t
- (assert (eq (functional-kind (lambda-home fun))
- :top-level))
+ (aver (eq (functional-kind (lambda-home fun))
+ :toplevel))
nil)))
leaf var))
t)))))
((and (null (rest (leaf-refs var)))
- (not *byte-compiling*)
(substitute-single-use-continuation arg var)))
(t
(propagate-to-refs var (continuation-type arg))))))
(defun propagate-local-call-args (call fun)
(declare (type combination call) (type clambda fun))
- (unless (or (functional-entry-function fun)
+ (unless (or (functional-entry-fun fun)
(lambda-optional-dispatch fun))
(let* ((vars (lambda-vars fun))
(union (mapcar #'(lambda (arg var)
(when fun-changed
(setf (continuation-reoptimize fun) nil)
(let ((type (continuation-type fun)))
- (when (function-type-p type)
- (derive-node-type node (function-type-returns type))))
+ (when (fun-type-p type)
+ (derive-node-type node (fun-type-returns type))))
(maybe-terminate-block node nil)
(let ((use (continuation-use fun)))
(when (and (ref-p use) (functional-p (ref-leaf use)))
(when (eq (basic-combination-kind node) :local)
(maybe-let-convert (ref-leaf use))))))
(unless (or (eq (basic-combination-kind node) :local)
- (eq (continuation-function-name fun) '%throw))
+ (eq (continuation-fun-name fun) '%throw))
(ir1-optimize-mv-call node))
(dolist (arg args)
(setf (continuation-reoptimize arg) nil))))
(return-from ir1-optimize-mv-call))
(multiple-value-bind (min max)
- (function-type-nargs (continuation-type fun))
+ (fun-type-nargs (continuation-type fun))
(let ((total-nvals
(multiple-value-bind (types nvals)
(values-types (continuation-derived-type (first args)))
(return-from ir1-optimize-mv-call)))
(let ((count (cond (total-nvals)
- ((and (policy node (zerop safety)) (eql min max))
+ ((and (policy node (zerop safety))
+ (eql min max))
min)
(t nil))))
(when count
(declare (ignore ,ignore))
(funcall ,(ref-leaf ref) ,@dums)))))
(change-ref-leaf ref fun)
- (assert (eq (basic-combination-kind node) :full))
- (local-call-analyze *current-component*)
- (assert (eq (basic-combination-kind node) :local)))))))))
+ (aver (eq (basic-combination-kind node) :full))
+ (locall-analyze-component *current-component*)
+ (aver (eq (basic-combination-kind node) :local)))))))))
(values))
;;; If we see:
(let* ((arg (first (basic-combination-args call)))
(use (continuation-use arg)))
(when (and (combination-p use)
- (eq (continuation-function-name (combination-fun use))
+ (eq (continuation-fun-name (combination-fun use))
'values))
(let* ((fun (combination-lambda call))
(vars (lambda-vars fun))
(defoptimizer (values-list optimizer) ((list) node)
(let ((use (continuation-use list)))
(when (and (combination-p use)
- (eq (continuation-function-name (combination-fun use))
+ (eq (continuation-fun-name (combination-fun use))
'list))
(change-ref-leaf (continuation-use (combination-fun node))
(find-free-function 'values "in a strange place"))
`(lambda (val ,@dummies)
(declare (ignore ,@dummies))
val))
- 'nil))
+ nil))