Skip to content

Commit 956422e

Browse files
committed
inference: re-enable inference overload for basic statements (#58027)
After #55575, inference for basic statements got inlined into `typeinf_local`. But external abstract interpreters like JET.jl need to overload this inference to customize this behavior. So this commit extracts the inlined inference logic back out into a separate `abstract_eval_basic_statement` method.
1 parent fd213c3 commit 956422e

File tree

1 file changed

+115
-82
lines changed

1 file changed

+115
-82
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 115 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3659,6 +3659,107 @@ function abstract_eval_ssavalue(s::SSAValue, ssavaluetypes::Vector{Any})
36593659
return typ
36603660
end
36613661

3662+
struct AbstractEvalBasicStatementResult
3663+
rt
3664+
exct
3665+
effects::Union{Nothing,Effects}
3666+
changes::Union{Nothing,StateUpdate}
3667+
refinements # ::Union{Nothing,SlotRefinement,Vector{Any}}
3668+
currsaw_latestworld::Bool
3669+
function AbstractEvalBasicStatementResult(rt, exct, effects::Union{Nothing,Effects},
3670+
changes::Union{Nothing,StateUpdate}, refinements, currsaw_latestworld::Bool)
3671+
@nospecialize rt exct refinements
3672+
return new(rt, exct, effects, changes, refinements, currsaw_latestworld)
3673+
end
3674+
end
3675+
3676+
function abstract_eval_basic_statement(interp::AbstractInterpreter, @nospecialize(stmt), sstate::StatementState, frame::InferenceState,
3677+
result::Union{Nothing,Future{RTEffects}}=nothing)
3678+
rt = nothing
3679+
exct = Bottom
3680+
changes = nothing
3681+
refinements = nothing
3682+
effects = nothing
3683+
currsaw_latestworld = sstate.saw_latestworld
3684+
if result !== nothing
3685+
@goto injectresult
3686+
end
3687+
if isa(stmt, NewvarNode)
3688+
changes = StateUpdate(stmt.slot, VarState(Bottom, true))
3689+
elseif isa(stmt, PhiNode)
3690+
add_curr_ssaflag!(frame, IR_FLAGS_REMOVABLE)
3691+
# Implement convergence for PhiNodes. In particular, PhiNodes need to tmerge over
3692+
# the incoming values from all iterations, but `abstract_eval_phi` will only tmerge
3693+
# over the first and last iterations. By tmerging in the current old_rt, we ensure that
3694+
# we will not lose an intermediate value.
3695+
rt = abstract_eval_phi(interp, stmt, sstate, frame)
3696+
old_rt = frame.ssavaluetypes[frame.currpc]
3697+
rt = old_rt === NOT_FOUND ? rt : tmerge(typeinf_lattice(interp), old_rt, rt)
3698+
else
3699+
lhs = nothing
3700+
if isexpr(stmt, :(=))
3701+
lhs = stmt.args[1]
3702+
stmt = stmt.args[2]
3703+
end
3704+
if !isa(stmt, Expr)
3705+
(; rt, exct, effects, refinements) = abstract_eval_special_value(interp, stmt, sstate, frame)
3706+
else
3707+
hd = stmt.head
3708+
if hd === :method
3709+
fname = stmt.args[1]
3710+
if isa(fname, SlotNumber)
3711+
changes = StateUpdate(fname, VarState(Any, false))
3712+
end
3713+
elseif (hd === :code_coverage_effect ||
3714+
# :boundscheck can be narrowed to Bool
3715+
(hd !== :boundscheck && is_meta_expr(stmt)))
3716+
rt = Nothing
3717+
elseif hd === :latestworld
3718+
currsaw_latestworld = true
3719+
rt = Nothing
3720+
else
3721+
result = abstract_eval_statement_expr(interp, stmt, sstate, frame)::Future{RTEffects}
3722+
if !isready(result) || !isempty(frame.tasks)
3723+
return result
3724+
3725+
@label injectresult
3726+
# reload local variables
3727+
lhs = nothing
3728+
if isexpr(stmt, :(=))
3729+
lhs = stmt.args[1]
3730+
stmt = stmt.args[2]
3731+
end
3732+
end
3733+
result = result[]
3734+
(; rt, exct, effects, refinements) = result
3735+
if effects.noub === NOUB_IF_NOINBOUNDS
3736+
if has_curr_ssaflag(frame, IR_FLAG_INBOUNDS)
3737+
effects = Effects(effects; noub=ALWAYS_FALSE)
3738+
elseif !propagate_inbounds(frame)
3739+
# The callee read our inbounds flag, but unless we propagate inbounds,
3740+
# we ourselves don't read our parent's inbounds.
3741+
effects = Effects(effects; noub=ALWAYS_TRUE)
3742+
end
3743+
end
3744+
@assert !isa(rt, TypeVar) "unhandled TypeVar"
3745+
rt = maybe_singleton_const(rt)
3746+
if !isempty(frame.pclimitations)
3747+
if rt isa Const || rt === Union{}
3748+
empty!(frame.pclimitations)
3749+
else
3750+
rt = LimitedAccuracy(rt, frame.pclimitations)
3751+
frame.pclimitations = IdSet{InferenceState}()
3752+
end
3753+
end
3754+
end
3755+
end
3756+
if lhs !== nothing && rt !== Bottom
3757+
changes = StateUpdate(lhs::SlotNumber, VarState(rt, false))
3758+
end
3759+
end
3760+
return AbstractEvalBasicStatementResult(rt, exct, effects, changes, refinements, currsaw_latestworld)
3761+
end
3762+
36623763
struct BestguessInfo{Interp<:AbstractInterpreter}
36633764
interp::Interp
36643765
bestguess
@@ -3940,14 +4041,16 @@ end
39404041

39414042
# make as much progress on `frame` as possible (without handling cycles)
39424043
struct CurrentState
3943-
result::Future
4044+
result::Future{RTEffects}
39444045
currstate::VarTable
39454046
currsaw_latestworld::Bool
39464047
bbstart::Int
39474048
bbend::Int
3948-
CurrentState(result::Future, currstate::VarTable, currsaw_latestworld::Bool, bbstart::Int, bbend::Int) = new(result, currstate, currsaw_latestworld, bbstart, bbend)
4049+
CurrentState(result::Future{RTEffects}, currstate::VarTable, currsaw_latestworld::Bool, bbstart::Int, bbend::Int) =
4050+
new(result, currstate, currsaw_latestworld, bbstart, bbend)
39494051
CurrentState() = new()
39504052
end
4053+
39514054
function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextresult::CurrentState)
39524055
@assert !is_inferred(frame)
39534056
W = frame.ip
@@ -3966,7 +4069,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr
39664069
bbend = nextresult.bbend
39674070
currstate = nextresult.currstate
39684071
currsaw_latestworld = nextresult.currsaw_latestworld
3969-
@goto injectresult
4072+
stmt = frame.src.code[currpc]
4073+
result = abstract_eval_basic_statement(interp, stmt, StatementState(currstate, currsaw_latestworld), frame, nextresult.result)
4074+
@goto injected_result
39704075
end
39714076

