-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
post-opt: add more test cases for visit_conditional_successors
#53642
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the augmented domtree still does not have a unique exit (must-throw blocks are not attached to the exit node), I think this is still leaving a bug in place:
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])
postdomtree = CC.construct_postdomtree(construct_augmented_cfg(ir).blocks)
[CC.postdominates(postdomtree, bb, 1) for bb in 1:3]
end
3-element Vector{Bool}:
1
1
0
This seems to be under the impression that BB (2) post-dominates (1), which is clearly false. This is probably computing post-dominance in each exit branch under the assumption that the corresponding exit is ultimately taken (e.g. for the "nothrow" augmented code path, this is now reporting postdominance under a :nothrow
assumption)
I think the fix is needed in the DomTree instead, so that the reported post-dominance corresponds to a must-happen-after relation like we'd expect.
I'm not sure whether to call this a mistake or not. If you don't consider the unreachable node as an exit point, then the only real exit point becomes BB (2), which means you could say BB (2) post-dominates BB (1). The issue seems to be that when you build a post-dominance tree using the original, unaugmented CFG, you end up with results that suggest the opposite: 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])
postdomtree = CC.construct_postdomtree(ir.cfg.blocks)
[CC.postdominates(postdomtree, bb, 1) for bb in 1:3]
end
3-element Vector{Bool}:
1
0
1 So, I'm with you on the need for a fix on the |
f1d4535
to
c336656
Compare
I'm with you there, but do we know enough about the DomTree bug to know that this behaves correctly even though we still don't have a post-dominating exit node (from the POV of the DomTree algorithm)? |
Here's an example where this still seems to go wrong: julia> let code = Any[
# block 1 (we should visit this block)
#= %1 =# Expr(:call, :opaque), # maybe-throw
#= %2 =# GotoIfNot(Argument(2), 6),
# block 2
#= %3 =# GotoIfNot(Argument(3), 7),
# block 3 (we should visit this block)
#= %4 =# Expr(:call, throw, "potential throw"),
#= %5 =# ReturnNode(), # unreachable
# block 4
#= %6 =# GotoNode(1),
# block 5
#= %7 =# ReturnNode(nothing)
]
ir = make_ircode(code; slottypes=Any[Any,Bool,Bool])
lazy = CC.LazyAugmentedDomtrees(ir)
visited = BitSet()
Core.Compiler.visit_conditional_successors(lazy, ir, #=bb=#1) do succ::Int
push!(visited, succ)
return false
end
visited
end
BitSet([4]) Whether this returns |
base/compiler/optimize.jl
Outdated
push!(cfg.blocks, BasicBlock(StmtRange(0:-1))) | ||
for bb = 1:(length(cfg.blocks)-1) | ||
terminator = ir[SSAValue(last(cfg.blocks[bb].stmts))][:stmt] | ||
if isa(terminator, ReturnNode) && isdefined(terminator, :val) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Keno, do you think it's appropriate to always add a virtual edge, regardless of whether the actual return node is reachable? I believe this adjustment is necessary for visit_conditional_successors
but I'm uncertain about its implications for the usage in iterated_dominance_frontier
at L853.
I agree with @topolarity that the postdomination query is wrong in the example. A block "A", postdominates "B" if every path that passes through B must also pass through "A". Our notion of postdominance does ignore throws, but I for those purposes, the |
So, are we saying that |
@nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. |
This is an alternative to #53642 The `dom_edges()` for an exit block in the CFG are empty when computing the PostDomTree so the loop below this may not actually run. In that case, the right semidominator is the ancestor from the DFSTree, which is the "virtual" -1 block. This resolves half of the issue in #53613: ```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 Passed ``` This needs some tests (esp. since I don't think we have any DomTree tests at all right now), but otherwise should be good to go.
This commit was initially crafted to fix an issue with post-opt analysis that came to light in #53613, planning to solve it by incorporating an augmented CFG directly into the post-opt analysis. But, with the issue now sorted out by #53739, this commit shifted focus to just adding test cases. === The original commit message === 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)).
visit_conditional_successors
visit_conditional_successors
This is an alternative to #53642 The `dom_edges()` for an exit block in the CFG are empty when computing the PostDomTree so the loop below this may not actually run. In that case, the right semidominator is the ancestor from the DFSTree, which is the "virtual" -1 block. This resolves half of the issue in #53613: ```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 Passed ``` This needs some tests (esp. since I don't think we have any DomTree tests at all right now), but otherwise should be good to go.
) 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)).
This is an alternative to #53642 The `dom_edges()` for an exit block in the CFG are empty when computing the PostDomTree so the loop below this may not actually run. In that case, the right semidominator is the ancestor from the DFSTree, which is the "virtual" -1 block. This resolves half of the issue in #53613: ```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 Passed ``` This needs some tests (esp. since I don't think we have any DomTree tests at all right now), but otherwise should be good to go.
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: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 invisit_conditional_successors
. Since the augmented post domtree is enforced to have a single return, we can keep using the currentpostdominates
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)).