;;; -*- Mode:LISP; Package:NC ; Base:10; Readtable:CL -*- ;;;; Live Variable Analysis ;;; ;;; Live variable analysis in a tree would be a simple matter of ;;; finding the live variables below a node, then removing those ;;; variables bound at the node. However, the control structure of code ;;; trees is tangled by function calls (also meaning goto's and ;;; continuations) back up in the tree. ;;; For example in: ;;; ;;; (defun live-y (x) ;;; (labels ((f (y) ;;; (g y) ;;; (print y)) ;;; (g (z) ;;; (print z))) ;;; (f x))) ;;; ;;; y is live in g. (Therefore z cannot share storage with y. This could ;;; happen because g wants to be substituted inline into f, in fact ;;; if the (print y) were not there it would be a good idea.) The ;;; continuation to g is known to be ((B_3 IGNORE_2) (PRINT K_0 Y_1)) ;;; therefore the variable y is live in g by virtue of g referring to ;;; its continuation. What we get is a graph with a definite root and ;;; leaves but with cross links and cycles. ;;; The algorithm for finding live variables is to make multiple passes ;;; over the graph, proceeding in a depth first manner. Each node will be ;;; marked with the pass number. At each node we check the pass number to ;;; see if we have looped. If so, we return the nodes current list of live variables. ;;; If not, we find the live variables of the children of the node and ;;; construct the live variables for this node. If this list is different ;;; we update the node and set a flag saying there were changes on this pass. ;;; We make passes over the graph until there are no changes. ;;; This algorithm calculates the live variables for a node once for ;;; each pass (therefore each pass is O(n) n = number of nodes), and ;;; makes a number of passes equal to 2 + the length of the longest chain up. (defvar *changes* nil) (defvar *pass* 0) (defun live-analyze-top (node) (do ((*changes* nil nil) (*pass* 0 (1+ *pass*))) (()) (live-analyze-node (call-arg-n 1 (lambda-body node))) (when (null *changes*) (return))) (debug :live (format t "~%Live Analysis:") (pp-cps node :extra #'(lambda (node) (let ((live (lambda-live node))) (if (consp live) (mapcar #'variable-unique-name live) live)))))) (defun live-analyze-node (node) (cond ((lambda-node? node) (live-analyze-lambda node)) ((leaf-node? node) (live-analyze-leaf node)) (t (bug "live-analyze-node called on a call-node ~s" node)))) (defun live-analyze-leaf (node) (cond ((literal-node? node) '()) ((primop-node? node) '()) (t (adjoin (leaf-value node) (let ((cont (variable-known (leaf-value node)))) (and cont (live-analyze-lambda cont))))))) (zl:defsubst lambda-pass (lambda) (lambda-db lambda)) (defun lambda-seen-yet-on-this-pass? (lambda) (eql (lambda-pass lambda) *pass*)) (defun live-analyze-lambda (node) (if (lambda-seen-yet-on-this-pass? node) (lambda-live node) (progn (setf (lambda-pass node) *pass*) (let ((vars (live-analyze-call (lambda-body node)))) (let ((live (set-difference vars (lambda-all-variables node)))) (if (set-eq (lambda-live node) live) live (progn ; (format t "~%**** ~a~&old: ~a~&new: ~a" ; (mapcar #'variable-unique-name (lambda-all-variables node)) ; (mapcar #'variable-unique-name (lambda-live node)) ; (mapcar #'variable-unique-name live)) (setq *changes* t) (setf (lambda-live node) live)))))))) (defun live-analyze-call (node) (let ((proc (call-proc node))) (cond ((primop-node? proc) (cond ((eq (primop-value proc) primop/y) (live-analyze-y (call-arg-n 1 node))) ((eq (primop-value proc) primop/setq-lexical) (delete (reference-variable (call-arg-n 2 node)) (live-analyze-call-args node))) (t (live-analyze-call-args node)))) ((lambda-node? proc) (live-analyze-let proc node)) (t (live-analyze-call-args node))))) (defun live-analyze-let (proc node) (do ((args (call-args node) (rest args)) (live (live-analyze-lambda proc) (union live (unless (lambda-node? (first args)) (live-analyze-node (first args)))))) ((null args) live))) (defun live-analyze-call-args (node) (do ((args (call-proc+args node) (rest args)) (live '() (union live (live-analyze-node (first args))))) ((null args) live))) ;(defun live-analyze-y (lambda-node) ; (do ((args (call-args (lambda-body lambda-node)) (rest args)) ; (live '() (union live (live-analyze-lambda (first args))))) ; ((null args) ; (let ((live (set-difference live (lambda-all-variables lambda-node)))) ; (do ((args (call-args (lambda-body lambda-node)) (rest args))) ; ((null args) live) ; (setf (lambda-live (first args)) live)))))) ;;; just analyze body ;;; it will analyze label procs that it calls (defun live-analyze-y (lambda-node) (let ((live (set-difference (live-analyze-lambda (car (call-args (lambda-body lambda-node)))) (lambda-all-variables lambda-node)))) (setf (lambda-live lambda-node) live)))