dominators.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. ;;; Ported from Scheme 48 1.9. See file COPYING for notices and license.
  2. ;;;
  3. ;;; Port Author: Andrew Whatson
  4. ;;;
  5. ;;; Original Authors: Richard Kelsey, Mark Reinhold
  6. ;;;
  7. ;;; scheme48-1.9.2/ps-compiler/util/dominators.scm
  8. ;;;
  9. ;;;;; Find immediate dominators in a directed graph
  10. ;;;;; Mark Reinhold (mbr@research.nj.nec.com)/3 February 1995
  11. ;;; Debugging code removed and everything reluctantly Scheme-ized by
  12. ;;; R. Kelsey, St. Valentine's Day, 1995
  13. ; This fast dominator code is based upon Lengauer and Tarjan, "A Fast
  14. ; Algorithm for Finding Dominators in a Flowgraph," ACM TOPLAS 1:1, pp.
  15. ; 121--141, July 1979. It runs in time $O(|E|\log|V|)$, where $|E|$ is the
  16. ; number of edges and $|V|$ is the number of vertices. A smaller time bound
  17. ; of $O(|E|\alpha(|E|,|V|))$, where $\alpha$ is the inverse of Ackerman's
  18. ; function, can be achieved with more complex versions of the internal link!
  19. ; and eval! procedures.
  20. ;
  21. ; The client provides a rooted, directed graph by passing a root node,
  22. ; successor and predecessor functions, and auxiliary procedures for accessing
  23. ; and setting a slot in each node. The dominator code creates a shadow of
  24. ; the client's graph using the vertex record type defined below. To keep
  25. ; things clear, the client's graph is considered to contain "nodes," while
  26. ; the shadow graph contains "vertices."
  27. (define-module (ps-compiler util dominators)
  28. #:use-module (srfi srfi-9)
  29. #:use-module (prescheme scheme48)
  30. #:use-module (ps-compiler util util)
  31. #:export (find-dominators!))
  32. (define-record-type :vertex
  33. (really-make-vertex node semi bucket ancestor debug)
  34. vertex?
  35. (node vertex-node) ;; Corresponding node in client's graph
  36. (semi vertex-semi ;; A number for this vertex, w, as follows:
  37. set-vertex-semi!) ;; After w is numbered, but before its semidominator
  38. ;; is computed: w's DFS number
  39. ;; After w's semidominator is computed:
  40. ;; the number of its semidominator
  41. (parent vertex-parent ;; Parent of this vertex in DFS spanning tree
  42. set-vertex-parent!)
  43. (pred vertex-pred ;; Parents
  44. set-vertex-pred!)
  45. (label vertex-label ;; Label in spanning forest, initially this vertex
  46. set-vertex-label!)
  47. (bucket vertex-bucket ;; List of vertices whose semidominator is this vertex
  48. set-vertex-bucket!)
  49. (dom vertex-dom ;; A vertex, as follows:
  50. set-vertex-dom!) ;; After step 3: If the semidominator of this
  51. ;; vertex, w, is its immediate dominator, then
  52. ;; this slot contains that vertex; otherwise,
  53. ;; this slot is a vertex v whose number is
  54. ;; smaller than w's and whose immediate dominator
  55. ;; is also w's immediate dominator
  56. ;; After step 4: The immediate dominator of this
  57. ;; vertex
  58. (ancestor vertex-ancestor ;; An ancestor of this vertex in the spanning forest
  59. set-vertex-ancestor!)
  60. (debug vertex-debug ;; Debug field ##
  61. set-vertex-debug!))
  62. (define (make-vertex node semi)
  63. (really-make-vertex node
  64. semi
  65. '() ;; bucket
  66. #f ;; ancestor
  67. #f)) ;; debug
  68. (define (push-vertex-bucket! inf elt)
  69. (set-vertex-bucket! inf (cons elt (vertex-bucket inf))))
  70. (define (find-dominators-quickly! root ;; root node
  71. succ ;; maps a node to its children
  72. pred ;; maps a node to its parents
  73. slot ;; result slot accessor
  74. set-slot!) ;; result slot setter
  75. ;; Compute the dominator tree of the given rooted, directed graph;
  76. ;; when done, the slot of each node will contain its immediate dominator.
  77. ;; Requires that each slot initially contain #f.
  78. (define (dfs root)
  79. (let ((n 0) (vertices '()))
  80. (let go ((node root) (parent #f))
  81. (let ((v (make-vertex node n)))
  82. (set-slot! node v)
  83. (set! n (+ n 1))
  84. (set-vertex-parent! v parent)
  85. (set-vertex-label! v v)
  86. (set! vertices (cons v vertices))
  87. (for-each (lambda (node)
  88. (if (not (slot node))
  89. (go node v)))
  90. (succ node))))
  91. (let ((vertex-map (list->vector (reverse! vertices))))
  92. (do ((i 0 (+ i 1)))
  93. ((= i (vector-length vertex-map)))
  94. (let ((v (vector-ref vertex-map i)))
  95. (set-vertex-pred! v (map slot (pred (vertex-node v))))))
  96. (values n vertex-map))))
  97. (define (compress! v)
  98. (let ((a (vertex-ancestor v)))
  99. (if (vertex-ancestor a)
  100. (begin
  101. (compress! a)
  102. (if (< (vertex-semi (vertex-label a))
  103. (vertex-semi (vertex-label v)))
  104. (set-vertex-label! v (vertex-label a)))
  105. (set-vertex-ancestor! v (vertex-ancestor (vertex-ancestor v)))))))
  106. (define (eval! v)
  107. (cond ((not (vertex-ancestor v))
  108. v)
  109. (else
  110. (compress! v)
  111. (vertex-label v))))
  112. (define (link! v w)
  113. (set-vertex-ancestor! w v))
  114. (receive (n vertex-map) (dfs root) ;; Step 1
  115. (do ((i (- n 1) (- i 1)))
  116. ((= i 0))
  117. (let ((w (vector-ref vertex-map i)))
  118. (for-each (lambda (v) ;; Step 2
  119. (let ((u (eval! v)))
  120. (if (< (vertex-semi u)
  121. (vertex-semi w))
  122. (set-vertex-semi! w
  123. (vertex-semi u)))))
  124. (vertex-pred w))
  125. (push-vertex-bucket! (vector-ref vertex-map (vertex-semi w)) w)
  126. (link! (vertex-parent w) w)
  127. (for-each (lambda (v) ;; Step 3
  128. ;; T&L delete v from the bucket list at this point,
  129. ;; but there is no reason to do so
  130. (let ((u (eval! v)))
  131. (set-vertex-dom! v
  132. (if (< (vertex-semi u)
  133. (vertex-semi v))
  134. u
  135. (vertex-parent w)))))
  136. (vertex-bucket (vertex-parent w)))))
  137. (do ((i 1 (+ i 1))) ;; Step 4
  138. ((= i n))
  139. (let ((w (vector-ref vertex-map i)))
  140. (if (not (eq? (vertex-dom w)
  141. (vector-ref vertex-map (vertex-semi w))))
  142. (set-vertex-dom! w
  143. (vertex-dom (vertex-dom w))))))
  144. (set-vertex-dom! (slot root) #f)
  145. ;;(show-nodes root succ slot) ;; ## debug
  146. (do ((i 0 (+ i 1))) ;; Set dominator pointers
  147. ((= i n))
  148. (let ((w (vector-ref vertex-map i)))
  149. (let ((d (vertex-dom w)))
  150. (set-slot! (vertex-node w) (if d (vertex-node d) #f)))))))
  151. ;;; The fast dominator algorithm is difficult to prove correct, so the
  152. ;;; following slow code is provided in order to check its results. The slow
  153. ;;; algorithm, which runs in time $O(|E||V|)$, is adapted from Aho and Ullman,
  154. ;;; _The Theory of Parsing, Translation, and Compiling_, Prentice-Hall, 1973,
  155. ;;; p. 916.
  156. (define (find-dominators-slowly! root succ pred slot set-slot!)
  157. (define vertex-succ vertex-pred)
  158. (define set-vertex-succ! set-vertex-pred!)
  159. (define vertex-mark vertex-ancestor)
  160. (define set-vertex-mark! set-vertex-ancestor!)
  161. (define (dfs root)
  162. (let ((n 0) (vertices '()))
  163. (let go ((node root) (parent #f))
  164. (let ((v (make-vertex node n)))
  165. (set-slot! node v)
  166. (set! n (+ n 1))
  167. (set! vertices (cons v vertices))
  168. (set-vertex-parent! v #f)
  169. (set-vertex-label! v #f)
  170. (for-each (lambda (node)
  171. (if (not (slot node))
  172. (go node v)))
  173. (succ node))))
  174. (for-each (lambda (v)
  175. (set-vertex-succ! v (map slot (succ (vertex-node v)))))
  176. vertices)
  177. (values n (reverse! vertices))))
  178. (receive (n vertices) (dfs root)
  179. (define (inaccessible v)
  180. ;; Determine set of vertices that are inaccessible if vertex v is ignored
  181. (set-vertex-mark! v #t)
  182. (let go ((w (car vertices)))
  183. (set-vertex-mark! w #t)
  184. (for-each (lambda (u)
  185. (if (not (vertex-mark u))
  186. (go u)))
  187. (vertex-succ w)))
  188. (filter (lambda (w)
  189. (cond
  190. ((vertex-mark w)
  191. (set-vertex-mark! w #f)
  192. #f)
  193. (else #t)))
  194. vertices))
  195. (for-each (lambda (v) (set-vertex-dom! v (car vertices)))
  196. (cdr vertices))
  197. (for-each (lambda (v)
  198. (let ((dominated-by-v (inaccessible v)))
  199. (for-each (lambda (w)
  200. (if (eq? (vertex-dom w) (vertex-dom v))
  201. (set-vertex-dom! w v)))
  202. dominated-by-v)))
  203. (cdr vertices))
  204. (set-vertex-dom! (car vertices) #f)
  205. ;;(show-nodes root succ slot) ;; ## debug
  206. (for-each (lambda (v)
  207. (set-slot! (vertex-node v)
  208. (let ((d (vertex-dom v)))
  209. (if d (vertex-node d) #f))))
  210. vertices)))
  211. (define (time-thunk thunk) (thunk))
  212. (define (find-and-check-dominators! root succ pred slot set-slot!)
  213. (let ((set-fast-slot! (lambda (x v) (set-car! (slot x) v)))
  214. (fast-slot (lambda (x) (car (slot x))))
  215. (set-slow-slot! (lambda (x v) (set-cdr! (slot x) v)))
  216. (slow-slot (lambda (x) (cdr (slot x)))))
  217. (let go ((node root))
  218. (set-slot! node (cons #f #f))
  219. (for-each (lambda (node)
  220. (if (not (slot node))
  221. (go node)))
  222. (succ node)))
  223. (let ((fast (time-thunk
  224. (lambda ()
  225. (find-dominators-quickly!
  226. root succ pred fast-slot set-fast-slot!))))
  227. (slow (time-thunk (lambda ()
  228. (find-dominators-slowly!
  229. root succ pred slow-slot set-slow-slot!)))))
  230. ;; (format #t "** find-and-check-dominators!: fast ~a, slow ~a~%" fast slow) ;; ##
  231. (let go ((node root))
  232. (if (not (eq? (fast-slot node) (slow-slot node)))
  233. (bug "Dominator algorithm error"))
  234. (set-slot! node (fast-slot node))
  235. (for-each (lambda (node)
  236. (if (pair? (slot node)) ;; ## Assumes nodes are not pairs
  237. (go node)))
  238. (succ node))))))
  239. (define *check?* #t)
  240. (define (find-dominators! . args)
  241. (apply (if *check?*
  242. find-and-check-dominators!
  243. find-dominators-quickly!)
  244. args))