File: loop-structure.sml

package info (click to toggle)
mlton 20210117%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 58,464 kB
  • sloc: ansic: 27,682; sh: 4,455; asm: 3,569; lisp: 2,879; makefile: 2,347; perl: 1,169; python: 191; pascal: 68; javascript: 7
file content (217 lines) | stat: -rw-r--r-- 7,555 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
(*
 * This module is responsible for locating loop structures (intervals).
 * All loops have only one single entry (via the header) but
 * potentially multiple exits, i.e. the header dominates all nodes.
 * Basically this is Tarjan's algorithm.  
 *
 * The old version is broken as reported by William Chen.
 * This is a rewrite.
 *)

functor LoopStructure (structure GraphImpl : GRAPH_IMPLEMENTATION
                       structure Dom       : DOMINATOR_TREE)
    : LOOP_STRUCTURE =
struct
 
   structure G   = Graph
   structure GI  = GraphImpl
   structure Dom = Dom
   structure A   = Array
   structure U   = URef

   datatype ('n,'e,'g) loop = 
      LOOP of { nesting    : int,
                header     : G.node_id,
                loop_nodes : G.node_id list,
                backedges  : 'e G.edge list,
                exits      : 'e G.edge list
              }

   datatype ('n,'e,'g) loop_info = 
       INFO of { dom : ('n,'e,'g) Dom.dominator_tree }

   type ('n,'e,'g) loop_structure = 
         (('n,'e,'g) loop, unit, ('n,'e,'g) loop_info) Graph.graph 

   fun dom(G.GRAPH{graph_info=INFO{dom,...},...}) = dom

   fun loop_structure DOM = 
   let
       val info               = INFO{ dom = DOM }
       val G.GRAPH cfg        = Dom.cfg DOM
       val G.GRAPH dom        = DOM
       val N                  = #capacity dom ()
       val dominates          = Dom.dominates DOM
       val LS as G.GRAPH ls   = GI.graph ("Loop structure",info,N) 
       val ENTRY              = case #entries cfg () of
   				   [ENTRY] => ENTRY
			        | _ => raise Graph.NotSingleEntry

       (* mapping from node id -> header *)
       val headers = A.array(N, ~1)

       (* mapping from header -> previous header in the loop *)
       val lastHeaders = A.array(N, ~1)

       (* mark all visited nodes during construction *)
       val visited = A.array(N, ~1)

       (* mapping from nodes id -> collapsed header during construction *)
       val P       = A.tabulate(N, U.uRef)

       (* walk the dominator tree and return a list of loops *)
       fun walk (X, loops) =
       let
           (* Look for backedges *)
           val backedges = List.filter 
               (fn (Y, X, _) => dominates(X, Y)) (#in_edges cfg X)
           (* X is a header iff it has backedges or X is the ENTRY *)
           val is_header = case backedges of [] => X = ENTRY | _ => true

           (* Walk the dominator tree first *)
           val loops = List.foldr walk loops (#succ dom X)
       in 
           (* If X is a header node then collaspe all the nodes within
            * the loop into the header.  The entry node has to be
            * treated specially, unfortunately.
            *)
           if is_header then
              let val L = mark(X, X, [])
                  val L = if X = ENTRY then find_entry_loop_nodes [] else L
                  val () = collapse(X, L)
                  val exits = find_exits(L, [])
              in  (* Create a new loop node *)
                  (X, backedges, L, exits)::loops
              end
           else
              loops
       end


          (* mark all the nodes that are within the loop identified
           * by the header.  Return a list of loop nodes.
           *)
       and mark(X, header, L) =
          if A.sub(visited, X) <> header then
          let
              (* mark X as visited *)
              val _ = A.update(visited, X, header) 

              (* header of X *)
              val H_X = A.sub(headers, X)

              val L = if H_X = ~1 then (* X has no header yet *)
                          X::L
                      else if H_X = X andalso A.sub(lastHeaders, X) = ~1 then
                          (* Add loop edge *)
                          (A.update(lastHeaders, X, header);
                           #add_edge ls (header, X, ());
                           L
                          )
                      else L
          in  List.foldr (fn ((Y, _, _), L) => 
                let val Y = U.!! (A.sub(P, Y))
                in  if dominates(header, Y) then mark(Y, header, L) else L
                end) L (#in_edges cfg X)
          end
          else L

          (* collapse all nodes in L to the header H *)
       and collapse(H, L) = 
           let val h = A.sub(P, H)
           in  List.app (fn X => 
                  (U.link (A.sub(P, X), h);
                   if A.sub(headers, X) = ~1 then
                      A.update(headers, X, H)
                   else ())) L
           end

          (* find all nodes that are not part of any loops *)
       and find_entry_loop_nodes L =
           List.foldr (fn ((X, _), L) => 
                 if A.sub(headers, X) = ~1 then
                     X::L
                 else if X <> ENTRY andalso 
                      A.sub(headers, X) = X andalso
                      A.sub(lastHeaders, X) = ~1 then
                      (#add_edge ls (ENTRY, X, ());
                       A.update(lastHeaders, X, ENTRY);
                       L
                      )
                 else 
                     L
                 ) L (#nodes cfg ())


           (* find all edges that can exit from the loop H *)
       and find_exits([],exits) = exits
         | find_exits(X::Xs,exits) =
           let fun f((e as (X,Y,_))::es,exits) =
                   if A.sub(headers,Y) = ~1 
                   then f(es,e::exits) 
                   else f(es,exits)
                 | f([], exits) = exits
           in  find_exits(Xs, f(#out_edges cfg X, exits))
           end

       (* walk tree and create edges *)
       val loops = walk (ENTRY, [])

       (* create nodes *)
       val () = List.app (fn (H, backedges, loop_nodes, exits) =>
             let val last = A.sub(lastHeaders, H)
                 val nesting = if last = ~1 then 0 
                               else 
                                  let val LOOP{nesting, ...} = 
                                          #node_info ls last
                                  in  nesting+1 end
             in  #add_node ls (H, LOOP{nesting    = nesting,
                                       header     = H,
                                       backedges  = backedges,
                                       loop_nodes = loop_nodes,
                                       exits      = exits})
             end) loops
   in
       LS
   end

   fun nesting_level(G.GRAPH L) = let
     val INFO{dom=G.GRAPH dom,...} = #graph_info L
     val N = #capacity dom ()
     val levels  = A.array(N,0)
     fun tabulate(_,LOOP{nesting,header,loop_nodes,...}) =
       (A.update(levels,header,nesting);
	app (fn i => A.update(levels,i,nesting)) loop_nodes)
   in
     #forall_nodes L tabulate;    levels
   end

   fun header(G.GRAPH L) = let
     val INFO{dom=G.GRAPH dom,...} = #graph_info L
     val N = #capacity dom ()
     val headers = A.array(N,0)
     fun tabulate(_,LOOP{header,loop_nodes,...}) =
       (A.update(headers,header,header);
	app (fn i => A.update(headers,i,header)) loop_nodes)
   in  
     #forall_nodes L tabulate;   headers
   end

   fun entryEdges(Loop as G.GRAPH L) = let
     val dom = dom Loop
     val G.GRAPH cfg = Dom.cfg dom
     val dominates = Dom.dominates dom
     fun entryEdges(header) = 
       if #has_node L header then 
	 List.filter (fn (i,j,_) => not(dominates(j,i)))
		     (#in_edges cfg header)
       else []
   in  entryEdges
   end

   fun isBackEdge(Loop as G.GRAPH L) = 
   let val dom = Dom.dominates(dom Loop)
   in  fn (v,w) => #has_node L w andalso dom(w,v)
   end
end