mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Eager graph pruning in CSE
* module/language/cps/cse.scm (elide-predecessor, prune-branch) (prune-successors, term-successors): New helpers. (eliminate-common-subexpressions-in-fun): When we modify the CFG, update the analysis. Also, thread the substs map through CSE so that closures in high-level CPS can take advantage of eliminated variables. (fold-renumbered-functions): Take multiple seeds. (eliminate-common-subexpressions): Thread var substs map through CSE.
This commit is contained in:
parent
6b1835a169
commit
a92c623a66
1 changed files with 197 additions and 90 deletions
|
@ -147,7 +147,87 @@ false. It could be that both true and false proofs are available."
|
|||
(avail analysis-avail)
|
||||
(truthy-labels analysis-truthy-labels))
|
||||
|
||||
(define (eliminate-common-subexpressions-in-fun kfun conts out)
|
||||
;; When we determine that we can replace an expression with
|
||||
;; already-bound variables, we change the expression to a $values. At
|
||||
;; its continuation, if it turns out that the $values expression is the
|
||||
;; only predecessor, we elide the predecessor, to make redundant branch
|
||||
;; folding easier. Ideally, elision results in redundant branches
|
||||
;; having multiple predecessors which already have values for the
|
||||
;; branch.
|
||||
;;
|
||||
;; We could avoid elision, and instead search backwards when we get to a
|
||||
;; branch that we'd like to elide. However it's gnarly: branch elisions
|
||||
;; reconfigure the control-flow graph, and thus affect the avail /
|
||||
;; truthy maps. If we forwarded such a distant predecessor, if there
|
||||
;; were no intermediate definitions, we'd have to replay the flow
|
||||
;; analysis from far away. Maybe it's possible but it's not obvious.
|
||||
;;
|
||||
;; The elision mechanism is to rewrite predecessors to continue to the
|
||||
;; successor. We could have instead replaced the predecessor with the
|
||||
;; body of the successor, but that would invalidate the values of the
|
||||
;; avail / truthy maps, as well as the clobber sets.
|
||||
;;
|
||||
;; We can't always elide the predecessor though. If any of the
|
||||
;; predecessor's predecessors is a back-edge, it hasn't been
|
||||
;; residualized yet and so we can't rewrite it. This is an
|
||||
;; implementation limitation.
|
||||
;;
|
||||
(define (elide-predecessor label pred out analysis)
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(let ((pred-preds (intmap-ref preds pred)))
|
||||
(and
|
||||
;; Don't elide predecessors that are the targets of back-edges.
|
||||
(< (intset-prev pred-preds) pred)
|
||||
(cons
|
||||
(intset-fold
|
||||
(lambda (pred-pred out)
|
||||
(define (rename k) (if (eqv? k pred) label k))
|
||||
(intmap-replace!
|
||||
out pred-pred
|
||||
(rewrite-cont (intmap-ref out pred-pred)
|
||||
(($ $kargs names vals ($ $continue k src exp))
|
||||
($kargs names vals ($continue (rename k) src ,exp)))
|
||||
(($ $kargs names vals ($ $branch kf kt src op param args))
|
||||
($kargs names vals ($branch (rename kf) (rename kt) src op param args)))
|
||||
(($ $kargs names vals ($ $prompt k kh src escape? tag))
|
||||
($kargs names vals ($prompt (rename k) (rename kh) src escape? tag)))
|
||||
(($ $kreceive ($ $arity req () rest () #f) kbody)
|
||||
($kreceive req rest (rename kbody)))
|
||||
(($ $kclause arity kbody kalternate)
|
||||
;; Can only be a body continuation.
|
||||
($kclause ,arity (rename kbody) kalternate)))))
|
||||
pred-preds
|
||||
(intmap-remove out pred))
|
||||
(make-analysis effects
|
||||
clobbers
|
||||
(intmap-add (intmap-add preds label pred intset-remove)
|
||||
label pred-preds intset-union)
|
||||
avail
|
||||
truthy-labels)))))))
|
||||
|
||||
(define (prune-branch analysis pred succ)
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(make-analysis effects
|
||||
clobbers
|
||||
(intmap-add preds succ pred intset-remove)
|
||||
avail
|
||||
truthy-labels))))
|
||||
|
||||
(define (prune-successors analysis pred succs)
|
||||
(intset-fold (lambda (succ analysis)
|
||||
(prune-branch analysis pred succ))
|
||||
succs analysis))
|
||||
|
||||
(define (term-successors term)
|
||||
(match term
|
||||
(($ $continue k) (intset k))
|
||||
(($ $branch kf kt) (intset kf kt))
|
||||
(($ $prompt k kh) (intset k kh))
|
||||
(($ $throw) empty-intset)))
|
||||
|
||||
(define (eliminate-common-subexpressions-in-fun kfun conts out substs)
|
||||
(define equiv-set (make-hash-table))
|
||||
(define (true-idx idx) (ash idx 1))
|
||||
(define (false-idx idx) (1+ (ash idx 1)))
|
||||
|
@ -177,41 +257,6 @@ false. It could be that both true and false proofs are available."
|
|||
(($ $prompt) #f)
|
||||
(($ $throw) #f)))
|
||||
|
||||
(define (add-substs label defs out substs analysis)
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(match (trivial-intset (intmap-ref preds label))
|
||||
(#f substs)
|
||||
(pred
|
||||
(match (intmap-ref out pred)
|
||||
(($ $kargs _ _ ($ $continue _ _ ($ $values vals)))
|
||||
;; FIXME: Eliminate predecessor entirely, retargetting its
|
||||
;; predecessors.
|
||||
(fold (lambda (def var substs)
|
||||
(intmap-add substs def var))
|
||||
substs defs vals))
|
||||
(($ $kargs _ _ term)
|
||||
(match (compute-term-key term)
|
||||
(#f #f)
|
||||
(term-key
|
||||
(let ((fx (intmap-ref effects pred)))
|
||||
;; Add residualized definition to the equivalence set.
|
||||
;; Note that expressions that allocate a fresh object
|
||||
;; or change the current fluid environment can't be
|
||||
;; eliminated by CSE (though DCE might do it if the
|
||||
;; value proves to be unused, in the allocation case).
|
||||
(when (and (not (causes-effect? fx &allocation))
|
||||
(not (effect-clobbers? fx (&read-object &fluid))))
|
||||
(let ((equiv (hash-ref equiv-set term-key '())))
|
||||
(hash-set! equiv-set term-key (acons pred defs equiv)))))
|
||||
;; If the predecessor defines auxiliary definitions, as
|
||||
;; `cons' does for the results of `car' and `cdr', define
|
||||
;; those as well.
|
||||
(add-auxiliary-definitions! pred defs substs term-key)))
|
||||
substs)
|
||||
(_
|
||||
substs)))))))
|
||||
|
||||
(define (add-auxiliary-definitions! label defs substs term-key)
|
||||
(define (add-def! aux-key var)
|
||||
(let ((equiv (hash-ref equiv-set aux-key '())))
|
||||
|
@ -295,69 +340,129 @@ false. It could be that both true and false proofs are available."
|
|||
(($ $throw src op param args)
|
||||
($throw src op param ,(map subst-var args)))))
|
||||
|
||||
(define (visit-term label term substs analysis)
|
||||
(let* ((term (rename-uses term substs)))
|
||||
(define (residualize)
|
||||
(values term analysis))
|
||||
(define (eliminate k src vals)
|
||||
(values (build-term ($continue k src ($values vals))) analysis))
|
||||
(define (fold-branch true? kf kt src)
|
||||
(values (build-term ($continue (if true? kt kf) src ($values ())))
|
||||
(prune-branch analysis label (if true? kf kt))))
|
||||
|
||||
(match (compute-term-key term)
|
||||
(#f (residualize))
|
||||
(term-key
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(let ((avail (intmap-ref avail label)))
|
||||
(let lp ((candidates (hash-ref equiv-set term-key '())))
|
||||
(match candidates
|
||||
(()
|
||||
;; No available expression; residualize.
|
||||
(residualize))
|
||||
(((candidate . vars) . candidates)
|
||||
(cond
|
||||
((not (intset-ref avail candidate))
|
||||
;; This expression isn't available here; try
|
||||
;; the next one.
|
||||
(lp candidates))
|
||||
(else
|
||||
(match term
|
||||
(($ $continue k src)
|
||||
;; Yay, a match; eliminate the expression.
|
||||
(eliminate k src vars))
|
||||
(($ $branch kf kt src)
|
||||
(let* ((bool (intmap-ref truthy-labels label))
|
||||
(t (intset-ref bool (true-idx candidate)))
|
||||
(f (intset-ref bool (false-idx candidate))))
|
||||
(if (eqv? t f)
|
||||
;; Can't fold the branch; keep on
|
||||
;; looking for another candidate.
|
||||
(lp candidates)
|
||||
;; Nice, the branch folded.
|
||||
(fold-branch t kf kt src)))))))))))))))))
|
||||
|
||||
(define (visit-label label cont out substs analysis)
|
||||
(define (add cont)
|
||||
(intmap-add! out label cont))
|
||||
(match cont
|
||||
(($ $kargs names vars term)
|
||||
(let* ((substs (add-substs label vars out substs analysis))
|
||||
(term (rename-uses term substs)))
|
||||
(define (residualize)
|
||||
(add (build-cont ($kargs names vars ,term))))
|
||||
(define (eliminate k src vals)
|
||||
(add (build-cont ($kargs names vars
|
||||
($continue k src ($values vals))))))
|
||||
|
||||
(values
|
||||
(match (compute-term-key term)
|
||||
(#f (residualize))
|
||||
(term-key
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(let ((avail (intmap-ref avail label)))
|
||||
(let lp ((candidates (hash-ref equiv-set term-key '())))
|
||||
(match candidates
|
||||
(()
|
||||
;; No available expression; residualize.
|
||||
(residualize))
|
||||
(((candidate . vars) . candidates)
|
||||
(cond
|
||||
((not (intset-ref avail candidate))
|
||||
;; This expression isn't available here; try
|
||||
;; the next one.
|
||||
(lp candidates))
|
||||
(else
|
||||
(match term
|
||||
(($ $continue k src)
|
||||
;; Yay, a match; eliminate the expression.
|
||||
(eliminate k src vars))
|
||||
(($ $branch kf kt src)
|
||||
(let* ((bool (intmap-ref truthy-labels label))
|
||||
(t (intset-ref bool (true-idx candidate)))
|
||||
(f (intset-ref bool (false-idx candidate))))
|
||||
(if (eqv? t f)
|
||||
;; Can't fold the branch; keep on
|
||||
;; looking for another candidate.
|
||||
(lp candidates)
|
||||
;; Nice, the branch folded.
|
||||
(eliminate (if t kt kf) src '())))))))))))))))
|
||||
substs analysis)))
|
||||
(_ (values (add cont) substs analysis))))
|
||||
(define (visit-term* names vars out substs analysis)
|
||||
(call-with-values (lambda ()
|
||||
(visit-term label term substs analysis))
|
||||
(lambda (term analysis)
|
||||
(values (intmap-add! out label
|
||||
(build-cont ($kargs names vars ,term)))
|
||||
substs
|
||||
analysis))))
|
||||
(define (visit-term-normally)
|
||||
(visit-term* names vars out substs analysis))
|
||||
(match analysis
|
||||
(($ <analysis> effects clobbers preds avail truthy-labels)
|
||||
(let ((preds (intmap-ref preds label)))
|
||||
(cond
|
||||
((eq? preds empty-intset)
|
||||
;; Branch folding made this term unreachable. Prune from
|
||||
;; preds set.
|
||||
(values out substs
|
||||
(prune-successors analysis label (term-successors term))))
|
||||
((trivial-intset preds)
|
||||
=> (lambda (pred)
|
||||
(match (intmap-ref out pred)
|
||||
(($ $kargs names' vars' ($ $continue _ _ ($ $values vals)))
|
||||
;; Substitute dominating definitions, and try to elide the
|
||||
;; predecessor entirely.
|
||||
(let ((substs (fold (lambda (var val substs)
|
||||
(intmap-add substs var val))
|
||||
substs vars vals)))
|
||||
(match (elide-predecessor label pred out analysis)
|
||||
(#f
|
||||
;; Can't elide; predecessor must be target of
|
||||
;; backwards branch.
|
||||
(visit-term* names vars out substs analysis))
|
||||
((out . analysis)
|
||||
(visit-term* names' vars' out substs analysis)))))
|
||||
(($ $kargs _ _ term)
|
||||
(match (compute-term-key term)
|
||||
(#f #f)
|
||||
(term-key
|
||||
(let ((fx (intmap-ref effects pred)))
|
||||
;; Add residualized definition to the equivalence set.
|
||||
;; Note that expressions that allocate a fresh object
|
||||
;; or change the current fluid environment can't be
|
||||
;; eliminated by CSE (though DCE might do it if the
|
||||
;; value proves to be unused, in the allocation case).
|
||||
(when (and (not (causes-effect? fx &allocation))
|
||||
(not (effect-clobbers? fx (&read-object &fluid))))
|
||||
(let ((equiv (hash-ref equiv-set term-key '())))
|
||||
(hash-set! equiv-set term-key (acons pred vars equiv)))))
|
||||
;; If the predecessor defines auxiliary definitions, as
|
||||
;; `cons' does for the results of `car' and `cdr', define
|
||||
;; those as well.
|
||||
(add-auxiliary-definitions! pred vars substs term-key)))
|
||||
(visit-term-normally))
|
||||
(_
|
||||
(visit-term-normally)))))
|
||||
(else
|
||||
(visit-term-normally)))))))
|
||||
(_ (values (intmap-add! out label cont) substs analysis))))
|
||||
|
||||
;; Because of the renumber pass, the labels are numbered in reverse
|
||||
;; post-order, so the intmap-fold will visit definitions before
|
||||
;; uses.
|
||||
(let* ((substs empty-intmap)
|
||||
(effects (synthesize-definition-effects (compute-effects conts)))
|
||||
(let* ((effects (synthesize-definition-effects (compute-effects conts)))
|
||||
(clobbers (compute-clobber-map effects))
|
||||
(succs (compute-successors conts kfun))
|
||||
(preds (invert-graph succs))
|
||||
(avail (compute-available-expressions succs kfun clobbers))
|
||||
(truthy-labels (compute-truthy-expressions conts kfun)))
|
||||
(intmap-fold visit-label conts out substs
|
||||
(make-analysis effects clobbers preds avail truthy-labels))))
|
||||
(call-with-values
|
||||
(lambda ()
|
||||
(intmap-fold visit-label conts out substs
|
||||
(make-analysis effects clobbers preds avail truthy-labels)))
|
||||
(lambda (out substs analysis)
|
||||
(values out substs)))))
|
||||
|
||||
(define (fold-renumbered-functions f conts seed)
|
||||
(define (fold-renumbered-functions f conts . seeds)
|
||||
;; Precondition: CONTS has been renumbered, and therefore functions
|
||||
;; contained within it are topologically sorted, and the conts of each
|
||||
;; function's body are numbered sequentially after the function's
|
||||
|
@ -373,14 +478,16 @@ false. It could be that both true and false proofs are available."
|
|||
(cont
|
||||
(lp (1+ k) (intmap-add! body k cont))))))))
|
||||
|
||||
(let fold ((kfun 0) (seed seed))
|
||||
(let fold ((kfun 0) (seeds seeds))
|
||||
(match (next-function-body kfun)
|
||||
(#f seed)
|
||||
(#f (apply values seeds))
|
||||
(conts
|
||||
(fold (1+ (intmap-prev conts)) (f kfun conts seed))))))
|
||||
(call-with-values (lambda () (apply f kfun conts seeds))
|
||||
(lambda seeds
|
||||
(fold (1+ (intmap-prev conts)) seeds)))))))
|
||||
|
||||
(define (eliminate-common-subexpressions conts)
|
||||
(let ((conts (renumber conts 0)))
|
||||
(persistent-intmap
|
||||
(fold-renumbered-functions eliminate-common-subexpressions-in-fun
|
||||
conts empty-intmap))))
|
||||
conts empty-intmap empty-intmap))))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue