; Adapted for Chicken Scheme by Ivan Raikov. (module graph-dominators (graph-find-dominators-quickly! graph-find-dominators-slowly!) (import scheme chicken data-structures) (require-extension srfi-1 matchable ) ; Copyright (c) 1993-1999 by Richard Kelsey. See file COPYING. ; Find immediate dominators in a directed graph ; Mark Reinhold (mbr@research.nj.nec.com)/3 February 1995 ; Debugging code removed and everything reluctantly Scheme-ized by ; R. Kelsey, St. Valentine's Day, 1995 ; In a CFG, block M dominates block N if every path from the entry ; that reaches block N has to pass through block M. The entry block ; dominates all blocks. ; ; Block M immediately dominates block N if M dominates N, and there is ; no intervening block P such that M dominates P and P dominates N. In ; other words, M is the last dominator on any path from entry to ; N. Each block has a unique immediate dominator, if it has any at ; all. ; This fast dominator code is based upon Lengauer and Tarjan, "A Fast ; Algorithm for Finding Dominators in a Flowgraph," ACM TOPLAS 1:1, pp. ; 121--141, July 1979. It runs in time $O(|E|\log|V|)$, where $|E|$ is the ; number of edges and $|V|$ is the number of vertices. A smaller time bound ; of $O(|E|\alpha(|E|,|V|))$, where $\alpha$ is the inverse of Ackerman's ; function, can be achieved with more complex versions of the internal link! ; and eval! procedures. ; ; The client provides a rooted, directed graph by passing a root node, ; successor and predecessor functions, and auxiliary procedures for accessing ; and setting a slot in each node. The dominator code creates a shadow of ; the client's graph using the vertex record type defined below. To keep ; things clear, the client's graph is considered to contain "nodes," while ; the shadow graph contains "vertices." (define-record-type vertex (really-make-vertex node semi bucket ancestor debug) vertex? (node vertex-node) ; Corresponding node in client's graph (semi vertex-semi ; A number for this vertex, w, as follows: set-vertex-semi!) ; After w is numbered, but before its semidominator ; is computed: w's DFS number ; After w's semidominator is computed: ; the number of its semidominator (parent vertex-parent ; Parent of this vertex in DFS spanning tree set-vertex-parent!) (pred vertex-pred ; Parents set-vertex-pred!) (label vertex-label ; Label in spanning forest, initially this vertex set-vertex-label!) (bucket vertex-bucket ; List of vertices whose semidominator is this vertex set-vertex-bucket!) (dom vertex-dom ; A vertex, as follows: set-vertex-dom!) ; After step 3: If the semidominator of this ; vertex, w, is its immediate dominator, then ; this slot contains that vertex; otherwise, ; this slot is a vertex v whose number is ; smaller than w's and whose immediate dominator ; is also w's immediate dominator ; After step 4: The immediate dominator of this ; vertex (ancestor vertex-ancestor ; An ancestor of this vertex in the spanning forest set-vertex-ancestor!) (debug vertex-debug ; Debug field ## set-vertex-debug!)) (define (make-vertex node semi) (really-make-vertex node semi '() ; bucket #f ; ancestor #f)) ; debug (define (push-vertex-bucket! inf elt) (set-vertex-bucket! inf (cons elt (vertex-bucket inf)))) (define (graph-find-dominators-quickly! g . rest) (let-optionals rest ((slot (lambda (n) (car ((g 'node-info) n)))) (set-slot! (lambda (n v) (set-car! ((g 'node-info) n) v)))) (let ((roots ((g 'roots))) (succ (g 'succ)) (pred (g 'pred))) (match roots ((root) (find-dominators-quickly! root succ pred slot set-slot!)) (else (error 'graph-find-dominators-quickly! "given graph must have exactly one root")))))) (define (find-dominators-quickly! root ; root node succ ; maps a node to its children pred ; maps a node to its parents slot ; result slot accessor set-slot!) ; result slot setter ;; Compute the dominator tree of the given rooted, directed graph; ;; when done, the slot of each node will contain its immediate dominator. ;; Requires that each slot initially contain #f. (define (dfs root) (let ((n 0) (vertices '())) (let go ((node root) (parent #f)) (let ((v (make-vertex node n))) (set-slot! node v) (set! n (+ n 1)) (set-vertex-parent! v parent) (set-vertex-label! v v) (set! vertices (cons v vertices)) (for-each (lambda (node) (if (not (slot node)) (go node v))) (succ node)))) (let ((vertex-map (list->vector (reverse! vertices)))) (do ((i 0 (+ i 1))) ((= i (vector-length vertex-map))) (let ((v (vector-ref vertex-map i))) (set-vertex-pred! v (map slot (pred (vertex-node v)))))) (values n vertex-map)))) (define (compress! v) (let ((a (vertex-ancestor v))) (if (vertex-ancestor a) (begin (compress! a) (if (< (vertex-semi (vertex-label a)) (vertex-semi (vertex-label v))) (set-vertex-label! v (vertex-label a))) (set-vertex-ancestor! v (vertex-ancestor (vertex-ancestor v))))))) (define (eval! v) (cond ((not (vertex-ancestor v)) v) (else (compress! v) (vertex-label v)))) (define (link! v w) (set-vertex-ancestor! w v)) (receive (n vertex-map) (dfs root) ; Step 1 (do ((i (- n 1) (- i 1))) ((= i 0)) (let ((w (vector-ref vertex-map i))) (for-each (lambda (v) ; Step 2 (let ((u (eval! v))) (if (< (vertex-semi u) (vertex-semi w)) (set-vertex-semi! w (vertex-semi u))))) (vertex-pred w)) (push-vertex-bucket! (vector-ref vertex-map (vertex-semi w)) w) (link! (vertex-parent w) w) (for-each (lambda (v) ; Step 3 ;; T&L delete v from the bucket list at this point, ;; but there is no reason to do so (let ((u (eval! v))) (set-vertex-dom! v (if (< (vertex-semi u) (vertex-semi v)) u (vertex-parent w))))) (vertex-bucket (vertex-parent w))))) (do ((i 1 (+ i 1))) ; Step 4 ((= i n)) (let ((w (vector-ref vertex-map i))) (if (not (eq? (vertex-dom w) (vector-ref vertex-map (vertex-semi w)))) (set-vertex-dom! w (vertex-dom (vertex-dom w)))))) (set-vertex-dom! (slot root) #f) ;(show-nodes root succ slot) ; ## debug (do ((i 0 (+ i 1))) ; Set dominator pointers ((= i n)) (let ((w (vector-ref vertex-map i))) (let ((d (vertex-dom w))) (set-slot! (vertex-node w) (if d (vertex-node d) #f))))))) (define (graph-find-dominators-slowly! g . rest) (let-optionals rest ((slot (lambda (n) (car ((g 'node-info) n)))) (set-slot! (lambda (n v) (set-car! ((g 'node-info-set!) n) v)))) (let ((roots ((g 'roots))) (succ (g 'succ)) (pred (g 'pred))) (match roots ((root) (find-dominators-slowly! root succ pred slot set-slot!)) (else (error 'graph-find-dominators-slowly! "given graph must have exactly one root")))))) ;;; The fast dominator algorithm is difficult to prove correct, so the ;;; following slow code is provided in order to check its results. The slow ;;; algorithm, which runs in time $O(|E||V|)$, is adapted from Aho and Ullman, ;;; _The Theory of Parsing, Translation, and Compiling_, Prentice-Hall, 1973, ;;; p. 916. (define (find-dominators-slowly! root succ pred slot set-slot!) (define vertex-succ vertex-pred) (define set-vertex-succ! set-vertex-pred!) (define vertex-mark vertex-ancestor) (define set-vertex-mark! set-vertex-ancestor!) (define (dfs root) (let ((n 0) (vertices '())) (let go ((node root) (parent #f)) (let ((v (make-vertex node n))) (set-slot! node v) (set! n (+ n 1)) (set! vertices (cons v vertices)) (set-vertex-parent! v #f) (set-vertex-label! v #f) (for-each (lambda (node) (if (not (slot node)) (go node v))) (succ node)))) (for-each (lambda (v) (set-vertex-succ! v (map slot (succ (vertex-node v))))) vertices) (values n (reverse! vertices)))) (receive (n vertices) (dfs root) (define (inaccessible v) ;; Determine set of vertices that are inaccessible if vertex v is ignored (set-vertex-mark! v #t) (let go ((w (car vertices))) (set-vertex-mark! w #t) (for-each (lambda (u) (if (not (vertex-mark u)) (go u))) (vertex-succ w))) (filter (lambda (w) (cond ((vertex-mark w) (set-vertex-mark! w #f) #f) (else #t))) vertices)) (for-each (lambda (v) (set-vertex-dom! v (car vertices))) (cdr vertices)) (for-each (lambda (v) (let ((dominated-by-v (inaccessible v))) (for-each (lambda (w) (if (eq? (vertex-dom w) (vertex-dom v)) (set-vertex-dom! w v))) dominated-by-v))) (cdr vertices)) (set-vertex-dom! (car vertices) #f) ;(show-nodes root succ slot) ; ## debug (for-each (lambda (v) (set-slot! (vertex-node v) (let ((d (vertex-dom v))) (if d (vertex-node d) #f)))) vertices))) )