Skip to content

Commit e6d9514

Browse files
committed
lattice: fix minor lattice issues
I found some lattice issues when implementing `MustAlias` under the new extendable lattice system in another PR.
1 parent 18e7f40 commit e6d9514

File tree

2 files changed

+130
-71
lines changed

2 files changed

+130
-71
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 127 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ end
5353
function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
5454
arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype),
5555
sv::InferenceState, max_methods::Int)
56-
= (typeinf_lattice(interp))
56+
= (ipo_lattice(interp))
5757
if !should_infer_this_call(sv)
5858
add_remark!(interp, sv, "Skipped call in throw block")
5959
nonoverlayed = false
@@ -134,7 +134,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
134134
result, f, this_arginfo, si, match, sv)
135135
const_result = nothing
136136
if const_call_result !== nothing
137-
if const_call_result.rt rt
137+
if const_call_result.rt rt
138138
rt = const_call_result.rt
139139
(; effects, const_result, edge) = const_call_result
140140
end
@@ -167,7 +167,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
167167
this_const_rt = widenwrappedconditional(const_call_result.rt)
168168
# return type of const-prop' inference can be wider than that of non const-prop' inference
169169
# e.g. in cases when there are cycles but cached result is still accurate
170-
if this_const_rt this_rt
170+
if this_const_rt this_rt
171171
this_conditional = this_const_conditional
172172
this_rt = this_const_rt
173173
(; effects, const_result, edge) = const_call_result
@@ -2399,19 +2399,52 @@ function abstract_eval_ssavalue(s::SSAValue, ssavaluetypes::Vector{Any})
23992399
return typ
24002400
end
24012401

