diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 05d78e8706072..3a0d8c6c52f00 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -325,7 +325,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR delete!(ssa_refined, idx) end check_ret!(stmt, idx) - is_terminator_or_phi = (isa(stmt, PhiNode) || isterminator(stmt)) + is_terminator_or_phi = (isa(stmt, PhiNode) || stmt === nothing || isterminator(stmt)) if typ === Bottom && !(idx == lstmt && is_terminator_or_phi) return true end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 34f7ed4a21cb0..8426611c3df37 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -2341,12 +2341,8 @@ function cfg_simplify!(ir::IRCode) curr = merged_succ[curr] end terminator = ir[SSAValue(bbs[curr].stmts[end])][:stmt] - is_throw = ir[SSAValue(bbs[curr].stmts[end])][:type] === Union{} && !isa(terminator, PhiNode) - if isa(terminator, GotoNode) || isa(terminator, ReturnNode) || is_throw - # Only advance to next block if it's a successor - # (avoid GotoNode, ReturnNode, throw()/Union{}) - break - elseif isa(terminator, GotoIfNot) + + if isa(terminator, GotoIfNot) if bb_rename_succ[terminator.dest] == 0 push!(worklist, terminator.dest) end @@ -2355,6 +2351,20 @@ function cfg_simplify!(ir::IRCode) if bb_rename_succ[catchbb] == 0 push!(worklist, catchbb) end + elseif isa(terminator, GotoNode) || isa(terminator, ReturnNode) + # No implicit fall through. Schedule from work list. + break + else + is_bottom = ir[SSAValue(bbs[curr].stmts[end])][:type] === Union{} + if is_bottom && !isa(terminator, PhiNode) && terminator !== nothing + # If this is a regular statement (not PhiNode/GotoNode/GotoIfNot + # or the `nothing` special case deletion marker), + # and the type is Union{}, then this may be a terminator. + # Ordinarily we normalize with ReturnNode(), but this is not + # required. In any case, we do not fall through, so we + # do not need to schedule the fall-through block. + break + end end ncurr = curr + 1 while !isempty(searchsorted(dropped_bbs, ncurr)) diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 21bf0a59fd289..5c56b6eb76aa5 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1911,3 +1911,41 @@ let code = Any[ Core.Compiler.verify_ir(ir) @test length(ir.cfg.blocks) == 3 # should have removed block 3 end + +let code = Any[ + # block 1 + EnterNode(4, 1), + # block 2 + GotoNode(3), # will be turned into nothing + # block 3 + GotoNode(5), + # block 4 + ReturnNode(), + # block 5 + Expr(:leave, SSAValue(1)), + # block 6 + GotoIfNot(Core.Argument(1), 8), + # block 7 + ReturnNode(1), + # block 8 + ReturnNode(2), + ] + ir = make_ircode(code; ssavaluetypes=Any[Union{}, Union{}, Union{}, Union{}, Nothing, Union{}, Union{}, Union{}]) + @test length(ir.cfg.blocks) == 8 + Core.Compiler.verify_ir(ir) + + # Union typed deletion marker in basic block 2 + Core.Compiler.setindex!(ir, nothing, SSAValue(2)) + + # Test cfg_simplify + Core.Compiler.verify_ir(ir) + ir = Core.Compiler.cfg_simplify!(ir) + Core.Compiler.verify_ir(ir) + @test length(ir.cfg.blocks) == 6 + gotoifnot = Core.Compiler.last(ir.cfg.blocks[3].stmts) + inst = ir[SSAValue(gotoifnot)] + @test isa(inst[:stmt], GotoIfNot) + # Make sure we didn't accidentally schedule the unreachable block as + # fallthrough + @test isdefined(ir[SSAValue(gotoifnot+1)][:inst]::ReturnNode, :val) +end