Skip to content

Commit e11b31c

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`, while also enforcing the augmented control flow graph (`construct_augmented_cfg`) to have a single exit node really. Since the augmented post domtree is now 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 4dcf357 commit e11b31c

File tree

3 files changed

+110
-47
lines changed

3 files changed

+110
-47
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 single 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 terminator isa ReturnNode
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: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -233,35 +233,79 @@ let code = Any[
233233
end
234234

235235
# issue #37919
236-
let ci = code_lowered(()->@isdefined(_not_def_37919_), ())[1]
236+
let ci = only(code_lowered(()->@isdefined(_not_def_37919_), ()))
237237
ir = Core.Compiler.inflate_ir(ci)
238238
@test Core.Compiler.verify_ir(ir) === nothing
239239
end
240240

241241
let code = Any[
242242
# block 1
243-
GotoIfNot(Argument(2), 4),
243+
GotoIfNot(Argument(2), 4)
244244
# block 2
245-
GotoNode(3),
245+
Expr(:call, throw, "potential throw")
246+
ReturnNode() # unreachable
246247
# block 3
247-
Expr(:call, throw, "potential throw"),
248+
ReturnNode(Argument(3))
249+
]
250+
ir = make_ircode(code; slottypes=Any[Any,Bool,Int])
251+
visited = BitSet()
252+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
253+
push!(visited, succ)
254+
return false
255+
end
256+
@test 2 visited
257+
@test 3 visited
258+
oc = Core.OpaqueClosure(ir)
259+
@test oc(false, 1) == 1
260+
@test_throws "potential throw" oc(true, 1)
261+
end
262+
263+
let code = Any[
264+
# block 1
265+
GotoIfNot(Argument(2), 3)
266+
# block 2
267+
ReturnNode(Argument(3))
268+
# block 3
269+
Expr(:call, throw, "potential throw")
270+
ReturnNode() # unreachable
271+
]
272+
ir = make_ircode(code; slottypes=Any[Any,Bool,Int])
273+
visited = BitSet()
274+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
275+
push!(visited, succ)
276+
return false
277+
end
278+
@test 2 visited
279+
@test 3 visited
280+
oc = Core.OpaqueClosure(ir)
281+
@test oc(true, 1) == 1
282+
@test_throws "potential throw" oc(false, 1)
283+
end
284+
285+
let code = Any[
286+
# block 1
287+
GotoIfNot(Argument(2), 5)
288+
# block 2
289+
GotoNode(3)
290+
# block 3
291+
Expr(:call, throw, "potential throw")
292+
ReturnNode()
248293
# block 4
249-
Expr(:call, Core.Intrinsics.add_int, Argument(3), Argument(4)),
250-
GotoNode(6),
294+
Expr(:call, Core.Intrinsics.add_int, Argument(3), Argument(4))
295+
GotoNode(7)
251296
# block 5
252-
ReturnNode(SSAValue(4))
297+
ReturnNode(SSAValue(5))
253298
]
254299
ir = make_ircode(code; slottypes=Any[Any,Bool,Int,Int])
255-
lazypostdomtree = Core.Compiler.LazyPostDomtree(ir)
256300
visited = BitSet()
257-
@test !Core.Compiler.visit_conditional_successors(lazypostdomtree, ir, #=bb=#1) do succ::Int
301+
@test !Core.Compiler.visit_conditional_successors(ir, #=bb=#1) do succ::Int
258302
push!(visited, succ)
259303
return false
260304
end
261305
@test 2 visited
262306
@test 3 visited
263-
@test 4 visited
264-
@test 5 visited
307+
@test 4 visited
308+
@test 5 visited
265309
oc = Core.OpaqueClosure(ir)
266310
@test oc(false, 1, 1) == 2
267311
@test_throws "potential throw" oc(true, 1, 1)

0 commit comments

Comments
 (0)