Skip to content

Don't inline callees if they might require _compute_sparams #59013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Compiler/src/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1472,15 +1472,22 @@ function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{Cod
return thiscost
end

function inline_cost_model(ir::IRCode, params::OptimizationParams, cost_threshold::Int)
function inline_cost_model(mi::MethodInstance, ir::IRCode, params::OptimizationParams, cost_threshold::Int)
bodycost = 0
uses_sparams = false
for i = 1:length(ir.stmts)
stmt = ir[SSAValue(i)][:stmt]
thiscost = statement_or_branch_cost(stmt, i, ir, ir.sptypes, params)
bodycost = plus_saturate(bodycost, thiscost)
if bodycost > cost_threshold
return MAX_INLINE_COST
end
uses_sparams |= isexpr(stmt, :static_parameter)
end
if uses_sparams && !validate_sparams(mi.sparam_vals)
# Inlining this would require the use of `_compute_sparams`,
# which is a bit too expensive to reasonably inline.
return MAX_INLINE_COST
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that Core._compute_sparams is completely unused now?
(unless an adventurous user writes it by hand)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, yes, unless we decide to go for a decision post-inlining (where we would rely on Core._compute_sparams being eventually removed), in this case it would live during optimization but would never be executed.

end
return inline_cost_clamp(bodycost)
end
Expand Down
2 changes: 1 addition & 1 deletion Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ function inline_cost_model(interp::AbstractInterpreter, result::InferenceResult,
cost_threshold += 4*default
end
end
return inline_cost_model(ir, params, cost_threshold)
return inline_cost_model(mi, ir, params, cost_threshold)
end
end

Expand Down
41 changes: 38 additions & 3 deletions Compiler/test/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1332,7 +1332,7 @@ mutable struct DoAllocNoEscapeSparam{T}
finalizer(finalizer_sparam, new{T}(x))
end
end
let src = code_typed1(Tuple{Any}) do x
let src = code_typed1(Tuple{Int}) do x
for i = 1:1000
DoAllocNoEscapeSparam(x)
end
Expand All @@ -1341,8 +1341,7 @@ let src = code_typed1(Tuple{Any}) do x
nnothrow_invokes = count(isinvoke(:nothrow_side_effect), src.code)
@test count(iscall(f->!isa(singleton_type(argextype(f, src)), Core.Builtin)), src.code) ==
count(iscall((src, nothrow_side_effect)), src.code) == 2 - nnothrow_invokes
# TODO: Our effect modeling is not yet strong enough to fully eliminate this
@test_broken count(isnew, src.code) == 0
@test count(isnew, src.code) == 0
end

# Test finalizer varargs
Expand Down Expand Up @@ -2314,4 +2313,40 @@ let src = code_typed1(g_noinline_invoke, (Union{Symbol,Nothing},))
@test !any(@nospecialize(x)->isa(x,GlobalRef), src.code)
end

# https://github.com/JuliaLang/julia/issues/58915
f58915(nt) = @inline Base.setindex(nt, 2, :next)
# This function should fully-inline, i.e. it should have only built-in / intrinsic calls
# and no invokes or dynamic calls of user code
let src = code_typed1(f58915, Tuple{@NamedTuple{next::UInt32,prev::UInt32}})
# Any calls should be built-in calls
@test count(iscall(f->!isa(singleton_type(argextype(f, src)), Core.Builtin)), src.code) == 0
# There should be no invoke at all
@test count(isinvoke(Returns(true)), src.code) == 0
end

# https://github.com/JuliaLang/julia/issues/58915#issuecomment-3061421895
let src = code_typed1(Base.setindex, (@NamedTuple{next::UInt32,prev::UInt32}, Int, Symbol))
@test count(isinvoke(:merge_fallback), src.code) == 0
@test count(iscall((src, Base.merge_fallback)), src.code) == 0
end

# https://github.com/JuliaLang/julia/pull/58996#issuecomment-3073496955
f58996(::Int) = :int
f58996(::String) = :string
call_f58996(x) = f58996(x)
callcall_f58996(x) = call_f58996(x);
let src = code_typed1(callcall_f58996, (Any,))
# Any calls should be built-in calls
@test count(iscall(f->!isa(singleton_type(argextype(f, src)), Core.Builtin)), src.code) == 0
# There should be no invoke at all
@test count(isinvoke(Returns(true)), src.code) == 0
end

f59013_using_sparams(::T) where {T} = T
f59013(x) = f59013_using_sparams(x)
let src = code_typed1(f59013, (Any,))
@test length(src.code) == 2
@test iscall((src, f59013_using_sparams), src.code[1])
end

end # module inline_tests