diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 7233cf62f6b93..ba8be9ca56159 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -235,9 +235,36 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle # with no active ip's, frame is done frames = frame.callers_in_cycle - isempty(frames) && push!(frames, frame) + if isempty(frames) + push!(frames, frame) + finish_nocycle(interp, frame) + elseif length(frames) == 1 + @assert frames[1] === frame "invalid callers_in_cycle" + finish_nocycle(interp, frame) + else + finish_cycle(interp, frames) + end + empty!(frames) + return true +end + +function finish_nocycle(::AbstractInterpreter, frame::InferenceState) + frame.dont_work_on_me = true + finish(frame, frame.interp) + opt = frame.result.src + if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` + optimize(frame.interp, opt, frame.result) + end + finish!(frame.interp, frame) + if is_cached(frame) + cache_result!(frame.interp, frame.result) + end + return nothing +end + +function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) cycle_valid_worlds = WorldRange() - cycle_effects = EFFECTS_TOTAL + cycle_valid_effects = EFFECTS_TOTAL for caller in frames @assert !(caller.dont_work_on_me) caller.dont_work_on_me = true @@ -246,11 +273,10 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) # that are simply the intersection of each partial computation, without having # dependencies on each other (unlike rt and exct) cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) - cycle_effects = merge_effects(cycle_effects, caller.ipo_effects) + cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end for caller in frames - caller.valid_worlds = cycle_valid_worlds - caller.ipo_effects = cycle_effects + adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finish(caller, caller.interp) end for caller in frames @@ -265,8 +291,22 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) cache_result!(caller.interp, caller.result) end end - empty!(frames) - return true + return nothing +end + +function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects) + sv.valid_worlds = cycle_valid_worlds + sv.ipo_effects = cycle_valid_effects + # traverse the callees of this cycle that are tracked within `sv.cycle_backedges` + # and adjust their statements so that they are consistent with the new `cycle_valid_effects` + new_flags = flags_for_effects(cycle_valid_effects) + for (callee, pc) in sv.cycle_backedges + old_currpc = callee.currpc + callee.currpc = pc + set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS) + callee.currpc = old_currpc + end + return nothing end function is_result_constabi_eligible(result::InferenceResult) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index c45f5f11bf62e..eca937dddc5ab 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5291,6 +5291,15 @@ end foo51090(b) = return bar51090(b) @test !fully_eliminated(foo51090, (Int,)) +Base.@assume_effects :terminates_globally @noinline function bar51090_terminates(b) + b == 0 && return + r = foo51090_terminates(b - 1) + Base.donotdelete(b) + return r +end +foo51090_terminates(b) = return bar51090_terminates(b) +@test !fully_eliminated(foo51090_terminates, (Int,)) + # exploit throwness from concrete eval for intrinsics @test Base.return_types() do Base.or_int(true, 1)