Skip to content

Commit fc92d7f

Browse files
Kenotopolarity
andcommitted
cfg_simplify: Don't accidentally consider GotoIfNot a throwing terminator
This addresses a bug in #54216, where a `GotoIfNot` was accidentally considered a throwing terminator. Just as I was about to PR this, I noticed that #54260 had already been opened for the same issue. However, there's three differences in this PR that made me open it anyway: 1. There's an additional missing case where the terminator is `nothing` which has special case semantics of allowing any type on it, because it serves as a deletion marker. 2. My test also test the `EnterNode` and `:leave` terminators, just to have a complete sampling. 3. I like the code flow in this version slightly better. Co-authored-by: Cody Tapscott <cody.tapscott@juliahub.com>
1 parent aeac289 commit fc92d7f

File tree

3 files changed

+55
-7
lines changed

3 files changed

+55
-7
lines changed

base/compiler/ssair/irinterp.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR
325325
delete!(ssa_refined, idx)
326326
end
327327
check_ret!(stmt, idx)
328-
is_terminator_or_phi = (isa(stmt, PhiNode) || isterminator(stmt))
328+
is_terminator_or_phi = (isa(stmt, PhiNode) || stmt === nothing || isterminator(stmt))
329329
if typ === Bottom && !(idx == lstmt && is_terminator_or_phi)
330330
return true
331331
end

base/compiler/ssair/passes.jl

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,12 +2341,8 @@ function cfg_simplify!(ir::IRCode)
23412341
curr = merged_succ[curr]
23422342
end
23432343
terminator = ir[SSAValue(bbs[curr].stmts[end])][:stmt]
2344-
is_throw = ir[SSAValue(bbs[curr].stmts[end])][:type] === Union{} && !isa(terminator, PhiNode)
2345-
if isa(terminator, GotoNode) || isa(terminator, ReturnNode) || is_throw
2346-
# Only advance to next block if it's a successor
2347-
# (avoid GotoNode, ReturnNode, throw()/Union{})
2348-
break
2349-
elseif isa(terminator, GotoIfNot)
2344+
2345+
if isa(terminator, GotoIfNot)
23502346
if bb_rename_succ[terminator.dest] == 0
23512347
push!(worklist, terminator.dest)
23522348
end
@@ -2355,6 +2351,20 @@ function cfg_simplify!(ir::IRCode)
23552351
if bb_rename_succ[catchbb] == 0
23562352
push!(worklist, catchbb)
23572353
end
2354+
elseif isa(terminator, GotoNode) || isa(terminator, ReturnNode)
2355+
# No implicit fall through. Schedule from work list.
2356+
break
2357+
else
2358+
is_bottom = ir[SSAValue(bbs[curr].stmts[end])][:type] === Union{}
2359+
if is_bottom && !isa(terminator, PhiNode) && terminator !== nothing
2360+
# If this is a regular statement (not PhiNode/GotoNode/GotoIfNot
2361+
# or the `nothing` special case deletion marker),
2362+
# and the type is Union{}, then this may be a terminator.
2363+
# Ordinarily we normalize with ReturnNode, but this is not,
2364+
# required. In any case, we do not fall through, so we
2365+
# do not need to schedule the fall-through block.
2366+
break
2367+
end
23582368
end
23592369
ncurr = curr + 1
23602370
while !isempty(searchsorted(dropped_bbs, ncurr))

test/compiler/irpasses.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,3 +1911,41 @@ let code = Any[
19111911
Core.Compiler.verify_ir(ir)
19121912
@test length(ir.cfg.blocks) == 3 # should have removed block 3
19131913
end
1914+
1915+
let code = Any[
1916+
# block 1
1917+
EnterNode(4, 1),
1918+
# block 2
1919+
GotoNode(3), # will be turned into nothing
1920+
# block 3
1921+
GotoNode(5),
1922+
# block 4
1923+
ReturnNode(),
1924+
# block 5
1925+
Expr(:leave, SSAValue(1)),
1926+
# block 6
1927+
GotoIfNot(Core.Argument(1), 8),
1928+
# block 7
1929+
ReturnNode(1),
1930+
# block 8
1931+
ReturnNode(2),
1932+
]
1933+
ir = make_ircode(code; ssavaluetypes=Any[Union{}, Union{}, Union{}, Union{}, Nothing, Union{}, Union{}, Union{}])
1934+
@test length(ir.cfg.blocks) == 7
1935+
Core.Compiler.verify_ir(ir)
1936+
1937+
# Union typed deletion marker in basic block 2
1938+
Core.Compiler.setindex!(ir, nothing, SSAValue(2))
1939+
1940+
# Test cfg_simplify
1941+
Core.Compiler.verify_ir(ir)
1942+
ir = Core.Compiler.cfg_simplify!(ir)
1943+
Core.Compiler.verify_ir(ir)
1944+
@test length(ir.cfg.blocks) == 6
1945+
gotoifnot = Core.Compiler.last(ir.cfg.blocks[3].stmts)
1946+
inst = ir[SSAValue(gotoifnot)]
1947+
@test isa(inst[:stmt], GotoIfNot)
1948+
# Make sure we didn't accidentally schedule the unreachable block as
1949+
# fallthrough
1950+
@test isdefined(ir[SSAValue(gotoifnot+1)][:inst]::ReturnNode, :val)
1951+
end

0 commit comments

Comments
 (0)