git.fiddlerwoaroof.com
depend/dependency-analysis.scm
4e987026
 ;;; depend/depend.scm     Author: John
 
 ;;; This performs dependency analysis.  All module definitions are gathered
 ;;; into a single nested let/let*.
 
 (define-walker depend ast-td-depend-walker)
 
 ;;; This extracts the declarations out of the top level of the modules and
 ;;; creates a single let defining all values from the modules.
 
 (define (do-dependency-analysis modules)
   (let ((all-decls '()))
     (dolist (mod modules)
       (setf all-decls (append (module-decls mod) all-decls)))
     (analyze-dependency-top
       (**let all-decls (make void)))))
 
 
 (define *depend-fn-table* (make-table))
 
 (define-syntax (var-depend-fn var)
   `(table-entry *depend-fn-table* ,var))
 
 (define (analyze-dependency-top x)
   (dynamic-let ((*depend-fn-table*  (make-table)))
     (analyze-dependency x)))
 
 
 ;;; This is the entry point to dependency analysis for an expression or decl
 
 (define (analyze-dependency x)
   (call-walker depend x))
 
 (define (analyze-dependency/list l)
   (dolist (x l)
     (analyze-dependency x)))
 
 ;;; This makes default walkers for dependency analysis.  Expressions are
 ;;; walked into; declaration lists must be sorted.
 
 (define-local-syntax (make-depend-code slot type)
   (let ((stype  (sd-type slot))
         (sname  (sd-name slot))
 	(depend-exp-types '(exp alt qual single-fun-def guarded-rhs)))
     (cond ((and (symbol? stype)
 		(memq stype depend-exp-types))
 	   `(analyze-dependency (struct-slot ',type ',sname object)))
           ((and (pair? stype)
                 (eq? (car stype) 'list)
                 (symbol? (cadr stype))
                 (memq (cadr stype) depend-exp-types)
 	   `(analyze-dependency/list
 		(struct-slot ',type ',sname object))))
           ((equal? stype '(list decl))
 	   `(setf (struct-slot ',type ',sname object)
 		  (restructure-decl-list (struct-slot ',type ',sname object))))
           (else
 ;           (format '#t "Depend: skipping slot ~A in ~A~%"
 ;                  (sd-name slot)
 ;                  type)
            '#f))))
 
 (define-modify-walker-methods depend
   (lambda let if case alt exp-sign app con-ref
    integer-const float-const char-const string-const
    list-exp sequence sequence-then sequence-to sequence-then-to
    list-comp section-l section-r qual-generator qual-filter omitted-guard
    con-number sel is-constructor cast void
    single-fun-def guarded-rhs
    case-block return-from and-exp
    )
   (object)
   make-depend-code)
 
 ;;; This sorts a list of decls.  Recursive groups are placed in
 ;;; special structures: recursive-decl-group
 
 (define (restructure-decl-list decls)
   (let ((stack '())
 	(now 0)
 	(sorted-decls '())
 	(edge-fn '()))
    (letrec ((visit (lambda (k)
 		     (let ((minval 0)
 			   (recursive? '#f)
 			   (old-edge-fn edge-fn))
 		       (incf now)
 ;		       (format '#t "Visiting ~A: id = ~A~%" (valdef-lhs k) now)
 		       (setf (valdef-depend-val k) now)
 		       (setf minval now)
 		       (push k stack)
 		       (setf edge-fn
 			     (lambda (tv)
 ;			       (format '#t "Edge ~A -> ~A~%" (valdef-lhs k)
 ;				                             (valdef-lhs tv))
 			       (let ((val (valdef-depend-val tv)))
                                 (cond ((eq? tv k)
 				       (setf recursive? '#t))
 				      ((eqv? val 0)
 				       (setf minval (min minval
 							 (funcall visit tv))))
 				      (else
 				       (setf minval (min minval val))))
 ;				(format '#t "Min for ~A is ~A~%"
 ;					(valdef-lhs k) minval)
 			       )))
 		       (analyze-dependency/list (valdef-definitions k))
 		       (setf edge-fn old-edge-fn)
 		       (when (eqv? minval (valdef-depend-val k))
 			 (let ((defs '()))
 			   (do ((quit? '#f)) (quit?)
 			     (push (car stack) defs)
 			     (setf (valdef-depend-val (car stack)) 100000)
 			     (setf quit? (eq? (car stack) k))
 			     (setf stack (cdr stack)))
 ;			   (format '#t "Popping stack: ~A~%"
 ;				   (map (lambda (x) (valdef-lhs x)) defs))
 			   (if (and (null? (cdr defs))
 				    (not recursive?))
 			       (push k sorted-decls)
 			       (push (make recursive-decl-group (decls defs))
 				     sorted-decls))))
 		       minval))))
     ;; for now assume all decl lists have only valdefs
     (dolist (d decls)
       (let ((decl d))  ; to force new binding for each closure
 	(setf (valdef-depend-val decl) 0)
 	(dolist (var (collect-pattern-vars (valdef-lhs decl)))
 	  (setf (var-depend-fn (var-ref-var var))
 		(lambda () (funcall edge-fn decl))))))
     (dolist (decl decls)
       (when (eqv? (valdef-depend-val decl) 0)
 	(funcall visit decl)))
     (dolist (decl decls)
       (dolist (var (collect-pattern-vars (valdef-lhs decl)))
 	(setf (var-depend-fn (var-ref-var var)) '#f)))
     (nreverse sorted-decls))))
 
 ;;; This is the only non-default walker needed.  When a reference to a
 ;;; variable is encountered, the sort algorithm above is notified.
 
 (define-walker-method depend var-ref (object)
   (let ((fn (var-depend-fn (var-ref-var object))))
     (when (not (eq? fn '#f))
        (funcall fn))))
 
 (define-walker-method depend overloaded-var-ref (object)
   (let ((fn (var-depend-fn (overloaded-var-ref-var object))))
     (when (not (eq? fn '#f))
        (funcall fn))))