Skip to content

Commit 4c805d2

Browse files
authored
Stop setting force_noinline for function of union arguments (#27057)
The original motivation for setting force_noinline for functions with union arguments seems to have been bugs in type intersection. However, the type system has improved quite a bit and at least in the test suites we don't seem to run into any issues. Thus, we can stop setting force_noinline. Nevertheless, it still make since to penalize functions with union arguments. Doing so encourages union splits to happen at the call site, rather than having multiple redundant union splits inside the function. However, for simple functions of intrinsics and builtins it can make a lot of sense to inline the union split signature.
1 parent 667ab89 commit 4c805d2

File tree

2 files changed

+24
-14
lines changed

2 files changed

+24
-14
lines changed

base/compiler/optimize.jl

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function add_backedge!(li::CodeInstance, caller::OptimizationState)
125125
nothing
126126
end
127127

128-
function isinlineable(m::Method, me::OptimizationState, params::OptimizationParams, bonus::Int=0)
128+
function isinlineable(m::Method, me::OptimizationState, params::OptimizationParams, union_penalties::Bool, bonus::Int=0)
129129
# compute the cost (size) of inlining this code
130130
inlineable = false
131131
cost_threshold = params.inline_cost_threshold
@@ -143,7 +143,7 @@ function isinlineable(m::Method, me::OptimizationState, params::OptimizationPara
143143
end
144144
end
145145
if !inlineable
146-
inlineable = inline_worthy(me.src.code, me.src, me.sptypes, me.slottypes, params, cost_threshold + bonus)
146+
inlineable = inline_worthy(me.src.code, me.src, me.sptypes, me.slottypes, params, union_penalties, cost_threshold + bonus)
147147
end
148148
return inlineable
149149
end
@@ -219,15 +219,14 @@ function optimize(opt::OptimizationState, params::OptimizationParams, @nospecial
219219
replace_code_newstyle!(opt.src, ir, nargs)
220220

221221
# determine and cache inlineability
222+
union_penalties = false
222223
if !force_noinline
223-
# don't keep ASTs for functions specialized on a Union argument
224-
# TODO: this helps avoid a type-system bug mis-computing sparams during intersection
225224
sig = unwrap_unionall(opt.linfo.specTypes)
226225
if isa(sig, DataType) && sig.name === Tuple.name
227226
for P in sig.parameters
228227
P = unwrap_unionall(P)
229228
if isa(P, Union)
230-
force_noinline = true
229+
union_penalties = true
231230
break
232231
end
233232
end
@@ -252,7 +251,7 @@ function optimize(opt::OptimizationState, params::OptimizationParams, @nospecial
252251
# For functions declared @inline, increase the cost threshold 20x
253252
bonus += params.inline_cost_threshold*19
254253
end
255-
opt.src.inlineable = isinlineable(def, opt, params, bonus)
254+
opt.src.inlineable = isinlineable(def, opt, params, union_penalties, bonus)
256255
end
257256
end
258257
nothing
@@ -281,7 +280,9 @@ plus_saturate(x::Int, y::Int) = max(x, y, x+y)
281280
# known return type
282281
isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isconcretetype(widenconst(T))
283282

284-
function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any}, slottypes::Vector{Any}, params::OptimizationParams, error_path::Bool = false)
283+
function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any},
284+
slottypes::Vector{Any}, union_penalties::Bool,
285+
params::OptimizationParams, error_path::Bool = false)
285286
head = ex.head
286287
if is_meta_expr_head(head)
287288
return 0
@@ -314,6 +315,13 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any}
314315
# tuple iteration/destructuring makes that impossible
315316
# return plus_saturate(argcost, isknowntype(extyp) ? 1 : params.inline_nonleaf_penalty)
316317
return 0
318+
elseif f === Main.Core.isa
319+
# If we're in a union context, we penalize type computations
320+
# on union types. In such cases, it is usually better to perform
321+
# union splitting on the outside.
322+
if union_penalties && isa(argextype(ex.args[2], src, sptypes, slottypes), Union)
323+
return params.inline_nonleaf_penalty
324+
end
317325
elseif (f === Main.Core.arrayref || f === Main.Core.const_arrayref) && length(ex.args) >= 3
318326
atyp = argextype(ex.args[3], src, sptypes, slottypes)
319327
return isknowntype(atyp) ? 4 : error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty
@@ -362,10 +370,12 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any}
362370
return 0
363371
end
364372

365-
function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::CodeInfo, sptypes::Vector{Any}, slottypes::Vector{Any}, params::OptimizationParams, throw_blocks::Union{Nothing,BitSet})
373+
function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::CodeInfo, sptypes::Vector{Any},
374+
slottypes::Vector{Any}, union_penalties::Bool, params::OptimizationParams,
375+
throw_blocks::Union{Nothing,BitSet})
366376
thiscost = 0
367377
if stmt isa Expr
368-
thiscost = statement_cost(stmt, line, src, sptypes, slottypes, params,
378+
thiscost = statement_cost(stmt, line, src, sptypes, slottypes, union_penalties, params,
369379
params.unoptimize_throw_blocks && line in throw_blocks)::Int
370380
elseif stmt isa GotoNode
371381
# loops are generally always expensive
@@ -379,24 +389,24 @@ function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::CodeInfo,
379389
end
380390

381391
function inline_worthy(body::Array{Any,1}, src::CodeInfo, sptypes::Vector{Any}, slottypes::Vector{Any},
382-
params::OptimizationParams, cost_threshold::Integer=params.inline_cost_threshold)
392+
params::OptimizationParams, union_penalties::Bool=false, cost_threshold::Integer=params.inline_cost_threshold)
383393
bodycost::Int = 0
384394
throw_blocks = params.unoptimize_throw_blocks ? find_throw_blocks(body) : nothing
385395
for line = 1:length(body)
386396
stmt = body[line]
387-
thiscost = statement_or_branch_cost(stmt, line, src, sptypes, slottypes, params, throw_blocks)
397+
thiscost = statement_or_branch_cost(stmt, line, src, sptypes, slottypes, union_penalties, params, throw_blocks)
388398
bodycost = plus_saturate(bodycost, thiscost)
389399
bodycost > cost_threshold && return false
390400
end
391401
return true
392402
end
393403

394-
function statement_costs!(cost::Vector{Int}, body::Vector{Any}, src::CodeInfo, sptypes::Vector{Any}, params::OptimizationParams)
404+
function statement_costs!(cost::Vector{Int}, body::Vector{Any}, src::CodeInfo, sptypes::Vector{Any}, unionpenalties::Bool, params::OptimizationParams)
395405
throw_blocks = params.unoptimize_throw_blocks ? find_throw_blocks(body) : nothing
396406
maxcost = 0
397407
for line = 1:length(body)
398408
stmt = body[line]
399-
thiscost = statement_or_branch_cost(stmt, line, src, sptypes, src.slottypes, params, throw_blocks)
409+
thiscost = statement_or_branch_cost(stmt, line, src, sptypes, src.slottypes, unionpenalties, params, throw_blocks)
400410
cost[line] = thiscost
401411
if thiscost > maxcost
402412
maxcost = thiscost

base/reflection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type);
11611161
code === nothing && error("inference not successful") # inference disabled?
11621162
empty!(cst)
11631163
resize!(cst, length(code.code))
1164-
maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], params)
1164+
maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], false, params)
11651165
nd = ndigits(maxcost)
11661166
println(io, meth)
11671167
IRShow.show_ir(io, code, (io, linestart, idx) -> (print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " "); return ""))

0 commit comments

Comments
 (0)