Skip to content

Commit 8c2209f

Browse files
committed
post-opt: use augmented post-domtree for visit_conditional_successors
This commit fixes the first problem that was found while digging into #53613. It turns out that the post-domtree constructed from regular `IRCode` doesn't work for visiting conditional successors for post-opt analysis in cases like: ```julia julia> let code = Any[ # block 1 GotoIfNot(Argument(2), 3), # block 2 ReturnNode(Argument(3)), # block 3 (we should visit this block) Expr(:call, throw, "potential throw"), ReturnNode(), # unreachable ] ir = make_ircode(code; slottypes=Any[Any,Bool,Bool]) visited = BitSet() @test !Core.Compiler.visit_conditional_successors(CC.LazyPostDomtree(ir), ir, #=bb=#1) do succ::Int push!(visited, succ) return false end @test 2 ∉ visited @test 3 ∈ visited end Test Failed at REPL[14]:16 Expression: 2 ∉ visited Evaluated: 2 ∉ BitSet([2]) ``` This might mean that we need to fix on the `postdominates` end, but for now, this commit tries to get around it by using the augmented post domtree in `visit_conditional_successors`. Since the augmented post domtree is enforced to have a single return, we can keep using the current `postdominates` to fix the issue. However, this commit isn't enough to fix the NeuralNetworkReachability segfault as reported in #53613, and we need to tackle the second issue reported there too (#53613 (comment)).
1 parent f6504e4 commit 8c2209f

File tree

3 files changed

+100
-38
lines changed

3 files changed

+100
-38
lines changed

base/compiler/optimize.jl

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -527,15 +527,53 @@ function any_stmt_may_throw(ir::IRCode, bb::Int)
527527
return false
528528
end
529529

