1.0.0.18:
authorJuho Snellman <jsnell@iki.fi>
Tue, 5 Dec 2006 04:35:55 +0000 (04:35 +0000)
committerJuho Snellman <jsnell@iki.fi>
Tue, 5 Dec 2006 04:35:55 +0000 (04:35 +0000)
        Add an xref facility, exported from sb-introspect.

        * Support who-calls/macroexpands/binds/sets/references, with
          full source path information for extra Slime-goodness.
        * Only causes relatively small amounts of compilation slowdown
          or fasl bloat, so enable it by default (unless (= SPEED 3)).
        * Does not handle: expanded compiler-macros, code in macrolet
          definition bodies, toplevel code
        * Xref data is currently stored in a new simple-fun slot (the
          FUN_RAW_ADDR mess has been cleaned up a little), in reverse
          format (that is, we store who-is-called rather than who-calls).
        * sb-introspect gets access to the simple-funs through the
          infodb, so each lookup requires looping through the whole
          db. This is snappy enough on my machine even with lots of
          code loaded, but some other storage strategy might be
          worth looking at later.

27 files changed:
NEWS
build-order.lisp-expr
contrib/sb-introspect/sb-introspect.lisp
contrib/sb-introspect/test-driver.lisp
contrib/sb-introspect/xref-test-data.lisp [new file with mode: 0644]
contrib/sb-introspect/xref-test.lisp [new file with mode: 0644]
package-data-list.lisp-expr
src/code/defmacro.lisp
src/code/early-fasl.lisp
src/code/fop.lisp
src/code/function-names.lisp
src/compiler/dump.lisp
src/compiler/entry.lisp
src/compiler/generic/genesis.lisp
src/compiler/generic/objdef.lisp
src/compiler/generic/target-core.lisp
src/compiler/ir1report.lisp
src/compiler/ir1tran.lisp
src/compiler/main.lisp
src/compiler/node.lisp
src/compiler/policies.lisp
src/compiler/vop.lisp
src/compiler/xref.lisp [new file with mode: 0644]
src/runtime/gc-common.c
src/runtime/gc-internal.h
src/runtime/purify.c
version.lisp-expr

diff --git a/NEWS b/NEWS
index f3aed3c..6d79fba 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,10 @@
 ;;;; -*- coding: utf-8; -*-
 changes in sbcl-1.0.1 relative to sbcl-1.0:
+  * new feature: the compiler stores cross-referencing information
+    abount function calls (who-calls), macroexpansion (who-macroexpands)
+    and special variables (who-binds, who-sets, who-references) for code
+    compiled with (< SPACE 3). This information is available through the
+    sb-introspect contrib.
   * improvement: sb-sprof traces call stacks to an arbitrary depth on
     x86/x86-64, rather than the previous fixed depth of 8
   * bug fix: non-ascii command-line arguments are processed correctly 
index 9b30bf1..a3d2ec0 100644 (file)
  ("src/compiler/dump")
 
  ("src/compiler/main") ; needs DEFSTRUCT FASL-OUTPUT from dump.lisp
+ ("src/compiler/xref")
  ("src/code/source-location")
  ("src/compiler/target-main" :not-host)
  ("src/compiler/ir1tran")
index 777ab6d..8ef3113 100644 (file)
            "DEFINITION-SOURCE-PLIST"
            "DEFINITION-NOT-FOUND" "DEFINITION-NAME"
            "FIND-FUNCTION-CALLEES"
-           "FIND-FUNCTION-CALLERS"))
+           "FIND-FUNCTION-CALLERS"
+           "WHO-BINDS"
+           "WHO-CALLS"
+           "WHO-REFERENCES"
+           "WHO-SETS"
+           "WHO-MACROEXPANDS"))
 
 (in-package :sb-introspect)
 
@@ -509,4 +514,76 @@ constant pool."
                          function))
             (funcall fn obj))))))))
 
