Skip to content

Commit 0bb8e7d

Browse files
committed
re-infer source when it's not cached
1 parent 4bb62b7 commit 0bb8e7d

File tree

5 files changed

+35
-38
lines changed

5 files changed

+35
-38
lines changed

base/compiler/abstractinterpretation.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter, match:
676676
cache_inf = code.inferred
677677
if !(cache_inf === nothing)
678678
# TODO maybe we want to respect callsite `@inline`/`@noinline` annotations here ?
679-
cache_inlineable = inlining_policy(interp)(cache_inf, nothing, match) !== nothing
679+
cache_inlineable = inlining_policy(interp, cache_inf, 0x00, match) !== nothing
680680
end
681681
end
682682
if !cache_inlineable

base/compiler/optimize.jl

+12-14
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ function push!(et::EdgeTracker, ci::CodeInstance)
2121
push!(et, ci.def)
2222
end
2323

24-
struct InliningState{S <: Union{EdgeTracker, Nothing}, T, P, U}
24+
struct InliningState{S <: Union{EdgeTracker, Nothing}, T, I<:AbstractInterpreter}
2525
params::OptimizationParams
2626
et::S
2727
mi_cache::T
28-
inf_cache::U
29-
policy::P
28+
interp::I
3029
end
3130

32-
function default_inlining_policy(@nospecialize(src), stmt_flag::Union{Nothing,UInt8}, match::Union{MethodMatch,InferenceResult})
31+
function inlining_policy(interp::AbstractInterpreter, @nospecialize(src), stmt_flag::UInt8, match::Union{MethodMatch,InferenceResult})
3332
if isa(src, CodeInfo) || isa(src, Vector{UInt8})
3433
src_inferred = ccall(:jl_ir_flag_inferred, Bool, (Any,), src)
3534
src_inlineable = is_stmt_inline(stmt_flag) || ccall(:jl_ir_flag_inlineable, Bool, (Any,), src)
@@ -38,9 +37,12 @@ function default_inlining_policy(@nospecialize(src), stmt_flag::Union{Nothing,UI
3837
return (is_stmt_inline(stmt_flag) || src.src.inlineable) ? src.ir : nothing
3938
elseif src === nothing && is_stmt_inline(stmt_flag) && isa(match, MethodMatch)
4039
# when the source isn't available at this moment, try to re-infer and inline it
41-
# HACK in order to avoid cycles here, we disable inlining and makes sure the following inference never comes here
42-
# TODO sort out `AbstractInterpreter` interface to handle this well, and also inference should try to keep the source if the statement will be inlined
43-
interp = NativeInterpreter(; opt_params = OptimizationParams(; inlining = false))
40+
# NOTE we can make inference try to keep the source if the call is going to be inlined,
41+
# but then inlining will depend on local state of inference and so the first entry
42+
# and the succeeding ones may generate different code; rather we always re-infer
43+
# the source to avoid the problem while it's obviously not most efficient
44+
# HACK disable inlining for the re-inference to avoid cycles by making sure the following inference never comes here again
45+
interp = NativeInterpreter(get_world_counter(interp); opt_params = OptimizationParams(; inlining = false))
4446
src, rt = typeinf_code(interp, match.method, match.spec_types, match.sparams, true)
4547
return src
4648
end
@@ -65,8 +67,7 @@ mutable struct OptimizationState
6567
inlining = InliningState(params,
6668
EdgeTracker(s_edges, frame.valid_worlds),
6769
WorldView(code_cache(interp), frame.world),
68-
get_inference_cache(interp),
69-
inlining_policy(interp))
70+
interp)
7071
return new(frame.linfo,
7172
frame.src, nothing, frame.stmt_info, frame.mod, frame.nargs,
7273
frame.sptypes, frame.slottypes, false,
@@ -100,8 +101,7 @@ mutable struct OptimizationState
100101
inlining = InliningState(params,
101102
nothing,
102103
WorldView(code_cache(interp), get_world_counter()),
103-
get_inference_cache(interp),
104-
inlining_policy(interp))
104+
interp)
105105
return new(linfo,
106106
src, nothing, stmt_info, inmodule, nargs,
107107
sptypes_from_meth_instance(linfo), slottypes, false,
@@ -192,10 +192,8 @@ function isinlineable(m::Method, me::OptimizationState, params::OptimizationPara
192192
return inlineable
193193
end
194194

195-
is_stmt_inline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_INLINE != 0
196-
is_stmt_inline(::Nothing) = false
195+
is_stmt_inline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_INLINE != 0
197196
is_stmt_noinline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_NOINLINE != 0
198-
is_stmt_noinline(::Nothing) = false # not used for now
199197

200198
# These affect control flow within the function (so may not be removed
201199
# if there is no usage within the function), but don't affect the purity

base/compiler/ssair/inlining.jl

+6-22
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
722722
(; match) = todo.spec::DelayedInliningSpec
723723

724724
#XXX: update_valid_age!(min_valid[1], max_valid[1], sv)
725-
isconst, src, argtypes = false, nothing, nothing
725+
isconst, src = false, nothing
726726
if isa(match, InferenceResult)
727727
let inferred_src = match.src
728728
if isa(inferred_src, Const)
@@ -734,9 +734,6 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
734734
isconst, src = false, inferred_src
735735
end
736736
end
737-
if is_stmt_inline(flag)
738-
argtypes = match.argtypes
739-
end
740737
else
741738
linfo = get(state.mi_cache, todo.mi, nothing)
742739
if linfo isa CodeInstance
@@ -749,9 +746,6 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
749746
else
750747
isconst, src = false, linfo
751748
end
752-
if is_stmt_inline(flag)
753-
argtypes = collect(todo.mi.specTypes.parameters)::Vector{Any}
754-
end
755749
end
756750

757751
et = state.et
@@ -761,18 +755,7 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
761755
return ConstantCase(src)
762756
end
763757

764-
if argtypes !== nothing && src === nothing
765-
inf_cache = state.inf_cache
766-
inf_result = cache_lookup(todo.mi, argtypes, inf_cache)
767-
if isa(inf_result, InferenceResult)
768-
src = inf_result.src
769-
if isa(src, OptimizationState)
770-
src = src.src
771-
end
772-
end
773-
end
774-
775-
src = state.policy(src, flag, match)
758+
src = inlining_policy(state.interp, src, flag, match)
776759

777760
if src === nothing
778761
return compileable_specialization(et, match)
@@ -1415,7 +1398,8 @@ end
14151398
function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::Expr, params::OptimizationParams)
14161399
f, ft, atypes = sig.f, sig.ft, sig.atypes
14171400
typ = ir.stmts[idx][:type]
1418-
if params.inlining && length(atypes) == 3 && istopfunction(f, :!==)
1401+
isinlining = params.inlining
1402+
if isinlining && length(atypes) == 3 && istopfunction(f, :!==)
14191403
# special-case inliner for !== that precedes _methods_by_ftype union splitting
14201404
# and that works, even though inference generally avoids inferring the `!==` Method
14211405
if isa(typ, Const)
@@ -1427,7 +1411,7 @@ function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::E
14271411
not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa)
14281412
ir[SSAValue(idx)] = not_call
14291413
return true
1430-
elseif params.inlining && length(atypes) == 3 && istopfunction(f, :(>:))
1414+
elseif isinlining && length(atypes) == 3 && istopfunction(f, :(>:))
14311415
# special-case inliner for issupertype
14321416
# that works, even though inference generally avoids inferring the `>:` Method
14331417
if isa(typ, Const) && _builtin_nothrow(<:, Any[atypes[3], atypes[2]], typ)
@@ -1437,7 +1421,7 @@ function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::E
14371421
subtype_call = Expr(:call, GlobalRef(Core, :(<:)), stmt.args[3], stmt.args[2])
14381422
ir[SSAValue(idx)] = subtype_call
14391423
return true
1440-
elseif params.inlining && f === TypeVar && 2 <= length(atypes) <= 4 && (atypes[2] Symbol)
1424+
elseif isinlining && f === TypeVar && 2 <= length(atypes) <= 4 && (atypes[2] Symbol)
14411425
ir[SSAValue(idx)] = Expr(:call, GlobalRef(Core, :_typevar), stmt.args[2],
14421426
length(stmt.args) < 4 ? Bottom : stmt.args[3],
14431427
length(stmt.args) == 2 ? Any : stmt.args[end])

base/compiler/types.jl

-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ may_discard_trees(ni::NativeInterpreter) = true
213213
verbose_stmt_info(ni::NativeInterpreter) = false
214214

215215
method_table(ai::AbstractInterpreter) = InternalMethodTable(get_world_counter(ai))
216-
inlining_policy(ai::AbstractInterpreter) = default_inlining_policy
217216

218217
# define inference bail out logic
219218
# `NativeInterpreter` bails out from inference when

test/compiler/inline.jl

+16
Original file line numberDiff line numberDiff line change
@@ -539,8 +539,19 @@ end
539539
end
540540
end
541541

542+
# test inlining of un-cached callsites
543+
542544
import Core.Compiler: isType
545+
543546
limited(a) = @noinline(isType(a)) ? @inline(limited(a.parameters[1])) : rand(a)
547+
548+
function multilimited(a)
549+
if @noinline(isType(a))
550+
return @inline(multilimited(a.parameters[1]))
551+
else
552+
return rand(Bool) ? rand(a) : @inline(multilimited(a))
553+
end
554+
end
544555
end
545556

546557
let ci = code_typed1(m.force_inline_explicit, (Int,))
@@ -593,6 +604,11 @@ end
593604
let ci = code_typed1(m.limited, (Any,))
594605
@test count(x->isinvoke(x, :isType), ci.code) == 2
595606
end
607+
# check that inlining for recursive callsites doesn't depend on inference local cache
608+
let ci1 = code_typed1(m.multilimited, (Any,))
609+
ci2 = code_typed1(m.multilimited, (Any,))
610+
@test ci1.code == ci2.code
611+
end
596612
end
597613

598614
# Issue #41299 - inlining deletes error check in :>

0 commit comments

Comments
 (0)