X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fir1opt.lisp;h=2badb1cefc6832a67f97eaacd7f3f557679a45fe;hb=e76ddf242a31a2acaae3a9cb818fa31500ebbf92;hp=35720894ff38948146f6700881528b85f4ae792a;hpb=ba38798a5ca26b90647a1993f348806cb32f2d1b;p=sbcl.git diff --git a/src/compiler/ir1opt.lisp b/src/compiler/ir1opt.lisp index 3572089..2badb1c 100644 --- a/src/compiler/ir1opt.lisp +++ b/src/compiler/ir1opt.lisp @@ -118,6 +118,43 @@ (declaim (ftype (function (continuation) ctype) continuation-type)) (defun continuation-type (cont) (single-value-type (continuation-derived-type cont))) + +;;; If CONT is an argument of a function, return a type which the +;;; function checks CONT for. +#!-sb-fluid (declaim (inline continuation-externally-checkable-type)) +(defun continuation-externally-checkable-type (cont) + (or (continuation-%externally-checkable-type cont) + (%continuation-%externally-checkable-type cont))) +(defun %continuation-%externally-checkable-type (cont) + (declare (type continuation cont)) + (let ((dest (continuation-dest cont))) + (if (not (and dest (combination-p dest))) + ;; TODO: MV-COMBINATION + (setf (continuation-%externally-checkable-type cont) *wild-type*) + (let* ((fun (combination-fun dest)) + (args (combination-args dest)) + (fun-type (continuation-type fun))) + (if (or (not (fun-type-p fun-type)) + ;; FUN-TYPE might be (AND FUNCTION (SATISFIES ...)). + (fun-type-wild-args fun-type)) + (progn (dolist (arg args) + (when arg + (setf (continuation-%externally-checkable-type arg) + *wild-type*))) + *wild-type*) + (let* ((arg-types (append (fun-type-required fun-type) + (fun-type-optional fun-type) + (let ((rest (list (or (fun-type-rest fun-type) + *wild-type*)))) + (setf (cdr rest) rest))))) + ;; TODO: &KEY + (loop + for arg of-type continuation in args + and type of-type ctype in arg-types + do (when arg + (setf (continuation-%externally-checkable-type arg) + type))) + (continuation-%externally-checkable-type cont))))))) ;;;; interface routines used by optimizers @@ -152,15 +189,15 @@ (setf (block-type-check (node-block node)) t))) (values)) -;;; Annotate Node to indicate that its result has been proven to be -;;; typep to RType. After IR1 conversion has happened, this is the +;;; Annotate NODE to indicate that its result has been proven to be +;;; TYPEP to RTYPE. After IR1 conversion has happened, this is the ;;; only correct way to supply information discovered about a node's -;;; type. If you screw with the Node-Derived-Type directly, then +;;; type. If you screw with the NODE-DERIVED-TYPE directly, then ;;; information may be lost and reoptimization may not happen. ;;; -;;; What we do is intersect Rtype with Node's Derived-Type. If the +;;; What we do is intersect RTYPE with NODE's DERIVED-TYPE. If the ;;; intersection is different from the old type, then we do a -;;; Reoptimize-Continuation on the Node-Cont. +;;; REOPTIMIZE-CONTINUATION on the NODE-CONT. (defun derive-node-type (node rtype) (declare (type node node) (type ctype rtype)) (let ((node-type (node-derived-type node))) @@ -179,23 +216,34 @@ (reoptimize-continuation (node-cont node)))))) (values)) +(defun set-continuation-type-assertion (cont atype ctype) + (declare (type continuation cont) (type ctype atype ctype)) + (when (eq atype *wild-type*) + (return-from set-continuation-type-assertion)) + (let* ((old-atype (continuation-asserted-type cont)) + (old-ctype (continuation-type-to-check cont)) + (new-atype (values-type-intersection old-atype atype)) + (new-ctype (values-type-intersection old-ctype ctype))) + (when (or (type/= old-atype new-atype) + (type/= old-ctype new-ctype)) + (setf (continuation-asserted-type cont) new-atype) + (setf (continuation-type-to-check cont) new-ctype) + (do-uses (node cont) + (setf (block-attributep (block-flags (node-block node)) + type-check type-asserted) + t)) + (reoptimize-continuation cont))) + (values)) + ;;; 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) +(defun assert-continuation-type (cont type policy) (declare (type continuation cont) (type ctype type)) - (let ((cont-type (continuation-asserted-type cont))) - (unless (eq cont-type type) - (let ((int (values-type-intersection cont-type type))) - (when (type/= cont-type int) - (setf (continuation-asserted-type cont) int) - (do-uses (node cont) - (setf (block-attributep (block-flags (node-block node)) - type-check type-asserted) - t)) - (reoptimize-continuation cont))))) - (values)) + (when (eq type *wild-type*) + (return-from assert-continuation-type)) + (set-continuation-type-assertion cont type (maybe-weaken-check type policy))) ;;; 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 @@ -203,20 +251,21 @@ (defun assert-call-type (call type) (declare (type combination call) (type fun-type type)) (derive-node-type call (fun-type-returns type)) - (let ((args (combination-args call))) + (let ((args (combination-args call)) + (policy (lexenv-policy (node-lexenv call)))) (dolist (req (fun-type-required type)) (when (null args) (return-from assert-call-type)) (let ((arg (pop args))) - (assert-continuation-type arg req))) + (assert-continuation-type arg req policy))) (dolist (opt (fun-type-optional type)) (when (null args) (return-from assert-call-type)) (let ((arg (pop args))) - (assert-continuation-type arg opt))) + (assert-continuation-type arg opt policy))) (let ((rest (fun-type-rest type))) (when rest (dolist (arg args) - (assert-continuation-type arg rest)))) + (assert-continuation-type arg rest policy)))) (dolist (key (fun-type-keywords type)) (let ((name (key-info-name key))) @@ -224,7 +273,8 @@ ((null arg)) (when (eq (continuation-value (first arg)) name) (assert-continuation-type - (second arg) (key-info-type key))))))) + (second arg) (key-info-type key) + policy)))))) (values)) ;;;; IR1-OPTIMIZE @@ -238,53 +288,47 @@ (setf (component-reoptimize component) nil) (do-blocks (block component) (cond - ((or (block-delete-p block) - (null (block-pred block))) - (delete-block block)) - ((eq (functional-kind (block-home-lambda block)) :deleted) - ;; Preserve the BLOCK-SUCC invariant that almost every block has - ;; one successor (and a block with DELETE-P set is an acceptable - ;; exception). - (labels ((mark-blocks (block) - (dolist (pred (block-pred block)) - (when (and (not (block-delete-p pred)) - (eq (functional-kind (block-home-lambda pred)) - :deleted)) - (setf (block-delete-p pred) t) - (mark-blocks pred))))) - (mark-blocks block) - (delete-block block))) - (t - (loop - (let ((succ (block-succ block))) - (unless (and succ (null (rest succ))) - (return))) - - (let ((last (block-last block))) - (typecase last - (cif - (flush-dest (if-test last)) - (when (unlink-node last) - (return))) - (exit - (when (maybe-delete-exit last) - (return))))) - - (unless (join-successor-if-possible block) - (return))) - - (when (and (block-reoptimize block) (block-component block)) - (aver (not (block-delete-p block))) - (ir1-optimize-block block)) - ;; We delete blocks when there is either no predecessor or the ;; block is in a lambda that has been deleted. These blocks ;; would eventually be deleted by DFO recomputation, but doing ;; it here immediately makes the effect available to IR1 ;; optimization. - (when (and (block-flush-p block) (block-component block)) - (aver (not (block-delete-p block))) - (flush-dead-code block))))) + ((or (block-delete-p block) + (null (block-pred block))) + (delete-block block)) + ((eq (functional-kind (block-home-lambda block)) :deleted) + ;; Preserve the BLOCK-SUCC invariant that almost every block has + ;; one successor (and a block with DELETE-P set is an acceptable + ;; exception). + (mark-for-deletion block) + (delete-block block)) + (t + (loop + (let ((succ (block-succ block))) + (unless (and succ (null (rest succ))) + (return))) + + (let ((last (block-last block))) + (typecase last + (cif + (flush-dest (if-test last)) + (when (unlink-node last) + (return))) + (exit + (when (maybe-delete-exit last) + (return))))) + + (unless (join-successor-if-possible block) + (return))) + + (when (and (block-reoptimize block) (block-component block)) + (aver (not (block-delete-p block))) + (ir1-optimize-block block)) + + (cond ((block-delete-p block) + (delete-block block)) + ((and (block-flush-p block) (block-component block)) + (flush-dead-code block)))))) (values)) @@ -441,13 +485,28 @@ (let ((info (combination-kind node))) (when (fun-info-p info) (let ((attr (fun-info-attributes info))) - (when (and (ir1-attributep attr flushable) + (when (and (not (ir1-attributep attr call)) ;; ### For now, don't delete potentially ;; flushable calls when they have the CALL ;; attribute. Someday we should look at the ;; functional args to determine if they have ;; any side effects. - (not (ir1-attributep attr call))) + (if (policy node (= safety 3)) + (and (ir1-attributep attr flushable) + (every (lambda (arg) + ;; FIXME: when bug 203 + ;; will be fixed, remove + ;; this check + (member (continuation-type-check arg) + '(nil :deleted))) + (basic-combination-args node)) + (valid-fun-use node + (info :function :type + (leaf-source-name (ref-leaf (continuation-use (basic-combination-fun node))))) + :result-test #'always-subtypep + :lossage-fun nil + :unwinnage-fun nil)) + (ir1-attributep attr unsafely-flushable))) (flush-dest (combination-fun node)) (dolist (arg (combination-args node)) (flush-dest arg)) @@ -615,6 +674,7 @@ (new-block (continuation-starts-block new-cont))) (link-node-to-previous-continuation new-node new-cont) (setf (continuation-dest new-cont) new-node) + (setf (continuation-%externally-checkable-type new-cont) nil) (add-continuation-use new-node dummy-cont) (setf (block-last new-block) new-node) @@ -935,31 +995,31 @@ (continuation-use (basic-combination-fun call)) call)) ((not leaf)) - ((or (info :function :source-transform (leaf-source-name leaf)) - (and info - (ir1-attributep (fun-info-attributes info) - predicate) - (let ((dest (continuation-dest (node-cont call)))) - (and dest (not (if-p dest)))))) - (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)) - (leaf-source-name leaf)))))))))) + ((and (leaf-has-source-name-p leaf) + (or (info :function :source-transform (leaf-source-name leaf)) + (and info + (ir1-attributep (fun-info-attributes info) + predicate) + (let ((dest (continuation-dest (node-cont call)))) + (and dest (not (if-p dest))))))) + ;; 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. + (when (symbolp (leaf-source-name leaf)) + (let ((dummies (make-gensym-list + (length (combination-args call))))) + (transform-call call + `(lambda ,dummies + (,(leaf-source-name leaf) + ,@dummies)) + (leaf-source-name leaf)))))))))) (values)) ;;;; known function optimization @@ -1137,7 +1197,42 @@ (let ((args (mapcar #'continuation-value (combination-args call))) (fun-name (combination-fun-source-name call))) (multiple-value-bind (values win) - (careful-call fun-name args call "constant folding") + (careful-call fun-name + args + call + ;; Note: CMU CL had COMPILER-WARN here, and that + ;; seems more natural, but it's probably not. + ;; + ;; It's especially not while bug 173 exists: + ;; Expressions like + ;; (COND (END + ;; (UNLESS (OR UNSAFE? (<= END SIZE))) + ;; ...)) + ;; can cause constant-folding TYPE-ERRORs (in + ;; #'<=) when END can be proved to be NIL, even + ;; though the code is perfectly legal and safe + ;; because a NIL value of END means that the + ;; #'<= will never be executed. + ;; + ;; Moreover, even without bug 173, + ;; quite-possibly-valid code like + ;; (COND ((NONINLINED-PREDICATE END) + ;; (UNLESS (<= END SIZE)) + ;; ...)) + ;; (where NONINLINED-PREDICATE is something the + ;; compiler can't do at compile time, but which + ;; turns out to make the #'<= expression + ;; unreachable when END=NIL) could cause errors + ;; when the compiler tries to constant-fold (<= + ;; END SIZE). + ;; + ;; So, with or without bug 173, it'd be + ;; unnecessarily evil to do a full + ;; COMPILER-WARNING (and thus return FAILURE-P=T + ;; from COMPILE-FILE) for legal code, so we we + ;; use a wimpier COMPILE-STYLE-WARNING instead. + #'compiler-style-warn + "constant folding") (if (not win) (setf (combination-kind call) :error) (let ((dummies (make-gensym-list (length args)))) @@ -1193,7 +1288,7 @@ (derive-node-type node (continuation-type (set-value node))) (values)) -;;; Return true if the value of Ref will always be the same (and is +;;; Return true if the value of REF will always be the same (and is ;;; thus legal to substitute.) (defun constant-reference-p (ref) (declare (type ref ref)) @@ -1204,9 +1299,12 @@ (null (lambda-var-sets leaf))) (defined-fun (not (eq (defined-fun-inlinep leaf) :notinline))) + #!+(and (not sb-fluid) (not sb-xc-host)) (global-var (case (global-var-kind leaf) - (:global-function t)))))) + (:global-function (let ((name (leaf-source-name leaf))) + (eq (symbol-package (fun-name-block-name name)) + *cl-package*)))))))) ;;; 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. @@ -1227,6 +1325,7 @@ (let* ((ref (first (leaf-refs var))) (cont (node-cont ref)) (cont-atype (continuation-asserted-type cont)) + (cont-ctype (continuation-type-to-check cont)) (dest (continuation-dest cont))) (when (and (eq (continuation-use cont) ref) dest @@ -1243,7 +1342,7 @@ (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) + (set-continuation-type-assertion arg cont-atype cont-ctype) (setf (node-derived-type ref) *wild-type*) (change-ref-leaf ref (find-constant nil)) (substitute-continuation arg cont) @@ -1568,7 +1667,8 @@ (flush-dest (combination-fun use)) (let ((fun-cont (basic-combination-fun call))) (setf (continuation-dest fun-cont) use) - (setf (combination-fun use) fun-cont)) + (setf (combination-fun use) fun-cont) + (setf (continuation-%externally-checkable-type fun-cont) nil)) (setf (combination-kind use) :local) (setf (functional-kind fun) :let) (flush-dest (first (basic-combination-args call))) @@ -1598,7 +1698,8 @@ (setf (combination-kind node) :full) (let ((args (combination-args use))) (dolist (arg args) - (setf (continuation-dest arg) node)) + (setf (continuation-dest arg) node) + (setf (continuation-%externally-checkable-type arg) nil)) (setf (combination-args use) nil) (flush-dest list) (setf (combination-args node) args))