+;;; XREF facility
+
+(defun get-simple-fun (functoid)
+  (etypecase functoid
+    (sb-kernel::fdefn
+     (get-simple-fun (sb-vm::fdefn-fun functoid)))
+    ((or null sb-impl::funcallable-instance)
+     nil)
+    (function
+     (sb-kernel::%closure-fun functoid))))
+
+(defun collect-xref (kind-index wanted-name)
+  (let ((ret nil))
+    (dolist (env sb-c::*info-environment* ret)
+      ;; Loop through the infodb ...
+      (sb-c::do-info (env :class class :type type :name info-name
+                          :value value)
+        ;; ... looking for function or macro definitions
+        (when (and (eql class :function)
+                   (or (eql type :macro-function)
+                       (eql type :definition)))
+          ;; Get a simple-fun for the definition, and an xref array
+          ;; from the table if available.
+          (let* ((simple-fun (get-simple-fun value))
+                 (xrefs (when simple-fun
+                          (sb-vm::%simple-fun-xrefs simple-fun)))
+                 (array (when xrefs
+                          (aref xrefs kind-index))))
+            ;; Loop through the name/path xref entries in the table
+            (loop for i from 0 below (length array) by 2
+                  for xref-name = (aref array i)
+                  for xref-path = (aref array (1+ i))
+                  do (when (eql xref-name wanted-name)
+                       (let ((source-location
+                              (find-function-definition-source simple-fun)))
+                         ;; Use the more accurate source path from
+                         ;; the xref entry.
+                         (setf (definition-source-form-path source-location)
+                               xref-path)
+                         (push (cons info-name source-location)
+                               ret))))))))))
+
+(defun who-calls (function-name)
+  "Use the xref facility to search for source locations where the
+global function named FUNCTION-NAME is called. Returns a list of
+function name, definition-source pairs."
+  (collect-xref #.(position :calls sb-c::*xref-kinds*) function-name))
+
+(defun who-binds (symbol)
+  "Use the xref facility to search for source locations where the
+special variable SYMBOL is rebound. Returns a list of function name,
+definition-source pairs."
+  (collect-xref #.(position :binds sb-c::*xref-kinds*) symbol))
+
+(defun who-references (symbol)
+  "Use the xref facility to search for source locations where the
+special variable or constant SYMBOL is read. Returns a list of function
+name, definition-source pairs."
+  (collect-xref #.(position :references sb-c::*xref-kinds*) symbol))
+
+(defun who-sets (symbol)
+  "Use the xref facility to search for source locations where the
+special variable SYMBOL is written to. Returns a list of function name,
+definition-source pairs."
+  (collect-xref #.(position :sets sb-c::*xref-kinds*) symbol))
+
+(defun who-macroexpands (macro-name)
+  "Use the xref facility to search for source locations where the
+macro MACRO-NAME is expanded. Returns a list of function name,
+definition-source pairs."
+  (collect-xref #.(position :macroexpands sb-c::*xref-kinds*) macro-name))
+
 (provide 'sb-introspect)
index 6abd985..03fdedd 100644 (file)
@@ -72,5 +72,9 @@
 (assert (matchp-name :function 'cl-user::one 2))
 (sb-profile:unprofile cl-user::one)
 
+;;; Test the xref facility
+
+(load (merge-pathnames "xref-test.lisp" *load-pathname*))
+
 ;;; Unix success convention for exit codes
 (sb-ext:quit :unix-status 0)
diff --git a/contrib/sb-introspect/xref-test-data.lisp b/contrib/sb-introspect/xref-test-data.lisp
new file mode 100644 (file)
index 0000000..f529839
--- /dev/null
@@ -0,0 +1,157 @@
+(defvar *a* nil)
+(defconstant +z+ 'zzz)
+
+(defun foo () 1)
+(defun bar (x) x)
+
+;; Should:
+;;   reference *a*
+;;   call bar
+;;   not call foo
+;;   not call xref/2
+(defun xref/1 ()
+  (flet ((foo ()
+           (bar *a*)))
+    (flet ((xref/2 ()
+             1))
+      (foo)
+      (xref/2))))
+
+;; Should:
+;;   reference *a*, set *a*, bind *a*
+;;   call xref/1
+;;   not bind b
+(defun xref/2 ()
+  (setf *a* *a*)
+  (let* ((b 1)
+         (*a* b))
+    (when nil
+      (xref/1))))
+
+(let ((x 1))
+  ;; Should:
+  ;;   call bar
+  ;;   not reference *a*
+  (defun xref/3 ()
+    (bar x))
+  ;; Should:
+  ;;   not call bar
+  ;;   reference *a*
+  (defun xref/4 ()
+    (setf x *a*)))
+
+
+(flet ((z ()
+         (xref/2)))
+  ;; Should:
+  ;;   call xref/2
+  ;;   not call z
+  (defun xref/5 ()
+    (z))
+  ;; Should:
+  ;;   call xref/2
+  ;;   not call z
+  (defun xref/6 ()
+    (z)))
+
+(defun xref/7 ()
+  (flet ((a ()
+           (xref/6)))
+    #'a))
+
+;; call xref/2
+(let ((a 1))
+  (defvar *b* (or (xref/2) a)))
+
+;; call xref/6
+(defvar *c* (xref/6))
+
+;; call xref/2 twice (not three times)
+(defun xref/8 ()
+  (flet ((a ()
+           (xref/2)))
+    (a)
+    (a)
+    (xref/2)))
+
+;; Methods work, even ones with lots of arguments.
+(defmethod xref/10 (a b c d e f g h (i fixnum))
+  (xref/2))
+
+;; Separate methods are indeed separate
+(defmethod xref/11 ((a fixnum))
+  (declare (ignore a))
+  (xref/2))
+
+(defmethod xref/11 ((a float))
+  (declare (ignore a))
+  (xref/3))
+
+(declaim (inline inline/1))
+(defun inline/1 ()
+  (xref/3)
+  (values +z+ *a*))
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (defun xref/12 ()
+    (flet ((a ()
+             ;; Counts as calling xref/2
+             (xref/2)))
+      (declare (inline a))
+      (a)
+      ;; Doesn't count as calling xref/3, or referring to +z+ / *a*
+      (inline/1))))
+
+;; calling a function in a macro body
+(defmacro macro/1 ()
+  (when nil
+    (xref/12))
+  nil)
+
+;; expanding a macro
+(defun macro-use/1 ()
+  (macro/1))
+
+;; expanding a macro in an flet/labels
+(defun macro-use/2 ()
+  (flet ((inner-flet ()
+           (macro/1)))
+    (inner-flet)))
+
+;; expanding a macro in an toplevel flet/labels
+(flet ((outer-flet ()
+         (macro/1)))
+  (defun macro-use/3 ()
+    (outer-flet)))
+
+;; expanding a macro in an inlined flet/labels
+(defun macro-use/4 ()
+  (flet ((inner-flet ()
+           (macro/1)))
+    (declare (inline inner-flet))
+    (inner-flet)))
+
+(declaim (inline inline/2))
+(defun inline/2 ()
+  (macro/1))
+
+;; Inlining inline/3 doesn't count as macroexpanding macro/1
+(defun macro-use/5 ()
+  (inline/2))
+
+;;; Code in the macrolet definition bodies is currently not considered
+;;; at all for XREF. Maybe it should be, but it's slightly tricky to
+;;; implement.
+#+nil
+(progn
+  (defun macrolet/1 ()
+    (macrolet ((a ()
+                 (inline/2)
+               1))
+      (a)))
+  (defun macrolet/2 ()
+    (macrolet ((inner-m ()
+                 (macro/1)))
+      (inner-m))))
+
+;;; Test references to / from compiler-macros
diff --git a/contrib/sb-introspect/xref-test.lisp b/contrib/sb-introspect/xref-test.lisp
new file mode 100644 (file)
index 0000000..8765f84
--- /dev/null
@@ -0,0 +1,46 @@
+(defpackage :sb-introspect-test/xref
+  (:use "SB-INTROSPECT" "CL"))
+
+(in-package :sb-introspect-test/xref)
+
+(load (compile-file (merge-pathnames "xref-test-data.lisp" *load-pathname*)))
+
+(labels ((natural< (a b)
+           (string< (princ-to-string a) (princ-to-string b))))
+  (let ((tests '(((sb-introspect::who-calls 'foo) ())
+                 ((sb-introspect::who-calls 'bar) (xref/1 xref/3))
+                 ((sb-introspect::who-calls 'xref/1) (xref/2))
+                 ((sb-introspect::who-calls 'xref/2)
+                  (xref/5 xref/6 xref/8 xref/8 xref/12
+                   (sb-pcl::fast-method xref/10
+                                        (t t t t t t t t fixnum))
+                   (sb-pcl::fast-method xref/11 (fixnum))))
+                 ((sb-introspect::who-calls 'xref/3)
+                  (inline/1 (sb-pcl::fast-method xref/11 (float))))
+                 ((sb-introspect::who-calls 'xref/4) ())
+                 ((sb-introspect::who-calls 'xref/5) ())
+                 ((sb-introspect::who-calls 'xref/6) (xref/7))
+                 ((sb-introspect::who-calls 'xref/7) ())
+                 ((sb-introspect::who-calls 'xref/8) ())
+                 ((sb-introspect::who-calls 'xref/10) ())
+                 ((sb-introspect::who-calls 'xref/11) ())
+                 ((sb-introspect::who-calls 'inline/1) (xref/12))
+                 ((sb-introspect::who-calls 'xref/12) (macro/1))
+                 ((sb-introspect::who-macroexpands 'macro/1)
+                  (macro-use/1 macro-use/2 macro-use/3 macro-use/4 inline/2))
+                 ((sb-introspect::who-binds '*a*) (xref/2))
+                 ((sb-introspect::who-sets '*a*) (xref/2))
+                 ((sb-introspect::who-references '*a*)
+                  (xref/1 xref/2 xref/4 inline/1))
+                 ((sb-introspect::who-references '+z+)
+                  (inline/1)))))
+    (loop for x in tests
+          for form = (first x)
+          for wanted = (sort (second x) #'natural<)
+          for result = (sort (loop for name in (eval form)
+                                   collect (car name))
+                             #'natural<)
+          do (assert (equalp wanted result)
+                     nil
+                     "form=~a~%wanted=~a~%result=~a~%" form wanted result))))
+
index 3b75870..c1721cc 100644 (file)
@@ -635,12 +635,6 @@ like *STACK-TOP-HINT* and unsupported stuff like *TRACED-FUN-LIST*."
                ;; and a mechanism for controlling same at compile time
                "MUFFLE-CONDITIONS" "UNMUFFLE-CONDITIONS"
 
-               ;; FIXME: This name doesn't match the DEFFOO - vs. -
-               ;; DEFINE-FOO convention used in the ANSI spec, and so
-               ;; was deprecated in sbcl-0.pre7, ca. 2001-12-12. After
-               ;; a year or so it can go away completely.
-               "DEF-SOURCE-CONTEXT"
-
                ;; extended declarations..
                "FREEZE-TYPE" "INHIBIT-WARNINGS"
                "MAYBE-INLINE"
@@ -1532,7 +1526,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
                "PROCLAIM-AS-FUN-NAME" "BECOME-DEFINED-FUN-NAME"
                "%NUMERATOR" "CLASSOID-TYPEP" "DSD-READ-ONLY"
                "DSD-DEFAULT" "LAYOUT-INHERITS" "DD-LENGTH"
-               "%CODE-ENTRY-POINTS" "%DENOMINATOR"
+               "%CODE-ENTRY-POINTS" "%DENOMINATOR" "%SIMPLE-FUN-XREFS"
 
                "STANDARD-CLASSOID" "CLASSOID-OF"
                "MAKE-STANDARD-CLASSOID" "CLASSOID-CELL-TYPEP"
index 21caf2d..d45a27c 100644 (file)
       (multiple-value-bind (new-body local-decs doc)
           (parse-defmacro lambda-list whole body name 'defmacro
                           :environment environment)
-        (let ((def `(lambda (,whole ,environment)
+        (let ((def `(#+sb-xc-host lambda
+                     ;; Use a named-lambda rather than a lambda so that
+                     ;; proper xref information can be stored. Use a
+                     ;; list-based name, since otherwise the compiler
+                     ;; will momentarily assume that it names a normal
+                     ;; function, and report spurious warnings about
+                     ;; redefinition a macro as a function, and then
+                     ;; vice versa.
+                     #-sb-xc-host #-sb-xc-host named-lambda (defmacro ,name)
+                     (,whole ,environment)
                       ,@local-decs
                       ,new-body))
-              ;; If we want to move over to list-style names
-              ;; [e.g. (DEFMACRO FOO), maybe to support some XREF-like
-              ;; functionality] here might be a good place to start.
               (debug-name (sb!c::debug-name 'macro-function name)))
           `(eval-when (:compile-toplevel :load-toplevel :execute)
              (sb!c::%defmacro ',name #',def ',lambda-list
index 93eb465..de3ffd1 100644 (file)
@@ -76,7 +76,7 @@
 ;;; versions which break binary compatibility. But it certainly should
 ;;; be incremented for release versions which break binary
 ;;; compatibility.
-(def!constant +fasl-file-version+ 71)
+(def!constant +fasl-file-version+ 72)
 ;;; (description of versions before 0.9.0.1 deleted in 0.9.17)
 ;;; 56: (2005-05-22) Something between 0.9.0.1 and 0.9.0.14. My money is
 ;;;     on 0.9.0.6 (MORE CASE CONSISTENCY).
 ;;; 69: (2006-08-17) changed validity of various initargs for methods
 ;;; 70: (2006-09-13) changes to *PSEUDO-ATOMIC* on x86 and x86-64
 ;;; 71: (2006-11-19) CLOS calling convention changes
+;;; 72: (2006-12-05) Added slot to the primitive function type
 
 ;;; the conventional file extension for our fasl files
 (declaim (type simple-string *fasl-file-type*))
index 6179833..146bec9 100644 (file)
@@ -649,7 +649,8 @@ bug.~:@>")
   #+sb-xc-host ; since xc host doesn't know how to compile %PRIMITIVE
   (error "FOP-FUN-ENTRY can't be defined without %PRIMITIVE.")
   #-sb-xc-host
-  (let ((type (pop-stack))
+  (let ((xrefs (pop-stack))
+        (type (pop-stack))
         (arglist (pop-stack))
         (name (pop-stack))
         (code-object (pop-stack))
@@ -664,6 +665,7 @@ bug.~:@>")
       (setf (%simple-fun-name fun) name)
       (setf (%simple-fun-arglist fun) arglist)
       (setf (%simple-fun-type fun) type)
+      (setf (%simple-fun-xrefs fun) xrefs)
       ;; FIXME: See the comment about *LOAD-PRINT* in FOP-EVAL.
       #+nil (when *load-print*
               (load-fresh-line)
index 029b795..822ab8c 100644 (file)
@@ -56,6 +56,24 @@ use as a BLOCK name in the function in question."
           (cons (unless (eq (car fun) 'setf)
                   (valid-function-name-p fun))))))))
 
+(defun macro-function-name (name)
+  (when (and (cdr name)
+             (consp (cdr name)))
+    (destructuring-bind (fun &rest rest) (cdr name)
+      (when (null rest)
+        (typecase fun
+          ;; (DEFMACRO FOO)
+          (symbol (values t fun))
+          ;; (DEFMACRO (SETF FOO))
+          (cons (when (eq (car fun) 'setf)
+                  (valid-function-name-p fun))))))))
+
+(define-function-name-syntax defmacro (name)
+  (macro-function-name name))
+
+(define-function-name-syntax macrolet (name)
+  (macro-function-name name))
+
 #-sb-xc-host
 (defun !function-names-cold-init ()
   (setf *valid-fun-names-alist* '#.*valid-fun-names-alist*))
index a296451..bf3527e 100644 (file)
     (dump-object name file)
     (dump-object (sb!c::entry-info-arguments entry) file)
     (dump-object (sb!c::entry-info-type entry) file)
+    (dump-object (sb!c::entry-info-xref entry) file)
     (dump-fop 'fop-fun-entry file)
     (dump-word (label-position (sb!c::entry-info-offset entry)) file)
     (dump-pop file)))
index 8495698..6863e05 100644 (file)
@@ -43,6 +43,8 @@
     (setf (entry-info-offset info) (gen-label))
     (setf (entry-info-name info)
           (leaf-debug-name internal-fun))
+    (setf (entry-info-xref info)
+          (pack-xref-data (functional-xref internal-fun)))
     (when (policy bind (>= debug 1))
       (let ((args (functional-arg-documentation internal-fun)))
         (aver (not (eq args :unspecified)))
index ef6968b..ce47985 100644 (file)
@@ -2439,7 +2439,8 @@ core and return a descriptor to it."
     (write-wordindexed code slot value)))
 
 (define-cold-fop (fop-fun-entry)
-  (let* ((type (pop-stack))
+  (let* ((xrefs (pop-stack))
+         (type (pop-stack))
          (arglist (pop-stack))
          (name (pop-stack))
          (code-object (pop-stack))
@@ -2496,6 +2497,7 @@ core and return a descriptor to it."
     (write-wordindexed fn sb!vm:simple-fun-name-slot name)
     (write-wordindexed fn sb!vm:simple-fun-arglist-slot arglist)
     (write-wordindexed fn sb!vm:simple-fun-type-slot type)
+    (write-wordindexed fn sb!vm::simple-fun-xrefs-slot xrefs)
     fn))
 
 (define-cold-fop (fop-foreign-fixup)
index defdafe..215bf76 100644 (file)
         :ref-trans %simple-fun-type
         :set-known (unsafe)
         :set-trans (setf %simple-fun-type))
+  (xrefs :init :null
+         :ref-trans %simple-fun-xrefs
+         :ref-known (flushable)
+         :set-trans (setf %simple-fun-xrefs)
+         :set-known ())
   ;; the SB!C::DEBUG-FUN object corresponding to this object, or NIL for none
   #+nil ; FIXME: doesn't work (gotcha, lowly maintenoid!) See notes on bug 137.
   (debug-fun :ref-known (flushable)
index 6b76b18..c07f5e5 100644 (file)
@@ -30,6 +30,7 @@
       (setf (%simple-fun-name res) (entry-info-name entry-info))
       (setf (%simple-fun-arglist res) (entry-info-arguments entry-info))
       (setf (%simple-fun-type res) (entry-info-type entry-info))
+      (setf (%simple-fun-xrefs res) (entry-info-xref entry-info))
 
       (note-fun entry-info res object))))
 
index 12a6580..0a90d60 100644 (file)
            (lambda (,n-whole)
              (destructuring-bind ,lambda-list ,n-whole ,@body)))))
 
-(defmacro def-source-context (&rest rest)
-  (deprecation-warning 'def-source-context 'define-source-context)
-  `(define-source-context ,@rest))
-
 (define-source-context defstruct (name-or-options &rest slots)
   (declare (ignore slots))
   `(defstruct ,(if (consp name-or-options)
index 81588fb..d876191 100644 (file)
     (:macro
      (ir1-convert start next result
                   (careful-expand-macro (info :function :macro-function fun)
-                                        form)))
+                                        form))
+     (unless (policy *lexenv* (zerop store-xref-data))
+       (record-macroexpansion fun (ctran-block start) *current-path*)))
     ((nil :function)
      (ir1-convert-srctran start next result
                           (find-free-fun fun "shouldn't happen! (no-cmacro)")
index 2fe6b17..b312c75 100644 (file)
 
   (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*
                          (apply #'ir1-convert-lambdalike
                                 definition
                                 (list :source-name name))))
+           (debug-name (debug-name 'tl-xep name))
            (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
index 6319c5b..3467f60 100644 (file)
   ;; entire initial component just to clear the flags.
   (flag nil)
   ;; some kind of info used by the back end
-  (info nil))
+  (info nil)
+  ;; what macroexpansions happened "in" this block, used for xref
+  (macroexpands nil :type list))
 (def!method print-object ((cblock cblock) stream)
   (print-unreadable-object (cblock stream :type t :identity t)
     (format stream "~W :START c~W"
   ;; sure that no closure is needed.
   (allocator nil :type (or null combination))
   ;; various rare miscellaneous info that drives code generation & stuff
-  (plist () :type list))
+  (plist () :type list)
+  ;; xref information for this functional (only used for functions with an
+  ;; XEP)
+  (xref () :type list))
 (defprinter (functional :identity t)
   %source-name
   %debug-name
index 3bcdcaa..19e3b03 100644 (file)
@@ -91,3 +91,9 @@
 (define-optimization-quality insert-array-bounds-checks
     (if (= safety 0) 0 3)
   ("no" "yes" "yes" "yes"))
+
+(define-optimization-quality store-xref-data
+    (if (= space 3)
+        0
+        3)
+  ("no" "yes" "yes" "yes"))
index cfcb0f4..4b03660 100644 (file)
   (arguments nil :type list)
   ;; a function type specifier representing the arguments and results
   ;; of this function
-  (type 'function :type (or list (member function))))
+  (type 'function :type (or list (member function)))
+  ;; xref information for the XEP
+  (xref nil :type (or null simple-vector)))
 
 ;;; An IR2-PHYSENV is used to annotate non-LET LAMBDAs with their
 ;;; passing locations. It is stored in the PHYSENV-INFO.
diff --git a/src/compiler/xref.lisp b/src/compiler/xref.lisp
new file mode 100644 (file)
index 0000000..40c0d37
--- /dev/null
@@ -0,0 +1,184 @@
+;;;; xref facility
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; This software is derived from the CMU CL system, which was
+;;;; written at Carnegie Mellon University and released into the
+;;;; public domain. The software is in the public domain and is
+;;;; provided with absolutely no warranty. See the COPYING and CREDITS
+;;;; files for more information.
+
+(in-package "SB!C")
+
+(defvar *xref-kinds* '(:binds :calls :sets :references :macroexpands))
+
+(defun record-component-xrefs (component)
+  (declare (type component component))
+  (when (policy *lexenv* (zerop store-xref-data))
+    (return-from record-component-xrefs))
+  (do ((block (block-next (component-head component)) (block-next block)))
+      ((null (block-next block)))
+    (let* ((this-cont (block-start block))
+           (last (block-last block)))
+      (flet ((handle-node (functional)
+               ;; Record xref information for all nodes in the block.
+               ;; Note that this code can get executed several times
+               ;; for the same block, if the functional is referenced
+               ;; from multiple XEPs.
+               (loop for node = (ctran-next this-cont) then (ctran-next (node-next node))
+                     until (eq node last)
+                     do (record-node-xrefs node functional))
+               ;; Properly record the deferred macroexpansion information
+               ;; that's been stored in the block.
+               (dolist (xref-data (block-macroexpands block))
+                 (record-xref :macroexpands
+                              (car xref-data)
+                              ;; We use the debug-name of the functional
+                              ;; as an identifier. This works quite nicely,
+                              ;; except for (fast/slow)-methods with non-symbol,
+                              ;; non-number eql specializers, for which
+                              ;; the debug-name doesn't map exactly
+                              ;; to the fdefinition of the method.
+                              functional
+                              nil
+                              (cdr xref-data)))))
+        (call-with-block-external-functionals block #'handle-node)))))
+
+(defun call-with-block-external-functionals (block fun)
+  (let* ((functional (block-home-lambda block))
+         (seen nil))
+    (labels ((local-function-name-p (name)
+               (and (consp name)
+                    (member (car name)
+                            '(flet labels lambda))))
+             (handle-functional (functional)
+               ;; If a functional looks like a global function (has a
+               ;; XEP, isn't a local function or a lambda) record xref
+               ;; information for it. Otherwise recurse on the
+               ;; home-lambdas of all references to the functional.
+               (when (eq (functional-kind functional) :external)
+                 (let ((entry (functional-entry-fun functional)))
+                   (when entry
+                     (let ((name (functional-debug-name entry)))
+                       (unless (local-function-name-p name)
+                         (return-from handle-functional
+                           (funcall fun entry)))))))
+               ;; Recurse only if we haven't already seen the
+               ;; functional.
+               (unless (member functional seen)
+                 (push functional seen)
+                 (dolist (ref (functional-refs functional))
+                   (handle-functional (node-home-lambda ref))))))
+      (unless (or (eq :deleted (functional-kind functional))
+                  ;; If the block came from an inlined global
+                  ;; function, ignore it.
+                  (and (functional-inlinep functional)
+                       (symbolp (functional-debug-name functional))))
+        (handle-functional functional)))))
+
+(defun record-node-xrefs (node context)
+  (declare (type node node))
+  (etypecase node
+    ((or creturn cif entry combination mv-combination cast))
+    (ref
+     (let ((leaf (ref-leaf node)))
+       (typecase leaf
+         (global-var
+          (let* ((name (leaf-debug-name leaf)))
+            (case (global-var-kind leaf)
+              ;; Reading a special
+              (:special
+               (record-xref :references name context node nil))
+              ;; Calling a function
+              (:global-function
+               (record-xref :calls name context node nil)))))
+         ;; Inlined global function
+         (clambda
+          (when (functional-inlinep leaf)
+            (let ((name (leaf-debug-name leaf)))
+              ;; FIXME: we should store the original var into the
+              ;; functional when creating inlined-functionals, so that
+              ;; we could just check whether it was a global-var,
+              ;; rather then needing to guess based on the debug-name.
+              (when (or (symbolp name)
+                        ;; Any non-SETF non-symbol names will
+                        ;; currently be either non-functions or
+                        ;; internals.
+                        (and (consp name)
+                             (equal (car name) 'setf)))
+                ;; TODO: a WHO-INLINES xref-kind could be useful
+                (record-xref :calls name context node nil)))))
+         ;; Reading a constant
+         (constant
+          (let* ((name (constant-%source-name leaf)))
+            (record-xref :references name context node nil))))))
+    ;; Setting a special variable
+    (cset
+     (let* ((var (set-var node)))
+       (when (and (global-var-p var)
+                  (eq :special (global-var-kind var)))
+         (record-xref :sets
+                      (leaf-debug-name var)
+                      context
+                      node
+                      nil))))
+    ;; Binding a special variable
+    (bind
+     (let ((vars (lambda-vars (bind-lambda node))))
+       (dolist (var vars)
+         (when (lambda-var-specvar var)
+           (record-xref :binds
+                        (lambda-var-%source-name var)
+                        context
+                        node
+                        nil)))))))
+
+(defun internal-name-p (what)
+  ;; Don't store XREF information for internals. We define as internal
+  ;; anything named only by symbols from either implementation
+  ;; packages, COMMON-LISP or KEYWORD. The last one is useful for
+  ;; example when dealing with ctors.
+  (typecase what
+    (list
+     (every #'internal-name-p what))
+    (symbol
+     (member (symbol-package what)
+             (load-time-value (list* (find-package "COMMON-LISP")
+                                     (find-package "KEYWORD")
+                                     (remove-if-not
+                                      (lambda (package)
+                                        (= (mismatch "SB!"
+                                                     (package-name package))
+                                           3))
+                                      (list-all-packages))))))
+    (t t)))
+
+(defun record-xref (kind what context node path)
+  (unless (internal-name-p what)
+    (let ((path (reverse
+                 (source-path-original-source
+                  (or path
+                      (node-source-path node))))))
+      (push (list what path)
+            (getf (functional-xref context) kind)))))
+
+(defun record-macroexpansion (what block path)
+  (unless (internal-name-p what)
+    (push (cons what path) (block-macroexpands block))))
+
+;;; Pack the xref table that was stored for a functional into a more
+;;; space-efficient form, and return that packed form.
+(defun pack-xref-data (xref-data)
+  (when xref-data
+    (let ((array (make-array (length *xref-kinds*))))
+      (loop for key in *xref-kinds*
+            for i from 0
+            for values = (remove-duplicates (getf xref-data key)
+                                            :test #'equal)
+            for flattened = (reduce #'append values :from-end t)
+            collect (setf (aref array i)
+                          (when flattened
+                            (make-array (length flattened)
+                                        :initial-contents flattened))))
+      array)))
index 543c20f..f84d150 100644 (file)
@@ -367,6 +367,7 @@ scav_code_header(lispobj *where, lispobj object)
         scavenge(&function_ptr->name, 1);
         scavenge(&function_ptr->arglist, 1);
         scavenge(&function_ptr->type, 1);
+        scavenge(&function_ptr->xrefs, 1);
     }
 
     return n_words;
index 7de67a1..c9d1911 100644 (file)
@@ -16,6 +16,8 @@
 #ifndef _GC_INTERNAL_H_
 #define _GC_INTERNAL_H_
 
+#include <genesis/simple-fun.h>
+
 /* disabling gc assertions made no discernable difference to GC speed,
  * last I tried it - dan 2003.12.21 */
 #if 1
@@ -59,20 +61,10 @@ NWORDS(unsigned long x, unsigned long n_bits)
 
 /* FIXME: Shouldn't this be defined in sbcl.h? */
 
-/* FIXME (1) this could probably be defined using something like
- *  sizeof(lispobj)*floor(sizeof(struct simple_fun)/sizeof(lispobj))
- *    -  FUN_POINTER_LOWTAG
- * as I'm reasonably sure that simple_fun->code must always be the
- * last slot in the object
-
- * FIXME (2) it also appears in purify.c, and it has a different value
- * for SPARC users in that bit
- */
-
 #if defined(LISP_FEATURE_SPARC)
 #define FUN_RAW_ADDR_OFFSET 0
 #else
-#define FUN_RAW_ADDR_OFFSET (6*sizeof(lispobj) - FUN_POINTER_LOWTAG)
+#define FUN_RAW_ADDR_OFFSET (offsetof(struct simple_fun, code) - FUN_POINTER_LOWTAG)
 #endif
 
 /* values for the *_alloc_* parameters */
index 944a5e7..aadf78b 100644 (file)
@@ -70,14 +70,6 @@ static long later_count = 0;
  #define SIMPLE_ARRAY_WORD_WIDETAG SIMPLE_ARRAY_UNSIGNED_BYTE_64_WIDETAG
 #endif
 
-/* FIXME: Shouldn't this be defined in sbcl.h?  See also notes in
- * cheneygc.c */
-
-#ifdef LISP_FEATURE_SPARC
-#define FUN_RAW_ADDR_OFFSET 0
-#else
-#define FUN_RAW_ADDR_OFFSET (6*sizeof(lispobj) - FUN_POINTER_LOWTAG)
-#endif
 \f
 static boolean
 forwarding_pointer_p(lispobj obj)
index bd376c6..e610e76 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.0.17"
+"1.0.0.18"