Skip to content

Commit

Permalink
Make domtree construction take Vector{BasicBlock} as input instead …
Browse files Browse the repository at this point in the history
…of CFG

This is in anticipation of domtrees being added to CFGs.
  • Loading branch information
yhls authored and vchuravy committed Oct 14, 2020
1 parent c285855 commit 4ef7a4e
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 51 deletions.
40 changes: 20 additions & 20 deletions base/compiler/ssair/domtree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function DFS!(D::DFSTree, blocks::Vector{BasicBlock})
to_visit[end] = (current_node_bb, parent_pre, true)

# Push children to the stack
for succ_bb in cfg.blocks[current_node_bb].succs
for succ_bb in blocks[current_node_bb].succs
push!(to_visit, (succ_bb, pre_num, false))
end

Expand Down Expand Up @@ -200,11 +200,11 @@ function DomTree()
return DomTree(DFSTree(0), SNCAData[], BBNumber[], DomTreeNode[])
end

function construct_domtree(cfg::CFG)
return update_domtree!(cfg, DomTree(), true, 0)
function construct_domtree(blocks::Vector{BasicBlock})
return update_domtree!(blocks, DomTree(), true, 0)
end

function update_domtree!(cfg::CFG, domtree::DomTree,
function update_domtree!(blocks::Vector{BasicBlock}, domtree::DomTree,
recompute_dfs::Bool, max_pre::PreNumber)
if recompute_dfs
DFS!(domtree.dfs_tree, blocks)
Expand All @@ -214,7 +214,7 @@ function update_domtree!(cfg::CFG, domtree::DomTree,
max_pre = length(domtree.dfs_tree)
end

SNCA!(domtree, cfg, max_pre)
SNCA!(domtree, blocks, max_pre)
compute_domtree_nodes!(domtree)
return domtree
end
Expand Down Expand Up @@ -249,11 +249,11 @@ pseudocode in [LG05] is not entirely accurate. The best way to understand
what's happening is to read [LT79], then the description of SLT in [LG05]
(warning: inconsistent notation), then the description of Semi-NCA.
"""
function SNCA!(domtree::DomTree, cfg::CFG, max_pre::PreNumber)
function SNCA!(domtree::DomTree, blocks::Vector{BasicBlock}, max_pre::PreNumber)
D = domtree.dfs_tree
state = domtree.snca_state
# There may be more blocks than are reachable in the DFS / dominator tree
n_blocks = length(cfg.blocks)
n_blocks = length(blocks)
n_nodes = length(D)

# `label` is initialized to the identity mapping (though the paper doesn't
Expand All @@ -275,7 +275,7 @@ function SNCA!(domtree::DomTree, cfg::CFG, max_pre::PreNumber)
# worst we'll discover it below). Save a memory reference here.
semi_w = typemax(PreNumber)
last_linked = PreNumber(w + 1)
for v cfg.blocks[D.from_pre[w]].preds
for v blocks[D.from_pre[w]].preds
# For the purpose of the domtree, ignore virtual predecessors into
# catch blocks.
v == 0 && continue
Expand Down Expand Up @@ -373,8 +373,8 @@ function snca_compress_worklist!(
end
end

"Given an updated CFG, update the given dominator tree with an inserted edge."
function domtree_insert_edge!(domtree::DomTree, cfg::CFG,
"Given updated blocks, update the given dominator tree with an inserted edge."
function domtree_insert_edge!(domtree::DomTree, blocks::Vector{BasicBlock},
from::BBNumber, to::BBNumber)
# Implements Section 3.1 of [GI16]
dt = domtree.dfs_tree
Expand All @@ -385,23 +385,23 @@ function domtree_insert_edge!(domtree::DomTree, cfg::CFG,
if to_pre == 0 || (from_pre < to_pre && from_post < to_post)
# The DFS tree is invalidated by the edge insertion, so run from
# scratch
update_domtree!(cfg, domtree, true, 0)
update_domtree!(blocks, domtree, true, 0)
else
# DFS tree is still valid, so update only affected nodes
update_domtree!(cfg, domtree, false, to_pre)
update_domtree!(blocks, domtree, false, to_pre)
end

return domtree
end

"Given an updated CFG, update the given dominator tree with a deleted edge."
function domtree_delete_edge!(domtree::DomTree, cfg::CFG,
"Given updated blocks, update the given dominator tree with a deleted edge."
function domtree_delete_edge!(domtree::DomTree, blocks::Vector{BasicBlock},
from::BBNumber, to::BBNumber)
# Implements Section 3.1 of [GI16]
if is_parent(domtree.dfs_tree, from, to)
# The `from` block is the parent of the `to` block in the DFS tree, so
# deleting the edge invalidates the DFS tree, so start from scratch
update_domtree!(cfg, domtree, true, 0)
update_domtree!(blocks, domtree, true, 0)
elseif on_semidominator_path(domtree, from, to)
# Recompute semidominators for blocks with preorder number up to that
# of `to` block. Semidominators for blocks with preorder number greater
Expand All @@ -410,7 +410,7 @@ function domtree_delete_edge!(domtree::DomTree, cfg::CFG,
# `to` would be lower than those of these blocks, and `to` is not their
# parent in the DFS tree).
to_pre = domtree.dfs_tree.to_pre[to]
update_domtree!(cfg, domtree, false, to_pre)
update_domtree!(blocks, domtree, false, to_pre)
end
# Otherwise, dominator tree is not affected

Expand Down Expand Up @@ -490,19 +490,19 @@ function iterate(doms::DominatedBlocks, state::Nothing=nothing)
return (bb, nothing)
end

function naive_idoms(cfg::CFG)
nblocks = length(cfg.blocks)
function naive_idoms(blocks::Vector{BasicBlock})
nblocks = length(blocks)
# The extra +1 helps us detect unreachable blocks below
dom_all = BitSet(1:nblocks+1)
dominators = BitSet[n == 1 ? BitSet(1) : copy(dom_all) for n = 1:nblocks]
changed = true
while changed
changed = false
for n = 2:nblocks
if isempty(cfg.blocks[n].preds)
if isempty(blocks[n].preds)
continue
end
firstp, rest = Iterators.peel(Iterators.filter(p->p != 0, cfg.blocks[n].preds))
firstp, rest = Iterators.peel(Iterators.filter(p->p != 0, blocks[n].preds))
new_doms = copy(dominators[firstp])
for p in rest
intersect!(new_doms, dominators[p])
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/driver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ end

function slot2reg(ir::IRCode, ci::CodeInfo, nargs::Int, sv::OptimizationState)
# need `ci` for the slot metadata, IR for the code
@timeit "domtree 1" domtree = construct_domtree(ir.cfg)
@timeit "domtree 1" domtree = construct_domtree(ir.cfg.blocks)
defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts.inst)
@timeit "construct_ssa" ir = construct_ssa!(ci, ir, domtree, defuse_insts, nargs, sv.sptypes, sv.slottypes) # consumes `ir`
return ir
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ function getfield_elim_pass!(ir::IRCode)
# IR. This needs to be after we iterate through the IR with
# `IncrementalCompact` because removing dead blocks can invalidate the
# domtree.
@timeit "domtree 2" domtree = construct_domtree(ir.cfg)
@timeit "domtree 2" domtree = construct_domtree(ir.cfg.blocks)

# Now go through any mutable structs and see which ones we can eliminate
for (idx, (intermediaries, defuse)) in defuses
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function verify_ir(ir::IRCode, print::Bool=true)
# Verify CFG
last_end = 0
# Verify statements
domtree = construct_domtree(ir.cfg)
domtree = construct_domtree(ir.cfg.blocks)
for (idx, block) in pairs(ir.cfg.blocks)
if first(block.stmts) != last_end + 1
#ranges = [(idx,first(bb.stmts),last(bb.stmts)) for (idx, bb) in pairs(ir.cfg.blocks)]
Expand Down
4 changes: 2 additions & 2 deletions test/compiler/irpasses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ let m = Meta.@lower 1 + 1
src.ssaflags = fill(Int32(0), nstmts)
ir = Core.Compiler.inflate_ir(src)
Core.Compiler.verify_ir(ir)
domtree = Core.Compiler.construct_domtree(ir.cfg)
domtree = Core.Compiler.construct_domtree(ir.cfg.blocks)
ir = Core.Compiler.domsort_ssa!(ir, domtree)
Core.Compiler.verify_ir(ir)
phi = ir.stmts.inst[3]
Expand Down Expand Up @@ -62,7 +62,7 @@ let m = Meta.@lower 1 + 1
src.ssaflags = fill(Int32(0), nstmts)
ir = Core.Compiler.inflate_ir(src)
Core.Compiler.verify_ir(ir)
domtree = Core.Compiler.construct_domtree(ir.cfg)
domtree = Core.Compiler.construct_domtree(ir.cfg.blocks)
ir = Core.Compiler.domsort_ssa!(ir, domtree)
Core.Compiler.verify_ir(ir)
end
Expand Down
52 changes: 26 additions & 26 deletions test/compiler/ssair.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ let cfg = CFG(BasicBlock[
make_bb([2, 3] , [5] ),
make_bb([2, 4] , [] ),
], Int[])
dfs = Compiler.DFS(cfg)
dfs = Compiler.DFS(cfg.blocks)
@test dfs.from_pre[dfs.to_parent_pre[dfs.to_pre[5]]] == 4
let correct_idoms = Compiler.naive_idoms(cfg)
@test Compiler.construct_domtree(cfg).idoms_bb == correct_idoms
let correct_idoms = Compiler.naive_idoms(cfg.blocks)
@test Compiler.construct_domtree(cfg.blocks).idoms_bb == correct_idoms
# For completeness, reverse the order of pred/succ in the CFG and verify
# the answer doesn't change (it does change the which node is chosen
# as the semi-dominator, since it changes the DFS numbering).
Expand All @@ -81,7 +81,7 @@ let cfg = CFG(BasicBlock[
c && (blocks[4] = make_bb(reverse(blocks[4].preds), blocks[4].succs))
d && (blocks[5] = make_bb(reverse(blocks[5].preds), blocks[5].succs))
cfg′ = CFG(blocks, cfg.index)
@test Compiler.construct_domtree(cfg′).idoms_bb == correct_idoms
@test Compiler.construct_domtree(cfg′.blocks).idoms_bb == correct_idoms
end
end
end
Expand Down Expand Up @@ -265,53 +265,53 @@ let cfg = CFG(BasicBlock[
make_bb([2, 6], []),
make_bb([4], [5, 3]),
], Int[])
domtree = Compiler.construct_domtree(cfg)
domtree = Compiler.construct_domtree(cfg.blocks)
@test domtree.dfs_tree.to_pre == [1, 2, 4, 5, 3, 6]
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]

# Test removal of edge between a parent and child in the DFS tree, which
# should trigger complete recomputation of domtree (first case in algorithm
# for removing edge from domtree dynamically)
Compiler.cfg_delete_edge!(cfg, 2, 5)
Compiler.domtree_delete_edge!(domtree, cfg, 2, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 6, 4]
Compiler.domtree_delete_edge!(domtree, cfg.blocks, 2, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 6, 4]
# Add edge back (testing first case for insertion)
Compiler.cfg_insert_edge!(cfg, 2, 5)
Compiler.domtree_insert_edge!(domtree, cfg, 2, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 2, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]

# Test second case in algorithm for removing edges from domtree, in which
# `from` is on a semidominator path from the semidominator of `to` to `to`
Compiler.cfg_delete_edge!(cfg, 6, 5)
Compiler.domtree_delete_edge!(domtree, cfg, 6, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 2, 4]
Compiler.domtree_delete_edge!(domtree, cfg.blocks, 6, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 2, 4]
# Add edge back (testing second case for insertion)
Compiler.cfg_insert_edge!(cfg, 6, 5)
Compiler.domtree_insert_edge!(domtree, cfg, 6, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 6, 5)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]

# Test last case for removing edges, in which edge does not satisfy either
# of the above conditions
Compiler.cfg_delete_edge!(cfg, 6, 3)
Compiler.domtree_delete_edge!(domtree, cfg, 6, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
Compiler.domtree_delete_edge!(domtree, cfg.blocks, 6, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]
# Add edge back (testing second case for insertion)
Compiler.cfg_insert_edge!(cfg, 6, 3)
Compiler.domtree_insert_edge!(domtree, cfg, 6, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 6, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]

# Try removing all edges from root
Compiler.cfg_delete_edge!(cfg, 1, 2)
Compiler.domtree_delete_edge!(domtree, cfg, 1, 2)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 0, 1, 3, 6, 4]
Compiler.domtree_delete_edge!(domtree, cfg.blocks, 1, 2)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 0, 1, 3, 6, 4]
Compiler.cfg_delete_edge!(cfg, 1, 3)
Compiler.domtree_delete_edge!(domtree, cfg, 1, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 0, 0, 0, 0, 0]
Compiler.domtree_delete_edge!(domtree, cfg.blocks, 1, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 0, 0, 0, 0, 0]
# Add edges back
Compiler.cfg_insert_edge!(cfg, 1, 2)
Compiler.domtree_insert_edge!(domtree, cfg, 1, 2)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 0, 0, 2, 0]
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 1, 2)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 0, 0, 2, 0]
Compiler.cfg_insert_edge!(cfg, 1, 3)
Compiler.domtree_insert_edge!(domtree, cfg, 1, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg) == [0, 1, 1, 3, 1, 4]
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 1, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]
end

0 comments on commit 4ef7a4e

Please sign in to comment.