Skip to content

Commit 18e7f40

Browse files
authored
lattice: not introduce Conditionals when external lattice doesn't handle it (#47555)
This slightly increases the complexity but hopefully comes without any actual performance penalty.
1 parent d0559c1 commit 18e7f40

File tree

7 files changed

+103
-58
lines changed

7 files changed

+103
-58
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
107107
val = pure_eval_call(interp, f, applicable, arginfo)
108108
val !== nothing && return CallMeta(val, all_effects, MethodResultPure(info)) # TODO: add some sort of edge(s)
109109

110+
𝕃ₚ = ipo_lattice(interp)
110111
for i in 1:napplicable
111112
match = applicable[i]::MethodMatch
112113
method = match.method
@@ -179,8 +180,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
179180
end
180181
@assert !(this_conditional isa Conditional) "invalid lattice element returned from inter-procedural context"
181182
seen += 1
182-
rettype = tmerge(ipo_lattice(interp), rettype, this_rt)
183-
if this_conditional !== Bottom && is_lattice_bool(ipo_lattice(interp), rettype) && fargs !== nothing
183+
rettype = tmerge(𝕃ₚ, rettype, this_rt)
184+
if has_conditional(𝕃ₚ) && this_conditional !== Bottom && is_lattice_bool(𝕃ₚ, rettype) && fargs !== nothing
184185
if conditionals === nothing
185186
conditionals = Any[Bottom for _ in 1:length(argtypes)],
186187
Any[Bottom for _ in 1:length(argtypes)]
@@ -211,7 +212,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
211212
all_effects = Effects(all_effects; nothrow=false)
212213
end
213214

214-
rettype = from_interprocedural!(ipo_lattice(interp), rettype, sv, arginfo, conditionals)
215+
rettype = from_interprocedural!(𝕃ₚ, rettype, sv, arginfo, conditionals)
215216

216217
# Also considering inferring the compilation signature for this method, so
217218
# it is available to the compiler in case it ends up needing it.
@@ -349,7 +350,7 @@ function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), meth
349350
end
350351

351352
"""
352-
from_interprocedural!(ipo_lattice::AbstractLattice, rt, sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> newrt
353+
from_interprocedural!(𝕃ₚ::AbstractLattice, rt, sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> newrt
353354
354355
Converts inter-procedural return type `rt` into a local lattice element `newrt`,
355356
that is appropriate in the context of current local analysis frame `sv`, especially:
@@ -368,13 +369,13 @@ In such cases `maybecondinfo` should be either of:
368369
When we deal with multiple `MethodMatch`es, it's better to precompute `maybecondinfo` by
369370
`tmerge`ing argument signature type of each method call.
370371
"""
371-
function from_interprocedural!(ipo_lattice::AbstractLattice, @nospecialize(rt), sv::InferenceState, arginfo::ArgInfo, @nospecialize(maybecondinfo))
372+
function from_interprocedural!(𝕃ₚ::AbstractLattice, @nospecialize(rt), sv::InferenceState, arginfo::ArgInfo, @nospecialize(maybecondinfo))
372373
rt = collect_limitations!(rt, sv)
373-
if is_lattice_bool(ipo_lattice, rt)
374+
if is_lattice_bool(𝕃ₚ, rt)
374375
if maybecondinfo === nothing
375376
rt = widenconditional(rt)
376377
else
377-
rt = from_interconditional(ipo_lattice, rt, sv, arginfo, maybecondinfo)
378+
rt = from_interconditional(𝕃ₚ, rt, sv, arginfo, maybecondinfo)
378379
end
379380
end
380381
@assert !(rt isa InterConditional) "invalid lattice element returned from inter-procedural context"
@@ -389,9 +390,10 @@ function collect_limitations!(@nospecialize(typ), sv::InferenceState)
389390
return typ
390391
end
391392