530-
function visit_conditional_successors(callback, lazypostdomtree::LazyPostDomtree, ir::IRCode, bb::Int)
530+
mutable struct LazyAugmentedDomtrees
531+
const ir::IRCode
532+
cfg::CFG
533+
domtree::DomTree
534+
postdomtree::PostDomTree
535+
LazyAugmentedDomtrees(ir::IRCode) = new(ir)
536+
end
537+
538+
function get!(lazyagdomtrees::LazyAugmentedDomtrees, sym::Symbol)
539+
isdefined(lazyagdomtrees, sym) && return getfield(lazyagdomtrees, sym)
540+
if sym === :cfg
541+
return lazyagdomtrees.cfg = construct_augmented_cfg(lazyagdomtrees.ir)
542+
elseif sym === :domtree
543+
return lazyagdomtrees.domtree = construct_domtree(get!(lazyagdomtrees, :cfg))
544+
elseif sym === :postdomtree
545+
return lazyagdomtrees.postdomtree = construct_postdomtree(get!(lazyagdomtrees, :cfg))
546+
else
547+
error("invalid field access")
548+
end
549+
end
550+
551+
function construct_augmented_cfg(ir::IRCode)
552+
cfg = copy(ir.cfg)
553+
# Add a virtual basic block to represent the exit
554+
push!(cfg.blocks, BasicBlock(StmtRange(0:-1)))
555+
for bb = 1:(length(cfg.blocks)-1)
556+
terminator = ir[SSAValue(last(cfg.blocks[bb].stmts))][:stmt]
557+
if isa(terminator, ReturnNode) && isdefined(terminator, :val)
558+
cfg_insert_edge!(cfg, bb, length(cfg.blocks))
559+
end
560+
end
561+
return cfg
562+
end
563+
564+
visit_conditional_successors(callback, ir::IRCode, bb::Int) =
565+
visit_conditional_successors(callback, construct_postdomtree(construct_augmented_cfg(ir)), ir, bb)
566+
visit_conditional_successors(callback, lazyagdomtrees::LazyAugmentedDomtrees, ir::IRCode, bb::Int) =
567+
visit_conditional_successors(callback, get!(lazyagdomtrees, :postdomtree), ir, bb)
568+
function visit_conditional_successors(callback, postdomtree::PostDomTree, ir::IRCode, bb::Int)
531569
visited = BitSet((bb,))
532570
worklist = Int[bb]
533571
while !isempty(worklist)
534572
thisbb = popfirst!(worklist)
535573
for succ in ir.cfg.blocks[thisbb].succs
536574
succ in visited && continue
537575
push!(visited, succ)
538-
if postdominates(get!(lazypostdomtree), succ, bb)
576+
if postdominates(postdomtree, succ, bb)
539577
# this successor is not conditional, so no need to visit it further
540578
continue
541579
elseif callback(succ)
@@ -548,40 +586,12 @@ function visit_conditional_successors(callback, lazypostdomtree::LazyPostDomtree
548586
return false
549587
end
550588

551-
struct AugmentedDomtree
552-
cfg::CFG
553-
domtree::DomTree
554-
end
555-
556-
mutable struct LazyAugmentedDomtree
557-
const ir::IRCode
558-
agdomtree::AugmentedDomtree
559-
LazyAugmentedDomtree(ir::IRCode) = new(ir)
560-
end
561-
562-
function get!(lazyagdomtree::LazyAugmentedDomtree)
563-
isdefined(lazyagdomtree, :agdomtree) && return lazyagdomtree.agdomtree
564-
ir = lazyagdomtree.ir
565-
cfg = copy(ir.cfg)
566-
# Add a virtual basic block to represent the exit
567-
push!(cfg.blocks, BasicBlock(StmtRange(0:-1)))
568-
for bb = 1:(length(cfg.blocks)-1)
569-
terminator = ir[SSAValue(last(cfg.blocks[bb].stmts))][:stmt]
570-
if isa(terminator, ReturnNode) && isdefined(terminator, :val)
571-
cfg_insert_edge!(cfg, bb, length(cfg.blocks))
572-
end
573-
end
574-
domtree = construct_domtree(cfg)
575-
return lazyagdomtree.agdomtree = AugmentedDomtree(cfg, domtree)
576-
end
577-
578589
mutable struct PostOptAnalysisState
579590
const result::InferenceResult
580591
const ir::IRCode
581592
const inconsistent::BitSetBoundedMinPrioritySet
582593
const tpdum::TwoPhaseDefUseMap
583-
const lazypostdomtree::LazyPostDomtree
584-
const lazyagdomtree::LazyAugmentedDomtree
594+
const lazyagdomtrees::LazyAugmentedDomtrees
585595
const ea_analysis_pending::Vector{Int}
586596
all_retpaths_consistent::Bool
587597
all_effect_free::Bool
@@ -592,9 +602,8 @@ mutable struct PostOptAnalysisState
592602
function PostOptAnalysisState(result::InferenceResult, ir::IRCode)
593603
inconsistent = BitSetBoundedMinPrioritySet(length(ir.stmts))
594604
tpdum = TwoPhaseDefUseMap(length(ir.stmts))
595-
lazypostdomtree = LazyPostDomtree(ir)
596-
lazyagdomtree = LazyAugmentedDomtree(ir)
597-
return new(result, ir, inconsistent, tpdum, lazypostdomtree, lazyagdomtree, Int[],
605+
lazyagdomtrees = LazyAugmentedDomtrees(ir)
606+
return new(result, ir, inconsistent, tpdum, lazyagdomtrees, Int[],
598607
true, true, nothing, true, true, false)
599608
end
600609
end
@@ -834,13 +843,13 @@ function ((; sv)::ScanStmt)(inst::Instruction, lstmt::Int, bb::Int)
834843
# inconsistent region.
835844
if !sv.result.ipo_effects.terminates
836845
sv.all_retpaths_consistent = false
837-
elseif visit_conditional_successors(sv.lazypostdomtree, sv.ir, bb) do succ::Int
846+
elseif visit_conditional_successors(sv.lazyagdomtrees, sv.ir, bb) do succ::Int
838847
return any_stmt_may_throw(sv.ir, succ)
839848
end
840849
# check if this `GotoIfNot` leads to conditional throws, which taints consistency
841850
sv.all_retpaths_consistent = false
842851
else
843-
(; cfg, domtree) = get!(sv.lazyagdomtree)
852+
cfg, domtree = get!(sv.lazyagdomtrees, :cfg), get!(sv.lazyagdomtrees, :domtree)
844853
for succ in iterated_dominance_frontier(cfg, BlockLiveness(sv.ir.cfg.blocks[bb].succs, nothing), domtree)
845854
if succ == length(cfg.blocks)
846855
# Phi node in the virtual exit -> We have a conditional

test/compiler/effects.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,3 +1387,13 @@ let; Base.Experimental.@force_compile; func52843(); end
13871387
# https://github.com/JuliaLang/julia/issues/53508
13881388
@test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (UnitRange{Int},Int)))
13891389
@test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Base.OneTo{Int},Int)))
1390+
1391+
@noinline f53613() = @assert isdefined(@__MODULE__, :v53613)
1392+
g53613() = f53613()
1393+
@test !Core.Compiler.is_consistent(Base.infer_effects(f53613))
1394+
@test_broken !Core.Compiler.is_consistent(Base.infer_effects(g53613))
1395+
@test_throws AssertionError f53613()
1396+
@test_throws AssertionError g53613()
1397+
global v53613 = nothing
1398+
@test f53613() === nothing
1399+
@test g53613() === nothing

