;;; 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))))