1.0.12.16: sequence optimizations: FILL
[sbcl.git] / src / compiler / main.lisp
index 6ed7cb3..57ec2ee 100644 (file)
@@ -37,7 +37,6 @@
 (defvar *flame-on-necessarily-undefined-function* nil)
 
 (defvar *check-consistency* nil)
-(defvar *all-components*)
 
 ;;; Set to NIL to disable loop analysis for register allocation.
 (defvar *loop-analyze* t)
 
 (defvar *compile-object* nil)
 (declaim (type object *compile-object*))
+
+(defvar *fopcompile-label-counter*)
+
+;; Used during compilation to map code paths to the matching
+;; instrumentation conses.
+(defvar *code-coverage-records* nil)
+;; Used during compilation to keep track of with source paths have been
+;; instrumented in which blocks.
+(defvar *code-coverage-blocks* nil)
+;; Stores the code coverage instrumentation results. Keys are namestrings,
+;; the value is a list of (CONS PATH STATE), where STATE is NIL for
+;; a path that has not been visited, and T for one that has.
+(defvar *code-coverage-info* (make-hash-table :test 'equal))
+
 \f
 ;;;; WITH-COMPILATION-UNIT and WITH-COMPILATION-VALUES
 
 
   (let* ((*component-being-compiled* component))
 
+    ;; Record xref information before optimization. This way the
+    ;; stored xref data reflects the real source as closely as
+    ;; possible.
+    (record-component-xrefs component)
+
     (ir1-phases component)
 
     (when *loop-analyze*
 ;;; *TOPLEVEL-LAMBDAS* instead.
 (defun convert-and-maybe-compile (form path)
   (declare (list path))
-  (let* ((*top-level-form-noted* (note-top-level-form form t))
-         (*lexenv* (make-lexenv
-                    :policy *policy*
-                    :handled-conditions *handled-conditions*
-                    :disabled-package-locks *disabled-package-locks*))
-         (tll (ir1-toplevel form path nil)))
-    (if (eq *block-compile* t)
-        (push tll *toplevel-lambdas*)
-        (compile-toplevel (list tll) nil))
-    nil))
+  (if (fopcompilable-p form)
+      (let ((*fopcompile-label-counter* 0))
+        (fopcompile form path nil))
+      (let* ((*top-level-form-noted* (note-top-level-form form t))
+             (*lexenv* (make-lexenv
+                        :policy *policy*
+                        :handled-conditions *handled-conditions*
+                        :disabled-package-locks *disabled-package-locks*))
+             (tll (ir1-toplevel form path nil)))
+        (if (eq *block-compile* t)
+            (push tll *toplevel-lambdas*)
+            (compile-toplevel (list tll) nil))
+        nil)))
 
 ;;; Macroexpand FORM in the current environment with an error handler.
 ;;; We only expand one level, so that we retain all the intervening
           (debug-name 'initial-component name))
     (setf (component-kind component) :initial)
     (let* ((locall-fun (let ((*allow-instrumenting* t))
-                         (apply #'ir1-convert-lambdalike
-                                definition
-                                (list :source-name name))))
+                         (funcall #'ir1-convert-lambdalike
+                                  definition
+                                  :source-name name)))
+           (debug-name (debug-name 'tl-xep
+                                   (or name
+                                       (functional-%source-name locall-fun))))
+           ;; Convert the XEP using the policy of the real
+           ;; function. Otherwise the wrong policy will be used for
+           ;; deciding whether to type-check the parameters of the
+           ;; real function (via CONVERT-CALL / PROPAGATE-TO-ARGS).
+           ;; -- JES, 2007-02-27
+           (*lexenv* (make-lexenv :policy (lexenv-policy
+                                           (functional-lexenv locall-fun))))
            (fun (ir1-convert-lambda (make-xep-lambda-expression locall-fun)
                                     :source-name (or name '.anonymous.)
-                                    :debug-name (debug-name 'tl-xep  name))))
+                                    :debug-name debug-name)))
       (when name
         (assert-global-function-definition-type name locall-fun))
       (setf (functional-entry-fun fun) locall-fun
             (functional-kind fun) :external
+            (functional-has-external-references-p locall-fun) t
             (functional-has-external-references-p fun) t)
       fun)))
 
                     :policy *policy*
                     :handled-conditions *handled-conditions*
                     :disabled-package-locks *disabled-package-locks*))
