Skip to content

Commit a1d2a28

Browse files
committed
Rewrite
1 parent e5db600 commit a1d2a28

File tree

1 file changed

+87
-51
lines changed

1 file changed

+87
-51
lines changed

src/codeedges.jl

Lines changed: 87 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,6 @@ function lines_required!(isrequired::AbstractVector{Union{Bool,Symbol}}, objs, s
640640
iter = 0
641641
while changed
642642
changed = false
643-
@show iter
644-
print_with_code(stdout, src, isrequired)
645-
println()
646643

647644
# Handle ssa predecessors
648645
changed |= add_ssa_preds!(isrequired, src, edges, norequire)
@@ -651,7 +648,6 @@ function lines_required!(isrequired::AbstractVector{Union{Bool,Symbol}}, objs, s
651648
changed |= add_named_dependencies!(isrequired, edges, objs, norequire)
652649

653650
# Add control-flow
654-
changed |= add_loops!(isrequired, cfg, domtree, postdomtree)
655651
changed |= add_control_flow!(isrequired, src, cfg, domtree, postdomtree)
656652

657653
# So far, everything is generic graph traversal. Now we add some domain-specific information
@@ -730,85 +726,125 @@ end
730726

731727
## Add control-flow
732728

733-
# Mark loops that contain evaluated statements
734-
function add_loops!(isrequired, cfg, domtree, postdomtree)
735-
changed = false
736-
for (ibb, bb) in enumerate(cfg.blocks)
737-
for ibbp in bb.preds
738-
# Is there a backwards-pointing predecessor, and if so are there any required statements between the two?
739-
ibbp > ibb || continue # not a loop-block predecessor
740-
if postdominates(postdomtree, ibb, ibbp)
741-
r = rng(cfg.blocks[ibbp])
742-
if isrequired[r[end]] != true
743-
isrequired[r[end]] = true
744-
changed = true
745-
end
746-
end
729+
iscf(stmt) = isa(stmt, Core.GotoNode) || isa(stmt, Core.GotoIfNot) || isa(stmt, Core.ReturnNode)
730+
731+
"""
732+
ispredecessor(blocks, i, j)
733+
734+
Determine whether block `i` is a predecessor of block `j` in the control-flow graph `blocks`.
735+
"""
736+
function ispredecessor(blocks, i, j, cache=Set{Int}())
737+
for p in blocks[j].preds # avoid putting `j` in the cache unless it loops back
738+
getpreds!(cache, blocks, p)
739+
end
740+
return i cache
741+
end
742+
function getpreds!(cache, blocks, j)
743+
if j cache
744+
return cache
745+
end
746+
push!(cache, j)
747+
for p in blocks[j].preds
748+
getpreds!(cache, blocks, p)
749+
end
750+
return cache
751+
end
752+
753+
function block_internals_needed(isrequired, src, r)
754+
needed = false
755+
for i in r
756+
if isrequired[i] == true
757+
iscf(src.code[i]) && continue
758+
needed = true
759+
break
747760
end
748761
end
749-
return changed
762+
return needed
750763
end
751764

752765
function add_control_flow!(isrequired, src, cfg, domtree, postdomtree)
753766
changed, _changed = false, true
754767
blocks = cfg.blocks
755-
nblocks = length(blocks)
768+
needed = falses(length(blocks))
769+
cache = Set{Int}()
756770
while _changed
757771
_changed = false
758772
for (ibb, bb) in enumerate(blocks)
759773
r = rng(bb)
760-
if any(==(true), view(isrequired, r))
761-
# Walk up the dominators
774+
if block_internals_needed(isrequired, src, r)
775+
needed[ibb] = true
776+
# Check control flow that's needed to reach this block by walking up the dominators
762777
jbb = ibb
763778
while jbb != 1
764-
jdbb = domtree.idoms_bb[jbb]
779+
jdbb = domtree.idoms_bb[jbb] # immediate dominator of jbb
765780
dbb = blocks[jdbb]
766-
# Check the successors; if jbb doesn't post-dominate, mark the last statement
767-
for s in dbb.succs
768-
if !postdominates(postdomtree, jbb, s)
769-
idxlast = rng(dbb)[end]
770-
if isrequired[idxlast] != true
771-
println("add 1: ", idxlast)
772-
_changed = true
773-
isrequired[idxlast] = true
781+
idxlast = rng(dbb)[end]
782+
if iscf(src.code[idxlast])
783+
# Check the idom's successors; if jbb doesn't post-dominate, mark the last statement
784+
for s in dbb.succs
785+
if !postdominates(postdomtree, jbb, s)
786+
if isrequired[idxlast] != true
787+
_changed = true
788+
isrequired[idxlast] = true
789+
break
790+
end
774791
end
775-
break
776792
end
777793
end
778794
jbb = jdbb
779795
end
780-
# Walk down the post-dominators, including self
796+
# Walk down the post-dominators, starting with self
781797
jbb = ibb
782-
while jbb != 0 && jbb < nblocks
783-
pdbb = blocks[jbb]
784-
# Check if the exit of this block is a GotoNode or `return`
785-
if length(pdbb.succs) < 2
798+
while jbb != 0
799+
empty!(cache)
800+
if ispredecessor(blocks, jbb, ibb, cache) # is post-dominator jbb also a predecessor of ibb? If so we have a loop.
801+
pdbb = blocks[jbb]
786802
idxlast = rng(pdbb)[end]
787803
stmt = src.code[idxlast]
788-
if isa(stmt, GotoNode) || isa(stmt, Core.ReturnNode)
789-
if isrequired[idxlast] == false
790-
println("add 2: ", idxlast)
804+
if iscf(stmt)
805+
if isrequired[idxlast] != true
791806
_changed = true
792-
isrequired[idxlast] = :exit
807+
if isa(stmt, Core.ReturnNode) && isrequired[idxlast] != :exit
808+
isrequired[idxlast] = :exit
809+
else
810+
isrequired[idxlast] = true
811+
if isa(stmt, Core.GotoIfNot) && idxlast < length(isrequired) && isrequired[idxlast+1] != true && iscf(src.code[idxlast+1])
812+
isrequired[idxlast+1] = true
813+
end
814+
end
793815
end
794816
end
795817
end
796818
jbb = postdomtree.idoms_bb[jbb]
797819
end
798-
elseif length(r) == 1
799-
# pdbb = blocks[ibb]
800-
# if length(pdbb.succs) < 2 && isa(src.code[r[1]], GotoNode)
801-
# idxlast = r[end]
802-
# if isrequired[idxlast] == false
803-
# println("add 3: ", idxlast)
804-
# _changed = true
805-
# isrequired[idxlast] = true
806-
# end
807-
# end
808820
end
809821
end
810822
changed |= _changed
811823
end
824+
# Now handle "exclusions": in code that would fall through during selective evaluation, find a post-dominator between the two
825+
# that is marked, or mark the end block
826+
marked = findall(needed)
827+
for k in Iterators.drop(eachindex(marked), 1)
828+
ibb, jbb = marked[k-1], marked[k]
829+
ok = false
830+
ipbb = ibb
831+
while ipbb < jbb
832+
ipbb = postdomtree.idoms_bb[ipbb]
833+
ipbb == 0 && break
834+
idxlast = rng(blocks[ipbb])[end]
835+
if isrequired[idxlast] != false
836+
ok = true
837+
break
838+
end
839+
end
840+
if !ok
841+
idxlast = rng(blocks[ibb])[end]
842+
if isrequired[idxlast] != true
843+
isrequired[idxlast] = true
844+
changed = true
845+
end
846+
end
847+
end
812848
return changed
813849
end
814850

0 commit comments

Comments
 (0)