Skip to content

Commit 8ac9368

Browse files
committed
inference: follow up #54323, override ssaflags with new cycle effects
#54323 ensures that all frames within a cycle have the same, cycle valid effects. However, `src.ssaflags` is calculated using partial effects, so when the effects of a `frame` within the cycle are updated, there would be an inconsistency between `frame.ipo_effects` and `frame.src.ssaflags`. Due to this inconsistency, #54323 breaks the test cases from #51092, when backported to v1.11. On the surface this is because #52999 hasn't been backported to v1.11, but the fundamental issue lies in this inconsistency between cycle effects and `ssaflags`. To resolve this issue, this commit traverses `cycle_backedges` to visit statements involved in the cycle, and updates each `ssaflags` according to new cycle valid effects if necessary.
1 parent ec32170 commit 8ac9368

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

base/compiler/typeinfer.jl

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,36 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
235235
typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle
236236
# with no active ip's, frame is done
237237
frames = frame.callers_in_cycle
238-
isempty(frames) && push!(frames, frame)
238+
if isempty(frames)
239+
push!(frames, frame)
240+
finish_nocycle(interp, frame)
241+
elseif length(frames) == 1
242+
@assert frames[1] === frame "invalid callers_in_cycle"
243+
finish_nocycle(interp, frame)
244+
else
245+
finish_cycle(interp, frames)
246+
end
247+
empty!(frames)
248+
return true
249+
end
250+
251+
function finish_nocycle(::AbstractInterpreter, frame::InferenceState)
252+
frame.dont_work_on_me = true
253+
finish(frame, frame.interp)
254+
opt = frame.result.src
255+
if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
256+
optimize(frame.interp, opt, frame.result)
257+
end
258+
finish!(frame.interp, frame)
259+
if is_cached(frame)
260+
cache_result!(frame.interp, frame.result)
261+
end
262+
return nothing
263+
end
264+
265+
function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState})
239266
cycle_valid_worlds = WorldRange()
240-
cycle_effects = EFFECTS_TOTAL
267+
cycle_valid_effects = EFFECTS_TOTAL
241268
for caller in frames
242269
@assert !(caller.dont_work_on_me)
243270
caller.dont_work_on_me = true
@@ -246,11 +273,10 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
246273
# that are simply the intersection of each partial computation, without having
247274
# dependencies on each other (unlike rt and exct)
248275
cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds)
249-
cycle_effects = merge_effects(cycle_effects, caller.ipo_effects)
276+
cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects)
250277
end
251278
for caller in frames
252-
caller.valid_worlds = cycle_valid_worlds
253-
caller.ipo_effects = cycle_effects
279+
adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects)
254280
finish(caller, caller.interp)
255281
end
256282
for caller in frames
@@ -265,8 +291,22 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
265291
cache_result!(caller.interp, caller.result)
266292
end
267293
end
268-
empty!(frames)
269-
return true
294+
return nothing
295+
end
296+
297+
function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects)
298+
sv.valid_worlds = cycle_valid_worlds
299+
sv.ipo_effects = cycle_valid_effects
300+
# traverse the callees of this cycle that are tracked within `sv.cycle_backedges`
301+
# and adjust their statements so that they are consistent with the new `cycle_valid_effects`
302+
new_flags = flags_for_effects(cycle_valid_effects)
303+
for (callee, pc) in sv.cycle_backedges
304+
old_currpc = callee.currpc
305+
callee.currpc = pc
306+
set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS)
307+
callee.currpc = old_currpc
308+
end
309+
return nothing
270310
end
271311

272312
function is_result_constabi_eligible(result::InferenceResult)

test/compiler/inference.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5291,6 +5291,15 @@ end
52915291
foo51090(b) = return bar51090(b)
52925292
@test !fully_eliminated(foo51090, (Int,))
52935293

5294+
Base.@assume_effects :terminates_globally @noinline function bar51090_terminates(b)
5295+
b == 0 && return
5296+
r = foo51090_terminates(b - 1)
5297+
Base.donotdelete(b)
5298+
return r
5299+
end
5300+
foo51090_terminates(b) = return bar51090_terminates(b)
5301+
@test !fully_eliminated(foo51090_terminates, (Int,))
5302+
52945303
# exploit throwness from concrete eval for intrinsics
52955304
@test Base.return_types() do
52965305
Base.or_int(true, 1)

0 commit comments

Comments
 (0)