use))))
(plu lvar)))
+(defun principal-lvar-dest (lvar)
+ (labels ((pld (lvar)
+ (declare (type lvar lvar))
+ (let ((dest (lvar-dest lvar)))
+ (if (cast-p dest)
+ (pld (cast-lvar dest))
+ dest))))
+ (pld lvar)))
+
;;; Update lvar use information so that NODE is no longer a use of its
;;; LVAR.
;;;
(first new-uses)
new-uses)))
(setf (lvar-uses lvar) nil))
- (setf (node-lvar node) nil)))
+ (flush-node node)))
(values))
;;; Delete NODE from its LVAR uses; if LVAR has no other uses, delete
;;; its DEST's block, which must be unreachable.
(t (eq (block-start (first (block-succ (node-block node))))
(node-prev dest))))))
+;;; Returns the defined (usually untrusted) type of the combination,
+;;; or NIL if we couldn't figure it out.
+(defun combination-defined-type (combination)
+ (let ((use (principal-lvar-use (basic-combination-fun combination))))
+ (or (when (ref-p use)
+ (let ((type (leaf-defined-type (ref-leaf use))))
+ (when (fun-type-p type)
+ (fun-type-returns type))))
+ *wild-type*)))
+
;;; Return true if LVAR destination is executed after node with only
;;; uninteresting nodes intervening.
;;;
;;;;
;;; Filter values of LVAR through FORM, which must be an ordinary/mv
-;;; call. First argument must be 'DUMMY, which will be replaced with
-;;; LVAR. In case of an ordinary call the function should not have
-;;; return type NIL. We create a new "filtered" lvar.
+;;; call. Exactly one argument must be 'DUMMY, which will be replaced
+;;; with LVAR. In case of an ordinary call the function should not
+;;; have return type NIL. We create a new "filtered" lvar.
;;;
;;; TODO: remove preconditions.
(defun filter-lvar (lvar form)
;; Replace 'DUMMY with the LVAR. (We can find 'DUMMY because
;; no LET conversion has been done yet.) The [mv-]combination
;; code from the call in the form will be the use of the new
- ;; check lvar. We substitute for the first argument of
- ;; this node.
+ ;; check lvar. We substitute exactly one argument.
(let* ((node (lvar-use filtered-lvar))
- (args (basic-combination-args node))
- (victim (first args)))
+ victim)
+ (dolist (arg (basic-combination-args node) (aver victim))
+ (let* ((arg (principal-lvar arg))
+ (use (lvar-use arg))
+ leaf)
+ (when (and (ref-p use)
+ (constant-p (setf leaf (ref-leaf use)))
+ (eql (constant-value leaf) 'dummy))
+ (aver (not victim))
+ (setf victim arg))))
(aver (eq (constant-value (ref-leaf (lvar-use victim)))
'dummy))
;;;; DYNAMIC-EXTENT related
+(defun lambda-var-original-name (leaf)
+ (let ((home (lambda-var-home leaf)))
+ (if (eq :external (functional-kind home))
+ (let* ((entry (functional-entry-fun home))
+ (p (1- (position leaf (lambda-vars home)))))
+ (leaf-debug-name
+ (if (optional-dispatch-p entry)
+ (elt (optional-dispatch-arglist entry) p)
+ (elt (lambda-vars entry) p))))
+ (leaf-debug-name leaf))))
+
(defun note-no-stack-allocation (lvar &key flush)
(do-uses (use (principal-lvar lvar))
(unless (or
;; Don't complain about not being able to stack allocate constants.
(and (ref-p use) (constant-p (ref-leaf use)))
;; If we're flushing, don't complain if we can flush the combination.
- (and flush (combination-p use) (flushable-combination-p use)))
+ (and flush (combination-p use) (flushable-combination-p use))
+ ;; Don't report those with homes in :OPTIONAL -- we'd get doubled
+ ;; reports that way.
+ (and (ref-p use) (lambda-var-p (ref-leaf use))
+ (eq :optional (lambda-kind (lambda-var-home (ref-leaf use))))))
+ ;; FIXME: For the first leg (lambda-bind (lambda-var-home ...))
+ ;; would be a far better description, but since we use
+ ;; *COMPILER-ERROR-CONTEXT* for muffling we can't -- as that node
+ ;; can have different handled conditions.
(let ((*compiler-error-context* use))
- (compiler-notify "could not stack allocate the result of ~S"
- (find-original-source (node-source-path use)))))))
+ (if (and (ref-p use) (lambda-var-p (ref-leaf use)))
+ (compiler-notify "~@<could~2:I not stack allocate ~S in: ~S~:@>"
+ (lambda-var-original-name (ref-leaf use))
+ (find-original-source (node-source-path use)))
+ (compiler-notify "~@<could~2:I not stack allocate: ~S~:@>"
+ (find-original-source (node-source-path use))))))))
-(declaim (ftype (sfunction (node (member nil t :truly) &optional (or null component))
- boolean) use-good-for-dx-p))
-(declaim (ftype (sfunction (lvar (member nil t :truly) &optional (or null component))
- boolean) lvar-good-for-dx-p))
(defun use-good-for-dx-p (use dx &optional component)
;; FIXME: Can casts point to LVARs in other components?
;; RECHECK-DYNAMIC-EXTENT-LVARS assumes that they can't -- that is, that the
(when (lambda-p clambda1)
(dolist (var (lambda-vars clambda1) t)
(dolist (var-ref (lambda-var-refs var))
- (let ((dest (lvar-dest (ref-lvar var-ref))))
+ (let ((dest (principal-lvar-dest (ref-lvar var-ref))))
(unless (and (combination-p dest) (recurse dest))
(return-from combination-args-flow-cleanly-p nil)))))))))))
(recurse combination1)))
+(defun ref-good-for-dx-p (ref)
+ (let* ((lvar (ref-lvar ref))
+ (dest (when lvar (lvar-dest lvar))))
+ (and (combination-p dest)
+ (eq :known (combination-kind dest))
+ (awhen (combination-fun-info dest)
+ (or (ir1-attributep (fun-info-attributes it) dx-safe)
+ (and (not (combination-lvar dest))
+ (awhen (fun-info-result-arg it)
+ (eql lvar (nth it (combination-args dest))))))))))
+
(defun trivial-lambda-var-ref-p (use)
(and (ref-p use)
(let ((var (ref-leaf use)))
- ;; lambda-var, no SETS
- (when (and (lambda-var-p var) (not (lambda-var-sets var)))
+ ;; lambda-var, no SETS, not explicitly indefinite-extent.
+ (when (and (lambda-var-p var) (not (lambda-var-sets var))
+ (neq :indefinite (lambda-var-extent var)))
(let ((home (lambda-var-home var))
(refs (lambda-var-refs var)))
- ;; bound by a system lambda, no other REFS
+ ;; bound by a non-XEP system lambda, no other REFS that aren't
+ ;; DX-SAFE, or are result-args when the result is discarded.
(when (and (lambda-system-lambda-p home)
- (eq use (car refs)) (not (cdr refs)))
+ (neq :external (lambda-kind home))
+ (dolist (ref refs t)
+ (unless (or (eq use ref) (ref-good-for-dx-p ref))
+ (return nil))))
;; the LAMBDA this var is bound by has only a single REF, going
;; to a combination
(let* ((lambda-refs (lambda-refs home))
(defun trivial-lambda-var-ref-lvar (use)
(let* ((this (ref-leaf use))
- (home (lambda-var-home this)))
- (multiple-value-bind (fun vars)
- (values home (lambda-vars home))
- (let* ((combination (lvar-dest (ref-lvar (car (lambda-refs fun)))))
- (args (combination-args combination)))
- (assert (= (length vars) (length args)))
- (loop for var in vars
- for arg in args
- when (eq var this)
- return arg)))))
+ (fun (lambda-var-home this))
+ (vars (lambda-vars fun))
+ (combination (lvar-dest (ref-lvar (car (lambda-refs fun)))))
+ (args (combination-args combination)))
+ (aver (= (length vars) (length args)))
+ (loop for var in vars
+ for arg in args
+ when (eq var this)
+ return arg)))
;;; This needs to play nice with LVAR-GOOD-FOR-DX-P and friends.
(defun handle-nested-dynamic-extent-lvars (dx lvar &optional recheck-component)
(defun source-path-forms (path)
(subseq path 0 (position 'original-source-start path)))
+(defun tree-some (predicate tree)
+ (let ((seen (make-hash-table)))
+ (labels ((walk (tree)
+ (cond ((funcall predicate tree))
+ ((and (consp tree)
+ (not (gethash tree seen)))
+ (setf (gethash tree seen) t)
+ (or (walk (car tree))
+ (walk (cdr tree)))))))
+ (walk tree))))
+
;;; Return the innermost source form for NODE.
(defun node-source-form (node)
(declare (type node node))
(let* ((path (node-source-path node))
- (forms (source-path-forms path)))
+ (forms (remove-if (lambda (x)
+ (tree-some #'leaf-p x))
+ (source-path-forms path))))
+ ;; another option: if first form includes a leaf, return
+ ;; find-original-source instead.
(if forms
(first forms)
(values (find-original-source path)))))
(values nil nil)
(values (node-source-form use) t))))
+(defun common-suffix (x y)
+ (let ((mismatch (mismatch x y :from-end t)))
+ (if mismatch
+ (subseq x mismatch)
+ x)))
+
+;;; If the LVAR has a single use, return NODE-SOURCE-FORM as a
+;;; singleton. Otherwise, return a list of the lowest common
+;;; ancestor source form of all the uses (if it can be found),
+;;; followed by all the uses' source forms.
+(defun lvar-all-sources (lvar)
+ (let ((use (lvar-uses lvar)))
+ (if (listp use)
+ (let ((forms '())
+ (path (node-source-path (first use))))
+ (dolist (use use (cons (if (find 'original-source-start path)
+ (find-original-source path)
+ "a hairy form")
+ forms))
+ (pushnew (node-source-form use) forms)
+ (setf path (common-suffix path
+ (node-source-path use)))))
+ (list (node-source-form use)))))
+
;;; Return the unique node, delivering a value to LVAR.
#!-sb-fluid (declaim (inline lvar-use))
(defun lvar-use (lvar)
(reoptimize-lvar prev)))
\f
;;; Return a new LEXENV just like DEFAULT except for the specified
-;;; slot values. Values for the alist slots are NCONCed to the
+;;; slot values. Values for the alist slots are APPENDed to the
;;; beginning of the current value, rather than replacing it entirely.
(defun make-lexenv (&key (default *lexenv*)
funs vars blocks tags
(macrolet ((frob (var slot)
`(let ((old (,slot default)))
(if ,var
- (nconc ,var old)
+ (append ,var old)
old))))
(internal-make-lexenv
(frob funs lexenv-funs)
(values))
+;;; This function is called to unlink a node from its LVAR;
+;;; we assume that the LVAR's USE list has already been updated,
+;;; and that we only have to mark the node as up for dead code
+;;; elimination, and to clear it LVAR slot.
+(defun flush-node (node)
+ (declare (type node node))
+ (let* ((prev (node-prev node))
+ (block (ctran-block prev)))
+ (reoptimize-component (block-component block) t)
+ (setf (block-attributep (block-flags block)
+ flush-p type-asserted type-check)
+ t))
+ (setf (node-lvar node) nil))
+
;;; This function is called by people who delete nodes; it provides a
;;; way to indicate that the value of a lvar is no longer used. We
;;; null out the LVAR-DEST, set FLUSH-P in the blocks containing uses
(setf (lvar-dest lvar) nil)
(flush-lvar-externally-checkable-type lvar)
(do-uses (use lvar)
- (let ((prev (node-prev use)))
- (let ((block (ctran-block prev)))
- (reoptimize-component (block-component block) t)
- (setf (block-attributep (block-flags block)
- flush-p type-asserted type-check)
- t)))
- (setf (node-lvar use) nil))
+ (flush-node use))
(setf (lvar-uses lvar) nil))
(values))
;;;; leaf hackery
;;; Change the LEAF that a REF refers to.
-(defun change-ref-leaf (ref leaf)
+(defun change-ref-leaf (ref leaf &key recklessly)
(declare (type ref ref) (type leaf leaf))
(unless (eq (ref-leaf ref) leaf)
(push ref (leaf-refs leaf))
(eq lvar (basic-combination-fun dest))
(csubtypep ltype (specifier-type 'function))))
(setf (node-derived-type ref) vltype)
- (derive-node-type ref vltype)))
+ (derive-node-type ref vltype :from-scratch recklessly)))
(reoptimize-lvar (node-lvar ref)))
(values))
(setf (block-reoptimize (node-block node)) t)
(reoptimize-component (node-component node) :maybe)))))))
-;;; Return true if LVAR's only use is a non-NOTINLINE reference to a
-;;; global function with one of the specified NAMES.
+;;; Return true if LVAR's only use is a reference to a global function
+;;; designator with one of the specified NAMES, that hasn't been
+;;; declared NOTINLINE.
(defun lvar-fun-is (lvar names)
(declare (type lvar lvar) (list names))
(let ((use (lvar-uses lvar)))
(and (ref-p use)
- (let ((leaf (ref-leaf use)))
- (and (global-var-p leaf)
- (eq (global-var-kind leaf) :global-function)
- (not (null (member (leaf-source-name leaf) names
- :test #'equal))))))))
+ (let* ((*lexenv* (node-lexenv use))
+ (leaf (ref-leaf use))
+ (name
+ (cond ((global-var-p leaf)
+ ;; Case 1: #'NAME
+ (and (eq (global-var-kind leaf) :global-function)
+ (car (member (leaf-source-name leaf) names
+ :test #'equal))))
+ ((constant-p leaf)
+ (let ((value (constant-value leaf)))
+ (car (if (functionp value)
+ ;; Case 2: #.#'NAME
+ (member value names
+ :key (lambda (name)
+ (and (fboundp name)
+ (fdefinition name)))
+ :test #'eq)
+ ;; Case 3: 'NAME
+ (member value names
+ :test #'equal))))))))
+ (and name
+ (not (fun-lexically-notinline-p name)))))))
;;; Return true if LVAR's only use is a call to one of the named functions
;;; (or any function if none are specified) with the specified number of
(and ok (member name fun-names :test #'eq))))
(or (not arg-count)
(= arg-count (length (combination-args use)))))))
+
+;;; True if the optional has a rest-argument.
+(defun optional-rest-p (opt)
+ (dolist (var (optional-dispatch-arglist opt) nil)
+ (let* ((info (when (lambda-var-p var)
+ (lambda-var-arg-info var)))
+ (kind (when info
+ (arg-info-kind info))))
+ (when (eq :rest kind)
+ (return t)))))
+
+;;; Don't substitute single-ref variables on high-debug / low speed, to
+;;; improve the debugging experience. ...but don't bother keeping those
+;;; from system lambdas.
+(defun preserve-single-use-debug-var-p (call var)
+ (and (policy call (eql preserve-single-use-debug-variables 3))
+ (or (not (lambda-var-p var))
+ (not (lambda-system-lambda-p (lambda-var-home var))))))