392-
function from_interconditional(ipo_lattice::AbstractLattice, @nospecialize(typ),
393+
function from_interconditional(𝕃ₚ::AbstractLattice, @nospecialize(typ),
393394
sv::InferenceState, (; fargs, argtypes)::ArgInfo, @nospecialize(maybecondinfo))
394-
lattice = widenlattice(ipo_lattice)
395+
𝕃 = widenlattice(𝕃ₚ)
396+
has_conditional(𝕃ₚ) || return widenconditional(typ)
395397
fargs === nothing && return widenconditional(typ)
396398
slot = 0
397399
thentype = elsetype = Any
@@ -417,21 +419,21 @@ function from_interconditional(ipo_lattice::AbstractLattice, @nospecialize(typ),
417419
end
418420
if condval === false
419421
thentype = Bottom
420-
elseif (lattice, new_thentype, thentype)
422+
elseif (𝕃, new_thentype, thentype)
421423
thentype = new_thentype
422424
else
423-
thentype = tmeet(lattice, thentype, widenconst(new_thentype))
425+
thentype = tmeet(𝕃, thentype, widenconst(new_thentype))
424426
end
425427
if condval === true
426428
elsetype = Bottom
427-
elseif (lattice, new_elsetype, elsetype)
429+
elseif (𝕃, new_elsetype, elsetype)
428430
elsetype = new_elsetype
429431
else
430-
elsetype = tmeet(lattice, elsetype, widenconst(new_elsetype))
432+
elsetype = tmeet(𝕃, elsetype, widenconst(new_elsetype))
431433
end
432-
if (slot > 0 || condval !== false) && (lattice, thentype, old)
434+
if (slot > 0 || condval !== false) && (𝕃, thentype, old)
433435
slot = id
434-
elseif (slot > 0 || condval !== true) && (lattice, elsetype, old)
436+
elseif (slot > 0 || condval !== true) && (𝕃, elsetype, old)
435437
slot = id
436438
else # reset: no new useful information for this slot
437439
thentype = elsetype = Any
@@ -847,8 +849,8 @@ function concrete_eval_call(interp::AbstractInterpreter,
847849
end
848850
end
849851

850-
has_conditional(argtypes::Vector{Any}) = any(@nospecialize(x)->isa(x, Conditional), argtypes)
851-
has_conditional((; argtypes)::ArgInfo) = has_conditional(argtypes)
852+
any_conditional(argtypes::Vector{Any}) = any(@nospecialize(x)->isa(x, Conditional), argtypes)
853+
any_conditional(arginfo::ArgInfo) = any_conditional(arginfo.argtypes)
852854

853855
function const_prop_enabled(interp::AbstractInterpreter, sv::InferenceState, match::MethodMatch)
854856
if !InferenceParams(interp).ipo_constant_propagation
@@ -954,7 +956,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
954956
mi = maybe_get_const_prop_profitable(interp, result, f, arginfo, si, match, sv)
955957
mi === nothing && return nothing
956958
# try semi-concrete evaluation
957-
if res::Bool && !has_conditional(arginfo)
959+
if res::Bool && !any_conditional(arginfo)
958960
mi_cache = WorldView(code_cache(interp), sv.world)
959961
code = get(mi_cache, mi, nothing)
960962
if code !== nothing
@@ -970,7 +972,8 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
970972
end
971973
# try constant prop'
972974
inf_cache = get_inference_cache(interp)
973-
inf_result = cache_lookup(typeinf_lattice(interp), mi, arginfo.argtypes, inf_cache)
975+
𝕃ᵢ = typeinf_lattice(interp)
976+
inf_result = cache_lookup(𝕃ᵢ, mi, arginfo.argtypes, inf_cache)
974977
if inf_result === nothing
975978
# if there might be a cycle, check to make sure we don't end up
976979
# calling ourselves here.
@@ -983,7 +986,8 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
983986
add_remark!(interp, sv, "[constprop] Edge cycle encountered")
984987
return nothing
985988
end
986-
inf_result = InferenceResult(mi, ConditionalArgtypes(arginfo, sv))
989+
argtypes = has_conditional(𝕃ᵢ) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
990+
inf_result = InferenceResult(mi, argtypes)
987991
if !any(inf_result.overridden_by_const)
988992
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
989993
return nothing
@@ -1572,9 +1576,9 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs
15721576
sv::Union{InferenceState, IRCode}, max_methods::Int)
15731577
@nospecialize f
15741578
la = length(argtypes)
1575-
lattice = typeinf_lattice(interp)
1576-
= (lattice)
1577-
if f === Core.ifelse && fargs isa Vector{Any} && la == 4
1579+
𝕃ᵢ = typeinf_lattice(interp)
1580+
= (𝕃ᵢ)
1581+
if has_conditional(𝕃ᵢ) && f === Core.ifelse && fargs isa Vector{Any} && la == 4
15781582
cnd = argtypes[2]
15791583
if isa(cnd, Conditional)
15801584
newcnd = widenconditional(cnd)
@@ -1588,17 +1592,17 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs
15881592
a = ssa_def_slot(fargs[3], sv)
15891593
b = ssa_def_slot(fargs[4], sv)
15901594
if isa(a, SlotNumber) && cnd.slot == slot_id(a)
1591-
tx = (cnd.thentype ᵢ tx ? cnd.thentype : tmeet(lattice, tx, widenconst(cnd.thentype)))
1595+
tx = (cnd.thentype ᵢ tx ? cnd.thentype : tmeet(𝕃ᵢ, tx, widenconst(cnd.thentype)))
15921596
end
15931597
if isa(b, SlotNumber) && cnd.slot == slot_id(b)
1594-
ty = (cnd.elsetype ᵢ ty ? cnd.elsetype : tmeet(lattice, ty, widenconst(cnd.elsetype)))
1598+
ty = (cnd.elsetype ᵢ ty ? cnd.elsetype : tmeet(𝕃ᵢ, ty, widenconst(cnd.elsetype)))
15951599
end
1596-
return tmerge(lattice, tx, ty)
1600+
return tmerge(𝕃ᵢ, tx, ty)
15971601
end
15981602
end
15991603
end
16001604
rt = builtin_tfunction(interp, f, argtypes[2:end], sv)
1601-
if (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any})
1605+
if has_conditional(𝕃ᵢ) && (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any})
16021606
# perform very limited back-propagation of type information for `is` and `isa`
16031607
if f === isa
16041608
a = ssa_def_slot(fargs[2], sv)
@@ -1816,6 +1820,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
18161820
(; fargs, argtypes) = arginfo
18171821
la = length(argtypes)
18181822