test/compiler/ssair.jl

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,8 @@ let code = Any[
252252
ReturnNode(SSAValue(4))
253253
]
254254
ir = make_ircode(code; slottypes=Any[Any,Bool,Int,Int])
255-
lazypostdomtree = Core.Compiler.LazyPostDomtree(ir)
256255
visited = BitSet()
257-
@test !Core.Compiler.visit_conditional_successors(lazypostdomtree, ir, #=bb=#1) do succ::Int
256+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
258257
push!(visited, succ)
259258
return false
260259
end
@@ -267,6 +266,50 @@ let code = Any[
267266
@test_throws "potential throw" oc(true, 1, 1)
268267
end
269268

269+
let code = Any[
270+
# block 1
271+
GotoIfNot(Argument(2), 4),
272+
# block 2
273+
Expr(:call, throw, "potential throw"),
274+
ReturnNode(), # unreachable
275+
# block 3
276+
ReturnNode(Argument(3)),
277+
]
278+
ir = make_ircode(code; slottypes=Any[Any,Bool,Bool])
279+
visited = BitSet()
280+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
281+
push!(visited, succ)
282+
return false
283+
end
284+
@test 2 visited
285+
@test 3 visited
286+
oc = Core.OpaqueClosure(ir)
287+
@test oc(false, 1) == 1
288+
@test_throws "potential throw" oc(true, 1)
289+
end
290+
291+
let code = Any[
292+
# block 1
293+
GotoIfNot(Argument(2), 3),
294+
# block 2
295+
ReturnNode(Argument(3)),
296+
# block 3
297+
Expr(:call, throw, "potential throw"),
298+
ReturnNode(), # unreachable
299+
]
300+
ir = make_ircode(code; slottypes=Any[Any,Bool,Bool])
301+
visited = BitSet()
302+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
303+
push!(visited, succ)
304+
return false
305+
end
306+
@test 2 visited
307+
@test 3 visited
308+
oc = Core.OpaqueClosure(ir)
309+
@test oc(true, 1) == 1
310+
@test_throws "potential throw" oc(false, 1)
311+
end
312+
270313
# Test dynamic update of domtree with edge insertions and deletions in the
271314
# following CFG:
272315
#

0 commit comments

Comments
 (0)