39724077
if currbb != 1
@@ -4119,87 +4224,15 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr
41194224
end
41204225
# Process non control-flow statements
41214226
@assert isempty(frame.tasks)
4122-
rt = nothing
4123-
exct = Bottom
4124-
changes = nothing
4125-
refinements = nothing
4126-
effects = nothing
4127-
if isa(stmt, NewvarNode)
4128-
changes = StateUpdate(stmt.slot, VarState(Bottom, true))
4129-
elseif isa(stmt, PhiNode)
4130-
add_curr_ssaflag!(frame, IR_FLAGS_REMOVABLE)
4131-
# Implement convergence for PhiNodes. In particular, PhiNodes need to tmerge over
4132-
# the incoming values from all iterations, but `abstract_eval_phi` will only tmerge
4133-
# over the first and last iterations. By tmerging in the current old_rt, we ensure that
4134-
# we will not lose an intermediate value.
4135-
rt = abstract_eval_phi(interp, stmt, StatementState(currstate, currsaw_latestworld), frame)
4136-
old_rt = frame.ssavaluetypes[currpc]
4137-
rt = old_rt === NOT_FOUND ? rt : tmerge(typeinf_lattice(interp), old_rt, rt)
4227+
sstate = StatementState(currstate, currsaw_latestworld)
4228+
result = abstract_eval_basic_statement(interp, stmt, sstate, frame)
4229+
if result isa Future{RTEffects}
4230+
return CurrentState(result, currstate, currsaw_latestworld, bbstart, bbend)
41384231
else
4139-
lhs = nothing
4140-
if isexpr(stmt, :(=))
4141-
lhs = stmt.args[1]
4142-
stmt = stmt.args[2]
4143-
end
4144-
if !isa(stmt, Expr)
4145-
(; rt, exct, effects, refinements) = abstract_eval_special_value(interp, stmt, StatementState(currstate, currsaw_latestworld), frame)
4146-
else
4147-
hd = stmt.head
4148-
if hd === :method
4149-
fname = stmt.args[1]
4150-
if isa(fname, SlotNumber)
4151-
changes = StateUpdate(fname, VarState(Any, false))
4152-
end
4153-
elseif (hd === :code_coverage_effect || (
4154-
hd !== :boundscheck && # :boundscheck can be narrowed to Bool
4155-
is_meta_expr(stmt)))
4156-
rt = Nothing
4157-
elseif hd === :latestworld
4158-
currsaw_latestworld = true
4159-
rt = Nothing
4160-
else
4161-
result = abstract_eval_statement_expr(interp, stmt, StatementState(currstate, currsaw_latestworld), frame)::Future
4162-
if !isready(result) || !isempty(frame.tasks)
4163-
return CurrentState(result, currstate, currsaw_latestworld, bbstart, bbend)
4164-
@label injectresult
4165-
# reload local variables
4166-
stmt = frame.src.code[currpc]
4167-
changes = nothing
4168-
lhs = nothing
4169-
if isexpr(stmt, :(=))
4170-
lhs = stmt.args[1]
4171-
stmt = stmt.args[2]
4172-
end
4173-
result = nextresult.result::Future{RTEffects}
4174-
end
4175-
result = result[]
4176-
(; rt, exct, effects, refinements) = result
4177-
if effects.noub === NOUB_IF_NOINBOUNDS
4178-
if has_curr_ssaflag(frame, IR_FLAG_INBOUNDS)
4179-
effects = Effects(effects; noub=ALWAYS_FALSE)
4180-
elseif !propagate_inbounds(frame)
4181-
# The callee read our inbounds flag, but unless we propagate inbounds,
4182-
# we ourselves don't read our parent's inbounds.
4183-
effects = Effects(effects; noub=ALWAYS_TRUE)
4184-
end
4185-
end
4186-
@assert !isa(rt, TypeVar) "unhandled TypeVar"
4187-
rt = maybe_singleton_const(rt)
4188-
if !isempty(frame.pclimitations)
4189-
if rt isa Const || rt === Union{}
4190-
empty!(frame.pclimitations)
4191-
else
4192-
rt = LimitedAccuracy(rt, frame.pclimitations)
4193-
frame.pclimitations = IdSet{InferenceState}()
4194-
end
4195-
end
4196-
end
4197-
end
4198-
effects === nothing || merge_override_effects!(interp, effects, frame)
4199-
if lhs !== nothing && rt !== Bottom
4200-
changes = StateUpdate(lhs::SlotNumber, VarState(rt, false))
4201-
end
4232+
@label injected_result
4233+
(; rt, exct, effects, changes, refinements, currsaw_latestworld) = result
42024234
end
4235+
effects === nothing || merge_override_effects!(interp, effects, frame)
42034236
if !has_curr_ssaflag(frame, IR_FLAG_NOTHROW)
42044237
if exct !== Union{}
42054238
update_exc_bestguess!(interp, exct, frame)

0 commit comments

Comments
 (0)