1823+
𝕃ᵢ = typeinf_lattice(interp)
18191824
if isa(f, Builtin)
18201825
if f === _apply_iterate
18211826
return abstract_apply(interp, argtypes, si, sv, max_methods)
@@ -1827,7 +1832,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
18271832
return abstract_finalizer(interp, argtypes, sv)
18281833
end
18291834
rt = abstract_call_builtin(interp, f, arginfo, sv, max_methods)
1830-
effects = builtin_effects(typeinf_lattice(interp), f, argtypes[2:end], rt)
1835+
effects = builtin_effects(𝕃ᵢ, f, argtypes[2:end], rt)
18311836
return CallMeta(rt, effects, NoCallInfo())
18321837
elseif isa(f, Core.OpaqueClosure)
18331838
# calling an OpaqueClosure about which we have no information returns no information

base/compiler/abstractlattice.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ has_nontrivial_const_info(lattice::AbstractLattice, @nospecialize t) =
153153
has_nontrivial_const_info(widenlattice(lattice), t)
154154
has_nontrivial_const_info(::JLTypeLattice, @nospecialize(t)) = false
155155

156+
has_conditional(𝕃::AbstractLattice) = has_conditional(widenlattice(𝕃))
157+
has_conditional(::AnyConditionalsLattice) = true
158+
has_conditional(::JLTypeLattice) = false
159+
156160
# Curried versions
157161
(lattice::AbstractLattice) = (@nospecialize(a), @nospecialize(b)) -> (lattice, a, b)
158162
(lattice::AbstractLattice) = (@nospecialize(a), @nospecialize(b)) -> (lattice, a, b)

base/compiler/inferenceresult.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function matching_cache_argtypes(linfo::MethodInstance)
2828
return cache_argtypes, falses(length(cache_argtypes))
2929
end
3030

31-
struct SimpleArgtypes
31+
struct SimpleArgtypes <: ForwardableArgtypes
3232
argtypes::Vector{Any}
3333
end
3434

base/compiler/ssair/passes.jl

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ function lift_comparison! end
520520

521521
function lift_comparison!(::typeof(===), compact::IncrementalCompact,
522522
idx::Int, stmt::Expr, lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue},
523-
opt_lattice::AbstractLattice = OptimizerLattice())
523+
𝕃ₒ::AbstractLattice = OptimizerLattice())
524524
args = stmt.args
525525
length(args) == 3 || return
526526
lhs, rhs = args[2], args[3]
@@ -536,23 +536,24 @@ function lift_comparison!(::typeof(===), compact::IncrementalCompact,
536536
else
537537
return
538538
end
539-
lift_comparison_leaves!(egal_tfunc, compact, val, cmp, lifting_cache, idx)
539+
egal_tfunc_opt(@nospecialize(x), @nospecialize(y)) = egal_tfunc(𝕃ₒ, x, y)
540+
lift_comparison_leaves!(egal_tfunc_opt, compact, val, cmp, lifting_cache, idx)
540541
end
541542

542543
function lift_comparison!(::typeof(isa), compact::IncrementalCompact,
543544
idx::Int, stmt::Expr, lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue},
544-
opt_lattice::AbstractLattice = OptimizerLattice())
545+
𝕃ₒ::AbstractLattice = OptimizerLattice())
545546
args = stmt.args
546547
length(args) == 3 || return
547548
cmp = argextype(args[3], compact)
548549
val = args[2]
549-
isa_tfunc_opt(@nospecialize(v), @nospecialize(typ)) = isa_tfunc(opt_lattice, v, typ)
550+
isa_tfunc_opt(@nospecialize(v), @nospecialize(typ)) = isa_tfunc(𝕃ₒ, v, typ)
550551
lift_comparison_leaves!(isa_tfunc_opt, compact, val, cmp, lifting_cache, idx)
551552
end
552553

