diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index c4f8874eab06f..f232094da3aff 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -572,7 +572,13 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: function scan_non_dataflow_flags!(inst::Instruction) flag = inst[:flag] - all_effect_free &= (flag & IR_FLAG_EFFECT_FREE) != 0 + istmt = inst[:stmt] + if !isterminator(istmt) && istmt !== nothing + # ignore control flow node – they are not removable on their own and thus not + # have `IR_FLAG_EFFECT_FREE` but still do not taint `:effect_free`-ness of + # the whole method invocation + all_effect_free &= (flag & IR_FLAG_EFFECT_FREE) != 0 + end all_nothrow &= (flag & IR_FLAG_NOTHROW) != 0 if (flag & IR_FLAG_NOUB) == 0 if !is_conditional_noub(inst) @@ -628,7 +634,7 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: # If we do not know this function terminates, taint consistency, now, # :consistent requires consistent termination. TODO: Just look at the # inconsistent region. - if !effects.terminates + if !result.ipo_effects.terminates all_retpaths_consistent = false # Check if there are potential throws that require elseif conditional_successors_may_throw(lazypostdomtree, ir, bb) @@ -652,7 +658,13 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: return true end - if !scan!(scan_stmt!, scanner, true) + + completed_scan = scan!(scan_stmt!, scanner, true) + + effects = result.ipo_effects + if completed_scan + had_trycatch && return effects + else if !all_retpaths_consistent # No longer any dataflow concerns, just scan the flags scan!(scanner, false) do inst::Instruction, idx::Int, lstmt::Int, bb::Int @@ -707,20 +719,11 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: end end - had_trycatch && return - if all_effect_free - effects = Effects(effects; effect_free = true) - end - if all_nothrow - effects = Effects(effects; nothrow = true) - end - if all_retpaths_consistent - effects = Effects(effects; consistent = ALWAYS_TRUE) - end - if all_noub - effects = Effects(effects; noub = any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) - end - result.ipo_effects = effects + return result.ipo_effects = Effects(effects; + consistent = all_retpaths_consistent ? ALWAYS_TRUE : effects.consistent, + effect_free = all_effect_free ? ALWAYS_TRUE : effects.effect_free, + nothrow = all_nothrow ? true : effects.nothrow, + noub = all_noub ? (any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub) end # run the optimization work diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index ea9bc24343715..17b34401405bb 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1078,3 +1078,19 @@ GLOBAL_MUTABLE_SWITCH[] = true @test (Base.return_types(check_switch2) |> only) === Nothing @test !Core.Compiler.is_consistent(Base.infer_effects(check_switch, (Base.RefValue{Bool},))) + +# post-opt IPO analysis refinement of `:effect_free`-ness +function post_opt_refine_effect_free(y, c=true) + x = Ref(c) + if x[] + return true + else + r = y[] isa Number + y[] = nothing + end + return r +end +@test Core.Compiler.is_effect_free(Base.infer_effects(post_opt_refine_effect_free, (Base.RefValue{Any},))) +@test Base.infer_effects((Base.RefValue{Any},)) do y + post_opt_refine_effect_free(y, true) +end |> Core.Compiler.is_effect_free