Skip to content

Commit 73d4077

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`. This commit uses a somewhat hacky approach to resolve this. It identifies statements involved in the cycle comparing `stmt_edges` to `callers_in_cycle`, and updates `ssaflags` according to new cycle valid effects if necessary. This resolves the issue, but ideally, it should be implemented more safely with the new `edges` format that will be implemented in the future. For now, this approach should be okay.
1 parent ec32170 commit 73d4077

File tree

2 files changed

+55
-7
lines changed

2 files changed

+55
-7
lines changed

base/compiler/typeinfer.jl

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,35 @@ 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+
finish_nocycle(interp, frame)
240+
elseif length(frames) == 1
241+
@assert frames[1] === frame "invalid callers_in_cycle"
242+
finish_nocycle(interp, frame)
243+
else
244+
finish_cycle(interp, frames)
245+
end
246+
empty!(frames)
247+
return true
248+
end
249+
250+
function finish_nocycle(::AbstractInterpreter, frame::InferenceState)
251+
frame.dont_work_on_me = true
252+
finish(frame, frame.interp)
253+
opt = frame.result.src
254+
if opt isa OptimizationState # implies `may_optimize(caller.interp) === true`
255+
optimize(frame.interp, opt, frame.result)
256+
end
257+
finish!(frame.interp, frame)
258+
if is_cached(frame)
259+
cache_result!(frame.interp, frame.result)
260+
end
261+
return nothing
262+
end
263+
264+
function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState})
239265
cycle_valid_worlds = WorldRange()
240-
cycle_effects = EFFECTS_TOTAL
266+
cycle_valid_effects = EFFECTS_TOTAL
241267
for caller in frames
242268
@assert !(caller.dont_work_on_me)
243269
caller.dont_work_on_me = true
@@ -246,11 +272,10 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
246272
# that are simply the intersection of each partial computation, without having
247273
# dependencies on each other (unlike rt and exct)
248274
cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds)
249-
cycle_effects = merge_effects(cycle_effects, caller.ipo_effects)
275+
cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects)
250276
end
251277
for caller in frames
252-
caller.valid_worlds = cycle_valid_worlds
253-
caller.ipo_effects = cycle_effects
278+
adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects)
254279
finish(caller, caller.interp)
255280
end
256281
for caller in frames
@@ -265,8 +290,22 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
265290
cache_result!(caller.interp, caller.result)
266291
end
267292
end
268-
empty!(frames)
269-
return true
293+
return nothing
294+
end
295+
296+
function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects)
297+
sv.valid_worlds = cycle_valid_worlds
298+
sv.ipo_effects = cycle_valid_effects
299+
# traverse the callees of this cycle that are tracked within `sv.cycle_backedges`
300+
# and adjust their statements so that they are consistent with the new `cycle_valid_effects`
301+
new_flags = flags_for_effects(cycle_valid_effects)
302+
for (callee, pc) in sv.cycle_backedges
303+
old_currpc = callee.currpc
304+
callee.currpc = pc
305+
set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS)
306+
callee.currpc = old_currpc
307+
end
308+
return nothing
270309
end
271310

272311
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)