X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fir1util.lisp;h=d0df90321a831f3eedc7c4f28f21e52640b2abfa;hb=63690d6dc4617f2140f229a142728d1784efd0b5;hp=e3ef6cfef7db4c28ac0f245eb1494c60c785d8c2;hpb=edb7acf8d242c0398ec33924e21c85dc54bc768d;p=sbcl.git diff --git a/src/compiler/ir1util.lisp b/src/compiler/ir1util.lisp index e3ef6cf..d0df903 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. ;;; @@ -149,6 +158,16 @@ (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. ;;; @@ -303,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) @@ -339,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)) @@ -446,16 +472,39 @@ ;;;; 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 "~@" + (lambda-var-original-name (ref-leaf use)) + (find-original-source (node-source-path use))) + (compiler-notify "~@" + (find-original-source (node-source-path use)))))))) (defun use-good-for-dx-p (use dx &optional component) ;; FIXME: Can casts point to LVARs in other components? @@ -527,11 +576,22 @@ (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))) @@ -540,9 +600,13 @@ (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)) @@ -553,16 +617,15 @@ (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) @@ -588,7 +651,6 @@ dx arg recheck-component))) (ref (let* ((other (trivial-lambda-var-ref-lvar use))) - (print (list :ref use other)) (unless (eq other lvar) (handle-nested-dynamic-extent-lvars dx other recheck-component))))))) @@ -703,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))))) @@ -791,7 +868,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 @@ -806,7 +883,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) @@ -2250,3 +2327,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))))))