(in-package "XLSCMP")
;;;;;
;;;;; Cell Insertion Phase
;;;;;
;;**** check this over carefully!!!
;;**** assumes cells can be determined from tree
;;**** change %setq to %set-cell-value here
(defun find-variables-set (tree)
(let ((svars nil)
(nodes (list tree)))
(loop
(if (null nodes) (return))
(let ((n (pop nodes)))
(dolist (c (node-children n))
(if (call-node-p c)
(let ((f (call-node-function c)))
(if (gfun-eq f '%setq)
(push (call-node-arg c 1) svars))))
(push c nodes))))
svars))
(defun insert-cells (tree)
(let ((svars (find-variables-set tree)))
(dolist (sv (remove-duplicates svars))
(let* ((owner (symbol-node-owner sv))
(ob (lambda-node-body owner))
(refs (find-references sv ob))
(cn (make-leaf-node (gensym "C"))))
(insert-node-tree
(make-call-node (get-gfun-node '%make-cell)
(make-continuation-node (list cn) ob)
sv)
owner
0)
(dolist (r refs)
(let* ((form (car r))
(i (cdr r))
(f (call-node-function form))
(p (node-parent form))
(j (position form (node-children p))))
(if (and (gfun-eq f '%setq) (= i 2))
(substitute-value cn r nil) ;**** better choice??
(let ((vn (make-leaf-node (gensym "V"))))
(substitute-value vn r nil) ;**** better choice??
(insert-node-tree
(make-call-node (get-gfun-node '%cell-value)
(make-continuation-node
(list vn)
form)
cn)
p
j))))))))
(flet ((fixup (s ns n)
(let ((nf (get-gfun-node ns)))
(dolist (r (find-references (get-gfun-node s) n))
(substitute-value nf r nil)))))
(fixup '%setq '%set-cell-value tree))
tree)
;;;;;
;;;;; Drop Unused Cells
;;;;;
(defun cell-used-p (refs n)
(dolist (r refs nil)
(let* ((form (car r))
(i (cdr r))
(f (call-node-function form)))
(unless (leaf-node-p f) (return t))
(case (gfun-symbol f)
((%cell-value %set-cell-value)
(unless (= i 2) (return t)))
(t (return t))))))
(defun find-unused-cell-variable-references (n)
(let ((refs (find-references (get-gfun-node '%make-cell) n))
(vrlist nil))
(dolist (r refs vrlist)
(let* ((c (call-node-arg (car r) 0))
(cc (if (lambda-node-p c) c (find-lambda-binding c)))
(v (first (lambda-node-arglist cc)))
(vrefs (find-references v n)))
(unless (cell-used-p vrefs n)
(push (list v (car r) vrefs) vrlist))))))
#|
;; **** this should produce correct results, but it may not get rid of all
;; **** the copies that could be dropped.
(defun safe-cell-access-p (ref)
(let* ((form (car ref))
(var (nth (cdr ref) (node-children form))) ;;****more efficient way?
(cc (symbol-node-owner var)))
(do ((p (node-parent form) (node-parent p)))
((eq p cc) t)
(if (and (call-node-p p)
(not (gfun-member (call-node-function p)
'(%copy %cell-value))))
(return nil)))))
|#
;; **** this is a little more agressive, but should still be OK
;; **** it would be better if the function could be recognized as a real fun
(defun safe-cell-access-p (ref)
(let* ((form (car ref))
(var (nth (cdr ref) (node-children form))) ;;****more efficient way?
(cc (symbol-node-owner var)))
(do ((p (node-parent form) (node-parent p)))
((eq p cc) t)
(if (call-node-p p)
(let ((f (call-node-function p))
(a (call-node-args p)))
(cond
((lambda-node-p f) (if (some #'lambda-node-p a) (return nil)))
((gfun-member f '(%setq %set-cell-value))
(let ((cell (call-node-arg (node-parent cc) 1)))
(if (eq cell (second a)) (return nil))))
(t (if (some #'lambda-node-p (rest a)) (return nil)))))))))
(defun remove-unused-cells (n)
(let ((cvars (find-unused-cell-variable-references n)))
(dolist (vr cvars)
(let* ((cname (first vr))
(refs (third vr))
(mcf (second vr))
(v (call-node-arg mcf 1))
(c (call-node-arg mcf 0)))
(dolist (r refs)
(let ((form (car r)))
(case (gfun-symbol (call-node-function form))
(%set-cell-value
(change-gfun form '%setq))
(%cell-value
(let* ((cc (call-node-arg form 0))
(vv (first (lambda-node-arglist cc)))
(vvrefs (find-references vv cc)))
(cond
((every #'safe-cell-access-p vvrefs)
(dolist (r vvrefs) (substitute-value cname r nil))
(set-lambda-node-arglist-fix cc nil)
(set-node-children form (list cc))
(set-node-parent cc form))
(t (change-gfun form '%copy))))))))
(change-gfun mcf '%copy))))
(collapse-null-lambda-calls n))
syntax highlighted by Code2HTML, v. 0.9.1