+         (*compiler-sset-counter* 0)
          (fun (make-functional-from-toplevel-lambda lambda-expression
                                                     :name name
                                                     :path path)))
 
     (locall-analyze-clambdas-until-done (list fun))
 
-    (multiple-value-bind (components-from-dfo top-components hairy-top)
-        (find-initial-dfo (list fun))
-      (declare (ignore hairy-top))
-
-      (let ((*all-components* (append components-from-dfo top-components)))
-        (dolist (component-from-dfo components-from-dfo)
-          (compile-component component-from-dfo)
-          (replace-toplevel-xeps component-from-dfo)))
+    (let ((components-from-dfo (find-initial-dfo (list fun))))
+      (dolist (component-from-dfo components-from-dfo)
+        (compile-component component-from-dfo)
+        (replace-toplevel-xeps component-from-dfo))
 
       (let ((entry-table (etypecase *compile-object*
                            (fasl-output (fasl-output-entry-table
   (declare (list path))
 
   (catch 'process-toplevel-form-error-abort
-    (let* ((path (or (gethash form *source-paths*) (cons form path)))
+    (let* ((path (or (get-source-path form) (cons form path)))
            (*compiler-error-bailout*
             (lambda (&optional condition)
               (convert-and-maybe-compile
 (defun compile-load-time-stuff (form for-value)
   (with-ir1-namespace
    (let* ((*lexenv* (make-null-lexenv))
-          (lambda (ir1-toplevel form *current-path* for-value)))
+          (lambda (ir1-toplevel form *current-path* for-value nil)))
      (compile-toplevel (list lambda) t)
      lambda)))
 
   (maybe-mumble "IDFO ")
   (multiple-value-bind (components top-components hairy-top)
       (find-initial-dfo lambdas)
-    (let ((*all-components* (append components top-components)))
+    (let ((all-components (append components top-components)))
       (when *check-consistency*
         (maybe-mumble "[check]~%")
-        (check-ir1-consistency *all-components*))
+        (check-ir1-consistency all-components))
 
       (dolist (component (append hairy-top top-components))
         (pre-physenv-analyze-toplevel component))
 
       (when *check-consistency*
         (maybe-mumble "[check]~%")
-        (check-ir1-consistency *all-components*))
+        (check-ir1-consistency all-components))
 
       (if load-time-value-p
           (compile-load-time-value-lambda lambdas)
               (invoke-restart it))))))))
 
 ;;; Read all forms from INFO and compile them, with output to OBJECT.
-;;; Return (VALUES NIL WARNINGS-P FAILURE-P).
+;;; Return (VALUES ABORT-P WARNINGS-P FAILURE-P).
 (defun sub-compile-file (info)
   (declare (type source-info info))
   (let ((*package* (sane-package))
         (sb!xc:*compile-file-pathname* nil) ; really bound in
         (sb!xc:*compile-file-truename* nil) ; SUB-SUB-COMPILE-FILE
         (*policy* *policy*)
+        (*code-coverage-records* (make-hash-table :test 'equal))
+        (*code-coverage-blocks* (make-hash-table :test 'equal))
         (*handled-conditions* *handled-conditions*)
         (*disabled-package-locks* *disabled-package-locks*)
         (*lexenv* (make-null-lexenv))
         (*compiler-error-bailout*
          (lambda ()
            (compiler-mumble "~2&; fatal error, aborting compilation~%")
-           (return-from sub-compile-file (values nil t t))))
+           (return-from sub-compile-file (values t t t))))
         (*current-path* nil)
         (*last-source-context* nil)
         (*last-original-source* nil)
         ;; and it's not obvious whether the rebinding to itself is
         ;; needed that SBCL doesn't need *BACKEND-INFO-ENVIRONMENT*.
         (*info-environment* *info-environment*)
+        (*compiler-sset-counter* 0)
         (*gensym-counter* 0))
     (handler-case
         (handler-bind (((satisfies handle-condition-p) #'handle-condition-handler))
 
                 (sub-sub-compile-file info)
 
+                (unless (zerop (hash-table-count *code-coverage-records*))
+                  ;; Dump the code coverage records into the fasl.
+                  (fopcompile `(record-code-coverage
+                                ',(namestring *compile-file-pathname*)
+                                ',(let (list)
+                                       (maphash (lambda (k v)
+                                                  (declare (ignore k))
+                                                  (push v list))
+                                                *code-coverage-records*)
+                                       list))
+                              nil
+                              nil))
+
                 (finish-block-compilation)
                 (let ((object *compile-object*))
                   (etypecase object
       ;; the input file.
       (fatal-compiler-error (condition)
        (signal condition)
-       (when *compile-verbose*
-         (format *standard-output*
+       (pprint-logical-block (*error-output* nil :per-line-prefix "; ")
+         (format *error-output*
                  "~@<compilation aborted because of fatal error: ~2I~_~A~:>"
                  condition))
-       (values nil t t)))))
+       (finish-output *error-output*)
+       (values t t t)))))
 
 ;;; Return a pathname for the named file. The file must exist.
 (defun verify-source-file (pathname-designator)
@@ -1620,7 +1666,7 @@ SPEED and COMPILATION-SPEED optimization values, and the
 |#
   (let* ((fasl-output nil)
          (output-file-name nil)
-         (compile-won nil)
+         (abort-p nil)
          (warnings-p nil)
          (failure-p t) ; T in case error keeps this from being set later
          (input-pathname (verify-source-file input-file))
@@ -1651,31 +1697,34 @@ SPEED and COMPILATION-SPEED optimization values, and the
 
           (when sb!xc:*compile-verbose*
             (print-compile-start-note source-info))
-          (let ((*compile-object* fasl-output)
-                dummy)
-            (multiple-value-setq (dummy warnings-p failure-p)
-              (sub-compile-file source-info)))
-          (setq compile-won t))
+
+          (let ((*compile-object* fasl-output))
+            (setf (values abort-p warnings-p failure-p)
+                  (sub-compile-file source-info))))
 
       (close-source-info source-info)
 
       (when fasl-output
-        (close-fasl-output fasl-output (not compile-won))
+        (close-fasl-output fasl-output abort-p)
         (setq output-file-name
               (pathname (fasl-output-stream fasl-output)))
-        (when (and compile-won sb!xc:*compile-verbose*)
+        (when (and (not abort-p) sb!xc:*compile-verbose*)
           (compiler-mumble "~2&; ~A written~%" (namestring output-file-name))))
 
       (when sb!xc:*compile-verbose*
-        (print-compile-end-note source-info compile-won))
+        (print-compile-end-note source-info (not abort-p)))
 
       (when *compiler-trace-output*
         (close *compiler-trace-output*)))
 
-    (values (if output-file
-                ;; Hack around filesystem race condition...
-                (or (probe-file output-file-name) output-file-name)
-                nil)
+    ;; CLHS says that the first value is NIL if the "file could not
+    ;; be created". We interpret this to mean "a valid fasl could not
+    ;; be created" -- which can happen if the compilation is aborted
+    ;; before the whole file has been processed, due to eg. a reader
+    ;; error.
+    (values (when (and (not abort-p) output-file)
+              ;; Hack around filesystem race condition...
+              (or (probe-file output-file-name) output-file-name))
             warnings-p
             failure-p)))
 \f
@@ -1703,13 +1752,14 @@ SPEED and COMPILATION-SPEED optimization values, and the
 ;;; -- WHN 2000-12-09
 (defun sb!xc:compile-file-pathname (input-file
                                     &key
-                                    (output-file (cfp-output-file-default
-                                                  input-file))
+                                    (output-file nil output-file-p)
                                     &allow-other-keys)
   #!+sb-doc
   "Return a pathname describing what file COMPILE-FILE would write to given
    these arguments."
-  (merge-pathnames output-file (merge-pathnames input-file)))
+  (if output-file-p
+      (merge-pathnames output-file (cfp-output-file-default input-file))
+      (cfp-output-file-default input-file)))
 \f
 ;;;; MAKE-LOAD-FORM stuff
 
@@ -1789,8 +1839,6 @@ SPEED and COMPILATION-SPEED optimization values, and the
         (:ignore-it
          nil)
         (t
-         (when (fasl-constant-already-dumped-p constant *compile-object*)
-           (return-from emit-make-load-form nil))
          (let* ((name (write-to-string constant :level 1 :length 2))
                 (info (if init-form
                           (list constant name init-form)