Skip to content

Commit 83b77e9

Browse files
committed
re-infer source when it's not cached
1 parent c09ef45 commit 83b77e9

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,
@@ -93,8 +94,7 @@ mutable struct OptimizationState
9394
inlining = InliningState(params,
9495
nothing,
9596
WorldView(code_cache(interp), get_world_counter()),
96-
get_inference_cache(interp),
97-
inlining_policy(interp))
97+
interp)
9898
return new(linfo,
9999
src, nothing, stmt_info, mod, nargs,
100100
sptypes_from_meth_instance(linfo), slottypes, false,
@@ -185,10 +185,8 @@ function isinlineable(m::Method, me::OptimizationState, params::OptimizationPara
185185
return inlineable
186186
end
187187

188-
is_stmt_inline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_INLINE != 0
189-
is_stmt_inline(::Nothing) = false
188+
is_stmt_inline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_INLINE != 0
190189
is_stmt_noinline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_NOINLINE != 0
191-
is_stmt_noinline(::Nothing) = false # not used for now
192190

193191
# These affect control flow within the function (so may not be removed
194192
# 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
@@ -725,7 +725,7 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
725725
(; match) = todo.spec::DelayedInliningSpec
726726

727727
#XXX: update_valid_age!(min_valid[1], max_valid[1], sv)
728-
isconst, src, argtypes = false, nothing, nothing
728+
isconst, src = false, nothing
729729
if isa(match, InferenceResult)
730730
let inferred_src = match.src
731731
if isa(inferred_src, Const)
@@ -737,9 +737,6 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
737737
isconst, src = false, inferred_src
738738
end
739739
end
740-
if is_stmt_inline(flag)
741-
argtypes = match.argtypes
742-
end
743740
else
744741
linfo = get(state.mi_cache, todo.mi, nothing)
745742
if linfo isa CodeInstance
@@ -752,9 +749,6 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
752749
else
753750
isconst, src = false, linfo
754751
end
755-
if is_stmt_inline(flag)
756-
argtypes = collect(todo.mi.specTypes.parameters)::Vector{Any}
757-
end
758752
end
759753

760754
et = state.et
@@ -764,18 +758,7 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
764758
return ConstantCase(src)
765759
end
766760

767-
if argtypes !== nothing && src === nothing
768-
inf_cache = state.inf_cache
769-
inf_result = cache_lookup(todo.mi, argtypes, inf_cache)
770-
if isa(inf_result, InferenceResult)
771-
src = inf_result.src
772-
if isa(src, OptimizationState)
773-
src = src.src
774-
end
775-
end
776-
end
777-
778-
src = state.policy(src, flag, match)
761+
src = inlining_policy(state.interp, src, flag, match)
779762

780763
if src === nothing
781764
return compileable_specialization(et, match)
@@ -1419,7 +1402,8 @@ end
14191402
function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::Expr, params::OptimizationParams)
14201403
f, ft, atypes = sig.f, sig.ft, sig.atypes
14211404
typ = ir.stmts[idx][:type]
1422-
if params.inlining && length(atypes) == 3 && istopfunction(f, :!==)
1405+
isinlining = params.inlining
1406+
if isinlining && length(atypes) == 3 && istopfunction(f, :!==)
14231407
# special-case inliner for !== that precedes _methods_by_ftype union splitting
14241408
# and that works, even though inference generally avoids inferring the `!==` Method
14251409
if isa(typ, Const)
@@ -1431,7 +1415,7 @@ function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::E
14311415
not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa)
14321416
ir[SSAValue(idx)] = not_call
14331417
return true
1434-
elseif params.inlining && length(atypes) == 3 && istopfunction(f, :(>:))
1418+
elseif isinlining && length(atypes) == 3 && istopfunction(f, :(>:))
14351419
# special-case inliner for issupertype
14361420
# that works, even though inference generally avoids inferring the `>:` Method
14371421
if isa(typ, Const) && _builtin_nothrow(<:, Any[atypes[3], atypes[2]], typ)
@@ -1441,7 +1425,7 @@ function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::E
14411425
subtype_call = Expr(:call, GlobalRef(Core, :(<:)), stmt.args[3], stmt.args[2])
14421426
ir[SSAValue(idx)] = subtype_call
14431427
return true
1444-
elseif params.inlining && f === TypeVar && 2 <= length(atypes) <= 4 && (atypes[2] Symbol)
1428+
elseif isinlining && f === TypeVar && 2 <= length(atypes) <= 4 && (atypes[2] Symbol)
14451429
ir[SSAValue(idx)] = Expr(:call, GlobalRef(Core, :_typevar), stmt.args[2],
14461430
length(stmt.args) < 4 ? Bottom : stmt.args[3],
14471431
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)