2402-
function widenreturn(ipo_lattice::AbstractLattice, @nospecialize(rt), @nospecialize(bestguess), nargs::Int, slottypes::Vector{Any}, changes::VarTable)
2403-
= (ipo_lattice)
2404-
inner_lattice = widenlattice(ipo_lattice)
2405-
= (inner_lattice)
2406-
if !(bestguess ₚ Bool) || bestguess === Bool
2402+
struct BestguessInfo{Interp<:AbstractInterpreter}
2403+
interp::Interp
2404+
bestguess
2405+
nargs::Int
2406+
slottypes::Vector{Any}
2407+
changes::VarTable
2408+
function BestguessInfo(interp::Interp, @nospecialize(bestguess), nargs::Int,
2409+
slottypes::Vector{Any}, changes::VarTable) where Interp<:AbstractInterpreter
2410+
new{Interp}(interp, bestguess, nargs, slottypes, changes)
2411+
end
2412+
end
2413+
2414+
"""
2415+
widenreturn(@nospecialize(rt), info::BestguessInfo) -> new_bestguess
2416+
2417+
Appropriately converts inferred type of a return value `rt` to such a type
2418+
that we know we can store in the cache and is valid and good inter-procedurally,
2419+
E.g. if `rt isa Conditional` then `rt` should be converted to `InterConditional`
2420+
or the other cachable lattice element.
2421+
2422+
External lattice `𝕃ₑ::ExternalLattice` may overload:
2423+
- `widenreturn(𝕃ₑ::ExternalLattice, @nospecialize(rt), info::BestguessInfo)`
2424+
- `widenreturn_noslotwrapper(𝕃ₑ::ExternalLattice, @nospecialize(rt), info::BestguessInfo)`
2425+
"""
2426+
function widenreturn(@nospecialize(rt), info::BestguessInfo)
2427+
return widenreturn(typeinf_lattice(info.interp), rt, info)
2428+
end
2429+
2430+
function widenreturn(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
2431+
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
2432+
end
2433+
function widenreturn_noslotwrapper(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
2434+
return widenreturn_noslotwrapper(widenlattice(𝕃ᵢ), rt, info)
2435+
end
2436+
2437+
function widenreturn(𝕃ᵢ::ConditionalsLattice, @nospecialize(rt), info::BestguessInfo)
2438+
= (𝕃ᵢ)
2439+
if !((ipo_lattice(info.interp), info.bestguess, Bool)) || info.bestguess === Bool
24072440
# give up inter-procedural constraint back-propagation
24082441
# when tmerge would widen the result anyways (as an optimization)
24092442
rt = widenconditional(rt)
24102443
else
24112444
if isa(rt, Conditional)
24122445
id = rt.slot
2413-
if 1 id nargs
2414-
old_id_type = widenconditional(slottypes[id]) # same as `(states[1]::VarTable)[id].typ`
2446+
if 1 id info.nargs
2447+
old_id_type = widenconditional(info.slottypes[id]) # same as `(states[1]::VarTable)[id].typ`
24152448
if (!(rt.thentype ᵢ old_id_type) || old_id_type ᵢ rt.thentype) &&
24162449
(!(rt.elsetype ᵢ old_id_type) || old_id_type ᵢ rt.elsetype)
24172450
# discard this `Conditional` since it imposes
@@ -2428,44 +2461,69 @@ function widenreturn(ipo_lattice::AbstractLattice, @nospecialize(rt), @nospecial
24282461
end
24292462
if isa(rt, Conditional)
24302463
rt = InterConditional(rt.slot, rt.thentype, rt.elsetype)
2431-
elseif is_lattice_bool(ipo_lattice, rt)
2432-
if isa(bestguess, InterConditional)
2433-
# if the bestguess so far is already `Conditional`, try to convert
2434-
# this `rt` into `Conditional` on the slot to avoid overapproximation
2435-
# due to conflict of different slots
2436-
rt = bool_rt_to_conditional(rt, slottypes, changes, bestguess.slot)
2437-
else
2438-
# pick up the first "interesting" slot, convert `rt` to its `Conditional`
2439-
# TODO: ideally we want `Conditional` and `InterConditional` to convey
2440-
# constraints on multiple slots
2441-
for slot_id in 1:nargs
2442-
rt = bool_rt_to_conditional(rt, slottypes, changes, slot_id)
2443-
rt isa InterConditional && break
2444-
end
2445-
end
2464+
elseif is_lattice_bool(𝕃ᵢ, rt)
2465+
rt = bool_rt_to_conditional(rt, info)
24462466
end
24472467
end
2448-
2449-
# only propagate information we know we can store
2450-
# and is valid and good inter-procedurally
24512468
isa(rt, Conditional) && return InterConditional(rt)
24522469
isa(rt, InterConditional) && return rt
2453-
return widenreturn_noconditional(widenlattice(ipo_lattice), rt)
2470+
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
2471+
end
2472+
function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
2473+
bestguess = info.bestguess
2474+
if isa(bestguess, InterConditional)
2475+
# if the bestguess so far is already `Conditional`, try to convert
2476+
# this `rt` into `Conditional` on the slot to avoid overapproximation
2477+
# due to conflict of different slots
2478+
rt = bool_rt_to_conditional(rt, bestguess.slot, info)
2479+
else
2480+
# pick up the first "interesting" slot, convert `rt` to its `Conditional`
2481+
# TODO: ideally we want `Conditional` and `InterConditional` to convey
2482+
# constraints on multiple slots
2483+
for slot_id = 1:info.nargs
2484+
rt = bool_rt_to_conditional(rt, slot_id, info)
2485+
rt isa InterConditional && break
2486+
end
2487+
end
2488+
return rt
2489+
end
2490+
function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::BestguessInfo)
2491+
= (typeinf_lattice(info.interp))
2492+
old = info.slottypes[slot_id]
2493+
new = widenconditional(info.changes[slot_id].typ) # avoid nested conditional
2494+
if new ᵢ old && !(old ᵢ new)
2495+
if isa(rt, Const)
2496+
val = rt.val
2497+
if val === true
2498+
return InterConditional(slot_id, new, Bottom)
2499+
elseif val === false
2500+
return InterConditional(slot_id, Bottom, new)
2501+
end
2502+
elseif rt === Bool
2503+
return InterConditional(slot_id, new, new)
2504+
end
2505+
end
2506+
return rt
24542507
end
24552508

2456-
function widenreturn_noconditional(inner_lattice::AbstractLattice, @nospecialize(rt))
2457-
isa(rt, Const) && return rt
2458-
isa(rt, Type) && return rt
2509+
function widenreturn(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
2510+
return widenreturn_partials(𝕃ᵢ, rt, info)
2511+
end
2512+
function widenreturn_noslotwrapper(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
2513+
return widenreturn_partials(𝕃ᵢ, rt, info)
2514+
end
2515+
function widenreturn_partials(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
24592516
if isa(rt, PartialStruct)
24602517
fields = copy(rt.fields)
24612518
local anyrefine = false
2519+
𝕃 = typeinf_lattice(info.interp)
24622520
for i in 1:length(fields)
24632521
a = fields[i]
2464-
a = isvarargtype(a) ? a : widenreturn_noconditional(inner_lattice, a)
2522+
a = isvarargtype(a) ? a : widenreturn_noslotwrapper(𝕃, a, info)
24652523
if !anyrefine
24662524
# TODO: consider adding && const_prop_profitable(a) here?
24672525
anyrefine = has_const_info(a) ||
2468-
(inner_lattice, a, fieldtype(rt.typ, i))
2526+
(𝕃, a, fieldtype(rt.typ, i))
24692527
end
24702528
fields[i] = a
24712529
end
@@ -2474,6 +2532,24 @@ function widenreturn_noconditional(inner_lattice::AbstractLattice, @nospecialize
24742532
if isa(rt, PartialOpaque)
24752533
return rt # XXX: this case was missed in #39512
24762534
end
2535+
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
2536+
end
2537+
2538+
function widenreturn(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
2539+
return widenreturn_consts(rt)
2540+
end
2541+
function widenreturn_noslotwrapper(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
2542+
return widenreturn_consts(rt)
2543+
end
2544+
function widenreturn_consts(@nospecialize(rt))
2545+
isa(rt, Const) && return rt
2546+
return widenconst(rt)
2547+
end
2548+
2549+
function widenreturn(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
2550+
return widenconst(rt)
2551+
end
2552+
function widenreturn_noslotwrapper(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
24772553
return widenconst(rt)
24782554
end
24792555

@@ -2537,15 +2613,15 @@ end
25372613
end
25382614
end
25392615

2540-
function update_bbstate!(lattice::AbstractLattice, frame::InferenceState, bb::Int, vartable::VarTable)
2616+
function update_bbstate!(𝕃ᵢ::AbstractLattice, frame::InferenceState, bb::Int, vartable::VarTable)
25412617
bbtable = frame.bb_vartables[bb]
25422618
if bbtable === nothing
25432619
# if a basic block hasn't been analyzed yet,
25442620
# we can update its state a bit more aggressively
25452621
frame.bb_vartables[bb] = copy(vartable)
25462622
return true
25472623
else
2548-
return stupdate!(lattice, bbtable, vartable)
2624+
return stupdate!(𝕃ᵢ, bbtable, vartable)
25492625
end
25502626
end
25512627

@@ -2567,6 +2643,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
25672643
ssavaluetypes = frame.ssavaluetypes
25682644
bbs = frame.cfg.blocks
25692645
nbbs = length(bbs)
2646+
𝕃ₚ, 𝕃ᵢ = ipo_lattice(interp), typeinf_lattice(interp)
25702647

25712648
currbb = frame.currbb
25722649
if currbb != 1
@@ -2636,19 +2713,19 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
26362713
# We continue with the true branch, but process the false
26372714
# branch here.
26382715
if isa(condt, Conditional)
2639-
else_change = conditional_change(currstate, condt.elsetype, condt.slot)
2716+
else_change = conditional_change(𝕃ᵢ, currstate, condt.elsetype, condt.slot)
26402717
if else_change !== nothing
26412718
false_vartable = stoverwrite1!(copy(currstate), else_change)
26422719
else
26432720
false_vartable = currstate
26442721
end
2645-
changed = update_bbstate!(typeinf_lattice(interp), frame, falsebb, false_vartable)
2646-
then_change = conditional_change(currstate, condt.thentype, condt.slot)
2722+
changed = update_bbstate!(𝕃ᵢ, frame, falsebb, false_vartable)
2723+
then_change = conditional_change(𝕃ᵢ, currstate, condt.thentype, condt.slot)
26472724
if then_change !== nothing
26482725
stoverwrite1!(currstate, then_change)
26492726
end
26502727
else
2651-
changed = update_bbstate!(typeinf_lattice(interp), frame, falsebb, currstate)
2728+
changed = update_bbstate!(𝕃ᵢ, frame, falsebb, currstate)
26522729
end
26532730
if changed
26542731
handle_control_backedge!(interp, frame, currpc, stmt.dest)
@@ -2660,7 +2737,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
26602737
elseif isa(stmt, ReturnNode)
26612738
bestguess = frame.bestguess
26622739
rt = abstract_eval_value(interp, stmt.val, currstate, frame)
2663-
rt = widenreturn(ipo_lattice(interp), rt, bestguess, nargs, slottypes, currstate)
2740+
rt = widenreturn(rt, BestguessInfo(interp, bestguess, nargs, slottypes, currstate))
26642741
# narrow representation of bestguess slightly to prepare for tmerge with rt
26652742
if rt isa InterConditional && bestguess isa Const
26662743
let slot_id = rt.slot
@@ -2680,9 +2757,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
26802757
if !isempty(frame.limitations)
26812758
rt = LimitedAccuracy(rt, copy(frame.limitations))
26822759
end
2683-
if tchanged(ipo_lattice(interp), rt, bestguess)
2760+
if tchanged(𝕃ₚ, rt, bestguess)
26842761
# new (wider) return type for frame
2685-
bestguess = tmerge(ipo_lattice(interp), bestguess, rt)
2762+
bestguess = tmerge(𝕃ₚ, bestguess, rt)
26862763
# TODO: if bestguess isa InterConditional && !interesting(bestguess); bestguess = widenconditional(bestguess); end
26872764
frame.bestguess = bestguess
26882765
for (caller, caller_pc) in frame.cycle_backedges
@@ -2698,7 +2775,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
26982775
# Propagate entry info to exception handler
26992776
l = stmt.args[1]::Int
27002777
catchbb = block_for_inst(frame.cfg, l)
2701-
if update_bbstate!(typeinf_lattice(interp), frame, catchbb, currstate)
2778+
if update_bbstate!(𝕃ᵢ, frame, catchbb, currstate)
27022779
push!(W, catchbb)
27032780
end
27042781
ssavaluetypes[currpc] = Any
@@ -2723,7 +2800,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
27232800
# propagate new type info to exception handler
27242801
# the handling for Expr(:enter) propagates all changes from before the try/catch
27252802
# so this only needs to propagate any changes
2726-
if stupdate1!(typeinf_lattice(interp), states[exceptbb]::VarTable, changes)
2803+
if stupdate1!(𝕃ᵢ, states[exceptbb]::VarTable, changes)
27272804
push!(W, exceptbb)
27282805
end
27292806
cur_hand = frame.handler_at[cur_hand]
@@ -2735,7 +2812,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
27352812
continue
27362813
end
27372814
if !isempty(frame.ssavalue_uses[currpc])
2738-
record_ssa_assign!(currpc, type, frame)
2815+
record_ssa_assign!(𝕃ᵢ, currpc, type, frame)
27392816
else
27402817
ssavaluetypes[currpc] = type
27412818
end
@@ -2748,7 +2825,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
27482825

27492826
# Case 2: Directly branch to a different BB
27502827
begin @label branch
2751-
if update_bbstate!(typeinf_lattice(interp), frame, nextbb, currstate)
2828+
if update_bbstate!(𝕃ᵢ, frame, nextbb, currstate)
27522829
push!(W, nextbb)
27532830
end
27542831
end
@@ -2772,13 +2849,13 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
27722849
nothing
27732850
end
27742851

2775-
function conditional_change(state::VarTable, @nospecialize(typ), slot::Int)
2852+
function conditional_change(𝕃ᵢ::AbstractLattice, state::VarTable, @nospecialize(typ), slot::Int)
27762853
vtype = state[slot]
27772854
oldtyp = vtype.typ
27782855
if iskindtype(typ)
27792856
# this code path corresponds to the special handling for `isa(x, iskindtype)` check
27802857
# implemented within `abstract_call_builtin`
2781-
elseif ignorelimited(typ) ignorelimited(oldtyp)
2858+
elseif (𝕃ᵢ, ignorelimited(typ), ignorelimited(oldtyp))
27822859
# approximate test for `typ ∩ oldtyp` being better than `oldtyp`
27832860
# since we probably formed these types with `typesubstract`,
27842861
# the comparison is likely simple
@@ -2788,29 +2865,11 @@ function conditional_change(state::VarTable, @nospecialize(typ), slot::Int)
27882865
if oldtyp isa LimitedAccuracy
27892866
# typ is better unlimited, but we may still need to compute the tmeet with the limit
27902867
# "causes" since we ignored those in the comparison
2791-
typ = tmerge(typ, LimitedAccuracy(Bottom, oldtyp.causes))
2868+
typ = tmerge(𝕃ᵢ, typ, LimitedAccuracy(Bottom, oldtyp.causes))
27922869
end
27932870
return StateUpdate(SlotNumber(slot), VarState(typ, vtype.undef), state, true)
27942871
end
27952872

2796-
function bool_rt_to_conditional(@nospecialize(rt), slottypes::Vector{Any}, state::VarTable, slot_id::Int)
2797-
old = slottypes[slot_id]
2798-
new = widenconditional(state[slot_id].typ) # avoid nested conditional
2799-
if new old && !(old new)
2800-
if isa(rt, Const)
2801-
val = rt.val
2802-
if val === true
2803-
return InterConditional(slot_id, new, Bottom)
2804-
elseif val === false
2805-
return InterConditional(slot_id, Bottom, new)
2806-
end
2807-
elseif rt === Bool
2808-
return InterConditional(slot_id, new, new)
2809-
end
2810-
end
2811-
return rt
2812-
end
2813-
28142873
# make as much progress on `frame` as possible (by handling cycles)
28152874
function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState)
28162875
typeinf_local(interp, frame)

base/compiler/inferencestate.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,14 +448,14 @@ end
448448

449449
update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(sv, edge.valid_worlds)
450450

451-
function record_ssa_assign!(ssa_id::Int, @nospecialize(new), frame::InferenceState)
451+
function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState)
452452
ssavaluetypes = frame.ssavaluetypes
453453
old = ssavaluetypes[ssa_id]
454-
if old === NOT_FOUND || !(new old)
454+
if old === NOT_FOUND || !(𝕃ᵢ, new, old)
455455
# typically, we expect that old ⊑ new (that output information only
456456
# gets less precise with worse input information), but to actually
457457
# guarantee convergence we need to use tmerge here to ensure that is true
458-
ssavaluetypes[ssa_id] = old === NOT_FOUND ? new : tmerge(old, new)
458+
ssavaluetypes[ssa_id] = old === NOT_FOUND ? new : tmerge(𝕃ᵢ, old, new)
459459
W = frame.ip
460460
for r in frame.ssavalue_uses[ssa_id]
461461
if was_reached(frame, r)

0 commit comments

Comments
 (0)