Skip to content

Commit 7bc013f

Browse files
committed
inlining: Don't inline concrete-eval'ed calls whose result was too large
This undoes some of the choices made in #47283 and #47305. As Shuhei correctly pointed out, even with the restriction to `nothrow`, adding the extra flags on the inlined statements results in incorrect IR. Also, my bigger motivating test case turns out to be insufficiently optimized without the effect_free flags (which I removed in the final revision of #47305). I think for the time being, the best course of action here is to just stop inlining concrete-eval'ed calls entirely. The const result is available for inference, so in most cases the call will get deleted. If there's an important case we care about where this does not happen, we should take a look at that separately.
1 parent f71b839 commit 7bc013f

File tree

2 files changed

+25
-93
lines changed

2 files changed

+25
-93
lines changed

base/compiler/ssair/inlining.jl

+25-73
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,7 @@ end
370370

371371
function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any},
372372
linetable::Vector{LineInfoNode}, item::InliningTodo,
373-
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}},
374-
extra_flags::UInt8 = inlined_flags_for_effects(item.effects))
373+
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
375374
# Ok, do the inlining here
376375
sparam_vals = item.mi.sparam_vals
377376
def = item.mi.def::Method
@@ -447,14 +446,6 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
447446
stmt′ = PhiNode(Int32[edge+bb_offset for edge in stmt′.edges], stmt′.values)
448447
end
449448
inline_compact[idx′] = stmt′
450-
if extra_flags != 0 && !isa(stmt′, Union{GotoNode, GotoIfNot})
451-
if (extra_flags & IR_FLAG_NOTHROW) != 0 && inline_compact[SSAValue(idx′)][:type] === Union{}
452-
# Shown nothrow, but also guaranteed to throw => unreachable
453-
inline_compact[idx′] = ReturnNode()
454-
else
455-
inline_compact[SSAValue(idx′)][:flag] |= extra_flags
456-
end
457-
end
458449
end
459450
just_fixup!(inline_compact, new_new_offset, late_fixup_offset)
460451
compact.result_idx = inline_compact.result_idx
@@ -1012,37 +1003,6 @@ function flags_for_effects(effects::Effects)
10121003
return flags
10131004
end
10141005

