X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fir1util.lisp;h=7d9f2f3d4842fe0449cf891513d32e744b220df5;hb=b83353d9f998e5c0e34604b5593df70c66d2c510;hp=f62f0a4c76061057bc19af3f06585f87ec4ee75e;hpb=40bff32181a4d9b591ae2bac69bbee3bd77a82bc;p=sbcl.git diff --git a/src/compiler/ir1util.lisp b/src/compiler/ir1util.lisp index f62f0a4..7d9f2f3 100644 --- a/src/compiler/ir1util.lisp +++ b/src/compiler/ir1util.lisp @@ -80,6 +80,15 @@ 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. ;;; @@ -101,7 +110,7 @@ (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. @@ -313,9 +322,9 @@ ;;;; ;;; 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) @@ -349,11 +358,18 @@ ;; 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)) @@ -560,7 +576,7 @@ (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))) @@ -749,11 +765,26 @@ (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))))) @@ -766,6 +797,30 @@ (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) @@ -837,7 +892,7 @@ (reoptimize-lvar prev))) ;;; 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 @@ -852,7 +907,7 @@ (macrolet ((frob (var slot) `(let ((old (,slot default))) (if ,var - (nconc ,var old) + (append ,var old) old)))) (internal-make-lexenv (frob funs lexenv-funs) @@ -1366,6 +1421,20 @@ (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 @@ -1378,13 +1447,7 @@ (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)) @@ -1831,7 +1894,7 @@ is :ANY, the function name is not checked." ;;;; 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)) @@ -1846,7 +1909,7 @@ is :ANY, the function name is not checked." (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)) @@ -2296,3 +2359,21 @@ is :ANY, the function name is not checked." (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))))))