Skip to content

Commit 0380f3c

Browse files
authored
move JET.strongly_connected_components to LCU (#97)
1 parent 3194ce0 commit 0380f3c

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

src/utils.jl

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,119 @@ function pushall!(dest, src)
257257
end
258258
return dest
259259
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

Comments
 (0)