1015-
"""
1016-
inlined_flags_for_effects(effects::Effects)
1017-
1018-
This function answers the query:
1019-
1020-
Given a call site annotated as `effects`, what can we say about each inlined
1021-
statement after the inlining?
1022-
1023-
Note that this is different from `flags_for_effects`, which just talks about
1024-
the call site itself. Consider for example:
1025-
1026-
````
1027-
function foo()
1028-
V = Any[]
1029-
push!(V, 1)
1030-
tuple(V...)
1031-
end
1032-
```
1033-
1034-
This function is properly inferred effect_free, because it has no global effects.
1035-
However, we may not inline each statement with an :effect_free flag, because
1036-
that would incorrectly lose the `push!`.
1037-
"""
1038-
function inlined_flags_for_effects(effects::Effects)
1039-
flags::UInt8 = 0
1040-
if is_nothrow(effects)
1041-
flags |= IR_FLAG_NOTHROW
1042-
end
1043-
return flags
1044-
end
1045-
10461006
function handle_single_case!(todo::Vector{Pair{Int,Any}},
10471007
ir::IRCode, idx::Int, stmt::Expr, @nospecialize(case), params::OptimizationParams,
10481008
isinvoke::Bool = false)
@@ -1221,24 +1181,20 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}},
12211181
invokesig = sig.argtypes
12221182
override_effects = EFFECTS_UNKNOWN′
12231183
if isa(result, ConcreteResult)
1224-
if may_inline_concrete_result(result)
1225-
item = concrete_result_item(result, state; invokesig)
1226-
handle_single_case!(todo, ir, idx, stmt, item, state.params, true)
1227-
return nothing
1228-
end
1229-
override_effects = result.effects
1230-
end
1231-
argtypes = invoke_rewrite(sig.argtypes)
1232-
if isa(result, ConstPropResult)
1233-
mi = result.result.linfo
1234-
validate_sparams(mi.sparam_vals) || return nothing
1235-
if argtypes_to_type(argtypes) <: mi.def.sig
1236-
item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig, override_effects)
1237-
handle_single_case!(todo, ir, idx, stmt, item, state.params, true)
1238-
return nothing
1184+
item = concrete_result_item(result, state, info; invokesig)
1185+
else
1186+
argtypes = invoke_rewrite(sig.argtypes)
1187+
if isa(result, ConstPropResult)
1188+
mi = result.result.linfo
1189+
validate_sparams(mi.sparam_vals) || return nothing
1190+
if argtypes_to_type(argtypes) <: mi.def.sig
1191+
item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig, override_effects)
1192+
handle_single_case!(todo, ir, idx, stmt, item, state.params, true)
1193+
return nothing
1194+
end
12391195
end
1196+
item = analyze_method!(match, argtypes, info, flag, state; allow_typevars=false, invokesig, override_effects)
12401197
end
1241-
item = analyze_method!(match, argtypes, info, flag, state; allow_typevars=false, invokesig, override_effects)
12421198
handle_single_case!(todo, ir, idx, stmt, item, state.params, true)
12431199
return nothing
12441200
end
@@ -1352,12 +1308,7 @@ function handle_any_const_result!(cases::Vector{InliningCase},
13521308
allow_abstract::Bool, allow_typevars::Bool)
13531309
override_effects = EFFECTS_UNKNOWN′
13541310
if isa(result, ConcreteResult)
1355-
if may_inline_concrete_result(result)
1356-
return handle_concrete_result!(cases, result, state)
1357-
else
1358-
override_effects = result.effects
1359-
result = nothing
1360-
end
1311+
return handle_concrete_result!(cases, result, state, info)
13611312
end
13621313
if isa(result, SemiConcreteResult)
13631314
result = inlining_policy(state.interp, result, info, flag, result.mi, argtypes)
@@ -1538,18 +1489,24 @@ function handle_semi_concrete_result!(cases::Vector{InliningCase}, result::SemiC
15381489
return true
15391490
end
15401491

1541-
function handle_concrete_result!(cases::Vector{InliningCase}, result::ConcreteResult, state::InliningState)
1542-
case = concrete_result_item(result, state)
1492+
function handle_concrete_result!(cases::Vector{InliningCase}, result::ConcreteResult, state::InliningState, @nospecialize(info::CallInfo))
1493+
case = concrete_result_item(result, state, info)
15431494
push!(cases, InliningCase(result.mi.specTypes, case))
15441495
return true
15451496
end
15461497

15471498
may_inline_concrete_result(result::ConcreteResult) =
15481499
isdefined(result, :result) && is_inlineable_constant(result.result)
15491500

1550-
function concrete_result_item(result::ConcreteResult, state::InliningState;
1501+
function concrete_result_item(result::ConcreteResult, state::InliningState, @nospecialize(info::CallInfo);
15511502
invokesig::Union{Nothing,Vector{Any}}=nothing)
1552-
@assert may_inline_concrete_result(result)
1503+
if !may_inline_concrete_result(result)
1504+
et = InliningEdgeTracker(state.et, invokesig)
1505+
case = compileable_specialization(result.mi, result.effects, et, info;
1506+
compilesig_invokes=state.params.compilesig_invokes)
1507+
@assert case !== nothing "concrete evaluation should never happen for uncompileable callsite"
1508+
return case
1509+
end
15531510
@assert result.effects === EFFECTS_TOTAL
15541511
return ConstantCase(quoted(result.result))
15551512
end
@@ -1583,12 +1540,7 @@ function handle_opaque_closure_call!(todo::Vector{Pair{Int,Any}},
15831540
validate_sparams(mi.sparam_vals) || return nothing
15841541
item = resolve_todo(mi, result.result, sig.argtypes, info, flag, state)
15851542
elseif isa(result, ConcreteResult)
1586-
if may_inline_concrete_result(result)
1587-
item = concrete_result_item(result, state)
1588-
else
1589-
override_effects = result.effects
1590-
item = analyze_method!(info.match, sig.argtypes, info, flag, state; allow_typevars=false, override_effects)
1591-
end
1543+
item = concrete_result_item(result, state, info)
15921544
else
15931545
item = analyze_method!(info.match, sig.argtypes, info, flag, state; allow_typevars=false)
15941546
end

test/compiler/inline.jl

-20
Original file line numberDiff line numberDiff line change
@@ -1039,26 +1039,6 @@ struct FooTheRef
10391039
x::Ref
10401040
FooTheRef(v) = new(v === nothing ? THE_REF_NULL : THE_REF)
10411041
end
1042-
let src = code_typed1() do
1043-
FooTheRef(nothing)
1044-
end
1045-
@test count(isnew, src.code) == 1
1046-
end
1047-
let src = code_typed1() do
1048-
FooTheRef(0)
1049-
end
1050-
@test count(isnew, src.code) == 1
1051-
end
1052-
let src = code_typed1() do
1053-
@invoke FooTheRef(nothing::Any)
1054-
end
1055-
@test count(isnew, src.code) == 1
1056-
end
1057-
let src = code_typed1() do
1058-
@invoke FooTheRef(0::Any)
1059-
end
1060-
@test count(isnew, src.code) == 1
1061-
end
10621042
@test fully_eliminated() do
10631043
FooTheRef(nothing)
10641044
nothing

0 commit comments

Comments
 (0)