553554
function lift_comparison!(::typeof(isdefined), compact::IncrementalCompact,
554555
idx::Int, stmt::Expr, lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue},
555-
opt_lattice::AbstractLattice = OptimizerLattice())
556+
𝕃ₒ::AbstractLattice = OptimizerLattice())
556557
args = stmt.args
557558
length(args) == 3 || return
558559
cmp = argextype(args[3], compact)
@@ -852,7 +853,7 @@ In a case when all usages are fully eliminated, `struct` allocation may also be
852853
a result of succeeding dead code elimination.
853854
"""
854855
function sroa_pass!(ir::IRCode, inlining::Union{Nothing, InliningState} = nothing)
855-
opt_lattice = inlining === nothing ? OptimizerLattice() : optimizer_lattice(inlining.interp)
856+
𝕃ₒ = inlining === nothing ? OptimizerLattice() : optimizer_lattice(inlining.interp)
856857
compact = IncrementalCompact(ir)
857858
defuses = nothing # will be initialized once we encounter mutability in order to reduce dynamic allocations
858859
lifting_cache = IdDict{Pair{AnySSAValue, Any}, AnySSAValue}()
@@ -946,9 +947,9 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing, InliningState} = nothin
946947
elseif is_known_call(stmt, Core._svec_ref, compact)
947948
lift_svec_ref!(compact, idx, stmt)
948949
elseif is_known_call(stmt, (===), compact)
949-
lift_comparison!(===, compact, idx, stmt, lifting_cache, opt_lattice)
950+
lift_comparison!(===, compact, idx, stmt, lifting_cache, 𝕃ₒ)
950951
elseif is_known_call(stmt, isa, compact)
951-
lift_comparison!(isa, compact, idx, stmt, lifting_cache, opt_lattice)
952+
lift_comparison!(isa, compact, idx, stmt, lifting_cache, 𝕃ₒ)
952953
end
953954
continue
954955
end
@@ -968,7 +969,7 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing, InliningState} = nothin
968969
struct_typ = unswitchtupleunion(struct_typ)
969970
end
970971
if isa(struct_typ, Union) && is_isdefined
971-
lift_comparison!(isdefined, compact, idx, stmt, lifting_cache, opt_lattice)
972+
lift_comparison!(isdefined, compact, idx, stmt, lifting_cache, 𝕃ₒ)
972973
continue
973974
end
974975
isa(struct_typ, DataType) || continue

base/compiler/tfuncs.jl

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -226,27 +226,38 @@ function ifelse_nothrow(@specialize(𝕃::AbstractLattice), @nospecialize(cond),
226226
return cond Bool
227227
end
228228

229-
function egal_tfunc(@nospecialize(x), @nospecialize(y))
230-
xx = widenconditional(x)
231-
yy = widenconditional(y)
232-
if isa(x, Conditional) && isa(yy, Const)
233-
yy.val === false && return Conditional(x.slot, x.elsetype, x.thentype)
234-
yy.val === true && return x
235-
return Const(false)
236-
elseif isa(y, Conditional) && isa(xx, Const)
237-
xx.val === false && return Conditional(y.slot, y.elsetype, y.thentype)
238-
xx.val === true && return y
239-
return Const(false)
240-
elseif isa(xx, Const) && isa(yy, Const)
241-
return Const(xx.val === yy.val)
242-
elseif !hasintersect(widenconst(xx), widenconst(yy))
229+
egal_tfunc(@specialize(𝕃::AbstractLattice), @nospecialize(x), @nospecialize(y)) =
230+
egal_tfunc(widenlattice(𝕃), x, y)
231+
function egal_tfunc(@specialize(𝕃::ConditionalsLattice), @nospecialize(x), @nospecialize(y))
232+
if isa(x, Conditional)
233+
y = widenconditional(y)
234+
if isa(y, Const)
235+
y.val === false && return Conditional(x.slot, x.elsetype, x.thentype)
236+
y.val === true && return x
237+
return Const(false)
238+
end
239+
elseif isa(y, Conditional)
240+
x = widenconditional(x)
241+
if isa(x, Const)
242+
x.val === false && return Conditional(y.slot, y.elsetype, y.thentype)
243+
x.val === true && return y
244+
return Const(false)
245+
end
246+
end
247+
return egal_tfunc(widenlattice(𝕃), x, y)
248+
end
249+
function egal_tfunc(::ConstsLattice, @nospecialize(x), @nospecialize(y))
250+
if isa(x, Const) && isa(y, Const)
251+
return Const(x.val === y.val)
252+
elseif !hasintersect(widenconst(x), widenconst(y))
243253
return Const(false)
244-
elseif (isa(xx, Const) && y === typeof(xx.val) && isdefined(y, :instance)) ||
245-
(isa(yy, Const) && x === typeof(yy.val) && isdefined(x, :instance))
254+
elseif (isa(x, Const) && y === typeof(x.val) && isdefined(y, :instance)) ||
255+
(isa(y, Const) && x === typeof(y.val) && isdefined(x, :instance))
246256
return Const(true)
247257
end
248258
return Bool
249259
end
260+
egal_tfunc(::JLTypeLattice, @nospecialize(x), @nospecialize(y)) = Bool
250261
add_tfunc(===, 2, 2, egal_tfunc, 1)
251262

252263
function isdefined_nothrow(@specialize(𝕃::AbstractLattice), @nospecialize(x), @nospecialize(name))
@@ -2140,8 +2151,9 @@ end
21402151

21412152
function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtypes::Vector{Any},
21422153
sv::Union{InferenceState,IRCode,Nothing})
2154+
𝕃ᵢ = typeinf_lattice(interp)
21432155
if f === tuple
2144-
return tuple_tfunc(typeinf_lattice(interp), argtypes)
2156+
return tuple_tfunc(𝕃ᵢ, argtypes)
21452157
end
21462158
if isa(f, IntrinsicFunction)
21472159
if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isa(a, Const), argtypes)
@@ -2188,7 +2200,9 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp
21882200
return Bottom
21892201
end
21902202
if f === getfield
2191-
return getfield_tfunc(typeinf_lattice(interp), argtypes...)
2203+
return getfield_tfunc(𝕃ᵢ, argtypes...)
2204+
elseif f === (===)
2205+
return egal_tfunc(𝕃ᵢ, argtypes...)
21922206
end
21932207
return tf[3](argtypes...)
21942208
end

test/compiler/AbstractInterpreter.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,26 @@ end
241241

242242
# code_typed(ifelse, (Bool, Int, Int); interp=TaintInterpreter())
243243

244+
# External lattice without `Conditional`
245+
246+
import .CC:
247+
AbstractLattice, ConstsLattice, PartialsLattice, InferenceLattice, OptimizerLattice,
248+
typeinf_lattice, ipo_lattice, optimizer_lattice
249+
250+
@newinterp NonconditionalInterpreter
251+
CC.typeinf_lattice(::NonconditionalInterpreter) = InferenceLattice(PartialsLattice(ConstsLattice()))
252+
CC.ipo_lattice(::NonconditionalInterpreter) = InferenceLattice(PartialsLattice(ConstsLattice()))
253+
CC.optimizer_lattice(::NonconditionalInterpreter) = OptimizerLattice(PartialsLattice(ConstsLattice()))
254+
255+
@test Base.return_types((Any,); interp=NonconditionalInterpreter()) do x
256+
c = isa(x, Int) || isa(x, Float64)
257+
if c
258+
return x
259+
else
260+
return nothing
261+
end
262+
end |> only === Any
263+
244264
# CallInfo × inlining
245265
# ===================
246266

test/compiler/inference.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,8 +1438,9 @@ end
14381438

14391439
let egal_tfunc
14401440
function egal_tfunc(a, b)
1441-
r = Core.Compiler.egal_tfunc(a, b)
1442-
@test r === Core.Compiler.egal_tfunc(b, a)
1441+
𝕃 = Core.Compiler.fallback_lattice
1442+
r = Core.Compiler.egal_tfunc(𝕃, a, b)
1443+
@test r === Core.Compiler.egal_tfunc(𝕃, b, a)
14431444
return r
14441445
end
14451446
@test egal_tfunc(Const(12345.12345), Const(12344.12345 + 1)) == Const(true)

0 commit comments

Comments
 (0)