@@ -257,3 +257,119 @@ function pushall!(dest, src)
257
257
end
258
258
return dest
259
259
end
260
+
261
+ # computes strongly connected components of a control flow graph `cfg`
262
+ # NOTE adapted from https://github.com/JuliaGraphs/Graphs.jl/blob/5878e7be4d68b2a1c179d1367aea670db115ebb5/src/connectivity.jl#L265-L357
263
+ # since to load an entire Graphs.jl is a bit cost-ineffective in terms of a trade-off of latency vs. maintainability
264
+ function strongly_connected_components (g:: Core.Compiler.CFG )
265
+ T = Int
266
+ zero_t = zero (T)
267
+ one_t = one (T)
268
+ nvg = nv (g)
269
+ count = one_t
270
+
271
+ index = zeros (T, nvg) # first time in which vertex is discovered
272
+ stack = Vector {T} () # stores vertices which have been discovered and not yet assigned to any component
273
+ onstack = zeros (Bool, nvg) # false if a vertex is waiting in the stack to receive a component assignment
274
+ lowlink = zeros (T, nvg) # lowest index vertex that it can reach through back edge (index array not vertex id number)
275
+ parents = zeros (T, nvg) # parent of every vertex in dfs
276
+ components = Vector {Vector{T}} () # maintains a list of scc (order is not guaranteed in API)
277
+
278
+ dfs_stack = Vector {T} ()
279
+
280
+ @inbounds for s in vertices (g)
281
+ if index[s] == zero_t
282
+ index[s] = count
283
+ lowlink[s] = count
284
+ onstack[s] = true
285
+ parents[s] = s
286
+ push! (stack, s)
287
+ count = count + one_t
288
+
289
+ # start dfs from 's'
290
+ push! (dfs_stack, s)
291
+
292
+ while ! isempty (dfs_stack)
293
+ v = dfs_stack[end ] # end is the most recently added item
294
+ u = zero_t
295
+ @inbounds for v_neighbor in outneighbors (g, v)
296
+ if index[v_neighbor] == zero_t
297
+ # unvisited neighbor found
298
+ u = v_neighbor
299
+ break
300
+ # GOTO A push u onto DFS stack and continue DFS
301
+ elseif onstack[v_neighbor]
302
+ # we have already seen n, but can update the lowlink of v
303
+ # which has the effect of possibly keeping v on the stack until n is ready to pop.
304
+ # update lowest index 'v' can reach through out neighbors
305
+ lowlink[v] = min (lowlink[v], index[v_neighbor])
306
+ end
307
+ end
308
+ if u == zero_t
309
+ # All out neighbors already visited or no out neighbors
310
+ # we have fully explored the DFS tree from v.
311
+ # time to start popping.
312
+ popped = pop! (dfs_stack)
313
+ lowlink[parents[popped]] = min (
314
+ lowlink[parents[popped]], lowlink[popped]
315
+ )
316
+
317
+ if index[v] == lowlink[v]
318
+ # found a cycle in a completed dfs tree.
319
+ component = Vector {T} ()
320
+
321
+ while ! isempty (stack) # break when popped == v
322
+ # drain stack until we see v.
323
+ # everything on the stack until we see v is in the SCC rooted at v.
324
+ popped = pop! (stack)
325
+ push! (component, popped)
326
+ onstack[popped] = false
327
+ # popped has been assigned a component, so we will never see it again.
328
+ if popped == v
329
+ # we have drained the stack of an entire component.
330
+ break
331
+ end
332
+ end
333
+
334
+ reverse! (component)
335
+ push! (components, component)
336
+ end
337
+
338
+ else # LABEL A
339
+ # add unvisited neighbor to dfs
340
+ index[u] = count
341
+ lowlink[u] = count
342
+ onstack[u] = true
343
+ parents[u] = v
344
+ count = count + one_t
345
+
346
+ push! (stack, u)
347
+ push! (dfs_stack, u)
348
+ # next iteration of while loop will expand the DFS tree from u.
349
+ end
350
+ end
351
+ end
352
+ end
353
+
354
+ # # assert with the original implementation
355
+ # oracle_components = oracle_scc(cfg_to_sdg(g))
356
+ # @assert Set(Set.(components)) == Set(Set.(oracle_components))
357
+
358
+ return components
359
+ end
360
+
361
+ # compatibility with Graphs.jl interfaces
362
+ @inline nv (cfg:: Core.Compiler.CFG ) = length (cfg. blocks)
363
+ @inline vertices (cfg:: Core.Compiler.CFG ) = 1 : nv (cfg)
364
+ @inline outneighbors (cfg:: Core.Compiler.CFG , v) = cfg. blocks[v]. succs
365
+
366
+ # using Graphs: SimpleDiGraph, add_edge!, strongly_connected_components as oracle_scc
367
+ # function cfg_to_sdg(cfg::Core.Compiler.CFG)
368
+ # g = SimpleDiGraph(length(cfg.blocks))
369
+ # for (v, block) in enumerate(cfg.blocks)
370
+ # for succ in block.succs
371
+ # add_edge!(g, v, succ)
372
+ # end
373
+ # end
374
+ # return g
375
+ # end
0 commit comments